Repository: dinerojs/dinero.js Branch: main Commit: 78b9d443cec6 Files: 573 Total size: 1.2 MB Directory structure: gitextract_430igyon/ ├── .agents/ │ └── skills/ │ ├── dinero-best-practices/ │ │ ├── SKILL.md │ │ └── rules/ │ │ ├── arithmetic-allocate-not-divide.md │ │ ├── arithmetic-immutability.md │ │ ├── arithmetic-percentages.md │ │ ├── arithmetic-scaled-amounts.md │ │ ├── creation-from-floats.md │ │ ├── creation-minor-units.md │ │ ├── creation-zero-exponent.md │ │ ├── imports-bigint-currencies.md │ │ ├── imports-tree-shaking.md │ │ ├── precision-bigint.md │ │ ├── precision-crypto.md │ │ └── precision-trim-scale.md │ ├── dinero-currency-patterns/ │ │ ├── SKILL.md │ │ └── rules/ │ │ ├── convert-reusable.md │ │ ├── convert-scaled-rates.md │ │ ├── payment-services.md │ │ ├── storage-database.md │ │ ├── storage-no-money-type.md │ │ ├── types-as-const.md │ │ ├── types-currency-mismatch.md │ │ └── types-lookup-validation.md │ ├── dinero-formatting/ │ │ ├── SKILL.md │ │ └── rules/ │ │ ├── display-no-currency-symbols.md │ │ ├── display-to-decimal.md │ │ ├── locale-intl-formatter.md │ │ ├── locale-multilingual.md │ │ ├── nondecimal-to-units.md │ │ ├── serialization-bigint-json.md │ │ └── serialization-snapshot.md │ ├── tsdown/ │ │ ├── SKILL.md │ │ └── references/ │ │ ├── advanced-ci.md │ │ ├── advanced-hooks.md │ │ ├── advanced-plugins.md │ │ ├── advanced-programmatic.md │ │ ├── advanced-rolldown-options.md │ │ ├── guide-getting-started.md │ │ ├── guide-migrate-from-tsup.md │ │ ├── option-cjs-default.md │ │ ├── option-cleaning.md │ │ ├── option-config-file.md │ │ ├── option-css.md │ │ ├── option-dependencies.md │ │ ├── option-dts.md │ │ ├── option-entry.md │ │ ├── option-lint.md │ │ ├── option-log-level.md │ │ ├── option-minification.md │ │ ├── option-output-directory.md │ │ ├── option-output-format.md │ │ ├── option-package-exports.md │ │ ├── option-platform.md │ │ ├── option-shims.md │ │ ├── option-sourcemap.md │ │ ├── option-target.md │ │ ├── option-tree-shaking.md │ │ ├── option-unbundle.md │ │ ├── option-watch-mode.md │ │ ├── recipe-react.md │ │ ├── recipe-vue.md │ │ ├── recipe-wasm.md │ │ └── reference-cli.md │ ├── vercel-composition-patterns/ │ │ ├── AGENTS.md │ │ ├── README.md │ │ ├── SKILL.md │ │ └── rules/ │ │ ├── architecture-avoid-boolean-props.md │ │ ├── architecture-compound-components.md │ │ ├── patterns-children-over-render-props.md │ │ ├── patterns-explicit-variants.md │ │ ├── react19-no-forwardref.md │ │ ├── state-context-interface.md │ │ ├── state-decouple-implementation.md │ │ └── state-lift-state.md │ ├── vercel-react-best-practices/ │ │ ├── AGENTS.md │ │ ├── README.md │ │ ├── SKILL.md │ │ └── rules/ │ │ ├── advanced-event-handler-refs.md │ │ ├── advanced-init-once.md │ │ ├── advanced-use-latest.md │ │ ├── async-api-routes.md │ │ ├── async-defer-await.md │ │ ├── async-dependencies.md │ │ ├── async-parallel.md │ │ ├── async-suspense-boundaries.md │ │ ├── bundle-barrel-imports.md │ │ ├── bundle-conditional.md │ │ ├── bundle-defer-third-party.md │ │ ├── bundle-dynamic-imports.md │ │ ├── bundle-preload.md │ │ ├── client-event-listeners.md │ │ ├── client-localstorage-schema.md │ │ ├── client-passive-event-listeners.md │ │ ├── client-swr-dedup.md │ │ ├── js-batch-dom-css.md │ │ ├── js-cache-function-results.md │ │ ├── js-cache-property-access.md │ │ ├── js-cache-storage.md │ │ ├── js-combine-iterations.md │ │ ├── js-early-exit.md │ │ ├── js-hoist-regexp.md │ │ ├── js-index-maps.md │ │ ├── js-length-check-first.md │ │ ├── js-min-max-loop.md │ │ ├── js-set-map-lookups.md │ │ ├── js-tosorted-immutable.md │ │ ├── rendering-activity.md │ │ ├── rendering-animate-svg-wrapper.md │ │ ├── rendering-conditional-render.md │ │ ├── rendering-content-visibility.md │ │ ├── rendering-hoist-jsx.md │ │ ├── rendering-hydration-no-flicker.md │ │ ├── rendering-hydration-suppress-warning.md │ │ ├── rendering-svg-precision.md │ │ ├── rendering-usetransition-loading.md │ │ ├── rerender-defer-reads.md │ │ ├── rerender-dependencies.md │ │ ├── rerender-derived-state-no-effect.md │ │ ├── rerender-derived-state.md │ │ ├── rerender-functional-setstate.md │ │ ├── rerender-lazy-state-init.md │ │ ├── rerender-memo-with-default-value.md │ │ ├── rerender-memo.md │ │ ├── rerender-move-effect-to-event.md │ │ ├── rerender-simple-expression-in-memo.md │ │ ├── rerender-transitions.md │ │ ├── rerender-use-ref-transient-values.md │ │ ├── server-after-nonblocking.md │ │ ├── server-auth-actions.md │ │ ├── server-cache-lru.md │ │ ├── server-cache-react.md │ │ ├── server-dedup-props.md │ │ ├── server-parallel-fetching.md │ │ └── server-serialization.md │ └── web-design-guidelines/ │ └── SKILL.md ├── .browserslistrc ├── .claude/ │ ├── docs/ │ │ ├── architecture.md │ │ ├── git-workflow.md │ │ └── linear.md │ └── skills/ │ └── commit/ │ └── SKILL.md ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ └── BUG.yml │ ├── semantic.yml │ └── workflows/ │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .npmrc ├── .nvmrc ├── .oxlintrc.json ├── .prettierignore ├── .prettierrc ├── .size-limit.json ├── .vscode/ │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CLAUDE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs/ │ ├── .vitepress/ │ │ ├── config.ts │ │ └── theme/ │ │ ├── HomeFeatures.vue │ │ ├── HomeHero.vue │ │ ├── NotFound.vue │ │ ├── index.ts │ │ └── style.css │ ├── about.md │ ├── agent-skills.md │ ├── api/ │ │ ├── comparisons/ │ │ │ ├── compare.md │ │ │ ├── equal.md │ │ │ ├── greater-than-or-equal.md │ │ │ ├── greater-than.md │ │ │ ├── has-sub-units.md │ │ │ ├── have-same-amount.md │ │ │ ├── have-same-currency.md │ │ │ ├── is-negative.md │ │ │ ├── is-positive.md │ │ │ ├── is-zero.md │ │ │ ├── less-than-or-equal.md │ │ │ ├── less-than.md │ │ │ ├── maximum.md │ │ │ └── minimum.md │ │ ├── conversions/ │ │ │ ├── convert.md │ │ │ ├── normalize-scale.md │ │ │ ├── transform-scale.md │ │ │ └── trim-scale.md │ │ ├── currencies.md │ │ ├── dinero.md │ │ ├── formatting/ │ │ │ ├── to-decimal.md │ │ │ ├── to-snapshot.md │ │ │ └── to-units.md │ │ ├── mutations/ │ │ │ ├── add.md │ │ │ ├── allocate.md │ │ │ ├── multiply.md │ │ │ └── subtract.md │ │ └── rounding/ │ │ ├── down.md │ │ ├── half-away-from-zero.md │ │ ├── half-down.md │ │ ├── half-even.md │ │ ├── half-odd.md │ │ ├── half-towards-zero.md │ │ ├── half-up.md │ │ └── up.md │ ├── build-examples.sh │ ├── core-concepts/ │ │ ├── amount.md │ │ ├── comparisons.md │ │ ├── currency.md │ │ ├── formatting.md │ │ ├── mutations.md │ │ └── scale.md │ ├── demos.md │ ├── faq/ │ │ ├── can-i-multiply-by-a-decimal.md │ │ ├── how-to-look-up-a-currency-by-code.md │ │ ├── why-cant-i-use-currencies-with-bigint.md │ │ ├── why-functions-instead-of-methods.md │ │ └── why-no-currency-formatting.md │ ├── getting-started/ │ │ ├── compatibility.md │ │ ├── optimizing-for-production.md │ │ ├── quick-start.md │ │ └── upgrade-guide.md │ ├── guides/ │ │ ├── calculating-percentages.md │ │ ├── creating-from-floats.md │ │ ├── cryptocurrencies.md │ │ ├── currency-type-safety.md │ │ ├── formatting-in-a-multilingual-site.md │ │ ├── formatting-non-decimal-currencies.md │ │ ├── integrating-with-payment-services.md │ │ ├── precision-and-large-numbers.md │ │ ├── storing-in-a-database.md │ │ └── transporting-and-restoring.md │ ├── index.md │ ├── package.json │ └── vercel.json ├── examples/ │ ├── cart-react/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── components/ │ │ │ │ ├── cart-line.tsx │ │ │ │ └── order-summary.tsx │ │ │ ├── data/ │ │ │ │ ├── index.ts │ │ │ │ ├── items.json │ │ │ │ └── shipping.json │ │ │ ├── index.css │ │ │ ├── lib/ │ │ │ │ └── money.ts │ │ │ ├── main.tsx │ │ │ └── types/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── cart-vue/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── env.d.ts │ │ ├── index.html │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── App.vue │ │ │ ├── components/ │ │ │ │ ├── CartLine.vue │ │ │ │ └── OrderSummary.vue │ │ │ ├── data/ │ │ │ │ ├── index.ts │ │ │ │ ├── items.json │ │ │ │ └── shipping.json │ │ │ ├── index.css │ │ │ ├── lib/ │ │ │ │ └── money.ts │ │ │ ├── main.ts │ │ │ └── types/ │ │ │ └── index.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── expense-splitter/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── components/ │ │ │ │ ├── add-expense.tsx │ │ │ │ ├── add-person.tsx │ │ │ │ ├── balances.tsx │ │ │ │ ├── expense-list.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── person-list.tsx │ │ │ │ └── settlements.tsx │ │ │ ├── index.css │ │ │ ├── lib/ │ │ │ │ └── money.ts │ │ │ ├── main.tsx │ │ │ ├── types/ │ │ │ │ └── index.ts │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── invoice-builder/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── components/ │ │ │ │ ├── editor-panel.tsx │ │ │ │ └── preview-panel.tsx │ │ │ ├── hooks/ │ │ │ │ └── use-invoice.ts │ │ │ ├── index.css │ │ │ ├── lib/ │ │ │ │ ├── invoice-types.ts │ │ │ │ └── money.ts │ │ │ ├── main.tsx │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── portfolio-tracker/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── components/ │ │ │ │ ├── breakdown-bar.tsx │ │ │ │ ├── dashboard.tsx │ │ │ │ ├── exchange-rates.tsx │ │ │ │ ├── holding-card.tsx │ │ │ │ ├── holdings-editor.tsx │ │ │ │ ├── holdings-table.tsx │ │ │ │ └── summary-cards.tsx │ │ │ ├── hooks/ │ │ │ │ └── use-portfolio.ts │ │ │ ├── index.css │ │ │ ├── lib/ │ │ │ │ ├── money.ts │ │ │ │ └── types.ts │ │ │ └── main.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── pricing-react/ │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── src/ │ │ ├── App.tsx │ │ ├── components/ │ │ │ ├── pricing-toggle.tsx │ │ │ ├── seat-slider.tsx │ │ │ └── tier-card.tsx │ │ ├── data/ │ │ │ ├── index.ts │ │ │ └── items.json │ │ ├── index.css │ │ ├── lib/ │ │ │ └── money.ts │ │ ├── main.tsx │ │ └── types/ │ │ └── index.ts │ ├── tsconfig.json │ └── vite.config.ts ├── global.d.ts ├── lint-staged.config.cjs ├── package.json ├── packages/ │ └── dinero.js/ │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── __tests__/ │ │ │ ├── currency-safety.typetest.ts │ │ │ └── dinero.test.ts │ │ ├── api/ │ │ │ ├── __tests__/ │ │ │ │ ├── add.test.ts │ │ │ │ ├── allocate.test.ts │ │ │ │ ├── compare.test.ts │ │ │ │ ├── convert.test.ts │ │ │ │ ├── equal.test.ts │ │ │ │ ├── greaterThan.test.ts │ │ │ │ ├── greaterThanOrEqual.test.ts │ │ │ │ ├── hasSubUnits.test.ts │ │ │ │ ├── haveSameAmount.test.ts │ │ │ │ ├── haveSameCurrency.test.ts │ │ │ │ ├── isNegative.test.ts │ │ │ │ ├── isPositive.test.ts │ │ │ │ ├── isZero.test.ts │ │ │ │ ├── lessThan.test.ts │ │ │ │ ├── lessThanOrEqual.test.ts │ │ │ │ ├── maximum.test.ts │ │ │ │ ├── minimum.test.ts │ │ │ │ ├── multiply.test.ts │ │ │ │ ├── normalizeScale.test.ts │ │ │ │ ├── subtract.test.ts │ │ │ │ ├── toDecimal.test.ts │ │ │ │ ├── toSnapshot.test.ts │ │ │ │ ├── toUnits.test.ts │ │ │ │ ├── transformScale.test.ts │ │ │ │ └── trimScale.test.ts │ │ │ ├── add.ts │ │ │ ├── allocate.ts │ │ │ ├── compare.ts │ │ │ ├── convert.ts │ │ │ ├── equal.ts │ │ │ ├── greaterThan.ts │ │ │ ├── greaterThanOrEqual.ts │ │ │ ├── hasSubUnits.ts │ │ │ ├── haveSameAmount.ts │ │ │ ├── haveSameCurrency.ts │ │ │ ├── index.ts │ │ │ ├── isNegative.ts │ │ │ ├── isPositive.ts │ │ │ ├── isZero.ts │ │ │ ├── lessThan.ts │ │ │ ├── lessThanOrEqual.ts │ │ │ ├── maximum.ts │ │ │ ├── minimum.ts │ │ │ ├── multiply.ts │ │ │ ├── normalizeScale.ts │ │ │ ├── subtract.ts │ │ │ ├── toDecimal.ts │ │ │ ├── toSnapshot.ts │ │ │ ├── toUnits.ts │ │ │ ├── transformScale.ts │ │ │ └── trimScale.ts │ │ ├── bigint/ │ │ │ ├── currencies/ │ │ │ │ ├── index.ts │ │ │ │ └── iso4217.ts │ │ │ ├── dinero.ts │ │ │ └── index.ts │ │ ├── calculator/ │ │ │ ├── bigint/ │ │ │ │ ├── api/ │ │ │ │ │ ├── __tests__/ │ │ │ │ │ │ ├── add.test.ts │ │ │ │ │ │ ├── compare.test.ts │ │ │ │ │ │ ├── decrement.test.ts │ │ │ │ │ │ ├── increment.test.ts │ │ │ │ │ │ ├── integerDivide.test.ts │ │ │ │ │ │ ├── modulo.test.ts │ │ │ │ │ │ ├── multiply.test.ts │ │ │ │ │ │ ├── power.test.ts │ │ │ │ │ │ ├── subtract.test.ts │ │ │ │ │ │ └── zero.test.ts │ │ │ │ │ ├── add.ts │ │ │ │ │ ├── compare.ts │ │ │ │ │ ├── decrement.ts │ │ │ │ │ ├── increment.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── integerDivide.ts │ │ │ │ │ ├── modulo.ts │ │ │ │ │ ├── multiply.ts │ │ │ │ │ ├── power.ts │ │ │ │ │ ├── subtract.ts │ │ │ │ │ └── zero.ts │ │ │ │ ├── calculator.ts │ │ │ │ └── index.ts │ │ │ └── number/ │ │ │ ├── api/ │ │ │ │ ├── __tests__/ │ │ │ │ │ ├── add.test.ts │ │ │ │ │ ├── compare.test.ts │ │ │ │ │ ├── decrement.test.ts │ │ │ │ │ ├── increment.test.ts │ │ │ │ │ ├── integerDivide.test.ts │ │ │ │ │ ├── modulo.test.ts │ │ │ │ │ ├── multiply.test.ts │ │ │ │ │ ├── power.test.ts │ │ │ │ │ ├── subtract.test.ts │ │ │ │ │ └── zero.test.ts │ │ │ │ ├── add.ts │ │ │ │ ├── compare.ts │ │ │ │ ├── decrement.ts │ │ │ │ ├── increment.ts │ │ │ │ ├── index.ts │ │ │ │ ├── integerDivide.ts │ │ │ │ ├── modulo.ts │ │ │ │ ├── multiply.ts │ │ │ │ ├── power.ts │ │ │ │ ├── subtract.ts │ │ │ │ └── zero.ts │ │ │ ├── calculator.ts │ │ │ └── index.ts │ │ ├── core/ │ │ │ ├── api/ │ │ │ │ ├── add.ts │ │ │ │ ├── allocate.ts │ │ │ │ ├── compare.ts │ │ │ │ ├── convert.ts │ │ │ │ ├── equal.ts │ │ │ │ ├── greaterThan.ts │ │ │ │ ├── greaterThanOrEqual.ts │ │ │ │ ├── hasSubUnits.ts │ │ │ │ ├── haveSameAmount.ts │ │ │ │ ├── haveSameCurrency.ts │ │ │ │ ├── index.ts │ │ │ │ ├── isNegative.ts │ │ │ │ ├── isPositive.ts │ │ │ │ ├── isZero.ts │ │ │ │ ├── lessThan.ts │ │ │ │ ├── lessThanOrEqual.ts │ │ │ │ ├── maximum.ts │ │ │ │ ├── minimum.ts │ │ │ │ ├── multiply.ts │ │ │ │ ├── normalizeScale.ts │ │ │ │ ├── subtract.ts │ │ │ │ ├── toDecimal.ts │ │ │ │ ├── toSnapshot.ts │ │ │ │ ├── toUnits.ts │ │ │ │ ├── transformScale.ts │ │ │ │ └── trimScale.ts │ │ │ ├── checks/ │ │ │ │ ├── index.ts │ │ │ │ └── messages.ts │ │ │ ├── divide/ │ │ │ │ ├── __tests__/ │ │ │ │ │ ├── down.test.ts │ │ │ │ │ ├── halfAwayFromZero.test.ts │ │ │ │ │ ├── halfDown.test.ts │ │ │ │ │ ├── halfEven.test.ts │ │ │ │ │ ├── halfOdd.test.ts │ │ │ │ │ ├── halfTowardsZero.test.ts │ │ │ │ │ ├── halfUp.test.ts │ │ │ │ │ └── up.test.ts │ │ │ │ ├── down.ts │ │ │ │ ├── halfAwayFromZero.ts │ │ │ │ ├── halfDown.ts │ │ │ │ ├── halfEven.ts │ │ │ │ ├── halfOdd.ts │ │ │ │ ├── halfTowardsZero.ts │ │ │ │ ├── halfUp.ts │ │ │ │ ├── index.ts │ │ │ │ └── up.ts │ │ │ ├── helpers/ │ │ │ │ ├── __tests__/ │ │ │ │ │ └── assert.test.ts │ │ │ │ ├── assert.ts │ │ │ │ ├── createDinero.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── types/ │ │ │ │ ├── Dinero.ts │ │ │ │ ├── DineroBinaryOperation.ts │ │ │ │ ├── DineroCalculator.ts │ │ │ │ ├── DineroDivideOperation.ts │ │ │ │ ├── DineroFactory.ts │ │ │ │ ├── DineroFormatter.ts │ │ │ │ ├── DineroOptions.ts │ │ │ │ ├── DineroRates.ts │ │ │ │ ├── DineroScaledAmount.ts │ │ │ │ ├── DineroSnapshot.ts │ │ │ │ ├── DineroTransformer.ts │ │ │ │ ├── DineroUnaryOperation.ts │ │ │ │ └── index.ts │ │ │ └── utils/ │ │ │ ├── __tests__/ │ │ │ │ ├── absolute.test.ts │ │ │ │ ├── compare.test.ts │ │ │ │ ├── computeBase.test.ts │ │ │ │ ├── countTrailingZeros.test.ts │ │ │ │ ├── distribute.test.ts │ │ │ │ ├── equal.test.ts │ │ │ │ ├── getAmountAndScale.test.ts │ │ │ │ ├── getDivisors.test.ts │ │ │ │ ├── greaterThan.test.ts │ │ │ │ ├── greaterThanOrEqual.test.ts │ │ │ │ ├── isArray.test.ts │ │ │ │ ├── isEven.test.ts │ │ │ │ ├── isHalf.test.ts │ │ │ │ ├── isScaledAmount.test.ts │ │ │ │ ├── lessThan.test.ts │ │ │ │ ├── lessThanOrEqual.test.ts │ │ │ │ ├── maximum.test.ts │ │ │ │ ├── minimum.test.ts │ │ │ │ └── sign.test.ts │ │ │ ├── absolute.ts │ │ │ ├── compare.ts │ │ │ ├── computeBase.ts │ │ │ ├── countTrailingZeros.ts │ │ │ ├── distribute.ts │ │ │ ├── equal.ts │ │ │ ├── getAmountAndScale.ts │ │ │ ├── getDivisors.ts │ │ │ ├── greaterThan.ts │ │ │ ├── greaterThanOrEqual.ts │ │ │ ├── index.ts │ │ │ ├── isArray.ts │ │ │ ├── isEven.ts │ │ │ ├── isHalf.ts │ │ │ ├── isScaledAmount.ts │ │ │ ├── lessThan.ts │ │ │ ├── lessThanOrEqual.ts │ │ │ ├── maximum.ts │ │ │ ├── minimum.ts │ │ │ └── sign.ts │ │ ├── currencies/ │ │ │ ├── index.ts │ │ │ ├── iso4217.ts │ │ │ └── types/ │ │ │ ├── DineroCurrency.ts │ │ │ └── index.ts │ │ ├── dinero.ts │ │ └── index.ts │ ├── tsconfig.json │ └── tsdown.config.ts ├── renovate.json ├── ship.config.cjs ├── skills-lock.json ├── test/ │ └── utils/ │ ├── castToBigintCurrency.ts │ ├── castToBigjsCurrency.ts │ ├── createBigintDinero.ts │ ├── createBigjsDinero.ts │ ├── createNumberDinero.ts │ └── index.ts ├── tsconfig.json ├── turbo.json └── vitest.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .agents/skills/dinero-best-practices/SKILL.md ================================================ --- name: dinero-best-practices description: > Core best practices for the Dinero.js money library. Use when writing, reviewing, or refactoring code that creates Dinero objects, performs arithmetic on monetary values, or handles money in JavaScript/TypeScript. Triggers on imports from 'dinero.js', monetary calculations, or price/cost handling logic. license: MIT metadata: author: dinerojs version: '1.0.0' --- # Dinero.js Best Practices Core rules for working with [Dinero.js](https://v2.dinerojs.com), the JavaScript/TypeScript library for creating, calculating, and formatting money safely. Contains rules across 4 categories, prioritized by impact. ## When to Apply Reference these guidelines when: - Creating Dinero objects from user input, API responses, or database values - Performing arithmetic on monetary values (adding, splitting, multiplying) - Choosing between `number` and `bigint` calculators - Importing from `dinero.js`, `dinero.js/currencies`, or `dinero.js/bigint` - Working with prices, costs, taxes, or any financial calculation ## Rule Categories by Priority | Priority | Category | Impact | Prefix | | -------- | --------------- | -------- | ------------- | | 1 | Object Creation | CRITICAL | `creation-` | | 2 | Arithmetic | CRITICAL | `arithmetic-` | | 3 | Precision | HIGH | `precision-` | | 4 | Imports | MEDIUM | `imports-` | ## Quick Reference ### 1. Object Creation (CRITICAL) - `creation-minor-units` - Always pass amounts as integers in minor currency units - `creation-from-floats` - Use a helper to convert float inputs to minor units - `creation-zero-exponent` - Currencies with exponent 0 (e.g., JPY) take major units directly ### 2. Arithmetic (CRITICAL) - `arithmetic-immutability` - All operations return new objects; capture the return value - `arithmetic-allocate-not-divide` - Use `allocate` for splitting money, not manual division - `arithmetic-scaled-amounts` - Never multiply by a raw decimal; use scaled amounts - `arithmetic-percentages` - Calculate percentages with `allocate` or scaled `multiply` ### 3. Precision (HIGH) - `precision-bigint` - Use `dinero.js/bigint` for amounts exceeding `Number.MAX_SAFE_INTEGER` - `precision-crypto` - Cryptocurrencies require bigint due to high exponents - `precision-trim-scale` - Use `trimScale` to remove trailing zeros after chained operations ### 4. Imports (MEDIUM) - `imports-tree-shaking` - Import only what you use; standalone functions enable tree-shaking - `imports-bigint-currencies` - Match calculator type: use `dinero.js/bigint/currencies` with `dinero.js/bigint` ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/creation-minor-units.md rules/arithmetic-allocate-not-divide.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references ================================================ FILE: .agents/skills/dinero-best-practices/rules/arithmetic-allocate-not-divide.md ================================================ --- title: Use allocate for Splitting Money, Not Manual Division impact: CRITICAL impactDescription: prevents lost cents from rounding tags: arithmetic, allocate, division, remainder --- ## Use allocate for Splitting Money, Not Manual Division When splitting money between parties, use `allocate` instead of dividing manually. `allocate` distributes remainders so no money is lost. **Incorrect (manual division loses money):** ```js import { dinero, multiply } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const total = dinero({ amount: 1003, currency: USD }); // $10.03 // Splitting three ways: 1003 / 3 = 334.33... — where does the extra cent go? const share = multiply(total, { amount: 1, scale: 0 }); // No good way to split evenly ``` **Correct (allocate distributes remainders):** ```js import { dinero, allocate } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const total = dinero({ amount: 1003, currency: USD }); // $10.03 const shares = allocate(total, [1, 1, 1]); // [$3.35, $3.34, $3.34] — extra cent goes to the first share ``` The ratios in `allocate` are relative. `[1, 1, 1]` splits evenly. `[70, 20, 10]` splits 70%/20%/10%. Reference: https://v2.dinerojs.com/api/mutations/allocate ================================================ FILE: .agents/skills/dinero-best-practices/rules/arithmetic-immutability.md ================================================ --- title: Capture Return Values — Dinero Objects Are Immutable impact: CRITICAL impactDescription: prevents silently lost calculations tags: arithmetic, immutability, pure-functions --- ## Capture Return Values — Dinero Objects Are Immutable All Dinero.js operations are pure functions that return new objects. The original objects are never modified. Discarding the return value means losing your calculation. **Incorrect (discarding the return value):** ```js import { dinero, add } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1000, currency: USD }); const tax = dinero({ amount: 100, currency: USD }); add(price, tax); // Return value discarded — price is unchanged ``` **Correct (capturing the result):** ```js import { dinero, add } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1000, currency: USD }); const tax = dinero({ amount: 100, currency: USD }); const total = add(price, tax); // $11.00 ``` This applies to all operations: `add`, `subtract`, `multiply`, `allocate`, `convert`, `trimScale`, `transformScale`, and `normalizeScale`. Reference: https://v2.dinerojs.com/core-concepts/mutations ================================================ FILE: .agents/skills/dinero-best-practices/rules/arithmetic-percentages.md ================================================ --- title: Calculate Percentages with allocate or Scaled multiply impact: HIGH impactDescription: prevents precision loss and thrown errors on percentage calculations tags: arithmetic, percentages, tax, discount --- ## Calculate Percentages with allocate or Scaled multiply There are two safe ways to calculate percentages of a monetary value: `allocate` (for splitting into complementary parts) and `multiply` with a scaled amount (for extracting a percentage). **Incorrect (float percentage):** ```js import { dinero, multiply } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const subtotal = dinero({ amount: 5000, currency: USD }); const tax = multiply(subtotal, 0.15); // Risky: may throw if result is non-integer ``` **Correct (allocate for complementary parts):** ```js import { dinero, allocate } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const subtotal = dinero({ amount: 5000, currency: USD }); const [tax, net] = allocate(subtotal, [15, 85]); // 15% tax, 85% net ``` **Correct (scaled multiply for a single percentage):** ```js import { dinero, multiply } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const subtotal = dinero({ amount: 5000, currency: USD }); const tax = multiply(subtotal, { amount: 15, scale: 2 }); // 15/100 = 15% ``` Use `allocate` when you need both parts (e.g., tax and net) to guarantee they sum to the original. Use `multiply` when you only need one part. Reference: https://v2.dinerojs.com/guides/calculating-percentages ================================================ FILE: .agents/skills/dinero-best-practices/rules/arithmetic-scaled-amounts.md ================================================ --- title: Never Multiply by a Raw Decimal — Use Scaled Amounts impact: CRITICAL impactDescription: prevents throws from non-integer intermediate results tags: arithmetic, multiply, scaled-amounts, decimals --- ## Never Multiply by a Raw Decimal — Use Scaled Amounts Dinero.js uses integer arithmetic. Multiplying by a decimal that produces a non-integer result will throw. Use scaled amounts instead: `{ amount, scale }` represents `amount / (base ^ scale)`. **Incorrect (raw decimal multiplier):** ```js import { dinero, multiply } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1001, currency: USD }); multiply(price, 0.5); // Throws: 1001 * 0.5 = 500.5 (not an integer) ``` **Correct (scaled amount):** ```js import { dinero, multiply } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1001, currency: USD }); multiply(price, { amount: 5, scale: 1 }); // 5/10 = 0.5, result: amount 5005, scale 3 ``` Common scaled amounts: | Decimal | Scaled amount | | ------- | -------------------------- | | 0.5 | `{ amount: 5, scale: 1 }` | | 0.1 | `{ amount: 1, scale: 1 }` | | 0.15 | `{ amount: 15, scale: 2 }` | | 1.5 | `{ amount: 15, scale: 1 }` | Reference: https://v2.dinerojs.com/faq/can-i-multiply-by-a-decimal ================================================ FILE: .agents/skills/dinero-best-practices/rules/creation-from-floats.md ================================================ --- title: Convert Float Inputs to Minor Units with a Helper impact: CRITICAL impactDescription: prevents throws and precision bugs from float inputs tags: creation, floats, conversion, external-input --- ## Convert Float Inputs to Minor Units with a Helper When receiving float values from external sources (APIs, user input, databases), convert them to integer minor units before creating a Dinero object. Never pass floats directly. **Incorrect (passing a float directly):** ```js import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const priceFromApi = 19.99; const d = dinero({ amount: priceFromApi, currency: USD }); // Throws ``` **Correct (converting with a helper):** ```js import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; function dineroFromFloat({ amount: float, currency, scale }) { const factor = currency.base ** (scale ?? currency.exponent); const amount = Math.round(float * factor); return dinero({ amount, currency, scale }); } const priceFromApi = 19.99; const d = dineroFromFloat({ amount: priceFromApi, currency: USD }); // $19.99 ``` `Math.round` is necessary to avoid floating-point artifacts (e.g., `19.99 * 100` evaluates to `1998.9999999999998` in JavaScript). Reference: https://v2.dinerojs.com/guides/creating-from-floats ================================================ FILE: .agents/skills/dinero-best-practices/rules/creation-minor-units.md ================================================ --- title: Always Pass Amounts as Integers in Minor Currency Units impact: CRITICAL impactDescription: prevents silent off-by-100x errors tags: creation, amount, minor-units, cents --- ## Always Pass Amounts as Integers in Minor Currency Units Dinero.js represents money in the smallest subdivision of a currency. For USD (exponent 2), the amount is in cents. Passing a major-unit value silently creates the wrong amount. **Incorrect (passing major units or floats):** ```js import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; // Wrong: 50 cents, not 50 dollars const d = dinero({ amount: 50, currency: USD }); // Wrong: throws on non-integer const d = dinero({ amount: 19.99, currency: USD }); ``` **Correct (minor units as integers):** ```js import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const d1 = dinero({ amount: 5000, currency: USD }); // $50.00 const d2 = dinero({ amount: 1999, currency: USD }); // $19.99 ``` Reference: https://v2.dinerojs.com/core-concepts/amount ================================================ FILE: .agents/skills/dinero-best-practices/rules/creation-zero-exponent.md ================================================ --- title: Currencies with Exponent 0 Take Major Units Directly impact: HIGH impactDescription: prevents wrong amounts for JPY, KRW, and similar currencies tags: creation, exponent, jpy, zero-decimal --- ## Currencies with Exponent 0 Take Major Units Directly Currencies like JPY and KRW have no minor units (exponent 0). The amount you pass is in major units directly. **Incorrect (treating JPY like USD):** ```js import { dinero } from 'dinero.js'; import { JPY } from 'dinero.js/currencies'; // Wrong: this is 500,000 yen, not 5,000 yen const d = dinero({ amount: 500000, currency: JPY }); ``` **Correct (major units for zero-exponent currencies):** ```js import { dinero } from 'dinero.js'; import { JPY } from 'dinero.js/currencies'; // JPY has exponent 0, so 5000 means 5,000 yen const d = dinero({ amount: 5000, currency: JPY }); ``` Check a currency's `exponent` property to determine the expected unit. USD has exponent 2 (cents), BHD has exponent 3 (fils), JPY has exponent 0 (yen). Reference: https://v2.dinerojs.com/core-concepts/amount ================================================ FILE: .agents/skills/dinero-best-practices/rules/imports-bigint-currencies.md ================================================ --- title: Match Calculator Type — Use Matching Currency Imports impact: HIGH impactDescription: prevents TypeError from mixing number and bigint arithmetic tags: imports, bigint, currencies, type-mismatch --- ## Match Calculator Type — Use Matching Currency Imports Currency definitions from `dinero.js/currencies` use `number` for `base` and `exponent`. Currency definitions from `dinero.js/bigint/currencies` use `bigint`. Mixing them throws a `TypeError`. **Incorrect (mixing number currencies with bigint calculator):** ```js import { dinero } from 'dinero.js/bigint'; import { USD } from 'dinero.js/currencies'; // number-typed // TypeError: Cannot mix BigInt and other types const d = dinero({ amount: 500n, currency: USD }); ``` **Correct (matching imports):** ```js // Number calculator + number currencies import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const d = dinero({ amount: 500, currency: USD }); // Bigint calculator + bigint currencies import { dinero } from 'dinero.js/bigint'; import { USD } from 'dinero.js/bigint/currencies'; const d = dinero({ amount: 500n, currency: USD }); ``` Reference: https://v2.dinerojs.com/faq/why-cant-i-use-currencies-with-bigint ================================================ FILE: .agents/skills/dinero-best-practices/rules/imports-tree-shaking.md ================================================ --- title: Import Only What You Use — Standalone Functions Enable Tree-Shaking impact: MEDIUM impactDescription: reduces bundle size by excluding unused operations tags: imports, tree-shaking, bundle-size, functions --- ## Import Only What You Use — Standalone Functions Enable Tree-Shaking Dinero.js exports standalone functions instead of methods on objects. This means bundlers can eliminate unused code. Import only the functions you need. **How it works:** ```js // Only add, toDecimal, and dinero are included in your bundle import { dinero, add, toDecimal } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const total = add( dinero({ amount: 1000, currency: USD }), dinero({ amount: 500, currency: USD }), ); toDecimal(total); // "15.00" ``` Functions like `multiply`, `allocate`, `compare`, `greaterThan`, etc. are not shipped if you don't import them. Note: Dinero.js uses standalone functions, not methods. Write `add(d1, d2)`, not `d1.add(d2)`. Reference: https://v2.dinerojs.com/faq/why-functions-instead-of-methods ================================================ FILE: .agents/skills/dinero-best-practices/rules/precision-bigint.md ================================================ --- title: Use bigint for Amounts Exceeding Number.MAX_SAFE_INTEGER impact: HIGH impactDescription: prevents silent precision loss on large monetary values tags: precision, bigint, large-amounts, safe-integer --- ## Use bigint for Amounts Exceeding Number.MAX_SAFE_INTEGER JavaScript `number` silently loses precision beyond `Number.MAX_SAFE_INTEGER` (9,007,199,254,740,991). For large monetary values, use the bigint entry point. **Incorrect (large amount with number calculator):** ```js import { dinero } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; // 25800000000000000 exceeds safe integer range — silently wrong const d = dinero({ amount: 25800000000000000, currency: USD }); ``` **Correct (bigint calculator):** ```js import { dinero } from 'dinero.js/bigint'; import { USD } from 'dinero.js/bigint/currencies'; const d = dinero({ amount: 25800000000000000n, currency: USD }); ``` Note: `dinero.js/bigint` uses its own currency definitions where `base` and `exponent` are bigint values. Always import currencies from `dinero.js/bigint/currencies` when using the bigint calculator. Reference: https://v2.dinerojs.com/guides/precision-and-large-numbers ================================================ FILE: .agents/skills/dinero-best-practices/rules/precision-crypto.md ================================================ --- title: Cryptocurrencies Require bigint Due to High Exponents impact: HIGH impactDescription: prevents overflow on crypto amounts (e.g., 1 ETH = 10^18 wei) tags: precision, bigint, cryptocurrency, ethereum, bitcoin --- ## Cryptocurrencies Require bigint Due to High Exponents Cryptocurrencies like ETH (exponent 18) and BTC (exponent 8) produce amounts that exceed the safe integer range even for small values. Always use the bigint calculator for crypto. **Incorrect (number calculator for ETH):** ```js import { dinero } from 'dinero.js'; const ETH = { code: 'ETH', base: 10, exponent: 18 }; // 1 ETH = 1000000000000000000 wei — exceeds Number.MAX_SAFE_INTEGER const d = dinero({ amount: 1000000000000000000, currency: ETH }); ``` **Correct (bigint calculator):** ```js import { dinero } from 'dinero.js/bigint'; const ETH = { code: 'ETH', base: 10n, exponent: 18n }; const d = dinero({ amount: 1000000000000000000n, currency: ETH }); ``` Avoid naming files after cryptocurrency ticker codes (e.g., `xbt.js`, `xmr.js`). Ad blockers may flag these file names as crypto mining scripts and block them from loading. Reference: https://v2.dinerojs.com/guides/cryptocurrencies ================================================ FILE: .agents/skills/dinero-best-practices/rules/precision-trim-scale.md ================================================ --- title: Use trimScale to Remove Trailing Zeros After Chained Operations impact: MEDIUM impactDescription: prevents unnecessary scale growth from chained operations tags: precision, scale, trim, chained-operations --- ## Use trimScale to Remove Trailing Zeros After Chained Operations Dinero.js automatically promotes to the highest scale when combining objects with different scales. Over many operations, scale can grow unnecessarily. Use `trimScale` to drop trailing zeros. **Before trimming:** ```js import { dinero, add, trimScale } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const d1 = dinero({ amount: 100, currency: USD }); const d2 = dinero({ amount: 2000000, currency: USD, scale: 6 }); const result = add(d1, d2); // amount: 3000000, scale: 6 ``` **After trimming:** ```js const trimmed = trimScale(result); // amount: 300, scale: 2 ``` This is especially useful before serialization (storing or transporting) where compact representation matters. Reference: https://v2.dinerojs.com/api/conversions/trim-scale ================================================ FILE: .agents/skills/dinero-currency-patterns/SKILL.md ================================================ --- name: dinero-currency-patterns description: > Currency handling patterns for the Dinero.js money library. Use when working with multiple currencies, converting between currencies, defining custom currencies, storing monetary values in databases, or integrating with payment services like Stripe or PayPal. Triggers on currency conversion, database schema design for money, or payment API integration with Dinero objects. license: MIT metadata: author: dinerojs version: '1.0.0' --- # Dinero.js Currency Patterns Patterns for handling currencies with [Dinero.js](https://v2.dinerojs.com): type safety, conversions, custom currencies, database storage, and payment service integration. ## When to Apply Reference these guidelines when: - Converting between currencies with `convert` - Defining custom currencies (e.g., cryptocurrencies, loyalty points) - Looking up currencies dynamically from external input - Storing monetary values in a database - Integrating with payment services (Stripe, PayPal, Square) ## Rule Categories by Priority | Priority | Category | Impact | Prefix | | -------- | ------------------- | ------ | ---------- | | 1 | Type Safety | HIGH | `types-` | | 2 | Conversion | HIGH | `convert-` | | 3 | Storage | HIGH | `storage-` | | 4 | Payment Integration | MEDIUM | `payment-` | ## Quick Reference ### 1. Type Safety (HIGH) - `types-as-const` - Define custom currencies with `as const satisfies` for compile-time safety - `types-currency-mismatch` - TypeScript catches currency mismatches in operations at compile time - `types-lookup-validation` - Validate currency codes from external sources at runtime ### 2. Conversion (HIGH) - `convert-scaled-rates` - Use scaled amounts for fractional exchange rates, not floats - `convert-reusable` - Build reusable converter functions with higher-order patterns ### 3. Storage (HIGH) - `storage-database` - Store amount, currency code, and scale as separate columns - `storage-no-money-type` - Avoid PostgreSQL's `money` type for multi-currency applications ### 4. Payment Integration (MEDIUM) - `payment-services` - Map Dinero objects to payment service formats with dedicated helpers ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/types-as-const.md rules/convert-scaled-rates.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/convert-reusable.md ================================================ --- title: Build Reusable Converter Functions impact: MEDIUM impactDescription: eliminates repeated rate passing across conversion call sites tags: convert, reusable, higher-order, pattern --- ## Build Reusable Converter Functions When converting many objects with the same rates, wrap `convert` in a higher-order function to avoid passing rates every time. **Correct (reusable converter):** ```js import { dinero, convert } from 'dinero.js'; import { USD, EUR } from 'dinero.js/currencies'; function createConverter(rates) { return function converter(dineroObject, newCurrency) { return convert(dineroObject, newCurrency, rates); }; } const rates = { EUR: { amount: 89, scale: 2 } }; const convertWithRates = createConverter(rates); const price = dinero({ amount: 500, currency: USD }); convertWithRates(price, EUR); // Dinero object in EUR ``` This pattern works well when rates are fetched once per request or session and reused across multiple conversions. Reference: https://v2.dinerojs.com/api/conversions/convert ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/convert-scaled-rates.md ================================================ --- title: Use Scaled Amounts for Fractional Exchange Rates, Not Floats impact: HIGH impactDescription: prevents precision loss from floating-point exchange rates tags: convert, rates, scaled-amounts, exchange-rate --- ## Use Scaled Amounts for Fractional Exchange Rates, Not Floats When converting between currencies, fractional exchange rates should be passed as scaled amounts (`{ amount, scale }`) instead of floats. This avoids floating-point precision issues. **Incorrect (float rate):** ```js import { dinero, convert } from 'dinero.js'; import { USD, EUR } from 'dinero.js/currencies'; const rates = { EUR: 0.89 }; // Float — imprecise const d = dinero({ amount: 500, currency: USD }); convert(d, EUR, rates); ``` **Correct (scaled rate):** ```js import { dinero, convert } from 'dinero.js'; import { USD, EUR } from 'dinero.js/currencies'; const rates = { EUR: { amount: 89, scale: 2 } }; // 89/100 = 0.89 — precise const d = dinero({ amount: 500, currency: USD }); convert(d, EUR, rates); // Dinero object with amount 44500, scale 4 ``` Integer rates can be passed directly without scaling: ```js const rates = { IQD: 1199 }; // 1 USD = 1199 IQD (integer, no scaling needed) convert(d, IQD, rates); ``` Reference: https://v2.dinerojs.com/api/conversions/convert ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/payment-services.md ================================================ --- title: Map Dinero Objects to Payment Service Formats with Dedicated Helpers impact: MEDIUM impactDescription: prevents payment API errors from wrong amount/currency formats tags: payment, stripe, paypal, square, integration --- ## Map Dinero Objects to Payment Service Formats with Dedicated Helpers Each payment service expects a different money format. Build dedicated helper functions to convert Dinero objects to the right shape. **Stripe (minor units, lowercase currency):** ```js import { toSnapshot } from 'dinero.js'; function toStripeMoney(dineroObject) { const { amount, currency } = toSnapshot(dineroObject); return { amount, currency: currency.code.toLowerCase(), }; } // { amount: 1999, currency: 'usd' } ``` **PayPal (decimal string, uppercase currency):** ```js import { toSnapshot, toDecimal } from 'dinero.js'; function toPaypalMoney(dineroObject) { const { currency } = toSnapshot(dineroObject); return { value: toDecimal(dineroObject), currency_code: currency.code, }; } // { value: '19.99', currency_code: 'USD' } ``` **Square (BigInt amount, uppercase currency):** ```js import { toSnapshot } from 'dinero.js'; function toSquareMoney(dineroObject) { const { amount, currency } = toSnapshot(dineroObject); return { amount: BigInt(amount), currency: currency.code, }; } // { amount: 1999n, currency: 'USD' } ``` Keep these helpers in a single module (e.g., `lib/money.js`) so format changes only need updating in one place. Reference: https://v2.dinerojs.com/guides/integrating-with-payment-services ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/storage-database.md ================================================ --- title: Store Amount, Currency Code, and Scale as Separate Columns impact: HIGH impactDescription: prevents data loss and enables correct restoration of Dinero objects tags: storage, database, schema, columns, restoration --- ## Store Amount, Currency Code, and Scale as Separate Columns Store each component of a Dinero object separately. This is portable across databases and preserves all information needed for reconstruction. **Correct (SQL schema):** ```sql CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, price_amount BIGINT NOT NULL, price_currency VARCHAR(3) NOT NULL, price_scale INTEGER NOT NULL DEFAULT 2 ); ``` **Correct (restoration from database row):** ```js import { dinero } from 'dinero.js'; import { getCurrency } from './currencies'; // Your validation helper function dineroFromRow(row) { const currency = getCurrency(row.price_currency); return dinero({ amount: row.price_amount, currency, scale: row.price_scale, }); } ``` Always store the `scale`, not just the currency exponent. If a Dinero object was created with a custom scale (e.g., from a `multiply` with a scaled amount), restoring with just the exponent produces the wrong value. Reference: https://v2.dinerojs.com/guides/storing-in-a-database ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/storage-no-money-type.md ================================================ --- title: Avoid PostgreSQL's money Type for Multi-Currency Applications impact: HIGH impactDescription: prevents locale-dependent bugs and precision loss tags: storage, database, postgresql, money-type --- ## Avoid PostgreSQL's money Type for Multi-Currency Applications PostgreSQL's `money` type has no currency information, is locale-dependent, and has fixed 2-decimal precision. It fails for currencies with 0 decimals (JPY), 3 decimals (BHD), or multi-currency support. **Incorrect (PostgreSQL money type):** ```sql CREATE TABLE products ( id SERIAL PRIMARY KEY, price MONEY NOT NULL -- No currency info, locale-dependent, fixed precision ); ``` **Correct (separate columns):** ```sql CREATE TABLE products ( id SERIAL PRIMARY KEY, price_amount BIGINT NOT NULL, price_currency VARCHAR(3) NOT NULL, price_scale INTEGER NOT NULL DEFAULT 2 ); ``` The same advice applies to other database-specific money types. Use standard integer and string columns for maximum portability and control. Reference: https://v2.dinerojs.com/guides/storing-in-a-database ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/types-as-const.md ================================================ --- title: Define Custom Currencies with as const satisfies for Type Safety impact: HIGH impactDescription: enables compile-time currency mismatch detection tags: types, currency, typescript, as-const, custom-currency --- ## Define Custom Currencies with as const satisfies for Type Safety When defining custom currencies (e.g., cryptocurrencies, loyalty points), use `as const satisfies` to get a literal type for the `code` property. Without it, TypeScript infers `string`, losing compile-time currency mismatch detection. **Incorrect (code inferred as string):** ```ts import type { Currency } from 'dinero.js'; const BTC = { code: 'BTC', base: 10, exponent: 8 }; // typeof BTC.code is string, not 'BTC' ``` **Correct (literal type with as const satisfies):** ```ts import type { Currency } from 'dinero.js'; const BTC = { code: 'BTC', base: 10, exponent: 8, } as const satisfies Currency; // typeof BTC.code is 'BTC' ``` With literal types, TypeScript catches mistakes like adding BTC and USD at compile time: ```ts const btcAmount = dinero({ amount: 100000000, currency: BTC }); const usdAmount = dinero({ amount: 500, currency: USD }); add(btcAmount, usdAmount); // Type error: 'USD' is not assignable to 'BTC' ``` All built-in currencies from `dinero.js/currencies` already have literal types. Reference: https://v2.dinerojs.com/guides/currency-type-safety ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/types-currency-mismatch.md ================================================ --- title: TypeScript Catches Currency Mismatches at Compile Time impact: HIGH impactDescription: prevents adding, subtracting, or comparing different currencies tags: types, currency, mismatch, compile-time, typescript --- ## TypeScript Catches Currency Mismatches at Compile Time When using typed currencies, TypeScript prevents operations between different currencies. This catches bugs that would otherwise only fail at runtime. **Caught at compile time:** ```ts import { dinero, add, subtract, equal } from 'dinero.js'; import { USD, EUR } from 'dinero.js/currencies'; const price = dinero({ amount: 500, currency: USD }); // Dinero const tax = dinero({ amount: 100, currency: EUR }); // Dinero add(price, tax); // Type error: 'EUR' is not assignable to 'USD' subtract(price, tax); // Type error equal(price, tax); // Type error ``` **Currency type preserved through operations:** ```ts import { dinero, multiply, convert } from 'dinero.js'; import { USD, EUR } from 'dinero.js/currencies'; const price = dinero({ amount: 500, currency: USD }); const doubled = multiply(price, 2); // Dinero — preserved const converted = convert(price, EUR, rates); // Dinero — changed add(doubled, converted); // Type error: 'EUR' is not assignable to 'USD' ``` Unary operations (`multiply`, `allocate`, `trimScale`) preserve the currency type. `convert` changes it to the target currency. Reference: https://v2.dinerojs.com/guides/currency-type-safety ================================================ FILE: .agents/skills/dinero-currency-patterns/rules/types-lookup-validation.md ================================================ --- title: Validate Currency Codes from External Sources at Runtime impact: HIGH impactDescription: prevents undefined currency errors from invalid or unknown codes tags: types, validation, runtime, lookup, external-input --- ## Validate Currency Codes from External Sources at Runtime Currency codes from APIs, databases, or user input may be invalid. Always validate before looking up a currency definition. **Incorrect (direct lookup without validation):** ```ts import * as currencies from 'dinero.js/currencies'; function createPrice(amount: number, code: string) { const currency = currencies[code]; // May be undefined return dinero({ amount, currency }); // Runtime error } ``` **Correct (validate before lookup):** ```ts import { dinero } from 'dinero.js'; import * as currencies from 'dinero.js/currencies'; function getCurrency(code: string) { if (!(code in currencies)) { throw new Error(`Unknown currency code: ${code}`); } return currencies[code as keyof typeof currencies]; } function createPrice(amount: number, code: string) { const currency = getCurrency(code); return dinero({ amount, currency }); } ``` Currency codes may also change between Dinero.js versions, as the library tracks ISO 4217 amendments. Pin your package version if you need stability. Reference: https://v2.dinerojs.com/faq/how-to-look-up-a-currency-by-code ================================================ FILE: .agents/skills/dinero-formatting/SKILL.md ================================================ --- name: dinero-formatting description: > Formatting patterns for the Dinero.js money library. Use when displaying monetary values to users, formatting prices with currency symbols, handling locale-specific number formats, or working with non-decimal currencies. Triggers on toDecimal, toUnits, toSnapshot calls, or Intl.NumberFormat usage with Dinero objects. license: MIT metadata: author: dinerojs version: '1.0.0' --- # Dinero.js Formatting Patterns for formatting [Dinero.js](https://v2.dinerojs.com) monetary values for display. Covers currency symbols, locale-aware formatting, non-decimal currencies, and serialization. ## When to Apply Reference these guidelines when: - Displaying prices, totals, or monetary values in a UI - Adding currency symbols or locale-specific formatting - Formatting non-decimal currencies (e.g., historical currencies with non-base-10 subdivisions) - Serializing Dinero objects for APIs, databases, or transport - Building reusable formatting utilities ## Rule Categories by Priority | Priority | Category | Impact | Prefix | | -------- | ------------- | -------- | ---------------- | | 1 | Display | CRITICAL | `display-` | | 2 | Locale | HIGH | `locale-` | | 3 | Serialization | HIGH | `serialization-` | | 4 | Non-Decimal | MEDIUM | `nondecimal-` | ## Quick Reference ### 1. Display (CRITICAL) - `display-to-decimal` - Use `toDecimal` for display strings, not `toSnapshot` - `display-no-currency-symbols` - Dinero.js does not format currency symbols; compose with `Intl.NumberFormat` ### 2. Locale (HIGH) - `locale-intl-formatter` - Build reusable formatters with `Intl.NumberFormat` - `locale-multilingual` - Create locale-parameterized formatters for multilingual sites ### 3. Serialization (HIGH) - `serialization-snapshot` - Use `toSnapshot` for transport and storage, not display - `serialization-bigint-json` - BigInt Dinero objects require a custom JSON replacer ### 4. Non-Decimal (MEDIUM) - `nondecimal-to-units` - Use `toUnits` for non-decimal currencies, not `toDecimal` ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/display-no-currency-symbols.md rules/locale-intl-formatter.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references ================================================ FILE: .agents/skills/dinero-formatting/rules/display-no-currency-symbols.md ================================================ --- title: Dinero.js Does Not Format Currency Symbols — Compose with Intl.NumberFormat impact: CRITICAL impactDescription: prevents missing currency symbols in UI tags: display, currency-symbols, intl, numberformat --- ## Dinero.js Does Not Format Currency Symbols — Compose with Intl.NumberFormat `toDecimal` returns a plain decimal string like `"19.99"`, not `"$19.99"`. This is by design: currency formatting varies by locale (`$19.99` in en-US, `19,99 $US` in fr-CA, `19,99 $` in fr-FR). Use `toDecimal` with a transformer to add locale-aware formatting. **Incorrect (expecting currency symbols from toDecimal):** ```js import { dinero, toDecimal } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1999, currency: USD }); toDecimal(price); // "19.99" — no currency symbol ``` **Correct (composing with Intl.NumberFormat):** ```js import { dinero, toDecimal } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1999, currency: USD }); toDecimal(price, ({ value, currency }) => { return Number(value).toLocaleString('en-US', { style: 'currency', currency: currency.code, }); }); // "$19.99" ``` Reference: https://v2.dinerojs.com/faq/why-no-currency-formatting ================================================ FILE: .agents/skills/dinero-formatting/rules/display-to-decimal.md ================================================ --- title: Use toDecimal for Display Strings, Not toSnapshot impact: CRITICAL impactDescription: prevents displaying raw minor-unit amounts to users tags: display, toDecimal, toSnapshot, formatting --- ## Use toDecimal for Display Strings, Not toSnapshot `toDecimal` returns a human-readable decimal string (e.g., `"19.99"`). `toSnapshot` returns the raw internal representation in minor units (e.g., `{ amount: 1999, ... }`). Use the right one for the right job. **Incorrect (using toSnapshot for display):** ```js import { dinero, toSnapshot } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1999, currency: USD }); const { amount } = toSnapshot(price); display.textContent = `$${amount}`; // Shows "$1999" — wrong ``` **Correct (using toDecimal for display):** ```js import { dinero, toDecimal } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1999, currency: USD }); toDecimal(price); // "19.99" ``` `toSnapshot` is for serialization (APIs, databases, transport). `toDecimal` is for rendering to users. Reference: https://v2.dinerojs.com/core-concepts/formatting ================================================ FILE: .agents/skills/dinero-formatting/rules/locale-intl-formatter.md ================================================ --- title: Build Reusable Formatters with Intl.NumberFormat impact: HIGH impactDescription: eliminates duplicated formatting logic across the codebase tags: locale, formatter, intl, reusable, higher-order --- ## Build Reusable Formatters with Intl.NumberFormat Instead of inlining `Intl.NumberFormat` at every call site, build a reusable formatter function. **Correct (reusable formatter):** ```js import { toDecimal } from 'dinero.js'; function intlFormat(dineroObject, locale, options = {}) { function transformer({ value, currency }) { return Number(value).toLocaleString(locale, { ...options, style: 'currency', currency: currency.code, }); } return toDecimal(dineroObject, transformer); } intlFormat(price, 'en-US'); // "$19.99" intlFormat(price, 'fr-FR'); // "19,99 $US" intlFormat(price, 'ja-JP'); // "$19.99" ``` You can also create a higher-order function that bakes in the locale: ```js function createFormatter(locale, options = {}) { return function format(dineroObject) { return intlFormat(dineroObject, locale, options); }; } const formatUSD = createFormatter('en-US'); formatUSD(price); // "$19.99" ``` Reference: https://v2.dinerojs.com/guides/formatting-in-a-multilingual-site ================================================ FILE: .agents/skills/dinero-formatting/rules/locale-multilingual.md ================================================ --- title: Parameterize Locale for Multilingual Sites impact: HIGH impactDescription: prevents hardcoded locale strings in formatting logic tags: locale, multilingual, i18n, internationalization --- ## Parameterize Locale for Multilingual Sites In multilingual applications, pass the locale as a parameter rather than hardcoding it. This lets you format the same Dinero object differently depending on the user's language. **Incorrect (hardcoded locale):** ```js function formatPrice(dineroObject) { return toDecimal(dineroObject, ({ value, currency }) => { return Number(value).toLocaleString('en-US', { style: 'currency', currency: currency.code, }); }); } ``` **Correct (locale as parameter):** ```js function formatPrice(dineroObject, locale) { return toDecimal(dineroObject, ({ value, currency }) => { return Number(value).toLocaleString(locale, { style: 'currency', currency: currency.code, }); }); } formatPrice(price, 'en-US'); // "$19.99" formatPrice(price, 'de-DE'); // "19,99 $" formatPrice(price, 'ja-JP'); // "$19.99" ``` In React, you can get the locale from your i18n context (e.g., `next-intl`, `react-intl`, `react-i18next`) and pass it to your formatter. Reference: https://v2.dinerojs.com/guides/formatting-in-a-multilingual-site ================================================ FILE: .agents/skills/dinero-formatting/rules/nondecimal-to-units.md ================================================ --- title: Use toUnits for Non-Decimal Currencies, Not toDecimal impact: MEDIUM impactDescription: prevents wrong output for currencies with non-base-10 subdivisions tags: nondecimal, toUnits, formatting, historical-currencies --- ## Use toUnits for Non-Decimal Currencies, Not toDecimal `toDecimal` assumes a decimal (base 10) currency. For currencies with non-decimal subdivisions (e.g., base 6, base 12, or multi-base like pre-decimal GBP), use `toUnits`. **Incorrect (toDecimal on non-decimal currency):** ```js import { dinero, toDecimal } from 'dinero.js'; const GRD = { code: 'GRD', base: 6, exponent: 1 }; const d = dinero({ amount: 9, currency: GRD }); toDecimal(d); // Throws or produces meaningless output ``` **Correct (toUnits with a custom transformer):** ```js import { dinero, toUnits } from 'dinero.js'; const GRD = { code: 'GRD', base: 6, exponent: 1 }; const d = dinero({ amount: 9, currency: GRD }); const labels = ['drachma', 'obol']; toUnits(d, ({ value }) => value .filter((v) => v > 0) .map((v, i) => `${v} ${labels[i]}`) .join(', '), ); // "1 drachma, 3 obols" ``` `toUnits` returns an array of unit values, one per subdivision level. It works with any base, including array bases like `[20, 12]` for pre-decimal GBP (pounds, shillings, pence). Reference: https://v2.dinerojs.com/guides/formatting-non-decimal-currencies ================================================ FILE: .agents/skills/dinero-formatting/rules/serialization-bigint-json.md ================================================ --- title: BigInt Dinero Objects Require a Custom JSON Replacer impact: HIGH impactDescription: prevents TypeError when serializing bigint Dinero objects tags: serialization, bigint, json, stringify, replacer --- ## BigInt Dinero Objects Require a Custom JSON Replacer `JSON.stringify` throws a `TypeError` on bigint values. When using the bigint calculator, provide a custom replacer. **Incorrect (stringify without replacer):** ```js import { dinero, toSnapshot } from 'dinero.js/bigint'; import { USD } from 'dinero.js/bigint/currencies'; const price = dinero({ amount: 500n, currency: USD }); JSON.stringify(toSnapshot(price)); // TypeError: Do not know how to serialize a BigInt ``` **Correct (with replacer):** ```js import { dinero, toSnapshot } from 'dinero.js/bigint'; import { USD } from 'dinero.js/bigint/currencies'; const price = dinero({ amount: 500n, currency: USD }); JSON.stringify(toSnapshot(price), (key, value) => { if (typeof value === 'bigint') { return String(value); } return value; }); ``` When restoring, convert string values back to bigint: ```js const data = JSON.parse(json, (key, value) => { if (typeof value === 'string' && /^\d+$/.test(value)) { return BigInt(value); } return value; }); const restored = dinero(data.price); ``` Reference: https://v2.dinerojs.com/guides/transporting-and-restoring ================================================ FILE: .agents/skills/dinero-formatting/rules/serialization-snapshot.md ================================================ --- title: Use toSnapshot for Transport and Storage, Not Display impact: HIGH impactDescription: prevents data loss from using display-oriented functions for serialization tags: serialization, toSnapshot, transport, storage, json --- ## Use toSnapshot for Transport and Storage, Not Display `toSnapshot` returns the full internal representation as a plain object, suitable for JSON serialization. Use it for APIs, databases, and transport. Use `toDecimal` for user-facing display. **Correct (serialization with toSnapshot):** ```js import { dinero, toSnapshot } from 'dinero.js'; import { USD } from 'dinero.js/currencies'; const price = dinero({ amount: 1999, currency: USD }); const snapshot = toSnapshot(price); // { amount: 1999, currency: { code: 'USD', base: 10, exponent: 2 }, scale: 2 } // Send to API await fetch('/api/products', { method: 'POST', body: JSON.stringify({ price: snapshot }), }); ``` **Correct (restoring from snapshot):** ```js import { dinero } from 'dinero.js'; // The snapshot can be passed directly to dinero() const restored = dinero(data.price); ``` Snapshots are plain objects with no methods, making them safe for `JSON.stringify`, database columns, and cross-service communication. Reference: https://v2.dinerojs.com/guides/transporting-and-restoring ================================================ FILE: .agents/skills/tsdown/SKILL.md ================================================ --- name: tsdown description: Bundle TypeScript and JavaScript libraries with blazing-fast speed powered by Rolldown. Use when building libraries, generating type declarations, bundling for multiple formats, or migrating from tsup. --- # tsdown - The Elegant Library Bundler Blazing-fast bundler for TypeScript/JavaScript libraries powered by Rolldown and Oxc. ## When to Use - Building TypeScript/JavaScript libraries for npm - Generating TypeScript declaration files (.d.ts) - Bundling for multiple formats (ESM, CJS, IIFE, UMD) - Optimizing bundles with tree shaking and minification - Migrating from tsup with minimal changes - Building React, Vue, Solid, or Svelte component libraries ## Quick Start ```bash # Install pnpm add -D tsdown # Basic usage npx tsdown # With config file npx tsdown --config tsdown.config.ts # Watch mode npx tsdown --watch # Migrate from tsup npx tsdown-migrate ``` ## Basic Configuration ```ts import { defineConfig } from 'tsdown' export default defineConfig({ entry: ['./src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, }) ``` ## Core References | Topic | Description | Reference | |-------|-------------|-----------| | Getting Started | Installation, first bundle, CLI basics | [guide-getting-started](references/guide-getting-started.md) | | Configuration File | Config file formats, multiple configs, workspace | [option-config-file](references/option-config-file.md) | | CLI Reference | All CLI commands and options | [reference-cli](references/reference-cli.md) | | Migrate from tsup | Migration guide and compatibility notes | [guide-migrate-from-tsup](references/guide-migrate-from-tsup.md) | | Plugins | Rolldown, Rollup, Unplugin support | [advanced-plugins](references/advanced-plugins.md) | | Hooks | Lifecycle hooks for custom logic | [advanced-hooks](references/advanced-hooks.md) | | Programmatic API | Build from Node.js scripts | [advanced-programmatic](references/advanced-programmatic.md) | | Rolldown Options | Pass options directly to Rolldown | [advanced-rolldown-options](references/advanced-rolldown-options.md) | | CI Environment | CI detection, `'ci-only'` / `'local-only'` values | [advanced-ci](references/advanced-ci.md) | ## Build Options | Option | Usage | Reference | |--------|-------|-----------| | Entry points | `entry: ['src/*.ts', '!**/*.test.ts']` | [option-entry](references/option-entry.md) | | Output formats | `format: ['esm', 'cjs', 'iife', 'umd']` | [option-output-format](references/option-output-format.md) | | Output directory | `outDir: 'dist'`, `outExtensions` | [option-output-directory](references/option-output-directory.md) | | Type declarations | `dts: true`, `dts: { sourcemap, compilerOptions, vue }` | [option-dts](references/option-dts.md) | | Target environment | `target: 'es2020'`, `target: 'esnext'` | [option-target](references/option-target.md) | | Platform | `platform: 'node'`, `platform: 'browser'` | [option-platform](references/option-platform.md) | | Tree shaking | `treeshake: true`, custom options | [option-tree-shaking](references/option-tree-shaking.md) | | Minification | `minify: true`, `minify: 'dce-only'` | [option-minification](references/option-minification.md) | | Source maps | `sourcemap: true`, `'inline'`, `'hidden'` | [option-sourcemap](references/option-sourcemap.md) | | Watch mode | `watch: true`, watch options | [option-watch-mode](references/option-watch-mode.md) | | Cleaning | `clean: true`, clean patterns | [option-cleaning](references/option-cleaning.md) | | Log level | `logLevel: 'silent'`, `failOnWarn: 'ci-only'` | [option-log-level](references/option-log-level.md) | ## Dependency Handling | Feature | Usage | Reference | |---------|-------|-----------| | External deps | `external: ['react', /^@myorg\//]` | [option-dependencies](references/option-dependencies.md) | | Inline deps | `noExternal: ['dep-to-bundle']` | [option-dependencies](references/option-dependencies.md) | | Auto external | Automatic peer/dependency externalization | [option-dependencies](references/option-dependencies.md) | ## Output Enhancement | Feature | Usage | Reference | |---------|-------|-----------| | Shims | `shims: true` - Add ESM/CJS compatibility | [option-shims](references/option-shims.md) | | CJS default | `cjsDefault: true` (default) / `false` | [option-cjs-default](references/option-cjs-default.md) | | Package exports | `exports: true` - Auto-generate exports field | [option-package-exports](references/option-package-exports.md) | | CSS handling | **[experimental]** Still in development | [option-css](references/option-css.md) | | Unbundle mode | `unbundle: true` - Preserve directory structure | [option-unbundle](references/option-unbundle.md) | | Package validation | `publint: true`, `attw: true` - Validate package | [option-lint](references/option-lint.md) | ## Framework & Runtime Support | Framework | Guide | Reference | |-----------|-------|-----------| | React | JSX transform, Fast Refresh | [recipe-react](references/recipe-react.md) | | Vue | SFC support, JSX | [recipe-vue](references/recipe-vue.md) | | WASM | WebAssembly modules via `rolldown-plugin-wasm` | [recipe-wasm](references/recipe-wasm.md) | ## Common Patterns ### Basic Library Bundle ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, }) ``` ### Multiple Entry Points ```ts export default defineConfig({ entry: { index: 'src/index.ts', utils: 'src/utils.ts', cli: 'src/cli.ts', }, format: ['esm', 'cjs'], dts: true, }) ``` ### Browser Library (IIFE/UMD) ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['iife'], globalName: 'MyLib', platform: 'browser', minify: true, }) ``` ### React Component Library ```ts export default defineConfig({ entry: ['src/index.tsx'], format: ['esm', 'cjs'], dts: true, external: ['react', 'react-dom'], plugins: [ // React Fast Refresh support ], }) ``` ### Preserve Directory Structure ```ts export default defineConfig({ entry: ['src/**/*.ts', '!**/*.test.ts'], unbundle: true, // Preserve file structure format: ['esm'], dts: true, }) ``` ### CI-Aware Configuration ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, failOnWarn: 'ci-only', publint: 'ci-only', attw: 'ci-only', }) ``` ### WASM Support ```ts import { wasm } from 'rolldown-plugin-wasm' import { defineConfig } from 'tsdown' export default defineConfig({ entry: ['src/index.ts'], plugins: [wasm()], }) ``` ### Advanced with Hooks ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, hooks: { 'build:before': async (context) => { console.log('Building...') }, 'build:done': async (context) => { console.log('Build complete!') }, }, }) ``` ## Configuration Features ### Multiple Configs Export an array for multiple build configurations: ```ts export default defineConfig([ { entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }, { entry: ['src/cli.ts'], format: ['esm'], platform: 'node', }, ]) ``` ### Conditional Config Use functions for dynamic configuration: ```ts export default defineConfig((options) => { const isDev = options.watch return { entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: !isDev, sourcemap: isDev, } }) ``` ### Workspace/Monorepo Use glob patterns to build multiple packages: ```ts export default defineConfig({ workspace: 'packages/*', entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) ``` ## CLI Quick Reference ```bash # Basic commands tsdown # Build once tsdown --watch # Watch mode tsdown --config custom.ts # Custom config npx tsdown-migrate # Migrate from tsup # Output options tsdown --format esm,cjs # Multiple formats tsdown --outDir lib # Custom output directory tsdown --minify # Enable minification tsdown --dts # Generate declarations # Entry options tsdown src/index.ts # Single entry tsdown src/*.ts # Glob patterns tsdown src/a.ts src/b.ts # Multiple entries # Development tsdown --watch # Watch mode tsdown --sourcemap # Generate source maps tsdown --clean # Clean output directory ``` ## Best Practices 1. **Always generate type declarations** for TypeScript libraries: ```ts { dts: true } ``` 2. **Externalize dependencies** to avoid bundling unnecessary code: ```ts { external: [/^react/, /^@myorg\//] } ``` 3. **Use tree shaking** for optimal bundle size: ```ts { treeshake: true } ``` 4. **Enable minification** for production builds: ```ts { minify: true } ``` 5. **Add shims** for better ESM/CJS compatibility: ```ts { shims: true } // Adds __dirname, __filename, etc. ``` 6. **Auto-generate package.json exports**: ```ts { exports: true } // Creates proper exports field ``` 7. **Use watch mode** during development: ```bash tsdown --watch ``` 8. **Preserve structure** for utilities with many files: ```ts { unbundle: true } // Keep directory structure ``` 9. **Validate packages** in CI before publishing: ```ts { publint: 'ci-only', attw: 'ci-only' } ``` ## Resources - Documentation: https://tsdown.dev - GitHub: https://github.com/rolldown/tsdown - Rolldown: https://rolldown.rs - Migration Guide: https://tsdown.dev/guide/migrate-from-tsup ================================================ FILE: .agents/skills/tsdown/references/advanced-ci.md ================================================ # CI Environment Support Automatically detect CI environments and toggle features based on local vs CI builds. ## Overview tsdown uses the [`is-in-ci`](https://www.npmjs.com/package/is-in-ci) package to detect CI environments. This covers GitHub Actions, GitLab CI, Jenkins, CircleCI, Travis CI, and more. ## CI-Aware Values Several options accept CI-aware string values: | Value | Behavior | |-------|----------| | `true` | Always enabled | | `false` | Always disabled | | `'ci-only'` | Enabled only in CI, disabled locally | | `'local-only'` | Enabled only locally, disabled in CI | ## Supported Options These options accept CI-aware values: - `dts` - TypeScript declaration file generation - `publint` - Package lint validation - `attw` - "Are the types wrong" validation - `report` - Bundle size reporting - `exports` - Auto-generate `package.json` exports - `unused` - Unused dependency check - `devtools` - DevTools integration - `failOnWarn` - Fail on warnings (defaults to `'ci-only'`) ## Usage ### String Form ```ts export default defineConfig({ dts: 'local-only', // Skip DTS in CI for faster builds publint: 'ci-only', // Only run publint in CI failOnWarn: 'ci-only', // Fail on warnings in CI only (default) }) ``` ### Object Form When an option takes a configuration object, set `enabled` to a CI-aware value: ```ts export default defineConfig({ publint: { enabled: 'ci-only', level: 'error', }, attw: { enabled: 'ci-only', profile: 'node16', }, }) ``` ### Config Function The config function receives a `ci` boolean in its context: ```ts export default defineConfig((_, { ci }) => ({ minify: ci, sourcemap: !ci, })) ``` ## Typical CI Configuration ```ts export default defineConfig({ entry: 'src/index.ts', format: ['esm', 'cjs'], dts: true, failOnWarn: 'ci-only', publint: 'ci-only', attw: 'ci-only', }) ``` ## Related Options - [Package Validation](option-lint.md) - publint and attw configuration - [Log Level](option-log-level.md) - `failOnWarn` option details ================================================ FILE: .agents/skills/tsdown/references/advanced-hooks.md ================================================ # Lifecycle Hooks Extend the build process with lifecycle hooks. ## Overview Hooks provide a way to inject custom logic at specific stages of the build lifecycle. Inspired by [unbuild](https://github.com/unjs/unbuild). **Recommendation:** Use [plugins](advanced-plugins.md) for most extensions. Use hooks for simple custom tasks or Rolldown plugin injection. ## Usage Patterns ### Object Syntax ```ts export default defineConfig({ entry: ['src/index.ts'], hooks: { 'build:prepare': async (context) => { console.log('Build starting...') }, 'build:done': async (context) => { console.log('Build complete!') }, }, }) ``` ### Function Syntax ```ts export default defineConfig({ entry: ['src/index.ts'], hooks(hooks) { hooks.hook('build:prepare', () => { console.log('Preparing build...') }) hooks.hook('build:before', (context) => { console.log(`Building format: ${context.format}`) }) }, }) ``` ## Available Hooks ### `build:prepare` Called before the build process starts. **When:** Once per build session **Context:** ```ts { options: ResolvedConfig, hooks: Hookable } ``` **Use cases:** - Setup tasks - Validation - Environment preparation **Example:** ```ts hooks: { 'build:prepare': async (context) => { console.log('Starting build for:', context.options.entry) await cleanOldFiles() }, } ``` ### `build:before` Called before each Rolldown build. **When:** Once per format (ESM, CJS, etc.) **Context:** ```ts { options: ResolvedConfig, buildOptions: BuildOptions, hooks: Hookable } ``` **Use cases:** - Modify build options per format - Inject plugins dynamically - Format-specific setup **Example:** ```ts hooks: { 'build:before': async (context) => { console.log(`Building ${context.buildOptions.format} format...`) // Add format-specific plugin if (context.buildOptions.format === 'iife') { context.buildOptions.plugins.push(browserPlugin()) } }, } ``` ### `build:done` Called after the build completes. **When:** Once per build session **Context:** ```ts { options: ResolvedConfig, chunks: RolldownChunk[], hooks: Hookable } ``` **Use cases:** - Post-processing - Asset copying - Notifications - Deployment **Example:** ```ts hooks: { 'build:done': async (context) => { console.log(`Built ${context.chunks.length} chunks`) // Copy additional files await copyAssets() // Send notification notifyBuildComplete() }, } ``` ## Common Patterns ### Build Notifications ```ts export default defineConfig({ hooks: { 'build:prepare': () => { console.log('🚀 Starting build...') }, 'build:done': (context) => { const size = context.chunks.reduce((sum, c) => sum + c.code.length, 0) console.log(`✅ Build complete! Total size: ${size} bytes`) }, }, }) ``` ### Conditional Plugin Injection ```ts export default defineConfig({ hooks(hooks) { hooks.hook('build:before', (context) => { // Add minification only for production if (process.env.NODE_ENV === 'production') { context.buildOptions.plugins.push(minifyPlugin()) } }) }, }) ``` ### Custom File Copy ```ts import { copyFile } from 'fs/promises' export default defineConfig({ hooks: { 'build:done': async (context) => { // Copy README to dist await copyFile('README.md', `${context.options.outDir}/README.md`) }, }, }) ``` ### Build Metrics ```ts export default defineConfig({ hooks: { 'build:prepare': (context) => { context.startTime = Date.now() }, 'build:done': (context) => { const duration = Date.now() - context.startTime console.log(`Build took ${duration}ms`) // Log chunk sizes context.chunks.forEach((chunk) => { console.log(`${chunk.fileName}: ${chunk.code.length} bytes`) }) }, }, }) ``` ### Format-Specific Logic ```ts export default defineConfig({ format: ['esm', 'cjs', 'iife'], hooks: { 'build:before': (context) => { const format = context.buildOptions.format if (format === 'iife') { // Browser-specific setup context.buildOptions.globalName = 'MyLib' } else if (format === 'cjs') { // Node-specific setup context.buildOptions.platform = 'node' } }, }, }) ``` ### Deployment Hook ```ts export default defineConfig({ hooks: { 'build:done': async (context) => { if (process.env.DEPLOY === 'true') { console.log('Deploying to CDN...') await deployToCDN(context.options.outDir) } }, }, }) ``` ## Advanced Usage ### Multiple Hooks ```ts export default defineConfig({ hooks(hooks) { // Register multiple hooks hooks.hook('build:prepare', setupEnvironment) hooks.hook('build:prepare', validateConfig) hooks.hook('build:before', injectPlugins) hooks.hook('build:before', logFormat) hooks.hook('build:done', generateManifest) hooks.hook('build:done', notifyComplete) }, }) ``` ### Async Hooks ```ts export default defineConfig({ hooks: { 'build:prepare': async (context) => { await fetchRemoteConfig() await initializeDatabase() }, 'build:done': async (context) => { await uploadToS3(context.chunks) await invalidateCDN() }, }, }) ``` ### Error Handling ```ts export default defineConfig({ hooks: { 'build:done': async (context) => { try { await riskyOperation() } catch (error) { console.error('Hook failed:', error) // Don't throw - allow build to complete } }, }, }) ``` ## Hookable API tsdown uses [hookable](https://github.com/unjs/hookable) for hooks. Additional methods: ```ts export default defineConfig({ hooks(hooks) { // Register hook hooks.hook('build:done', handler) // Register hook once hooks.hookOnce('build:prepare', handler) // Remove hook hooks.removeHook('build:done', handler) // Clear all hooks for event hooks.removeHooks('build:done') // Call hooks manually await hooks.callHook('build:done', context) }, }) ``` ## Tips 1. **Use plugins** for most extensions 2. **Hooks for simple tasks** like notifications or file copying 3. **Async hooks supported** for I/O operations 4. **Don't throw errors** unless you want to fail the build 5. **Context is mutable** in `build:before` for advanced use cases 6. **Multiple hooks allowed** for the same event ## Troubleshooting ### Hook Not Called - Verify hook name is correct - Check hook is registered in config - Ensure async hooks are awaited ### Build Fails in Hook - Add try/catch for error handling - Don't throw unless intentional - Log errors for debugging ### Context Undefined - Check which hook you're using - Verify context properties available for that hook ## Related - [Plugins](advanced-plugins.md) - Plugin system - [Rolldown Options](advanced-rolldown-options.md) - Build options - [Watch Mode](option-watch-mode.md) - Development workflow ================================================ FILE: .agents/skills/tsdown/references/advanced-plugins.md ================================================ # Plugins Extend tsdown with plugins from multiple ecosystems. ## Overview tsdown, built on Rolldown, supports plugins from multiple ecosystems to extend and customize the bundling process. ## Supported Ecosystems ### 1. Rolldown Plugins Native plugins designed for Rolldown: ```ts import RolldownPlugin from 'rolldown-plugin-something' export default defineConfig({ plugins: [RolldownPlugin()], }) ``` **Compatibility:** ✅ Full support ### 2. Unplugin Universal plugins that work across bundlers: ```ts import UnpluginPlugin from 'unplugin-something' export default defineConfig({ plugins: [UnpluginPlugin.rolldown()], }) ``` **Compatibility:** ✅ Most unplugin-* plugins work **Examples:** - `unplugin-vue-components` - `unplugin-auto-import` - `unplugin-icons` ### 3. Rollup Plugins Most Rollup plugins work with tsdown: ```ts import RollupPlugin from '@rollup/plugin-something' export default defineConfig({ plugins: [RollupPlugin()], }) ``` **Compatibility:** ✅ High compatibility **Type Issues:** May cause TypeScript errors - use type casting: ```ts import RollupPlugin from 'rollup-plugin-something' export default defineConfig({ plugins: [ // @ts-expect-error Rollup plugin type mismatch RollupPlugin(), // Or cast to any RollupPlugin() as any, ], }) ``` ### 4. Vite Plugins Some Vite plugins may work: ```ts import VitePlugin from 'vite-plugin-something' export default defineConfig({ plugins: [ // @ts-expect-error Vite plugin type mismatch VitePlugin(), ], }) ``` **Compatibility:** ⚠️ Limited - only if not using Vite-specific APIs **Note:** Improved support planned for future releases. ## Usage ### Basic Plugin Usage ```ts import { defineConfig } from 'tsdown' import SomePlugin from 'some-plugin' export default defineConfig({ entry: ['src/index.ts'], plugins: [SomePlugin()], }) ``` ### Multiple Plugins ```ts import PluginA from 'plugin-a' import PluginB from 'plugin-b' import PluginC from 'plugin-c' export default defineConfig({ entry: ['src/index.ts'], plugins: [ PluginA(), PluginB({ option: true }), PluginC(), ], }) ``` ### Conditional Plugins ```ts export default defineConfig((options) => ({ entry: ['src/index.ts'], plugins: [ SomePlugin(), options.watch && DevPlugin(), !options.watch && ProdPlugin(), ].filter(Boolean), })) ``` ## Common Plugin Patterns ### JSON Import ```ts import json from '@rollup/plugin-json' export default defineConfig({ plugins: [json()], }) ``` ### Node Resolve ```ts import { nodeResolve } from '@rollup/plugin-node-resolve' export default defineConfig({ plugins: [nodeResolve()], }) ``` ### CommonJS ```ts import commonjs from '@rollup/plugin-commonjs' export default defineConfig({ plugins: [commonjs()], }) ``` ### Replace ```ts import replace from '@rollup/plugin-replace' export default defineConfig({ plugins: [ replace({ 'process.env.NODE_ENV': JSON.stringify('production'), __VERSION__: JSON.stringify('1.0.0'), }), ], }) ``` ### Auto Import ```ts import AutoImport from 'unplugin-auto-import/rolldown' export default defineConfig({ plugins: [ AutoImport({ imports: ['vue', 'vue-router'], dts: 'src/auto-imports.d.ts', }), ], }) ``` ### Vue Components ```ts import Components from 'unplugin-vue-components/rolldown' export default defineConfig({ plugins: [ Components({ dts: 'src/components.d.ts', }), ], }) ``` ## Framework-Specific Plugins ### React ```ts import react from '@vitejs/plugin-react' export default defineConfig({ entry: ['src/index.tsx'], plugins: [ // @ts-expect-error Vite plugin react(), ], }) ``` ### Vue ```ts import vue from '@vitejs/plugin-vue' export default defineConfig({ entry: ['src/index.ts'], plugins: [ // @ts-expect-error Vite plugin vue(), ], }) ``` ### Solid ```ts import solid from 'vite-plugin-solid' export default defineConfig({ entry: ['src/index.tsx'], plugins: [ // @ts-expect-error Vite plugin solid(), ], }) ``` ### Svelte ```ts import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ entry: ['src/index.ts'], plugins: [ // @ts-expect-error Vite plugin svelte(), ], }) ``` ## Writing Custom Plugins Follow Rolldown's plugin development guide: ### Basic Plugin Structure ```ts import type { Plugin } from 'rolldown' function myPlugin(): Plugin { return { name: 'my-plugin', // Transform hook transform(code, id) { if (id.endsWith('.custom')) { return { code: transformCode(code), map: null, } } }, // Other hooks... } } ``` ### Using Custom Plugin ```ts import { myPlugin } from './my-plugin' export default defineConfig({ plugins: [myPlugin()], }) ``` ## Plugin Configuration ### Plugin-Specific Options Refer to each plugin's documentation for configuration options. ### Plugin Order Plugins run in the order they're defined: ```ts export default defineConfig({ plugins: [ PluginA(), // Runs first PluginB(), // Runs second PluginC(), // Runs last ], }) ``` ## Troubleshooting ### Type Errors with Rollup/Vite Plugins Use type casting: ```ts plugins: [ // Option 1: @ts-expect-error // @ts-expect-error Plugin type mismatch SomePlugin(), // Option 2: as any SomePlugin() as any, ] ``` ### Plugin Not Working 1. **Check compatibility** - Verify plugin supports your bundler 2. **Read documentation** - Follow plugin's setup instructions 3. **Check plugin order** - Some plugins depend on execution order 4. **Enable debug mode** - Use `--debug` flag ### Vite Plugin Fails Vite plugins may rely on Vite-specific APIs: 1. **Find Rollup equivalent** - Look for Rollup version of plugin 2. **Use Unplugin version** - Check for `unplugin-*` alternative 3. **Wait for support** - Vite plugin support improving ## Resources - [Rolldown Plugin Development](https://rolldown.rs/guide/plugin-development) - [Unplugin Documentation](https://unplugin.unjs.io/) - [Rollup Plugins](https://github.com/rollup/plugins) - [Vite Plugins](https://vitejs.dev/plugins/) ## Tips 1. **Prefer Rolldown plugins** for best compatibility 2. **Use Unplugin** for cross-bundler support 3. **Cast types** for Rollup/Vite plugins 4. **Test thoroughly** when using cross-ecosystem plugins 5. **Check plugin docs** for specific configuration 6. **Write custom plugins** for unique needs ## Related - [Hooks](advanced-hooks.md) - Lifecycle hooks - [Rolldown Options](advanced-rolldown-options.md) - Advanced Rolldown config - [React Recipe](recipe-react.md) - React setup with plugins - [Vue Recipe](recipe-vue.md) - Vue setup with plugins ================================================ FILE: .agents/skills/tsdown/references/advanced-programmatic.md ================================================ # Programmatic Usage Use tsdown from JavaScript/TypeScript code. ## Overview tsdown can be imported and used programmatically in your Node.js scripts, custom build tools, or automation workflows. ## Basic Usage ### Simple Build ```ts import { build } from 'tsdown' await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) ``` ### With Options ```ts import { build } from 'tsdown' await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], outDir: 'dist', dts: true, minify: true, sourcemap: true, clean: true, }) ``` ## API Reference ### build() Main function to run a build. ```ts import { build } from 'tsdown' await build(options) ``` **Parameters:** - `options` - Build configuration object (same as config file) **Returns:** - `Promise` - Resolves when build completes **Throws:** - Build errors if compilation fails ## Configuration Object All config file options are available: ```ts import { build, defineConfig } from 'tsdown' const config = defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, minify: true, sourcemap: true, external: ['react', 'react-dom'], plugins: [/* plugins */], hooks: { 'build:done': async () => { console.log('Build complete!') }, }, }) await build(config) ``` See [Config Reference](option-config-file.md) for all options. ## Common Patterns ### Custom Build Script ```ts // scripts/build.ts import { build } from 'tsdown' async function main() { console.log('Building library...') await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, }) console.log('Build complete!') } main().catch(console.error) ``` Run with: ```bash tsx scripts/build.ts ``` ### Multiple Builds ```ts import { build } from 'tsdown' // Build main library await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], outDir: 'dist', dts: true, }) // Build CLI tool await build({ entry: ['src/cli.ts'], format: ['esm'], outDir: 'dist/bin', platform: 'node', shims: true, }) ``` ### Conditional Build ```ts import { build } from 'tsdown' const isDev = process.env.NODE_ENV === 'development' await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: !isDev, sourcemap: isDev, clean: !isDev, }) ``` ### With Error Handling ```ts import { build } from 'tsdown' try { await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) console.log('✅ Build successful') } catch (error) { console.error('❌ Build failed:', error) process.exit(1) } ``` ### Automated Workflow ```ts import { build } from 'tsdown' import { execSync } from 'child_process' async function release() { // Clean console.log('Cleaning...') execSync('rm -rf dist') // Build console.log('Building...') await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, minify: true, }) // Test console.log('Testing...') execSync('npm test') // Publish console.log('Publishing...') execSync('npm publish') } release().catch(console.error) ``` ### Build with Post-Processing ```ts import { build } from 'tsdown' import { copyFileSync } from 'fs' await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, hooks: { 'build:done': async () => { // Copy additional files copyFileSync('README.md', 'dist/README.md') copyFileSync('LICENSE', 'dist/LICENSE') console.log('Copied additional files') }, }, }) ``` ## Watch Mode Unfortunately, watch mode is not directly exposed in the programmatic API. Use the CLI for watch mode: ```ts // Use CLI for watch mode import { spawn } from 'child_process' spawn('tsdown', ['--watch'], { stdio: 'inherit', shell: true, }) ``` ## Integration Examples ### With Task Runner ```ts // gulpfile.js import { build } from 'tsdown' import gulp from 'gulp' gulp.task('build', async () => { await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) }) gulp.task('watch', () => { return gulp.watch('src/**/*.ts', gulp.series('build')) }) ``` ### With Custom CLI ```ts // scripts/cli.ts import { build } from 'tsdown' import { Command } from 'commander' const program = new Command() program .command('build') .option('--prod', 'Production build') .action(async (options) => { await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: options.prod, sourcemap: !options.prod, }) }) program.parse() ``` ### With CI/CD ```ts // .github/scripts/build.ts import { build } from 'tsdown' const isCI = process.env.CI === 'true' await build({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, minify: isCI, clean: true, }) // Upload to artifact storage if (isCI) { // Upload dist/ to S3, etc. } ``` ## TypeScript Support ```ts // scripts/build.ts import { build, type UserConfig } from 'tsdown' const config: UserConfig = { entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, } await build(config) ``` ## Tips 1. **Use TypeScript** for type safety 2. **Handle errors** properly 3. **Use hooks** for custom logic 4. **Log progress** for visibility 5. **Use CLI for watch** mode 6. **Exit on error** in scripts ## Troubleshooting ### Import Errors Ensure tsdown is installed: ```bash pnpm add -D tsdown ``` ### Type Errors Import types: ```ts import type { UserConfig } from 'tsdown' ``` ### Build Fails Silently Add error handling: ```ts try { await build(config) } catch (error) { console.error(error) process.exit(1) } ``` ### Options Not Working Check spelling and types: ```ts // ✅ Correct { format: ['esm', 'cjs'] } // ❌ Wrong { formats: ['esm', 'cjs'] } ``` ## Related - [Config File](option-config-file.md) - Configuration options - [Hooks](advanced-hooks.md) - Lifecycle hooks - [CLI](reference-cli.md) - Command-line interface - [Plugins](advanced-plugins.md) - Plugin system ================================================ FILE: .agents/skills/tsdown/references/advanced-rolldown-options.md ================================================ # Customizing Rolldown Options Pass options directly to the underlying Rolldown bundler. ## Overview tsdown uses [Rolldown](https://rolldown.rs) as its core bundling engine. You can override Rolldown's input and output options directly for fine-grained control. **Warning:** You should be familiar with Rolldown's behavior before overriding options. Refer to the [Rolldown Config Options](https://rolldown.rs/options/input) documentation. ## Input Options ### Using an Object ```ts export default defineConfig({ inputOptions: { cwd: './custom-directory', }, }) ``` ### Using a Function Dynamically modify options based on the output format: ```ts export default defineConfig({ inputOptions(inputOptions, format) { inputOptions.cwd = './custom-directory' return inputOptions }, }) ``` ## Output Options ### Using an Object ```ts export default defineConfig({ outputOptions: { legalComments: 'inline', }, }) ``` ### Using a Function ```ts export default defineConfig({ outputOptions(outputOptions, format) { if (format === 'esm') { outputOptions.legalComments = 'inline' } return outputOptions }, }) ``` ## Common Use Cases ### Preserve Legal Comments ```ts export default defineConfig({ entry: ['src/index.ts'], outputOptions: { legalComments: 'inline', }, }) ``` ### Custom Working Directory ```ts export default defineConfig({ entry: ['src/index.ts'], inputOptions: { cwd: './packages/my-lib', }, }) ``` ### Format-Specific Options ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], outputOptions(outputOptions, format) { if (format === 'esm') { outputOptions.legalComments = 'inline' } return outputOptions }, }) ``` ## When to Use - When tsdown doesn't expose a specific Rolldown option - For format-specific Rolldown customizations - For advanced bundling scenarios ## Tips 1. **Read Rolldown docs** before overriding options 2. **Use functions** for format-specific customization 3. **Test thoroughly** when overriding defaults 4. **Prefer tsdown options** when available (e.g., use `minify` instead of setting it via `outputOptions`) ## Related - [Plugins](advanced-plugins.md) - Plugin system - [Hooks](advanced-hooks.md) - Lifecycle hooks - [Config File](option-config-file.md) - Configuration options ================================================ FILE: .agents/skills/tsdown/references/guide-getting-started.md ================================================ # Getting Started Quick guide to installing and using tsdown for the first time. ## Installation Install tsdown as a development dependency: ```bash pnpm add -D tsdown # Optionally install TypeScript if not using isolatedDeclarations pnpm add -D typescript ``` **Requirements:** - Node.js 20.19 or higher - Experimental support for Deno and Bun ## Quick Start Templates Use `create-tsdown` CLI for instant setup: ```bash pnpm create tsdown@latest ``` Provides templates for: - Pure TypeScript libraries - React component libraries - Vue component libraries - Ready-to-use configurations ## First Bundle ### 1. Create Source Files ```ts // src/index.ts import { hello } from './hello.ts' hello() // src/hello.ts export function hello() { console.log('Hello tsdown!') } ``` ### 2. Create Config File ```ts // tsdown.config.ts import { defineConfig } from 'tsdown' export default defineConfig({ entry: ['./src/index.ts'], }) ``` ### 3. Run Build ```bash ./node_modules/.bin/tsdown ``` Output: `dist/index.mjs` ### 4. Test Output ```bash node dist/index.mjs # Output: Hello tsdown! ``` ## Add to npm Scripts ```json { "scripts": { "build": "tsdown" } } ``` Run with: ```bash pnpm build ``` ## CLI Commands ```bash # Check version tsdown --version # View help tsdown --help # Build with watch mode tsdown --watch # Build with specific format tsdown --format esm,cjs # Generate type declarations tsdown --dts ``` ## Basic Configurations ### TypeScript Library (ESM + CJS) ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, }) ``` ### Browser Library (IIFE) ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['iife'], globalName: 'MyLib', platform: 'browser', minify: true, }) ``` ### Multiple Entry Points ```ts export default defineConfig({ entry: { index: 'src/index.ts', utils: 'src/utils.ts', cli: 'src/cli.ts', }, format: ['esm', 'cjs'], dts: true, }) ``` ## Using Plugins Add Rolldown, Rollup, or Unplugin plugins: ```ts import SomePlugin from 'some-plugin' export default defineConfig({ entry: ['src/index.ts'], plugins: [SomePlugin()], }) ``` ## Watch Mode Enable automatic rebuilds on file changes: ```bash tsdown --watch # or tsdown -w ``` ## Next Steps - Configure [entry points](option-entry.md) with glob patterns - Set up [multiple output formats](option-output-format.md) - Enable [type declaration generation](option-dts.md) - Explore [plugins](advanced-plugins.md) for extended functionality - Read [migration guide](guide-migrate-from-tsup.md) if coming from tsup ================================================ FILE: .agents/skills/tsdown/references/guide-migrate-from-tsup.md ================================================ # Migrate from tsup Migration guide for switching from tsup to tsdown. ## Overview tsdown is built on Rolldown (Rust-based) vs tsup's esbuild, providing faster and more powerful bundling while maintaining compatibility. ## Automatic Migration ### Single Package ```bash npx tsdown-migrate ``` ### Monorepo ```bash # Using glob patterns npx tsdown-migrate packages/* # Multiple directories npx tsdown-migrate packages/foo packages/bar ``` ### Migration Options - `[...dirs]` - Directories to migrate (supports globs) - `--dry-run` or `-d` - Preview changes without modifying files **Important:** Commit your changes before running migration. ## Key Differences ### Default Values | Option | tsup | tsdown | |--------|------|--------| | `format` | `['cjs']` | `['esm']` | | `clean` | `false` | `true` | | `dts` | `false` | Auto-enabled if `types`/`typings` in package.json | | `target` | Manual | Auto-read from `engines.node` in package.json | ### New Features in tsdown #### Node Protocol Control ```ts export default defineConfig({ nodeProtocol: true, // Add node: prefix (fs → node:fs) nodeProtocol: 'strip', // Remove node: prefix (node:fs → fs) nodeProtocol: false, // Keep as-is (default) }) ``` #### Better Workspace Support ```ts export default defineConfig({ workspace: 'packages/*', // Build all packages }) ``` ## Migration Checklist 1. **Backup your code** - Commit all changes 2. **Run migration tool** - `npx tsdown-migrate` 3. **Review changes** - Check modified config files 4. **Update scripts** - Change `tsup` to `tsdown` in package.json 5. **Test build** - Run `pnpm build` to verify 6. **Adjust config** - Fine-tune based on your needs ## Common Migration Patterns ### Basic Library **Before (tsup):** ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], dts: true, }) ``` **After (tsdown):** ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], // ESM now default dts: true, clean: true, // Now enabled by default }) ``` ### With Custom Target **Before (tsup):** ```ts export default defineConfig({ entry: ['src/index.ts'], target: 'es2020', }) ``` **After (tsdown):** ```ts export default defineConfig({ entry: ['src/index.ts'], // target auto-reads from package.json engines.node // Or override explicitly: target: 'es2020', }) ``` ### CLI Scripts **Before (package.json):** ```json { "scripts": { "build": "tsup", "dev": "tsup --watch" } } ``` **After (package.json):** ```json { "scripts": { "build": "tsdown", "dev": "tsdown --watch" } } ``` ## Feature Compatibility ### Supported tsup Features Most tsup features are supported: - ✅ Multiple entry points - ✅ Multiple formats (ESM, CJS, IIFE, UMD) - ✅ TypeScript declarations - ✅ Source maps - ✅ Minification - ✅ Watch mode - ✅ External dependencies - ✅ Tree shaking - ✅ Shims - ✅ Plugins (Rollup compatible) ### Missing Features Some tsup features are not yet available. Check [GitHub issues](https://github.com/rolldown/tsdown/issues) for status and request features. ## Troubleshooting ### Build Fails After Migration 1. **Check Node.js version** - Requires Node.js 20.19+ 2. **Install TypeScript** - Required for DTS generation 3. **Review config changes** - Ensure format and options are correct 4. **Check dependencies** - Verify all dependencies are installed ### Different Output - **Format order** - tsdown defaults to ESM first - **Clean behavior** - tsdown cleans outDir by default - **Target** - tsdown auto-detects from package.json ### Performance Issues tsdown should be faster than tsup. If not: 1. Enable `isolatedDeclarations` for faster DTS generation 2. Check for large dependencies being bundled 3. Use `skipNodeModulesBundle` if needed ## Getting Help - [GitHub Issues](https://github.com/rolldown/tsdown/issues) - Report bugs or request features - [Documentation](https://tsdown.dev) - Full documentation - [Migration Tool](https://github.com/rolldown/tsdown/tree/main/packages/tsdown-migrate) - Source code ## Acknowledgements tsdown is heavily inspired by tsup and incorporates parts of its codebase. Thanks to [@egoist](https://github.com/egoist) and the tsup community. ================================================ FILE: .agents/skills/tsdown/references/option-cjs-default.md ================================================ # CJS Default Export Control how default exports are handled in CommonJS output. ## Overview The `cjsDefault` option improves compatibility when generating CommonJS modules. When enabled (default), modules with only a single default export use `module.exports = ...` instead of `exports.default = ...`. ## Type ```ts cjsDefault?: boolean // default: true ``` ## Basic Usage ### Enabled (Default) ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['cjs'], cjsDefault: true, // default behavior }) ``` ### Disabled ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['cjs'], cjsDefault: false, }) ``` ## How It Works ### With `cjsDefault: true` (Default) When your module has **only a single default export**, tsdown transforms: **Source:** ```ts // src/index.ts export default function greet() { console.log('Hello, world!') } ``` **Generated CJS:** ```js // dist/index.cjs function greet() { console.log('Hello, world!') } module.exports = greet ``` **Generated Declaration:** ```ts // dist/index.d.cts declare function greet(): void export = greet ``` This allows consumers to use `const greet = require('your-module')` directly. ### With `cjsDefault: false` The default export stays as `exports.default`: ```js // dist/index.cjs function greet() { console.log('Hello, world!') } exports.default = greet ``` Consumers need `require('your-module').default`. ## When to Disable - When your module has both default and named exports - When you need consistent `exports.default` behavior - When consumers always use ESM imports ## Tips 1. **Leave enabled** for most libraries (default `true`) 2. **Disable** if you have both default and named exports and need consistent behavior 3. **Test CJS consumers** to verify compatibility ## Related Options - [Output Format](option-output-format.md) - Module formats - [Shims](option-shims.md) - ESM/CJS compatibility ================================================ FILE: .agents/skills/tsdown/references/option-cleaning.md ================================================ # Output Directory Cleaning Control how the output directory is cleaned before builds. ## Overview By default, tsdown **cleans the output directory** before each build to remove stale files from previous builds. ## Basic Usage ### CLI ```bash # Clean enabled (default) tsdown # Disable cleaning tsdown --no-clean ``` ### Config File ```ts export default defineConfig({ entry: ['src/index.ts'], clean: true, // Default }) ``` ## Behavior ### With Cleaning (Default) Before each build: 1. All files in `outDir` are removed 2. Fresh build starts with empty directory 3. Only current build outputs remain **Benefits:** - No stale files - Predictable output - Clean slate each build ### Without Cleaning Build outputs are added to existing files: ```ts export default defineConfig({ clean: false, }) ``` **Use when:** - Multiple builds to same directory - Incremental builds - Preserving other files - Watch mode (faster rebuilds) ## Common Patterns ### Production Build ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], clean: true, // Ensure clean output minify: true, }) ``` ### Development Mode ```ts export default defineConfig((options) => ({ entry: ['src/index.ts'], clean: !options.watch, // Don't clean in watch mode sourcemap: options.watch, })) ``` ### Multiple Builds ```ts export default defineConfig([ { entry: ['src/index.ts'], outDir: 'dist', clean: true, // Clean once }, { entry: ['src/cli.ts'], outDir: 'dist', clean: false, // Don't clean, add to same dir }, ]) ``` ### Monorepo Package ```ts export default defineConfig({ workspace: 'packages/*', entry: ['src/index.ts'], clean: true, // Clean each package's dist }) ``` ### Preserve Static Files ```ts export default defineConfig({ entry: ['src/index.ts'], clean: false, // Keep manually added files outDir: 'dist', }) // Manually copy files first // Then run tsdown --no-clean ``` ## Clean Patterns ### Selective Cleaning ```ts import { rmSync } from 'fs' export default defineConfig({ clean: false, // Disable auto clean hooks: { 'build:prepare': () => { // Custom cleaning logic rmSync('dist/*.js', { force: true }) // Keep other files }, }, }) ``` ### Clean Specific Directories ```ts export default defineConfig({ clean: false, hooks: { 'build:prepare': async () => { const { rm } = await import('fs/promises') // Only clean specific subdirectories await rm('dist/esm', { recursive: true, force: true }) await rm('dist/cjs', { recursive: true, force: true }) // Keep dist/types }, }, }) ``` ## Watch Mode Behavior In watch mode, cleaning behavior is important: ### Clean on First Build Only ```ts export default defineConfig((options) => ({ entry: ['src/index.ts'], watch: options.watch, clean: !options.watch, // Only clean initial build })) ``` **Result:** - First build: Clean - Subsequent rebuilds: Incremental ### Always Clean ```ts export default defineConfig({ watch: true, clean: true, // Clean every rebuild }) ``` **Trade-off:** Slower rebuilds, but always fresh output. ## Tips 1. **Leave enabled** for production builds 2. **Disable in watch mode** for faster rebuilds 3. **Use multiple configs** carefully with cleaning 4. **Custom clean logic** via hooks if needed 5. **Be cautious** - cleaning removes ALL files in outDir 6. **Test cleaning** - ensure no important files are lost ## Troubleshooting ### Important Files Deleted - Don't put non-build files in outDir - Use separate directory for static files - Disable cleaning and manage manually ### Stale Files in Output - Enable cleaning: `clean: true` - Or manually remove before build ### Slow Rebuilds in Watch - Disable cleaning in watch mode - Use incremental builds ## CLI Examples ```bash # Default (clean enabled) tsdown # Disable cleaning tsdown --no-clean # Watch mode without cleaning tsdown --watch --no-clean # Multiple formats with cleaning tsdown --format esm,cjs --clean ``` ## Examples ### Safe Production Build ```bash # Clean before build rm -rf dist tsdown --clean ``` ### Incremental Development ```ts export default defineConfig({ entry: ['src/index.ts'], watch: true, clean: false, // Faster rebuilds sourcemap: true, }) ``` ### Multi-Stage Build ```ts // Stage 1: Clean and build main export default defineConfig([ { entry: ['src/index.ts'], outDir: 'dist', clean: true, }, { entry: ['src/utils.ts'], outDir: 'dist', clean: false, // Add to same directory }, ]) ``` ## Related Options - [Output Directory](option-output-directory.md) - Configure outDir - [Watch Mode](option-watch-mode.md) - Development workflow - [Hooks](advanced-hooks.md) - Custom clean logic - [Entry](option-entry.md) - Entry points ================================================ FILE: .agents/skills/tsdown/references/option-config-file.md ================================================ # Configuration File Centralize and manage build settings with a configuration file. ## Overview tsdown searches for config files automatically in the current directory and parent directories. ## Supported File Names tsdown looks for these files (in order): - `tsdown.config.ts` - `tsdown.config.mts` - `tsdown.config.cts` - `tsdown.config.js` - `tsdown.config.mjs` - `tsdown.config.cjs` - `tsdown.config.json` - `tsdown.config` - `package.json` (in `tsdown` field) ## Basic Configuration ### TypeScript Config ```ts // tsdown.config.ts import { defineConfig } from 'tsdown' export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, }) ``` ### JavaScript Config ```js // tsdown.config.js export default { entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, } ``` ### JSON Config ```json // tsdown.config.json { "entry": ["src/index.ts"], "format": ["esm", "cjs"], "dts": true } ``` ### Package.json Config ```json // package.json { "name": "my-library", "tsdown": { "entry": ["src/index.ts"], "format": ["esm", "cjs"], "dts": true } } ``` ## Multiple Configurations Build multiple outputs with different settings: ```ts export default defineConfig([ { entry: 'src/index.ts', format: ['esm', 'cjs'], platform: 'node', dts: true, }, { entry: 'src/browser.ts', format: ['iife'], platform: 'browser', globalName: 'MyLib', minify: true, }, ]) ``` Each configuration runs as a separate build. ## Dynamic Configuration Use a function for conditional config: ```ts export default defineConfig((options) => { const isDev = options.watch return { entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: !isDev, sourcemap: isDev, clean: !isDev, } }) ``` Available options: - `watch` - Whether watch mode is enabled - Other CLI flags passed to config ## Config Loaders Control how TypeScript config files are loaded: ### Auto Loader (Default) Uses native TypeScript support if available, otherwise falls back to `unrun`: ```bash tsdown # Uses auto loader ``` ### Native Loader Uses runtime's native TypeScript support (Node.js 23+, Bun, Deno): ```bash tsdown --config-loader native ``` ### Unrun Loader Uses [unrun](https://gugustinette.github.io/unrun/) library for loading: ```bash tsdown --config-loader unrun ``` **Tip:** Use `unrun` loader if you need to load TypeScript configs without file extensions in Node.js. ## Custom Config Path Specify a custom config file location: ```bash tsdown --config ./configs/build.config.ts # or tsdown -c custom-config.ts ``` ## Disable Config File Ignore config files and use CLI options only: ```bash tsdown --no-config src/index.ts --format esm ``` ## Extend Vite/Vitest Config (Experimental) Reuse existing Vite or Vitest configurations: ```bash # Extend vite.config.* tsdown --from-vite # Extend vitest.config.* tsdown --from-vite vitest ``` **Note:** Only specific options like `resolve` and `plugins` are reused. Test thoroughly as this feature is experimental. ## Workspace / Monorepo Build multiple packages with a single config: ```ts export default defineConfig({ workspace: 'packages/*', entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) ``` Each package directory matching the glob pattern will be built with the same configuration. ## Common Patterns ### Library with Multiple Builds ```ts export default defineConfig([ // Node.js build { entry: ['src/index.ts'], format: ['esm', 'cjs'], platform: 'node', dts: true, }, // Browser build { entry: ['src/browser.ts'], format: ['iife'], platform: 'browser', globalName: 'MyLib', }, ]) ``` ### Development vs Production ```ts export default defineConfig((options) => ({ entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: !options.watch, sourcemap: options.watch ? true : false, clean: !options.watch, })) ``` ### Monorepo Root Config ```ts // Root tsdown.config.ts export default defineConfig({ workspace: 'packages/*', entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, clean: true, // Shared config for all packages }) ``` ### Per-Package Override ```ts // packages/special/tsdown.config.ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], // Override: only ESM platform: 'browser', // Override: browser only }) ``` ## Config Precedence When multiple configs exist: 1. CLI options (highest priority) 2. Config file specified with `--config` 3. Auto-discovered config files 4. Package.json `tsdown` field 5. Default values ## Tips 1. **Use TypeScript config** for type checking and autocomplete 2. **Use defineConfig** helper for better DX 3. **Export arrays** for multiple build configurations 4. **Use functions** for dynamic/conditional configs 5. **Keep configs simple** - prefer convention over configuration 6. **Use workspace** for monorepo builds 7. **Test experimental features** thoroughly before production use ## Related Options - [Entry](option-entry.md) - Configure entry points - [Output Format](option-output-format.md) - Output formats - [Watch Mode](option-watch-mode.md) - Watch mode configuration ================================================ FILE: .agents/skills/tsdown/references/option-css.md ================================================ # CSS Support **Status: Experimental — still in active development.** CSS handling in tsdown is not yet stable. The API and capabilities may change significantly in future releases. Avoid relying on CSS-related options in production builds until the feature is marked as stable. ================================================ FILE: .agents/skills/tsdown/references/option-dependencies.md ================================================ # Dependencies Control how dependencies are bundled or externalized. ## Overview tsdown intelligently handles dependencies to keep your library lightweight while ensuring all necessary code is included. ## Default Behavior ### Auto-Externalized These are **NOT bundled** by default: - **`dependencies`** - Installed automatically with your package - **`peerDependencies`** - User must install manually ### Conditionally Bundled These are **bundled ONLY if imported**: - **`devDependencies`** - Only if actually used in source code - **Phantom dependencies** - In node_modules but not in package.json ## Configuration Options ### `external` Mark dependencies as external (not bundled): ```ts export default defineConfig({ entry: ['src/index.ts'], external: [ 'react', // Single package 'react-dom', /^@myorg\//, // Regex pattern (all @myorg/* packages) /^lodash/, // All lodash packages ], }) ``` ### `noExternal` Force dependencies to be bundled: ```ts export default defineConfig({ entry: ['src/index.ts'], noExternal: [ 'some-package', // Bundle this even if in dependencies 'vendor-lib', ], }) ``` ### `skipNodeModulesBundle` Skip resolving and bundling ALL node_modules: ```ts export default defineConfig({ entry: ['src/index.ts'], skipNodeModulesBundle: true, }) ``` **Result:** No dependencies from node_modules are parsed or bundled. ## Common Patterns ### React Component Library ```ts export default defineConfig({ entry: ['src/index.tsx'], format: ['esm', 'cjs'], external: [ 'react', 'react-dom', /^react\//, // react/jsx-runtime, etc. ], dts: true, }) ``` ### Utility Library with Shared Deps ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], // Bundle lodash utilities noExternal: ['lodash-es'], dts: true, }) ``` ### Monorepo Package ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], external: [ /^@mycompany\//, // Don't bundle other workspace packages ], dts: true, }) ``` ### CLI Tool (Bundle Everything) ```ts export default defineConfig({ entry: ['src/cli.ts'], format: ['esm'], platform: 'node', // Bundle all dependencies for standalone CLI noExternal: [/.*/], shims: true, }) ``` ### Library with Specific Externals ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], external: [ 'vue', '@vue/runtime-core', '@vue/reactivity', ], dts: true, }) ``` ## Declaration Files Dependency handling for `.d.ts` files follows the same rules as JavaScript. ### Complex Type Resolution Use TypeScript resolver for complex third-party types: ```ts export default defineConfig({ entry: ['src/index.ts'], dts: { resolver: 'tsc', // Use TypeScript resolver instead of Oxc }, }) ``` **When to use `tsc` resolver:** - Types in `@types/*` packages with non-standard naming (e.g., `@types/babel__generator`) - Complex type dependencies - Issues with default Oxc resolver **Trade-off:** `tsc` is slower but more compatible. ## CLI Usage ### External ```bash tsdown --external react --external react-dom tsdown --external '/^@myorg\/.*/' ``` ### No External ```bash tsdown --no-external some-package ``` ## Examples by Use Case ### Framework Component ```ts // Don't bundle framework export default defineConfig({ external: ['vue', 'react', 'solid-js', 'svelte'], }) ``` ### Standalone App ```ts // Bundle everything export default defineConfig({ noExternal: [/.*/], skipNodeModulesBundle: false, }) ``` ### Shared Library ```ts // Bundle only specific utils export default defineConfig({ external: [/.*/], // External by default noExternal: ['tiny-utils'], // Except this one }) ``` ### Monorepo Package ```ts // External workspace packages, bundle utilities export default defineConfig({ external: [ /^@workspace\//, // Other workspace packages 'react', 'react-dom', ], noExternal: [ 'lodash-es', // Bundle utility libraries ], }) ``` ## Troubleshooting ### Dependency Bundled Unexpectedly Check if it's in `devDependencies` and imported. Move to `dependencies`: ```json { "dependencies": { "should-be-external": "^1.0.0" } } ``` Or explicitly externalize: ```ts export default defineConfig({ external: ['should-be-external'], }) ``` ### Missing Dependency at Runtime Ensure it's in `dependencies` or `peerDependencies`: ```json { "dependencies": { "needed-package": "^1.0.0" } } ``` Or bundle it: ```ts export default defineConfig({ noExternal: ['needed-package'], }) ``` ### Type Resolution Errors Use TypeScript resolver for complex types: ```ts export default defineConfig({ dts: { resolver: 'tsc', }, }) ``` ## Summary **Default behavior:** - `dependencies` & `peerDependencies` → External - `devDependencies` & phantom deps → Bundled if imported **Override:** - `external` → Force external - `noExternal` → Force bundled - `skipNodeModulesBundle` → Skip all node_modules **Declaration files:** - Same bundling logic as JavaScript - Use `resolver: 'tsc'` for complex types ## Tips 1. **Keep dependencies external** for libraries 2. **Bundle everything** for standalone CLIs 3. **Use regex patterns** for namespaced packages 4. **Check bundle size** to verify external/bundled split 5. **Test with fresh install** to catch missing dependencies 6. **Use tsc resolver** only when needed (slower) ## Related Options - [External](option-dependencies.md) - This page - [Platform](option-platform.md) - Runtime environment - [Output Format](option-output-format.md) - Module formats - [DTS](option-dts.md) - Type declarations ================================================ FILE: .agents/skills/tsdown/references/option-dts.md ================================================ # TypeScript Declaration Files Generate `.d.ts` type declaration files for your library. ## Overview tsdown uses [rolldown-plugin-dts](https://github.com/sxzz/rolldown-plugin-dts) to generate and bundle TypeScript declaration files. **Requirements:** - TypeScript must be installed in your project ## Enabling DTS Generation ### Auto-Enabled DTS generation is **automatically enabled** if `package.json` contains: - `types` field, or - `typings` field ### Manual Enable #### CLI ```bash tsdown --dts ``` #### Config File ```ts export default defineConfig({ dts: true, }) ``` ## Performance ### With `isolatedDeclarations` (Recommended) **Extremely fast** - uses oxc-transform for generation. ```json // tsconfig.json { "compilerOptions": { "isolatedDeclarations": true } } ``` ### Without `isolatedDeclarations` Falls back to TypeScript compiler. Reliable but slower. ## Declaration Maps Map `.d.ts` files back to original `.ts` sources (useful for monorepos). ### Enable in tsconfig.json ```json { "compilerOptions": { "declarationMap": true } } ``` ### Enable in tsdown Config ```ts export default defineConfig({ dts: { sourcemap: true, }, }) ``` ## Advanced Options ### Custom Compiler Options Override TypeScript compiler options: ```ts export default defineConfig({ dts: { compilerOptions: { removeComments: false, }, }, }) ``` ## Build Process - **ESM format**: `.js` and `.d.ts` files generated in same build - **CJS format**: Separate build process for `.d.ts` files ## Common Patterns ### Basic Library ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, }) ``` Output: - `dist/index.mjs` - `dist/index.cjs` - `dist/index.d.ts` ### Multiple Entry Points ```ts export default defineConfig({ entry: { index: 'src/index.ts', utils: 'src/utils.ts', }, format: ['esm', 'cjs'], dts: true, }) ``` Output: - `dist/index.mjs`, `dist/index.cjs`, `dist/index.d.ts` - `dist/utils.mjs`, `dist/utils.cjs`, `dist/utils.d.ts` ### With Monorepo Support ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: { sourcemap: true, // Enable declaration maps }, }) ``` ### Fast Build (Isolated Declarations) ```json // tsconfig.json { "compilerOptions": { "isolatedDeclarations": true, "declaration": true, "declarationMap": true } } ``` ```ts // tsdown.config.ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, // Will use fast oxc-transform }) ``` ## Troubleshooting ### Missing Types Ensure TypeScript is installed: ```bash pnpm add -D typescript ``` ### Slow Generation Enable `isolatedDeclarations` in `tsconfig.json` for faster builds. ### Declaration Errors Check that all exports have explicit types (required for `isolatedDeclarations`). ### Report Issues For DTS-specific issues, report to [rolldown-plugin-dts](https://github.com/sxzz/rolldown-plugin-dts/issues). ### Vue Support Enable Vue component type generation (requires `vue-tsc`): ```ts export default defineConfig({ dts: { vue: true, }, }) ``` ### Oxc Transform Control Oxc usage for declaration generation: ```ts export default defineConfig({ dts: { oxc: true, // Use oxc-transform (fast, requires isolatedDeclarations) }, }) ``` ### Custom TSConfig Specify a different tsconfig for DTS generation: ```ts export default defineConfig({ dts: { tsconfig: './tsconfig.build.json', }, }) ``` ## Available DTS Options | Option | Type | Description | |--------|------|-------------| | `sourcemap` | `boolean` | Generate declaration source maps | | `compilerOptions` | `object` | Override TypeScript compiler options | | `vue` | `boolean` | Enable Vue type generation (requires vue-tsc) | | `oxc` | `boolean` | Use oxc-transform for fast generation | | `tsconfig` | `string` | Path to tsconfig file | | `resolver` | `'oxc' \| 'tsc'` | Module resolver: `'oxc'` (default, fast) or `'tsc'` (more compatible) | | `cjsDefault` | `boolean` | CJS default export handling | | `sideEffects` | `boolean` | Preserve side effects in declarations | ## Tips 1. **Always enable DTS** for TypeScript libraries 2. **Use isolatedDeclarations** for fast builds 3. **Enable declaration maps** in monorepos 4. **Ensure explicit types** for all exports 5. **Install TypeScript** as dev dependency ## Related Options - [Entry](option-entry.md) - Configure entry points - [Output Format](option-output-format.md) - Multiple output formats - [Target](option-target.md) - JavaScript version ================================================ FILE: .agents/skills/tsdown/references/option-entry.md ================================================ # Entry Points Configure which files to bundle as entry points. ## Overview Entry points are the starting files for the bundling process. Each entry point generates a separate bundle. ## Usage Patterns ### CLI ```bash # Single entry tsdown src/index.ts # Multiple entries tsdown src/index.ts src/cli.ts # Glob patterns tsdown 'src/*.ts' ``` ### Config File #### Single Entry ```ts export default defineConfig({ entry: 'src/index.ts', }) ``` #### Multiple Entries (Array) ```ts export default defineConfig({ entry: ['src/entry1.ts', 'src/entry2.ts'], }) ``` #### Named Entries (Object) ```ts export default defineConfig({ entry: { main: 'src/index.ts', utils: 'src/utils.ts', cli: 'src/cli.ts', }, }) ``` Output files will match the keys: - `dist/main.mjs` - `dist/utils.mjs` - `dist/cli.mjs` ## Glob Patterns Match multiple files dynamically using glob patterns: ### All TypeScript Files ```ts export default defineConfig({ entry: 'src/**/*.ts', }) ``` ### Exclude Test Files ```ts export default defineConfig({ entry: ['src/*.ts', '!src/*.test.ts'], }) ``` ### Object Entries with Glob Patterns Use glob wildcards (`*`) in both keys and values. The `*` in the key acts as a placeholder replaced with the matched file name (without extension): ```ts export default defineConfig({ entry: { // Maps src/foo.ts → dist/lib/foo.js, src/bar.ts → dist/lib/bar.js 'lib/*': 'src/*.ts', }, }) ``` #### Negation Patterns in Object Entries Values can be an array with negation patterns (`!`): ```ts export default defineConfig({ entry: { 'hooks/*': ['src/hooks/*.ts', '!src/hooks/index.ts'], }, }) ``` Multiple positive and negation patterns: ```ts export default defineConfig({ entry: { 'utils/*': [ 'src/utils/*.ts', 'src/utils/*.tsx', '!src/utils/index.ts', '!src/utils/internal.ts', ], }, }) ``` **Warning:** Multiple positive patterns in an array value must share the same base directory. ### Mixed Entries Mix strings, glob patterns, and object entries in an array: ```ts export default defineConfig({ entry: [ 'src/*', '!src/foo.ts', { main: 'index.ts' }, { 'lib/*': ['src/*.ts', '!src/bar.ts'] }, ], }) ``` Object entries take precedence when output names conflict. ### Windows Compatibility Use forward slashes `/` instead of backslashes `\` on Windows: ```ts // ✅ Correct entry: 'src/utils/*.ts' // ❌ Wrong on Windows entry: 'src\\utils\\*.ts' ``` ## Common Patterns ### Library with Main Export ```ts export default defineConfig({ entry: 'src/index.ts', format: ['esm', 'cjs'], dts: true, }) ``` ### Library with Multiple Exports ```ts export default defineConfig({ entry: { index: 'src/index.ts', client: 'src/client.ts', server: 'src/server.ts', }, format: ['esm', 'cjs'], dts: true, }) ``` ### CLI Tool ```ts export default defineConfig({ entry: { cli: 'src/cli.ts', }, format: ['esm'], platform: 'node', }) ``` ### Preserve Directory Structure Use with `unbundle: true` to keep file structure: ```ts export default defineConfig({ entry: ['src/**/*.ts', '!**/*.test.ts'], unbundle: true, format: ['esm'], dts: true, }) ``` This will output files matching the source structure: - `src/index.ts` → `dist/index.mjs` - `src/utils/helper.ts` → `dist/utils/helper.mjs` ## Tips 1. **Use glob patterns** for multiple related files 2. **Use object syntax** for custom output names 3. **Exclude test files** with negation patterns `!**/*.test.ts` 4. **Combine with unbundle** to preserve directory structure 5. **Use named entries** for better control over output filenames ================================================ FILE: .agents/skills/tsdown/references/option-lint.md ================================================ # Package Validation (publint & attw) Validate your package configuration and type declarations before publishing. ## Overview tsdown integrates with [publint](https://publint.dev/) and [Are the types wrong?](https://arethetypeswrong.github.io/) (attw) to catch common packaging issues. Both are optional dependencies. ## Installation ```bash # publint only npm install -D publint # attw only npm install -D @arethetypeswrong/core # both npm install -D publint @arethetypeswrong/core ``` ## publint Checks that `package.json` fields (`exports`, `main`, `module`, `types`) match your actual output files. ### Enable ```ts export default defineConfig({ publint: true, }) ``` ### Configuration ```ts export default defineConfig({ publint: { level: 'error', // 'warning' | 'error' | 'suggestion' }, }) ``` ### CLI ```bash tsdown --publint ``` ## attw (Are the types wrong?) Verifies TypeScript declarations are correct across different module resolution strategies (`node10`, `node16`, `bundler`). ### Enable ```ts export default defineConfig({ attw: true, }) ``` ### Configuration ```ts export default defineConfig({ attw: { profile: 'node16', // 'strict' | 'node16' | 'esm-only' level: 'error', // 'warn' | 'error' ignoreRules: ['false-cjs', 'cjs-resolves-to-esm'], }, }) ``` ### Profiles | Profile | Description | |---------|-------------| | `strict` | Requires all resolutions to pass (default) | | `node16` | Ignores `node10` resolution failures | | `esm-only` | Ignores `node10` and `node16-cjs` resolution failures | ### Ignore Rules Suppress specific problem types with `ignoreRules`: | Rule | Description | |------|-------------| | `no-resolution` | Module could not be resolved | | `untyped-resolution` | Resolution succeeded but has no types | | `false-cjs` | Types indicate CJS but implementation is ESM | | `false-esm` | Types indicate ESM but implementation is CJS | | `cjs-resolves-to-esm` | CJS resolution points to an ESM module | | `fallback-condition` | A fallback/wildcard condition was used | | `cjs-only-exports-default` | CJS module only exports a default | | `named-exports` | Named exports mismatch between types and implementation | | `false-export-default` | Types declare a default export that doesn't exist | | `missing-export-equals` | Types are missing `export =` for CJS | | `unexpected-module-syntax` | File uses unexpected module syntax | | `internal-resolution-error` | Internal resolution error in type checking | ### CLI ```bash tsdown --attw ``` ## CI Integration Both tools support CI-aware options: ```ts export default defineConfig({ publint: 'ci-only', attw: { enabled: 'ci-only', profile: 'node16', level: 'error', }, }) ``` Both tools require a `package.json` in your project directory. ## Related Options - [CI Environment](advanced-ci.md) - CI-aware option details - [Package Exports](option-package-exports.md) - Auto-generate exports field ================================================ FILE: .agents/skills/tsdown/references/option-log-level.md ================================================ # Log Level Control the verbosity of build output. ## Overview The `logLevel` option controls how much information tsdown displays during the build process. ## Type ```ts logLevel?: 'silent' | 'error' | 'warn' | 'info' ``` **Default:** `'info'` ## Basic Usage ### CLI ```bash # Suppress all output tsdown --log-level silent # Only show errors tsdown --log-level error # Show warnings and errors tsdown --log-level warn # Show all info (default) tsdown --log-level info ``` ### Config File ```ts export default defineConfig({ entry: ['src/index.ts'], logLevel: 'error', }) ``` ## Available Levels | Level | Shows | Use Case | |-------|-------|----------| | `silent` | Nothing | CI/CD pipelines, scripting | | `error` | Errors only | Minimal output | | `warn` | Warnings + errors | Standard CI/CD | | `info` | All messages | Development (default) | ## Common Patterns ### CI/CD Pipeline ```ts export default defineConfig({ entry: ['src/index.ts'], logLevel: 'error', // Only show errors in CI }) ``` ### Scripting ```ts export default defineConfig({ entry: ['src/index.ts'], logLevel: 'silent', // No output for automation }) ``` ## Fail on Warnings The `failOnWarn` option controls whether warnings cause the build to exit with a non-zero code. Defaults to `'ci-only'` — warnings fail the build in CI but not locally. ```ts export default defineConfig({ failOnWarn: 'ci-only', // Default: fail on warnings only in CI // failOnWarn: true, // Always fail on warnings // failOnWarn: false, // Never fail on warnings }) ``` See [CI Environment](advanced-ci.md) for more about CI-aware options. ## Related Options - [CI Environment](advanced-ci.md) - CI-aware option details - [CLI Reference](reference-cli.md) - All CLI options - [Config File](option-config-file.md) - Configuration setup ================================================ FILE: .agents/skills/tsdown/references/option-minification.md ================================================ # Minification Compress code to reduce bundle size. ## Overview Minification removes unnecessary characters (whitespace, comments) and optimizes code for production, reducing bundle size and improving load times. **Note:** Uses [Oxc minifier](https://oxc.rs/docs/contribute/minifier) internally. The minifier is currently in alpha. ## Type ```ts minify?: boolean | 'dce-only' | MinifyOptions ``` - `true` — Enable full minification (whitespace removal, mangling, compression) - `false` — Disable minification (default) - `'dce-only'` — Only perform dead code elimination without full minification - `MinifyOptions` — Pass detailed options to the Oxc minifier ## Basic Usage ### CLI ```bash # Enable minification tsdown --minify # Disable minification tsdown --no-minify ``` **Note:** The CLI `--minify` flag is a boolean toggle. For `'dce-only'` mode or advanced options, use the config file. ### Config File ```ts export default defineConfig({ entry: ['src/index.ts'], minify: true, }) ``` ### DCE-Only Mode Remove dead code without full minification (keeps readable output): ```ts export default defineConfig({ entry: ['src/index.ts'], minify: 'dce-only', }) ``` ## Example Output ### Without Minification ```js // dist/index.mjs const x = 1 function hello(x$1) { console.log('Hello World') console.log(x$1) } hello(x) ``` ### With Minification ```js // dist/index.mjs const e=1;function t(e){console.log(`Hello World`),console.log(e)}t(e); ``` ## Common Patterns ### Production Build ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], minify: true, clean: true, }) ``` ### Conditional Minification ```ts export default defineConfig((options) => ({ entry: ['src/index.ts'], format: ['esm'], minify: !options.watch, // Only minify in production })) ``` ### Browser Library ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['iife'], platform: 'browser', globalName: 'MyLib', minify: true, }) ``` ### Multiple Builds ```ts export default defineConfig([ // Development build { entry: ['src/index.ts'], format: ['esm'], minify: false, outDir: 'dist/dev', }, // Production build { entry: ['src/index.ts'], format: ['esm'], minify: true, outDir: 'dist/prod', }, ]) ``` ## CLI Examples ```bash # Production build with minification tsdown --minify --clean # Multiple formats with minification tsdown --format esm --format cjs --minify # Conditional minification (only when not watching) tsdown --minify # Or omit --watch ``` ## Tips 1. **Use `minify: true`** for production builds 2. **Use `'dce-only'`** to remove dead code while keeping output readable 3. **Skip minification** during development for faster rebuilds 4. **Combine with tree shaking** for best results 5. **Test minified output** thoroughly (Oxc minifier is in alpha) ## Troubleshooting ### Minified Code Has Bugs Oxc minifier is in alpha and may have issues: 1. **Use DCE-only mode**: `minify: 'dce-only'` 2. **Report bug** to [Oxc project](https://github.com/oxc-project/oxc/issues) 3. **Disable minification**: `minify: false` ### Unexpected Output - **Test unminified** first to isolate issue - **Check source maps** for debugging - **Verify target compatibility** ## Related Options - [Tree Shaking](option-tree-shaking.md) - Remove unused code - [Target](option-target.md) - Syntax transformations - [Output Format](option-output-format.md) - Module formats - [Sourcemap](option-sourcemap.md) - Debug information ================================================ FILE: .agents/skills/tsdown/references/option-output-directory.md ================================================ # Output Directory Configure the output directory for bundled files. ## Overview By default, tsdown outputs bundled files to the `dist` directory. You can customize this location using the `outDir` option. ## Basic Usage ### CLI ```bash # Default output to dist/ tsdown # Custom output directory tsdown --out-dir build tsdown -d lib ``` ### Config File ```ts export default defineConfig({ entry: ['src/index.ts'], outDir: 'build', }) ``` ## Common Patterns ### Standard Library ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], outDir: 'dist', // Default dts: true, }) ``` **Output:** ``` dist/ ├── index.mjs ├── index.cjs └── index.d.ts ``` ### Separate Directories by Format ```ts export default defineConfig([ { entry: ['src/index.ts'], format: ['esm'], outDir: 'dist/esm', }, { entry: ['src/index.ts'], format: ['cjs'], outDir: 'dist/cjs', }, ]) ``` **Output:** ``` dist/ ├── esm/ │ └── index.js └── cjs/ └── index.js ``` ### Monorepo Package ```ts export default defineConfig({ entry: ['src/index.ts'], outDir: 'lib', // Custom directory clean: true, }) ``` ### Build to Root ```ts export default defineConfig({ entry: ['src/index.ts'], outDir: '.', // Output to project root (not recommended) clean: false, // Don't clean root! }) ``` **Warning:** Be careful when outputting to root to avoid deleting important files. ## Output Extensions ### Custom Extensions Use `outExtensions` to control file extensions: ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], outDir: 'dist', outExtensions({ format }) { return { js: format === 'esm' ? '.mjs' : '.cjs', } }, }) ``` ### Default Extensions | Format | Default Extension | With `type: "module"` | |--------|-------------------|----------------------| | `esm` | `.mjs` | `.js` | | `cjs` | `.cjs` | `.js` | | `iife` | `.global.js` | `.global.js` | | `umd` | `.umd.js` | `.umd.js` | ### ESM with .js Extension ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], outExtensions: () => ({ js: '.js' }), }) ``` Requires `"type": "module"` in package.json. ## File Naming ### Entry Names Control output filenames based on entry names: ```ts export default defineConfig({ entry: { index: 'src/index.ts', utils: 'src/utils.ts', }, outDir: 'dist', }) ``` **Output:** ``` dist/ ├── index.mjs └── utils.mjs ``` ### Glob Entry ```ts export default defineConfig({ entry: ['src/**/*.ts', '!**/*.test.ts'], outDir: 'dist', unbundle: true, // Preserve structure }) ``` **Output:** ``` dist/ ├── index.mjs ├── utils/ │ └── helper.mjs └── components/ └── button.mjs ``` ## Multiple Builds ### Same Output Directory ```ts export default defineConfig([ { entry: ['src/index.ts'], outDir: 'dist', clean: true, // Clean first }, { entry: ['src/cli.ts'], outDir: 'dist', clean: false, // Don't clean again }, ]) ``` ### Different Output Directories ```ts export default defineConfig([ { entry: ['src/index.ts'], format: ['esm', 'cjs'], outDir: 'dist/lib', }, { entry: ['src/cli.ts'], format: ['esm'], outDir: 'dist/bin', }, ]) ``` ## CLI Examples ```bash # Default tsdown # Custom directory tsdown --out-dir build tsdown -d lib # Nested directory tsdown --out-dir dist/lib # With other options tsdown --out-dir build --format esm,cjs --dts ``` ## Tips 1. **Use default `dist`** for standard projects 2. **Be careful with root** - avoid `outDir: '.'` 3. **Clean before build** - use `clean: true` 4. **Consistent naming** - match your project conventions 5. **Separate by format** if needed for clarity 6. **Check .gitignore** - ensure output dir is ignored ## Troubleshooting ### Files Not in Expected Location - Check `outDir` config - Verify build completed successfully - Look for typos in path ### Files Deleted Unexpectedly - Check if `clean: true` - Ensure outDir doesn't overlap with source - Don't use root as outDir ### Permission Errors - Check write permissions - Ensure directory isn't locked - Try different location ## Related Options - [Cleaning](option-cleaning.md) - Clean output directory - [Entry](option-entry.md) - Entry points - [Output Format](option-output-format.md) - Module formats - [Unbundle](option-unbundle.md) - Preserve structure ================================================ FILE: .agents/skills/tsdown/references/option-output-format.md ================================================ # Output Format Configure the module format(s) for generated bundles. ## Overview tsdown can generate bundles in multiple formats. Default is ESM. ## Available Formats | Format | Description | Use Case | |--------|-------------|----------| | `esm` | ECMAScript Module (default) | Modern Node.js, browsers, Deno | | `cjs` | CommonJS | Legacy Node.js, require() | | `iife` | Immediately Invoked Function Expression | Browser ` ``` ### Export Components ```ts // src/index.ts export { default as Button } from './Button.vue' export { default as Input } from './Input.vue' export { default as Modal } from './Modal.vue' // Re-export types export type { ButtonProps } from './Button.vue' ``` ## Common Patterns ### Component Library ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], platform: 'neutral', external: ['vue'], plugins: [ Vue({ isProduction: true, style: { trim: true, }, }), ], dts: { vue: true, }, clean: true, }) ``` ### Multiple Components ```ts export default defineConfig({ entry: { index: 'src/index.ts', Button: 'src/Button.vue', Input: 'src/Input.vue', Modal: 'src/Modal.vue', }, format: ['esm', 'cjs'], external: ['vue'], plugins: [Vue({ isProduction: true })], dts: { vue: true }, }) ``` ### With Composition Utilities ```ts // src/composables/useCounter.ts import { ref } from 'vue' export function useCounter(initial = 0) { const count = ref(initial) const increment = () => count.value++ const decrement = () => count.value-- return { count, increment, decrement } } ``` ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], external: ['vue'], plugins: [Vue({ isProduction: true })], dts: { vue: true }, }) ``` ### TypeScript Configuration ```json // tsconfig.json { "compilerOptions": { "target": "ES2020", "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "jsx": "preserve", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "strict": true, "isolatedDeclarations": true, "skipLibCheck": true }, "include": ["src"], "exclude": ["node_modules", "dist"] } ``` ### Package.json Configuration ```json { "name": "my-vue-library", "version": "1.0.0", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" }, }, "files": ["dist"], "peerDependencies": { "vue": "^3.0.0" }, "devDependencies": { "tsdown": "^0.9.0", "typescript": "^5.0.0", "unplugin-vue": "^5.0.0", "vue": "^3.4.0", "vue-tsc": "^2.0.0" } } ``` ## Advanced Patterns ### With Vite Plugins Some Vite Vue plugins may work: ```ts import Vue from 'unplugin-vue/rolldown' import Components from 'unplugin-vue-components/rolldown' export default defineConfig({ entry: ['src/index.ts'], external: ['vue'], plugins: [ Vue({ isProduction: true }), Components({ dts: 'src/components.d.ts', }), ], dts: { vue: true }, }) ``` ### JSX Support ```ts export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], external: ['vue'], plugins: [ Vue({ isProduction: true, script: { propsDestructure: true, }, }), ], inputOptions: { transform: { jsx: 'automatic', jsxImportSource: 'vue', }, }, dts: { vue: true }, }) ``` ### Monorepo Vue Packages ```ts export default defineConfig({ workspace: 'packages/*', entry: ['src/index.ts'], format: ['esm', 'cjs'], external: ['vue', /^@mycompany\//], plugins: [Vue({ isProduction: true })], dts: { vue: true }, }) ``` ## Plugin Options ### unplugin-vue Options ```ts Vue({ isProduction: true, script: { defineModel: true, propsDestructure: true, }, style: { trim: true, }, template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('custom-'), }, }, }) ``` ## Tips 1. **Always externalize Vue** - Don't bundle Vue itself 2. **Enable vue: true in dts** - For proper type generation 3. **Use platform: 'neutral'** - Maximum compatibility 4. **Install vue-tsc** - Required for type generation 5. **Set isProduction: true** - Optimize for production 6. **Add peer dependency** - Vue as peer dependency ## Troubleshooting ### Type Generation Fails Ensure vue-tsc is installed: ```bash pnpm add -D vue-tsc ``` Enable in config: ```ts dts: { vue: true } ``` ### Component Types Missing Check TypeScript config: ```json { "compilerOptions": { "jsx": "preserve", "moduleResolution": "bundler" } } ``` ### Vue Not Externalized Add to external: ```ts external: ['vue'] ``` ### SFC Compilation Errors Check unplugin-vue version: ```bash pnpm add -D unplugin-vue@latest ``` ## Related - [Plugins](advanced-plugins.md) - Plugin system - [Dependencies](option-dependencies.md) - External packages - [DTS](option-dts.md) - Type declarations - [React Recipe](recipe-react.md) - React component libraries ================================================ FILE: .agents/skills/tsdown/references/recipe-wasm.md ================================================ # WASM Support Bundle WebAssembly modules in your TypeScript/JavaScript project. ## Overview tsdown supports WASM through [`rolldown-plugin-wasm`](https://github.com/sxzz/rolldown-plugin-wasm), enabling direct `.wasm` imports with synchronous and asynchronous instantiation. ## Setup ### Install ```bash pnpm add -D rolldown-plugin-wasm ``` ### Configure ```ts import { wasm } from 'rolldown-plugin-wasm' import { defineConfig } from 'tsdown' export default defineConfig({ entry: ['./src/index.ts'], plugins: [wasm()], }) ``` ### TypeScript Support Add type declarations to `tsconfig.json`: ```jsonc { "compilerOptions": { "types": ["rolldown-plugin-wasm/types"] } } ``` ## Importing WASM Modules ### Direct Import ```ts import { add } from './add.wasm' add(1, 2) ``` ### Async Init Use `?init` query for async initialization: ```ts import init from './add.wasm?init' const instance = await init(imports) // imports optional instance.exports.add(1, 2) ``` ### Sync Init Use `?init&sync` query for synchronous initialization: ```ts import initSync from './add.wasm?init&sync' const instance = initSync(imports) // imports optional instance.exports.add(1, 2) ``` ## wasm-bindgen Support ### Target `bundler` (Recommended) ```ts import { add } from 'some-pkg' add(1, 2) ``` ### Target `web` (Node.js) ```ts import { readFile } from 'node:fs/promises' import init, { add } from 'some-pkg' import wasmUrl from 'some-pkg/add_bg.wasm?url' await init({ module_or_path: readFile(new URL(wasmUrl, import.meta.url)), }) add(1, 2) ``` ### Target `web` (Browser) ```ts import init, { add } from 'some-pkg/add.js' import wasmUrl from 'some-pkg/add_bg.wasm?url' await init({ module_or_path: wasmUrl }) add(1, 2) ``` `nodejs` and `no-modules` wasm-bindgen targets are not supported. ## Plugin Options ```ts wasm({ maxFileSize: 14 * 1024, // Max size for inline (default: 14KB) fileName: '[hash][extname]', // Output file name pattern publicPath: '', // Prefix for non-inlined file paths targetEnv: 'auto', // 'auto' | 'auto-inline' | 'browser' | 'node' }) ``` | Option | Default | Description | |--------|---------|-------------| | `maxFileSize` | `14 * 1024` | Max file size for inlining. Set to `0` to always copy. | | `fileName` | `'[hash][extname]'` | Pattern for emitted WASM files | | `publicPath` | — | Prefix for non-inlined WASM file paths | | `targetEnv` | `'auto'` | `'auto'` detects at runtime; `'browser'` omits Node builtins; `'node'` omits fetch | ## Related Options - [Plugins](advanced-plugins.md) - Plugin system overview - [Platform](option-platform.md) - Target platform configuration ================================================ FILE: .agents/skills/tsdown/references/reference-cli.md ================================================ # CLI Reference Complete reference for tsdown command-line interface. ## Overview All CLI flags can also be set in the config file. CLI flags override config file options. ## Flag Patterns CLI flag mapping rules: - `--foo` sets `foo: true` - `--no-foo` sets `foo: false` - `--foo.bar` sets `foo: { bar: true }` - `--format esm --format cjs` sets `format: ['esm', 'cjs']` CLI flags support both camelCase and kebab-case. For example, `--outDir` and `--out-dir` are equivalent. ## Basic Commands ### Build ```bash # Build with default config tsdown # Build specific files tsdown src/index.ts src/cli.ts # Build with watch mode tsdown --watch ``` ## Configuration ### `--config, -c ` Specify custom config file: ```bash tsdown --config build.config.ts tsdown -c custom-config.js ``` ### `--no-config` Disable config file loading: ```bash tsdown --no-config src/index.ts ``` ### `--config-loader ` Choose config loader (`auto`, `native`, `unrun`): ```bash tsdown --config-loader unrun ``` ### `--tsconfig ` Specify TypeScript config file: ```bash tsdown --tsconfig tsconfig.build.json ``` ## Entry Points ### `[...files]` Specify entry files as arguments: ```bash tsdown src/index.ts src/utils.ts ``` ## Output Options ### `--format ` Output format (`esm`, `cjs`, `iife`, `umd`): ```bash tsdown --format esm tsdown --format esm --format cjs ``` ### `--out-dir, -d ` Output directory: ```bash tsdown --out-dir lib tsdown -d dist ``` ### `--dts` Generate TypeScript declarations: ```bash tsdown --dts ``` ### `--clean` Clean output directory before build: ```bash tsdown --clean ``` ## Build Options ### `--target ` JavaScript target version: ```bash tsdown --target es2020 tsdown --target node18 tsdown --target chrome100 tsdown --no-target # Disable transformations ``` ### `--platform ` Target platform (`node`, `browser`, `neutral`): ```bash tsdown --platform node tsdown --platform browser ``` ### `--minify` Enable minification: ```bash tsdown --minify tsdown --no-minify ``` ### `--sourcemap` Generate source maps: ```bash tsdown --sourcemap tsdown --sourcemap inline ``` ### `--treeshake` Enable/disable tree shaking: ```bash tsdown --treeshake tsdown --no-treeshake ``` ## Dependencies ### `--external ` Mark module as external (not bundled): ```bash tsdown --external react --external react-dom ``` ### `--shims` Add ESM/CJS compatibility shims: ```bash tsdown --shims ``` ## Development ### `--watch, -w [path]` Enable watch mode: ```bash tsdown --watch tsdown -w tsdown --watch src # Watch specific directory ``` ### `--ignore-watch ` Ignore paths in watch mode: ```bash tsdown --watch --ignore-watch test ``` ### `--on-success ` Run command after successful build: ```bash tsdown --watch --on-success "echo Build complete!" ``` ## Environment Variables ### `--env.* ` Set compile-time environment variables: ```bash tsdown --env.NODE_ENV=production --env.API_URL=https://api.example.com ``` Access as `import.meta.env.*` or `process.env.*`. ### `--env-file ` Load environment variables from file: ```bash tsdown --env-file .env.production ``` ### `--env-prefix ` Filter environment variables by prefix (default: `TSDOWN_`): ```bash tsdown --env-file .env --env-prefix APP_ --env-prefix TSDOWN_ ``` ## Assets ### `--copy ` Copy directory to output: ```bash tsdown --copy public tsdown --copy assets --copy static ``` ## Package Management ### `--exports` Auto-generate package.json exports field: ```bash tsdown --exports ``` ### `--publint` Enable package validation: ```bash tsdown --publint ``` ### `--attw` Enable "Are the types wrong" validation: ```bash tsdown --attw ``` ### `--unused` Check for unused dependencies: ```bash tsdown --unused ``` ## Logging ### `--log-level ` Set logging verbosity (`silent`, `error`, `warn`, `info`): ```bash tsdown --log-level error tsdown --log-level warn ``` ### `--report` / `--no-report` Enable/disable build report: ```bash tsdown --no-report # Disable size report tsdown --report # Enable (default) ``` ### `--debug [feat]` Show debug logs: ```bash tsdown --debug tsdown --debug rolldown # Debug specific feature ``` ## Integration ### `--from-vite [vitest]` Extend Vite or Vitest config: ```bash tsdown --from-vite # Use vite.config.* tsdown --from-vite vitest # Use vitest.config.* ``` ## Common Usage Patterns ### Basic Build ```bash tsdown ``` ### Library (ESM + CJS + Types) ```bash tsdown --format esm --format cjs --dts --clean ``` ### Production Build ```bash tsdown --minify --clean --no-report ``` ### Development (Watch) ```bash tsdown --watch --sourcemap ``` ### Browser Bundle (IIFE) ```bash tsdown --format iife --platform browser --minify ``` ### Node.js CLI Tool ```bash tsdown --format esm --platform node --shims ``` ### Monorepo Package ```bash tsdown --clean --dts --exports --publint ``` ### With Environment Variables ```bash tsdown --env-file .env.production --env.BUILD_TIME=$(date +%s) ``` ### Copy Assets ```bash tsdown --copy public --copy assets --clean ``` ## Tips 1. **Use config file** for complex setups 2. **CLI flags override** config file options 3. **Chain multiple formats** for multi-target builds 4. **Use --clean** to avoid stale files 5. **Enable --dts** for TypeScript libraries 6. **Use --watch** during development 7. **Add --on-success** for post-build tasks 8. **Use --exports** to auto-generate package.json fields ## Related Documentation - [Config File](option-config-file.md) - Configuration file options - [Entry](option-entry.md) - Entry point configuration - [Output Format](option-output-format.md) - Format options - [Watch Mode](option-watch-mode.md) - Watch mode details ================================================ FILE: .agents/skills/vercel-composition-patterns/AGENTS.md ================================================ # React Composition Patterns **Version 1.0.0** Engineering January 2026 > **Note:** > This document is mainly for agents and LLMs to follow when maintaining, > generating, or refactoring React codebases using composition. Humans > may also find it useful, but guidance here is optimized for automation > and consistency by AI-assisted workflows. --- ## Abstract Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale. --- ## Table of Contents 1. [Component Architecture](#1-component-architecture) — **HIGH** - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation) - 1.2 [Use Compound Components](#12-use-compound-components) 2. [State Management](#2-state-management) — **MEDIUM** - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui) - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection) - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components) 3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM** - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants) - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props) 4. [React 19 APIs](#4-react-19-apis) — **MEDIUM** - 4.1 [React 19 API Changes](#41-react-19-api-changes) --- ## 1. Component Architecture **Impact: HIGH** Fundamental patterns for structuring components to avoid prop proliferation and enable flexible composition. ### 1.1 Avoid Boolean Prop Proliferation **Impact: CRITICAL (prevents unmaintainable component variants)** Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead. **Incorrect: boolean props create exponential complexity** ```tsx function Composer({ onSubmit, isThread, channelId, isDMThread, dmId, isEditing, isForwarding, }: Props) { return (
{isDMThread ? ( ) : isThread ? ( ) : null} {isEditing ? ( ) : isForwarding ? ( ) : ( )}