@savvy-web/tsdown-plugins 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 +2 -1
- package/build/build-target-groups.js +42 -9
- package/build/cjs-default-interop.js +1 -0
- package/build/loose-files.js +1 -0
- package/build/node-builtin-default-interop.js +15 -9
- package/build/strip-maps.js +1 -0
- package/build/sync-public.js +1 -0
- package/build/target-groups.js +7 -2
- package/catalog/resolve-catalogs.js +1 -0
- package/changesets/next-versions.js +1 -0
- package/config-validation/ConfigValidator.js +5 -1
- package/config-validation/ConfigValidatorLive.js +6 -2
- package/dts/resolved-tsconfig.js +10 -2
- package/entry/extract.js +1 -0
- package/entry/package-json-entries.js +5 -1
- package/errors.js +10 -2
- package/exe/build.js +20 -2
- package/exe/config.js +6 -1
- package/exe/filename.js +1 -0
- package/index.d.ts +679 -593
- package/index.js +8 -4
- package/jsx/config.js +2 -0
- package/manifest/emit-manifest.js +10 -2
- package/manifest/transform.js +18 -4
- package/meta/api-extractor.js +41 -1
- package/meta/config.js +5 -1
- package/meta/generate.js +6 -2
- package/meta/optimistic.js +1 -0
- package/meta/run-pass.js +75 -0
- package/meta/tsconfig-resolver.js +12 -12
- package/package.json +1 -1
- package/report/collector.js +141 -0
- package/report/formatters/ci-annotations.js +9 -2
- package/report/formatters/diagnostics.js +23 -0
- package/report/formatters/json.js +1 -0
- package/report/formatters/markdown.js +19 -9
- package/report/formatters/silent.js +1 -0
- package/report/formatters/terminal.js +32 -3
- package/report/issues-artifact.js +88 -0
- package/report/layers/EnvironmentDetectorLive.js +1 -0
- package/report/layers/ExecutorResolverLive.js +1 -0
- package/report/layers/FormatSelectorLive.js +1 -0
- package/report/layers/OutputRendererLive.js +2 -1
- package/report/metrics-plugin.js +63 -0
- package/report/pipeline.js +6 -1
- package/report/schema.js +52 -10
- package/report/services/EnvironmentDetector.js +1 -0
- package/report/services/ExecutorResolver.js +1 -0
- package/report/services/FormatSelector.js +1 -0
- package/report/services/OutputRenderer.js +1 -0
- package/report/timer.js +6 -1
- package/report/tsdown-logger.js +42 -0
- package/targets/binding.js +5 -1
- package/targets/config.js +5 -1
- package/targets/resolve-targets.js +5 -1
- package/tsdoc-metadata.json +11 -0
- package/report/schema-export.js +0 -18
package/README.md
CHANGED
|
@@ -50,8 +50,9 @@ export default defineConfig({
|
|
|
50
50
|
- **Per-target build loop** — `deriveTargetGroupOptions` and `buildTargetGroups` map a target to its `tsdown` options and run the build once per target, exposed as a helper so the escape hatch gets multi-target builds too. A `format` of `["esm", "cjs"]` (the `BuildFormat` type) derives a dual-format build — a require-able CJS output with default-export interop and `.d.cts` declarations alongside the ESM one. The `bundleNodeModules`, `bundledPackages` and `dtsExternals` options thread the dependency-bundling posture into both the JS and declaration passes. A per-entry override partition can also set `platform` (the JS-pass target, `"browser"` for a client bundle), `css` (forwarded to tsdown's `css` option for `@tsdown/css`) and `outSubdir` (build the partition into an isolated `<group>/pkg/<subdir>/` sub-package). The `define` option forwards compile-time global replacements to both passes, merged with an auto-injected `process.env.__PACKAGE_VERSION__` constant.
|
|
51
51
|
- **Loose files** — `normalizeLooseFiles` resolves a `LooseFiles` map of literal output filenames to `NormalizedLooseFile` descriptors, inferring the module format from each `.mjs`/`.cjs` key and raising `ConfigValidationError` on a path separator, an unsupported extension or an ambiguous `.js`. `buildTargetGroups` takes the normalized form as its `looseFiles` option and emits one extra single-entry, bundled, declaration-free and manifest-free pass per file per target group, inheriting the group's bundling posture so each file is self-contained.
|
|
52
52
|
- **Bundled declarations** — each target runs two `tsdown` passes: a JavaScript pass that preserves per-module output, then a declaration-only pass that rolls every re-exported type into a single `.d.ts` per public entry (`deriveDtsPassOptions`). Per-module JavaScript stays intact while consumers keep reaching re-exported types through your published subpaths.
|
|
53
|
-
- **API Extractor meta** — `
|
|
53
|
+
- **API Extractor meta** — `runMetaPass` is the single meta-generation orchestrator the bundler front door and both self-hosting escape hatches share: it derives the export paths, applies the optimistic next-version forward-look and drives API Extractor over a package's emitted `.d.ts` to write an api-model bundle (`.api.json`, `tsdoc-metadata.json`, resolved `tsconfig.json`). `generateMeta` is the lower-level pass it wraps, and `normalizeMetaOptions` fills the `MetaOptions` defaults that drive it.
|
|
54
54
|
- **Output reporter** — `renderReport` plus the `BuildReport` schema and a set of formatters (terminal, JSON, markdown, CI annotations, silent) render a build report for humans, agents or CI.
|
|
55
|
+
- **Issues artifact** — `writeIssuesArtifact` (with the pure `flattenIssues` and `serializeIssues` behind it) writes a deduplicated `dist/<target>/issues.json` on every build, collecting the build's warnings, errors and suppressed diagnostics in a stable JSON shape (`BuildIssues`/`PlainDiagnostic`) so an agent or CI script reads the diagnostics straight from disk instead of parsing terminal output.
|
|
55
56
|
|
|
56
57
|
## Effect
|
|
57
58
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { emitManifest } from "../manifest/emit-manifest.js";
|
|
2
|
+
import { buildMetricsPlugin } from "../report/metrics-plugin.js";
|
|
3
|
+
import { createTimer } from "../report/timer.js";
|
|
4
|
+
import { createTsdownLogger } from "../report/tsdown-logger.js";
|
|
2
5
|
import { cjsDefaultInterop } from "./cjs-default-interop.js";
|
|
3
6
|
import { nodeBuiltinDefaultInterop } from "./node-builtin-default-interop.js";
|
|
4
7
|
import { syncPublicDir } from "./sync-public.js";
|
|
@@ -20,11 +23,28 @@ import { dirname, join } from "node:path";
|
|
|
20
23
|
* build (JS and the dts plugin share it), so a single pass cannot give per-module JS + bundled
|
|
21
24
|
* dts. Per-module dts breaks type portability (TS2883); bundling the JS re-bundles workspace
|
|
22
25
|
* consumers. The split keeps per-module JS AND rolled-up, self-contained declarations.
|
|
26
|
+
* @public
|
|
23
27
|
*/
|
|
24
28
|
async function buildTargetGroups(options) {
|
|
25
29
|
const build = options.build ?? (await import("tsdown")).build;
|
|
26
30
|
const publicDir = join(options.cwd, "public");
|
|
31
|
+
const collector = options.collector;
|
|
32
|
+
const verbose = options.verbose ?? false;
|
|
33
|
+
const instrument = (groupId) => collector === void 0 ? {} : {
|
|
34
|
+
logLevel: "silent",
|
|
35
|
+
customLogger: createTsdownLogger(collector, groupId)
|
|
36
|
+
};
|
|
37
|
+
const metricsPlugins = (groupId, pass) => collector === void 0 ? [] : [buildMetricsPlugin(collector, groupId, pass, verbose)];
|
|
38
|
+
const timed = async (groupId, pass, run) => {
|
|
39
|
+
const timer = createTimer();
|
|
40
|
+
try {
|
|
41
|
+
await run();
|
|
42
|
+
} finally {
|
|
43
|
+
if (collector !== void 0) collector.recordPassTiming(groupId, pass, timer.elapsed());
|
|
44
|
+
}
|
|
45
|
+
};
|
|
27
46
|
for (const group of options.groups) {
|
|
47
|
+
if (collector !== void 0) collector.registerGroup(group.id, Object.keys(options.entry));
|
|
28
48
|
const partitions = [{
|
|
29
49
|
entry: options.entry,
|
|
30
50
|
...options.format !== void 0 ? { format: options.format } : {},
|
|
@@ -81,7 +101,7 @@ async function buildTargetGroups(options) {
|
|
|
81
101
|
...options.subdirExports !== void 0 ? { subdirExports: options.subdirExports } : {},
|
|
82
102
|
...options.exeRewrite !== void 0 ? { exeRewrite: options.exeRewrite } : {}
|
|
83
103
|
}) : void 0;
|
|
84
|
-
await build({
|
|
104
|
+
await timed(group.id, "js", () => build({
|
|
85
105
|
config: false,
|
|
86
106
|
cwd: options.cwd,
|
|
87
107
|
entry: jsEntry,
|
|
@@ -95,6 +115,7 @@ async function buildTargetGroups(options) {
|
|
|
95
115
|
fixedExtension: js.fixedExtension,
|
|
96
116
|
dts: js.dts,
|
|
97
117
|
define: js.define,
|
|
118
|
+
...instrument(group.id),
|
|
98
119
|
...part.css !== void 0 ? { css: part.css } : {},
|
|
99
120
|
...partExternals?.length || partBundleNodeModules || partBundle?.length ? { deps: {
|
|
100
121
|
...partExternals?.length ? { neverBundle: partExternals } : {},
|
|
@@ -106,12 +127,14 @@ async function buildTargetGroups(options) {
|
|
|
106
127
|
plugins: [
|
|
107
128
|
...manifestPlugin ? [manifestPlugin] : [],
|
|
108
129
|
...js.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
|
|
109
|
-
...options.extraPlugins ?? []
|
|
130
|
+
...options.extraPlugins ?? [],
|
|
131
|
+
...metricsPlugins(group.id, "js")
|
|
110
132
|
]
|
|
111
|
-
});
|
|
133
|
+
}));
|
|
112
134
|
if (isBase) syncPublicDir(publicDir, join(js.outDir, "public"));
|
|
113
135
|
const dtsNeverBundle = [...partExternals ?? [], ...partDtsExternals ?? []];
|
|
114
|
-
|
|
136
|
+
if (Object.keys(dts.entry).length === 0) continue;
|
|
137
|
+
await timed(group.id, "dts", () => build({
|
|
115
138
|
config: false,
|
|
116
139
|
cwd: options.cwd,
|
|
117
140
|
entry: dts.entry,
|
|
@@ -124,6 +147,7 @@ async function buildTargetGroups(options) {
|
|
|
124
147
|
fixedExtension: dts.fixedExtension,
|
|
125
148
|
dts: dts.dts,
|
|
126
149
|
define: dts.define,
|
|
150
|
+
...instrument(group.id),
|
|
127
151
|
...dtsNeverBundle.length > 0 || partBundleNodeModules || dts.bundledPackages ? { deps: {
|
|
128
152
|
...dtsNeverBundle.length > 0 ? { neverBundle: dtsNeverBundle } : {},
|
|
129
153
|
...partBundleNodeModules ? {
|
|
@@ -135,8 +159,12 @@ async function buildTargetGroups(options) {
|
|
|
135
159
|
} : {}
|
|
136
160
|
} } : {},
|
|
137
161
|
...dts.jsx !== void 0 ? { inputOptions: { jsx: dts.jsx } } : {},
|
|
138
|
-
plugins: [
|
|
139
|
-
|
|
162
|
+
plugins: [
|
|
163
|
+
...dts.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
|
|
164
|
+
...options.extraPlugins ?? [],
|
|
165
|
+
...metricsPlugins(group.id, "dts")
|
|
166
|
+
]
|
|
167
|
+
}));
|
|
140
168
|
}
|
|
141
169
|
const looseOutDir = outDirFor(options.cwd, group.id);
|
|
142
170
|
const isProdGroup = group.id !== "dev";
|
|
@@ -147,7 +175,7 @@ async function buildTargetGroups(options) {
|
|
|
147
175
|
};
|
|
148
176
|
for (const lf of options.looseFiles ?? []) {
|
|
149
177
|
const hasCjs = lf.format === "cjs";
|
|
150
|
-
await build({
|
|
178
|
+
await timed(group.id, "loose", () => build({
|
|
151
179
|
config: false,
|
|
152
180
|
cwd: options.cwd,
|
|
153
181
|
entry: { [lf.entryName]: lf.source },
|
|
@@ -164,10 +192,15 @@ async function buildTargetGroups(options) {
|
|
|
164
192
|
"process.env.__PACKAGE_VERSION__": JSON.stringify(options.version),
|
|
165
193
|
...options.define
|
|
166
194
|
},
|
|
195
|
+
...instrument(group.id),
|
|
167
196
|
...Object.keys(looseDeps).length > 0 ? { deps: looseDeps } : {},
|
|
168
197
|
...hasCjs ? { cjsDefault: true } : {},
|
|
169
|
-
plugins: [
|
|
170
|
-
|
|
198
|
+
plugins: [
|
|
199
|
+
...hasCjs ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
|
|
200
|
+
...options.extraPlugins ?? [],
|
|
201
|
+
...metricsPlugins(group.id, "loose")
|
|
202
|
+
]
|
|
203
|
+
}));
|
|
171
204
|
}
|
|
172
205
|
}
|
|
173
206
|
}
|
package/build/loose-files.js
CHANGED
|
@@ -13,6 +13,7 @@ const EXT_FORMAT = {
|
|
|
13
13
|
* a missing `source` is surfaced later by tsdown's entry resolution. Throws
|
|
14
14
|
* {@link ConfigValidationError} on any structural problem so the bundler's ConfigValidator
|
|
15
15
|
* surfaces it as a typed, fast-fail config error.
|
|
16
|
+
* @public
|
|
16
17
|
*/
|
|
17
18
|
function normalizeLooseFiles(files) {
|
|
18
19
|
const out = [];
|
|
@@ -18,13 +18,15 @@ function isNodeBuiltin(spec) {
|
|
|
18
18
|
* Why this exists — a rolldown 1.1.0 codegen defect (verified against the latest
|
|
19
19
|
* published rolldown 1.1.0 / tsdown 0.22.2, with no newer release to upgrade to):
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* // SOURCE (e.g. vfile's lib/minproc.js)
|
|
23
|
+
* export {default as minproc} from 'node:process'
|
|
24
|
+
* // ...consumed as minproc.cwd()
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* // rolldown CJS OUTPUT (BROKEN)
|
|
27
|
+
* let node_process = require("node:process");
|
|
28
|
+
* node_process.default.cwd() // <- require("node:process").default is undefined
|
|
29
|
+
* ```
|
|
28
30
|
*
|
|
29
31
|
* For a default import of an EXTERNAL Node builtin, rolldown emits a bare
|
|
30
32
|
* `require("node:x")` WITHOUT its `__toESM` interop wrapper, yet still accesses
|
|
@@ -41,14 +43,18 @@ function isNodeBuiltin(spec) {
|
|
|
41
43
|
* layer, which is why the correction happens here on the source.
|
|
42
44
|
*
|
|
43
45
|
* Rewrites (the two static forms that occur in practice, anchored to statement start):
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* import NAME from "node:x" -> import * as NAME from "node:x"
|
|
49
|
+
* export { default as NAME } from "node:x" -> export * as NAME from "node:x"
|
|
50
|
+
* import NAME, { a, b } from "node:x" -> import * as NAME from "node:x"; import { a, b } from "node:x"
|
|
51
|
+
* ```
|
|
47
52
|
*
|
|
48
53
|
* The namespace binding NAME carries the builtin's named exports (`NAME.cwd`,
|
|
49
54
|
* `NAME.join`, ...), which is exactly how a default import of a builtin is consumed
|
|
50
55
|
* in practice. ESM output is unaffected at runtime (a namespace import of a builtin
|
|
51
56
|
* resolves to the same members), so the plugin is safe to attach to dual builds.
|
|
57
|
+
* @public
|
|
52
58
|
*/
|
|
53
59
|
function nodeBuiltinDefaultInterop() {
|
|
54
60
|
const DEFAULT_REEXPORT = /(^|\n)([ \t]*)export\s*\{\s*default\s+as\s+([A-Za-z_$][\w$]*)\s*\}\s*from\s*(["'])([^"']+)\4/g;
|
package/build/strip-maps.js
CHANGED
|
@@ -15,6 +15,7 @@ import { readdirSync, rmSync } from "node:fs";
|
|
|
15
15
|
*
|
|
16
16
|
* Recurses, but skips `node_modules` so it does not traverse a self-contained bundle's
|
|
17
17
|
* vendored tree — only the package's own emitted declarations carry maps worth stripping.
|
|
18
|
+
* @public
|
|
18
19
|
*/
|
|
19
20
|
function removeDeclarationMaps(pkgDir) {
|
|
20
21
|
const removed = [];
|
package/build/sync-public.js
CHANGED
|
@@ -42,6 +42,7 @@ function pruneEmptyDirs(dir) {
|
|
|
42
42
|
*
|
|
43
43
|
* The byte-diff keeps unchanged files (and their timestamps) untouched, so a large copied asset
|
|
44
44
|
* tree — e.g. the mcp markdown corpus — is not rewritten on every build.
|
|
45
|
+
* @public
|
|
45
46
|
*/
|
|
46
47
|
function syncPublicDir(sourceDir, targetDir) {
|
|
47
48
|
if (!existsSync(sourceDir)) return;
|
package/build/target-groups.js
CHANGED
|
@@ -3,7 +3,11 @@ import { join } from "node:path";
|
|
|
3
3
|
//#region src/build/target-groups.ts
|
|
4
4
|
/** The output dir for a group: dev -> dist/dev/pkg, prod -> dist/prod/<group>/pkg. */
|
|
5
5
|
const outDirFor = (cwd, group) => group === "dev" ? join(cwd, "dist/dev/pkg") : join(cwd, "dist/prod", group, "pkg");
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Derive the JS-pass tsdown options for one TargetGroup (per-module JS, no dts).
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
7
11
|
function deriveTargetGroupOptions(options) {
|
|
8
12
|
const isProd = options.group !== "dev";
|
|
9
13
|
const format = options.format ?? ["esm"];
|
|
@@ -32,6 +36,7 @@ function deriveTargetGroupOptions(options) {
|
|
|
32
36
|
function deriveDtsPassOptions(options) {
|
|
33
37
|
const isProd = options.group !== "dev";
|
|
34
38
|
const format = options.format ?? ["esm"];
|
|
39
|
+
const entry = Object.fromEntries(Object.entries(options.entry).filter(([name]) => !name.startsWith("bin/")));
|
|
35
40
|
return {
|
|
36
41
|
outDir: outDirFor(options.cwd, options.group),
|
|
37
42
|
sourcemap: false,
|
|
@@ -40,7 +45,7 @@ function deriveDtsPassOptions(options) {
|
|
|
40
45
|
clean: false,
|
|
41
46
|
platform: "node",
|
|
42
47
|
fixedExtension: false,
|
|
43
|
-
entry
|
|
48
|
+
entry,
|
|
44
49
|
dts: {
|
|
45
50
|
tsconfig: options.tsconfigPath,
|
|
46
51
|
emitDtsOnly: true
|
|
@@ -12,6 +12,7 @@ import { Effect } from "effect";
|
|
|
12
12
|
*
|
|
13
13
|
* Rejects with `CatalogResolutionError` on an unresolvable reference, or
|
|
14
14
|
* `CatalogAssemblyError` if the workspace catalog set cannot be assembled.
|
|
15
|
+
* @public
|
|
15
16
|
*/
|
|
16
17
|
function resolveManifest(pkg) {
|
|
17
18
|
const program = Effect.gen(function* () {
|
|
@@ -9,6 +9,7 @@ import { getPackages } from "@manypkg/get-packages";
|
|
|
9
9
|
* each package's CURRENT version, then overlays `newVersion` for changeset-affected packages
|
|
10
10
|
* via `@changesets/get-release-plan`. Never rejects: any failure (not a workspace, missing
|
|
11
11
|
* `.changeset/config.json`, parse error) degrades to current versions (or an empty map).
|
|
12
|
+
* @public
|
|
12
13
|
*/
|
|
13
14
|
async function resolveNextVersions(cwd) {
|
|
14
15
|
try {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Context } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/config-validation/ConfigValidator.ts
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Fast-fail config validator; runs first in the bundler over the resolved config.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
5
9
|
var ConfigValidator = class extends Context.Tag("@savvy-web/tsdown-plugins/ConfigValidator")() {};
|
|
6
10
|
|
|
7
11
|
//#endregion
|
|
@@ -7,7 +7,7 @@ import { Effect, Layer } from "effect";
|
|
|
7
7
|
import { existsSync, statSync } from "node:fs";
|
|
8
8
|
|
|
9
9
|
//#region src/config-validation/ConfigValidatorLive.ts
|
|
10
|
-
const VALID_SYNTAX_KINDS = new Set([
|
|
10
|
+
const VALID_SYNTAX_KINDS = /* @__PURE__ */ new Set([
|
|
11
11
|
"block",
|
|
12
12
|
"inline",
|
|
13
13
|
"modifier"
|
|
@@ -50,7 +50,11 @@ function check(input) {
|
|
|
50
50
|
}
|
|
51
51
|
if (input.looseFiles !== void 0) normalizeLooseFiles(input.looseFiles);
|
|
52
52
|
}
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Live ConfigValidator: wraps the synchronous rule set, surfacing ConfigValidationError as a typed Effect failure.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
54
58
|
const ConfigValidatorLive = Layer.succeed(ConfigValidator, { validate: (input) => Effect.try({
|
|
55
59
|
try: () => check(input),
|
|
56
60
|
catch: (e) => e instanceof ConfigValidationError ? e : new ConfigValidationError({
|
package/dts/resolved-tsconfig.js
CHANGED
|
@@ -3,7 +3,11 @@ import { writeFileSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
|
|
5
5
|
//#region src/dts/resolved-tsconfig.ts
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Build the portable absolute-path tsconfig object (ported from rslib writeBundleTempConfig).
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
7
11
|
function buildResolvedTsconfig(options) {
|
|
8
12
|
const cwd = options.cwd;
|
|
9
13
|
return {
|
|
@@ -32,7 +36,11 @@ function buildResolvedTsconfig(options) {
|
|
|
32
36
|
exclude: [join(cwd, "node_modules"), join(cwd, "dist/**/*")]
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Write the resolved tsconfig to a temp file and return its absolute path.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
36
44
|
function writeResolvedTsconfig(options) {
|
|
37
45
|
const cfg = buildResolvedTsconfig(options);
|
|
38
46
|
const path = join(tmpdir(), `tsconfig-bundle-${process.pid}-${options.cwd.replace(/[^\w]/g, "_")}.json`);
|
package/entry/extract.js
CHANGED
|
@@ -26,6 +26,7 @@ const createEntryName = (exportKey, exportsAsIndexes) => {
|
|
|
26
26
|
const withoutPrefix = exportKey.replace(/^\.\//, "");
|
|
27
27
|
return exportsAsIndexes ? `${withoutPrefix}/index` : withoutPrefix.replace(/\//g, "-");
|
|
28
28
|
};
|
|
29
|
+
/** @public */
|
|
29
30
|
function extractEntries(pkg, options = {}) {
|
|
30
31
|
const entries = {};
|
|
31
32
|
const exportPaths = {};
|
|
@@ -3,7 +3,11 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
|
|
5
5
|
//#region src/entry/package-json-entries.ts
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Derive a tsdown `entry` record (name to source path) from a package.json.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
7
11
|
function packageJsonEntries(options = {}) {
|
|
8
12
|
return extractEntries(options.pkg ?? JSON.parse(readFileSync(resolve(options.cwd ?? process.cwd(), "package.json"), "utf-8")), {
|
|
9
13
|
exportsAsIndexes: options.exportsAsIndexes,
|
package/errors.js
CHANGED
|
@@ -19,13 +19,21 @@ var BuildFailed = class extends Data.TaggedError("BuildFailed") {
|
|
|
19
19
|
return `Build failed for TargetGroup "${this.targetGroup}": ${this.reason}`;
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* API Extractor meta generation failed for an entry.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
23
27
|
var MetaGenerationError = class extends Data.TaggedError("MetaGenerationError") {
|
|
24
28
|
get message() {
|
|
25
29
|
return `Meta generation failed for entry "${this.entry}": ${this.reason}`;
|
|
26
30
|
}
|
|
27
31
|
};
|
|
28
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* A savvy.build.ts or publishConfig.targets config is structurally invalid; raised before any build work.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
29
37
|
var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
|
|
30
38
|
get message() {
|
|
31
39
|
return `Config validation failed at "${this.path}": ${this.reason}`;
|
package/exe/build.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
+
import { buildMetricsPlugin } from "../report/metrics-plugin.js";
|
|
2
|
+
import { createTimer } from "../report/timer.js";
|
|
3
|
+
import { createTsdownLogger } from "../report/tsdown-logger.js";
|
|
1
4
|
import { join } from "node:path";
|
|
2
5
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
6
|
import { tmpdir } from "node:os";
|
|
4
7
|
|
|
5
8
|
//#region src/exe/build.ts
|
|
6
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Compile each SEA binary via tsdown's exe mode. One tsdown build per spec.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
7
14
|
async function runExeBuild(options) {
|
|
8
15
|
const build = options.build ?? (await import("tsdown")).build;
|
|
9
16
|
for (const spec of options.specs) {
|
|
10
17
|
const scratch = mkdtempSync(join(tmpdir(), "savvy-exe-"));
|
|
18
|
+
const collector = options.collector;
|
|
19
|
+
const groupId = options.groupId;
|
|
20
|
+
const instrument = collector !== void 0 && groupId !== void 0 ? {
|
|
21
|
+
logLevel: "silent",
|
|
22
|
+
customLogger: createTsdownLogger(collector, groupId)
|
|
23
|
+
} : {};
|
|
24
|
+
const plugins = collector !== void 0 && groupId !== void 0 ? [buildMetricsPlugin(collector, groupId, "exe", options.verbose ?? false)] : [];
|
|
25
|
+
const timer = createTimer();
|
|
11
26
|
try {
|
|
12
27
|
await build({
|
|
13
28
|
cwd: options.cwd,
|
|
@@ -23,9 +38,12 @@ async function runExeBuild(options) {
|
|
|
23
38
|
outDir: options.outDir,
|
|
24
39
|
seaConfig: spec.seaConfig,
|
|
25
40
|
targets: spec.targets
|
|
26
|
-
}
|
|
41
|
+
},
|
|
42
|
+
...instrument,
|
|
43
|
+
...plugins.length > 0 ? { plugins } : {}
|
|
27
44
|
});
|
|
28
45
|
} finally {
|
|
46
|
+
if (collector !== void 0 && groupId !== void 0) collector.recordPassTiming(groupId, "exe", timer.elapsed());
|
|
29
47
|
rmSync(scratch, {
|
|
30
48
|
recursive: true,
|
|
31
49
|
force: true
|
package/exe/config.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
//#region src/exe/config.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Default Node runtime embedded in the SEA (parity with the vitest-agent reference).
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
3
7
|
const DEFAULT_EXE_NODE_VERSION = "25.9.0";
|
|
4
8
|
/** Map a package.json os value to the tsdown exe platform token. */
|
|
5
9
|
function platformToken(os) {
|
|
@@ -24,6 +28,7 @@ function inferTargets(pkg) {
|
|
|
24
28
|
*
|
|
25
29
|
* Pure function; structural validation (missing fileName, empty targets) lives in the
|
|
26
30
|
* config-validation layer.
|
|
31
|
+
* @public
|
|
27
32
|
*/
|
|
28
33
|
function normalizeExeOptions(exe, pkg) {
|
|
29
34
|
return (Array.isArray(exe) ? exe : [exe]).map((c) => {
|
package/exe/filename.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* The exact filename `@tsdown/exe` emits for a SEA target, mirroring tsdown's
|
|
4
4
|
* `resolveOutputFileName`: base fileName + `-<platform>-<arch>` + `.exe` on win.
|
|
5
5
|
* Single source of truth so the manifest value never drifts from the on-disk file.
|
|
6
|
+
* @public
|
|
6
7
|
*/
|
|
7
8
|
function computeExeFileName(fileName, target) {
|
|
8
9
|
return `${fileName}${`-${target.platform}-${target.arch}`}${target.platform === "win" ? ".exe" : ""}`;
|