@savvy-web/bundler 0.1.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 +257 -0
- package/config.js +39 -0
- package/index.d.ts +180 -0
- package/index.js +5 -0
- package/package.json +37 -0
- package/public/ecma.json +55 -0
- package/run.js +225 -0
package/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# @savvy-web/bundler
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@savvy-web/bundler)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
The zero-config bundler for [Silk Suite](https://github.com/savvy-web/systems) TypeScript packages. Configure a package with a single self-executing `savvy.build.ts`, run it against the `dev` or `npm` target and get a clean, publishable `dist/<target>/pkg`. Install one devDependency; `tsdown` is pinned and tested transitively, so a toolchain upgrade is a bundler release rather than a peer bump across your repos.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install --save-dev @savvy-web/bundler
|
|
12
|
+
# or
|
|
13
|
+
pnpm add -D @savvy-web/bundler
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
Add a `savvy.build.ts` to the package root. It both exports a config object and runs the build when invoked directly:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// savvy.build.ts
|
|
22
|
+
import { defineBuild, runBuild } from "@savvy-web/bundler";
|
|
23
|
+
|
|
24
|
+
const config = defineBuild({
|
|
25
|
+
format: ["esm"],
|
|
26
|
+
devManifest: "preserve",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export default config;
|
|
30
|
+
|
|
31
|
+
if (import.meta.main) {
|
|
32
|
+
await runBuild(config, { cwd: import.meta.dirname, argv: process.argv.slice(2) });
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Wire the two targets into `package.json` scripts and run them with Node's native TypeScript support (Node 24.11+):
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build:dev": "node savvy.build.ts --target dev",
|
|
42
|
+
"build:prod": "node savvy.build.ts --target prod"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run build:prod
|
|
49
|
+
# writes dist/prod/npm/pkg — the tarball root, with a resolved manifest and built code
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--target dev` writes `dist/dev/pkg`, the local-link target with `catalog:`/`workspace:` specifiers preserved. `--target prod` writes `dist/prod/npm/pkg` with those specifiers resolved to concrete ranges, ready to publish. Two further targets, `--target meta` and `--target exe`, are covered below.
|
|
53
|
+
|
|
54
|
+
Every build emits per-module JavaScript alongside a single rolled-up, self-contained `.d.ts` per public entry. Each entry's declaration file pulls in every re-exported type, so a consumer that infers a type from your public API never has to reach into a deep sibling module that no export subpath addresses.
|
|
55
|
+
|
|
56
|
+
## TypeScript config
|
|
57
|
+
|
|
58
|
+
The bundler ships its shared TypeScript base as a subpath export. Extend it from your package's `tsconfig.json` so source and declaration emit line up with what the bundler expects:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"extends": ["@savvy-web/bundler/ecma.json"]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`ecma.json` sets ESNext libs, NodeNext resolution, strict mode and `composite` declaration output. Override any of it in your own `tsconfig.json`.
|
|
67
|
+
|
|
68
|
+
## Multi-target publishing
|
|
69
|
+
|
|
70
|
+
By default `--target prod` builds a single group named after the package and writes it to `dist/prod/npm/pkg`. To publish the same package to more than one registry, or under more than one name, declare a `publishConfig.targets` map in `package.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"publishConfig": {
|
|
75
|
+
"targets": {
|
|
76
|
+
"npm": true,
|
|
77
|
+
"github": "@scope/internal-name",
|
|
78
|
+
"mirror": { "registry": "https://registry.example.com", "from": "npm" }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Each key is a target. `true` publishes under the package's own name to a well-known registry (`npm`, `github`); a string renames the group for that target; an object form takes `{ registry }` plus either `name` (a rename) or `from` (reuse another target's built bytes). `--target prod` then builds one byte-variant group per distinct name, applies the rename to each group's manifest and writes `dist/prod/<group>/pkg`. It also writes `dist/prod/targets.json`, the group-to-registry binding the release step consumes to know what to publish where.
|
|
85
|
+
|
|
86
|
+
With no `targets` map the build falls back to the single-`npm` group above.
|
|
87
|
+
|
|
88
|
+
## API Extractor meta
|
|
89
|
+
|
|
90
|
+
Set the optional `meta` field on `defineBuild` to generate an [API Extractor](https://api-extractor.com/) api-model from a package's type declarations:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const config = defineBuild({
|
|
94
|
+
format: ["esm"],
|
|
95
|
+
meta: {
|
|
96
|
+
// directories the generated api-model is copied into on `--target meta`
|
|
97
|
+
localPaths: ["../mcp/models/@savvy-web/bundler"],
|
|
98
|
+
tsdoc: {
|
|
99
|
+
suppressWarnings: [{ messageId: "ae-undocumented" }],
|
|
100
|
+
tagDefinitions: [{ tagName: "@internal", syntaxKind: "modifier" }],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
With `meta` set, two behaviors come online:
|
|
107
|
+
|
|
108
|
+
- `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.
|
|
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.
|
|
112
|
+
|
|
113
|
+
## Executable binaries
|
|
114
|
+
|
|
115
|
+
Set the optional `exe` field to compile a single-executable application (SEA) from a bin entry, via [`@tsdown/exe`](https://www.npmjs.com/package/@tsdown/exe):
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const config = defineBuild({
|
|
119
|
+
format: ["esm"],
|
|
120
|
+
exe: {
|
|
121
|
+
fileName: "savvy",
|
|
122
|
+
entry: "./src/bin.ts",
|
|
123
|
+
// targets default to the package's own os/cpu when omitted
|
|
124
|
+
targets: [{ platform: "linux", arch: "x64" }],
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`savvy build --target exe` compiles each declared binary into `dist/dev/pkg/bin`. Pass an array to `exe` to compile several. When `targets` is omitted the platform is inferred from the package's `os`/`cpu` fields. `--target exe` errors if the config has no `exe` field.
|
|
130
|
+
|
|
131
|
+
## JSX
|
|
132
|
+
|
|
133
|
+
Packages that emit JSX inherit their transform from `tsconfig.json` (`compilerOptions.jsx`/`jsxImportSource`) with no extra config. Set the optional `jsx` field on `defineBuild` to override it:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const config = defineBuild({
|
|
137
|
+
format: ["esm"],
|
|
138
|
+
jsx: { runtime: "automatic", importSource: "preact" },
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The resolved JSX settings feed both the dts tsconfig and the tsdown transform. Omit `jsx` to inherit the tsconfig value.
|
|
143
|
+
|
|
144
|
+
## Dual-format output
|
|
145
|
+
|
|
146
|
+
Builds are esm-only by default. Set the `format` field to add a CommonJS output alongside the ESM one:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const config = defineBuild({
|
|
150
|
+
format: ["esm", "cjs"],
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A dual-format build emits an ESM `.js` and a require-able CJS `.cjs` plus matching `.d.ts` and `.d.cts` declarations, and writes a manifest carrying both `import` and `require` export conditions. The CJS output uses default-export interop — `module.exports` is the module's default export, so a `require()` of the package yields that value directly. Omit `format`, or pass `["esm"]`, for an ESM-only build.
|
|
155
|
+
|
|
156
|
+
## Bundling dependencies
|
|
157
|
+
|
|
158
|
+
Dependencies you declare in `package.json` are externalized automatically — they stay `import`ed from the published `.js` and referenced from the `.d.ts`, and the consumer resolves them from their own `node_modules`. You don't list declared deps anywhere; `externals` exists only to externalize a package tsdown would otherwise bundle (a transitive dep you reference but don't declare). Four fields change the bundling posture, for the cases where a dependency cannot be left external:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const config = defineBuild({
|
|
162
|
+
// force-inline these specific packages into the .js, even ones declared in package.json
|
|
163
|
+
bundle: ["some-declared-dep"],
|
|
164
|
+
// force-bundle every non-externalized node_modules and workspace dep into the output
|
|
165
|
+
bundleNodeModules: true,
|
|
166
|
+
// inline only these packages' types into the bundled .d.ts; the rest stay external
|
|
167
|
+
bundledPackages: ["some-types-only-dep"],
|
|
168
|
+
// externalize these in the declaration pass only — referenced via import in the .d.ts,
|
|
169
|
+
// still bundled in the .js
|
|
170
|
+
dtsExternals: ["effect"],
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- `bundle` inlines the listed packages into the JavaScript output, the inverse of `externals`, even for packages declared in `package.json` that would otherwise be auto-externalized. Declarations are not inlined by this option — pair it with `bundledPackages` to roll a package's types into the `.d.ts` too.
|
|
175
|
+
- `bundleNodeModules` inlines node_modules and workspace JavaScript into the output so the published package is self-contained, and inlines their types into the bundled `.d.ts` to match.
|
|
176
|
+
- `bundledPackages` inlines only the listed packages' declarations into the `.d.ts` while every other dependency stays external. Use it for a types-only dependency you don't want consumers to install.
|
|
177
|
+
- `dtsExternals` keeps a package out of the declaration bundle when its types cannot be safely inlined — effect's cross-module `declare module` augmentations, for one, inline into conflicting interface extensions in consumers. The package is referenced by `import` in the `.d.ts` and still bundled in the JavaScript, so declare it as a package dependency.
|
|
178
|
+
|
|
179
|
+
## Per-entry overrides
|
|
180
|
+
|
|
181
|
+
The format and bundling fields above apply to every export entry. Use `overrides` to pin a subset of entries to their own format and bundling, layered onto the base config. The base build stays as configured; only the listed `entries` (by export subpath) get the override:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const config = defineBuild({
|
|
185
|
+
format: ["esm"], // base entries are ESM-only
|
|
186
|
+
overrides: [
|
|
187
|
+
{
|
|
188
|
+
// this one entry is also require-able, and inlines its node_modules so a
|
|
189
|
+
// CommonJS caller never has to resolve an ESM-only dependency
|
|
190
|
+
entries: ["./changesets/markdownlint"],
|
|
191
|
+
format: ["esm", "cjs"],
|
|
192
|
+
bundleNodeModules: true,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each override carries the same `format`, `bundle`, `externals`, `bundleNodeModules`, `bundledPackages` and `dtsExternals` fields as the base config. An override does not inherit the base `externals` — list what that partition needs. The build errors if an override names an export path the package does not declare.
|
|
199
|
+
|
|
200
|
+
## Minified output
|
|
201
|
+
|
|
202
|
+
Prod output is not minified by default. This builder targets Node libraries, where readable output matters more than bundle size — minified code degrades stack traces and trips some security scanners. Set `minify` to opt back in:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const config = defineBuild({
|
|
206
|
+
minify: true, // applies to prod target groups only; dev is never minified
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Manifest transform
|
|
211
|
+
|
|
212
|
+
Every build runs a manifest transform after resolving `publishConfig.targets`, to produce the published `package.json`. By default it strips build- and dev-only fields (`devDependencies`, `scripts`, `publishConfig` and the like). Supplying your own `transform` replaces that default — import `defaultManifestTransform` and call it from yours if you still want the stripping:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { defaultManifestTransform, defineBuild } from "@savvy-web/bundler";
|
|
216
|
+
|
|
217
|
+
const config = defineBuild({
|
|
218
|
+
transform: ({ pkg }) => {
|
|
219
|
+
// custom manifest work here
|
|
220
|
+
return defaultManifestTransform({ pkg });
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Features
|
|
226
|
+
|
|
227
|
+
- **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.
|
|
228
|
+
- **Four build targets** — `dev` for local linking, `npm` for a resolved publishable manifest, `meta` for an API Extractor api-model and `exe` for SEA binaries, on disjoint `dist/dev` and `dist/prod` output paths for clean caching.
|
|
229
|
+
- **Bundled declarations** — per-module JavaScript with a single rolled-up `.d.ts` per public entry, so re-exported types stay reachable through your published export subpaths.
|
|
230
|
+
- **Shared tsconfig base** — extend `@savvy-web/bundler/ecma.json` for the ESNext/NodeNext/strict settings the build expects.
|
|
231
|
+
- **Manifest resolution** — `catalog:` and `workspace:` specifiers are resolved against the workspace for the published target, and preserved for the linked dev target.
|
|
232
|
+
- **Multi-target publishing** — a `publishConfig.targets` map publishes one package to several registries or under several names; `--target prod` builds the distinct byte variants and writes a `targets.json` binding for the release step.
|
|
233
|
+
- **Executable binaries** — an `exe` config compiles SEA binaries from a bin entry via `@tsdown/exe`, inferring the platform from the package's `os`/`cpu` when targets are omitted.
|
|
234
|
+
- **JSX, config-first** — JSX transform is inherited from `tsconfig.json` and overridable via the `jsx` field, feeding both the dts tsconfig and the tsdown transform.
|
|
235
|
+
- **Dual-format output** — esm-only by default; set `format` to `["esm", "cjs"]` for a require-able CJS output with default-export interop, `.d.cts` declarations and dual `import`/`require` export conditions.
|
|
236
|
+
- **Dependency bundling** — declared dependencies stay external by default; `bundle`, `bundleNodeModules`, `bundledPackages` and `dtsExternals` force-inline specific packages or all node_modules into the output, inline select declarations into the `.d.ts` or hold a package out of the declaration bundle when its types cannot be inlined.
|
|
237
|
+
- **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
|
+
- **Readable prod output** — prod output is unminified by default to keep stack traces legible and pass security scanners; `minify` opts back in.
|
|
239
|
+
- **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`.
|
|
240
|
+
- **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
|
+
- **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
|
+
- **Injectable orchestration** — `runBuild` takes its IO dependencies as options, so the build is testable without spawning a real bundle.
|
|
243
|
+
- **Escape hatch** — every build behavior lives in [`@savvy-web/tsdown-plugins`](https://www.npmjs.com/package/@savvy-web/tsdown-plugins); compose the same helpers in a hand-written `tsdown.config.ts` when you outgrow the front door.
|
|
244
|
+
|
|
245
|
+
## API
|
|
246
|
+
|
|
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.
|
|
248
|
+
- `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
|
+
- `parseArgs(argv)` — the argument parser behind `runBuild`, exported for embedding.
|
|
250
|
+
|
|
251
|
+
## Turbo tasks
|
|
252
|
+
|
|
253
|
+
`pnpm turbo run build:meta` regenerates api-models into the `localPaths` configured in each package's `savvy.build.ts`, reading the dev build's `dist/dev/pkg` dts; it depends on `build:dev` and is intentionally uncached because it writes outside the package's own cache scope.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
[MIT](LICENSE)
|
package/config.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defaultManifestTransform } from "@savvy-web/tsdown-plugins";
|
|
2
|
+
|
|
3
|
+
//#region src/config.ts
|
|
4
|
+
/** Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts). */
|
|
5
|
+
function defineBuild(input = {}) {
|
|
6
|
+
return {
|
|
7
|
+
formats: input.formats ?? ["esm"],
|
|
8
|
+
externals: input.externals ?? [],
|
|
9
|
+
bundledPackages: input.bundledPackages,
|
|
10
|
+
dtsExternals: input.dtsExternals,
|
|
11
|
+
bundleNodeModules: input.bundleNodeModules,
|
|
12
|
+
bundle: input.bundle,
|
|
13
|
+
minify: input.minify ?? false,
|
|
14
|
+
devManifest: input.devManifest ?? "preserve",
|
|
15
|
+
transform: input.transform ?? defaultManifestTransform,
|
|
16
|
+
output: input.output,
|
|
17
|
+
meta: input.meta,
|
|
18
|
+
jsx: input.jsx,
|
|
19
|
+
exe: input.exe,
|
|
20
|
+
format: input.format,
|
|
21
|
+
overrides: input.overrides
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
let target = "dev";
|
|
26
|
+
let watch = false;
|
|
27
|
+
for (let i = 0; i < argv.length; i++) if (argv[i] === "--target") {
|
|
28
|
+
const v = argv[i + 1];
|
|
29
|
+
if (v === "dev" || v === "prod" || v === "meta" || v === "exe") target = v;
|
|
30
|
+
i++;
|
|
31
|
+
} else if (argv[i] === "--watch") watch = true;
|
|
32
|
+
return {
|
|
33
|
+
target,
|
|
34
|
+
watch
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { defineBuild, parseArgs };
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { BuildFormat, BuildTargetGroupsOptions, ExeConfig, GenerateMetaOptions, Json, JsxConfig, MetaOptions, MetaResult, PublishTargets, RenderedOutput, RunExeBuildOptions, TargetGroupRef, TargetResolution, TsconfigJsx, defaultManifestTransform } from "@savvy-web/tsdown-plugins";
|
|
2
|
+
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
interface BuildEntryOverride {
|
|
5
|
+
/** Export paths to pin to this partition, e.g. "./changesets/markdownlint" (or "." for root). */
|
|
6
|
+
readonly entries: ReadonlyArray<string>;
|
|
7
|
+
readonly format?: ReadonlyArray<BuildFormat> | undefined;
|
|
8
|
+
readonly bundle?: ReadonlyArray<string> | undefined;
|
|
9
|
+
readonly externals?: ReadonlyArray<string> | undefined;
|
|
10
|
+
readonly bundleNodeModules?: boolean | undefined;
|
|
11
|
+
readonly bundledPackages?: ReadonlyArray<string> | undefined;
|
|
12
|
+
readonly dtsExternals?: ReadonlyArray<string> | undefined;
|
|
13
|
+
}
|
|
14
|
+
interface OutputConfig {
|
|
15
|
+
readonly console?: {
|
|
16
|
+
readonly human?: boolean;
|
|
17
|
+
readonly agent?: boolean;
|
|
18
|
+
readonly ci?: boolean;
|
|
19
|
+
};
|
|
20
|
+
readonly format?: "terminal" | "json" | "markdown" | "ci-annotations" | "silent";
|
|
21
|
+
}
|
|
22
|
+
interface BuildConfigInput {
|
|
23
|
+
readonly formats?: ReadonlyArray<"esm">;
|
|
24
|
+
readonly externals?: ReadonlyArray<string>;
|
|
25
|
+
/**
|
|
26
|
+
* External packages whose type declarations are inlined into the bundled dts
|
|
27
|
+
* (the rslib `dtsBundledPackages` equivalent). Only these node_modules
|
|
28
|
+
* packages are rolled into the emitted `.d.ts`; all other deps stay external.
|
|
29
|
+
*/
|
|
30
|
+
readonly bundledPackages?: ReadonlyArray<string> | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Packages externalized in the dts pass ONLY — referenced via `import` in the
|
|
33
|
+
* emitted `.d.ts` rather than inlined — while the JS pass still bundles them per
|
|
34
|
+
* `bundleNodeModules`. Use when a dependency's types cannot be safely inlined,
|
|
35
|
+
* for example effect's cross-module `declare module` augmentations, which inline
|
|
36
|
+
* into conflicting interface-extension errors in consumers. Declare these as
|
|
37
|
+
* package dependencies so consumers can resolve the emitted type imports.
|
|
38
|
+
*/
|
|
39
|
+
readonly dtsExternals?: ReadonlyArray<string> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Force-bundle node_modules (and workspace) JS dependencies that are not
|
|
42
|
+
* externalized into the package output, restoring the self-contained bundle
|
|
43
|
+
* the rslib builder produced. Threads tsdown `deps.skipNodeModulesBundle:
|
|
44
|
+
* false` into BOTH the JS output and the bundled declarations: the dts posture
|
|
45
|
+
* tracks the JS posture, so node_modules types are inlined into the `.d.ts`
|
|
46
|
+
* and the published package needs no extra declared deps for them. Defaults to false.
|
|
47
|
+
*/
|
|
48
|
+
readonly bundleNodeModules?: boolean | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Force-bundle (inline) these packages into the JS output, even ones declared in
|
|
51
|
+
* package.json that would otherwise be auto-externalized. The inverse of `externals`;
|
|
52
|
+
* maps to tsdown `deps.alwaysBundle`. Accepts package names. Use when you declare a
|
|
53
|
+
* dependency for metadata/types but want its code inlined. Declarations are NOT
|
|
54
|
+
* inlined by this option — use `bundledPackages` to also roll a package's types into
|
|
55
|
+
* the emitted `.d.ts`.
|
|
56
|
+
*/
|
|
57
|
+
readonly bundle?: ReadonlyArray<string> | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Minify the prod build output. Applies ONLY to prod target groups (dev is never
|
|
60
|
+
* minified) and defaults to false: this builder targets Node libraries, where
|
|
61
|
+
* readable output matters more than bundle size — minified/obfuscated code trips
|
|
62
|
+
* security/SCA scanners and degrades stack traces. Set true to opt back in.
|
|
63
|
+
*/
|
|
64
|
+
readonly minify?: boolean | undefined;
|
|
65
|
+
readonly devManifest?: "preserve" | "resolve";
|
|
66
|
+
/**
|
|
67
|
+
* Final mutation of the emitted package.json, run after the declarative
|
|
68
|
+
* `publishConfig.targets` rename. Defaults to {@link defaultManifestTransform},
|
|
69
|
+
* which strips build/dev-only fields (devDependencies, scripts, publishConfig,
|
|
70
|
+
* etc.). Supplying your own REPLACES that default — import and call
|
|
71
|
+
* `defaultManifestTransform` from it if you still want the stripping.
|
|
72
|
+
*/
|
|
73
|
+
readonly transform?: (args: {
|
|
74
|
+
pkg: Json;
|
|
75
|
+
targetGroup: TargetGroupRef;
|
|
76
|
+
}) => Json;
|
|
77
|
+
readonly output?: OutputConfig;
|
|
78
|
+
readonly meta?: MetaOptions;
|
|
79
|
+
readonly jsx?: JsxConfig | undefined;
|
|
80
|
+
readonly exe?: ExeConfig | ReadonlyArray<ExeConfig> | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Output module formats forwarded to the tsdown build. Defaults to esm-only;
|
|
83
|
+
* add "cjs" for a dual-format esm plus cjs build. This is the live field;
|
|
84
|
+
* the legacy "formats" field above is not consumed by the build.
|
|
85
|
+
*/
|
|
86
|
+
readonly format?: ReadonlyArray<BuildFormat> | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Per-entry format/bundling overrides. Each group pins its `entries` (export paths) to
|
|
89
|
+
* its own format and bundling, layered onto the base build. Use to keep one entry CJS in
|
|
90
|
+
* an otherwise ESM-only package (e.g. silk's `./changesets/markdownlint`).
|
|
91
|
+
*/
|
|
92
|
+
readonly overrides?: ReadonlyArray<BuildEntryOverride> | undefined;
|
|
93
|
+
}
|
|
94
|
+
interface BuildConfig {
|
|
95
|
+
readonly formats: ReadonlyArray<"esm">;
|
|
96
|
+
readonly externals: ReadonlyArray<string>;
|
|
97
|
+
/**
|
|
98
|
+
* External packages whose type declarations are inlined into the bundled dts
|
|
99
|
+
* (the rslib `dtsBundledPackages` equivalent). Only these node_modules
|
|
100
|
+
* packages are rolled into the emitted `.d.ts`; all other deps stay external.
|
|
101
|
+
*/
|
|
102
|
+
readonly bundledPackages?: ReadonlyArray<string> | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Packages externalized in the dts pass ONLY — referenced via `import` in the
|
|
105
|
+
* emitted `.d.ts` rather than inlined — while the JS pass still bundles them per
|
|
106
|
+
* `bundleNodeModules`. Use when a dependency's types cannot be safely inlined
|
|
107
|
+
* (e.g. effect's cross-module `declare module` augmentations). Declare these as
|
|
108
|
+
* package dependencies so consumers can resolve the emitted type imports.
|
|
109
|
+
*/
|
|
110
|
+
readonly dtsExternals?: ReadonlyArray<string> | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* Force-bundle node_modules (and workspace) JS dependencies that are not
|
|
113
|
+
* externalized into the package output (rslib parity). Threads tsdown
|
|
114
|
+
* `deps.skipNodeModulesBundle: false` into BOTH the JS output and the bundled
|
|
115
|
+
* declarations — the dts posture tracks the JS posture, inlining node_modules
|
|
116
|
+
* types into the `.d.ts`. Defaults to false.
|
|
117
|
+
*/
|
|
118
|
+
readonly bundleNodeModules?: boolean | undefined;
|
|
119
|
+
/** Force-bundle (inline) these packages into the JS output (tsdown `deps.alwaysBundle`). Inverse of `externals`. */
|
|
120
|
+
readonly bundle?: ReadonlyArray<string> | undefined;
|
|
121
|
+
/** Minify prod output (prod groups only; dev is never minified). defineBuild defaults this to false. */
|
|
122
|
+
readonly minify?: boolean | undefined;
|
|
123
|
+
readonly devManifest: "preserve" | "resolve";
|
|
124
|
+
readonly transform?: ((args: {
|
|
125
|
+
pkg: Json;
|
|
126
|
+
targetGroup: TargetGroupRef;
|
|
127
|
+
}) => Json) | undefined;
|
|
128
|
+
readonly output?: OutputConfig | undefined;
|
|
129
|
+
readonly meta?: MetaOptions | undefined;
|
|
130
|
+
readonly jsx?: JsxConfig | undefined;
|
|
131
|
+
readonly exe?: ExeConfig | ReadonlyArray<ExeConfig> | undefined;
|
|
132
|
+
/** Output module formats forwarded to the tsdown build (esm-only by default; add "cjs" for dual-format). */
|
|
133
|
+
readonly format?: ReadonlyArray<BuildFormat> | undefined;
|
|
134
|
+
readonly overrides?: ReadonlyArray<BuildEntryOverride> | undefined;
|
|
135
|
+
}
|
|
136
|
+
/** Normalize + validate a defineBuild config. Pure when imported; self-runs when entry (see run.ts). */
|
|
137
|
+
declare function defineBuild(input?: BuildConfigInput): BuildConfig;
|
|
138
|
+
interface ParsedArgs {
|
|
139
|
+
readonly target: "dev" | "prod" | "meta" | "exe";
|
|
140
|
+
readonly watch: boolean;
|
|
141
|
+
}
|
|
142
|
+
declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/run.d.ts
|
|
145
|
+
interface RunOptions {
|
|
146
|
+
readonly cwd: string;
|
|
147
|
+
readonly argv: ReadonlyArray<string>;
|
|
148
|
+
/** Injectable for tests. */
|
|
149
|
+
readonly buildTargetGroups?: (o: BuildTargetGroupsOptions) => Promise<void>;
|
|
150
|
+
/** Injectable for tests: consumes rendered output (defaults to process.stdout.write). */
|
|
151
|
+
readonly writeOutput?: (output: RenderedOutput) => void;
|
|
152
|
+
/** Injectable for tests: returns the package version string. */
|
|
153
|
+
readonly readVersion?: () => string;
|
|
154
|
+
/** Injectable for tests: returns the package name. */
|
|
155
|
+
readonly readPackageName?: () => string;
|
|
156
|
+
/** Injectable for tests: writes the resolved tsconfig and returns its path (defaults to writeResolvedTsconfig, which writes to the OS temp dir). */
|
|
157
|
+
readonly writeTsconfig?: (cwd: string) => string;
|
|
158
|
+
/** Injectable for tests. */
|
|
159
|
+
readonly generateMeta?: (o: GenerateMetaOptions) => Promise<MetaResult>;
|
|
160
|
+
/** Injectable for tests: returns the package.json `exports` map. */
|
|
161
|
+
readonly readExports?: () => Record<string, string> | undefined;
|
|
162
|
+
/** Injectable for tests: returns package.json publishConfig.targets, or undefined. */
|
|
163
|
+
readonly readPublishTargets?: (() => PublishTargets | undefined) | undefined;
|
|
164
|
+
/** Injectable for tests: writes the target binding artifact. */
|
|
165
|
+
readonly writeTargetsBinding?: ((cwd: string, resolution: TargetResolution) => string) | undefined;
|
|
166
|
+
/** Injectable for tests: reads the jsx-relevant tsconfig compilerOptions slice. */
|
|
167
|
+
readonly readTsconfigJsx?: (() => TsconfigJsx) | undefined;
|
|
168
|
+
/** Injectable for tests. */
|
|
169
|
+
readonly runExeBuild?: ((o: RunExeBuildOptions) => Promise<void>) | undefined;
|
|
170
|
+
/** Injectable for tests: returns the package os/cpu arrays. */
|
|
171
|
+
readonly readOsCpu?: (() => {
|
|
172
|
+
os: ReadonlyArray<string>;
|
|
173
|
+
cpu: ReadonlyArray<string>;
|
|
174
|
+
}) | undefined;
|
|
175
|
+
}
|
|
176
|
+
/** Run a build from a normalized config. Pure orchestration; all IO injectable. */
|
|
177
|
+
declare function runBuild(config: BuildConfig, options: RunOptions): Promise<void>;
|
|
178
|
+
//#endregion
|
|
179
|
+
export { type BuildConfig, type BuildConfigInput, type BuildEntryOverride, type OutputConfig, type ParsedArgs, type RunOptions, defaultManifestTransform, defineBuild, parseArgs, runBuild };
|
|
180
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@savvy-web/bundler",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Zero-config tsdown-based bundler for Silk Suite TypeScript packages",
|
|
6
|
+
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/bundler",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/savvy-web/systems/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/savvy-web/systems.git",
|
|
13
|
+
"directory": "packages/bundler"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "C. Spencer Beggs",
|
|
18
|
+
"email": "spencer@savvyweb.systems",
|
|
19
|
+
"url": "https://savvyweb.systems"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./index.d.ts",
|
|
25
|
+
"import": "./index.js"
|
|
26
|
+
},
|
|
27
|
+
"./ecma.json": "./public/ecma.json"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@savvy-web/tsdown-plugins": "0.1.0",
|
|
31
|
+
"@tsdown/exe": "^0.22.1",
|
|
32
|
+
"tsdown": "^0.22.2"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"effect": ">=3.21.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/public/ecma.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"composite": true,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationDir": "${configDir}/dist",
|
|
8
|
+
"declarationMap": false,
|
|
9
|
+
"emitDeclarationOnly": false,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"explainFiles": false,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"isolatedDeclarations": false,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"exactOptionalPropertyTypes": true,
|
|
17
|
+
"jsx": "preserve",
|
|
18
|
+
"lib": ["esnext"],
|
|
19
|
+
"module": "nodenext",
|
|
20
|
+
"moduleResolution": "nodenext",
|
|
21
|
+
"outDir": "${configDir}/dist",
|
|
22
|
+
"resolveJsonModule": true,
|
|
23
|
+
"rootDir": "${configDir}",
|
|
24
|
+
"skipLibCheck": true,
|
|
25
|
+
"sourceMap": false,
|
|
26
|
+
"strict": true,
|
|
27
|
+
"strictNullChecks": true,
|
|
28
|
+
"target": "es2023",
|
|
29
|
+
"tsBuildInfoFile": "${configDir}/dist/.tsbuildinfo.lib",
|
|
30
|
+
"typeRoots": ["${configDir}/node_modules/@types", "${configDir}/types"],
|
|
31
|
+
"verbatimModuleSyntax": true,
|
|
32
|
+
"types": ["node"]
|
|
33
|
+
},
|
|
34
|
+
"exclude": ["${configDir}/node_modules", "${configDir}/dist/**/*"],
|
|
35
|
+
"include": [
|
|
36
|
+
"${configDir}/types/*.ts",
|
|
37
|
+
"${configDir}/package.json",
|
|
38
|
+
"${configDir}/*.ts",
|
|
39
|
+
"${configDir}/*.cts",
|
|
40
|
+
"${configDir}/*.mts",
|
|
41
|
+
"${configDir}/src/**/*.ts",
|
|
42
|
+
"${configDir}/src/**/*.tsx",
|
|
43
|
+
"${configDir}/src/**/*.cts",
|
|
44
|
+
"${configDir}/src/**/*.mts",
|
|
45
|
+
"${configDir}/lib/**/*.ts",
|
|
46
|
+
"${configDir}/lib/**/*.tsx",
|
|
47
|
+
"${configDir}/lib/**/*.cts",
|
|
48
|
+
"${configDir}/lib/**/*.mts",
|
|
49
|
+
"${configDir}/__test__/**/*.ts",
|
|
50
|
+
"${configDir}/__test__/**/*.tsx",
|
|
51
|
+
"${configDir}/__test__/**/*.cts",
|
|
52
|
+
"${configDir}/__test__/**/*.mts",
|
|
53
|
+
"${configDir}/public/**/*.json"
|
|
54
|
+
]
|
|
55
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { parseArgs } from "./config.js";
|
|
2
|
+
import { ConfigValidator, ConfigValidatorLive, ReportPipelineLive, buildTargetGroups, createEntryName, generateMeta, normalizeExeOptions, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveTargets, runExeBuild, writeResolvedTsconfig, writeTargetsBinding } from "@savvy-web/tsdown-plugins";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { Effect } from "effect";
|
|
6
|
+
|
|
7
|
+
//#region src/run.ts
|
|
8
|
+
/** Read and parse package.json at cwd, returning an empty object on any error. */
|
|
9
|
+
function readPackageJson(cwd) {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
12
|
+
} catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Resolve the prod build groups and the target binding from publishConfig.targets (or the single-npm default). */
|
|
17
|
+
function deriveProdGroups(targets, baseName) {
|
|
18
|
+
const resolution = resolveTargets({
|
|
19
|
+
targets: targets !== void 0 && Object.keys(targets).length > 0 ? targets : { npm: true },
|
|
20
|
+
baseName
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
groups: resolution.groups.map((g) => ({
|
|
24
|
+
id: g.id,
|
|
25
|
+
name: g.name
|
|
26
|
+
})),
|
|
27
|
+
resolution
|
|
28
|
+
};
|
|
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
|
+
/** Run a build from a normalized config. Pure orchestration; all IO injectable. */
|
|
39
|
+
async function runBuild(config, options) {
|
|
40
|
+
const { target } = parseArgs(options.argv);
|
|
41
|
+
const build = options.buildTargetGroups ?? buildTargetGroups;
|
|
42
|
+
const cwd = options.cwd;
|
|
43
|
+
const pkg = readPackageJson(cwd);
|
|
44
|
+
const version = options.readVersion ? options.readVersion() : pkg.version ?? "0.0.0";
|
|
45
|
+
const packageName = options.readPackageName ? options.readPackageName() : pkg.name ?? "unknown";
|
|
46
|
+
const jsx = resolveJsxConfig((options.readTsconfigJsx ?? (() => readTsconfigJsx(cwd)))(), config.jsx);
|
|
47
|
+
const tsconfigPath = (options.writeTsconfig ?? ((c) => writeResolvedTsconfig({
|
|
48
|
+
cwd: c,
|
|
49
|
+
...jsx?.runtime === "automatic" ? {
|
|
50
|
+
jsx: "react-jsx",
|
|
51
|
+
jsxImportSource: jsx.importSource
|
|
52
|
+
} : {},
|
|
53
|
+
...jsx?.runtime === "classic" ? { jsx: "react" } : {}
|
|
54
|
+
})))(cwd);
|
|
55
|
+
const entries = packageJsonEntries({
|
|
56
|
+
pkg,
|
|
57
|
+
cwd
|
|
58
|
+
});
|
|
59
|
+
const exportsMap = options.readExports ? options.readExports() : pkg.exports;
|
|
60
|
+
const runGenerateMeta = options.generateMeta ?? generateMeta;
|
|
61
|
+
const publishTargets = (options.readPublishTargets ?? (() => {
|
|
62
|
+
const declared = pkg.publishConfig?.targets;
|
|
63
|
+
return declared !== void 0 && !Array.isArray(declared) && typeof declared === "object" ? declared : void 0;
|
|
64
|
+
}))();
|
|
65
|
+
const writeBinding = options.writeTargetsBinding ?? writeTargetsBinding;
|
|
66
|
+
const osCpuForValidate = options.readOsCpu ? options.readOsCpu() : {
|
|
67
|
+
os: pkg.os ?? [],
|
|
68
|
+
cpu: pkg.cpu ?? []
|
|
69
|
+
};
|
|
70
|
+
await Effect.runPromise(Effect.flatMap(ConfigValidator, (v) => v.validate({
|
|
71
|
+
baseName: packageName,
|
|
72
|
+
hasExports: exportsMap !== void 0 && Object.keys(exportsMap).length > 0,
|
|
73
|
+
...publishTargets !== void 0 ? { targets: publishTargets } : {},
|
|
74
|
+
...config.exe !== void 0 ? { exe: config.exe } : {},
|
|
75
|
+
osCpu: osCpuForValidate,
|
|
76
|
+
...config.meta !== void 0 ? { meta: config.meta } : {}
|
|
77
|
+
})).pipe(Effect.provide(ConfigValidatorLive)));
|
|
78
|
+
if (target === "meta") {
|
|
79
|
+
if (config.meta === void 0) throw new Error("`savvy build --target meta` requires a `meta` option in the build config");
|
|
80
|
+
const norm = normalizeMetaOptions(config.meta);
|
|
81
|
+
const dtsBasenames = {};
|
|
82
|
+
for (const name of Object.keys(entries)) dtsBasenames[name] = name;
|
|
83
|
+
await runGenerateMeta({
|
|
84
|
+
cwd,
|
|
85
|
+
packageName,
|
|
86
|
+
tsconfigPath,
|
|
87
|
+
dtsDir: join(cwd, "dist", "dev", "pkg"),
|
|
88
|
+
entries: dtsBasenames,
|
|
89
|
+
exportPaths: deriveExportPaths(entries, exportsMap),
|
|
90
|
+
outMetaDir: join(cwd, "dist", "dev", "meta"),
|
|
91
|
+
localPaths: norm.localPaths,
|
|
92
|
+
tsdoc: norm.tsdoc
|
|
93
|
+
});
|
|
94
|
+
(options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`)))({
|
|
95
|
+
target: "stdout",
|
|
96
|
+
contentType: "text/plain",
|
|
97
|
+
content: `meta: wrote api-model for ${packageName} to ${norm.localPaths.length} localPath(s)`
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (target === "exe") {
|
|
102
|
+
if (config.exe === void 0) throw new Error("`savvy build --target exe` requires an `exe` option in the build config");
|
|
103
|
+
const specs = normalizeExeOptions(config.exe, osCpuForValidate);
|
|
104
|
+
await (options.runExeBuild ?? runExeBuild)({
|
|
105
|
+
cwd,
|
|
106
|
+
outDir: join(cwd, "dist", "dev", "pkg", "bin"),
|
|
107
|
+
specs
|
|
108
|
+
});
|
|
109
|
+
(options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`)))({
|
|
110
|
+
target: "stdout",
|
|
111
|
+
contentType: "text/plain",
|
|
112
|
+
content: `exe: compiled ${specs.length} binary/binaries for ${packageName}`
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let overridePartitions = [];
|
|
117
|
+
let baseEntries = entries;
|
|
118
|
+
let dualExports;
|
|
119
|
+
if (config.overrides) {
|
|
120
|
+
const partitions = [];
|
|
121
|
+
const overriddenEntryNames = /* @__PURE__ */ new Set();
|
|
122
|
+
const baseFormatHasCjs = (config.format ?? ["esm"]).includes("cjs");
|
|
123
|
+
const dualExportKeys = /* @__PURE__ */ new Set();
|
|
124
|
+
const exportPathByEntry = deriveExportPaths(entries, exportsMap);
|
|
125
|
+
for (const ov of config.overrides) {
|
|
126
|
+
const partEntry = {};
|
|
127
|
+
for (const exportPath of ov.entries) {
|
|
128
|
+
if (exportPath !== "." && !exportPath.startsWith("./")) throw new Error(`overrides: entry "${exportPath}" must be a canonical export path — use "." for the root or a "./"-prefixed subpath (e.g. "./changesets/markdownlint")`);
|
|
129
|
+
const entryName = createEntryName(exportPath, false);
|
|
130
|
+
const src = entries[entryName];
|
|
131
|
+
if (src === void 0) throw new Error(`overrides: export path "${exportPath}" (entry "${entryName}") is not a build entry of ${packageName}`);
|
|
132
|
+
partEntry[entryName] = src;
|
|
133
|
+
overriddenEntryNames.add(entryName);
|
|
134
|
+
if ((ov.format ?? config.format ?? ["esm"]).includes("cjs")) dualExportKeys.add(exportPath);
|
|
135
|
+
}
|
|
136
|
+
partitions.push({
|
|
137
|
+
entry: partEntry,
|
|
138
|
+
...ov.format !== void 0 ? { format: ov.format } : {},
|
|
139
|
+
...ov.externals !== void 0 ? { externals: ov.externals } : {},
|
|
140
|
+
...ov.bundle !== void 0 ? { bundle: ov.bundle } : {},
|
|
141
|
+
...ov.bundleNodeModules !== void 0 ? { bundleNodeModules: ov.bundleNodeModules } : {},
|
|
142
|
+
...ov.bundledPackages !== void 0 ? { bundledPackages: ov.bundledPackages } : {},
|
|
143
|
+
...ov.dtsExternals !== void 0 ? { dtsExternals: ov.dtsExternals } : {}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const onlyBase = {};
|
|
147
|
+
for (const [name, src] of Object.entries(entries)) {
|
|
148
|
+
if (overriddenEntryNames.has(name)) continue;
|
|
149
|
+
onlyBase[name] = src;
|
|
150
|
+
if (baseFormatHasCjs) dualExportKeys.add(exportPathByEntry[name] ?? (name === "index" ? "." : `./${name}`));
|
|
151
|
+
}
|
|
152
|
+
overridePartitions = partitions;
|
|
153
|
+
baseEntries = onlyBase;
|
|
154
|
+
dualExports = dualExportKeys;
|
|
155
|
+
}
|
|
156
|
+
const startMs = Date.now();
|
|
157
|
+
const { groups, resolution } = target === "dev" ? {
|
|
158
|
+
groups: [{
|
|
159
|
+
id: "dev",
|
|
160
|
+
name: packageName
|
|
161
|
+
}],
|
|
162
|
+
resolution: void 0
|
|
163
|
+
} : deriveProdGroups(publishTargets, packageName);
|
|
164
|
+
await build({
|
|
165
|
+
cwd,
|
|
166
|
+
version,
|
|
167
|
+
entry: config.overrides !== void 0 ? baseEntries : entries,
|
|
168
|
+
tsconfigPath,
|
|
169
|
+
groups,
|
|
170
|
+
devManifest: config.devManifest,
|
|
171
|
+
externals: config.externals,
|
|
172
|
+
...config.bundledPackages !== void 0 ? { bundledPackages: config.bundledPackages } : {},
|
|
173
|
+
...config.dtsExternals !== void 0 ? { dtsExternals: config.dtsExternals } : {},
|
|
174
|
+
...config.bundleNodeModules !== void 0 ? { bundleNodeModules: config.bundleNodeModules } : {},
|
|
175
|
+
...config.bundle !== void 0 ? { bundle: config.bundle } : {},
|
|
176
|
+
...config.minify !== void 0 ? { minify: config.minify } : {},
|
|
177
|
+
...config.transform !== void 0 ? { transform: config.transform } : {},
|
|
178
|
+
...jsx !== void 0 ? { jsx } : {},
|
|
179
|
+
...config.format !== void 0 ? { format: config.format } : {},
|
|
180
|
+
...overridePartitions.length > 0 ? { overrides: overridePartitions } : {},
|
|
181
|
+
...dualExports !== void 0 ? { dualExports } : {}
|
|
182
|
+
});
|
|
183
|
+
if (target === "prod" && resolution !== void 0) writeBinding(cwd, resolution);
|
|
184
|
+
if (target === "prod" && config.meta !== void 0) {
|
|
185
|
+
const metaGroupId = (groups.find((g) => g.name === packageName) ?? groups[0])?.id ?? "npm";
|
|
186
|
+
const norm = normalizeMetaOptions(config.meta);
|
|
187
|
+
const dtsBasenames = {};
|
|
188
|
+
for (const name of Object.keys(entries)) dtsBasenames[name] = name;
|
|
189
|
+
await runGenerateMeta({
|
|
190
|
+
cwd,
|
|
191
|
+
packageName,
|
|
192
|
+
tsconfigPath,
|
|
193
|
+
dtsDir: join(cwd, "dist", "prod", metaGroupId, "pkg"),
|
|
194
|
+
entries: dtsBasenames,
|
|
195
|
+
exportPaths: deriveExportPaths(entries, exportsMap),
|
|
196
|
+
outMetaDir: join(cwd, "dist", "prod", metaGroupId, "meta"),
|
|
197
|
+
localPaths: [],
|
|
198
|
+
tsdoc: norm.tsdoc
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (target === "prod") for (const g of groups) removeDeclarationMaps(join(cwd, "dist", "prod", g.id, "pkg"));
|
|
202
|
+
const totalMs = Date.now() - startMs;
|
|
203
|
+
const reportEntries = Object.keys(entries);
|
|
204
|
+
const report = {
|
|
205
|
+
package: packageName,
|
|
206
|
+
targetGroups: groups.map((g) => ({
|
|
207
|
+
id: g.id,
|
|
208
|
+
entries: reportEntries,
|
|
209
|
+
emittedFiles: [],
|
|
210
|
+
timings: { totalMs },
|
|
211
|
+
warnings: [],
|
|
212
|
+
errors: []
|
|
213
|
+
}))
|
|
214
|
+
};
|
|
215
|
+
const explicitFormat = config.output?.format;
|
|
216
|
+
const rendered = await Effect.runPromise(renderReport([report], {
|
|
217
|
+
...explicitFormat !== void 0 ? { explicitFormat } : {},
|
|
218
|
+
noColor: process.env.NO_COLOR !== void 0 || !process.stdout.isTTY
|
|
219
|
+
}).pipe(Effect.provide(ReportPipelineLive)));
|
|
220
|
+
const writeOutput = options.writeOutput ?? ((o) => process.stdout.write(`${o.content}\n`));
|
|
221
|
+
for (const output of rendered) writeOutput(output);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
//#endregion
|
|
225
|
+
export { runBuild };
|