@savvy-web/bundler 0.2.1 → 0.3.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 +37 -9
- package/config.js +2 -1
- package/index.d.ts +17 -2
- package/package.json +4 -3
- package/run.js +13 -5
package/README.md
CHANGED
|
@@ -87,7 +87,16 @@ With no `targets` map the build falls back to the single-`npm` group above.
|
|
|
87
87
|
|
|
88
88
|
## API Extractor meta
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
The bundler generates an [API Extractor](https://api-extractor.com/) api-model from a package's type declarations. Two behaviors come online:
|
|
91
|
+
|
|
92
|
+
- `savvy build --target meta` runs API Extractor over the dev build's `.d.ts` — no tsdown build, so it depends only on a prior `--target dev`. It writes the api-model (`<unscoped>.api.json`, `tsdoc-metadata.json` and a resolved `tsconfig.json`) into each `localPaths` directory.
|
|
93
|
+
- `savvy build --target prod` additionally emits the same bundle into `dist/prod/npm/meta` as a release asset alongside `pkg/`.
|
|
94
|
+
|
|
95
|
+
The `meta` field on `defineBuild` is tri-state and controls these:
|
|
96
|
+
|
|
97
|
+
- **Omitted** (or `undefined`) — generation runs with default options. `--target meta` works with no configuration and `--target prod` emits the meta asset. This is the default; you do not need a `meta` field to use `--target meta`.
|
|
98
|
+
- **An object** — override the defaults: `localPaths` (directories the api-model is copied into on `--target meta`) and `tsdoc` (warning suppression and custom tags).
|
|
99
|
+
- **`false`** — opt out entirely; both `--target meta` and the prod meta asset become no-ops.
|
|
91
100
|
|
|
92
101
|
```ts
|
|
93
102
|
const config = defineBuild({
|
|
@@ -101,14 +110,10 @@ const config = defineBuild({
|
|
|
101
110
|
},
|
|
102
111
|
},
|
|
103
112
|
});
|
|
104
|
-
```
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- `savvy build --target prod` additionally emits the same bundle into `dist/prod/npm/meta` as a release asset alongside `pkg/`.
|
|
110
|
-
|
|
111
|
-
`meta` is optional; omit it and neither behavior runs. `--target meta` errors if the config has no `meta` field.
|
|
114
|
+
// Or opt out of api-model generation altogether:
|
|
115
|
+
// const config = defineBuild({ meta: false });
|
|
116
|
+
```
|
|
112
117
|
|
|
113
118
|
## Executable binaries
|
|
114
119
|
|
|
@@ -222,6 +227,28 @@ const config = defineBuild({
|
|
|
222
227
|
});
|
|
223
228
|
```
|
|
224
229
|
|
|
230
|
+
## Build-time constants
|
|
231
|
+
|
|
232
|
+
The build injects `process.env.__PACKAGE_VERSION__` as a compile-time constant set to the package's version, so source can read its own version without importing `package.json` at runtime:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// somewhere in src/
|
|
236
|
+
const version = process.env.__PACKAGE_VERSION__;
|
|
237
|
+
// the reference is replaced at build time with the package's version as a string literal
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Add your own compile-time replacements with the `define` field. Values are inserted verbatim, so string literals must be pre-quoted:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const config = defineBuild({
|
|
244
|
+
define: {
|
|
245
|
+
"process.env.FLAG": JSON.stringify("on"),
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`define` merges with the auto-injected version constant; a key of `process.env.__PACKAGE_VERSION__` in your own `define` wins.
|
|
251
|
+
|
|
225
252
|
## Features
|
|
226
253
|
|
|
227
254
|
- **One self-executing config** — `savvy.build.ts` exports a `defineBuild` object for tooling to introspect and runs the build when invoked directly. No factory-notation config file.
|
|
@@ -237,6 +264,7 @@ const config = defineBuild({
|
|
|
237
264
|
- **Per-entry overrides** — `overrides` pins a subset of export entries to their own format and bundling, so one entry can ship dual-format CJS in an otherwise ESM-only package without changing the rest.
|
|
238
265
|
- **Readable prod output** — prod output is unminified by default to keep stack traces legible and pass security scanners; `minify` opts back in.
|
|
239
266
|
- **Default manifest stripping** — the published `package.json` drops build- and dev-only fields automatically; a custom `transform` replaces the default and can re-apply it via `defaultManifestTransform`.
|
|
267
|
+
- **Build-time constants** — the package version is injected as `process.env.__PACKAGE_VERSION__`, and the `define` field adds your own verbatim compile-time replacements.
|
|
240
268
|
- **Fast-fail config validation** — `runBuild` validates the config (`publishConfig.targets`, `exe`, `meta`) before any build work, raising a typed `ConfigValidationError` on the first violation.
|
|
241
269
|
- **One devDependency** — `tsdown` is a regular dependency, pinned and tested transitively, so you never carry it or its plugin peers in your own tree.
|
|
242
270
|
- **Injectable orchestration** — `runBuild` takes its IO dependencies as options, so the build is testable without spawning a real bundle.
|
|
@@ -244,7 +272,7 @@ const config = defineBuild({
|
|
|
244
272
|
|
|
245
273
|
## API
|
|
246
274
|
|
|
247
|
-
- `defineBuild(input)` — normalizes a build config (`externals`, `bundle`, `bundleNodeModules`, `bundledPackages`, `dtsExternals`, `minify`, `devManifest`, `transform`, `output`, `meta`, `jsx`, `exe`, `format`, `overrides`), 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.
|
|
275
|
+
- `defineBuild(input)` — normalizes a build config (`externals`, `bundle`, `bundleNodeModules`, `bundledPackages`, `dtsExternals`, `minify`, `devManifest`, `transform`, `output`, `meta`, `jsx`, `exe`, `format`, `overrides`, `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.
|
|
248
276
|
- `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.
|
|
249
277
|
- `parseArgs(argv)` — the argument parser behind `runBuild`, exported for embedding.
|
|
250
278
|
|
package/config.js
CHANGED
package/index.d.ts
CHANGED
|
@@ -75,7 +75,13 @@ interface BuildConfigInput {
|
|
|
75
75
|
targetGroup: TargetGroupRef;
|
|
76
76
|
}) => Json;
|
|
77
77
|
readonly output?: OutputConfig;
|
|
78
|
-
|
|
78
|
+
/**
|
|
79
|
+
* API-model (meta) generation. Tri-state: omit it (or `undefined`) to generate with
|
|
80
|
+
* DEFAULT options — `savvy build --target meta` always works and `--target prod` emits the
|
|
81
|
+
* meta release asset. Pass an object to override the defaults (`localPaths`, `tsdoc`). Pass
|
|
82
|
+
* `false` to opt OUT entirely — both `--target meta` and the prod meta asset become no-ops.
|
|
83
|
+
*/
|
|
84
|
+
readonly meta?: MetaOptions | false;
|
|
79
85
|
readonly jsx?: JsxConfig | undefined;
|
|
80
86
|
readonly exe?: ExeConfig | ReadonlyArray<ExeConfig> | undefined;
|
|
81
87
|
/**
|
|
@@ -90,6 +96,13 @@ interface BuildConfigInput {
|
|
|
90
96
|
* an otherwise ESM-only package (e.g. silk's `./changesets/markdownlint`).
|
|
91
97
|
*/
|
|
92
98
|
readonly overrides?: ReadonlyArray<BuildEntryOverride> | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Compile-time global replacements forwarded to the tsdown/rolldown build `define`.
|
|
101
|
+
* Values are inserted VERBATIM, so string literals must be quoted:
|
|
102
|
+
* `{ "process.env.FLAG": JSON.stringify("on") }`. Merged with the auto-injected
|
|
103
|
+
* `process.env.__PACKAGE_VERSION__` define; a user key of the same name wins.
|
|
104
|
+
*/
|
|
105
|
+
readonly define?: Record<string, string> | undefined;
|
|
93
106
|
}
|
|
94
107
|
interface BuildConfig {
|
|
95
108
|
readonly formats: ReadonlyArray<"esm">;
|
|
@@ -126,12 +139,14 @@ interface BuildConfig {
|
|
|
126
139
|
targetGroup: TargetGroupRef;
|
|
127
140
|
}) => Json) | undefined;
|
|
128
141
|
readonly output?: OutputConfig | undefined;
|
|
129
|
-
readonly meta?: MetaOptions | undefined;
|
|
142
|
+
readonly meta?: MetaOptions | false | undefined;
|
|
130
143
|
readonly jsx?: JsxConfig | undefined;
|
|
131
144
|
readonly exe?: ExeConfig | ReadonlyArray<ExeConfig> | undefined;
|
|
132
145
|
/** Output module formats forwarded to the tsdown build (esm-only by default; add "cjs" for dual-format). */
|
|
133
146
|
readonly format?: ReadonlyArray<BuildFormat> | undefined;
|
|
134
147
|
readonly overrides?: ReadonlyArray<BuildEntryOverride> | undefined;
|
|
148
|
+
/** Compile-time global replacements forwarded to the build `define` (merged with the auto-version). */
|
|
149
|
+
readonly define?: Record<string, string> | undefined;
|
|
135
150
|
}
|
|
136
151
|
/** Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts). */
|
|
137
152
|
declare function defineBuild(input?: BuildConfigInput): BuildConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/bundler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
"types": "./index.d.ts",
|
|
25
25
|
"import": "./index.js"
|
|
26
26
|
},
|
|
27
|
-
"./ecma.json": "./public/ecma.json"
|
|
27
|
+
"./ecma.json": "./public/ecma.json",
|
|
28
|
+
"./package.json": "./package.json"
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
|
-
"@savvy-web/tsdown-plugins": "0.
|
|
31
|
+
"@savvy-web/tsdown-plugins": "0.3.0",
|
|
31
32
|
"@tsdown/exe": "^0.22.1",
|
|
32
33
|
"tsdown": "^0.22.2"
|
|
33
34
|
},
|
package/run.js
CHANGED
|
@@ -73,11 +73,18 @@ async function runBuild(config, options) {
|
|
|
73
73
|
...publishTargets !== void 0 ? { targets: publishTargets } : {},
|
|
74
74
|
...config.exe !== void 0 ? { exe: config.exe } : {},
|
|
75
75
|
osCpu: osCpuForValidate,
|
|
76
|
-
...config.meta !== void 0 ? { meta: config.meta } : {}
|
|
76
|
+
...config.meta !== void 0 && config.meta !== false ? { meta: config.meta } : {}
|
|
77
77
|
})).pipe(Effect.provide(ConfigValidatorLive)));
|
|
78
78
|
if (target === "meta") {
|
|
79
|
-
if (config.meta ===
|
|
80
|
-
|
|
79
|
+
if (config.meta === false) {
|
|
80
|
+
(options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`)))({
|
|
81
|
+
target: "stdout",
|
|
82
|
+
contentType: "text/plain",
|
|
83
|
+
content: `meta: generation disabled (meta: false) for ${packageName}`
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const norm = normalizeMetaOptions(config.meta ?? {});
|
|
81
88
|
const dtsBasenames = {};
|
|
82
89
|
for (const name of Object.keys(entries)) dtsBasenames[name] = name;
|
|
83
90
|
await runGenerateMeta({
|
|
@@ -177,13 +184,14 @@ async function runBuild(config, options) {
|
|
|
177
184
|
...config.transform !== void 0 ? { transform: config.transform } : {},
|
|
178
185
|
...jsx !== void 0 ? { jsx } : {},
|
|
179
186
|
...config.format !== void 0 ? { format: config.format } : {},
|
|
187
|
+
...config.define !== void 0 ? { define: config.define } : {},
|
|
180
188
|
...overridePartitions.length > 0 ? { overrides: overridePartitions } : {},
|
|
181
189
|
...dualExports !== void 0 ? { dualExports } : {}
|
|
182
190
|
});
|
|
183
191
|
if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
|
|
184
|
-
if (target === "prod" && config.meta !==
|
|
192
|
+
if (target === "prod" && config.meta !== false) {
|
|
185
193
|
const metaGroupId = (groups.find((g) => g.name === packageName) ?? groups[0])?.id ?? "npm";
|
|
186
|
-
const norm = normalizeMetaOptions(config.meta);
|
|
194
|
+
const norm = normalizeMetaOptions(config.meta ?? {});
|
|
187
195
|
const dtsBasenames = {};
|
|
188
196
|
for (const name of Object.keys(entries)) dtsBasenames[name] = name;
|
|
189
197
|
await runGenerateMeta({
|