@savvy-web/tsdown-plugins 0.4.1 → 0.5.0

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
@@ -47,7 +47,7 @@ export default defineConfig({
47
47
  - **Executable binaries** — `normalizeExeOptions` fills the SEA defaults and infers targets from the package's `os`/`cpu`; `runExeBuild` drives `@tsdown/exe` to compile the binaries.
48
48
  - **Config validation** — the `ConfigValidator` Effect service (with `ConfigValidatorLive`) fast-fails on a bad `publishConfig.targets`, `exe` or `meta` config, raising the typed `ConfigValidationError`.
49
49
  - **dts tsconfig port** — `buildResolvedTsconfig` and `writeResolvedTsconfig` write a temp tsconfig with absolute paths so type declarations emit cleanly under pnpm symlinks.
50
- - **Per-target build loop** — `deriveTargetGroupOptions` and `buildTargetGroups` map a target to its `tsdown` options and run the build once per target, exposed as a helper so the escape hatch gets multi-target builds too. A `format` of `["esm", "cjs"]` (the `BuildFormat` type) derives a dual-format build — a require-able CJS output with default-export interop and `.d.cts` declarations alongside the ESM one. The `bundleNodeModules`, `bundledPackages` and `dtsExternals` options thread the dependency-bundling posture into both the JS and declaration passes. The `define` option forwards compile-time global replacements to both passes, merged with an auto-injected `process.env.__PACKAGE_VERSION__` constant.
50
+ - **Per-target build loop** — `deriveTargetGroupOptions` and `buildTargetGroups` map a target to its `tsdown` options and run the build once per target, exposed as a helper so the escape hatch gets multi-target builds too. A `format` of `["esm", "cjs"]` (the `BuildFormat` type) derives a dual-format build — a require-able CJS output with default-export interop and `.d.cts` declarations alongside the ESM one. The `bundleNodeModules`, `bundledPackages` and `dtsExternals` options thread the dependency-bundling posture into both the JS and declaration passes. A per-entry override partition can also set `platform` (the JS-pass target, `"browser"` for a client bundle), `css` (forwarded to tsdown's `css` option for `@tsdown/css`) and `outSubdir` (build the partition into an isolated `<group>/pkg/<subdir>/` sub-package). The `define` option forwards compile-time global replacements to both passes, merged with an auto-injected `process.env.__PACKAGE_VERSION__` constant.
51
51
  - **Loose files** — `normalizeLooseFiles` resolves a `LooseFiles` map of literal output filenames to `NormalizedLooseFile` descriptors, inferring the module format from each `.mjs`/`.cjs` key and raising `ConfigValidationError` on a path separator, an unsupported extension or an ambiguous `.js`. `buildTargetGroups` takes the normalized form as its `looseFiles` option and emits one extra single-entry, bundled, declaration-free and manifest-free pass per file per target group, inheriting the group's bundling posture so each file is self-contained.
52
52
  - **Bundled declarations** — each target runs two `tsdown` passes: a JavaScript pass that preserves per-module output, then a declaration-only pass that rolls every re-exported type into a single `.d.ts` per public entry (`deriveDtsPassOptions`). Per-module JavaScript stays intact while consumers keep reaching re-exported types through your published subpaths.
53
53
  - **API Extractor meta** — `generateMeta` runs [API Extractor](https://api-extractor.com/) over a package's emitted `.d.ts` to write an api-model bundle (`.api.json`, `tsdoc-metadata.json`, resolved `tsconfig.json`); `normalizeMetaOptions` fills the `MetaOptions` defaults that drive it.
@@ -3,7 +3,7 @@ import { cjsDefaultInterop } from "./cjs-default-interop.js";
3
3
  import { nodeBuiltinDefaultInterop } from "./node-builtin-default-interop.js";
4
4
  import { syncPublicDir } from "./sync-public.js";
5
5
  import { deriveDtsPassOptions, deriveTargetGroupOptions, outDirFor } from "./target-groups.js";
6
- import { join } from "node:path";
6
+ import { dirname, join } from "node:path";
7
7
 
8
8
  //#region src/build/build-target-groups.ts
9
9
  /**
@@ -55,27 +55,36 @@ async function buildTargetGroups(options) {
55
55
  ...part.format !== void 0 ? { format: part.format } : {},
56
56
  ...options.minify !== void 0 ? { minify: options.minify } : {},
57
57
  ...options.jsx !== void 0 ? { jsx: options.jsx } : {},
58
- ...options.define !== void 0 ? { define: options.define } : {}
58
+ ...options.define !== void 0 ? { define: options.define } : {},
59
+ ...part.platform !== void 0 ? { platform: part.platform } : {}
59
60
  };
60
61
  const js = deriveTargetGroupOptions(deriveInput);
61
62
  const dts = deriveDtsPassOptions(deriveInput);
63
+ const partOutDir = part.outSubdir !== void 0 ? join(js.outDir, part.outSubdir) : js.outDir;
62
64
  const targetGroup = {
63
65
  id: group.id,
64
66
  name: group.name,
65
67
  isProd: js.isProd
66
68
  };
69
+ const srcDir = dirname(Object.values(part.entry)[0] ?? "");
70
+ const jsEntry = part.outSubdir !== void 0 ? [
71
+ `${srcDir}/**/*.{ts,tsx,mts,cts}`,
72
+ `!${srcDir}/**/*.{test,spec}.{ts,tsx,mts,cts}`,
73
+ `!${srcDir}/**/*.d.{ts,cts,mts}`
74
+ ] : js.entry;
67
75
  const manifestPlugin = isBase ? emitManifest({
68
76
  targetGroup,
69
77
  devManifest: options.devManifest,
70
78
  transform: options.transform,
71
79
  sourceDir: options.cwd,
72
- dual: options.dualExports ?? js.format.includes("cjs")
80
+ dual: options.dualExports ?? js.format.includes("cjs"),
81
+ ...options.subdirExports !== void 0 ? { subdirExports: options.subdirExports } : {}
73
82
  }) : void 0;
74
83
  await build({
75
84
  config: false,
76
85
  cwd: options.cwd,
77
- entry: js.entry,
78
- outDir: js.outDir,
86
+ entry: jsEntry,
87
+ outDir: partOutDir,
79
88
  format: js.format,
80
89
  platform: js.platform,
81
90
  sourcemap: js.sourcemap,
@@ -85,6 +94,7 @@ async function buildTargetGroups(options) {
85
94
  fixedExtension: js.fixedExtension,
86
95
  dts: js.dts,
87
96
  define: js.define,
97
+ ...part.css !== void 0 ? { css: part.css } : {},
88
98
  ...partExternals?.length || partBundleNodeModules || partBundle?.length ? { deps: {
89
99
  ...partExternals?.length ? { neverBundle: partExternals } : {},
90
100
  ...partBundle?.length ? { alwaysBundle: partBundle } : {},
@@ -104,7 +114,7 @@ async function buildTargetGroups(options) {
104
114
  config: false,
105
115
  cwd: options.cwd,
106
116
  entry: dts.entry,
107
- outDir: dts.outDir,
117
+ outDir: partOutDir,
108
118
  format: dts.format,
109
119
  platform: dts.platform,
110
120
  sourcemap: dts.sourcemap,
@@ -15,7 +15,7 @@ function deriveTargetGroupOptions(options) {
15
15
  format,
16
16
  unbundle: true,
17
17
  clean: true,
18
- platform: "node",
18
+ platform: options.platform ?? "node",
19
19
  fixedExtension: false,
20
20
  entry: options.entry,
21
21
  dts: false,
package/entry/extract.js CHANGED
@@ -1,5 +1,6 @@
1
1
  //#region src/entry/extract.ts
2
- const isTypeScriptFile = (p) => p.endsWith(".ts") || p.endsWith(".tsx");
2
+ const isDeclarationFile = (p) => p.endsWith(".d.ts") || p.endsWith(".d.cts") || p.endsWith(".d.mts");
3
+ const isTypeScriptFile = (p) => !isDeclarationFile(p) && (p.endsWith(".ts") || p.endsWith(".tsx"));
3
4
  /** /dist/*.js to /src/*.ts; otherwise unchanged. */
4
5
  const resolveToTypeScript = (p) => p.endsWith(".js") && p.includes("/dist/") ? p.replace("/dist/", "/src/").replace(/\.js$/, ".ts") : p;
5
6
  /** Resolve an export value to a source path: import || default || types (NOT require). */
package/index.d.ts CHANGED
@@ -74,8 +74,11 @@ declare function defaultManifestTransform({
74
74
  *
75
75
  * The output path is derived from the export KEY via the shared entry-name function,
76
76
  * never from the source path, so the manifest target always matches the emitted file.
77
+ *
78
+ * Export keys in `subdirExports` are built into an isolated `<key>/index.*` subdir (e.g. an
79
+ * RSPress `./runtime`), so their conditions gain an `/index` segment.
77
80
  */
78
- declare function transformExports(exports: unknown, dual?: DualExports): unknown;
81
+ declare function transformExports(exports: unknown, dual?: DualExports, subdirExports?: ReadonlySet<string>): unknown;
79
82
  /** Rewrite bin: TS targets to bin/[command].js (string to bin/cli.js); strip leading ./ otherwise. */
80
83
  declare function transformBin(bin: unknown): unknown;
81
84
  /** FINAL guard: strip leading ./ from bin paths (npm 11.x drops ./-prefixed bins). */
@@ -85,6 +88,8 @@ interface TransformManifestOptions {
85
88
  readonly transform?: ((pkg: Json) => Json) | undefined;
86
89
  /** Which exports emit dual import/require conditions. boolean = uniform; Set = per-export-key. */
87
90
  readonly dual?: DualExports | undefined;
91
+ /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
92
+ readonly subdirExports?: ReadonlySet<string> | undefined;
88
93
  }
89
94
  /** Apply the full standard manifest transform (excluding catalog resolution, done upstream). */
90
95
  declare function transformManifest(pkg: Json, options?: TransformManifestOptions): Json;
@@ -106,6 +111,8 @@ interface BuildEmittedManifestOptions {
106
111
  }) => Json) | undefined;
107
112
  /** Which exports emit dual import/require conditions. boolean (uniform) or a Set of export keys (per-entry). */
108
113
  readonly dual?: DualExports | undefined;
114
+ /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
115
+ readonly subdirExports?: ReadonlySet<string> | undefined;
109
116
  }
110
117
  /** Compute the final manifest bytes for a TargetGroup (catalog resolution + standard transforms). */
111
118
  declare function buildEmittedManifest(options: BuildEmittedManifestOptions): Promise<Json>;
@@ -120,6 +127,8 @@ interface EmitManifestOptions {
120
127
  readonly sourceDir: string;
121
128
  /** Which exports emit dual import/require conditions. boolean (uniform) or a Set of export keys (per-entry). */
122
129
  readonly dual?: DualExports | undefined;
130
+ /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
131
+ readonly subdirExports?: ReadonlySet<string> | undefined;
123
132
  }
124
133
  /** Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root. */
125
134
  declare function emitManifest(options: EmitManifestOptions): Plugin;
@@ -129,6 +138,8 @@ declare function emitManifest(options: EmitManifestOptions): Plugin;
129
138
  type TargetGroupId = string;
130
139
  /** An output module format the build can emit. */
131
140
  type BuildFormat = "esm" | "cjs";
141
+ /** Bundling platform for the JS pass. Defaults to "node". Use "browser" for web runtime partitions. */
142
+ type BuildPlatform = "node" | "browser" | "neutral";
132
143
  /** A prod/dev group to build: its folder id and the resolved package name its manifest carries. */
133
144
  interface BuildGroupSpec {
134
145
  readonly id: TargetGroupId;
@@ -142,6 +153,8 @@ interface DeriveOptions {
142
153
  readonly tsconfigPath: string;
143
154
  readonly devManifest: "preserve" | "resolve";
144
155
  readonly externals?: ReadonlyArray<string>;
156
+ /** JS-pass platform. Defaults to "node"; set "browser" for an RSPress runtime partition. */
157
+ readonly platform?: BuildPlatform | undefined;
145
158
  /** Output formats to emit. Defaults to esm-only when unset. */
146
159
  readonly format?: ReadonlyArray<BuildFormat> | undefined;
147
160
  /** Minify prod output (prod groups only; dev is never minified). Defaults to false. */
@@ -188,7 +201,7 @@ interface DerivedTsdownOptions {
188
201
  readonly unbundle: true;
189
202
  /** JS pass starts fresh; it owns the outDir before the dts pass appends to it. */
190
203
  readonly clean: true;
191
- readonly platform: "node";
204
+ readonly platform: BuildPlatform;
192
205
  /**
193
206
  * Controls output file extensions. Always false for this builder.
194
207
  *
@@ -270,6 +283,19 @@ declare function normalizeLooseFiles(files: LooseFiles): ReadonlyArray<Normalize
270
283
  //#region src/build/build-target-groups.d.ts
271
284
  /** Signature compatible with tsdown's `build(inlineConfig)`. */
272
285
  type TsdownBuild = (config: Record<string, unknown>) => Promise<unknown>;
286
+ /**
287
+ * CSS handling for a partition's JS pass, forwarded VERBATIM to tsdown's `css` option (consumed
288
+ * by `@tsdown/css`). Structurally typed so tsdown-plugins takes no dependency on `@tsdown/css`.
289
+ * The package whose runtime is built must install `@tsdown/css`; tsdown loads it lazily.
290
+ */
291
+ interface CssOptions {
292
+ readonly modules?: boolean | {
293
+ readonly localsConvention?: string;
294
+ readonly namedExport?: boolean;
295
+ readonly [k: string]: unknown;
296
+ };
297
+ readonly [k: string]: unknown;
298
+ }
273
299
  /**
274
300
  * One entry partition built with its own format + bundling posture, layered into the
275
301
  * SAME outDir as the base build (clean:false). Anything omitted falls back to the base
@@ -283,6 +309,17 @@ interface EntryOverride {
283
309
  readonly bundleNodeModules?: boolean | undefined;
284
310
  readonly bundledPackages?: ReadonlyArray<string> | undefined;
285
311
  readonly dtsExternals?: ReadonlyArray<string> | undefined;
312
+ /** JS-pass platform for this partition. Defaults to the base "node". Use "browser" for a web runtime. */
313
+ readonly platform?: BuildPlatform | undefined;
314
+ /** CSS handling forwarded to tsdown's `css` option (JS pass only). Enables `@tsdown/css`. */
315
+ readonly css?: CssOptions | undefined;
316
+ /**
317
+ * Build this partition into `<groupOutDir>/<outSubdir>/` instead of the shared group root.
318
+ * Isolates a sub-package (e.g. an RSPress `./runtime`) so its bundleless per-file output cannot
319
+ * collide with the base partition's output and its barrel path is deterministic. The partition's
320
+ * entry should be `{ index: <barrel source> }` so it emits `<outSubdir>/index.js` + `<outSubdir>/index.d.ts`.
321
+ */
322
+ readonly outSubdir?: string | undefined;
286
323
  }
287
324
  interface BuildTargetGroupsOptions {
288
325
  readonly cwd: string;
@@ -375,6 +412,8 @@ interface BuildTargetGroupsOptions {
375
412
  * `format`-includes-cjs behavior.
376
413
  */
377
414
  readonly dualExports?: DualExports | undefined;
415
+ /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
416
+ readonly subdirExports?: ReadonlySet<string> | undefined;
378
417
  /** Injectable for tests; defaults to tsdown's build. */
379
418
  readonly build?: TsdownBuild;
380
419
  }
@@ -1509,5 +1548,5 @@ declare function resolveTargets(options: {
1509
1548
  baseName: string;
1510
1549
  }): TargetResolution;
1511
1550
  //#endregion
1512
- export { type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, type DeriveOptions, type DerivedTsdownOptions, type DualExports, type EmitManifestOptions, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, ExecutorResolverLive, type ExtractOptions, type ExtractResult, FormatSelector, FormatSelectorLive, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, type ManifestLike, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PkgOsCpu, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, TsconfigResolver, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type ValidationInput, type WarningSuppressionRule, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolvePortableTsconfig, resolveTargets, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
1551
+ export { type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, type CssOptions, DEFAULT_EXE_NODE_VERSION, type DeriveOptions, type DerivedTsdownOptions, type DualExports, type EmitManifestOptions, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, ExecutorResolverLive, type ExtractOptions, type ExtractResult, FormatSelector, FormatSelectorLive, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, type ManifestLike, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PkgOsCpu, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, TsconfigResolver, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type ValidationInput, type WarningSuppressionRule, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolvePortableTsconfig, resolveTargets, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
1513
1552
  //# sourceMappingURL=index.d.ts.map
@@ -19,7 +19,8 @@ async function buildEmittedManifest(options) {
19
19
  pkg: p,
20
20
  targetGroup
21
21
  }) : void 0,
22
- dual: options.dual ?? false
22
+ dual: options.dual ?? false,
23
+ subdirExports: options.subdirExports
23
24
  });
24
25
  }
25
26
  /** Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root. */
@@ -33,7 +34,8 @@ function emitManifest(options) {
33
34
  targetGroup: options.targetGroup,
34
35
  devManifest: options.devManifest ?? "preserve",
35
36
  transform: options.transform,
36
- dual: options.dual
37
+ dual: options.dual,
38
+ subdirExports: options.subdirExports
37
39
  });
38
40
  this.emitFile({
39
41
  type: "asset",
@@ -50,15 +50,16 @@ const stripLeadingDotSlash = (p) => p.startsWith("./") ? p.slice(2) : p;
50
50
  * always matches the file tsdown emits. The build never sets exportsAsIndexes, so the
51
51
  * manifest mirrors the flat (false) naming here.
52
52
  */
53
- const toBuiltJs = (exportKey) => `./${createEntryName(exportKey, false)}.js`;
54
- const toBuiltDts = (exportKey) => toBuiltJs(exportKey).replace(/\.js$/, ".d.ts");
55
- const toBuiltCjs = (exportKey) => toBuiltJs(exportKey).replace(/\.js$/, ".cjs");
56
- const isTs = (p) => p.endsWith(".ts") || p.endsWith(".tsx");
53
+ const toBuiltJs = (exportKey, subdirExports) => subdirExports?.has(exportKey) ? `./${createEntryName(exportKey, false)}/index.js` : `./${createEntryName(exportKey, false)}.js`;
54
+ const toBuiltDts = (exportKey, subdirExports) => toBuiltJs(exportKey, subdirExports).replace(/\.js$/, ".d.ts");
55
+ const toBuiltCjs = (exportKey, subdirExports) => toBuiltJs(exportKey, subdirExports).replace(/\.js$/, ".cjs");
56
+ const isDeclarationFile = (p) => p.endsWith(".d.ts") || p.endsWith(".d.cts") || p.endsWith(".d.mts");
57
+ const isTs = (p) => !isDeclarationFile(p) && (p.endsWith(".ts") || p.endsWith(".tsx"));
57
58
  /** Build the conditions object for a TS export target (adds require when dual-format). */
58
- const tsConditions = (exportKey, dual) => ({
59
- types: toBuiltDts(exportKey),
60
- import: toBuiltJs(exportKey),
61
- ...dual ? { require: toBuiltCjs(exportKey) } : {}
59
+ const tsConditions = (exportKey, dual, subdirExports) => ({
60
+ types: toBuiltDts(exportKey, subdirExports),
61
+ import: toBuiltJs(exportKey, subdirExports),
62
+ ...dual ? { require: toBuiltCjs(exportKey, subdirExports) } : {}
62
63
  });
63
64
  /**
64
65
  * Rewrite an exports map: TS string targets become a types/import conditions object.
@@ -67,9 +68,12 @@ const tsConditions = (exportKey, dual) => ({
67
68
  *
68
69
  * The output path is derived from the export KEY via the shared entry-name function,
69
70
  * never from the source path, so the manifest target always matches the emitted file.
71
+ *
72
+ * Export keys in `subdirExports` are built into an isolated `<key>/index.*` subdir (e.g. an
73
+ * RSPress `./runtime`), so their conditions gain an `/index` segment.
70
74
  */
71
- function transformExports(exports, dual = false) {
72
- if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, ".")) : exports;
75
+ function transformExports(exports, dual = false, subdirExports) {
76
+ if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, "."), subdirExports) : exports;
73
77
  if (exports && typeof exports === "object") {
74
78
  const out = {};
75
79
  for (const [key, value] of Object.entries(exports)) {
@@ -77,8 +81,8 @@ function transformExports(exports, dual = false) {
77
81
  out[key] = value;
78
82
  continue;
79
83
  }
80
- if (typeof value === "string" && isTs(value)) out[key] = tsConditions(key, isDualKey(dual, key));
81
- else out[key] = transformExports(value, dual);
84
+ if (typeof value === "string" && isTs(value)) out[key] = tsConditions(key, isDualKey(dual, key), subdirExports);
85
+ else out[key] = transformExports(value, dual, subdirExports);
82
86
  }
83
87
  return out;
84
88
  }
@@ -114,7 +118,7 @@ function transformManifest(pkg, options = {}) {
114
118
  };
115
119
  if (result.exports !== void 0 && result.exports !== null) {
116
120
  const original = result.exports;
117
- const transformed = transformExports(original, options.dual ?? false);
121
+ const transformed = transformExports(original, options.dual ?? false, options.subdirExports);
118
122
  const asMap = typeof original === "string" ? { ".": transformed } : { ...transformed };
119
123
  if (!("./package.json" in asMap)) asMap["./package.json"] = "./package.json";
120
124
  result.exports = asMap;
@@ -97,6 +97,7 @@ var TsconfigResolver = class TsconfigResolver {
97
97
  [ScriptTarget.ES2022, "es2022"],
98
98
  [ScriptTarget.ES2023, "es2023"],
99
99
  [ScriptTarget.ES2024, "es2024"],
100
+ [ScriptTarget.ES2025, "es2025"],
100
101
  [ScriptTarget.ESNext, "esnext"],
101
102
  [ScriptTarget.JSON, "json"]
102
103
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/tsdown-plugins",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "description": "Interface-only tsdown/rolldown plugin pack powering @savvy-web/bundler",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/tsdown-plugins",
@@ -29,11 +29,11 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@effect/platform-node": "^0.107.0",
32
- "@microsoft/api-extractor": "^7.58.8",
32
+ "@microsoft/api-extractor": "^7.58.9",
33
33
  "@microsoft/tsdoc": "^0.16.0",
34
34
  "@microsoft/tsdoc-config": "^0.18.1",
35
35
  "deep-equal": "^2.2.3",
36
- "json-schema-effect": "^0.2.2",
36
+ "json-schema-effect": "^0.2.4",
37
37
  "picocolors": "^1.1.1",
38
38
  "sort-package-json": "^4.0.0",
39
39
  "std-env": "^4.1.0",