@sanity/tsdown-config 0.20.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,66 @@ export default defineConfig({
43
43
  })
44
44
  ```
45
45
 
46
+ ### React Server Components (`reactServer`)
47
+
48
+ React Server Components refuse to load React Compiler output — `react/compiler-runtime` throws
49
+ in the `react-server` environment, since memoization can never pay off for components that
50
+ render exactly once. The React team's guidance for libraries that ship compiled code is to
51
+ [publish two entrypoints](https://github.com/facebook/react/issues/31702): the compiled one for
52
+ the client, and an uncompiled one under the `react-server` export condition.
53
+
54
+ The `reactServer` option of `reactCompiler` (an option of this config, never forwarded to the
55
+ compiler) bakes that pattern in. It's experimental (`@alpha`) and not covered by semver: it can
56
+ change behavior or be removed entirely in a minor version.
57
+
58
+ ```ts
59
+ export default defineConfig({
60
+ tsconfig: 'tsconfig.dist.json',
61
+ reactCompiler: {target: '19', reactServer: true},
62
+ })
63
+ ```
64
+
65
+ Every entry is built twice from the same source, and the only difference is that React Compiler
66
+ auto-memoization is applied to the non-`react-server` output. The uncompiled build lands next to
67
+ the compiled one with `.react-server` inserted before the extension (`dist/index.js` ↔
68
+ `dist/index.react-server.js`), only the compiled build emits the `.d.ts` files, and — when the
69
+ [`exports` feature](#exports) is enabled — every entry export gains a `react-server` condition,
70
+ with a `types` condition specified before it (the `.react-server.` files have no declaration
71
+ siblings, so the explicit `types` condition points every resolution mode — including consumers
72
+ with `react-server` in their `customConditions` — at the compiled build's declarations):
73
+
74
+ ```json
75
+ {
76
+ "exports": {
77
+ ".": {
78
+ "types": "./dist/index.d.ts",
79
+ "react-server": "./dist/index.react-server.js",
80
+ "default": "./dist/index.js"
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ React Server Components resolve the uncompiled build, everything else (client components, SSR,
87
+ plain Node) resolves the compiled one. The `.react-server.` files never become export subpaths of
88
+ their own.
89
+
90
+ Nothing is stripped from either output, so pair `reactServer` with deleting manual
91
+ `useMemo`/`useCallback` calls from the source: server components stop paying for memoization
92
+ that cannot pay off, and client components get the compiler's finer-grained auto-memoization
93
+ instead of the hand-written hooks.
94
+
95
+ `reactServer` is meant for isomorphic libraries that render in both worlds without a
96
+ `'use client'` directive (pure renderers like `@portabletext/react`). Client-only packages that
97
+ ship `'use client'` don't need it — they always load through the `default` condition anyway.
98
+ Also note the general dual-entrypoint caveat: the two conditions are two module instances, so
99
+ module-scope identity (e.g. `createContext`) is not shared between server and client trees —
100
+ which is exactly the boundary React draws for such libraries anyway.
101
+
102
+ With `reactServer` the config resolves to two tsdown configs, so the
103
+ [isolated declarations annotation](#isolated-declarations) becomes
104
+ `satisfies Promise<UserConfig[]>`.
105
+
46
106
  ## styled-components
47
107
 
48
108
  If your package uses `styled-components`, enable the same `styledComponents` transform that `@sanity/pkg-utils` has:
@@ -252,6 +312,8 @@ Without the annotation, type-checking `tsdown.config.ts` with the `@sanity/tscon
252
312
  enable `declaration`) fails with TS2883 in pnpm projects: the inferred type of the default export
253
313
  can only be named through `@sanity/tsdown-config`'s own copy of `tsdown`, which isn't portable.
254
314
  `satisfies Promise<UserConfig>` names the type through your own `tsdown` dependency instead.
315
+ With [`reactCompiler.reactServer`](#react-server-components-reactserver) the config resolves to
316
+ two tsdown configs, so the annotation becomes `satisfies Promise<UserConfig[]>`.
255
317
 
256
318
  Keep the `isolated-declarations` preset scoped to the tsconfig that tsdown builds with (e.g. a
257
319
  `tsconfig.dist.json` that only includes `./src`). If `isolatedDeclarations` covers
package/dist/index.d.mts CHANGED
@@ -53,9 +53,43 @@ interface StyledComponentsOptions {
53
53
  * The typings resolve in userland once `babel-plugin-react-compiler` (an optional peer
54
54
  * dependency, required to use `reactCompiler`) is installed, and always match the installed
55
55
  * version of the compiler.
56
+ *
57
+ * On top of the compiler's own options, `reactServer` opts into the dual React Server
58
+ * Components build — it's handled by this config and never forwarded to
59
+ * `babel-plugin-react-compiler`.
56
60
  * @public
57
61
  */
58
- type ReactCompilerOptions = Partial<PluginOptions>;
62
+ type ReactCompilerOptions = Partial<PluginOptions> & {
63
+ /**
64
+ * Also emit an uncompiled build of every entry (`<name>.react-server.js` next to `<name>.js`),
65
+ * wired to the `react-server` export condition in `package.json`:
66
+ *
67
+ * ```json
68
+ * ".": {
69
+ * "types": "./dist/index.d.ts",
70
+ * "react-server": "./dist/index.react-server.js",
71
+ * "default": "./dist/index.js"
72
+ * }
73
+ * ```
74
+ *
75
+ * React Server Components refuse to load React Compiler output (`react/compiler-runtime`
76
+ * throws in the `react-server` environment), so a library that ships compiled code
77
+ * publishes two entrypoints: the compiled one (the `default` condition) and an uncompiled
78
+ * one (the `react-server` condition) — see
79
+ * https://github.com/facebook/react/issues/31702.
80
+ *
81
+ * Both builds come from the same source, and the only difference is that React Compiler
82
+ * auto-memoization is applied to the non-`react-server` output; nothing is stripped from
83
+ * either output. Pair it with deleting manual `useMemo`/`useCallback` calls from the
84
+ * source: server components stop paying for memoization that can never pay off (they
85
+ * render exactly once), and client components get the compiler's finer-grained
86
+ * memoization instead.
87
+ * @defaultValue false
88
+ * @alpha This option is experimental and not covered by semver: it can change behavior or
89
+ * be removed entirely in a minor version.
90
+ */
91
+ reactServer?: boolean;
92
+ };
59
93
  /**
60
94
  * @public
61
95
  */
@@ -137,6 +171,11 @@ interface PackageOptions extends Pick<UserConfig, "tsconfig" | "entry" | "format
137
171
  * This is the same feature as the `babel: {reactCompiler: true}` and `reactCompilerOptions`
138
172
  * options in `@sanity/pkg-utils`. Unlike `styledComponents` there's no oxc native port of the
139
173
  * React Compiler yet, so `babel-plugin-react-compiler` needs to be installed.
174
+ *
175
+ * The options object also accepts `reactServer: true` (an option of this config, not the
176
+ * compiler), which additionally emits an uncompiled build of every entry wired to the
177
+ * `react-server` export condition, for libraries that render in React Server Components —
178
+ * see {@link ReactCompilerOptions.reactServer}.
140
179
  * @defaultValue false
141
180
  */
142
181
  reactCompiler?: boolean | ReactCompilerOptions;
@@ -167,6 +206,34 @@ interface PackageOptions extends Pick<UserConfig, "tsconfig" | "entry" | "format
167
206
  */
168
207
  vanillaExtract?: boolean | PackageVanillaExtractOptions;
169
208
  }
209
+ /**
210
+ * With `reactCompiler.reactServer` the config resolves to two tsdown configs: the compiled
211
+ * `default` variant and the uncompiled `react-server` variant.
212
+ * @public
213
+ */
214
+ declare function defineConfig(options: PackageOptions & {
215
+ reactCompiler: ReactCompilerOptions & {
216
+ reactServer: true;
217
+ };
218
+ }): Promise<UserConfig[]>;
219
+ /**
220
+ * Without `reactCompiler.reactServer` the config resolves to a single tsdown config.
221
+ * @public
222
+ */
223
+ declare function defineConfig(options: PackageOptions & {
224
+ reactCompiler: ReactCompilerOptions & {
225
+ reactServer?: false;
226
+ };
227
+ }): Promise<UserConfig>;
228
+ /**
229
+ * When TypeScript can't tell whether `reactCompiler.reactServer` is set (a widened `boolean`,
230
+ * e.g. through a `ReactCompilerOptions`-typed variable), the config resolves to either shape —
231
+ * check `Array.isArray` on the result.
232
+ * @public
233
+ */
234
+ declare function defineConfig(options: PackageOptions & {
235
+ reactCompiler: ReactCompilerOptions;
236
+ }): Promise<UserConfig | UserConfig[]>;
170
237
  /**
171
238
  * @public
172
239
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;KA8BY,+BAA+B;;;;;UAM1B;;EAEf;;;;;EAKA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;EAEA;;;;;;;;;KAUU,uBAAuB,QAAQ;;;;UAK1B,uBAAuB,KACtC;;;;;;;;;;;;;EAeA,WAAW;;;;;;;;;;;;;;;;;;;EAmBX,QAAQ;;;;;;;;;;;;;;;EAeR,UAAU;;;;;;;EAOV,YAAY;;;;;;EAMZ,OAAO;;;;;;;;;;EAUP,MAAM;;;;;;;;;;EAUN,0BAA0B;;;;;;;;EAQ1B,6BAA6B;;;;;;;;;;;;;;;;;;EAkB7B,2BAA2B;;;;;iBAMP,aAAa,UAAS,iBAAsB,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;KAgCY,+BAA+B;;;;;UAM1B;;EAEf;;;;;EAKA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;EAEA;;;;;;;;;;;;;KAcU,uBAAuB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BzC;;;;;UAMe,uBAAuB,KACtC;;;;;;;;;;;;;EAeA,WAAW;;;;;;;;;;;;;;;;;;;EAmBX,QAAQ;;;;;;;;;;;;;;;EAeR,UAAU;;;;;;;EAOV,YAAY;;;;;;EAMZ,OAAO;;;;;;;;;;EAUP,MAAM;;;;;;;;;;;;;;;EAeN,0BAA0B;;;;;;;;EAQ1B,6BAA6B;;;;;;;;;;;;;;;;;;EAkB7B,2BAA2B;;;;;;;iBAQb,aACd,SAAS;EAAkB,eAAe;IAAwB;;IACjE,QAAQ;;;;;iBAKK,aACd,SAAS;EAAkB,eAAe;IAAwB;;IACjE,QAAQ;;;;;;;iBAOK,aACd,SAAS;EAAkB,eAAe;IACzC,QAAQ,aAAa;;;;iBAIR,aAAa,UAAU,iBAAiB,QAAQ"}
package/dist/index.mjs CHANGED
@@ -1,10 +1,15 @@
1
+ import path from "node:path";
1
2
  import { detect } from "package-manager-detector/detect";
2
3
  import { defineConfig as defineConfig$1, mergeConfig } from "tsdown";
3
4
  /**
4
5
  * @public
5
6
  */
6
7
  async function defineConfig(options = {}) {
7
- let { entry, tsconfig, dts, define, target, outDir, clean, css } = options, platform = options.platform ?? "neutral", sourcemap = options.sourcemap ?? !0, reactCompiler = options.reactCompiler ?? !1, styledComponents = options.styledComponents ?? !1, report = { gzip: !1 }, format = options.format ?? "esm", inputOptions = {
8
+ let reactCompiler = options.reactCompiler ?? !1;
9
+ return typeof reactCompiler == "object" && reactCompiler.reactServer === !0 ? Promise.all([resolvePackageConfig(options, "default"), resolvePackageConfig(options, "react-server")]) : resolvePackageConfig(options, "standalone");
10
+ }
11
+ async function resolvePackageConfig(options, variant) {
12
+ let { entry, tsconfig, define, target, outDir, css } = options, isReactServer = variant === "react-server", dts = !isReactServer && options.dts, clean = !isReactServer && options.clean, publint = !isReactServer, platform = options.platform ?? "neutral", sourcemap = options.sourcemap ?? !0, reactCompiler = isReactServer ? !1 : options.reactCompiler ?? !1, styledComponents = options.styledComponents ?? !1, report = { gzip: !1 }, format = options.format ?? "esm", inputOptions = {
8
13
  preserveEntrySignatures: "strict",
9
14
  experimental: { attachDebugInfo: "none" },
10
15
  ...platform === "neutral" && { resolve: { mainFields: ["module", "main"] } },
@@ -20,8 +25,8 @@ async function defineConfig(options = {}) {
20
25
  ...neverBundle === void 0 ? {} : { neverBundle }
21
26
  }, plugins = [];
22
27
  if (reactCompiler !== !1) {
23
- let [{ default: pluginBabel }, { reactCompilerPreset }] = await Promise.all([import("@rolldown/plugin-babel"), import("@vitejs/plugin-react")]);
24
- plugins.push(await pluginBabel({ presets: [reactCompilerPreset(typeof reactCompiler == "object" ? reactCompiler : {})] }));
28
+ let [{ default: pluginBabel }, { reactCompilerPreset }] = await Promise.all([import("@rolldown/plugin-babel"), import("@vitejs/plugin-react")]), { reactServer: _reactServer, ...reactCompilerOptions } = typeof reactCompiler == "object" ? reactCompiler : {};
29
+ plugins.push(await pluginBabel({ presets: [reactCompilerPreset(reactCompilerOptions)] }));
25
30
  }
26
31
  if (options.vanillaExtract) {
27
32
  let { esbuildTargetToLightningCSS, vanillaExtractPlugin } = await import("@sanity/vanilla-extract-tsdown-plugin"), vanillaExtract = options.vanillaExtract === !0 ? {} : options.vanillaExtract, cssTarget = vanillaExtract.target ?? target, { lightningcss } = vanillaExtract;
@@ -43,10 +48,14 @@ async function defineConfig(options = {}) {
43
48
  lightningcss
44
49
  }));
45
50
  }
46
- let { exports } = mergeConfig({ exports: {
47
- enabled: "local-only",
48
- ...(await detect({ cwd: process.cwd() }))?.name === "pnpm" && { devExports: !0 }
49
- } }, { exports: options.exports });
51
+ let exports = !1;
52
+ if (!isReactServer) {
53
+ let packageManager = await detect({ cwd: process.cwd() });
54
+ ({exports} = mergeConfig({ exports: {
55
+ enabled: "local-only",
56
+ ...packageManager?.name === "pnpm" && { devExports: !0 }
57
+ } }, { exports: options.exports })), variant === "default" && exports && (exports = withReactServerExports(exports));
58
+ }
50
59
  return defineConfig$1({
51
60
  checks: { circularDependency: !0 },
52
61
  clean,
@@ -59,9 +68,10 @@ async function defineConfig(options = {}) {
59
68
  format,
60
69
  inputOptions,
61
70
  outDir,
71
+ outExtensions: isReactServer ? reactServerOutExtensions : void 0,
62
72
  platform,
63
73
  plugins,
64
- publint: !0,
74
+ publint,
65
75
  report,
66
76
  sourcemap,
67
77
  target,
@@ -76,6 +86,113 @@ async function defineConfig(options = {}) {
76
86
  }
77
87
  });
78
88
  }
89
+ /**
90
+ * The `outExtensions` of the `react-server` variant: tsdown's default extension for the
91
+ * format and package type (see `resolveJsOutputExtension` in tsdown), with `.react-server` inserted
92
+ * so the variant's entries sit next to the compiled ones (`index.js` ↔ `index.react-server.js`,
93
+ * `index.cjs` ↔ `index.react-server.cjs`, and `.mjs` accordingly for CommonJS packages).
94
+ */
95
+ const reactServerOutExtensions = ({ format, pkgType }) => ({ js: format === "cjs" ? pkgType === "module" ? ".react-server.cjs" : ".react-server.js" : pkgType === "module" ? ".react-server.js" : ".react-server.mjs" });
96
+ /**
97
+ * Wires the `react-server` conditions into tsdown's `exports` feature by composing into
98
+ * `exports.customExports` (the same composition `@sanity/vanilla-extract-tsdown-plugin` uses
99
+ * for its conditional CSS export): a pre-existing `customExports` applies first — both its
100
+ * function and record forms, mirroring how tsdown itself applies them — and the
101
+ * `react-server` conditions are inserted into the result. The composed function runs for both
102
+ * the local `exports` map and `publishConfig.exports`; with `devExports` the local map points
103
+ * at source files, which the entry-file matching leaves untouched (source resolves for every
104
+ * condition in development, which is correct — uncompiled source is exactly what the
105
+ * `react-server` condition ships).
106
+ */
107
+ function withReactServerExports(exportsOption) {
108
+ let exportsOptions = exportsOption === !0 ? {} : typeof exportsOption == "string" ? { enabled: exportsOption } : exportsOption, previousCustomExports = exportsOptions.customExports;
109
+ return exportsOptions.customExports = async (exportsMap, context) => addReactServerConditions(typeof previousCustomExports == "function" ? await previousCustomExports(exportsMap, context) : previousCustomExports ? {
110
+ ...exportsMap,
111
+ ...previousCustomExports
112
+ } : exportsMap, context), exportsOptions;
113
+ }
114
+ /** Matches the JS output extensions tsdown emits for `es`/`cjs` chunks. */
115
+ const RE_JS_FILE = /\.(m?js|cjs)$/, RUNTIME_CONDITIONS = /* @__PURE__ */ new Set([
116
+ "import",
117
+ "require",
118
+ "default"
119
+ ]);
120
+ /**
121
+ * Inserts a `react-server` condition into every entry export of an `exports`-shaped map,
122
+ * pointing at the `.react-server.` sibling emitted by the `react-server` variant — e.g.
123
+ * `"./dist/index.js"` becomes
124
+ * `{"types": "./dist/index.d.ts", "react-server": "./dist/index.react-server.js", "default": "./dist/index.js"}`.
125
+ *
126
+ * The `react-server` variant emits no `.d.ts` siblings, so TypeScript's adjacent-file lookup
127
+ * cannot resolve types for `./dist/index.react-server.js` — a `types` condition pointing at
128
+ * the compiled variant's declarations is specified before `react-server` instead (conditions
129
+ * match in order, so it covers every resolution mode). It's only added when the declarations
130
+ * actually exist among the compiled variant's chunks, and an already-present `types`
131
+ * condition is left in place.
132
+ *
133
+ * Entries are recognized by comparing export targets against the compiled variant's entry
134
+ * chunks — reconstructed in the exact `./<outDir relative to the package root>/<fileName>`
135
+ * form tsdown writes into the exports map, so entries that share a leaf name (`index.js` vs
136
+ * `features/index.js`) can never shadow each other, and non-entry exports (`./package.json`,
137
+ * the conditional `./bundle.css` export of `vanillaExtract`, `devExports` source paths like
138
+ * `./src/index.ts`) pass through untouched. The `react-server` files themselves never enter
139
+ * the map — the `react-server` variant builds with `exports: false` — so they don't become
140
+ * export subpaths of their own.
141
+ */
142
+ function addReactServerConditions(exportsMap, context) {
143
+ let pkg = context.pkg, pkgRoot = path.dirname(pkg.packageJsonPath ?? "package.json"), entryFiles = /* @__PURE__ */ new Set(), dtsFiles = /* @__PURE__ */ new Set();
144
+ for (let [format, formatChunks] of Object.entries(context.chunks)) if (format === "es" || format === "cjs") for (let chunk of formatChunks) {
145
+ if (chunk.type !== "chunk") continue;
146
+ let target = exportTarget(pkgRoot, chunk.outDir, chunk.fileName);
147
+ chunk.isEntry && RE_JS_FILE.test(chunk.fileName) ? entryFiles.add(target) : RE_DTS_FILE.test(chunk.fileName) && dtsFiles.add(target);
148
+ }
149
+ let isEntryFile = (value) => typeof value == "string" && entryFiles.has(value), dtsFor = (value) => {
150
+ let dtsFile = toDtsFile(value);
151
+ return dtsFiles.has(dtsFile) ? dtsFile : void 0;
152
+ }, result = {};
153
+ for (let [key, value] of Object.entries(exportsMap)) result[key] = withReactServerCondition(value, isEntryFile, dtsFor);
154
+ return result;
155
+ }
156
+ /**
157
+ * Mirrors tsdown's own `join` for exports targets:
158
+ * `./<outDir relative to the package root>/<fileName>`, with POSIX separators.
159
+ */
160
+ function exportTarget(pkgRoot, outDir, fileName) {
161
+ let outDirRelative = path.relative(pkgRoot, outDir).replaceAll("\\", "/");
162
+ return `${outDirRelative ? `./${outDirRelative}` : "."}/${fileName.replaceAll("\\", "/")}`;
163
+ }
164
+ function withReactServerCondition(value, isEntryFile, dtsFor) {
165
+ if (isEntryFile(value)) {
166
+ let types = dtsFor(value);
167
+ return {
168
+ ...types === void 0 ? {} : { types },
169
+ "react-server": toServerFile(value),
170
+ default: value
171
+ };
172
+ }
173
+ if (typeof value != "object" || !value || Array.isArray(value)) return value;
174
+ let conditions = Object.entries(value), matched = conditions.filter((entry) => RUNTIME_CONDITIONS.has(entry[0]) && isEntryFile(entry[1])), [firstMatch] = matched;
175
+ if (!firstMatch) return value;
176
+ let hasTypes = conditions.some(([condition]) => condition === "types"), missingTypes = hasTypes ? void 0 : dtsFor(firstMatch[1]), serverTarget = matched.length === 1 ? toServerFile(firstMatch[1]) : Object.fromEntries(matched.map(([condition, target]) => {
177
+ let types = hasTypes ? void 0 : dtsFor(target);
178
+ return [condition, types === void 0 ? toServerFile(target) : {
179
+ types,
180
+ default: toServerFile(target)
181
+ }];
182
+ })), next = {}, inserted = !1;
183
+ for (let [condition, target] of conditions) !inserted && RUNTIME_CONDITIONS.has(condition) && (matched.length === 1 && missingTypes !== void 0 && (next.types = missingTypes), next["react-server"] = serverTarget, inserted = !0), next[condition] = target;
184
+ return inserted || (next["react-server"] = serverTarget), next;
185
+ }
186
+ /** Matches the declaration file extensions tsdown emits next to `es`/`cjs` chunks. */
187
+ const RE_DTS_FILE = /\.d\.(ts|mts|cts)$/;
188
+ /** `./dist/index.js` → `./dist/index.react-server.js` (and `.mjs`/`.cjs` accordingly). */
189
+ function toServerFile(file) {
190
+ return file.replace(RE_JS_FILE, ".react-server.$1");
191
+ }
192
+ /** `./dist/index.js` → `./dist/index.d.ts` (`.mjs` → `.d.mts`, `.cjs` → `.d.cts`). */
193
+ function toDtsFile(file) {
194
+ return file.replace(RE_JS_FILE, (extension) => extension === ".mjs" ? ".d.mts" : extension === ".cjs" ? ".d.cts" : ".d.ts");
195
+ }
79
196
  export { defineConfig };
80
197
 
81
198
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["defineTsdownConfig"],"sources":["../src/index.ts"],"sourcesContent":["import type {Options as VanillaExtractPluginOptions} from '@sanity/vanilla-extract-tsdown-plugin'\nimport type {PluginOptions as ReactCompilerPluginOptions} from 'babel-plugin-react-compiler'\nimport {detect} from 'package-manager-detector/detect'\nimport {\n defineConfig as defineTsdownConfig,\n mergeConfig,\n type Rolldown,\n type UserConfig,\n} from 'tsdown'\n\n/**\n * Options for the `vanillaExtract` option — the same options as\n * `@sanity/vanilla-extract-tsdown-plugin` (`identifiers`, `fileName`, `minify`, `target`,\n * `lightningcss`, and `inject`, all modeled after the `css` options of `@tsdown/css`), with\n * three Sanity-flavored defaults on top:\n *\n * - `inject` defaults to `{nodeCompat: true}` (instead of the plugin's `false`), wiring up the\n * conditional CSS export pattern that Sanity libraries ship with. Set `inject: true` for a\n * plain relative CSS import, or `inject: false` to only extract the CSS.\n * - `minify` defaults to `true` (instead of the plugin's `false`, which matches `css.minify`\n * in `@tsdown/css`): published Sanity libraries ship minified CSS. Set `minify: false` for\n * readable output.\n * - When the effective CSS syntax lowering target (`target`, falling back to the top-level\n * `target`) is undefined or names no browsers (e.g. `'node20'`, resolved from\n * `engines.node`), the lowering targets are resolved from `@sanity/browserslist-config` and\n * passed through `lightningcss.targets` — where the plugin (like `@tsdown/css`) would skip\n * lowering. `target: false` stays the explicit off switch, and a user-provided\n * `lightningcss.targets` wins over the fallback.\n * @public\n */\nexport type PackageVanillaExtractOptions = VanillaExtractPluginOptions\n\n/**\n * Options for the `styled-components` transform, the same options as `babel-plugin-styled-components`.\n * @public\n */\nexport interface StyledComponentsOptions {\n /** @defaultValue true */\n displayName?: boolean\n /**\n * @defaultValue []\n * @example [\"\\@xstyled/styled-components\", \"\\@xstyled/styled-components/*\"]\n */\n topLevelImportPaths?: string[]\n /** @defaultValue true */\n ssr?: boolean\n /** @defaultValue false */\n fileName?: boolean\n /** @defaultValue [\"index\"] */\n meaninglessFileNames?: string[]\n /** @defaultValue true */\n minify?: boolean\n /** @defaultValue false */\n transpileTemplateLiterals?: boolean\n namespace?: string\n /** @defaultValue true */\n pure?: boolean\n}\n\n/**\n * Options for the React Compiler, the same options as `babel-plugin-react-compiler`.\n * The typings resolve in userland once `babel-plugin-react-compiler` (an optional peer\n * dependency, required to use `reactCompiler`) is installed, and always match the installed\n * version of the compiler.\n * @public\n */\nexport type ReactCompilerOptions = Partial<ReactCompilerPluginOptions>\n\n/**\n * @public\n */\nexport interface PackageOptions extends Pick<\n UserConfig,\n 'tsconfig' | 'entry' | 'format' | 'dts' | 'define' | 'target' | 'outDir'\n> {\n /**\n * @defaultValue 'neutral'\n *\n * When left at `'neutral'` (the default), two resolution tweaks compensate for rolldown's\n * strict neutral defaults: `deps.neverBundle` includes `/^node:/` (node built-ins stay external\n * so neutral does not warn about them as unresolvable; only node runtimes execute those code\n * paths), and `inputOptions.resolve.mainFields` falls back to `['module', 'main']` for inlined\n * dependencies that ship no `exports` map (e.g. `rxjs-etc/operators`). Prefer `'neutral'` over\n * `'node'` for packages that also run in the browser - `'node'` makes CommonJS-interop emit a\n * module-scope `createRequire(import.meta.url)` for inlined CJS deps, which crashes\n * browser-bundled consumers.\n */\n platform?: UserConfig['platform']\n /**\n * Clean directories before each build. Prefer an array of folders over a separate `\"clean\"`\n * script in `package.json` (e.g. `rimraf dist coverage`) — tsdown removes them as part of\n * `tsdown` / `pnpm build`, so packages don't need `rimraf`, a `clean` script, or\n * `prebuild`/`run-s clean build` wiring.\n *\n * - `true` (tsdown's default when left undefined) cleans `outDir` (`'dist'` by default)\n * - `false` skips cleaning\n * - a `string[]` replaces that default with the listed paths/globs — include `outDir` (e.g.\n * `'dist'`) when you still want it cleaned alongside other folders\n *\n * @defaultValue true\n * @example\n * ```ts\n * // Instead of `\"clean\": \"rimraf dist coverage\"` in package.json:\n * clean: ['dist', 'coverage']\n * ```\n */\n clean?: UserConfig['clean']\n /**\n * tsdown's `exports` option, with defaults suited for publishing Sanity libraries:\n * `enabled: 'local-only'` generates the `exports` map during local builds and skips it in CI\n * (where the committed `package.json` is already up to date). When pnpm is detected,\n * `devExports: true` also keeps the local `exports` map pointing at source files while\n * `publishConfig.exports` receives the built files.\n *\n * Userland values apply with tsdown's `mergeConfig` semantics: an object deep-merges over\n * these defaults (so individual fields can be overridden), while any other value - `false`\n * to disable exports generation, or a bare CI condition (`'ci-only'`/`'local-only'`) -\n * replaces them entirely.\n * @defaultValue `{enabled: 'local-only', devExports: true}` for pnpm projects;\n * `{enabled: 'local-only'}` otherwise.\n */\n exports?: UserConfig['exports']\n /**\n * Whether to generate source map files. The same default as the `sourcemap` option in\n * `@sanity/pkg-utils` - tsdown itself defaults to `false` and does not read `sourceMap` from\n * the tsconfig.\n * @defaultValue true\n */\n sourcemap?: UserConfig['sourcemap']\n /**\n * tsdown's `deps` option. When `platform` is `'neutral'` (the default), `neverBundle` always\n * includes `/^node:/` and userland `neverBundle` entries are appended (tsdown's `mergeConfig`\n * would replace the array). Other `deps` fields pass through as-is.\n */\n deps?: UserConfig['deps']\n /**\n * tsdown's experimental [`css` option](https://tsdown.dev/options/css) (CSS modules,\n * preprocessors, Lightning CSS / PostCSS, inject, etc). Passed through as-is — requires\n * [`@tsdown/css`](https://www.npmjs.com/package/@tsdown/css) to be installed in the project.\n * Safe to combine with {@link PackageOptions.vanillaExtract}: vanilla-extract extracts into\n * `bundle.css` by default, while `@tsdown/css` merges other CSS (including `.module.css`)\n * into `style.css`, so the two pipelines do not collide.\n * @see https://tsdown.dev/reference/api/Interface.InlineConfig#css\n */\n css?: UserConfig['css']\n /**\n * Runs `babel-plugin-react-compiler` on the source files before they are bundled, so published\n * components are memoized automatically. Pass `true` to use the defaults, or an options object\n * to configure the compiler (e.g. `{target: '18'}`).\n * This is the same feature as the `babel: {reactCompiler: true}` and `reactCompilerOptions`\n * options in `@sanity/pkg-utils`. Unlike `styledComponents` there's no oxc native port of the\n * React Compiler yet, so `babel-plugin-react-compiler` needs to be installed.\n * @defaultValue false\n */\n reactCompiler?: boolean | ReactCompilerOptions\n /**\n * Applies the `styled-components` transform (`displayName`, `componentId`, CSS minification, etc)\n * with the same defaults as the `babel: {styledComponents: true}` option in `@sanity/pkg-utils`.\n * Unlike `@sanity/pkg-utils` it doesn't use `babel-plugin-styled-components`, but oxc's native port of it,\n * so there's no need to install babel dependencies.\n * @defaultValue false\n */\n styledComponents?: boolean | StyledComponentsOptions\n /**\n * Enables `@sanity/vanilla-extract-tsdown-plugin` to extract CSS into a separate file,\n * minified and lowered with `lightningcss`. The lowering targets come from the effective\n * `target` (`vanillaExtract.target`, falling back to the top-level `target`) when it names\n * browsers; when it's undefined or browserless (e.g. `'node20'`), they're resolved from\n * `@sanity/browserslist-config` instead — see {@link PackageVanillaExtractOptions}. Pass\n * `true` to use the defaults, or an object to customize.\n *\n * By default (`inject: {nodeCompat: true}`) the plugin also injects the self-referential\n * `import \"<pkg>/bundle.css\"`, emits a `bundle-css.js` shim, and writes the conditional\n * `\"./bundle.css\"` export to `package.json` - see {@link PackageVanillaExtractOptions}.\n * This is the same feature as `rollup.vanillaExtract` in `@sanity/pkg-utils`.\n *\n * Combines with {@link PackageOptions.css}: enable both when a package uses vanilla-extract\n * alongside CSS modules (or other `@tsdown/css` features).\n * @alpha\n */\n vanillaExtract?: boolean | PackageVanillaExtractOptions\n}\n\n/**\n * @public\n */\nexport async function defineConfig(options: PackageOptions = {}): Promise<UserConfig> {\n // `tsconfig`, `entry`, `dts`, `define`, `target`, `outDir`, `clean` and `css` are passed\n // through to tsdown as-is. When left undefined, tsdown keeps its default behavior\n // (`tsconfig` is auto-detected from the project, `dts` from `package.json`, `define`\n // replaces nothing, `target` applies no syntax downleveling, `outDir` defaults to `'dist'`,\n // `clean` defaults to `true` — cleaning `outDir` before each build — and `css` stays off\n // unless `@tsdown/css` is installed and the option is set).\n const {entry, tsconfig, dts, define, target, outDir, clean, css} = options\n const platform = options.platform ?? 'neutral'\n const sourcemap = options.sourcemap ?? true\n const reactCompiler = options.reactCompiler ?? false\n const styledComponents = options.styledComponents ?? false\n const report = {gzip: false} as const satisfies UserConfig['report']\n const publint = true\n const format = options.format ?? 'esm'\n // When `platform` is `'neutral'`, restore the conventional `module`/`main` fallback that\n // rolldown's strict neutral defaults drop - needed for inlined deps without an `exports` map.\n const inputOptions = {\n // https://github.com/rolldown/rolldown/blob/main/packages/rolldown/src/options/docs/preserve-entry-signatures.md#strict\n preserveEntrySignatures: 'strict',\n experimental: {attachDebugInfo: 'none'},\n ...(platform === 'neutral' && {\n resolve: {mainFields: ['module', 'main']},\n }),\n ...(styledComponents !== false && {\n transform: {\n plugins: {\n styledComponents: {\n // The same defaults as `babel: {styledComponents: true}` in `@sanity/pkg-utils`:\n // `fileName` is unnecessary, as the way we use styled-components in Sanity is usually by wrapping\n // `@sanity/ui` primitives, not declaring new ones like \"const Button = styled.button``\"\n fileName: false,\n // Native template literals take less space than this transpilation, and unlike\n // `babel-plugin-styled-components`, oxc doesn't add a `@__PURE__` annotation to the\n // transpiled call expression either, so enabling it wouldn't improve tree-shaking\n transpileTemplateLiterals: false,\n // Helps dead code elimination and tree-shaking, although oxc only annotates plain call\n // expressions so far, not tagged template expressions (https://github.com/rollup/rollup/issues/4035)\n pure: true,\n // Disabled, as tsdown tends to be used for npm publishing, while other tooling,\n // like `sanity dev`, `next dev`, etc are used for testing\n cssProp: false,\n ...(typeof styledComponents === 'object' ? styledComponents : {}),\n },\n },\n },\n }),\n } satisfies UserConfig['inputOptions']\n\n // `neverBundle` is concatenated (not `mergeConfig`'d) so per-package externals add to the\n // `/^node:/` default for `platform: 'neutral'` instead of replacing it. Rolldown's\n // `ExternalOption` array form is `Array<string | RegExp>` only; a function override is composed.\n const userNeverBundle = options.deps?.neverBundle\n const nodeBuiltinExternal = platform === 'neutral' ? /^node:/ : undefined\n const neverBundle: NonNullable<UserConfig['deps']>['neverBundle'] =\n userNeverBundle == null\n ? nodeBuiltinExternal && [nodeBuiltinExternal]\n : nodeBuiltinExternal == null || userNeverBundle === true\n ? userNeverBundle\n : typeof userNeverBundle === 'function'\n ? (id, importer, isResolved) =>\n nodeBuiltinExternal.test(id) || userNeverBundle(id, importer, isResolved)\n : Array.isArray(userNeverBundle)\n ? [nodeBuiltinExternal, ...userNeverBundle]\n : [nodeBuiltinExternal, userNeverBundle]\n const deps: UserConfig['deps'] =\n options.deps === undefined && neverBundle === undefined\n ? undefined\n : {\n ...options.deps,\n ...(neverBundle === undefined ? {} : {neverBundle}),\n }\n\n // `outputOptions` is left to tsdown's defaults - notably chunk filenames keep tsdown's hashed\n // default (unless userland sets `hash`), which prevents chunk/entry filename collisions\n // (https://github.com/sanity-io/ui/issues/2262).\n const plugins: Rolldown.Plugin[] = []\n if (reactCompiler !== false) {\n // Follows the official tsdown recipe for the React Compiler:\n // https://tsdown.dev/recipes/react-support#enabling-react-compiler\n // The plugins are lazy loaded so they're only paid for when the React Compiler is enabled.\n // `babel-plugin-react-compiler` itself is resolved by Babel from the consumer package during\n // the build, which is why it can be an optional peer dependency. Once rolldown ships its rust\n // port of the React Compiler this can be swapped out for an oxc transform, like `styledComponents`.\n const [{default: pluginBabel}, {reactCompilerPreset}] = await Promise.all([\n import('@rolldown/plugin-babel'),\n import('@vitejs/plugin-react'),\n ])\n plugins.push(\n await pluginBabel({\n presets: [reactCompilerPreset(typeof reactCompiler === 'object' ? reactCompiler : {})],\n }),\n )\n }\n if (options.vanillaExtract) {\n // Lazy loaded, like `reactCompiler`, so the CSS toolchain is only paid for when the option is\n // enabled. The plugin compiles the `.css.ts` files and extracts the CSS into a single file.\n // Its `inject` option is general purpose (and, like `css.inject` in `@tsdown/css`, disabled\n // by default), so this config supplies the default most Sanity libraries want:\n // `{nodeCompat: true}` wires up the whole conditional CSS export pattern - the\n // self-referential CSS import, the no-op JS shim, and the conditional `./<fileName>` export\n // written through this config's `exports` option (which the plugin's `tsdownConfig` hook\n // composes into).\n const {esbuildTargetToLightningCSS, vanillaExtractPlugin} =\n await import('@sanity/vanilla-extract-tsdown-plugin')\n const vanillaExtract = options.vanillaExtract === true ? {} : options.vanillaExtract\n\n // The plugin follows `@tsdown/css`: without browser targets, CSS syntax lowering is\n // skipped. The extracted CSS always runs in browsers, so when the effective target\n // (`vanillaExtract.target`, falling back to the top-level `target`) is undefined or names\n // no browsers (e.g. `'node20'` - also what tsdown derives from `engines.node`, which\n // speaks to the JS runtime), this config resolves the lowering targets from\n // `@sanity/browserslist-config` and passes them through `lightningcss.targets` instead.\n // `target: false` stays the explicit off switch, and a user-provided\n // `lightningcss.targets` wins over the fallback.\n const cssTarget = vanillaExtract.target ?? target\n let {lightningcss} = vanillaExtract\n if (\n cssTarget !== false &&\n !lightningcss?.targets &&\n (cssTarget === undefined || !esbuildTargetToLightningCSS(cssTarget))\n ) {\n // Lazy loaded as well: `browserslistToTargets` is a pure helper, but `lightningcss` is a\n // native package that only needs to load when the fallback applies\n const [{default: browserslist}, {default: browserslistConfig}, {browserslistToTargets}] =\n await Promise.all([\n import('browserslist'),\n import('@sanity/browserslist-config'),\n import('lightningcss'),\n ])\n lightningcss = {\n ...lightningcss,\n targets: browserslistToTargets(browserslist(browserslistConfig)),\n }\n }\n\n plugins.push(\n vanillaExtractPlugin({\n inject: {nodeCompat: true},\n minify: true,\n ...vanillaExtract,\n lightningcss,\n }),\n )\n }\n\n const packageManager = await detect({cwd: process.cwd()})\n // tsdown's `exports` feature is enabled with Sanity-flavored defaults, and userland values\n // apply with tsdown's own `mergeConfig` semantics: an object deep-merges over the defaults,\n // anything else (`false`, a CI condition) replaces them.\n const {exports} = mergeConfig(\n {\n exports: {\n enabled: 'local-only',\n // Only opt in by default when pnpm is detected: support for replacing package fields\n // from `publishConfig` is not reliable across package managers.\n ...(packageManager?.name === 'pnpm' && {devExports: true}),\n },\n },\n {exports: options.exports},\n )\n\n return defineTsdownConfig({\n // Rolldown defaults `circularDependency` to `false`; enable it so Sanity library builds\n // surface import cycles (bigger bundles / execution-order hazards) as warnings.\n // Override via `mergeConfig(..., {checks: {circularDependency: false}})`.\n // https://rolldown.rs/reference/InputOptions.checks#circulardependency\n checks: {circularDependency: true},\n clean,\n css,\n define,\n deps,\n dts,\n entry,\n exports,\n format,\n inputOptions,\n outDir,\n platform,\n plugins,\n publint,\n report,\n sourcemap,\n target,\n tsconfig,\n // Compress the output (constant folding, dead code elimination) without mangling\n // identifiers or stripping whitespace: consumers' production builds minify `node_modules`\n // again anyway, so full minification here would only hurt debuggability, not final app\n // bundle sizes. `keepNames` preserves the function and class names the compress pass\n // would otherwise strip as unreferenced - e.g. the inner name in\n // `forwardRef(function Button(…) {…})`, which React DevTools reads via `Function.name`.\n // That name is the tree-shakeable alternative to a top-level `Button.displayName = '…'`\n // assignment (a side effect that pins unused components into consumer bundles, see\n // https://github.com/sanity-io/ui/pull/2435), and names stripped at publish time are\n // unrecoverable in userland.\n minify: {\n compress: {keepNames: {function: true, class: true}},\n codegen: false,\n mangle: false,\n },\n })\n}\n"],"mappings":";;;;;AA0LA,eAAsB,aAAa,UAA0B,CAAC,GAAwB;CAOpF,IAAM,EAAC,OAAO,UAAU,KAAK,QAAQ,QAAQ,QAAQ,OAAO,QAAO,SAC7D,WAAW,QAAQ,YAAY,WAC/B,YAAY,QAAQ,aAAa,IACjC,gBAAgB,QAAQ,iBAAiB,IACzC,mBAAmB,QAAQ,oBAAoB,IAC/C,SAAS,EAAC,MAAM,GAAK,GAErB,SAAS,QAAQ,UAAU,OAG3B,eAAe;EAEnB,yBAAyB;EACzB,cAAc,EAAC,iBAAiB,OAAM;EACtC,GAAI,aAAa,aAAa,EAC5B,SAAS,EAAC,YAAY,CAAC,UAAU,MAAM,EAAC,EAC1C;EACA,GAAI,qBAAqB,MAAS,EAChC,WAAW,EACT,SAAS,EACP,kBAAkB;GAIhB,UAAU;GAIV,2BAA2B;GAG3B,MAAM;GAGN,SAAS;GACT,GAAI,OAAO,oBAAqB,WAAW,mBAAmB,CAAC;EACjE,EACF,EACF,EACF;CACF,GAKM,kBAAkB,QAAQ,MAAM,aAChC,sBAAsB,aAAa,YAAY,WAAW,KAAA,GAC1D,cACJ,mBAAmB,OACf,uBAAuB,CAAC,mBAAmB,IAC3C,uBAAuB,QAAQ,oBAAoB,KACjD,kBACA,OAAO,mBAAoB,cACxB,IAAI,UAAU,eACb,oBAAoB,KAAK,EAAE,KAAK,gBAAgB,IAAI,UAAU,UAAU,IAC1E,MAAM,QAAQ,eAAe,IAC3B,CAAC,qBAAqB,GAAG,eAAe,IACxC,CAAC,qBAAqB,eAAe,GAC3C,OACJ,QAAQ,SAAS,KAAA,KAAa,gBAAgB,KAAA,IAC1C,KAAA,IACA;EACE,GAAG,QAAQ;EACX,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAC,YAAW;CACnD,GAKA,UAA6B,CAAC;CACpC,IAAI,kBAAkB,IAAO;EAO3B,IAAM,CAAC,EAAC,SAAS,eAAc,EAAC,yBAAwB,MAAM,QAAQ,IAAI,CACxE,OAAO,2BACP,OAAO,uBACT,CAAC;EACD,QAAQ,KACN,MAAM,YAAY,EAChB,SAAS,CAAC,oBAAoB,OAAO,iBAAkB,WAAW,gBAAgB,CAAC,CAAC,CAAC,EACvF,CAAC,CACH;CACF;CACA,IAAI,QAAQ,gBAAgB;EAS1B,IAAM,EAAC,6BAA6B,yBAClC,MAAM,OAAO,0CACT,iBAAiB,QAAQ,mBAAmB,KAAO,CAAC,IAAI,QAAQ,gBAUhE,YAAY,eAAe,UAAU,QACvC,EAAC,iBAAgB;EACrB,IACE,cAAc,MACd,CAAC,cAAc,YACd,cAAc,KAAA,KAAa,CAAC,4BAA4B,SAAS,IAClE;GAGA,IAAM,CAAC,EAAC,SAAS,gBAAe,EAAC,SAAS,sBAAqB,EAAC,2BAC9D,MAAM,QAAQ,IAAI;IAChB,OAAO;IACP,OAAO;IACP,OAAO;GACT,CAAC;GACH,eAAe;IACb,GAAG;IACH,SAAS,sBAAsB,aAAa,kBAAkB,CAAC;GACjE;EACF;EAEA,QAAQ,KACN,qBAAqB;GACnB,QAAQ,EAAC,YAAY,GAAI;GACzB,QAAQ;GACR,GAAG;GACH;EACF,CAAC,CACH;CACF;CAMA,IAAM,EAAC,YAAW,YAChB,EACE,SAAS;EACP,SAAS;EAGT,IAAI,MAVmB,OAAO,EAAC,KAAK,QAAQ,IAAI,EAAC,CAAC,EAAA,EAU9B,SAAS,UAAU,EAAC,YAAY,GAAI;CAC1D,EACF,GACA,EAAC,SAAS,QAAQ,QAAO,CAC3B;CAEA,OAAOA,eAAmB;EAKxB,QAAQ,EAAC,oBAAoB,GAAI;EACjC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAWA,QAAQ;GACN,UAAU,EAAC,WAAW;IAAC,UAAU;IAAM,OAAO;GAAI,EAAC;GACnD,SAAS;GACT,QAAQ;EACV;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"index.mjs","names":["defineTsdownConfig"],"sources":["../src/index.ts"],"sourcesContent":["import path from 'node:path'\nimport type {Options as VanillaExtractPluginOptions} from '@sanity/vanilla-extract-tsdown-plugin'\nimport type {PluginOptions as ReactCompilerPluginOptions} from 'babel-plugin-react-compiler'\nimport {detect} from 'package-manager-detector/detect'\nimport {\n defineConfig as defineTsdownConfig,\n mergeConfig,\n type PackageJsonWithPath,\n type Rolldown,\n type UserConfig,\n} from 'tsdown'\n\n/**\n * Options for the `vanillaExtract` option — the same options as\n * `@sanity/vanilla-extract-tsdown-plugin` (`identifiers`, `fileName`, `minify`, `target`,\n * `lightningcss`, and `inject`, all modeled after the `css` options of `@tsdown/css`), with\n * three Sanity-flavored defaults on top:\n *\n * - `inject` defaults to `{nodeCompat: true}` (instead of the plugin's `false`), wiring up the\n * conditional CSS export pattern that Sanity libraries ship with. Set `inject: true` for a\n * plain relative CSS import, or `inject: false` to only extract the CSS.\n * - `minify` defaults to `true` (instead of the plugin's `false`, which matches `css.minify`\n * in `@tsdown/css`): published Sanity libraries ship minified CSS. Set `minify: false` for\n * readable output.\n * - When the effective CSS syntax lowering target (`target`, falling back to the top-level\n * `target`) is undefined or names no browsers (e.g. `'node20'`, resolved from\n * `engines.node`), the lowering targets are resolved from `@sanity/browserslist-config` and\n * passed through `lightningcss.targets` — where the plugin (like `@tsdown/css`) would skip\n * lowering. `target: false` stays the explicit off switch, and a user-provided\n * `lightningcss.targets` wins over the fallback.\n * @public\n */\nexport type PackageVanillaExtractOptions = VanillaExtractPluginOptions\n\n/**\n * Options for the `styled-components` transform, the same options as `babel-plugin-styled-components`.\n * @public\n */\nexport interface StyledComponentsOptions {\n /** @defaultValue true */\n displayName?: boolean\n /**\n * @defaultValue []\n * @example [\"\\@xstyled/styled-components\", \"\\@xstyled/styled-components/*\"]\n */\n topLevelImportPaths?: string[]\n /** @defaultValue true */\n ssr?: boolean\n /** @defaultValue false */\n fileName?: boolean\n /** @defaultValue [\"index\"] */\n meaninglessFileNames?: string[]\n /** @defaultValue true */\n minify?: boolean\n /** @defaultValue false */\n transpileTemplateLiterals?: boolean\n namespace?: string\n /** @defaultValue true */\n pure?: boolean\n}\n\n/**\n * Options for the React Compiler, the same options as `babel-plugin-react-compiler`.\n * The typings resolve in userland once `babel-plugin-react-compiler` (an optional peer\n * dependency, required to use `reactCompiler`) is installed, and always match the installed\n * version of the compiler.\n *\n * On top of the compiler's own options, `reactServer` opts into the dual React Server\n * Components build — it's handled by this config and never forwarded to\n * `babel-plugin-react-compiler`.\n * @public\n */\nexport type ReactCompilerOptions = Partial<ReactCompilerPluginOptions> & {\n /**\n * Also emit an uncompiled build of every entry (`<name>.react-server.js` next to `<name>.js`),\n * wired to the `react-server` export condition in `package.json`:\n *\n * ```json\n * \".\": {\n * \"types\": \"./dist/index.d.ts\",\n * \"react-server\": \"./dist/index.react-server.js\",\n * \"default\": \"./dist/index.js\"\n * }\n * ```\n *\n * React Server Components refuse to load React Compiler output (`react/compiler-runtime`\n * throws in the `react-server` environment), so a library that ships compiled code\n * publishes two entrypoints: the compiled one (the `default` condition) and an uncompiled\n * one (the `react-server` condition) — see\n * https://github.com/facebook/react/issues/31702.\n *\n * Both builds come from the same source, and the only difference is that React Compiler\n * auto-memoization is applied to the non-`react-server` output; nothing is stripped from\n * either output. Pair it with deleting manual `useMemo`/`useCallback` calls from the\n * source: server components stop paying for memoization that can never pay off (they\n * render exactly once), and client components get the compiler's finer-grained\n * memoization instead.\n * @defaultValue false\n * @alpha This option is experimental and not covered by semver: it can change behavior or\n * be removed entirely in a minor version.\n */\n reactServer?: boolean\n}\n\n/**\n * @public\n */\nexport interface PackageOptions extends Pick<\n UserConfig,\n 'tsconfig' | 'entry' | 'format' | 'dts' | 'define' | 'target' | 'outDir'\n> {\n /**\n * @defaultValue 'neutral'\n *\n * When left at `'neutral'` (the default), two resolution tweaks compensate for rolldown's\n * strict neutral defaults: `deps.neverBundle` includes `/^node:/` (node built-ins stay external\n * so neutral does not warn about them as unresolvable; only node runtimes execute those code\n * paths), and `inputOptions.resolve.mainFields` falls back to `['module', 'main']` for inlined\n * dependencies that ship no `exports` map (e.g. `rxjs-etc/operators`). Prefer `'neutral'` over\n * `'node'` for packages that also run in the browser - `'node'` makes CommonJS-interop emit a\n * module-scope `createRequire(import.meta.url)` for inlined CJS deps, which crashes\n * browser-bundled consumers.\n */\n platform?: UserConfig['platform']\n /**\n * Clean directories before each build. Prefer an array of folders over a separate `\"clean\"`\n * script in `package.json` (e.g. `rimraf dist coverage`) — tsdown removes them as part of\n * `tsdown` / `pnpm build`, so packages don't need `rimraf`, a `clean` script, or\n * `prebuild`/`run-s clean build` wiring.\n *\n * - `true` (tsdown's default when left undefined) cleans `outDir` (`'dist'` by default)\n * - `false` skips cleaning\n * - a `string[]` replaces that default with the listed paths/globs — include `outDir` (e.g.\n * `'dist'`) when you still want it cleaned alongside other folders\n *\n * @defaultValue true\n * @example\n * ```ts\n * // Instead of `\"clean\": \"rimraf dist coverage\"` in package.json:\n * clean: ['dist', 'coverage']\n * ```\n */\n clean?: UserConfig['clean']\n /**\n * tsdown's `exports` option, with defaults suited for publishing Sanity libraries:\n * `enabled: 'local-only'` generates the `exports` map during local builds and skips it in CI\n * (where the committed `package.json` is already up to date). When pnpm is detected,\n * `devExports: true` also keeps the local `exports` map pointing at source files while\n * `publishConfig.exports` receives the built files.\n *\n * Userland values apply with tsdown's `mergeConfig` semantics: an object deep-merges over\n * these defaults (so individual fields can be overridden), while any other value - `false`\n * to disable exports generation, or a bare CI condition (`'ci-only'`/`'local-only'`) -\n * replaces them entirely.\n * @defaultValue `{enabled: 'local-only', devExports: true}` for pnpm projects;\n * `{enabled: 'local-only'}` otherwise.\n */\n exports?: UserConfig['exports']\n /**\n * Whether to generate source map files. The same default as the `sourcemap` option in\n * `@sanity/pkg-utils` - tsdown itself defaults to `false` and does not read `sourceMap` from\n * the tsconfig.\n * @defaultValue true\n */\n sourcemap?: UserConfig['sourcemap']\n /**\n * tsdown's `deps` option. When `platform` is `'neutral'` (the default), `neverBundle` always\n * includes `/^node:/` and userland `neverBundle` entries are appended (tsdown's `mergeConfig`\n * would replace the array). Other `deps` fields pass through as-is.\n */\n deps?: UserConfig['deps']\n /**\n * tsdown's experimental [`css` option](https://tsdown.dev/options/css) (CSS modules,\n * preprocessors, Lightning CSS / PostCSS, inject, etc). Passed through as-is — requires\n * [`@tsdown/css`](https://www.npmjs.com/package/@tsdown/css) to be installed in the project.\n * Safe to combine with {@link PackageOptions.vanillaExtract}: vanilla-extract extracts into\n * `bundle.css` by default, while `@tsdown/css` merges other CSS (including `.module.css`)\n * into `style.css`, so the two pipelines do not collide.\n * @see https://tsdown.dev/reference/api/Interface.InlineConfig#css\n */\n css?: UserConfig['css']\n /**\n * Runs `babel-plugin-react-compiler` on the source files before they are bundled, so published\n * components are memoized automatically. Pass `true` to use the defaults, or an options object\n * to configure the compiler (e.g. `{target: '18'}`).\n * This is the same feature as the `babel: {reactCompiler: true}` and `reactCompilerOptions`\n * options in `@sanity/pkg-utils`. Unlike `styledComponents` there's no oxc native port of the\n * React Compiler yet, so `babel-plugin-react-compiler` needs to be installed.\n *\n * The options object also accepts `reactServer: true` (an option of this config, not the\n * compiler), which additionally emits an uncompiled build of every entry wired to the\n * `react-server` export condition, for libraries that render in React Server Components —\n * see {@link ReactCompilerOptions.reactServer}.\n * @defaultValue false\n */\n reactCompiler?: boolean | ReactCompilerOptions\n /**\n * Applies the `styled-components` transform (`displayName`, `componentId`, CSS minification, etc)\n * with the same defaults as the `babel: {styledComponents: true}` option in `@sanity/pkg-utils`.\n * Unlike `@sanity/pkg-utils` it doesn't use `babel-plugin-styled-components`, but oxc's native port of it,\n * so there's no need to install babel dependencies.\n * @defaultValue false\n */\n styledComponents?: boolean | StyledComponentsOptions\n /**\n * Enables `@sanity/vanilla-extract-tsdown-plugin` to extract CSS into a separate file,\n * minified and lowered with `lightningcss`. The lowering targets come from the effective\n * `target` (`vanillaExtract.target`, falling back to the top-level `target`) when it names\n * browsers; when it's undefined or browserless (e.g. `'node20'`), they're resolved from\n * `@sanity/browserslist-config` instead — see {@link PackageVanillaExtractOptions}. Pass\n * `true` to use the defaults, or an object to customize.\n *\n * By default (`inject: {nodeCompat: true}`) the plugin also injects the self-referential\n * `import \"<pkg>/bundle.css\"`, emits a `bundle-css.js` shim, and writes the conditional\n * `\"./bundle.css\"` export to `package.json` - see {@link PackageVanillaExtractOptions}.\n * This is the same feature as `rollup.vanillaExtract` in `@sanity/pkg-utils`.\n *\n * Combines with {@link PackageOptions.css}: enable both when a package uses vanilla-extract\n * alongside CSS modules (or other `@tsdown/css` features).\n * @alpha\n */\n vanillaExtract?: boolean | PackageVanillaExtractOptions\n}\n\n/**\n * With `reactCompiler.reactServer` the config resolves to two tsdown configs: the compiled\n * `default` variant and the uncompiled `react-server` variant.\n * @public\n */\nexport function defineConfig(\n options: PackageOptions & {reactCompiler: ReactCompilerOptions & {reactServer: true}},\n): Promise<UserConfig[]>\n/**\n * Without `reactCompiler.reactServer` the config resolves to a single tsdown config.\n * @public\n */\nexport function defineConfig(\n options: PackageOptions & {reactCompiler: ReactCompilerOptions & {reactServer?: false}},\n): Promise<UserConfig>\n/**\n * When TypeScript can't tell whether `reactCompiler.reactServer` is set (a widened `boolean`,\n * e.g. through a `ReactCompilerOptions`-typed variable), the config resolves to either shape —\n * check `Array.isArray` on the result.\n * @public\n */\nexport function defineConfig(\n options: PackageOptions & {reactCompiler: ReactCompilerOptions},\n): Promise<UserConfig | UserConfig[]>\n/**\n * @public\n */\nexport function defineConfig(options?: PackageOptions): Promise<UserConfig>\n/**\n * @public\n */\nexport async function defineConfig(\n options: PackageOptions = {},\n): Promise<UserConfig | UserConfig[]> {\n const reactCompiler = options.reactCompiler ?? false\n // `reactCompiler.reactServer` opts into the dual React Server Components build: the same\n // config twice, where the only difference is that React Compiler auto-memoization is applied\n // to the non-`react-server` variant. Both variants build in one `tsdown` run — tsdown builds\n // the configs in parallel, cleans the shared `outDir` once before either variant emits, and\n // runs `exports` generation and `publint` per `package.json` only after every build for that\n // package has finished (so the `react-server` files exist by the time they're validated).\n if (typeof reactCompiler === 'object' && reactCompiler.reactServer === true) {\n return Promise.all([\n resolvePackageConfig(options, 'default'),\n resolvePackageConfig(options, 'react-server'),\n ])\n }\n return resolvePackageConfig(options, 'standalone')\n}\n\n/**\n * The build variants {@link defineConfig} produces: the classic single build\n * (`'standalone'`), or — with `reactCompiler.reactServer` — the compiled `'default'` variant\n * and the uncompiled `'react-server'` variant of the dual React Server Components build.\n */\ntype PackageConfigVariant = 'standalone' | 'default' | 'react-server'\n\nasync function resolvePackageConfig(\n options: PackageOptions,\n variant: PackageConfigVariant,\n): Promise<UserConfig> {\n // `tsconfig`, `entry`, `dts`, `define`, `target`, `outDir`, `clean` and `css` are passed\n // through to tsdown as-is. When left undefined, tsdown keeps its default behavior\n // (`tsconfig` is auto-detected from the project, `dts` from `package.json`, `define`\n // replaces nothing, `target` applies no syntax downleveling, `outDir` defaults to `'dist'`,\n // `clean` defaults to `true` — cleaning `outDir` before each build — and `css` stays off\n // unless `@tsdown/css` is installed and the option is set).\n const {entry, tsconfig, define, target, outDir, css} = options\n const isReactServer = variant === 'react-server'\n // The `react-server` variant skips d.ts generation (the compiled variant's declarations\n // serve both entries — a `types` condition specified before `react-server` points every\n // resolution mode at them), never cleans (tsdown collects the clean paths of all configs\n // and cleans once, before either variant emits, so the compiled variant's `clean` already\n // covers the run), and skips `publint` (it runs once, from the compiled variant, after\n // both builds finish).\n const dts = isReactServer ? false : options.dts\n const clean = isReactServer ? false : options.clean\n const publint = !isReactServer\n const platform = options.platform ?? 'neutral'\n const sourcemap = options.sourcemap ?? true\n // The React Compiler is what the `react-server` variant exists to avoid: compiled output\n // loads `react/compiler-runtime`, which throws in the `react-server` environment.\n const reactCompiler = isReactServer ? false : (options.reactCompiler ?? false)\n const styledComponents = options.styledComponents ?? false\n const report = {gzip: false} as const satisfies UserConfig['report']\n const format = options.format ?? 'esm'\n // When `platform` is `'neutral'`, restore the conventional `module`/`main` fallback that\n // rolldown's strict neutral defaults drop - needed for inlined deps without an `exports` map.\n const inputOptions = {\n // https://github.com/rolldown/rolldown/blob/main/packages/rolldown/src/options/docs/preserve-entry-signatures.md#strict\n preserveEntrySignatures: 'strict',\n experimental: {attachDebugInfo: 'none'},\n ...(platform === 'neutral' && {\n resolve: {mainFields: ['module', 'main']},\n }),\n ...(styledComponents !== false && {\n transform: {\n plugins: {\n styledComponents: {\n // The same defaults as `babel: {styledComponents: true}` in `@sanity/pkg-utils`:\n // `fileName` is unnecessary, as the way we use styled-components in Sanity is usually by wrapping\n // `@sanity/ui` primitives, not declaring new ones like \"const Button = styled.button``\"\n fileName: false,\n // Native template literals take less space than this transpilation, and unlike\n // `babel-plugin-styled-components`, oxc doesn't add a `@__PURE__` annotation to the\n // transpiled call expression either, so enabling it wouldn't improve tree-shaking\n transpileTemplateLiterals: false,\n // Helps dead code elimination and tree-shaking, although oxc only annotates plain call\n // expressions so far, not tagged template expressions (https://github.com/rollup/rollup/issues/4035)\n pure: true,\n // Disabled, as tsdown tends to be used for npm publishing, while other tooling,\n // like `sanity dev`, `next dev`, etc are used for testing\n cssProp: false,\n ...(typeof styledComponents === 'object' ? styledComponents : {}),\n },\n },\n },\n }),\n } satisfies UserConfig['inputOptions']\n\n // `neverBundle` is concatenated (not `mergeConfig`'d) so per-package externals add to the\n // `/^node:/` default for `platform: 'neutral'` instead of replacing it. Rolldown's\n // `ExternalOption` array form is `Array<string | RegExp>` only; a function override is composed.\n const userNeverBundle = options.deps?.neverBundle\n const nodeBuiltinExternal = platform === 'neutral' ? /^node:/ : undefined\n const neverBundle: NonNullable<UserConfig['deps']>['neverBundle'] =\n userNeverBundle == null\n ? nodeBuiltinExternal && [nodeBuiltinExternal]\n : nodeBuiltinExternal == null || userNeverBundle === true\n ? userNeverBundle\n : typeof userNeverBundle === 'function'\n ? (id, importer, isResolved) =>\n nodeBuiltinExternal.test(id) || userNeverBundle(id, importer, isResolved)\n : Array.isArray(userNeverBundle)\n ? [nodeBuiltinExternal, ...userNeverBundle]\n : [nodeBuiltinExternal, userNeverBundle]\n const deps: UserConfig['deps'] =\n options.deps === undefined && neverBundle === undefined\n ? undefined\n : {\n ...options.deps,\n ...(neverBundle === undefined ? {} : {neverBundle}),\n }\n\n // `outputOptions` is left to tsdown's defaults - notably chunk filenames keep tsdown's hashed\n // default (unless userland sets `hash`), which prevents chunk/entry filename collisions\n // (https://github.com/sanity-io/ui/issues/2262).\n const plugins: Rolldown.Plugin[] = []\n if (reactCompiler !== false) {\n // Follows the official tsdown recipe for the React Compiler:\n // https://tsdown.dev/recipes/react-support#enabling-react-compiler\n // The plugins are lazy loaded so they're only paid for when the React Compiler is enabled.\n // `babel-plugin-react-compiler` itself is resolved by Babel from the consumer package during\n // the build, which is why it can be an optional peer dependency. Once rolldown ships its rust\n // port of the React Compiler this can be swapped out for an oxc transform, like `styledComponents`.\n const [{default: pluginBabel}, {reactCompilerPreset}] = await Promise.all([\n import('@rolldown/plugin-babel'),\n import('@vitejs/plugin-react'),\n ])\n // `reactServer` belongs to this config, not the compiler — drop it before handing the\n // options over to the babel preset.\n const {reactServer: _reactServer, ...reactCompilerOptions} =\n typeof reactCompiler === 'object' ? reactCompiler : {}\n plugins.push(\n await pluginBabel({\n presets: [reactCompilerPreset(reactCompilerOptions)],\n }),\n )\n }\n if (options.vanillaExtract) {\n // Lazy loaded, like `reactCompiler`, so the CSS toolchain is only paid for when the option is\n // enabled. The plugin compiles the `.css.ts` files and extracts the CSS into a single file.\n // Its `inject` option is general purpose (and, like `css.inject` in `@tsdown/css`, disabled\n // by default), so this config supplies the default most Sanity libraries want:\n // `{nodeCompat: true}` wires up the whole conditional CSS export pattern - the\n // self-referential CSS import, the no-op JS shim, and the conditional `./<fileName>` export\n // written through this config's `exports` option (which the plugin's `tsdownConfig` hook\n // composes into).\n const {esbuildTargetToLightningCSS, vanillaExtractPlugin} =\n await import('@sanity/vanilla-extract-tsdown-plugin')\n const vanillaExtract = options.vanillaExtract === true ? {} : options.vanillaExtract\n\n // The plugin follows `@tsdown/css`: without browser targets, CSS syntax lowering is\n // skipped. The extracted CSS always runs in browsers, so when the effective target\n // (`vanillaExtract.target`, falling back to the top-level `target`) is undefined or names\n // no browsers (e.g. `'node20'` - also what tsdown derives from `engines.node`, which\n // speaks to the JS runtime), this config resolves the lowering targets from\n // `@sanity/browserslist-config` and passes them through `lightningcss.targets` instead.\n // `target: false` stays the explicit off switch, and a user-provided\n // `lightningcss.targets` wins over the fallback.\n const cssTarget = vanillaExtract.target ?? target\n let {lightningcss} = vanillaExtract\n if (\n cssTarget !== false &&\n !lightningcss?.targets &&\n (cssTarget === undefined || !esbuildTargetToLightningCSS(cssTarget))\n ) {\n // Lazy loaded as well: `browserslistToTargets` is a pure helper, but `lightningcss` is a\n // native package that only needs to load when the fallback applies\n const [{default: browserslist}, {default: browserslistConfig}, {browserslistToTargets}] =\n await Promise.all([\n import('browserslist'),\n import('@sanity/browserslist-config'),\n import('lightningcss'),\n ])\n lightningcss = {\n ...lightningcss,\n targets: browserslistToTargets(browserslist(browserslistConfig)),\n }\n }\n\n plugins.push(\n vanillaExtractPlugin({\n inject: {nodeCompat: true},\n minify: true,\n ...vanillaExtract,\n lightningcss,\n }),\n )\n }\n\n // The `react-server` variant does not participate in `exports` generation: its files are\n // wired in through the compiled variant's `react-server` conditions instead of becoming\n // export subpaths of their own.\n let exports: UserConfig['exports'] = false\n if (!isReactServer) {\n const packageManager = await detect({cwd: process.cwd()})\n // tsdown's `exports` feature is enabled with Sanity-flavored defaults, and userland values\n // apply with tsdown's own `mergeConfig` semantics: an object deep-merges over the defaults,\n // anything else (`false`, a CI condition) replaces them.\n ;({exports} = mergeConfig(\n {\n exports: {\n enabled: 'local-only',\n // Only opt in by default when pnpm is detected: support for replacing package fields\n // from `publishConfig` is not reliable across package managers.\n ...(packageManager?.name === 'pnpm' && {devExports: true}),\n },\n },\n {exports: options.exports},\n ))\n if (variant === 'default' && exports) {\n // The compiled variant owns `exports` generation for the dual build, so the\n // `react-server` conditions are composed into its `customExports`: every entry export\n // gains a `react-server` condition pointing at the `.react-server.` sibling that the\n // `react-server` variant emits.\n exports = withReactServerExports(exports)\n }\n }\n\n return defineTsdownConfig({\n // Rolldown defaults `circularDependency` to `false`; enable it so Sanity library builds\n // surface import cycles (bigger bundles / execution-order hazards) as warnings.\n // Override via `mergeConfig(..., {checks: {circularDependency: false}})`.\n // https://rolldown.rs/reference/InputOptions.checks#circulardependency\n checks: {circularDependency: true},\n clean,\n css,\n define,\n deps,\n dts,\n entry,\n exports,\n format,\n inputOptions,\n outDir,\n // The `react-server` variant writes its files next to the compiled ones, with `.react-server`\n // inserted before tsdown's default extension (`index.js` ↔ `index.react-server.js`). Hashed\n // chunk filenames get the suffix too, so the two variants' chunks never collide in the\n // shared `outDir`.\n outExtensions: isReactServer ? reactServerOutExtensions : undefined,\n platform,\n plugins,\n publint,\n report,\n sourcemap,\n target,\n tsconfig,\n // Compress the output (constant folding, dead code elimination) without mangling\n // identifiers or stripping whitespace: consumers' production builds minify `node_modules`\n // again anyway, so full minification here would only hurt debuggability, not final app\n // bundle sizes. `keepNames` preserves the function and class names the compress pass\n // would otherwise strip as unreferenced - e.g. the inner name in\n // `forwardRef(function Button(…) {…})`, which React DevTools reads via `Function.name`.\n // That name is the tree-shakeable alternative to a top-level `Button.displayName = '…'`\n // assignment (a side effect that pins unused components into consumer bundles, see\n // https://github.com/sanity-io/ui/pull/2435), and names stripped at publish time are\n // unrecoverable in userland.\n minify: {\n compress: {keepNames: {function: true, class: true}},\n codegen: false,\n mangle: false,\n },\n })\n}\n\n/**\n * The `outExtensions` of the `react-server` variant: tsdown's default extension for the\n * format and package type (see `resolveJsOutputExtension` in tsdown), with `.react-server` inserted\n * so the variant's entries sit next to the compiled ones (`index.js` ↔ `index.react-server.js`,\n * `index.cjs` ↔ `index.react-server.cjs`, and `.mjs` accordingly for CommonJS packages).\n */\nconst reactServerOutExtensions: NonNullable<UserConfig['outExtensions']> = ({format, pkgType}) => ({\n js:\n format === 'cjs'\n ? pkgType === 'module'\n ? '.react-server.cjs'\n : '.react-server.js'\n : pkgType === 'module'\n ? '.react-server.js'\n : '.react-server.mjs',\n})\n\ntype ExportsOptions = Extract<NonNullable<UserConfig['exports']>, object>\ntype CustomExportsFunction = Extract<\n NonNullable<ExportsOptions['customExports']>,\n (...args: never[]) => unknown\n>\ntype ExportsMap = Parameters<CustomExportsFunction>[0]\n/**\n * Wires the `react-server` conditions into tsdown's `exports` feature by composing into\n * `exports.customExports` (the same composition `@sanity/vanilla-extract-tsdown-plugin` uses\n * for its conditional CSS export): a pre-existing `customExports` applies first — both its\n * function and record forms, mirroring how tsdown itself applies them — and the\n * `react-server` conditions are inserted into the result. The composed function runs for both\n * the local `exports` map and `publishConfig.exports`; with `devExports` the local map points\n * at source files, which the entry-file matching leaves untouched (source resolves for every\n * condition in development, which is correct — uncompiled source is exactly what the\n * `react-server` condition ships).\n */\nfunction withReactServerExports(\n exportsOption: Exclude<NonNullable<UserConfig['exports']>, false>,\n): ExportsOptions {\n // Normalize the `boolean | CIOption | object` forms of the `exports` option into the\n // object form, preserving the enabled-ness (`true` and bare CI conditions mean enabled)\n const exportsOptions: ExportsOptions =\n exportsOption === true\n ? {}\n : typeof exportsOption === 'string'\n ? {enabled: exportsOption}\n : exportsOption\n const previousCustomExports = exportsOptions.customExports\n exportsOptions.customExports = async (exportsMap, context) => {\n // Apply a pre-existing `customExports` first (both its function and record forms,\n // mirroring how tsdown itself applies them), then insert the `react-server` conditions\n const base =\n typeof previousCustomExports === 'function'\n ? await previousCustomExports(exportsMap, context)\n : previousCustomExports\n ? {...exportsMap, ...previousCustomExports}\n : exportsMap\n return addReactServerConditions(base, context)\n }\n return exportsOptions\n}\n\n/** Matches the JS output extensions tsdown emits for `es`/`cjs` chunks. */\nconst RE_JS_FILE = /\\.(m?js|cjs)$/\n\n/** The conditions tsdown generates that resolve at runtime, in contrast to `types` etc. */\nconst RUNTIME_CONDITIONS = new Set(['import', 'require', 'default'])\n\n/**\n * Inserts a `react-server` condition into every entry export of an `exports`-shaped map,\n * pointing at the `.react-server.` sibling emitted by the `react-server` variant — e.g.\n * `\"./dist/index.js\"` becomes\n * `{\"types\": \"./dist/index.d.ts\", \"react-server\": \"./dist/index.react-server.js\", \"default\": \"./dist/index.js\"}`.\n *\n * The `react-server` variant emits no `.d.ts` siblings, so TypeScript's adjacent-file lookup\n * cannot resolve types for `./dist/index.react-server.js` — a `types` condition pointing at\n * the compiled variant's declarations is specified before `react-server` instead (conditions\n * match in order, so it covers every resolution mode). It's only added when the declarations\n * actually exist among the compiled variant's chunks, and an already-present `types`\n * condition is left in place.\n *\n * Entries are recognized by comparing export targets against the compiled variant's entry\n * chunks — reconstructed in the exact `./<outDir relative to the package root>/<fileName>`\n * form tsdown writes into the exports map, so entries that share a leaf name (`index.js` vs\n * `features/index.js`) can never shadow each other, and non-entry exports (`./package.json`,\n * the conditional `./bundle.css` export of `vanillaExtract`, `devExports` source paths like\n * `./src/index.ts`) pass through untouched. The `react-server` files themselves never enter\n * the map — the `react-server` variant builds with `exports: false` — so they don't become\n * export subpaths of their own.\n */\nfunction addReactServerConditions(\n exportsMap: ExportsMap,\n context: Parameters<CustomExportsFunction>[1],\n): ExportsMap {\n // tsdown always resolves the package with its path here: its own exports generation reads\n // `packageJsonPath` unconditionally from the same object it passes to `customExports` -\n // only the declared context type is the plain `PackageJson` shape.\n const pkg: Partial<PackageJsonWithPath> = context.pkg\n const pkgRoot = path.dirname(pkg.packageJsonPath ?? 'package.json')\n // Export targets of the compiled variant's entry JS and declaration chunks; the\n // `react-server` variant's files sit next to the entries with `.react-server` inserted\n // before the extension\n const entryFiles = new Set<string>()\n const dtsFiles = new Set<string>()\n for (const [format, formatChunks] of Object.entries(context.chunks)) {\n // Only `es` and `cjs` chunks become package exports (mirroring tsdown's own generation)\n if (format !== 'es' && format !== 'cjs') continue\n for (const chunk of formatChunks) {\n if (chunk.type !== 'chunk') continue\n const target = exportTarget(pkgRoot, chunk.outDir, chunk.fileName)\n if (chunk.isEntry && RE_JS_FILE.test(chunk.fileName)) {\n entryFiles.add(target)\n } else if (RE_DTS_FILE.test(chunk.fileName)) {\n dtsFiles.add(target)\n }\n }\n }\n const isEntryFile = (value: unknown): value is string =>\n typeof value === 'string' && entryFiles.has(value)\n // The `types` file for an entry export target, when the compiled variant emitted it\n const dtsFor = (value: string): string | undefined => {\n const dtsFile = toDtsFile(value)\n return dtsFiles.has(dtsFile) ? dtsFile : undefined\n }\n\n const result: ExportsMap = {}\n for (const [key, value] of Object.entries(exportsMap as Record<string, unknown>)) {\n result[key] = withReactServerCondition(value, isEntryFile, dtsFor)\n }\n return result\n}\n\n/**\n * Mirrors tsdown's own `join` for exports targets:\n * `./<outDir relative to the package root>/<fileName>`, with POSIX separators.\n */\nfunction exportTarget(pkgRoot: string, outDir: string, fileName: string): string {\n const outDirRelative = path.relative(pkgRoot, outDir).replaceAll('\\\\', '/')\n return `${outDirRelative ? `./${outDirRelative}` : '.'}/${fileName.replaceAll('\\\\', '/')}`\n}\n\nfunction withReactServerCondition(\n value: unknown,\n isEntryFile: (value: unknown) => value is string,\n dtsFor: (value: string) => string | undefined,\n): unknown {\n // A bare-string entry export (e.g. the pure-ESM publish shape `\".\": \"./dist/index.js\"`)\n // becomes a conditional export, with `types` specified before `react-server` so type\n // resolution never reaches the declaration-less `.react-server.` file\n if (isEntryFile(value)) {\n const types = dtsFor(value)\n return {\n ...(types === undefined ? {} : {types}),\n 'react-server': toServerFile(value),\n 'default': value,\n }\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return value\n const conditions = Object.entries(value)\n const matched = conditions.filter(\n (entry): entry is [string, string] => RUNTIME_CONDITIONS.has(entry[0]) && isEntryFile(entry[1]),\n )\n const [firstMatch] = matched\n if (!firstMatch) return value\n const hasTypes = conditions.some(([condition]) => condition === 'types')\n // A single runtime file maps to a plain path (with a `types` condition inserted ahead of\n // `react-server` below, unless one is already specified). A dual-format entry nests its\n // `import` and `require` files under the `react-server` condition, each with its own\n // format-matched `types` — a single top-level `types` file cannot serve both resolution\n // modes, which is also why tsdown's own dual-format output relies on adjacent-file lookup.\n const missingTypes = hasTypes ? undefined : dtsFor(firstMatch[1])\n const serverTarget =\n matched.length === 1\n ? toServerFile(firstMatch[1])\n : Object.fromEntries(\n matched.map(([condition, target]) => {\n const types = hasTypes ? undefined : dtsFor(target)\n return [\n condition,\n types === undefined ? toServerFile(target) : {types, default: toServerFile(target)},\n ]\n }),\n )\n // Conditions match in order, so `react-server` is inserted right before the first runtime\n // condition (`import`/`require`/`default`), keeping `types` and custom development\n // conditions (`devExports`) ahead of it\n const next: Record<string, unknown> = {}\n let inserted = false\n for (const [condition, target] of conditions) {\n if (!inserted && RUNTIME_CONDITIONS.has(condition)) {\n if (matched.length === 1 && missingTypes !== undefined) {\n next['types'] = missingTypes\n }\n next['react-server'] = serverTarget\n inserted = true\n }\n next[condition] = target\n }\n if (!inserted) {\n next['react-server'] = serverTarget\n }\n return next\n}\n\n/** Matches the declaration file extensions tsdown emits next to `es`/`cjs` chunks. */\nconst RE_DTS_FILE = /\\.d\\.(ts|mts|cts)$/\n\n/** `./dist/index.js` → `./dist/index.react-server.js` (and `.mjs`/`.cjs` accordingly). */\nfunction toServerFile(file: string): string {\n return file.replace(RE_JS_FILE, '.react-server.$1')\n}\n\n/** `./dist/index.js` → `./dist/index.d.ts` (`.mjs` → `.d.mts`, `.cjs` → `.d.cts`). */\nfunction toDtsFile(file: string): string {\n return file.replace(RE_JS_FILE, (extension) =>\n extension === '.mjs' ? '.d.mts' : extension === '.cjs' ? '.d.cts' : '.d.ts',\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/tsdown-config",
3
- "version": "0.20.0",
3
+ "version": "0.21.1",
4
4
  "description": "Shared configuration for tsdown",
5
5
  "keywords": [
6
6
  "sanity-io",
@@ -38,23 +38,23 @@
38
38
  "@babel/core": "^7.29.7",
39
39
  "@rolldown/plugin-babel": "^0.2.3",
40
40
  "@sanity/browserslist-config": "^1.0.5",
41
- "@vitejs/plugin-react": "^6.0.3",
42
- "browserslist": "^4.28.6",
41
+ "@vitejs/plugin-react": "^6.0.4",
42
+ "browserslist": "^4.28.7",
43
43
  "lightningcss": "^1.33.0",
44
- "package-manager-detector": "^1.7.0",
45
- "publint": "^0.3.21",
46
- "@sanity/vanilla-extract-tsdown-plugin": "^0.2.9"
44
+ "package-manager-detector": "^1.8.0",
45
+ "publint": "^0.3.22",
46
+ "@sanity/vanilla-extract-tsdown-plugin": "^0.2.10"
47
47
  },
48
48
  "devDependencies": {
49
- "@tsdown/css": "^0.22.13",
49
+ "@tsdown/css": "^0.22.14",
50
50
  "@types/babel__core": "^7.20.5",
51
51
  "babel-plugin-react-compiler": "^1.0.0",
52
52
  "rolldown": "~1.2.0",
53
53
  "tinyexec": "^1.2.4",
54
- "tsdown": "^0.22.13",
54
+ "tsdown": "^0.22.14",
55
55
  "typescript": "7.0.2",
56
56
  "vitest": "^4.1.10",
57
- "@sanity/pkg-utils": "^11.0.14",
57
+ "@sanity/pkg-utils": "^11.0.15",
58
58
  "@sanity/tsconfig": "^2.2.1"
59
59
  },
60
60
  "peerDependencies": {