@savvy-web/bundler 0.7.0 → 0.8.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 +1 -1
- package/config.js +4 -1
- package/index.d.ts +1 -0
- package/package.json +24 -3
- package/run.js +108 -100
package/README.md
CHANGED
|
@@ -297,7 +297,7 @@ const config = defineBuild({
|
|
|
297
297
|
## API
|
|
298
298
|
|
|
299
299
|
- `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.
|
|
300
|
+
- `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
301
|
- `parseArgs(argv)` — the argument parser behind `runBuild`, exported for embedding.
|
|
302
302
|
|
|
303
303
|
## Turbo tasks
|
package/config.js
CHANGED
|
@@ -27,16 +27,19 @@ function parseArgs(argv) {
|
|
|
27
27
|
let target = "dev";
|
|
28
28
|
let watch = false;
|
|
29
29
|
let noExe = false;
|
|
30
|
+
let verbose = false;
|
|
30
31
|
for (let i = 0; i < argv.length; i++) if (argv[i] === "--target") {
|
|
31
32
|
const v = argv[i + 1];
|
|
32
33
|
if (v === "dev" || v === "prod" || v === "meta" || v === "exe") target = v;
|
|
33
34
|
i++;
|
|
34
35
|
} else if (argv[i] === "--watch") watch = true;
|
|
35
36
|
else if (argv[i] === "--no-exe") noExe = true;
|
|
37
|
+
else if (argv[i] === "--verbose") verbose = true;
|
|
36
38
|
return {
|
|
37
39
|
target,
|
|
38
40
|
watch,
|
|
39
|
-
noExe
|
|
41
|
+
noExe,
|
|
42
|
+
verbose
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
|
package/index.d.ts
CHANGED
|
@@ -176,6 +176,7 @@ interface ParsedArgs {
|
|
|
176
176
|
readonly watch: boolean;
|
|
177
177
|
/** Skip the SEA compile step of a dev/prod build (the manifest is still programmed). Used by `prepare`. */
|
|
178
178
|
readonly noExe: boolean;
|
|
179
|
+
readonly verbose: boolean;
|
|
179
180
|
}
|
|
180
181
|
declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
|
|
181
182
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/bundler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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.
|
|
32
|
+
"@savvy-web/tsdown-plugins": "0.8.0",
|
|
33
33
|
"@tsdown/exe": "^0.22.1",
|
|
34
|
+
"effect": "^3.21.3",
|
|
34
35
|
"tsdown": "^0.22.3"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
|
-
"
|
|
38
|
+
"@types/node": "^25.9.0",
|
|
39
|
+
"@types/react": "^19.2.0",
|
|
40
|
+
"@types/react-dom": "^19.2.0",
|
|
41
|
+
"@typescript/native-preview": "^7.0.0-dev.20260513.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, generateMeta, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveNextVersions, resolveTargets, rewriteMetaVersions, runExeBuild, 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";
|
|
@@ -74,12 +74,13 @@ function applySubdirMetaEntries(overrides, dtsBasenames, exportPaths) {
|
|
|
74
74
|
}
|
|
75
75
|
/** Run a build from a normalized config. Pure orchestration; all IO injectable. */
|
|
76
76
|
async function runBuild(config, options) {
|
|
77
|
-
const { target, noExe } = parseArgs(options.argv);
|
|
77
|
+
const { target, noExe, verbose } = parseArgs(options.argv);
|
|
78
78
|
const build = options.buildTargetGroups ?? buildTargetGroups;
|
|
79
79
|
const cwd = options.cwd;
|
|
80
80
|
const pkg = readPackageJson(cwd);
|
|
81
81
|
const version = options.readVersion ? options.readVersion() : pkg.version ?? "0.0.0";
|
|
82
82
|
const packageName = options.readPackageName ? options.readPackageName() : pkg.name ?? "unknown";
|
|
83
|
+
const collector = new BuildCollector();
|
|
83
84
|
const jsx = resolveJsxConfig((options.readTsconfigJsx ?? (() => readTsconfigJsx(cwd)))(), config.jsx);
|
|
84
85
|
const tsconfigPath = (options.writeTsconfig ?? ((c) => writeResolvedTsconfig({
|
|
85
86
|
cwd: c,
|
|
@@ -140,7 +141,10 @@ async function runBuild(config, options) {
|
|
|
140
141
|
await (options.runExeBuild ?? runExeBuild)({
|
|
141
142
|
cwd,
|
|
142
143
|
outDir: join(cwd, "dist", "dev", "pkg", "bin"),
|
|
143
|
-
specs: exeSpecs
|
|
144
|
+
specs: exeSpecs,
|
|
145
|
+
collector,
|
|
146
|
+
groupId: "dev",
|
|
147
|
+
verbose
|
|
144
148
|
});
|
|
145
149
|
(options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`)))({
|
|
146
150
|
target: "stdout",
|
|
@@ -197,7 +201,6 @@ async function runBuild(config, options) {
|
|
|
197
201
|
dualExports = dualExportKeys;
|
|
198
202
|
}
|
|
199
203
|
const looseFiles = config.looseFiles !== void 0 ? normalizeLooseFiles(config.looseFiles) : void 0;
|
|
200
|
-
const startMs = Date.now();
|
|
201
204
|
const { groups, resolution } = target === "dev" ? {
|
|
202
205
|
groups: [{
|
|
203
206
|
id: "dev",
|
|
@@ -205,108 +208,113 @@ async function runBuild(config, options) {
|
|
|
205
208
|
}],
|
|
206
209
|
resolution: void 0
|
|
207
210
|
} : deriveProdGroups(publishTargets, packageName);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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({
|
|
211
|
+
const explicitFormat = config.output?.format;
|
|
212
|
+
const writeOutput = options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`));
|
|
213
|
+
const renderAndWrite = async () => {
|
|
214
|
+
const rendered = await Effect.runPromise(renderReport(collector.snapshot(packageName), {
|
|
215
|
+
...explicitFormat !== void 0 ? { explicitFormat } : {},
|
|
216
|
+
verbose,
|
|
217
|
+
noColor: process.env.NO_COLOR !== void 0 || !process.stdout.isTTY
|
|
218
|
+
}).pipe(Effect.provide(ReportPipelineLive)));
|
|
219
|
+
for (const output of rendered) writeOutput(output);
|
|
220
|
+
};
|
|
221
|
+
try {
|
|
222
|
+
if (hasJsEntries || config.exe === void 0) await build({
|
|
243
223
|
cwd,
|
|
244
|
-
|
|
224
|
+
version,
|
|
225
|
+
entry: config.overrides !== void 0 ? baseEntries : entries,
|
|
245
226
|
tsconfigPath,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
...
|
|
227
|
+
groups,
|
|
228
|
+
devManifest: config.devManifest,
|
|
229
|
+
externals: config.externals,
|
|
230
|
+
...config.bundledPackages !== void 0 ? { bundledPackages: config.bundledPackages } : {},
|
|
231
|
+
...config.dtsExternals !== void 0 ? { dtsExternals: config.dtsExternals } : {},
|
|
232
|
+
...config.bundleNodeModules !== void 0 ? { bundleNodeModules: config.bundleNodeModules } : {},
|
|
233
|
+
...config.bundle !== void 0 ? { bundle: config.bundle } : {},
|
|
234
|
+
...config.minify !== void 0 ? { minify: config.minify } : {},
|
|
235
|
+
...config.transform !== void 0 ? { transform: config.transform } : {},
|
|
236
|
+
...jsx !== void 0 ? { jsx } : {},
|
|
237
|
+
...config.format !== void 0 ? { format: config.format } : {},
|
|
238
|
+
...config.define !== void 0 ? { define: config.define } : {},
|
|
239
|
+
...overridePartitions.length > 0 ? { overrides: overridePartitions } : {},
|
|
240
|
+
...dualExports !== void 0 ? { dualExports } : {},
|
|
241
|
+
...subdirExports.size > 0 ? { subdirExports } : {},
|
|
242
|
+
...looseFiles !== void 0 ? { looseFiles } : {},
|
|
243
|
+
...exeRewrite !== void 0 ? { exeRewrite } : {},
|
|
244
|
+
collector,
|
|
245
|
+
verbose
|
|
253
246
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
247
|
+
if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
|
|
248
|
+
if (target === "prod" && config.meta !== false && (config.exe === void 0 || hasJsEntries)) {
|
|
249
|
+
const norm = normalizeMetaOptions(config.meta ?? {});
|
|
250
|
+
const canonicalId = (groups.find((g) => g.name === packageName) ?? groups[0])?.id ?? "npm";
|
|
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({
|
|
259
|
+
cwd,
|
|
260
|
+
packageName,
|
|
261
|
+
tsconfigPath,
|
|
262
|
+
dtsDir: join(cwd, "dist", "prod", g.id, "pkg"),
|
|
263
|
+
entries: dtsBasenames,
|
|
264
|
+
exportPaths,
|
|
265
|
+
outMetaDir: join(cwd, "dist", "prod", g.id, "meta"),
|
|
266
|
+
localPaths: g.id === canonicalId ? norm.localPaths : [],
|
|
267
|
+
tsdoc: norm.tsdoc,
|
|
268
|
+
...manifestTransform !== void 0 ? { manifestTransform } : {},
|
|
269
|
+
onMessage: (e) => {
|
|
270
|
+
if (e.level === "error") collector.recordError(g.id, e);
|
|
271
|
+
else collector.recordWarning(g.id, e);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (target === "prod") for (const g of groups) removeDeclarationMaps(join(cwd, "dist", "prod", g.id, "pkg"));
|
|
276
|
+
if (config.exe !== void 0 && exeSpec !== void 0 && exeRewrite !== void 0 && exeFileName !== void 0) {
|
|
277
|
+
const runExe = options.runExeBuild ?? runExeBuild;
|
|
278
|
+
const groupOutDir = (g) => target === "dev" ? join(cwd, "dist", "dev", "pkg") : join(cwd, "dist", "prod", g.id, "pkg");
|
|
279
|
+
for (const g of groups) {
|
|
280
|
+
const outDir = groupOutDir(g);
|
|
281
|
+
if (!hasJsEntries) {
|
|
282
|
+
const manifest = await buildEmittedManifest({
|
|
283
|
+
pkg,
|
|
284
|
+
targetGroup: {
|
|
285
|
+
id: g.id,
|
|
286
|
+
name: g.name,
|
|
287
|
+
isProd: target === "prod"
|
|
288
|
+
},
|
|
289
|
+
devManifest: config.devManifest,
|
|
290
|
+
...config.transform !== void 0 ? { transform: config.transform } : {},
|
|
291
|
+
exeRewrite
|
|
292
|
+
});
|
|
293
|
+
mkdirSync(outDir, { recursive: true });
|
|
294
|
+
writeFileSync(join(outDir, "package.json"), `${JSON.stringify(manifest, null, " ")}\n`);
|
|
295
|
+
for (const file of ["LICENSE", "README.md"]) try {
|
|
296
|
+
copyFileSync(join(cwd, file), join(outDir, file));
|
|
297
|
+
} catch {}
|
|
298
|
+
}
|
|
299
|
+
if (!noExe) {
|
|
300
|
+
const binDir = join(outDir, "bin");
|
|
301
|
+
await runExe({
|
|
302
|
+
cwd,
|
|
303
|
+
outDir: binDir,
|
|
304
|
+
specs: exeSpecs,
|
|
305
|
+
collector,
|
|
306
|
+
groupId: g.id,
|
|
307
|
+
verbose
|
|
308
|
+
});
|
|
309
|
+
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`);
|
|
310
|
+
}
|
|
287
311
|
}
|
|
288
312
|
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
await renderAndWrite();
|
|
315
|
+
throw err;
|
|
289
316
|
}
|
|
290
|
-
|
|
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);
|
|
317
|
+
await renderAndWrite();
|
|
310
318
|
}
|
|
311
319
|
|
|
312
320
|
//#endregion
|