@sanity/pkg-utils 12.0.1 → 12.1.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.
Files changed (34) hide show
  1. package/MIGRATE.md +23 -22
  2. package/README.md +59 -4
  3. package/dist/{buildAction-D-M3KYyN.js → buildAction-C8MCKsDz.js} +10 -6
  4. package/dist/buildAction-C8MCKsDz.js.map +1 -0
  5. package/dist/{checkAction-B8M5CP3j.js → checkAction-0QxzAxUd.js} +42 -37
  6. package/dist/checkAction-0QxzAxUd.js.map +1 -0
  7. package/dist/cli.js +5 -5
  8. package/dist/index.d.ts +40 -26
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +12 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/{resolveBuildContext-CbwvYKj1.js → resolveBuildContext-DYZxWVVc.js} +78 -50
  13. package/dist/resolveBuildContext-DYZxWVVc.js.map +1 -0
  14. package/dist/{resolveTsdownConfig-CGE_7cMM.js → resolveTsdownConfig-D4boYIwx.js} +143 -12
  15. package/dist/resolveTsdownConfig-D4boYIwx.js.map +1 -0
  16. package/dist/watchAction-PjRXiQRe.js +117 -0
  17. package/dist/watchAction-PjRXiQRe.js.map +1 -0
  18. package/dist/writeBundleCssExports-DGTofClh.js +64 -0
  19. package/dist/writeBundleCssExports-DGTofClh.js.map +1 -0
  20. package/package.json +11 -10
  21. package/dist/buildAction-D-M3KYyN.js.map +0 -1
  22. package/dist/checkAction-B8M5CP3j.js.map +0 -1
  23. package/dist/createApiExtractorConfig-CJoZqw8e.js +0 -30
  24. package/dist/createApiExtractorConfig-CJoZqw8e.js.map +0 -1
  25. package/dist/createTSDocConfig-ylfirNWP.js +0 -25
  26. package/dist/createTSDocConfig-ylfirNWP.js.map +0 -1
  27. package/dist/getExtractMessagesConfig-CXljwFbQ.js +0 -75
  28. package/dist/getExtractMessagesConfig-CXljwFbQ.js.map +0 -1
  29. package/dist/printExtractMessages-DyaYtByp.js +0 -33
  30. package/dist/printExtractMessages-DyaYtByp.js.map +0 -1
  31. package/dist/resolveBuildContext-CbwvYKj1.js.map +0 -1
  32. package/dist/resolveTsdownConfig-CGE_7cMM.js.map +0 -1
  33. package/dist/watchAction-BQG5_hhl.js +0 -179
  34. package/dist/watchAction-BQG5_hhl.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as isRecord } from "./handleError-83GwKIFM.js";
2
- import { i as pkgExtMap, r as fileEnding } from "./resolveBuildContext-CbwvYKj1.js";
2
+ import { i as pkgExtMap, r as fileEnding } from "./resolveBuildContext-DYZxWVVc.js";
3
3
  import path from "node:path";
4
4
  import { mergeConfig } from "tsdown";
5
5
  import { defineConfig } from "@sanity/tsdown-config";
@@ -71,7 +71,20 @@ function resolveTsdownBuilds(ctx) {
71
71
  " `tsdown.config.ts` with one config per `platform` — the fully supported path for",
72
72
  " this level of customization."
73
73
  ].join("\n"));
74
- let builds = [], toBuild = (key, runtime, canonical) => {
74
+ let builds = [], cssEntries = ctx.cssExports.map((cssExport) => ({
75
+ alias: cssEntryAlias(cssExport._path),
76
+ source: cssExport.source,
77
+ exportPath: cssExport._path,
78
+ formats: ["esm"]
79
+ }));
80
+ cssEntries.length && builds.push({
81
+ key: "css",
82
+ runtime: ctx.runtime,
83
+ canonical: !1,
84
+ entries: cssEntries,
85
+ css: !0
86
+ });
87
+ let toBuild = (key, runtime, canonical) => {
75
88
  let drafts = draftsByBuild.get(key);
76
89
  return !drafts || drafts.size === 0 ? null : {
77
90
  key,
@@ -94,6 +107,67 @@ function resolveTsdownBuilds(ctx) {
94
107
  return canonical && builds.push(canonical), builds;
95
108
  }
96
109
  /**
110
+ * The tsdown entry alias of a `.css` export subpath: the subpath without its leading `./` and
111
+ * `.css` ending, so `@tsdown/css` (with `splitting`) emits the stylesheet at exactly the path
112
+ * the subpath promises — `"./ui/styles.css"` -> alias `ui/styles` -> `dist/ui/styles.css`.
113
+ */
114
+ function cssEntryAlias(exportPath) {
115
+ return exportPath.replace(/^\.\//, "").replace(/\.css$/, "");
116
+ }
117
+ /**
118
+ * The no-op JS shim file name for a CSS file under vanilla-extract compat mode.
119
+ *
120
+ * `bundle.css` → `bundle-css.js` — deliberately not `${cssFileName}.js` (`bundle.css.js`),
121
+ * which vanilla-extract's `cssFileFilter` (`/\.css\.(js|cjs|mjs|jsx|ts|tsx)$/`) would treat as
122
+ * a stylesheet module. Kept in sync with `cssShimFileName` in
123
+ * `@sanity/vanilla-extract-rolldown-plugin`.
124
+ *
125
+ * @internal
126
+ */
127
+ function cssShimFileName(cssFileName) {
128
+ return `${cssFileName.replace(/\.css$/, "-css")}.js`;
129
+ }
130
+ /**
131
+ * The `.d.ts` companion for {@link cssShimFileName}. `bundle.css` → `bundle-css.d.ts`.
132
+ *
133
+ * @internal
134
+ */
135
+ function cssShimDtsFileName(cssFileName) {
136
+ return `${cssFileName.replace(/\.css$/, "-css")}.d.ts`;
137
+ }
138
+ /**
139
+ * Build the conditional CSS export object that `exports.nodeCompat` expects, e.g.
140
+ * ```json
141
+ * {
142
+ * "types": "./dist/bundle-css.d.ts",
143
+ * "browser": "./dist/bundle.css",
144
+ * "style": "./dist/bundle.css",
145
+ * "node": "./dist/bundle-css.js",
146
+ * "default": "./dist/bundle-css.js"
147
+ * }
148
+ * ```
149
+ * The shim is named `bundle-css.js` (not `bundle.css.js`) so it does not match
150
+ * vanilla-extract's `cssFileFilter`. An explicit `types` condition (rather than relying on
151
+ * TypeScript's extension-substitution fallback, which only works when the shim shares the CSS
152
+ * file's basename, and which TypeScript is deprecating anyway - microsoft/TypeScript#50762)
153
+ * points resolvers straight at the shim's declaration file.
154
+ *
155
+ * Kept in sync with `createConditionalCssExport` in `@sanity/vanilla-extract-tsdown-plugin`,
156
+ * which writes the same entry through tsdown's `exports.customExports` during full builds.
157
+ *
158
+ * @internal
159
+ */
160
+ function createConditionalCssExport(cssName, distRel) {
161
+ let cssFile = `./${path.posix.join(distRel, cssName)}`, shimFile = `./${path.posix.join(distRel, cssShimFileName(cssName))}`;
162
+ return {
163
+ types: `./${path.posix.join(distRel, cssShimDtsFileName(cssName))}`,
164
+ browser: cssFile,
165
+ style: cssFile,
166
+ node: shimFile,
167
+ default: shimFile
168
+ };
169
+ }
170
+ /**
97
171
  * The pkg-utils opinion layer over tsdown's generated `exports` map, composed into
98
172
  * `exports.customExports` of the canonical build (the same composition hook
99
173
  * `@sanity/vanilla-extract-tsdown-plugin` uses for its conditional CSS export).
@@ -122,7 +196,9 @@ function resolveTsdownBuilds(ctx) {
122
196
  * @internal
123
197
  */
124
198
  function createExportsComposer(ctx, build) {
125
- let { pkg } = ctx, type = pkg.type === "module" ? "module" : "commonjs", aliasToExportPath = /* @__PURE__ */ new Map();
199
+ let { pkg } = ctx, type = pkg.type === "module" ? "module" : "commonjs", distRel = (path.relative(ctx.cwd, ctx.distPath) || "dist").split(path.sep).join("/"), cssExportPaths = new Set(ctx.cssExports.map((cssExport) => cssExport._path)), cssSources = {};
200
+ for (let cssExport of ctx.cssExports) cssSources[cssExport._path] = cssExport.source;
201
+ let aliasToExportPath = /* @__PURE__ */ new Map();
126
202
  for (let entry of build.entries) entry.exportPath !== void 0 && aliasToExportPath.set(entry.alias, entry.exportPath);
127
203
  return (exportsMap, context) => {
128
204
  let { isPublish } = context, remapped = {};
@@ -141,12 +217,28 @@ function createExportsComposer(ctx, build) {
141
217
  });
142
218
  }, authoredPaths = Object.keys(authoredRaw);
143
219
  for (let exportPath of Object.keys(sourceRaw)) Object.prototype.hasOwnProperty.call(authoredRaw, exportPath) || authoredPaths.push(exportPath);
144
- for (let exportPath of authoredPaths) result[exportPath] = exportPath in remapped ? reconcile(exportPath, remapped[exportPath]) : Object.prototype.hasOwnProperty.call(authoredRaw, exportPath) ? authoredRaw[exportPath] : sourceRaw[exportPath];
220
+ for (let exportPath of authoredPaths) result[exportPath] = exportPath in remapped ? reconcile(exportPath, remapped[exportPath]) : cssExportPaths.has(exportPath) ? reconcileCssEntry(exportPath, {
221
+ distRel,
222
+ source: cssSources[exportPath],
223
+ isPublish
224
+ }) : Object.prototype.hasOwnProperty.call(authoredRaw, exportPath) ? authoredRaw[exportPath] : sourceRaw[exportPath];
145
225
  for (let [exportPath, value] of Object.entries(remapped)) exportPath in result || (result[exportPath] = reconcile(exportPath, value));
146
226
  return result;
147
227
  };
148
228
  }
149
229
  /**
230
+ * The conditional CSS export of a `.css` subpath built by the stylesheet build. `source`
231
+ * resolves at development time, so it is kept in `exports` and stripped from the publish
232
+ * variant — the same split the build entries get.
233
+ */
234
+ function reconcileCssEntry(exportPath, options) {
235
+ let { distRel, source, isPublish } = options, conditions = createConditionalCssExport(exportPath.replace(/^\.\//, ""), distRel);
236
+ return isPublish || source === void 0 ? conditions : {
237
+ source,
238
+ ...conditions
239
+ };
240
+ }
241
+ /**
150
242
  * Rebuilds a generated subpath entry in its authored condition order, re-inserting the
151
243
  * hand-written conditions tsdown's generator cannot express.
152
244
  */
@@ -235,19 +327,25 @@ async function resolveTsdownConfig(ctx, build, options) {
235
327
  ctx.logger.warn(`${names} declare${partial.length === 1 ? "s" : ""} fewer formats than the rest of the package. tsdown emits every format of a build for every entry, so the missing format is built anyway (and local exports generation will declare it). Declare both \`import\` and \`require\` targets for every subpath — or for none — to keep the exports map unambiguous.`);
236
328
  }
237
329
  }
238
- let format = [...formats.has("esm") ? ["esm"] : [], ...formats.has("commonjs") ? ["cjs"] : []], platform = build.runtime === "node" ? "node" : build.runtime === "browser" ? "browser" : "neutral", define = {};
330
+ let format = [...formats.has("esm") ? ["esm"] : [], ...formats.has("commonjs") ? ["cjs"] : []], platform = build.runtime === "node" ? "node" : build.runtime === "browser" ? "browser" : "neutral", css = config?.css || ctx.cssExports.length ? {
331
+ ...config?.css,
332
+ ...build.css ? { splitting: !0 } : {}
333
+ } : void 0, define = {};
239
334
  pkg.name !== "@sanity/pkg-utils" && (define["process.env.PKG_VERSION"] = JSON.stringify(process.env.PKG_VERSION || pkg.version));
240
335
  for (let [key, value] of Object.entries(config?.define || {})) define[key] = JSON.stringify(value);
241
- let hasTsSources = build.entries.some((buildEntry) => RE_TS_SOURCE.test(buildEntry.source)), dtsPassthrough = typeof config?.dts == "object" ? config.dts : void 0, dts = hasTsSources && config?.dts !== !1 ? {
336
+ let hasTsSources = !build.css && build.entries.some((buildEntry) => RE_TS_SOURCE.test(buildEntry.source)), dtsPassthrough = typeof config?.dts == "object" ? config.dts : void 0, dts = hasTsSources && config?.dts !== !1 ? {
242
337
  ...typeof pkg.devDependencies == "object" && "@typescript/native-preview" in pkg.devDependencies ? { tsgo: !0 } : {},
243
338
  newContext: !0,
244
339
  ...dtsPassthrough,
245
340
  ...ctx.emitDeclarationOnly ? { emitDtsOnly: !0 } : {}
246
- } : !1, exports = build.canonical && !ctx.emitDeclarationOnly && !options.watch ? {
341
+ } : !1, exports = build.canonical && !ctx.emitDeclarationOnly && !options.watch && !build.css ? {
247
342
  devExports: "source",
248
343
  customExports: createExportsComposer(ctx, build),
249
344
  ...pkg.main || pkg.module ? { legacy: !0 } : {}
250
- } : !1, base = await defineConfig({
345
+ } : !1, tsdocOption = config?.tsdoc !== !1 && {
346
+ ...typeof config?.tsdoc == "object" ? config.tsdoc : {},
347
+ bundledPackages: ctx.bundledPackages
348
+ }, base = await defineConfig({
251
349
  cwd,
252
350
  tsconfig: ctx.ts.configPath,
253
351
  platform,
@@ -261,9 +359,11 @@ async function resolveTsdownConfig(ctx, build, options) {
261
359
  dts,
262
360
  deps: ctx.deps,
263
361
  exports,
362
+ css,
264
363
  reactCompiler: config?.reactCompiler,
265
364
  styledComponents: config?.styledComponents,
266
- vanillaExtract: config?.vanillaExtract
365
+ vanillaExtract: config?.vanillaExtract,
366
+ tsdoc: !options.watch && tsdocOption
267
367
  }), extMap = pkgExtMap[pkg.type === "module" ? "module" : "commonjs"], outExtensions = ({ format: outputFormat }) => ({ js: outputFormat === "cjs" ? extMap.commonjs : extMap.esm });
268
368
  return {
269
369
  ...mergeConfig(base, {
@@ -271,13 +371,44 @@ async function resolveTsdownConfig(ctx, build, options) {
271
371
  publint: !1,
272
372
  report: !1,
273
373
  ...config?.minify === !0 ? { minify: !0 } : {},
274
- ...config?.plugins === void 0 ? {} : { plugins: config.plugins }
374
+ ...config?.plugins === void 0 ? {} : { plugins: config.plugins },
375
+ ...options.watch && css ? { hooks: createWatchCssExportsHook(ctx, css) } : {}
275
376
  }),
276
377
  config: !1,
277
378
  logLevel: "warn",
278
379
  ...options.watch ? { watch: !0 } : {}
279
380
  };
280
381
  }
281
- export { resolveTsdownBuilds as n, resolveTsdownConfig as t };
382
+ /**
383
+ * Declares the conditional export of every CSS file a watch rebuild emitted.
384
+ *
385
+ * A full build leaves this to `cssNodeCompatPlugin`, which composes into tsdown's
386
+ * `exports.customExports`. Watch mode turns tsdown's `exports` feature off (a `package.json`
387
+ * write per rebuild would loop the watcher), so `pkg watch` maintains the exports itself. Most
388
+ * of them are known before the build and are written once per context in `watch.ts`, but the
389
+ * merged `style.css` of CSS imported from JS only exists when something actually imports CSS —
390
+ * declaring it from the config alone would point the export at files nobody produced.
391
+ *
392
+ * `build:done` is the only place that knows: in watch mode `build()` resolves before the first
393
+ * rebuild runs, so the returned bundle's chunks are still empty. The write is idempotent, so
394
+ * the `package.json` watcher settles after one extra rebuild rather than looping.
395
+ * @internal
396
+ */
397
+ function createWatchCssExportsHook(ctx, css) {
398
+ let mergedCssName = css.splitting ? void 0 : css.fileName || "style.css";
399
+ return (hooks) => {
400
+ hooks.hook("build:done", async ({ chunks }) => {
401
+ if (mergedCssName === void 0 || !chunks.some((chunk) => chunk.type === "asset" && chunk.fileName === mergedCssName)) return;
402
+ let { writeBundleCssExports } = await import("./writeBundleCssExports-DGTofClh.js").then((n) => n.n);
403
+ await writeBundleCssExports({
404
+ cwd: ctx.cwd,
405
+ distPath: ctx.distPath,
406
+ cssNames: [mergedCssName],
407
+ logger: ctx.logger
408
+ });
409
+ });
410
+ };
411
+ }
412
+ export { createConditionalCssExport as n, resolveTsdownBuilds as r, resolveTsdownConfig as t };
282
413
 
283
- //# sourceMappingURL=resolveTsdownConfig-CGE_7cMM.js.map
414
+ //# sourceMappingURL=resolveTsdownConfig-D4boYIwx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTsdownConfig-D4boYIwx.js","names":[],"sources":["../src/node/tasks/tsdown/resolveTsdownBuilds.ts","../src/node/core/pkg/cssShimFileName.ts","../src/node/core/pkg/cssExport.ts","../src/node/tasks/tsdown/composeExports.ts","../src/node/tasks/tsdown/resolveTsdownConfig.ts"],"sourcesContent":["import path from 'node:path'\nimport type {PkgFormat, PkgRuntime} from '../../core/config/types.ts'\nimport type {BuildContext} from '../../core/contexts/buildContext.ts'\nimport {fileEnding} from '../../core/pkg/pkgExt.ts'\n\n/**\n * One entry of a tsdown build.\n * @internal\n */\nexport interface TsdownBuildEntry {\n /**\n * The entry alias handed to tsdown: the output path relative to `dist` without the\n * extension (e.g. `index`, `index.browser`, `sub/feature`), so the emitted filenames match\n * the hand-written `exports` targets exactly.\n */\n alias: string\n source: string\n /** The hand-written export subpath this entry backs (`undefined` for `bundles`). */\n exportPath?: string\n /** The formats the hand-written exports declare for this entry. */\n formats: PkgFormat[]\n}\n\n/**\n * One tsdown `build()` call of the waterfall. Builds run serially — variants and bundles\n * first, the canonical build last, so its exports generation and publint see every emitted\n * file on disk.\n * @internal\n */\nexport interface TsdownBuild {\n /** Stable identifier, e.g. `neutral`, `browser`, `node`, `bundles`, `css`. */\n key: string\n runtime: PkgRuntime\n /** The canonical build owns `dist` conventions: exports generation runs here. */\n canonical: boolean\n entries: TsdownBuildEntry[]\n /**\n * The stylesheet build: its entries are `.css` files rather than JS, so it emits CSS assets\n * (one per entry) and no JS at all. It runs on its own because a `.css` entry has nothing to\n * declare types for, and because its per-entry CSS output needs `css.splitting`.\n */\n css?: boolean\n}\n\n/** The build key of the stylesheet build. */\nconst CSS_BUILD_KEY = 'css'\n\n/**\n * Collapses the hand-written `exports` map (+ `bundles`) into the per-platform tsdown build\n * waterfall: one canonical build for the package's default runtime, plus a variant build per\n * `browser`/`node` exports condition, plus builds for `bundles` (which must not participate\n * in exports generation).\n * @internal\n */\nexport function resolveTsdownBuilds(ctx: BuildContext): TsdownBuild[] {\n const {config, cwd, distPath, logger} = ctx\n\n const entryAlias = (output: string): string => {\n const alias = path\n .relative(distPath, path.resolve(cwd, output))\n .replaceAll('\\\\', '/')\n .replace(fileEnding, '')\n if (alias.startsWith('..')) {\n throw new Error(`output file is outside the \\`dist\\` folder: ${output}`)\n }\n return alias\n }\n\n interface EntryDraft {\n alias: string\n source: string\n exportPath?: string | undefined\n formats: Set<PkgFormat>\n }\n\n const draftsByBuild = new Map<string, Map<string, EntryDraft>>()\n const runtimeByBuild = new Map<string, PkgRuntime>()\n\n const addEntry = (\n buildKey: string,\n runtime: PkgRuntime,\n entry: {\n source: string\n exportPath?: string | undefined\n import?: string | undefined\n require?: string | undefined\n },\n ) => {\n runtimeByBuild.set(buildKey, runtime)\n const {source, exportPath} = entry\n const aliases = new Set<string>()\n const formats = new Set<PkgFormat>()\n if (entry.import) {\n aliases.add(entryAlias(entry.import))\n formats.add('esm')\n }\n if (entry.require) {\n aliases.add(entryAlias(entry.require))\n formats.add('commonjs')\n }\n if (aliases.size === 0) return\n if (aliases.size > 1) {\n throw new Error(\n `the \\`import\\` and \\`require\\` targets of ${\n exportPath ? `exports[\"${exportPath}\"]` : `the bundle for ${source}`\n } must share a basename (e.g. \\`./dist/index.js\\` + \\`./dist/index.cjs\\`), ` +\n `got: ${entry.import} and ${entry.require}`,\n )\n }\n const [alias] = aliases\n let drafts = draftsByBuild.get(buildKey)\n if (!drafts) {\n drafts = new Map()\n draftsByBuild.set(buildKey, drafts)\n }\n const existing = drafts.get(alias!)\n if (existing) {\n if (existing.source !== source) {\n throw new Error(\n `conflicting sources for the output alias \"${alias}\": ${existing.source} and ${source}`,\n )\n }\n for (const format of formats) existing.formats.add(format)\n return\n }\n drafts.set(alias!, {alias: alias!, source, exportPath, formats})\n }\n\n const exports = Object.entries(ctx.exports || {})\n\n let hasRuntimeConditions = false\n\n for (const [exportPath, exp] of exports) {\n addEntry('canonical', ctx.runtime, {\n source: exp.source,\n exportPath,\n import: exp.import,\n require: exp.require,\n })\n\n if (exp.browser?.import || exp.browser?.require) {\n hasRuntimeConditions = true\n addEntry('browser', 'browser', {\n source: exp.browser.source || exp.source,\n exportPath,\n import: exp.browser.import,\n require: exp.browser.require,\n })\n }\n\n if (exp.node?.import || exp.node?.require) {\n hasRuntimeConditions = true\n addEntry('node', 'node', {\n source: exp.node.source || exp.source,\n exportPath,\n import: exp.node.import,\n require: exp.node.require,\n })\n }\n }\n\n // `bundles` are extra entrypoints that are deliberately not in the exports map (CLI workers\n // and similar), so they build separately from the canonical build — exports generation\n // derives subpaths from every entry of its build, and bundles must never become export\n // subpaths of their own.\n for (const bundle of config?.bundles || []) {\n const runtime = bundle.runtime || ctx.runtime\n addEntry(runtime === ctx.runtime ? 'bundles' : `bundles:${runtime}`, runtime, {\n source: bundle.source,\n import: bundle.import,\n require: bundle.require,\n })\n }\n\n if (hasRuntimeConditions) {\n logger.warn(\n [\n 'The `exports[].browser.source` / `exports[].node.source` pattern is not recommended: every',\n 'runtime condition adds a full extra build (complexity and build time). Consider instead:',\n ' 1. separate npm packages per platform/runtime, selected through export conditions that',\n ' pick the right package per environment,',\n ' 2. when possible, a single neutral build using JS that works in both runtimes without',\n ' special-casing (e.g. `new URL` over `require(\"url\")`, WebCrypto over',\n ' `require(\"crypto\")`), or',\n ' 3. using `tsdown` + `@sanity/tsdown-config` directly, exporting an array from',\n ' `tsdown.config.ts` with one config per `platform` — the fully supported path for',\n ' this level of customization.',\n ].join('\\n'),\n )\n }\n\n const builds: TsdownBuild[] = []\n\n // `.css` export subpaths that declare a `source` build in their own pass: their entries are\n // stylesheets, so the emitted file name follows the export subpath (`./ui/styles.css` ->\n // `dist/ui/styles.css`) instead of an `import`/`require` target, and `dts` has nothing to do.\n const cssEntries: TsdownBuildEntry[] = ctx.cssExports.map((cssExport) => ({\n alias: cssEntryAlias(cssExport._path),\n source: cssExport.source,\n exportPath: cssExport._path,\n formats: ['esm'],\n }))\n if (cssEntries.length) {\n builds.push({\n key: CSS_BUILD_KEY,\n runtime: ctx.runtime,\n canonical: false,\n entries: cssEntries,\n css: true,\n })\n }\n\n const toBuild = (key: string, runtime: PkgRuntime, canonical: boolean): TsdownBuild | null => {\n const drafts = draftsByBuild.get(key)\n if (!drafts || drafts.size === 0) return null\n return {\n key,\n runtime,\n canonical,\n entries: Array.from(drafts.values(), (draft) => ({\n alias: draft.alias,\n source: draft.source,\n ...(draft.exportPath === undefined ? {} : {exportPath: draft.exportPath}),\n formats: Array.from(draft.formats),\n })),\n }\n }\n\n // Variants and bundles run first; the canonical build runs last so its exports generation\n // and publint see the other builds' files on disk. Each build's runtime was recorded when\n // its entries were added, so nothing is re-derived from the build key (a bundle with\n // `runtime: '*'` in a `runtime: 'node'` package must build for `'*'`/neutral).\n for (const key of draftsByBuild.keys()) {\n if (key === 'canonical') continue\n const build = toBuild(key, runtimeByBuild.get(key) ?? ctx.runtime, false)\n if (build) builds.push(build)\n }\n\n const canonical = toBuild('canonical', ctx.runtime, true)\n if (canonical) builds.push(canonical)\n\n return builds\n}\n\n/**\n * The tsdown entry alias of a `.css` export subpath: the subpath without its leading `./` and\n * `.css` ending, so `@tsdown/css` (with `splitting`) emits the stylesheet at exactly the path\n * the subpath promises — `\"./ui/styles.css\"` -> alias `ui/styles` -> `dist/ui/styles.css`.\n */\nfunction cssEntryAlias(exportPath: string): string {\n return exportPath.replace(/^\\.\\//, '').replace(/\\.css$/, '')\n}\n","/**\n * The no-op JS shim file name for a CSS file under vanilla-extract compat mode.\n *\n * `bundle.css` → `bundle-css.js` — deliberately not `${cssFileName}.js` (`bundle.css.js`),\n * which vanilla-extract's `cssFileFilter` (`/\\.css\\.(js|cjs|mjs|jsx|ts|tsx)$/`) would treat as\n * a stylesheet module. Kept in sync with `cssShimFileName` in\n * `@sanity/vanilla-extract-rolldown-plugin`.\n *\n * @internal\n */\nexport function cssShimFileName(cssFileName: string): string {\n return `${cssFileName.replace(/\\.css$/, '-css')}.js`\n}\n\n/**\n * The `.d.ts` companion for {@link cssShimFileName}. `bundle.css` → `bundle-css.d.ts`.\n *\n * @internal\n */\nexport function cssShimDtsFileName(cssFileName: string): string {\n return `${cssFileName.replace(/\\.css$/, '-css')}.d.ts`\n}\n","import path from 'node:path'\nimport {cssShimDtsFileName, cssShimFileName} from './cssShimFileName.ts'\n\n/**\n * Build the conditional CSS export object that `exports.nodeCompat` expects, e.g.\n * ```json\n * {\n * \"types\": \"./dist/bundle-css.d.ts\",\n * \"browser\": \"./dist/bundle.css\",\n * \"style\": \"./dist/bundle.css\",\n * \"node\": \"./dist/bundle-css.js\",\n * \"default\": \"./dist/bundle-css.js\"\n * }\n * ```\n * The shim is named `bundle-css.js` (not `bundle.css.js`) so it does not match\n * vanilla-extract's `cssFileFilter`. An explicit `types` condition (rather than relying on\n * TypeScript's extension-substitution fallback, which only works when the shim shares the CSS\n * file's basename, and which TypeScript is deprecating anyway - microsoft/TypeScript#50762)\n * points resolvers straight at the shim's declaration file.\n *\n * Kept in sync with `createConditionalCssExport` in `@sanity/vanilla-extract-tsdown-plugin`,\n * which writes the same entry through tsdown's `exports.customExports` during full builds.\n *\n * @internal\n */\nexport function createConditionalCssExport(\n cssName: string,\n distRel: string,\n): Record<string, string> {\n const cssFile = `./${path.posix.join(distRel, cssName)}`\n const shimFile = `./${path.posix.join(distRel, cssShimFileName(cssName))}`\n const shimDtsFile = `./${path.posix.join(distRel, cssShimDtsFileName(cssName))}`\n return {types: shimDtsFile, browser: cssFile, style: cssFile, node: shimFile, default: shimFile}\n}\n","import path from 'node:path'\nimport type {PkgExport} from '../../core/config/types.ts'\nimport type {BuildContext} from '../../core/contexts/buildContext.ts'\nimport {isRecord} from '../../core/isRecord.ts'\nimport {createConditionalCssExport} from '../../core/pkg/cssExport.ts'\nimport type {TsdownBuild} from './resolveTsdownBuilds.ts'\n\ntype ExportsMap = Record<string, unknown>\ninterface ComposeContext {\n isPublish: boolean\n}\n\n/**\n * The pkg-utils opinion layer over tsdown's generated `exports` map, composed into\n * `exports.customExports` of the canonical build (the same composition hook\n * `@sanity/vanilla-extract-tsdown-plugin` uses for its conditional CSS export).\n *\n * tsdown generates subpaths from the entry aliases with `source`/`import`/`require` conditions\n * (`devExports: 'source'`) and a `source`-less `publishConfig.exports` — already the Sanity\n * convention. This composer reconciles the generated map with the hand-written one, which\n * remains the input:\n *\n * - generated keys are remapped to the hand-written subpaths (entry aliases are derived from\n * the output paths, which don't have to match the subpath names),\n * - the hand-written `types`, `browser`, `node`, `development` and `monorepo` conditions are\n * re-inserted (tsdown's generator cannot express them; the `browser`/`node` files are built\n * by the variant builds of the waterfall) — with `source`-like conditions stripped from the\n * publish variant,\n * - all hand-written conditions (`react-server`, `worker`, … included) retain their authored\n * order in each map independently, because earlier matching conditions take precedence,\n * - generated conditions for conditional entries are materialized in both `exports` and\n * `publishConfig.exports`, so they can be reordered directly in `package.json` and keep that\n * position on later builds (plain-string entries stay compact),\n * - a trailing `default` condition is kept on dual-format entries (tsdown emits bare\n * `import`/`require` pairs; the Sanity convention always ends with `default`),\n * - hand-written subpaths that aren't build entries (`.css`/`.json` exports, `svelte`\n * entries) are carried over untouched, and\n * - the hand-written subpath and condition key order of each map is preserved.\n * @internal\n */\nexport function createExportsComposer(\n ctx: BuildContext,\n build: TsdownBuild,\n): (exportsMap: ExportsMap, context: ComposeContext) => ExportsMap {\n const {pkg} = ctx\n const type = pkg.type === 'module' ? 'module' : 'commonjs'\n\n // POSIX separators: on Windows `path.relative` yields backslashes, which must never leak\n // into generated `package.json` export targets.\n const distRel = (path.relative(ctx.cwd, ctx.distPath) || 'dist').split(path.sep).join('/')\n const cssExportPaths = new Set(ctx.cssExports.map((cssExport) => cssExport._path))\n const cssSources: Record<string, string> = {}\n for (const cssExport of ctx.cssExports) {\n cssSources[cssExport._path] = cssExport.source\n }\n\n // alias -> hand-written subpath, e.g. `index` -> `.`, `sub/feature` -> `./feature`\n const aliasToExportPath = new Map<string, string>()\n for (const entry of build.entries) {\n if (entry.exportPath !== undefined) {\n aliasToExportPath.set(entry.alias, entry.exportPath)\n }\n }\n\n return (exportsMap, context) => {\n const {isPublish} = context\n\n // 1. Remap the generated keys (`.` for the `index` alias, `./<alias>` otherwise) back to\n // the hand-written subpaths.\n const remapped: ExportsMap = {}\n for (const [key, value] of Object.entries(exportsMap)) {\n const alias = key === '.' ? 'index' : key.startsWith('./') ? key.slice(2) : key\n const exportPath = aliasToExportPath.get(alias) ?? key\n remapped[exportPath] = value\n }\n\n // 2. Reconcile each generated entry with its hand-written counterpart.\n const result: ExportsMap = {}\n const handwritten = ctx.exports || {}\n const sourceRaw: ExportsMap = pkg.exports || {}\n const publishRaw: ExportsMap | undefined = pkg.publishConfig?.exports\n const authoredRaw = isPublish && publishRaw ? publishRaw : sourceRaw\n\n const reconcile = (exportPath: string, value: unknown): unknown => {\n const exp = handwritten[exportPath]\n if (!exp) return value\n const raw = authoredRaw[exportPath]\n const source = sourceRaw[exportPath]\n // A configured exports map is itself authoritative. Otherwise use the raw package entry\n // for the map being generated. Fall back to `exports` when a new publish map/entry is\n // being generated, so inferred conditions don't masquerade as ordering choices.\n const authored =\n ctx.config?.exports === undefined\n ? isRecord(raw)\n ? raw\n : isRecord(source)\n ? source\n : exp\n : exp\n return reconcileEntry(exp, value, {authored, isPublish, type})\n }\n\n // 3. Follow the hand-written key order of the map being generated. Source-only passthrough\n // subpaths missing from an existing publish map are appended, followed by generated extras.\n const authoredPaths = Object.keys(authoredRaw)\n for (const exportPath of Object.keys(sourceRaw)) {\n if (!Object.prototype.hasOwnProperty.call(authoredRaw, exportPath)) {\n authoredPaths.push(exportPath)\n }\n }\n for (const exportPath of authoredPaths) {\n if (exportPath in remapped) {\n result[exportPath] = reconcile(exportPath, remapped[exportPath])\n } else if (cssExportPaths.has(exportPath)) {\n // A `.css` subpath with a `source` is built by the stylesheet build, which does not\n // participate in exports generation (its entries are stylesheets, not JS). Its\n // conditions are materialized here instead, from the export subpath: `./ui/styles.css`\n // is built to `<dist>/ui/styles.css` with the shim next to it.\n result[exportPath] = reconcileCssEntry(exportPath, {\n distRel,\n source: cssSources[exportPath],\n isPublish,\n })\n } else {\n // Hand-written subpaths that aren't build entries (plain `.css`/`.json` exports,\n // `svelte` entries) pass through untouched.\n result[exportPath] = Object.prototype.hasOwnProperty.call(authoredRaw, exportPath)\n ? authoredRaw[exportPath]\n : sourceRaw[exportPath]\n }\n }\n for (const [exportPath, value] of Object.entries(remapped)) {\n if (exportPath in result) continue\n result[exportPath] = reconcile(exportPath, value)\n }\n\n return result\n }\n}\n\n/**\n * The conditional CSS export of a `.css` subpath built by the stylesheet build. `source`\n * resolves at development time, so it is kept in `exports` and stripped from the publish\n * variant — the same split the build entries get.\n */\nfunction reconcileCssEntry(\n exportPath: string,\n options: {distRel: string; source: string | undefined; isPublish: boolean},\n): Record<string, string> {\n const {distRel, source, isPublish} = options\n const cssName = exportPath.replace(/^\\.\\//, '')\n const conditions = createConditionalCssExport(cssName, distRel)\n return isPublish || source === undefined ? conditions : {source, ...conditions}\n}\n\n/**\n * Rebuilds a generated subpath entry in its authored condition order, re-inserting the\n * hand-written conditions tsdown's generator cannot express.\n */\nfunction reconcileEntry(\n exp: PkgExport,\n generated: unknown,\n options: {authored: object; isPublish: boolean; type: 'commonjs' | 'module'},\n): unknown {\n const {authored, isPublish, type} = options\n const authoredRecord = isRecord(authored) ? authored : {}\n\n // tsdown's publish variant of a single-format entry is a plain string\n const gen: Record<string, unknown> | undefined =\n typeof generated === 'string'\n ? {default: generated}\n : isRecord(generated)\n ? generated\n : undefined\n if (!gen) return generated\n\n const browserOrder = isRecord(authoredRecord['browser']) ? authoredRecord['browser'] : exp.browser\n const browser =\n exp.browser && (exp.browser.import || exp.browser.require)\n ? pickConditions(exp.browser, isPublish, browserOrder ?? exp.browser)\n : undefined\n const nodeOrder = isRecord(authoredRecord['node']) ? authoredRecord['node'] : exp.node\n const node =\n exp.node && (exp.node.import || exp.node.require)\n ? pickConditions(exp.node, isPublish, nodeOrder ?? exp.node)\n : undefined\n const custom = pickCustomConditions(exp)\n\n // Preserve tsdown's compact single-format publish shape unless hand-written conditions need\n // to be re-inserted. There is no condition ordering to preserve in a plain string entry.\n if (typeof generated === 'string' && !exp.types && !browser && !node && custom.length === 0) {\n return generated\n }\n\n const next: Record<string, unknown> = {}\n\n // `source`-like conditions resolve at development time and are stripped from the publish\n // variant (tsdown already does this for `source`; `development`/`monorepo` follow)\n if (!isPublish) {\n if (typeof gen['source'] === 'string') next['source'] = gen['source']\n else if (exp.source) next['source'] = exp.source\n if (exp.development) next['development'] = exp.development\n if (exp.monorepo) next['monorepo'] = exp.monorepo\n }\n\n if (exp.types) next['types'] = exp.types\n if (browser) next['browser'] = browser\n if (node) next['node'] = node\n\n // Hand-written custom conditions (`react-server`, `worker`, …) aren't built, but they are\n // the author's: carry their targets over, then restore every condition's authored position.\n for (const [condition, target] of custom) {\n const authoredTarget = authoredRecord[condition]\n next[condition] =\n isRecord(target) && isRecord(authoredTarget)\n ? preserveConditionOrder(target, authoredTarget)\n : target\n }\n\n if (typeof gen['import'] === 'string' && typeof gen['require'] === 'string') {\n next['import'] = gen['import']\n next['require'] = gen['require']\n // tsdown emits bare `import`/`require` pairs; the Sanity convention ends with `default`\n next['default'] = type === 'module' ? gen['import'] : gen['require']\n } else {\n // Single-format entries keep the generated shape (`{source, default}` in development)\n for (const [condition, target] of Object.entries(gen)) {\n if (condition in next || condition === 'source') continue\n next[condition] = target\n }\n }\n\n return preserveConditionOrder(next, authored)\n}\n\n/** The hand-written `browser`/`node` condition object, minus `source` for the publish map. */\nfunction pickConditions(\n conditions: {source?: string; import?: string; require?: string},\n isPublish: boolean,\n authored: object = conditions,\n): Record<string, string> {\n const next: Record<string, string> = {}\n if (!isPublish && conditions.source) next['source'] = conditions.source\n if (conditions.import) next['import'] = conditions.import\n if (conditions.require) next['require'] = conditions.require\n return preserveConditionOrder(next, authored)\n}\n\n/**\n * Reorders reconciled conditions to match their hand-written order. Conditions generated by\n * tsdown but absent from the hand-written entry are inserted before its `default` fallback.\n */\nfunction preserveConditionOrder<T>(\n conditions: Record<string, T>,\n authored: object,\n): Record<string, T> {\n const entries: [string, T][] = []\n const added = new Set<string>()\n const conditionEntries = Object.entries(conditions)\n const entriesByCondition = new Map(conditionEntries.map((entry) => [entry[0], entry]))\n const authoredOrder = Object.keys(authored).filter((condition) => !condition.startsWith('_'))\n const authoredConditions = new Set(authoredOrder)\n\n const addGeneratedConditions = () => {\n for (const entry of conditionEntries) {\n if (!authoredConditions.has(entry[0]) && !added.has(entry[0])) {\n entries.push(entry)\n added.add(entry[0])\n }\n }\n }\n\n for (const condition of authoredOrder) {\n // A generated condition has no authored position. Keep the explicit `default` as the final\n // fallback by placing generated conditions immediately before it.\n if (condition === 'default') addGeneratedConditions()\n const entry = entriesByCondition.get(condition)\n if (!entry) continue\n entries.push(entry)\n added.add(condition)\n }\n\n addGeneratedConditions()\n\n return Object.fromEntries(entries)\n}\n\n/** The conditions the pipeline owns (or re-inserts itself) on a build entry. */\nconst managedConditions = new Set([\n 'source',\n 'development',\n 'monorepo',\n 'types',\n 'browser',\n 'node',\n 'import',\n 'require',\n 'default',\n])\n\n/**\n * Hand-written conditions the pipeline knows nothing about (`react-server`, `worker`,\n * `edge-light`, …), in authored order. `parseExports` spreads the raw entry, so they survive\n * on the parsed `PkgExport` beyond its typed fields.\n */\nfunction pickCustomConditions(exp: PkgExport): [string, unknown][] {\n return Object.entries(exp).filter(\n ([condition, target]) =>\n !managedConditions.has(condition) && !condition.startsWith('_') && target !== undefined,\n )\n}\n","import path from 'node:path'\nimport {defineConfig} from '@sanity/tsdown-config'\nimport {mergeConfig, type InlineConfig, type UserConfig} from 'tsdown'\nimport type {PkgConfigOptions} from '../../core/config/types.ts'\nimport type {BuildContext} from '../../core/contexts/buildContext.ts'\nimport {pkgExtMap} from '../../core/pkg/pkgExt.ts'\nimport {createExportsComposer} from './composeExports.ts'\nimport type {TsdownBuild} from './resolveTsdownBuilds.ts'\n\nconst RE_TS_SOURCE = /\\.[cm]?tsx?$/\n\n/**\n * Composes the tsdown config for one build of the waterfall: `@sanity/tsdown-config`'s\n * `defineConfig()` provides the shared Sanity base, and the pkg-utils opinions (browserslist\n * targets, `PKG_*` defines, exports reconciliation, dts selection) layer over it with\n * tsdown's `mergeConfig`.\n *\n * pkg-utils owns its own experience: the returned config carries `config: false`, so tsdown\n * never loads `tsdown.config.*` files — `package.config.ts` is the sole config source — and\n * `logLevel: 'warn'` keeps tsdown's info chatter out of pkg-utils' own output.\n * @internal\n */\nexport async function resolveTsdownConfig(\n ctx: BuildContext,\n build: TsdownBuild,\n options: {\n /**\n * Whether this build may clean: only the first build of the waterfall cleans (so later\n * builds can't wipe earlier output), and `--no-clean` turns it off for the whole run.\n */\n clean: boolean\n watch?: boolean\n },\n): Promise<InlineConfig> {\n const {config, cwd, distPath, pkg} = ctx\n\n const reactCompiler = config?.reactCompiler\n if (typeof reactCompiler === 'object' && reactCompiler.reactServer === true) {\n throw new Error(\n [\n 'package.config.ts: `reactCompiler.reactServer` is not supported by `pkg build` — the',\n 'dual React Server Components build needs one tsdown run driving multiple configs.',\n 'Use `tsdown` + `@sanity/tsdown-config` directly instead: export the config from',\n '`tsdown.config.ts` and build with `tsdown`.',\n ].join('\\n'),\n )\n }\n\n const entry: Record<string, string> = {}\n for (const buildEntry of build.entries) {\n entry[buildEntry.alias] = buildEntry.source\n }\n\n // tsdown's `format` applies to the whole build (and its exports generation composes the\n // dual `import`/`require` map from both formats' chunks of one build), so the entries'\n // formats union: every entry is emitted in every format of the build. Mixed per-entry\n // coverage gets a heads-up — the extra files are emitted, and local exports generation\n // will declare them.\n const formats = new Set(build.entries.flatMap((buildEntry) => buildEntry.formats))\n if (formats.size > 1) {\n const partial = build.entries.filter((buildEntry) => buildEntry.formats.length < formats.size)\n if (partial.length) {\n const names = partial\n .map((buildEntry) =>\n buildEntry.exportPath ? `exports[\"${buildEntry.exportPath}\"]` : buildEntry.source,\n )\n .join(', ')\n ctx.logger.warn(\n `${names} declare${partial.length === 1 ? 's' : ''} fewer formats than the rest of the package. tsdown emits every format of a build for every entry, so the missing format is built anyway (and local exports generation will declare it). Declare both \\`import\\` and \\`require\\` targets for every subpath — or for none — to keep the exports map unambiguous.`,\n )\n }\n }\n const format = [\n ...(formats.has('esm') ? ['esm' as const] : []),\n ...(formats.has('commonjs') ? ['cjs' as const] : []),\n ]\n\n const platform =\n build.runtime === 'node' ? 'node' : build.runtime === 'browser' ? 'browser' : 'neutral'\n\n // The `@tsdown/css` pipeline turns on when it's configured, and automatically for a package\n // that declares a `.css` export subpath with a `source`. The stylesheet build needs\n // `splitting` so each entry emits its own file at the path its subpath promises; the JS\n // builds keep `@tsdown/css`'s merged default, so CSS imported from JS lands in a single\n // `style.css` with one export and one injected import - the `bundle.css` shape of\n // `vanillaExtract`.\n const css: PkgConfigOptions['css'] | undefined =\n config?.css || ctx.cssExports.length\n ? {...config?.css, ...(build.css ? {splitting: true} : {})}\n : undefined\n\n // Build-time constants: `PKG_VERSION` reads the environment override first, like v11.\n // pkg-utils' own build skips it so the replacement logic in this very file survives its own\n // bundling. (`PKG_FORMAT`, `PKG_RUNTIME` and `PKG_FILE_PATH` were removed in v12 — see\n // MIGRATE.md for the `package.json#imports` / `import.meta.url` replacements.)\n const define: Record<string, string> = {}\n if (pkg.name !== '@sanity/pkg-utils') {\n define['process.env.PKG_VERSION'] = JSON.stringify(process.env['PKG_VERSION'] || pkg.version)\n }\n for (const [key, value] of Object.entries(config?.define || {})) {\n define[key] = JSON.stringify(value)\n }\n\n // Types are generated with tsdown (rolldown-plugin-dts). `@typescript/native-preview` in\n // devDependencies auto-enables tsgo, like v11; an explicit `dts.tsgo` wins. Only the object\n // form spreads: when the `legacyChecks` migration errors are skipped\n // (`NODE_ENV=production` / `legacyChecks: false`), a leftover v11 string like\n // `dts: 'rolldown'` must degrade to the default behavior (which is what it meant) instead\n // of spreading into numeric character keys.\n const hasTsSources =\n !build.css && build.entries.some((buildEntry) => RE_TS_SOURCE.test(buildEntry.source))\n const dtsPassthrough = typeof config?.dts === 'object' ? config.dts : undefined\n const dts =\n hasTsSources && config?.dts !== false\n ? {\n ...(typeof pkg.devDependencies === 'object' &&\n '@typescript/native-preview' in pkg.devDependencies\n ? {tsgo: true}\n : {}),\n // Always create dts from scratch, don't reuse contexts from previous builds\n newContext: true,\n ...dtsPassthrough,\n ...(ctx.emitDeclarationOnly ? {emitDtsOnly: true} : {}),\n }\n : false\n\n // Exports generation runs on the canonical build only, with `devExports: 'source'` — the\n // hand-written Sanity convention (`source` conditions in `exports`, a `source`-less\n // `publishConfig.exports`) — and the pkg-utils composer reconciling the generated map with\n // the hand-written one. `@sanity/tsdown-config`'s always-on exports default applies: the map\n // is rewritten on every build (CI included), so environments that set `CI=true` without\n // meaning \"skip package.json\" (Cursor Cloud, …) still keep exports in sync. A types-only\n // build never rewrites `package.json`, and neither do watch builds (a rewrite would\n // re-trigger the `package.json` watcher).\n const exports: UserConfig['exports'] =\n build.canonical && !ctx.emitDeclarationOnly && !options.watch && !build.css\n ? {\n devExports: 'source',\n customExports: createExportsComposer(ctx, build),\n // Keep the hand-written legacy fields (`main`/`module`) in sync instead of deleting\n // them; packages without them don't gain them\n ...(pkg.main || pkg.module ? {legacy: true} : {}),\n }\n : false\n\n // `@sanity/tsdown-config` defaults `tsdoc` to `false`; pkg-utils keeps the historical\n // default of enabled (`true`), and forwards an options object (with `bundledPackages` for\n // API Extractor's type resolution of inlined deps) when the user customized rules/tags.\n const tsdocOption =\n config?.tsdoc === false\n ? false\n : {\n ...(typeof config?.tsdoc === 'object' ? config.tsdoc : {}),\n bundledPackages: ctx.bundledPackages,\n }\n\n const base = await defineConfig({\n cwd,\n tsconfig: ctx.ts.configPath,\n platform,\n format,\n entry,\n // POSIX separators: on Windows `path.relative` yields backslashes, which would leak into\n // generated `package.json` export targets (e.g. the conditional vanilla-extract export)\n outDir: path.relative(cwd, distPath).replaceAll('\\\\', '/') || '.',\n target: ctx.target[build.runtime],\n define,\n sourcemap: config?.sourcemap,\n // tsdown owns cleaning: the first build of the waterfall carries the effective `clean`\n // (the config passthrough, or tsdown's default `true`), every later build gets `false`.\n // A types-only build never cleans, so it can't delete JS output.\n clean: options.clean && !ctx.emitDeclarationOnly ? config?.clean : false,\n dts,\n deps: ctx.deps,\n exports,\n css,\n reactCompiler: config?.reactCompiler,\n styledComponents: config?.styledComponents,\n vanillaExtract: config?.vanillaExtract,\n // Types-only builds still emit `.d.ts` files that deserve the check; watch mode skips it\n // so a failing TSDoc rule doesn't tear down the watcher on every save.\n tsdoc: options.watch ? false : tsdocOption,\n })\n\n // The hand-written exports define the emitted extensions (`.js`/`.mjs`/`.cjs` per\n // `package.json#type`, enforced by `validateExports`), so the extensions are pinned\n // explicitly instead of relying on tsdown's defaults (whose `fixedExtension` kicks in for\n // `platform: 'node'` and would emit `.mjs` for `type: module` packages).\n const extMap = pkgExtMap[pkg.type === 'module' ? 'module' : 'commonjs']\n const outExtensions: UserConfig['outExtensions'] = ({format: outputFormat}) => ({\n js: outputFormat === 'cjs' ? extMap.commonjs : extMap.esm,\n })\n\n const merged = mergeConfig(base, {\n outExtensions,\n // publint runs during `pkg check` (via its node API), not inside the build\n publint: false,\n // the per-file size report logs through tsdown's info channel; pkg-utils prints its own\n report: false,\n ...(config?.minify === true ? {minify: true} : {}),\n ...(config?.plugins === undefined ? {} : {plugins: config.plugins}),\n ...(options.watch && css ? {hooks: createWatchCssExportsHook(ctx, css)} : {}),\n })\n\n return {\n ...merged,\n config: false,\n logLevel: 'warn',\n ...(options.watch ? {watch: true} : {}),\n }\n}\n\n/**\n * Declares the conditional export of every CSS file a watch rebuild emitted.\n *\n * A full build leaves this to `cssNodeCompatPlugin`, which composes into tsdown's\n * `exports.customExports`. Watch mode turns tsdown's `exports` feature off (a `package.json`\n * write per rebuild would loop the watcher), so `pkg watch` maintains the exports itself. Most\n * of them are known before the build and are written once per context in `watch.ts`, but the\n * merged `style.css` of CSS imported from JS only exists when something actually imports CSS —\n * declaring it from the config alone would point the export at files nobody produced.\n *\n * `build:done` is the only place that knows: in watch mode `build()` resolves before the first\n * rebuild runs, so the returned bundle's chunks are still empty. The write is idempotent, so\n * the `package.json` watcher settles after one extra rebuild rather than looping.\n * @internal\n */\nfunction createWatchCssExportsHook(\n ctx: BuildContext,\n css: NonNullable<PkgConfigOptions['css']>,\n): NonNullable<UserConfig['hooks']> {\n // Only the merged mode has a CSS file name to declare up front. With `splitting` the names\n // follow the chunk names and the export is the host's to wire up, so a full build declares\n // nothing either.\n const mergedCssName = css.splitting ? undefined : css.fileName || 'style.css'\n\n return (hooks) => {\n hooks.hook('build:done', async ({chunks}) => {\n if (mergedCssName === undefined) return\n const emitted = chunks.some(\n (chunk) => chunk.type === 'asset' && chunk.fileName === mergedCssName,\n )\n if (!emitted) return\n\n const {writeBundleCssExports} = await import('../../core/pkg/writeBundleCssExports.ts')\n await writeBundleCssExports({\n cwd: ctx.cwd,\n distPath: ctx.distPath,\n cssNames: [mergedCssName],\n logger: ctx.logger,\n })\n })\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ import { n as createLogger, r as isRecord, t as handleError } from "./handleError-83GwKIFM.js";
2
+ import { a as loadPkgWithReporting, o as loadConfig, t as resolveBuildContext } from "./resolveBuildContext-DYZxWVVc.js";
3
+ import { r as resolveTsdownBuilds, t as resolveTsdownConfig } from "./resolveTsdownConfig-D4boYIwx.js";
4
+ import { t as writeBundleCssExports } from "./writeBundleCssExports-DGTofClh.js";
5
+ import { up } from "empathic/package";
6
+ import { build } from "tsdown";
7
+ import { switchMap } from "rxjs";
8
+ /**
9
+ * Whether a CSS pipeline's options wire up the conditional CSS export pattern — the no-op JS
10
+ * shim, its declaration file, and the `"./<fileName>"` export whose `node`/`default` conditions
11
+ * point at the shim.
12
+ *
13
+ * `pkg watch` needs this to decide which exports to maintain itself, because watch mode turns
14
+ * tsdown's `exports` feature off. Full builds never ask: the plugins own the answer there.
15
+ *
16
+ * Only `exports` decides. `@sanity/tsdown-config` composes the plugin options as
17
+ * `{inject: true, exports: {nodeCompat: true}, ...userOptions}`, so `exports` is always set by
18
+ * the time `resolveCssExportOptions` sees them — a user-supplied `inject` never clears the
19
+ * default. That makes the plugin's compatibility fallback for the deprecated
20
+ * `inject: {nodeCompat: true}` spelling unreachable from here, and reading `inject` would get
21
+ * `{inject: {nodeCompat: false}}` wrong: it still leaves the `exports` default in place, so the
22
+ * conditional export is written.
23
+ *
24
+ * Kept in sync with `resolveCssExportOptions` in `@sanity/vanilla-extract-rolldown-plugin` —
25
+ * the same reason `cssShimFileName` has a copy here. Importing the canonical one would make
26
+ * `@sanity/vanilla-extract-tsdown-plugin` a static dependency of this module graph, pulling the
27
+ * whole vanilla-extract toolchain into every `pkg build`.
28
+ *
29
+ * @internal
30
+ */
31
+ function usesCssExportNodeCompat(options) {
32
+ return options.exports === void 0 || isRecord(options.exports) && options.exports.nodeCompat === !0;
33
+ }
34
+ const asyncDispose = Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose");
35
+ /** @public */
36
+ async function watch(options) {
37
+ let { cwd, strict = !1, tsconfig: tsconfigOption, signal } = options, logger = createLogger(), { watchConfigFiles } = await import("./watchConfigFiles-AGBDblwf.js"), configFiles$ = await watchConfigFiles({
38
+ cwd,
39
+ logger
40
+ }), bundles = [], runId = 0, disposeBundles = async () => {
41
+ let disposing = bundles;
42
+ bundles = [];
43
+ for (let bundle of disposing) await bundle[asyncDispose]();
44
+ }, ctxSubscription = configFiles$.pipe(switchMap(async () => {
45
+ let pkgPath = up({ cwd });
46
+ if (!pkgPath) throw Error("missing package.json", { cause: { cwd } });
47
+ let config = await loadConfig({
48
+ cwd,
49
+ pkgPath
50
+ }), { parseStrictOptions } = await import("./resolveBuildContext-DYZxWVVc.js").then((n) => n.n), strictOptions = parseStrictOptions(config?.strictOptions ?? {}), pkg = await loadPkgWithReporting({
51
+ pkgPath,
52
+ logger,
53
+ strict,
54
+ strictOptions
55
+ }), tsconfig = tsconfigOption || config?.tsconfig || "tsconfig.json";
56
+ return resolveBuildContext({
57
+ config,
58
+ cwd,
59
+ logger,
60
+ pkg,
61
+ strict,
62
+ tsconfig
63
+ });
64
+ })).subscribe(async (ctx) => {
65
+ let id = ++runId, runBundles = [];
66
+ try {
67
+ await disposeBundles();
68
+ let cssNames = [], cssSources = {}, vanillaExtract = ctx.config?.vanillaExtract;
69
+ if (vanillaExtract) {
70
+ let veOptions = vanillaExtract === !0 ? {} : vanillaExtract;
71
+ usesCssExportNodeCompat(veOptions) && cssNames.push(veOptions.fileName || "bundle.css");
72
+ }
73
+ let cssConfig = ctx.config?.css;
74
+ if ((cssConfig || ctx.cssExports.length > 0) && usesCssExportNodeCompat(cssConfig ?? {})) for (let cssExport of ctx.cssExports) cssNames.push(cssExport._path.replace(/^\.\//, "")), cssSources[cssExport._path] = cssExport.source;
75
+ await writeBundleCssExports({
76
+ cwd,
77
+ distPath: ctx.distPath,
78
+ cssNames,
79
+ sources: cssSources,
80
+ logger
81
+ });
82
+ let builds = resolveTsdownBuilds(ctx), first = !0;
83
+ for (let buildDef of builds) {
84
+ if (id !== runId) break;
85
+ let inlineConfig = await resolveTsdownConfig(ctx, buildDef, {
86
+ clean: first,
87
+ watch: !0
88
+ });
89
+ first = !1, runBundles.push(...await build(inlineConfig));
90
+ }
91
+ if (id !== runId) {
92
+ for (let bundle of runBundles) await bundle[asyncDispose]();
93
+ return;
94
+ }
95
+ bundles = runBundles, logger.success(`${ctx.pkg.name}: watching for file changes\u2026`), logger.log();
96
+ } catch (err) {
97
+ ctx.logger.error(err), ctx.logger.log(), process.exit(1);
98
+ }
99
+ });
100
+ signal && signal.addEventListener("abort", () => {
101
+ runId++, ctxSubscription.unsubscribe(), disposeBundles();
102
+ }, { once: !0 });
103
+ }
104
+ async function watchAction(options) {
105
+ try {
106
+ await watch({
107
+ cwd: process.cwd(),
108
+ strict: options.strict,
109
+ tsconfig: options.tsconfig
110
+ });
111
+ } catch (err) {
112
+ handleError(err);
113
+ }
114
+ }
115
+ export { watchAction };
116
+
117
+ //# sourceMappingURL=watchAction-PjRXiQRe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchAction-PjRXiQRe.js","names":["findPkgPath","tsdownBuild"],"sources":["../src/node/core/pkg/cssExportOptions.ts","../src/node/watch.ts","../src/cli/watchAction.ts"],"sourcesContent":["import {isRecord} from '../isRecord.ts'\n\n/**\n * Whether a CSS pipeline's options wire up the conditional CSS export pattern — the no-op JS\n * shim, its declaration file, and the `\"./<fileName>\"` export whose `node`/`default` conditions\n * point at the shim.\n *\n * `pkg watch` needs this to decide which exports to maintain itself, because watch mode turns\n * tsdown's `exports` feature off. Full builds never ask: the plugins own the answer there.\n *\n * Only `exports` decides. `@sanity/tsdown-config` composes the plugin options as\n * `{inject: true, exports: {nodeCompat: true}, ...userOptions}`, so `exports` is always set by\n * the time `resolveCssExportOptions` sees them — a user-supplied `inject` never clears the\n * default. That makes the plugin's compatibility fallback for the deprecated\n * `inject: {nodeCompat: true}` spelling unreachable from here, and reading `inject` would get\n * `{inject: {nodeCompat: false}}` wrong: it still leaves the `exports` default in place, so the\n * conditional export is written.\n *\n * Kept in sync with `resolveCssExportOptions` in `@sanity/vanilla-extract-rolldown-plugin` —\n * the same reason `cssShimFileName` has a copy here. Importing the canonical one would make\n * `@sanity/vanilla-extract-tsdown-plugin` a static dependency of this module graph, pulling the\n * whole vanilla-extract toolchain into every `pkg build`.\n *\n * @internal\n */\nexport function usesCssExportNodeCompat(options: {exports?: unknown}): boolean {\n if (options.exports === undefined) {\n // `@sanity/tsdown-config`'s `exports: {nodeCompat: true}` default applies\n return true\n }\n return isRecord(options.exports) && options.exports['nodeCompat'] === true\n}\n","import {up as findPkgPath} from 'empathic/package'\nimport type {Subscription} from 'rxjs'\nimport {switchMap} from 'rxjs'\nimport {build as tsdownBuild, type TsdownBundle} from 'tsdown'\nimport {loadConfig} from './core/config/loadConfig.ts'\nimport {usesCssExportNodeCompat} from './core/pkg/cssExportOptions.ts'\nimport {loadPkgWithReporting} from './core/pkg/loadPkgWithReporting.ts'\nimport {writeBundleCssExports} from './core/pkg/writeBundleCssExports.ts'\nimport {createLogger} from './logger.ts'\nimport {resolveBuildContext} from './resolveBuildContext.ts'\nimport {resolveTsdownBuilds} from './tasks/tsdown/resolveTsdownBuilds.ts'\nimport {resolveTsdownConfig} from './tasks/tsdown/resolveTsdownConfig.ts'\n\nconst asyncDispose: typeof Symbol.asyncDispose =\n Symbol.asyncDispose || Symbol.for('Symbol.asyncDispose')\n\n/** @public */\nexport async function watch(options: {\n cwd: string\n strict?: boolean\n tsconfig?: string\n signal?: AbortSignal\n}): Promise<void> {\n const {cwd, strict = false, tsconfig: tsconfigOption, signal} = options\n\n const logger = createLogger()\n\n const {watchConfigFiles} = await import('./watchConfigFiles.ts')\n const configFiles$ = await watchConfigFiles({cwd, logger})\n\n // Every rebuild of the waterfall holds tsdown watchers (one per platform build); they are\n // disposed when the config files change (the waterfall restarts) or the signal aborts.\n // RxJS does not await async subscriber callbacks, so a monotonically increasing run id\n // guards the rebuilds: only the latest run may publish into `bundles`, and a run that turns\n // stale mid-flight (a newer config-file event, or the abort signal) disposes the watchers\n // it created instead of leaking them.\n let bundles: TsdownBundle[] = []\n let runId = 0\n const disposeBundles = async () => {\n const disposing = bundles\n bundles = []\n for (const bundle of disposing) {\n await bundle[asyncDispose]()\n }\n }\n\n const ctx$ = configFiles$.pipe(\n switchMap(async () => {\n const pkgPath = findPkgPath({cwd})\n if (!pkgPath) {\n throw new Error('missing package.json', {cause: {cwd}})\n }\n\n const config = await loadConfig({cwd, pkgPath})\n const {parseStrictOptions} = await import('./strict.ts')\n const strictOptions = parseStrictOptions(config?.strictOptions ?? {})\n const pkg = await loadPkgWithReporting({pkgPath, logger, strict, strictOptions})\n const tsconfig = tsconfigOption || config?.tsconfig || 'tsconfig.json'\n\n return resolveBuildContext({config, cwd, logger, pkg, strict, tsconfig})\n }),\n )\n\n const ctxSubscription: Subscription = ctx$.subscribe(async (ctx) => {\n const id = ++runId\n const runBundles: TsdownBundle[] = []\n try {\n await disposeBundles()\n\n // Full builds write the conditional `./<css>` export through tsdown's\n // `exports.customExports` composition, but watch mode disables tsdown's `exports` feature\n // (a package.json write per rebuild would loop the watcher). Keep the export in sync here\n // instead, once per context, like v11 — the write is idempotent, so it won't loop.\n const cssNames: string[] = []\n const cssSources: Record<string, string> = {}\n\n const vanillaExtract = ctx.config?.vanillaExtract\n if (vanillaExtract) {\n const veOptions = vanillaExtract === true ? {} : vanillaExtract\n if (usesCssExportNodeCompat(veOptions)) {\n cssNames.push(veOptions.fileName || 'bundle.css')\n }\n }\n\n // The `@tsdown/css` pipeline's own exports: the `.css` export subpaths built by the\n // stylesheet build. Their file names follow their subpath and a declared entry always\n // emits, so they are known up front. The merged `style.css` of CSS imported from JS is\n // not — it only exists once something actually imports CSS — so `resolveTsdownConfig`\n // declares that one from a `build:done` hook instead, matching what\n // `cssNodeCompatPlugin` declares in a full build.\n const cssConfig = ctx.config?.css\n const cssNodeCompat =\n (Boolean(cssConfig) || ctx.cssExports.length > 0) &&\n usesCssExportNodeCompat(cssConfig ?? {})\n if (cssNodeCompat) {\n for (const cssExport of ctx.cssExports) {\n cssNames.push(cssExport._path.replace(/^\\.\\//, ''))\n cssSources[cssExport._path] = cssExport.source\n }\n }\n\n await writeBundleCssExports({\n cwd,\n distPath: ctx.distPath,\n cssNames,\n sources: cssSources,\n logger,\n })\n\n const builds = resolveTsdownBuilds(ctx)\n\n let first = true\n for (const buildDef of builds) {\n if (id !== runId) break\n\n const inlineConfig = await resolveTsdownConfig(ctx, buildDef, {\n clean: first,\n watch: true,\n })\n first = false\n\n runBundles.push(...(await tsdownBuild(inlineConfig)))\n }\n\n if (id !== runId) {\n // A newer run (or the abort signal) took over while this rebuild was in flight —\n // dispose everything this run created instead of publishing it\n for (const bundle of runBundles) {\n await bundle[asyncDispose]()\n }\n return\n }\n\n bundles = runBundles\n\n logger.success(`${ctx.pkg.name}: watching for file changes\\u2026`)\n logger.log()\n } catch (err) {\n ctx.logger.error(err)\n ctx.logger.log()\n\n process.exit(1)\n }\n })\n\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n runId++\n ctxSubscription.unsubscribe()\n void disposeBundles()\n },\n {once: true},\n )\n }\n}\n","import {watch} from '../node/watch.ts'\nimport {handleError} from './handleError.ts'\n\nexport async function watchAction(options: {strict?: boolean; tsconfig?: string}): Promise<void> {\n try {\n await watch({\n cwd: process.cwd(),\n strict: options.strict,\n tsconfig: options.tsconfig,\n })\n } catch (err) {\n handleError(err)\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { r as isRecord } from "./handleError-83GwKIFM.js";
2
+ import { n as createConditionalCssExport } from "./resolveTsdownConfig-D4boYIwx.js";
3
+ import { t as __exportAll } from "./initAction-CmOO8eod.js";
4
+ import path from "node:path";
5
+ import { readFile, writeFile } from "node:fs/promises";
6
+ var writeBundleCssExports_exports = /* @__PURE__ */ __exportAll({ writeBundleCssExports: () => writeBundleCssExports });
7
+ function hasMatchingExport(value, expected) {
8
+ if (typeof value != "object" || !value) return !1;
9
+ let actual = Object.fromEntries(Object.entries(value)), keys = Object.keys(expected);
10
+ return keys.length === Object.keys(actual).length && keys.every((key) => actual[key] === expected[key]);
11
+ }
12
+ /**
13
+ * Insert (or replace) the `"./<cssName>"` export in an `exports`-shaped map, preserving the existing
14
+ * order and placing it before `./package.json` when present.
15
+ */
16
+ function insertCssExport(exports, exportKey, conditionalExport) {
17
+ let nextExports = {}, inserted = !1;
18
+ for (let [key, value] of Object.entries(exports)) key !== exportKey && (key === "./package.json" && !inserted && (nextExports[exportKey] = conditionalExport, inserted = !0), nextExports[key] = value);
19
+ return inserted || (nextExports[exportKey] = conditionalExport), nextExports;
20
+ }
21
+ function detectIndent(source) {
22
+ let match = source.match(/\n([ \t]+)\S/);
23
+ if (!match) return 2;
24
+ let indent = match[1];
25
+ return indent.includes(" ") ? " " : indent.length;
26
+ }
27
+ /**
28
+ * Write the conditional `"./<cssName>"` export of every CSS file to `package.json` (used by
29
+ * `exports.nodeCompat`), so userland does not have to maintain it by hand. The write is
30
+ * idempotent: if every export already matches, the file is left untouched.
31
+ *
32
+ * Full builds write these entries through tsdown's `exports.customExports` (the
33
+ * `@sanity/vanilla-extract-tsdown-plugin` composition, and the `@sanity/tsdown-config` one for
34
+ * `@tsdown/css` output), but watch mode disables tsdown's `exports` feature (a `package.json`
35
+ * write per rebuild would loop the watcher) — `pkg watch` calls this once per context instead,
36
+ * like v11 did.
37
+ *
38
+ * When `publishConfig.exports` is present, the same conditional CSS exports are mirrored into it.
39
+ * A conditional CSS export has no `source`/`development`/`monorepo` conditions to strip, so the
40
+ * entries are identical in both places — except for a hand-written `source`, which stays in
41
+ * `exports` only. Keeping them in sync prevents the `publishConfig.exports` validation from
42
+ * failing with a "missing export path" error for the auto-added `./<cssName>` exports.
43
+ *
44
+ * @internal
45
+ */
46
+ async function writeBundleCssExports(options) {
47
+ let { cwd, distPath, cssNames, sources = {}, logger } = options;
48
+ if (cssNames.length === 0) return;
49
+ let pkgPath = path.resolve(cwd, "package.json"), source = await readFile(pkgPath, "utf8"), pkg = JSON.parse(source), distRel = (path.relative(cwd, distPath) || "dist").split(path.sep).join("/"), publishConfig = pkg.publishConfig, publishConfigExports = isRecord(publishConfig?.exports) ? publishConfig.exports : void 0, written = [], exports = pkg.exports ?? {}, publishExports = publishConfigExports;
50
+ for (let cssName of cssNames) {
51
+ let exportKey = `./${cssName}`, publishExport = createConditionalCssExport(cssName, distRel), sourceCondition = sources[exportKey], localExport = sourceCondition === void 0 ? publishExport : {
52
+ source: sourceCondition,
53
+ ...publishExport
54
+ }, exportsMatch = hasMatchingExport(exports[exportKey], localExport), publishExportsMatch = !publishExports || hasMatchingExport(publishExports[exportKey], publishExport);
55
+ exportsMatch && publishExportsMatch || (exports = insertCssExport(exports, exportKey, localExport), publishExports &&= insertCssExport(publishExports, exportKey, publishExport), written.push(exportKey));
56
+ }
57
+ if (written.length === 0) return;
58
+ pkg.exports = exports, publishConfig && publishExports && (publishConfig.exports = publishExports), await writeFile(pkgPath, `${JSON.stringify(pkg, null, detectIndent(source))}\n`);
59
+ let maps = publishConfigExports ? ["exports", "publishConfig.exports"] : ["exports"], keys = maps.flatMap((map) => written.map((key) => `\`${map}["${key}"]\``)).join(", ").replace(/, ([^,]*)$/, written.length * maps.length > 1 ? " and $1" : "$1");
60
+ logger.log(`Updated package.json: added ${keys} for the conditional CSS export pattern`);
61
+ }
62
+ export { writeBundleCssExports_exports as n, writeBundleCssExports as t };
63
+
64
+ //# sourceMappingURL=writeBundleCssExports-DGTofClh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeBundleCssExports-DGTofClh.js","names":[],"sources":["../src/node/core/pkg/writeBundleCssExports.ts"],"sourcesContent":["import {readFile, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport type {Logger} from '../../logger.ts'\nimport {isRecord} from '../isRecord.ts'\nimport {createConditionalCssExport} from './cssExport.ts'\n\nfunction hasMatchingExport(value: unknown, expected: Record<string, string>): boolean {\n if (typeof value !== 'object' || value === null) return false\n const actual = Object.fromEntries(Object.entries(value))\n const keys = Object.keys(expected)\n return (\n keys.length === Object.keys(actual).length && keys.every((key) => actual[key] === expected[key])\n )\n}\n\n/**\n * Insert (or replace) the `\"./<cssName>\"` export in an `exports`-shaped map, preserving the existing\n * order and placing it before `./package.json` when present.\n */\nfunction insertCssExport(\n exports: Record<string, unknown>,\n exportKey: string,\n conditionalExport: Record<string, string>,\n): Record<string, unknown> {\n const nextExports: Record<string, unknown> = {}\n let inserted = false\n for (const [key, value] of Object.entries(exports)) {\n if (key === exportKey) continue\n if (key === './package.json' && !inserted) {\n nextExports[exportKey] = conditionalExport\n inserted = true\n }\n nextExports[key] = value\n }\n if (!inserted) {\n nextExports[exportKey] = conditionalExport\n }\n return nextExports\n}\n\nfunction detectIndent(source: string): string | number {\n const match = source.match(/\\n([ \\t]+)\\S/)\n if (!match) return 2\n const indent = match[1]!\n return indent.includes('\\t') ? '\\t' : indent.length\n}\n\n/**\n * Write the conditional `\"./<cssName>\"` export of every CSS file to `package.json` (used by\n * `exports.nodeCompat`), so userland does not have to maintain it by hand. The write is\n * idempotent: if every export already matches, the file is left untouched.\n *\n * Full builds write these entries through tsdown's `exports.customExports` (the\n * `@sanity/vanilla-extract-tsdown-plugin` composition, and the `@sanity/tsdown-config` one for\n * `@tsdown/css` output), but watch mode disables tsdown's `exports` feature (a `package.json`\n * write per rebuild would loop the watcher) — `pkg watch` calls this once per context instead,\n * like v11 did.\n *\n * When `publishConfig.exports` is present, the same conditional CSS exports are mirrored into it.\n * A conditional CSS export has no `source`/`development`/`monorepo` conditions to strip, so the\n * entries are identical in both places — except for a hand-written `source`, which stays in\n * `exports` only. Keeping them in sync prevents the `publishConfig.exports` validation from\n * failing with a \"missing export path\" error for the auto-added `./<cssName>` exports.\n *\n * @internal\n */\nexport async function writeBundleCssExports(options: {\n cwd: string\n distPath: string\n cssNames: string[]\n /** Hand-written `source` conditions to preserve in `exports`, keyed by export subpath. */\n sources?: Record<string, string>\n logger: Logger\n}): Promise<void> {\n const {cwd, distPath, cssNames, sources = {}, logger} = options\n if (cssNames.length === 0) return\n\n const pkgPath = path.resolve(cwd, 'package.json')\n const source = await readFile(pkgPath, 'utf8')\n // oxlint-disable-next-line no-unsafe-type-assertion\n const pkg = JSON.parse(source) as {\n exports?: Record<string, unknown>\n publishConfig?: {exports?: Record<string, unknown>}\n }\n\n // Normalize to POSIX separators - `path.relative` uses `\\\\` on Windows, but `exports` paths in\n // package.json must always use `/`.\n const distRel = (path.relative(cwd, distPath) || 'dist').split(path.sep).join('/')\n\n // Only mirror into `publishConfig.exports` when it already exists; never create it here.\n const publishConfig = pkg.publishConfig\n const publishConfigExports = isRecord(publishConfig?.exports) ? publishConfig.exports : undefined\n\n const written: string[] = []\n let exports = pkg.exports ?? {}\n let publishExports = publishConfigExports\n\n for (const cssName of cssNames) {\n const exportKey = `./${cssName}`\n const publishExport = createConditionalCssExport(cssName, distRel)\n // A hand-written `source` resolves at development time only, so it stays out of the\n // publish map — the same split `composeExports` applies to build entries.\n const sourceCondition = sources[exportKey]\n const localExport =\n sourceCondition === undefined ? publishExport : {source: sourceCondition, ...publishExport}\n\n const exportsMatch = hasMatchingExport(exports[exportKey], localExport)\n const publishExportsMatch =\n !publishExports || hasMatchingExport(publishExports[exportKey], publishExport)\n if (exportsMatch && publishExportsMatch) continue\n\n exports = insertCssExport(exports, exportKey, localExport)\n if (publishExports) {\n publishExports = insertCssExport(publishExports, exportKey, publishExport)\n }\n written.push(exportKey)\n }\n\n if (written.length === 0) return\n\n pkg.exports = exports\n if (publishConfig && publishExports) {\n publishConfig.exports = publishExports\n }\n\n await writeFile(pkgPath, `${JSON.stringify(pkg, null, detectIndent(source))}\\n`)\n const maps = publishConfigExports ? ['exports', 'publishConfig.exports'] : ['exports']\n const keys = maps\n .flatMap((map) => written.map((key) => `\\`${map}[\"${key}\"]\\``))\n .join(', ')\n .replace(/, ([^,]*)$/, written.length * maps.length > 1 ? ' and $1' : '$1')\n logger.log(`Updated package.json: added ${keys} for the conditional CSS export pattern`)\n}\n"],"mappings":";;;;;;AAMA,SAAS,kBAAkB,OAAgB,UAA2C;CACpF,IAAI,OAAO,SAAU,aAAY,OAAgB,OAAO;CACxD,IAAM,SAAS,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,GACjD,OAAO,OAAO,KAAK,QAAQ;CACjC,OACE,KAAK,WAAW,OAAO,KAAK,MAAM,CAAC,CAAC,UAAU,KAAK,OAAO,QAAQ,OAAO,SAAS,SAAS,IAAI;AAEnG;;;;;AAMA,SAAS,gBACP,SACA,WACA,mBACyB;CACzB,IAAM,cAAuC,CAAC,GAC1C,WAAW;CACf,KAAK,IAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAC3C,QAAQ,cACR,QAAQ,oBAAoB,CAAC,aAC/B,YAAY,aAAa,mBACzB,WAAW,KAEb,YAAY,OAAO;CAKrB,OAHK,aACH,YAAY,aAAa,oBAEpB;AACT;AAEA,SAAS,aAAa,QAAiC;CACrD,IAAM,QAAQ,OAAO,MAAM,cAAc;CACzC,IAAI,CAAC,OAAO,OAAO;CACnB,IAAM,SAAS,MAAM;CACrB,OAAO,OAAO,SAAS,GAAI,IAAI,MAAO,OAAO;AAC/C;;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,sBAAsB,SAO1B;CAChB,IAAM,EAAC,KAAK,UAAU,UAAU,UAAU,CAAC,GAAG,WAAU;CACxD,IAAI,SAAS,WAAW,GAAG;CAE3B,IAAM,UAAU,KAAK,QAAQ,KAAK,cAAc,GAC1C,SAAS,MAAM,SAAS,SAAS,MAAM,GAEvC,MAAM,KAAK,MAAM,MAAM,GAOvB,WAAW,KAAK,SAAS,KAAK,QAAQ,KAAK,OAAA,CAAQ,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,GAG3E,gBAAgB,IAAI,eACpB,uBAAuB,SAAS,eAAe,OAAO,IAAI,cAAc,UAAU,KAAA,GAElF,UAAoB,CAAC,GACvB,UAAU,IAAI,WAAW,CAAC,GAC1B,iBAAiB;CAErB,KAAK,IAAM,WAAW,UAAU;EAC9B,IAAM,YAAY,KAAK,WACjB,gBAAgB,2BAA2B,SAAS,OAAO,GAG3D,kBAAkB,QAAQ,YAC1B,cACJ,oBAAoB,KAAA,IAAY,gBAAgB;GAAC,QAAQ;GAAiB,GAAG;EAAa,GAEtF,eAAe,kBAAkB,QAAQ,YAAY,WAAW,GAChE,sBACJ,CAAC,kBAAkB,kBAAkB,eAAe,YAAY,aAAa;EAC3E,gBAAgB,wBAEpB,UAAU,gBAAgB,SAAS,WAAW,WAAW,GACzD,AACE,mBAAiB,gBAAgB,gBAAgB,WAAW,aAAa,GAE3E,QAAQ,KAAK,SAAS;CACxB;CAEA,IAAI,QAAQ,WAAW,GAAG;CAO1B,AALA,IAAI,UAAU,SACV,iBAAiB,mBACnB,cAAc,UAAU,iBAG1B,MAAM,UAAU,SAAS,GAAG,KAAK,UAAU,KAAK,MAAM,aAAa,MAAM,CAAC,EAAE,GAAG;CAC/E,IAAM,OAAO,uBAAuB,CAAC,WAAW,uBAAuB,IAAI,CAAC,SAAS,GAC/E,OAAO,KACV,SAAS,QAAQ,QAAQ,KAAK,QAAQ,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAC9D,KAAK,IAAI,CAAC,CACV,QAAQ,cAAc,QAAQ,SAAS,KAAK,SAAS,IAAI,YAAY,IAAI;CAC5E,OAAO,IAAI,+BAA+B,KAAK,wCAAwC;AACzF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/pkg-utils",
3
- "version": "12.0.1",
3
+ "version": "12.1.1",
4
4
  "description": "Simple utilities for modern npm packages.",
5
5
  "keywords": [
6
6
  "sanity-io",
@@ -44,11 +44,9 @@
44
44
  ],
45
45
  "browserslist": "extends @sanity/browserslist-config",
46
46
  "dependencies": {
47
- "@microsoft/api-extractor": "^7.58.12",
48
- "@microsoft/tsdoc-config": "^0.18.1",
49
47
  "@sanity/browserslist-config": "^1.0.5",
50
48
  "@typescript/typescript6": "^6.0.2",
51
- "browserslist": "^4.28.7",
49
+ "browserslist": "^4.28.8",
52
50
  "browserslist-to-esbuild": "2.1.1",
53
51
  "cac": "^7.0.0",
54
52
  "chalk": "^6.0.0",
@@ -56,22 +54,21 @@
56
54
  "empathic": "^2.0.1",
57
55
  "find-config": "^1.0.0",
58
56
  "get-latest-version": "^6.0.1",
59
- "globby": "^16.2.2",
60
- "jsonc-parser": "^3.3.1",
57
+ "globby": "^16.2.3",
61
58
  "mkdirp": "^3.0.1",
62
59
  "outdent": "^0.8.0",
63
60
  "prettier": "^3.9.6",
64
61
  "pretty-bytes": "^7.1.1",
65
62
  "prompts": "^2.4.2",
66
- "publint": "^0.3.22",
63
+ "publint": "^0.3.23",
67
64
  "rxjs": "^7.8.2",
68
65
  "treeify": "^1.1.0",
69
66
  "tsdown": "^0.22.14",
70
- "tsx": "^4.23.4",
67
+ "tsx": "^4.23.11",
71
68
  "zod": "^4.4.3",
72
69
  "zod-validation-error": "^5.0.0",
73
- "@sanity/parse-package-json": "^2.2.11",
74
- "@sanity/tsdown-config": "^0.23.0"
70
+ "@sanity/tsdown-config": "^0.24.1",
71
+ "@sanity/parse-package-json": "^2.3.0"
75
72
  },
76
73
  "devDependencies": {
77
74
  "@types/find-config": "^1.0.4",
@@ -88,10 +85,14 @@
88
85
  "@sanity/tsconfig": "^2.2.1"
89
86
  },
90
87
  "peerDependencies": {
88
+ "@tsdown/css": "*",
91
89
  "babel-plugin-react-compiler": "*",
92
90
  "typescript": "6.x || 7.x"
93
91
  },
94
92
  "peerDependenciesMeta": {
93
+ "@tsdown/css": {
94
+ "optional": true
95
+ },
95
96
  "babel-plugin-react-compiler": {
96
97
  "optional": true
97
98
  }