@savvy-web/bundler 0.7.0 → 0.9.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
@@ -89,6 +89,8 @@ With no `targets` map the build falls back to the single-`npm` group above.
89
89
 
90
90
  `savvy build --target prod` generates an [API Extractor](https://api-extractor.com/) api-model from each prod group's resolved `.d.ts`. For every group it writes the bundle (`<unscoped>.api.json`, `tsdoc-metadata.json` and a resolved `tsconfig.json`) into `dist/prod/<group>/meta` as a release asset alongside `pkg/`, and copies the canonical group's bundle into any `localPaths` directories. Because it reads the prod build, the meta `package.json` carries concrete dependency versions rather than `catalog:`/`workspace:` specifiers.
91
91
 
92
+ API Extractor's analyzer messages — forgotten exports, missing release tags and TSDoc issues — surface in the build log rather than being dropped. A forgotten export (a type reachable from your public API but not itself exported) fails the build under CI (`CI` or `GITHUB_ACTIONS` set); locally it stays a warning so an incremental build is not blocked. Listing the message in `tsdoc.suppressWarnings` suppresses both the local warning and the CI failure, and the build log accounts for what it suppressed.
93
+
92
94
  The `meta` field on `defineBuild` is tri-state:
93
95
 
94
96
  - **Omitted** (or `undefined`) — generation runs with default options. This is the default; you do not need a `meta` field for `--target prod` to emit the api-model.
@@ -297,7 +299,7 @@ const config = defineBuild({
297
299
  ## API
298
300
 
299
301
  - `defineBuild(input)` — normalizes a build config (`externals`, `bundle`, `bundleNodeModules`, `bundledPackages`, `dtsExternals`, `minify`, `devManifest`, `transform`, `output`, `meta`, `jsx`, `exe`, `format`, `overrides`, `looseFiles`, `define`), applying defaults. The `format` field controls the output module formats forwarded to tsdown (esm-only by default; add `"cjs"` for a dual-format esm+cjs build). `minify` defaults to false, `transform` defaults to a manifest stripper, and `overrides` pins a subset of entries to their own format and bundling. Pure; it does not run the build.
300
- - `runBuild(config, options)` — the orchestrator. Parses `--target`/`--watch` from `options.argv`, reads `package.json` at `options.cwd`, derives entries, drives the build for the selected target and renders a report. Every IO dependency on `options` is injectable for tests.
302
+ - `runBuild(config, options)` — the orchestrator. Parses `--target`/`--watch`/`--verbose` from `options.argv`, reads `package.json` at `options.cwd`, derives entries, drives the build for the selected target and renders a report. `--verbose` expands the report to a per-file table; the report is quiet by default. Every IO dependency on `options` is injectable for tests.
301
303
  - `parseArgs(argv)` — the argument parser behind `runBuild`, exported for embedding.
302
304
 
303
305
  ## Turbo tasks
package/config.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import { defaultManifestTransform } from "@savvy-web/tsdown-plugins";
2
2
 
3
3
  //#region src/config.ts
4
- /** Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts). */
4
+ /**
5
+ * Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts).
6
+ *
7
+ * @public
8
+ */
5
9
  function defineBuild(input = {}) {
6
10
  return {
7
11
  formats: input.formats ?? ["esm"],
@@ -23,20 +27,24 @@ function defineBuild(input = {}) {
23
27
  define: input.define
24
28
  };
25
29
  }
30
+ /** Parse the build CLI argv into the normalized target/flags shape. @public */
26
31
  function parseArgs(argv) {
27
32
  let target = "dev";
28
33
  let watch = false;
29
34
  let noExe = false;
35
+ let verbose = false;
30
36
  for (let i = 0; i < argv.length; i++) if (argv[i] === "--target") {
31
37
  const v = argv[i + 1];
32
38
  if (v === "dev" || v === "prod" || v === "meta" || v === "exe") target = v;
33
39
  i++;
34
40
  } else if (argv[i] === "--watch") watch = true;
35
41
  else if (argv[i] === "--no-exe") noExe = true;
42
+ else if (argv[i] === "--verbose") verbose = true;
36
43
  return {
37
44
  target,
38
45
  watch,
39
- noExe
46
+ noExe,
47
+ verbose
40
48
  };
41
49
  }
42
50
 
package/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { BuildFormat, BuildPlatform, BuildTargetGroupsOptions, CssOptions, ExeConfig, GenerateMetaOptions, Json, JsxConfig, LooseFiles, MetaOptions, MetaResult, NextVersions, PublishTargets, RenderedOutput, RunExeBuildOptions, TargetGroupRef, TargetResolution, TsconfigJsx, defaultManifestTransform } from "@savvy-web/tsdown-plugins";
1
+ import { BuildFormat, BuildPlatform, BuildReport, BuildTargetGroupsOptions, CssOptions, ExeConfig, GenerateMetaOptions, Json, JsxConfig, LooseFiles, MetaOptions, MetaResult, NextVersions, PublishTargets, RenderedOutput, RunExeBuildOptions, TargetGroupRef, TargetResolution, TsconfigJsx, defaultManifestTransform } from "@savvy-web/tsdown-plugins";
2
2
 
3
3
  //#region src/config.d.ts
4
+ /** @public */
4
5
  interface BuildEntryOverride {
5
6
  /** Export paths to pin to this partition, e.g. "./changesets/markdownlint" (or "." for root). */
6
7
  readonly entries: ReadonlyArray<string>;
@@ -21,6 +22,7 @@ interface BuildEntryOverride {
21
22
  */
22
23
  readonly outSubdir?: string | undefined;
23
24
  }
25
+ /** @public */
24
26
  interface OutputConfig {
25
27
  readonly console?: {
26
28
  readonly human?: boolean;
@@ -29,6 +31,7 @@ interface OutputConfig {
29
31
  };
30
32
  readonly format?: "terminal" | "json" | "markdown" | "ci-annotations" | "silent";
31
33
  }
34
+ /** @public */
32
35
  interface BuildConfigInput {
33
36
  readonly formats?: ReadonlyArray<"esm">;
34
37
  readonly externals?: ReadonlyArray<string>;
@@ -50,8 +53,8 @@ interface BuildConfigInput {
50
53
  /**
51
54
  * Force-bundle node_modules (and workspace) JS dependencies that are not
52
55
  * externalized into the package output, restoring the self-contained bundle
53
- * the rslib builder produced. Threads tsdown `deps.skipNodeModulesBundle:
54
- * false` into BOTH the JS output and the bundled declarations: the dts posture
56
+ * the rslib builder produced. Threads tsdown `deps.skipNodeModulesBundle: false`
57
+ * into BOTH the JS output and the bundled declarations: the dts posture
55
58
  * tracks the JS posture, so node_modules types are inlined into the `.d.ts`
56
59
  * and the published package needs no extra declared deps for them. Defaults to false.
57
60
  */
@@ -75,7 +78,7 @@ interface BuildConfigInput {
75
78
  readonly devManifest?: "preserve" | "resolve";
76
79
  /**
77
80
  * Final mutation of the emitted package.json, run after the declarative
78
- * `publishConfig.targets` rename. Defaults to {@link defaultManifestTransform},
81
+ * `publishConfig.targets` rename. Defaults to `defaultManifestTransform`,
79
82
  * which strips build/dev-only fields (devDependencies, scripts, publishConfig,
80
83
  * etc.). Supplying your own REPLACES that default — import and call
81
84
  * `defaultManifestTransform` from it if you still want the stripping.
@@ -123,6 +126,7 @@ interface BuildConfigInput {
123
126
  */
124
127
  readonly define?: Record<string, string> | undefined;
125
128
  }
129
+ /** @public */
126
130
  interface BuildConfig {
127
131
  readonly formats: ReadonlyArray<"esm">;
128
132
  readonly externals: ReadonlyArray<string>;
@@ -169,17 +173,25 @@ interface BuildConfig {
169
173
  /** Compile-time global replacements forwarded to the build `define` (merged with the auto-version). */
170
174
  readonly define?: Record<string, string> | undefined;
171
175
  }
172
- /** Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts). */
176
+ /**
177
+ * Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts).
178
+ *
179
+ * @public
180
+ */
173
181
  declare function defineBuild(input?: BuildConfigInput): BuildConfig;
182
+ /** @public */
174
183
  interface ParsedArgs {
175
184
  readonly target: "dev" | "prod" | "meta" | "exe";
176
185
  readonly watch: boolean;
177
186
  /** Skip the SEA compile step of a dev/prod build (the manifest is still programmed). Used by `prepare`. */
178
187
  readonly noExe: boolean;
188
+ readonly verbose: boolean;
179
189
  }
190
+ /** Parse the build CLI argv into the normalized target/flags shape. @public */
180
191
  declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
181
192
  //#endregion
182
193
  //#region src/run.d.ts
194
+ /** @public */
183
195
  interface RunOptions {
184
196
  readonly cwd: string;
185
197
  readonly argv: ReadonlyArray<string>;
@@ -212,8 +224,15 @@ interface RunOptions {
212
224
  }) | undefined;
213
225
  /** Injectable for tests: resolves next release versions for the optimistic meta rewrite. */
214
226
  readonly resolveNextVersions?: ((cwd: string) => Promise<NextVersions>) | undefined;
227
+ /** Injectable issues-artifact writer (defaults to writeIssuesArtifact). */
228
+ readonly writeIssues?: (opts: {
229
+ cwd: string;
230
+ target: "dev" | "prod";
231
+ reports: ReadonlyArray<BuildReport>;
232
+ now?: () => Date;
233
+ }) => string | undefined;
215
234
  }
216
- /** Run a build from a normalized config. Pure orchestration; all IO injectable. */
235
+ /** Run a build from a normalized config. Pure orchestration; all IO injectable. @public */
217
236
  declare function runBuild(config: BuildConfig, options: RunOptions): Promise<void>;
218
237
  //#endregion
219
238
  export { type BuildConfig, type BuildConfigInput, type BuildEntryOverride, type OutputConfig, type ParsedArgs, type RunOptions, defaultManifestTransform, defineBuild, parseArgs, runBuild };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/bundler",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "Zero-config tsdown-based bundler for Silk Suite TypeScript packages",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/bundler",
@@ -29,11 +29,32 @@
29
29
  "./package.json": "./package.json"
30
30
  },
31
31
  "dependencies": {
32
- "@savvy-web/tsdown-plugins": "0.7.0",
32
+ "@savvy-web/tsdown-plugins": "0.9.0",
33
33
  "@tsdown/exe": "^0.22.1",
34
+ "effect": "^3.21.4",
34
35
  "tsdown": "^0.22.3"
35
36
  },
36
37
  "peerDependencies": {
37
- "effect": ">=3.21.0"
38
+ "@types/node": "^26.0.0",
39
+ "@types/react": "^19.2.0",
40
+ "@types/react-dom": "^19.2.0",
41
+ "@typescript/native-preview": "^7.0.0-dev.20260612.1",
42
+ "react": "^19.2.0",
43
+ "react-dom": "^19.2.0",
44
+ "typescript": "^6.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@types/react": {
48
+ "optional": true
49
+ },
50
+ "@types/react-dom": {
51
+ "optional": true
52
+ },
53
+ "react": {
54
+ "optional": true
55
+ },
56
+ "react-dom": {
57
+ "optional": true
58
+ }
38
59
  }
39
60
  }
package/run.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parseArgs } from "./config.js";
2
- import { ConfigValidator, ConfigValidatorLive, ReportPipelineLive, buildEmittedManifest, buildTargetGroups, computeExeFileName, createEntryName, generateMeta, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveNextVersions, resolveTargets, rewriteMetaVersions, runExeBuild, writeResolvedTsconfig, writeTargetsBinding } from "@savvy-web/tsdown-plugins";
2
+ import { BuildCollector, ConfigValidator, ConfigValidatorLive, ReportPipelineLive, buildEmittedManifest, buildTargetGroups, computeExeFileName, createEntryName, deriveExportPaths, normalizeExeOptions, normalizeLooseFiles, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveTargets, runExeBuild, runMetaPass, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding } from "@savvy-web/tsdown-plugins";
3
3
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { Effect } from "effect";
@@ -27,22 +27,14 @@ function deriveProdGroups(targets, baseName) {
27
27
  resolution
28
28
  };
29
29
  }
30
- /** Map entry names to export paths using the package exports map. index maps to ".". */
31
- function deriveExportPaths(entries, exportsMap) {
32
- const out = {};
33
- const sourceToKey = /* @__PURE__ */ new Map();
34
- if (exportsMap) for (const [key, src] of Object.entries(exportsMap)) sourceToKey.set(src, key);
35
- for (const [entryName, src] of Object.entries(entries)) out[entryName] = sourceToKey.get(src) ?? (entryName === "index" ? "." : `./${entryName}`);
36
- return out;
37
- }
38
30
  /**
39
31
  * Fast-fail validation for `outSubdir` overrides, run on EVERY target path. The dev/prod override-partition
40
32
  * loop already validates these (and all other overrides) before building, but `--target meta` returns early
41
- * (before that loop) and remaps meta dts basenames via `applySubdirMetaEntries` — which assumes validated
42
- * input. Without this guard, a malformed `outSubdir` override (more than one entry, a non-canonical export
43
- * path, or an export path that is not a real build entry) would silently remap a wrong/nonexistent key on the
44
- * meta path. Mirrors the override loop's conditions and messages verbatim so every target path fast-fails
45
- * identically. No-op when there are no overrides or none set `outSubdir`.
33
+ * (before that loop) and remaps meta dts basenames (logic now in @savvy-web/tsdown-plugins) — which assumes
34
+ * validated input. Without this guard, a malformed `outSubdir` override (more than one entry, a non-canonical
35
+ * export path, or an export path that is not a real build entry) would silently remap a wrong/nonexistent key
36
+ * on the meta path. Mirrors the override loop's conditions and messages verbatim so every target path
37
+ * fast-fails identically. No-op when there are no overrides or none set `outSubdir`.
46
38
  */
47
39
  function validateSubdirOverrides(overrides, entries, packageName) {
48
40
  if (overrides === void 0) return;
@@ -55,31 +47,15 @@ function validateSubdirOverrides(overrides, entries, packageName) {
55
47
  if (entries[flatName] === void 0) throw new Error(`overrides: export path "${exportPath}" (entry "${flatName}") is not a build entry of ${packageName}`);
56
48
  }
57
49
  }
58
- /**
59
- * For each `outSubdir` override, point its meta entry at the isolated sub-package barrel: the dts lives
60
- * at `<subdir>/index.d.ts` (not `<flatName>.d.ts`). Keyed by the stable flattened entry name so it
61
- * overwrites the default `dtsBasenames[flatName] = flatName` set from the full entry map. No-op when no
62
- * override sets `outSubdir`.
63
- */
64
- function applySubdirMetaEntries(overrides, dtsBasenames, exportPaths) {
65
- if (overrides === void 0) return;
66
- for (const ov of overrides) {
67
- if (ov.outSubdir === void 0) continue;
68
- const exportPath = ov.entries[0];
69
- if (exportPath === void 0) continue;
70
- const flatName = createEntryName(exportPath, false);
71
- dtsBasenames[flatName] = `${ov.outSubdir}/index`;
72
- exportPaths[flatName] = exportPath;
73
- }
74
- }
75
- /** Run a build from a normalized config. Pure orchestration; all IO injectable. */
50
+ /** Run a build from a normalized config. Pure orchestration; all IO injectable. @public */
76
51
  async function runBuild(config, options) {
77
- const { target, noExe } = parseArgs(options.argv);
52
+ const { target, noExe, verbose } = parseArgs(options.argv);
78
53
  const build = options.buildTargetGroups ?? buildTargetGroups;
79
54
  const cwd = options.cwd;
80
55
  const pkg = readPackageJson(cwd);
81
56
  const version = options.readVersion ? options.readVersion() : pkg.version ?? "0.0.0";
82
57
  const packageName = options.readPackageName ? options.readPackageName() : pkg.name ?? "unknown";
58
+ const collector = new BuildCollector();
83
59
  const jsx = resolveJsxConfig((options.readTsconfigJsx ?? (() => readTsconfigJsx(cwd)))(), config.jsx);
84
60
  const tsconfigPath = (options.writeTsconfig ?? ((c) => writeResolvedTsconfig({
85
61
  cwd: c,
@@ -90,7 +66,6 @@ async function runBuild(config, options) {
90
66
  ...jsx?.runtime === "classic" ? { jsx: "react" } : {}
91
67
  })))(cwd);
92
68
  const exportsMap = options.readExports ? options.readExports() : pkg.exports;
93
- const runGenerateMeta = options.generateMeta ?? generateMeta;
94
69
  const publishTargets = (options.readPublishTargets ?? (() => {
95
70
  const declared = pkg.publishConfig?.targets;
96
71
  return declared !== void 0 && !Array.isArray(declared) && typeof declared === "object" ? declared : void 0;
@@ -140,7 +115,10 @@ async function runBuild(config, options) {
140
115
  await (options.runExeBuild ?? runExeBuild)({
141
116
  cwd,
142
117
  outDir: join(cwd, "dist", "dev", "pkg", "bin"),
143
- specs: exeSpecs
118
+ specs: exeSpecs,
119
+ collector,
120
+ groupId: "dev",
121
+ verbose
144
122
  });
145
123
  (options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`)))({
146
124
  target: "stdout",
@@ -197,7 +175,6 @@ async function runBuild(config, options) {
197
175
  dualExports = dualExportKeys;
198
176
  }
199
177
  const looseFiles = config.looseFiles !== void 0 ? normalizeLooseFiles(config.looseFiles) : void 0;
200
- const startMs = Date.now();
201
178
  const { groups, resolution } = target === "dev" ? {
202
179
  groups: [{
203
180
  id: "dev",
@@ -205,108 +182,115 @@ async function runBuild(config, options) {
205
182
  }],
206
183
  resolution: void 0
207
184
  } : deriveProdGroups(publishTargets, packageName);
208
- if (hasJsEntries || config.exe === void 0) await build({
209
- cwd,
210
- version,
211
- entry: config.overrides !== void 0 ? baseEntries : entries,
212
- tsconfigPath,
213
- groups,
214
- devManifest: config.devManifest,
215
- externals: config.externals,
216
- ...config.bundledPackages !== void 0 ? { bundledPackages: config.bundledPackages } : {},
217
- ...config.dtsExternals !== void 0 ? { dtsExternals: config.dtsExternals } : {},
218
- ...config.bundleNodeModules !== void 0 ? { bundleNodeModules: config.bundleNodeModules } : {},
219
- ...config.bundle !== void 0 ? { bundle: config.bundle } : {},
220
- ...config.minify !== void 0 ? { minify: config.minify } : {},
221
- ...config.transform !== void 0 ? { transform: config.transform } : {},
222
- ...jsx !== void 0 ? { jsx } : {},
223
- ...config.format !== void 0 ? { format: config.format } : {},
224
- ...config.define !== void 0 ? { define: config.define } : {},
225
- ...overridePartitions.length > 0 ? { overrides: overridePartitions } : {},
226
- ...dualExports !== void 0 ? { dualExports } : {},
227
- ...subdirExports.size > 0 ? { subdirExports } : {},
228
- ...looseFiles !== void 0 ? { looseFiles } : {},
229
- ...exeRewrite !== void 0 ? { exeRewrite } : {}
230
- });
231
- if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
232
- if (target === "prod" && config.meta !== false && (config.exe === void 0 || hasJsEntries)) {
233
- const norm = normalizeMetaOptions(config.meta ?? {});
234
- const canonicalId = (groups.find((g) => g.name === packageName) ?? groups[0])?.id ?? "npm";
235
- const dtsBasenames = {};
236
- for (const name of Object.keys(entries)) dtsBasenames[name] = name;
237
- const exportPaths = deriveExportPaths(entries, exportsMap);
238
- applySubdirMetaEntries(config.overrides, dtsBasenames, exportPaths);
239
- const resolveNext = options.resolveNextVersions ?? resolveNextVersions;
240
- const nextVersions = norm.optimistic ? await resolveNext(cwd) : void 0;
241
- const manifestTransform = nextVersions ? (m) => rewriteMetaVersions(m, nextVersions.versions, packageName) : void 0;
242
- for (const g of groups) await runGenerateMeta({
185
+ const explicitFormat = config.output?.format;
186
+ const writeOutput = options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`));
187
+ const renderAndWrite = async () => {
188
+ const rendered = await Effect.runPromise(renderReport(collector.snapshot(packageName), {
189
+ ...explicitFormat !== void 0 ? { explicitFormat } : {},
190
+ verbose,
191
+ noColor: process.env.NO_COLOR !== void 0 || !process.stdout.isTTY
192
+ }).pipe(Effect.provide(ReportPipelineLive)));
193
+ for (const output of rendered) writeOutput(output);
194
+ };
195
+ const writeIssuesBestEffort = () => {
196
+ if (target !== "dev" && target !== "prod") return;
197
+ try {
198
+ (options.writeIssues ?? writeIssuesArtifact)({
199
+ cwd,
200
+ target,
201
+ reports: collector.snapshot(packageName)
202
+ });
203
+ } catch {}
204
+ };
205
+ try {
206
+ if (hasJsEntries || config.exe === void 0) await build({
243
207
  cwd,
244
- packageName,
208
+ version,
209
+ entry: config.overrides !== void 0 ? baseEntries : entries,
245
210
  tsconfigPath,
246
- dtsDir: join(cwd, "dist", "prod", g.id, "pkg"),
247
- entries: dtsBasenames,
248
- exportPaths,
249
- outMetaDir: join(cwd, "dist", "prod", g.id, "meta"),
250
- localPaths: g.id === canonicalId ? norm.localPaths : [],
251
- tsdoc: norm.tsdoc,
252
- ...manifestTransform !== void 0 ? { manifestTransform } : {}
211
+ groups,
212
+ devManifest: config.devManifest,
213
+ externals: config.externals,
214
+ ...config.bundledPackages !== void 0 ? { bundledPackages: config.bundledPackages } : {},
215
+ ...config.dtsExternals !== void 0 ? { dtsExternals: config.dtsExternals } : {},
216
+ ...config.bundleNodeModules !== void 0 ? { bundleNodeModules: config.bundleNodeModules } : {},
217
+ ...config.bundle !== void 0 ? { bundle: config.bundle } : {},
218
+ ...config.minify !== void 0 ? { minify: config.minify } : {},
219
+ ...config.transform !== void 0 ? { transform: config.transform } : {},
220
+ ...jsx !== void 0 ? { jsx } : {},
221
+ ...config.format !== void 0 ? { format: config.format } : {},
222
+ ...config.define !== void 0 ? { define: config.define } : {},
223
+ ...overridePartitions.length > 0 ? { overrides: overridePartitions } : {},
224
+ ...dualExports !== void 0 ? { dualExports } : {},
225
+ ...subdirExports.size > 0 ? { subdirExports } : {},
226
+ ...looseFiles !== void 0 ? { looseFiles } : {},
227
+ ...exeRewrite !== void 0 ? { exeRewrite } : {},
228
+ collector,
229
+ verbose
253
230
  });
254
- }
255
- if (target === "prod") for (const g of groups) removeDeclarationMaps(join(cwd, "dist", "prod", g.id, "pkg"));
256
- if (config.exe !== void 0 && exeSpec !== void 0 && exeRewrite !== void 0 && exeFileName !== void 0) {
257
- const runExe = options.runExeBuild ?? runExeBuild;
258
- const groupOutDir = (g) => target === "dev" ? join(cwd, "dist", "dev", "pkg") : join(cwd, "dist", "prod", g.id, "pkg");
259
- for (const g of groups) {
260
- const outDir = groupOutDir(g);
261
- if (!hasJsEntries) {
262
- const manifest = await buildEmittedManifest({
263
- pkg,
264
- targetGroup: {
265
- id: g.id,
266
- name: g.name,
267
- isProd: target === "prod"
268
- },
269
- devManifest: config.devManifest,
270
- ...config.transform !== void 0 ? { transform: config.transform } : {},
271
- exeRewrite
272
- });
273
- mkdirSync(outDir, { recursive: true });
274
- writeFileSync(join(outDir, "package.json"), `${JSON.stringify(manifest, null, " ")}\n`);
275
- for (const file of ["LICENSE", "README.md"]) try {
276
- copyFileSync(join(cwd, file), join(outDir, file));
277
- } catch {}
278
- }
279
- if (!noExe) {
280
- const binDir = join(outDir, "bin");
281
- await runExe({
282
- cwd,
283
- outDir: binDir,
284
- specs: exeSpecs
285
- });
286
- if (options.runExeBuild === void 0 && !existsSync(join(binDir, exeFileName))) throw new Error(`exe: expected compiled binary at ${join(binDir, exeFileName)} but it is missing — tsdown's emitted name may have drifted from computeExeFileName`);
231
+ if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
232
+ if (target === "prod" && config.meta !== false && (config.exe === void 0 || hasJsEntries)) {
233
+ const ci = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
234
+ await runMetaPass({
235
+ cwd,
236
+ packageName,
237
+ tsconfigPath,
238
+ groups,
239
+ entries,
240
+ exportsMap,
241
+ overrides: config.overrides,
242
+ meta: config.meta ?? {},
243
+ collector,
244
+ ci,
245
+ ...options.generateMeta !== void 0 ? { generateMeta: options.generateMeta } : {},
246
+ ...options.resolveNextVersions !== void 0 ? { resolveNextVersions: options.resolveNextVersions } : {}
247
+ });
248
+ }
249
+ if (target === "prod") for (const g of groups) removeDeclarationMaps(join(cwd, "dist", "prod", g.id, "pkg"));
250
+ if (config.exe !== void 0 && exeSpec !== void 0 && exeRewrite !== void 0 && exeFileName !== void 0) {
251
+ const runExe = options.runExeBuild ?? runExeBuild;
252
+ const groupOutDir = (g) => target === "dev" ? join(cwd, "dist", "dev", "pkg") : join(cwd, "dist", "prod", g.id, "pkg");
253
+ for (const g of groups) {
254
+ const outDir = groupOutDir(g);
255
+ if (!hasJsEntries) {
256
+ const manifest = await buildEmittedManifest({
257
+ pkg,
258
+ targetGroup: {
259
+ id: g.id,
260
+ name: g.name,
261
+ isProd: target === "prod"
262
+ },
263
+ devManifest: config.devManifest,
264
+ ...config.transform !== void 0 ? { transform: config.transform } : {},
265
+ exeRewrite
266
+ });
267
+ mkdirSync(outDir, { recursive: true });
268
+ writeFileSync(join(outDir, "package.json"), `${JSON.stringify(manifest, null, " ")}\n`);
269
+ for (const file of ["LICENSE", "README.md"]) try {
270
+ copyFileSync(join(cwd, file), join(outDir, file));
271
+ } catch {}
272
+ }
273
+ if (!noExe) {
274
+ const binDir = join(outDir, "bin");
275
+ await runExe({
276
+ cwd,
277
+ outDir: binDir,
278
+ specs: exeSpecs,
279
+ collector,
280
+ groupId: g.id,
281
+ verbose
282
+ });
283
+ if (options.runExeBuild === void 0 && !existsSync(join(binDir, exeFileName))) throw new Error(`exe: expected compiled binary at ${join(binDir, exeFileName)} but it is missing — tsdown's emitted name may have drifted from computeExeFileName`);
284
+ }
287
285
  }
288
286
  }
287
+ } catch (err) {
288
+ await renderAndWrite();
289
+ writeIssuesBestEffort();
290
+ throw err;
289
291
  }
290
- const totalMs = Date.now() - startMs;
291
- const reportEntries = Object.keys(entries);
292
- const report = {
293
- package: packageName,
294
- targetGroups: groups.map((g) => ({
295
- id: g.id,
296
- entries: reportEntries,
297
- emittedFiles: [],
298
- timings: { totalMs },
299
- warnings: [],
300
- errors: []
301
- }))
302
- };
303
- const explicitFormat = config.output?.format;
304
- const rendered = await Effect.runPromise(renderReport([report], {
305
- ...explicitFormat !== void 0 ? { explicitFormat } : {},
306
- noColor: process.env.NO_COLOR !== void 0 || !process.stdout.isTTY
307
- }).pipe(Effect.provide(ReportPipelineLive)));
308
- const writeOutput = options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`));
309
- for (const output of rendered) writeOutput(output);
292
+ await renderAndWrite();
293
+ writeIssuesBestEffort();
310
294
  }
311
295
 
312
296
  //#endregion
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.58.9"
9
+ }
10
+ ]
11
+ }