@savvy-web/tsdown-plugins 0.11.0 → 0.12.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.
@@ -1,3 +1,5 @@
1
+ import { analyzeReexportBarrel, collectExportNames, renderReexportStub } from "../dts/reexport-stub.js";
2
+ import { writeDtsEmitTsconfig } from "../dts/resolved-tsconfig.js";
1
3
  import { emitManifest } from "../manifest/emit-manifest.js";
2
4
  import { buildMetricsPlugin } from "../report/metrics-plugin.js";
3
5
  import { createTimer } from "../report/timer.js";
@@ -6,10 +8,17 @@ import { cjsDefaultInterop } from "./cjs-default-interop.js";
6
8
  import { nodeBuiltinDefaultInterop } from "./node-builtin-default-interop.js";
7
9
  import { syncPublicDir } from "./sync-public.js";
8
10
  import { deriveDeclarationsPassOptions, deriveDtsPassOptions, deriveTargetGroupOptions, outDirFor } from "./target-groups.js";
9
- import { dirname, join } from "node:path";
11
+ import { readFileSync, writeFileSync } from "node:fs";
12
+ import { dirname, isAbsolute, join } from "node:path";
10
13
 
11
14
  //#region src/build/build-target-groups.ts
12
15
  /**
16
+ * Entry name of the conventional primary (".") export, which `createEntryName` maps to "index". It is
17
+ * the re-export-stub base: a secondary entry that re-exports a subset of it gets a thin stub pointing
18
+ * here instead of a self-contained rollup.
19
+ */
20
+ const STUB_BASE_ENTRY = "index";
21
+ /**
13
22
  * Run tsdown.build() per TargetGroup. Composable so the escape hatch gets multi-group too.
14
23
  *
15
24
  * Each group runs TWO passes to the SAME outDir:
@@ -28,6 +37,7 @@ import { dirname, join } from "node:path";
28
37
  async function buildTargetGroups(options) {
29
38
  const build = options.build ?? (await import("tsdown")).build;
30
39
  const publicDir = join(options.cwd, "public");
40
+ const dtsEmitTsconfigPath = writeDtsEmitTsconfig(options.tsconfigPath);
31
41
  const collector = options.collector;
32
42
  const verbose = options.verbose ?? false;
33
43
  const instrument = (groupId) => collector === void 0 ? {} : {
@@ -68,13 +78,12 @@ async function buildTargetGroups(options) {
68
78
  cwd: options.cwd,
69
79
  version: options.version,
70
80
  entry: part.entry,
71
- tsconfigPath: options.tsconfigPath,
81
+ tsconfigPath: dtsEmitTsconfigPath,
72
82
  devManifest: options.devManifest,
73
83
  ...partExternals !== void 0 ? { externals: partExternals } : {},
74
84
  ...partBundledPackages !== void 0 ? { bundledPackages: partBundledPackages } : {},
75
85
  ...part.format !== void 0 ? { format: part.format } : {},
76
86
  ...options.minify !== void 0 ? { minify: options.minify } : {},
77
- ...options.jsx !== void 0 ? { jsx: options.jsx } : {},
78
87
  ...options.define !== void 0 ? { define: options.define } : {},
79
88
  ...part.platform !== void 0 ? { platform: part.platform } : {}
80
89
  };
@@ -124,7 +133,6 @@ async function buildTargetGroups(options) {
124
133
  ...partBundleNodeModules ? { skipNodeModulesBundle: false } : {}
125
134
  } } : {},
126
135
  ...js.cjsDefault !== void 0 ? { cjsDefault: js.cjsDefault } : {},
127
- ...js.jsx !== void 0 ? { inputOptions: { jsx: js.jsx } } : {},
128
136
  plugins: [
129
137
  ...manifestPlugin ? [manifestPlugin] : [],
130
138
  ...js.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
@@ -135,37 +143,91 @@ async function buildTargetGroups(options) {
135
143
  if (isBase) syncPublicDir(publicDir, join(js.outDir, "public"));
136
144
  const dtsNeverBundle = [...partExternals ?? [], ...partDtsExternals ?? []];
137
145
  if (Object.keys(dts.entry).length === 0) continue;
138
- await timed(group.id, "dts", () => build({
139
- config: false,
140
- cwd: options.cwd,
141
- entry: dts.entry,
142
- outDir: partOutDir,
143
- format: dts.format,
144
- platform: dts.platform,
145
- sourcemap: dts.sourcemap,
146
- unbundle: dts.unbundle,
147
- clean: false,
148
- fixedExtension: dts.fixedExtension,
149
- dts: dts.dts,
150
- define: dts.define,
151
- ...instrument(group.id),
152
- ...dtsNeverBundle.length > 0 || partBundleNodeModules || dts.bundledPackages ? { deps: {
153
- ...dtsNeverBundle.length > 0 ? { neverBundle: dtsNeverBundle } : {},
154
- ...partBundleNodeModules ? {
155
- skipNodeModulesBundle: false,
156
- ...dts.bundledPackages ? { dts: { alwaysBundle: dts.bundledPackages } } : {}
157
- } : dts.bundledPackages ? {
158
- skipNodeModulesBundle: true,
159
- dts: { alwaysBundle: dts.bundledPackages }
160
- } : {}
161
- } } : {},
162
- ...dts.jsx !== void 0 ? { inputOptions: { jsx: dts.jsx } } : {},
163
- plugins: [
164
- ...dts.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
165
- ...options.extraPlugins ?? [],
166
- ...metricsPlugins(group.id, "dts")
167
- ]
168
- }));
146
+ const dtsDeps = dtsNeverBundle.length > 0 || partBundleNodeModules || dts.bundledPackages ? { deps: {
147
+ ...dtsNeverBundle.length > 0 ? { neverBundle: dtsNeverBundle } : {},
148
+ ...partBundleNodeModules ? {
149
+ skipNodeModulesBundle: false,
150
+ ...dts.bundledPackages ? { dts: { alwaysBundle: dts.bundledPackages } } : {}
151
+ } : dts.bundledPackages ? {
152
+ skipNodeModulesBundle: true,
153
+ dts: { alwaysBundle: dts.bundledPackages }
154
+ } : {}
155
+ } } : {};
156
+ const stubEntries = /* @__PURE__ */ new Map();
157
+ const readSource = (src) => {
158
+ try {
159
+ return readFileSync(isAbsolute(src) ? src : join(options.cwd, src), "utf-8");
160
+ } catch {
161
+ return;
162
+ }
163
+ };
164
+ const baseSrc = dts.entry[STUB_BASE_ENTRY];
165
+ if (baseSrc !== void 0 && Object.keys(dts.entry).length > 1) {
166
+ const baseSource = readSource(baseSrc);
167
+ const base = baseSource !== void 0 ? collectExportNames(baseSource) : void 0;
168
+ if (base?.complete === true) for (const [entryName, entrySource] of Object.entries(dts.entry)) {
169
+ if (entryName === STUB_BASE_ENTRY || entryName.includes("/")) continue;
170
+ const source = readSource(entrySource);
171
+ if (source === void 0) continue;
172
+ const analysis = analyzeReexportBarrel(source);
173
+ if (!analysis.isPureNamedReexportBarrel) continue;
174
+ const all = [...analysis.valueNames, ...analysis.typeNames];
175
+ if (all.length === 0 || !all.every((n) => base.names.has(n))) continue;
176
+ stubEntries.set(entryName, {
177
+ valueNames: analysis.valueNames,
178
+ typeNames: analysis.typeNames
179
+ });
180
+ }
181
+ }
182
+ await timed(group.id, "dts", async () => {
183
+ for (const [entryName, entrySource] of Object.entries(dts.entry)) {
184
+ const stub = stubEntries.get(entryName);
185
+ if (stub !== void 0) {
186
+ const variants = [{
187
+ file: `${entryName}.d.ts`,
188
+ spec: `./${STUB_BASE_ENTRY}.js`
189
+ }];
190
+ if (dts.format.includes("cjs")) variants.push({
191
+ file: `${entryName}.d.cts`,
192
+ spec: `./${STUB_BASE_ENTRY}.cjs`
193
+ });
194
+ for (const variant of variants) {
195
+ const content = renderReexportStub({
196
+ valueNames: stub.valueNames,
197
+ typeNames: stub.typeNames,
198
+ baseSpecifier: variant.spec
199
+ });
200
+ writeFileSync(join(partOutDir, variant.file), content, "utf-8");
201
+ collector?.recordEmitted(group.id, "dts", {
202
+ path: variant.file,
203
+ bytes: Buffer.byteLength(content)
204
+ });
205
+ }
206
+ continue;
207
+ }
208
+ await build({
209
+ config: false,
210
+ cwd: options.cwd,
211
+ entry: { [entryName]: entrySource },
212
+ outDir: partOutDir,
213
+ format: dts.format,
214
+ platform: dts.platform,
215
+ sourcemap: dts.sourcemap,
216
+ unbundle: dts.unbundle,
217
+ clean: false,
218
+ fixedExtension: dts.fixedExtension,
219
+ dts: dts.dts,
220
+ define: dts.define,
221
+ ...instrument(group.id),
222
+ ...dtsDeps,
223
+ plugins: [
224
+ ...dts.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
225
+ ...options.extraPlugins ?? [],
226
+ ...metricsPlugins(group.id, "dts")
227
+ ]
228
+ });
229
+ }
230
+ });
169
231
  if (js.isProd && options.emitDeclarations === true && Object.keys(decl.entry).length > 0) {
170
232
  const partDeclDir = part.outSubdir !== void 0 ? join(decl.outDir, part.outSubdir) : decl.outDir;
171
233
  try {
@@ -193,7 +255,6 @@ async function buildTargetGroups(options) {
193
255
  dts: { alwaysBundle: decl.bundledPackages }
194
256
  } : {}
195
257
  } } : {},
196
- ...decl.jsx !== void 0 ? { inputOptions: { jsx: decl.jsx } } : {},
197
258
  ...options.extraPlugins !== void 0 ? { plugins: [...options.extraPlugins] } : {}
198
259
  });
199
260
  } catch (err) {
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
1
  import { readdirSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
 
4
4
  //#region src/build/strip-maps.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { dirname, join, relative } from "node:path";
2
1
  import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
2
+ import { dirname, join, relative } from "node:path";
3
3
 
4
4
  //#region src/build/sync-public.ts
5
5
  /** Recursively collect every file path under `dir`, relative to `base`. */
@@ -34,8 +34,7 @@ function deriveTargetGroupOptions(options) {
34
34
  ...options.define
35
35
  },
36
36
  isProd,
37
- ...hasCjs ? { cjsDefault: true } : {},
38
- ...options.jsx !== void 0 ? { jsx: options.jsx } : {}
37
+ ...hasCjs ? { cjsDefault: true } : {}
39
38
  };
40
39
  }
41
40
  /** Derive the dts-pass tsdown options for one TargetGroup (bundled declarations only). */
@@ -61,7 +60,6 @@ function deriveDtsPassOptions(options) {
61
60
  ...options.define
62
61
  },
63
62
  isProd,
64
- ...options.jsx !== void 0 ? { jsx: options.jsx } : {},
65
63
  ...options.bundledPackages !== void 0 ? { bundledPackages: options.bundledPackages } : {}
66
64
  };
67
65
  }
@@ -90,8 +88,7 @@ function deriveDeclarationsPassOptions(options) {
90
88
  "process.env.__PACKAGE_VERSION__": JSON.stringify(options.version),
91
89
  ...options.define
92
90
  },
93
- ...options.bundledPackages !== void 0 ? { bundledPackages: options.bundledPackages } : {},
94
- ...options.jsx !== void 0 ? { jsx: options.jsx } : {}
91
+ ...options.bundledPackages !== void 0 ? { bundledPackages: options.bundledPackages } : {}
95
92
  };
96
93
  }
97
94
 
@@ -3,8 +3,8 @@ import { normalizeLooseFiles } from "../build/loose-files.js";
3
3
  import { ConfigValidator } from "./ConfigValidator.js";
4
4
  import { normalizeExeOptions } from "../exe/config.js";
5
5
  import { resolveTargets } from "../targets/resolve-targets.js";
6
- import { Effect, Layer } from "effect";
7
6
  import { existsSync, statSync } from "node:fs";
7
+ import { Effect, Layer } from "effect";
8
8
 
9
9
  //#region src/config-validation/ConfigValidatorLive.ts
10
10
  const VALID_SYNTAX_KINDS = /* @__PURE__ */ new Set([
@@ -0,0 +1,92 @@
1
+ import ts from "typescript";
2
+
3
+ //#region src/dts/reexport-stub.ts
4
+ function parse(source, fileName) {
5
+ return ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
6
+ }
7
+ /**
8
+ * Analyze an entry source as a candidate pure re-export barrel: classify its re-exported names into
9
+ * value vs type-only and report whether it is expressible as a thin stub. Pure parsing — no I/O.
10
+ *
11
+ * @public
12
+ */
13
+ function analyzeReexportBarrel(source, fileName = "entry.ts") {
14
+ const sf = parse(source, fileName);
15
+ const valueNames = [];
16
+ const typeNames = [];
17
+ let pure = true;
18
+ for (const stmt of sf.statements) {
19
+ if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier !== void 0) {
20
+ const clause = stmt.exportClause;
21
+ if (clause !== void 0 && ts.isNamedExports(clause)) {
22
+ for (const el of clause.elements) (stmt.isTypeOnly || el.isTypeOnly ? typeNames : valueNames).push(el.name.text);
23
+ continue;
24
+ }
25
+ pure = false;
26
+ continue;
27
+ }
28
+ pure = false;
29
+ }
30
+ return {
31
+ valueNames,
32
+ typeNames,
33
+ isPureNamedReexportBarrel: pure
34
+ };
35
+ }
36
+ /**
37
+ * Collect every name a module exports (named re-exports, namespace re-exports, and local `export`
38
+ * declarations). Used to test whether a barrel's re-exports are a strict subset of a base entry, so
39
+ * a stub re-exporting from that base resolves every symbol. Pure parsing — no I/O.
40
+ *
41
+ * @public
42
+ */
43
+ function collectExportNames(source, fileName = "entry.ts") {
44
+ const sf = parse(source, fileName);
45
+ const names = /* @__PURE__ */ new Set();
46
+ let complete = true;
47
+ const addBindingName = (name) => {
48
+ if (ts.isIdentifier(name)) names.add(name.text);
49
+ else for (const el of name.elements) if (ts.isBindingElement(el)) addBindingName(el.name);
50
+ };
51
+ for (const stmt of sf.statements) {
52
+ if (ts.isExportDeclaration(stmt)) {
53
+ const clause = stmt.exportClause;
54
+ if (clause === void 0) {
55
+ if (stmt.moduleSpecifier !== void 0) complete = false;
56
+ continue;
57
+ }
58
+ if (ts.isNamespaceExport(clause)) {
59
+ names.add(clause.name.text);
60
+ continue;
61
+ }
62
+ for (const el of clause.elements) names.add(el.name.text);
63
+ continue;
64
+ }
65
+ if (!(ts.canHaveModifiers(stmt) ? (ts.getModifiers(stmt) ?? []).some((m) => m.kind === ts.SyntaxKind.ExportKeyword) : false)) continue;
66
+ if (ts.isVariableStatement(stmt)) for (const decl of stmt.declarationList.declarations) addBindingName(decl.name);
67
+ else if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt) || ts.isInterfaceDeclaration(stmt) || ts.isTypeAliasDeclaration(stmt) || ts.isEnumDeclaration(stmt) || ts.isModuleDeclaration(stmt)) && stmt.name !== void 0 && ts.isIdentifier(stmt.name)) names.add(stmt.name.text);
68
+ }
69
+ return {
70
+ names,
71
+ complete
72
+ };
73
+ }
74
+ /**
75
+ * Render a thin re-export-stub `.d.ts`/`.d.cts` body: named re-exports of `valueNames` and
76
+ * `typeNames` from `baseSpecifier` (the published file of the base entry, e.g. `./index.js` for the
77
+ * ESM `.d.ts` or `./index.cjs` for the CJS `.d.cts`). Names are sorted so the output is
78
+ * deterministic. Returns the empty string when there is nothing to re-export.
79
+ *
80
+ * @public
81
+ */
82
+ function renderReexportStub(options) {
83
+ const sortedValues = [...options.valueNames].sort();
84
+ const sortedTypes = [...options.typeNames].sort();
85
+ const lines = [];
86
+ if (sortedValues.length > 0) lines.push(`export { ${sortedValues.join(", ")} } from "${options.baseSpecifier}";`);
87
+ if (sortedTypes.length > 0) lines.push(`export type { ${sortedTypes.join(", ")} } from "${options.baseSpecifier}";`);
88
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
89
+ }
90
+
91
+ //#endregion
92
+ export { analyzeReexportBarrel, collectExportNames, renderReexportStub };
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
- import { writeFileSync } from "node:fs";
1
+ import { existsSync, writeFileSync } from "node:fs";
2
+ import { isAbsolute, join, resolve } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
 
5
5
  //#region src/dts/resolved-tsconfig.ts
@@ -47,6 +47,33 @@ function writeResolvedTsconfig(options) {
47
47
  writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
48
48
  return path;
49
49
  }
50
+ /**
51
+ * Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
52
+ * `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
53
+ * (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
54
+ * multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
55
+ * order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
56
+ * api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
57
+ * predates the flag and hard-errors on the unknown compiler option — so only the emit passes
58
+ * (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
59
+ *
60
+ * Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
61
+ * never written), the original path is returned unchanged — the emit then simply keeps TS's
62
+ * default ordering rather than aborting the build at this layer.
63
+ *
64
+ * @public
65
+ */
66
+ function writeDtsEmitTsconfig(resolvedTsconfigPath) {
67
+ const absBase = isAbsolute(resolvedTsconfigPath) ? resolvedTsconfigPath : resolve(resolvedTsconfigPath);
68
+ if (!existsSync(absBase)) return resolvedTsconfigPath;
69
+ const cfg = {
70
+ extends: absBase,
71
+ compilerOptions: { stableTypeOrdering: true }
72
+ };
73
+ const path = join(tmpdir(), `tsconfig-dts-emit-${process.pid}-${absBase.replace(/[^\w]/g, "_")}.json`);
74
+ writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
75
+ return path;
76
+ }
50
77
 
51
78
  //#endregion
52
- export { buildResolvedTsconfig, writeResolvedTsconfig };
79
+ export { buildResolvedTsconfig, writeDtsEmitTsconfig, writeResolvedTsconfig };
@@ -1,6 +1,6 @@
1
1
  import { extractEntries } from "./extract.js";
2
- import { resolve } from "node:path";
3
2
  import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
4
 
5
5
  //#region src/entry/package-json-entries.ts
6
6
  /**
package/exe/build.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { buildMetricsPlugin } from "../report/metrics-plugin.js";
2
2
  import { createTimer } from "../report/timer.js";
3
3
  import { createTsdownLogger } from "../report/tsdown-logger.js";
4
- import { join } from "node:path";
5
4
  import { mkdtempSync, rmSync } from "node:fs";
5
+ import { join } from "node:path";
6
6
  import { tmpdir } from "node:os";
7
7
 
8
8
  //#region src/exe/build.ts
package/index.d.ts CHANGED
@@ -3,40 +3,6 @@ import { Plugin } from "rolldown";
3
3
  import { Context, Effect, Layer, Schema } from "effect";
4
4
  import { JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ParsedCommandLine, ScriptTarget } from "typescript";
5
5
 
6
- //#region src/jsx/config.d.ts
7
- /**
8
- * Resolved JSX transform settings, mirroring the subset of rolldown's JsxOptions the bundler forwards.
9
- *
10
- * @public
11
- */
12
- interface JsxConfig {
13
- /** "automatic" auto-imports the JSX factories (react-jsx); "classic" does not (React.createElement). */
14
- readonly runtime?: "classic" | "automatic" | undefined;
15
- /** The JSX import source for the automatic runtime (e.g. "react", "preact"). */
16
- readonly importSource?: string | undefined;
17
- }
18
- /**
19
- * The jsx-relevant slice of a tsconfig's compilerOptions.
20
- *
21
- * @public
22
- */
23
- interface TsconfigJsx {
24
- readonly jsx?: string | undefined;
25
- readonly jsxImportSource?: string | undefined;
26
- }
27
- /**
28
- * Resolve the effective JSX config: an explicit override wins; otherwise infer from the tsconfig
29
- * values. Returns undefined when no JSX transform is needed (preserve/none).
30
- * @public
31
- */
32
- declare function resolveJsxConfig(tsconfig: TsconfigJsx, override: JsxConfig | undefined): JsxConfig | undefined;
33
- /**
34
- * Read the jsx-relevant compilerOptions from a package's own tsconfig.json (best-effort;
35
- * returns empty on absence or parse error).
36
- * @public
37
- */
38
- declare function readTsconfigJsx(cwd: string): TsconfigJsx;
39
- //#endregion
40
6
  //#region src/manifest/transform.d.ts
41
7
  /** @public */
42
8
  type Json = Record<string, unknown>;
@@ -211,21 +177,21 @@ declare const DiagnosticEntry_base: Schema.Class<DiagnosticEntry, {
211
177
  line: Schema.optional<typeof Schema.Number>;
212
178
  column: Schema.optional<typeof Schema.Number>;
213
179
  }>, never, {
214
- readonly text: string;
215
- } & {
216
- readonly source: "tsdown" | "rolldown" | "api-extractor";
217
- } & {
218
- readonly level: "warn" | "error";
180
+ readonly ciFatal?: boolean | undefined;
219
181
  } & {
220
182
  readonly code?: string | undefined;
221
183
  } & {
222
- readonly ciFatal?: boolean | undefined;
184
+ readonly column?: number | undefined;
223
185
  } & {
224
186
  readonly file?: string | undefined;
225
187
  } & {
226
188
  readonly line?: number | undefined;
227
189
  } & {
228
- readonly column?: number | undefined;
190
+ readonly level: "error" | "warn";
191
+ } & {
192
+ readonly source: "api-extractor" | "rolldown" | "tsdown";
193
+ } & {
194
+ readonly text: string;
229
195
  }, {}, {}>;
230
196
  /**
231
197
  * A captured warning or error, from tsdown's logger, rolldown's onLog, or API Extractor.
@@ -242,11 +208,11 @@ declare const EmittedFile_base: Schema.Class<EmittedFile, {
242
208
  bytes: typeof Schema.Number;
243
209
  gzip: Schema.optional<typeof Schema.Number>;
244
210
  }>, never, {
245
- readonly path: string;
211
+ readonly gzip?: number | undefined;
246
212
  } & {
247
213
  readonly bytes: number;
248
214
  } & {
249
- readonly gzip?: number | undefined;
215
+ readonly path: string;
250
216
  }, {}, {}>;
251
217
  /**
252
218
  * One emitted output file with its in-memory byte size (gzip only when --verbose).
@@ -263,11 +229,11 @@ declare const PassReport_base: Schema.Class<PassReport, {
263
229
  files: Schema.Array$<typeof EmittedFile>;
264
230
  ms: typeof Schema.Number;
265
231
  }>, never, {
266
- readonly ms: number;
232
+ readonly files: readonly EmittedFile[];
267
233
  } & {
268
- readonly id: "js" | "dts" | "loose" | "exe" | "meta";
234
+ readonly id: "dts" | "exe" | "js" | "loose" | "meta";
269
235
  } & {
270
- readonly files: readonly EmittedFile[];
236
+ readonly ms: number;
271
237
  }, {}, {}>;
272
238
  /**
273
239
  * One build pass within a target group (js / dts / loose / exe / meta).
@@ -294,17 +260,17 @@ declare const TargetGroupReport_base: Schema.Class<TargetGroupReport, {
294
260
  }>, never, {
295
261
  readonly entries: readonly string[];
296
262
  } & {
297
- readonly id: string;
263
+ readonly errors: readonly DiagnosticEntry[];
298
264
  } & {
299
- readonly timings: ReportTimings;
265
+ readonly id: string;
300
266
  } & {
301
267
  readonly passes: readonly PassReport[];
302
268
  } & {
303
- readonly warnings: readonly DiagnosticEntry[];
269
+ readonly suppressed: readonly DiagnosticEntry[];
304
270
  } & {
305
- readonly errors: readonly DiagnosticEntry[];
271
+ readonly timings: ReportTimings;
306
272
  } & {
307
- readonly suppressed: readonly DiagnosticEntry[];
273
+ readonly warnings: readonly DiagnosticEntry[];
308
274
  }, {}, {}>;
309
275
  /** @public */
310
276
  declare class TargetGroupReport extends TargetGroupReport_base {}
@@ -401,8 +367,6 @@ interface DeriveOptions {
401
367
  readonly format?: ReadonlyArray<BuildFormat> | undefined;
402
368
  /** Minify prod output (prod groups only; dev is never minified). Defaults to false. */
403
369
  readonly minify?: boolean | undefined;
404
- /** JSX transform settings to forward to rolldown's inputOptions. */
405
- readonly jsx?: JsxConfig | undefined;
406
370
  /**
407
371
  * Compile-time global replacements forwarded to the build `define`. Merged AFTER the
408
372
  * auto-injected `process.env.__PACKAGE_VERSION__` so a user key of the same name wins.
@@ -483,8 +447,6 @@ interface DerivedTsdownOptions {
483
447
  * default untouched and stay byte-identical to before.
484
448
  */
485
449
  readonly cjsDefault?: boolean | undefined;
486
- /** JSX transform settings to forward to rolldown's inputOptions. */
487
- readonly jsx?: JsxConfig | undefined;
488
450
  }
489
451
  /**
490
452
  * Derive the JS-pass tsdown options for one TargetGroup (per-module JS, no dts).
@@ -651,8 +613,6 @@ interface BuildTargetGroupsOptions {
651
613
  * firing exactly once should guard the second invocation.
652
614
  */
653
615
  readonly extraPlugins?: ReadonlyArray<Plugin>;
654
- /** JSX transform settings forwarded to rolldown's inputOptions. */
655
- readonly jsx?: JsxConfig | undefined;
656
616
  /**
657
617
  * Compile-time global replacements forwarded to BOTH the JS and dts passes' `define`.
658
618
  * Build-wide (shared by every entry partition); merged after the auto-injected
@@ -1149,6 +1109,69 @@ declare class ConfigValidator extends ConfigValidator_base {}
1149
1109
  */
1150
1110
  declare const ConfigValidatorLive: Layer.Layer<ConfigValidator, never, never>;
1151
1111
  //#endregion
1112
+ //#region src/dts/reexport-stub.d.ts
1113
+ /**
1114
+ * The analysis of an entry source treated as a candidate re-export barrel.
1115
+ *
1116
+ * @public
1117
+ */
1118
+ interface ReexportBarrelAnalysis {
1119
+ /** Value (non-type-only) names the module re-exports, after `as` aliasing. */
1120
+ readonly valueNames: ReadonlyArray<string>;
1121
+ /** Type-only names the module re-exports (`export type { … }`), after `as` aliasing. */
1122
+ readonly typeNames: ReadonlyArray<string>;
1123
+ /**
1124
+ * True iff EVERY top-level statement is a NAMED re-export `from` another module
1125
+ * (`export { … } from "…"` / `export type { … } from "…"`). A module that declares anything
1126
+ * locally, re-exports a namespace (`export * as NS from`), star-re-exports (`export * from`), or
1127
+ * has a bare `export { … }` without `from` is NOT a pure named barrel and cannot be expressed as
1128
+ * a thin re-export stub of another entry.
1129
+ */
1130
+ readonly isPureNamedReexportBarrel: boolean;
1131
+ }
1132
+ /**
1133
+ * The set of names a module exports, plus whether that set is fully known.
1134
+ *
1135
+ * @public
1136
+ */
1137
+ interface ModuleExportNames {
1138
+ readonly names: ReadonlySet<string>;
1139
+ /**
1140
+ * False when the module contains a star re-export (`export * from "…"`) whose target exports
1141
+ * cannot be enumerated from this source alone — the name set is then a lower bound, not complete,
1142
+ * so callers must not use it for a strict subset decision.
1143
+ */
1144
+ readonly complete: boolean;
1145
+ }
1146
+ /**
1147
+ * Analyze an entry source as a candidate pure re-export barrel: classify its re-exported names into
1148
+ * value vs type-only and report whether it is expressible as a thin stub. Pure parsing — no I/O.
1149
+ *
1150
+ * @public
1151
+ */
1152
+ declare function analyzeReexportBarrel(source: string, fileName?: string): ReexportBarrelAnalysis;
1153
+ /**
1154
+ * Collect every name a module exports (named re-exports, namespace re-exports, and local `export`
1155
+ * declarations). Used to test whether a barrel's re-exports are a strict subset of a base entry, so
1156
+ * a stub re-exporting from that base resolves every symbol. Pure parsing — no I/O.
1157
+ *
1158
+ * @public
1159
+ */
1160
+ declare function collectExportNames(source: string, fileName?: string): ModuleExportNames;
1161
+ /**
1162
+ * Render a thin re-export-stub `.d.ts`/`.d.cts` body: named re-exports of `valueNames` and
1163
+ * `typeNames` from `baseSpecifier` (the published file of the base entry, e.g. `./index.js` for the
1164
+ * ESM `.d.ts` or `./index.cjs` for the CJS `.d.cts`). Names are sorted so the output is
1165
+ * deterministic. Returns the empty string when there is nothing to re-export.
1166
+ *
1167
+ * @public
1168
+ */
1169
+ declare function renderReexportStub(options: {
1170
+ readonly valueNames: ReadonlyArray<string>;
1171
+ readonly typeNames: ReadonlyArray<string>;
1172
+ readonly baseSpecifier: string;
1173
+ }): string;
1174
+ //#endregion
1152
1175
  //#region src/dts/resolved-tsconfig.d.ts
1153
1176
  /** @public */
1154
1177
  interface ResolvedTsconfigOptions {
@@ -1179,6 +1202,23 @@ declare function buildResolvedTsconfig(options: ResolvedTsconfigOptions): Resolv
1179
1202
  * @public
1180
1203
  */
1181
1204
  declare function writeResolvedTsconfig(options: ResolvedTsconfigOptions): string;
1205
+ /**
1206
+ * Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
1207
+ * `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
1208
+ * (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
1209
+ * multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
1210
+ * order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
1211
+ * api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
1212
+ * predates the flag and hard-errors on the unknown compiler option — so only the emit passes
1213
+ * (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
1214
+ *
1215
+ * Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
1216
+ * never written), the original path is returned unchanged — the emit then simply keeps TS's
1217
+ * default ordering rather than aborting the build at this layer.
1218
+ *
1219
+ * @public
1220
+ */
1221
+ declare function writeDtsEmitTsconfig(resolvedTsconfigPath: string): string;
1182
1222
  //#endregion
1183
1223
  //#region src/entry/extract.d.ts
1184
1224
  /** @public */
@@ -1271,6 +1311,42 @@ declare function runExeBuild(options: RunExeBuildOptions): Promise<void>;
1271
1311
  */
1272
1312
  declare function computeExeFileName(fileName: string, target: ExeTarget): string;
1273
1313
  //#endregion
1314
+ //#region src/jsx/config.d.ts
1315
+ /**
1316
+ * Resolved JSX transform settings. The shape mirrors the subset of rolldown's JsxOptions, but the
1317
+ * bundler consumes it to populate the generated dts tsconfig's `jsx`/`jsxImportSource`, not by
1318
+ * forwarding it into rolldown's input options.
1319
+ *
1320
+ * @public
1321
+ */
1322
+ interface JsxConfig {
1323
+ /** "automatic" auto-imports the JSX factories (react-jsx); "classic" does not (React.createElement). */
1324
+ readonly runtime?: "classic" | "automatic" | undefined;
1325
+ /** The JSX import source for the automatic runtime (e.g. "react", "preact"). */
1326
+ readonly importSource?: string | undefined;
1327
+ }
1328
+ /**
1329
+ * The jsx-relevant slice of a tsconfig's compilerOptions.
1330
+ *
1331
+ * @public
1332
+ */
1333
+ interface TsconfigJsx {
1334
+ readonly jsx?: string | undefined;
1335
+ readonly jsxImportSource?: string | undefined;
1336
+ }
1337
+ /**
1338
+ * Resolve the effective JSX config: an explicit override wins; otherwise infer from the tsconfig
1339
+ * values. Returns undefined when no JSX transform is needed (preserve/none).
1340
+ * @public
1341
+ */
1342
+ declare function resolveJsxConfig(tsconfig: TsconfigJsx, override: JsxConfig | undefined): JsxConfig | undefined;
1343
+ /**
1344
+ * Read the jsx-relevant compilerOptions from a package's own tsconfig.json (best-effort;
1345
+ * returns empty on absence or parse error).
1346
+ * @public
1347
+ */
1348
+ declare function readTsconfigJsx(cwd: string): TsconfigJsx;
1349
+ //#endregion
1274
1350
  //#region src/meta/generate.d.ts
1275
1351
  /** @public */
1276
1352
  interface GenerateMetaOptions {
@@ -1722,5 +1798,5 @@ declare function resolveTargets(options: {
1722
1798
  baseName: string;
1723
1799
  }): TargetResolution;
1724
1800
  //#endregion
1725
- export { BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, 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 DiagnosticEntry, type DiagnosticInput, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeRewrite, 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 NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, 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, type RunMetaPassOptions, 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 TsdownLogger, type ValidationInput, type WarningSuppressionRule, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, syncPublicDir, transformBin, transformExports, transformManifest, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
1801
+ export { BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, 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 DiagnosticEntry, type DiagnosticInput, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeRewrite, 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 ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, 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 TsdownLogger, type ValidationInput, type WarningSuppressionRule, analyzeReexportBarrel, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, collectExportNames, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, syncPublicDir, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
1726
1802
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { analyzeReexportBarrel, collectExportNames, renderReexportStub } from "./dts/reexport-stub.js";
2
+ import { buildResolvedTsconfig, writeDtsEmitTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
1
3
  import { resolveManifest } from "./catalog/resolve-catalogs.js";
2
4
  import { createEntryName, extractEntries } from "./entry/extract.js";
3
5
  import { defaultManifestTransform, normalizeBinPaths, transformBin, transformExports, transformManifest } from "./manifest/transform.js";
@@ -19,7 +21,6 @@ import { DEFAULT_EXE_NODE_VERSION, normalizeExeOptions } from "./exe/config.js";
19
21
  import { isTargetObject } from "./targets/config.js";
20
22
  import { resolveTargets } from "./targets/resolve-targets.js";
21
23
  import { ConfigValidatorLive } from "./config-validation/ConfigValidatorLive.js";
22
- import { buildResolvedTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
23
24
  import { packageJsonEntries } from "./entry/package-json-entries.js";
24
25
  import { runExeBuild } from "./exe/build.js";
25
26
  import { computeExeFileName } from "./exe/filename.js";
@@ -49,4 +50,4 @@ import { ReportPipelineLive, renderReport } from "./report/pipeline.js";
49
50
  import { writeTargetsBinding } from "./targets/binding.js";
50
51
  import { CatalogAssemblyError, CatalogResolutionError } from "workspaces-effect";
51
52
 
52
- export { BuildCollector, BuildCollectorTag, BuildReport as BuildReportSchema, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, EnvironmentDetector, EnvironmentDetectorLive, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, JsonFormatter, MarkdownFormatter, MetaGenerationError, OutputRenderer, OutputRendererLive, ReportPipelineLive, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsconfigResolver, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, syncPublicDir, transformBin, transformExports, transformManifest, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
53
+ export { BuildCollector, BuildCollectorTag, BuildReport as BuildReportSchema, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, EnvironmentDetector, EnvironmentDetectorLive, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, JsonFormatter, MarkdownFormatter, MetaGenerationError, OutputRenderer, OutputRendererLive, ReportPipelineLive, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsconfigResolver, analyzeReexportBarrel, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, collectExportNames, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, syncPublicDir, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
package/jsx/config.js CHANGED
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
1
  import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
 
4
4
  //#region src/jsx/config.ts
5
5
  /**
@@ -43,7 +43,8 @@ function runApiExtractor(options) {
43
43
  compiler: { tsconfigFilePath: options.tsconfigPath },
44
44
  docModel: options.emitDocModel === false ? { enabled: false } : {
45
45
  enabled: true,
46
- apiJsonFilePath: options.apiJsonPath
46
+ apiJsonFilePath: options.apiJsonPath,
47
+ includeForgottenExports: true
47
48
  },
48
49
  ...options.tsdocMetadataPath !== void 0 ? { tsdocMetadata: {
49
50
  enabled: true,
@@ -102,4 +103,4 @@ function runApiExtractor(options) {
102
103
  }
103
104
 
104
105
  //#endregion
105
- export { runApiExtractor };
106
+ export { mapExtractorMessage, runApiExtractor };
package/meta/generate.js CHANGED
@@ -2,8 +2,8 @@ import { runApiExtractor } from "./api-extractor.js";
2
2
  import { mergeApiModels } from "./merge-models.js";
3
3
  import { resolvePortableTsconfig } from "./tsconfig-resolver.js";
4
4
  import { writeTsdocConfig } from "./tsdoc-config.js";
5
- import { join } from "node:path";
6
5
  import { copyFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
7
 
8
8
  //#region src/meta/generate.ts
9
9
  function unscopedName(name) {
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
1
  import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
  import { JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ScriptTarget, flattenDiagnosticMessageText, getParsedCommandLineOfConfigFile, sys } from "typescript";
4
4
 
5
5
  //#region src/meta/tsconfig-resolver.ts
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
  import { StandardTags } from "@microsoft/tsdoc";
4
4
  import deepEqual from "deep-equal";
5
5
 
@@ -44,4 +44,4 @@ function writeTsdocConfig(cwd, tsdoc) {
44
44
  }
45
45
 
46
46
  //#endregion
47
- export { writeTsdocConfig };
47
+ export { buildTsdocConfig, writeTsdocConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/tsdown-plugins",
3
- "version": "0.11.0",
3
+ "version": "0.12.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",
@@ -1,5 +1,5 @@
1
- import { dirname, join } from "node:path";
2
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
3
 
4
4
  //#region src/report/issues-artifact.ts
5
5
  /** Copy a DiagnosticEntry to a plain object, omitting undefined optionals for stable output. */
@@ -1,5 +1,5 @@
1
- import { join } from "node:path";
2
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
 
4
4
  //#region src/targets/binding.ts
5
5
  /**