@savvy-web/bundler 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/config.js +6 -1
- package/index.d.ts +24 -6
- package/package.json +5 -5
- package/run.js +30 -54
- package/tsdoc-metadata.json +11 -0
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.
|
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
|
-
/**
|
|
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,6 +27,7 @@ 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;
|
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
|
-
*
|
|
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
|
|
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,8 +173,13 @@ 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
|
-
/**
|
|
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;
|
|
@@ -178,9 +187,11 @@ interface ParsedArgs {
|
|
|
178
187
|
readonly noExe: boolean;
|
|
179
188
|
readonly verbose: boolean;
|
|
180
189
|
}
|
|
190
|
+
/** Parse the build CLI argv into the normalized target/flags shape. @public */
|
|
181
191
|
declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
|
|
182
192
|
//#endregion
|
|
183
193
|
//#region src/run.d.ts
|
|
194
|
+
/** @public */
|
|
184
195
|
interface RunOptions {
|
|
185
196
|
readonly cwd: string;
|
|
186
197
|
readonly argv: ReadonlyArray<string>;
|
|
@@ -213,8 +224,15 @@ interface RunOptions {
|
|
|
213
224
|
}) | undefined;
|
|
214
225
|
/** Injectable for tests: resolves next release versions for the optimistic meta rewrite. */
|
|
215
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;
|
|
216
234
|
}
|
|
217
|
-
/** 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 */
|
|
218
236
|
declare function runBuild(config: BuildConfig, options: RunOptions): Promise<void>;
|
|
219
237
|
//#endregion
|
|
220
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.
|
|
3
|
+
"version": "0.9.1",
|
|
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,16 +29,16 @@
|
|
|
29
29
|
"./package.json": "./package.json"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@savvy-web/tsdown-plugins": "0.
|
|
32
|
+
"@savvy-web/tsdown-plugins": "0.9.1",
|
|
33
33
|
"@tsdown/exe": "^0.22.1",
|
|
34
|
-
"effect": "^3.21.
|
|
34
|
+
"effect": "^3.21.4",
|
|
35
35
|
"tsdown": "^0.22.3"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^26.0.0",
|
|
39
39
|
"@types/react": "^19.2.0",
|
|
40
40
|
"@types/react-dom": "^19.2.0",
|
|
41
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
41
|
+
"@typescript/native-preview": "^7.0.0-dev.20260612.1",
|
|
42
42
|
"react": "^19.2.0",
|
|
43
43
|
"react-dom": "^19.2.0",
|
|
44
44
|
"typescript": "^6.0.0"
|
package/run.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseArgs } from "./config.js";
|
|
2
|
-
import { BuildCollector, ConfigValidator, ConfigValidatorLive, ReportPipelineLive, buildEmittedManifest, buildTargetGroups, computeExeFileName, createEntryName,
|
|
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
|
|
42
|
-
* input. Without this guard, a malformed `outSubdir` override (more than one entry, a non-canonical
|
|
43
|
-
* path, or an export path that is not a real build entry) would silently remap a wrong/nonexistent key
|
|
44
|
-
* meta path. Mirrors the override loop's conditions and messages verbatim so every target path
|
|
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,24 +47,7 @@ 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
52
|
const { target, noExe, verbose } = parseArgs(options.argv);
|
|
78
53
|
const build = options.buildTargetGroups ?? buildTargetGroups;
|
|
@@ -91,7 +66,6 @@ async function runBuild(config, options) {
|
|
|
91
66
|
...jsx?.runtime === "classic" ? { jsx: "react" } : {}
|
|
92
67
|
})))(cwd);
|
|
93
68
|
const exportsMap = options.readExports ? options.readExports() : pkg.exports;
|
|
94
|
-
const runGenerateMeta = options.generateMeta ?? generateMeta;
|
|
95
69
|
const publishTargets = (options.readPublishTargets ?? (() => {
|
|
96
70
|
const declared = pkg.publishConfig?.targets;
|
|
97
71
|
return declared !== void 0 && !Array.isArray(declared) && typeof declared === "object" ? declared : void 0;
|
|
@@ -218,6 +192,16 @@ async function runBuild(config, options) {
|
|
|
218
192
|
}).pipe(Effect.provide(ReportPipelineLive)));
|
|
219
193
|
for (const output of rendered) writeOutput(output);
|
|
220
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
|
+
};
|
|
221
205
|
try {
|
|
222
206
|
if (hasJsEntries || config.exe === void 0) await build({
|
|
223
207
|
cwd,
|
|
@@ -246,30 +230,20 @@ async function runBuild(config, options) {
|
|
|
246
230
|
});
|
|
247
231
|
if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
|
|
248
232
|
if (target === "prod" && config.meta !== false && (config.exe === void 0 || hasJsEntries)) {
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
const dtsBasenames = {};
|
|
252
|
-
for (const name of Object.keys(entries)) if (!name.startsWith("bin/")) dtsBasenames[name] = name;
|
|
253
|
-
const exportPaths = deriveExportPaths(entries, exportsMap);
|
|
254
|
-
applySubdirMetaEntries(config.overrides, dtsBasenames, exportPaths);
|
|
255
|
-
const resolveNext = options.resolveNextVersions ?? resolveNextVersions;
|
|
256
|
-
const nextVersions = norm.optimistic ? await resolveNext(cwd) : void 0;
|
|
257
|
-
const manifestTransform = nextVersions ? (m) => rewriteMetaVersions(m, nextVersions.versions, packageName) : void 0;
|
|
258
|
-
for (const g of groups) await runGenerateMeta({
|
|
233
|
+
const ci = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
|
234
|
+
await runMetaPass({
|
|
259
235
|
cwd,
|
|
260
236
|
packageName,
|
|
261
237
|
tsconfigPath,
|
|
262
|
-
|
|
263
|
-
entries
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
else collector.recordWarning(g.id, e);
|
|
272
|
-
}
|
|
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 } : {}
|
|
273
247
|
});
|
|
274
248
|
}
|
|
275
249
|
if (target === "prod") for (const g of groups) removeDeclarationMaps(join(cwd, "dist", "prod", g.id, "pkg"));
|
|
@@ -312,9 +286,11 @@ async function runBuild(config, options) {
|
|
|
312
286
|
}
|
|
313
287
|
} catch (err) {
|
|
314
288
|
await renderAndWrite();
|
|
289
|
+
writeIssuesBestEffort();
|
|
315
290
|
throw err;
|
|
316
291
|
}
|
|
317
292
|
await renderAndWrite();
|
|
293
|
+
writeIssuesBestEffort();
|
|
318
294
|
}
|
|
319
295
|
|
|
320
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
|
+
}
|