pkgbld 1.36.0 → 2.0.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 +26 -88
- package/index.js +2 -2
- package/package.json +31 -27
- package/src/build-configuration.js +395 -0
- package/src/build-entries.js +214 -0
- package/src/build-plugin-lifecycle.js +100 -0
- package/src/builtin-plugins/binify.js +31 -0
- package/src/builtin-plugins/clean.js +30 -0
- package/src/builtin-plugins/commonjs.js +14 -0
- package/src/builtin-plugins/externals.js +107 -0
- package/src/builtin-plugins/json.js +14 -0
- package/src/builtin-plugins/preprocess.js +37 -0
- package/src/builtin-plugins/resolve.js +14 -0
- package/src/builtin-plugins/terser.js +50 -0
- package/src/eject.js +151 -0
- package/src/get-json.js +16 -0
- package/src/get-plugins.js +43 -0
- package/src/get-rollup-configs.js +224 -0
- package/src/helpers.js +229 -0
- package/src/index.js +133 -0
- package/src/load-plugins.js +37 -0
- package/src/messages.js +13 -0
- package/src/options/index.js +266 -0
- package/src/options/types.js +24 -0
- package/src/plugin-name.js +6 -0
- package/src/priorities.js +10 -0
- package/src/process-pkg.js +237 -0
- package/src/process-ts-config.js +80 -0
- package/src/rollup-plugin-preprocess.d.ts +1 -0
- package/src/types.js +170 -0
- package/src/write-json.js +21 -0
- package/types/index.d.ts +298 -0
- package/dist/index.d.ts +0 -79
- package/dist/index.mjs +0 -1626
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ npm install --save-dev pkgbld
|
|
|
24
24
|
|
|
25
25
|
1. Start by creating package.json using `npm init`
|
|
26
26
|
2. Add pkgbld `npm install --save-dev pkgbld`
|
|
27
|
-
3. Create `src/index.
|
|
27
|
+
3. Create `src/index.js`
|
|
28
28
|
4. Add pkgbld in the 'scripts' field of your package.json like:
|
|
29
29
|
|
|
30
30
|
```json
|
|
@@ -35,6 +35,10 @@ npm install --save-dev pkgbld
|
|
|
35
35
|
|
|
36
36
|
Run `npm run build`.
|
|
37
37
|
|
|
38
|
+
For TypeScript or TSX sources, also install
|
|
39
|
+
[`pkgbld-plugin-swc`](https://github.com/kshutkin/package-build/tree/main/pkgbld-plugin-swc).
|
|
40
|
+
`pkgbld` discovers the plugin from your project dependencies and uses SWC to strip types.
|
|
41
|
+
|
|
38
42
|
## package.json
|
|
39
43
|
|
|
40
44
|
`pkgbld` expects the name field to be filled in the package.json file. `exports` field defines what entries/outputs should be built for this package.
|
|
@@ -195,102 +199,36 @@ pkgbld --no-exports
|
|
|
195
199
|
|
|
196
200
|
Do not add exports field in package.json.
|
|
197
201
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
pkgbld prune
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
prune devDependencies and redundant scripts from package.json
|
|
205
|
-
|
|
206
|
-
### prune --profile=<profile>
|
|
207
|
-
|
|
208
|
-
There are two profiles: `library` and `app`. `library` is default.
|
|
209
|
-
|
|
210
|
-
Right now it only affects how `prune` command removes entries in the `scripts` field.
|
|
211
|
-
|
|
212
|
-
For `library` profile it retains: 'preinstall', 'install', 'postinstall', 'prepublish', 'preprepare', 'prepare', 'postprepare'.
|
|
213
|
-
|
|
214
|
-
For `app` profile it retains in addition: 'prestart', 'start', 'poststart', 'prerestart', 'restart', 'postrestart', 'prestop', 'stop', 'poststop', 'pretest', 'test', 'posttest'.
|
|
202
|
+
This also disables entry-point discovery from an existing `exports` field. Only the top-level `src/index` entry point is built
|
|
203
|
+
unless a plugin provides additional inputs.
|
|
215
204
|
|
|
216
|
-
|
|
205
|
+
## Build plugin interface
|
|
217
206
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
Flattens file structure by moving all files from `dist` or other directory to the root directory and updating package.json.
|
|
223
|
-
|
|
224
|
-
If the directory is not specified it is guessed from package.json.
|
|
225
|
-
|
|
226
|
-
If files cannot be copied because of name conflicts the command will fail.
|
|
227
|
-
|
|
228
|
-
### removeSourcemaps
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
pkgbld prune --remove-sourcemaps
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Removes all sourcemaps from the package. The logic is very simple and removes all files with `.map` extension and references in format `//# sourceMappingURL=<mapFile>`.
|
|
235
|
-
|
|
236
|
-
### optimizeFiles (default)
|
|
237
|
-
|
|
238
|
-
```
|
|
239
|
-
pkgbld prune --optimize-files=false
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
Optimizes files by removing all files that are not required for pack at the given moment.
|
|
243
|
-
|
|
244
|
-
You might want to disable this option in some edge cases.
|
|
245
|
-
|
|
246
|
-
### no-subpackages
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
pkgbld --no-subpackages
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
Do not create subpackage directories with package.json files for non-index entry points.
|
|
253
|
-
|
|
254
|
-
By default, pkgbld creates a directory for each non-index entry point (e.g., `second/package.json` for a `./second` export) to enable simpler imports. Use this flag to disable this behavior.
|
|
255
|
-
|
|
256
|
-
Note: The `pkgbld-plugin-dts-buddy` plugin automatically sets this flag when loaded, as it provides alternative type resolution through the dts-buddy bundling approach.
|
|
257
|
-
|
|
258
|
-
### removeLegalComments
|
|
259
|
-
|
|
260
|
-
```
|
|
261
|
-
pkgbld prune --remove-legal-comments --compress=es,cjs
|
|
262
|
-
```
|
|
207
|
+
`pkgbld` loads plugins named `pkgbld-plugin-*` or `@scope/pkgbld-plugin-*` from
|
|
208
|
+
`dependencies`, `devDependencies`, and `peerDependencies`. The package name after
|
|
209
|
+
the optional scope must start with `pkgbld-plugin-`.
|
|
263
210
|
|
|
264
|
-
|
|
211
|
+
Plugins implement one or more lifecycle methods on the object returned by the plugin module's `create()` function.
|
|
265
212
|
|
|
266
|
-
|
|
213
|
+
Build configuration is resolved from defaults, package metadata, and explicit CLI options before `configure` runs. Plugins receive the effective mutable draft and have final authority. After all `configure` hooks finish, `pkgbld` normalizes, validates, and deeply freezes the configuration; every later hook receives that frozen value.
|
|
267
214
|
|
|
268
|
-
|
|
215
|
+
Build entries are resolved after configuration. A plugin that needs to add a source module does so during `contributeEntries`; later phases receive immutable entries containing the canonical name, concrete source path, source extension, and enabled output paths. Configured UMD and preprocessing selections must resolve to discovered Build entries.
|
|
269
216
|
|
|
270
|
-
|
|
217
|
+
`shared` is mutable state scoped to one build for coordination between plugins. Lifecycle phase boundaries are preserved, but plugin order within one phase is not guaranteed and asynchronous hooks in that phase may run in parallel. Plugins must not depend on the order of same-phase reads and writes. State owned by one plugin should remain in the closure created by `create()`.
|
|
271
218
|
|
|
272
219
|
```typescript
|
|
273
220
|
interface PkgbldPlugin {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
): void
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
provider: Provider,
|
|
286
|
-
config: Record<string, string | string[] | boolean>,
|
|
287
|
-
inputs: string[]
|
|
288
|
-
): Promise<void>;
|
|
289
|
-
getExtraOutputSettings(
|
|
290
|
-
format: InternalModuleFormat,
|
|
291
|
-
inputs: string[]
|
|
292
|
-
): Partial<OutputOptions>;
|
|
293
|
-
buildEnd(): Promise<void>;
|
|
221
|
+
configure(context: {
|
|
222
|
+
draft: BuildConfigurationDraft;
|
|
223
|
+
sources: BuildConfigurationSources;
|
|
224
|
+
shared: Map<unknown, unknown>;
|
|
225
|
+
}): void;
|
|
226
|
+
contributeEntries(context: PluginContributeEntriesContext): void;
|
|
227
|
+
processPackageJson(context: PluginPackageContext): void;
|
|
228
|
+
processTsConfig(context: PluginTsConfigContext): void;
|
|
229
|
+
providePlugins(context: PluginRollupContext): Promise<void>;
|
|
230
|
+
getExtraOutputSettings(context: PluginOutputContext): Partial<OutputOptions>;
|
|
231
|
+
buildEnd(context: PluginBuildEndContext): Promise<void>;
|
|
294
232
|
}
|
|
295
233
|
```
|
|
296
234
|
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import('./
|
|
2
|
+
|
|
3
|
+
import('./src/index.js');
|
package/package.json
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "2.0.0",
|
|
3
3
|
"name": "pkgbld",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Konstantin Shutkin",
|
|
6
6
|
"bin": "./index.js",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "./
|
|
9
|
-
"types": "./
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
8
|
+
"main": "./src/index.js",
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./types/index.d.ts",
|
|
13
|
+
"default": "./src/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./options": {
|
|
16
|
+
"types": "./types/index.d.ts",
|
|
17
|
+
"default": "./src/options/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
13
21
|
"engines": {
|
|
14
22
|
"node": ">=20"
|
|
15
23
|
},
|
|
@@ -28,28 +36,24 @@
|
|
|
28
36
|
"rollup"
|
|
29
37
|
],
|
|
30
38
|
"dependencies": {
|
|
31
|
-
"@niceties/logger": "^1.1
|
|
32
|
-
"@niceties/draftlog-appender": "^1.
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"rollup
|
|
39
|
+
"@niceties/logger": "^2.1.1",
|
|
40
|
+
"@niceties/draftlog-appender": "^2.1.1",
|
|
41
|
+
"fast-is-equal": "^1.3.3",
|
|
42
|
+
"type-fest": "^5.9.0",
|
|
43
|
+
"rollup": "^4.63.1",
|
|
36
44
|
"rollup-plugin-preprocess": "^0.0.4",
|
|
37
|
-
"@rollup/plugin-commonjs": "^
|
|
38
|
-
"@rollup/plugin-terser": "^0.
|
|
45
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
46
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
39
47
|
"@rollup/plugin-json": "^6.1.0",
|
|
40
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
41
|
-
"@rollup-extras/plugin-clean": "^
|
|
42
|
-
"@rollup-extras/plugin-binify": "^
|
|
43
|
-
"@rollup-extras/plugin-externals": "^
|
|
44
|
-
"@slimlib/refine-partition": "^
|
|
45
|
-
"@slimlib/smart-mock": "^0.
|
|
46
|
-
"is-builtin-module": "^
|
|
47
|
-
"terser": "^5.
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"jsonata": "^2.0.6"
|
|
51
|
-
},
|
|
52
|
-
"peerDependencies": {
|
|
53
|
-
"typescript": ">=5.3.3"
|
|
48
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
49
|
+
"@rollup-extras/plugin-clean": "^2.0.1",
|
|
50
|
+
"@rollup-extras/plugin-binify": "^2.0.0",
|
|
51
|
+
"@rollup-extras/plugin-externals": "^2.0.0",
|
|
52
|
+
"@slimlib/refine-partition": "^2.0.2",
|
|
53
|
+
"@slimlib/smart-mock": "^1.0.2",
|
|
54
|
+
"is-builtin-module": "^5.0.0",
|
|
55
|
+
"terser": "^5.51.2",
|
|
56
|
+
"@niceties/ansi": "^1.1.2",
|
|
57
|
+
"@niceties/node-parseargs-plus": "^0.6.0"
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { parseArgsPlus } from '@niceties/node-parseargs-plus';
|
|
2
|
+
import { camelCase } from '@niceties/node-parseargs-plus/camel-case';
|
|
3
|
+
import { customValue } from '@niceties/node-parseargs-plus/custom-value';
|
|
4
|
+
import { help } from '@niceties/node-parseargs-plus/help';
|
|
5
|
+
|
|
6
|
+
import { cliFlags, cliFlagsDefaults as defaults } from './options/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('type-fest').PackageJson} PackageJson
|
|
10
|
+
* @typedef {import('./types.js').BuildFormat} BuildFormat
|
|
11
|
+
* @typedef {import('./types.js').BuildConfiguration} BuildConfiguration
|
|
12
|
+
* @typedef {import('./types.js').BuildConfigurationDraft} BuildConfigurationDraft
|
|
13
|
+
* @typedef {import('./types.js').ParsedOptions} ParsedOptions
|
|
14
|
+
* @typedef {ReturnType<typeof import('./build-plugin-lifecycle.js').createBuildPluginLifecycle>} BuildPluginLifecycle
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const formats = new Set(['es', 'cjs', 'umd']);
|
|
18
|
+
|
|
19
|
+
export class BuildConfigurationError extends Error {
|
|
20
|
+
/**
|
|
21
|
+
* @param {{ code: string; path: string; message: string; value?: unknown }[]} issues
|
|
22
|
+
* @param {{ cause?: unknown }} [options]
|
|
23
|
+
*/
|
|
24
|
+
constructor(issues, options) {
|
|
25
|
+
super(`Invalid Build configuration:\n${issues.map(issue => `- ${issue.path}: ${issue.message}`).join('\n')}`, options);
|
|
26
|
+
this.name = 'BuildConfigurationError';
|
|
27
|
+
this.issues = issues;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the immutable Build configuration for one build.
|
|
33
|
+
*
|
|
34
|
+
* @param {{
|
|
35
|
+
* argv?: readonly string[];
|
|
36
|
+
* packageJson: PackageJson;
|
|
37
|
+
* pluginLifecycle: BuildPluginLifecycle;
|
|
38
|
+
* }} input
|
|
39
|
+
* @returns {BuildConfiguration}
|
|
40
|
+
*/
|
|
41
|
+
export function resolveBuildConfiguration({ argv = process.argv.slice(2), packageJson, pluginLifecycle }) {
|
|
42
|
+
const draft = createDefaultDraft();
|
|
43
|
+
const defaultSource = deepFreeze(structuredClone(draft));
|
|
44
|
+
applyPackageMetadata(draft, packageJson);
|
|
45
|
+
|
|
46
|
+
const cli = parseCli(argv, packageJson);
|
|
47
|
+
applyCli(draft, cli.values, cli.provided);
|
|
48
|
+
|
|
49
|
+
const sources = deepFreeze({
|
|
50
|
+
defaults: defaultSource,
|
|
51
|
+
package: structuredClone(packageJson),
|
|
52
|
+
cli: {
|
|
53
|
+
values: structuredClone(cli.values),
|
|
54
|
+
provided: { ...cli.provided },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
pluginLifecycle.configure(draft, sources);
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
throw new BuildConfigurationError(
|
|
62
|
+
[{ code: 'PLUGIN_CONFIGURATION_FAILED', path: 'plugins', message: 'Build plugin configuration failed' }],
|
|
63
|
+
{ cause }
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const structuralIssues = validateStructure(createDefaultDraft(), draft);
|
|
68
|
+
if (structuralIssues.length > 0) {
|
|
69
|
+
throw new BuildConfigurationError(structuralIssues);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
normalize(draft);
|
|
73
|
+
const issues = validate(draft, packageJson);
|
|
74
|
+
if (issues.length > 0) {
|
|
75
|
+
throw new BuildConfigurationError(issues);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return /** @type {BuildConfiguration} */ (deepFreeze(draft));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @returns {BuildConfigurationDraft} */
|
|
82
|
+
function createDefaultDraft() {
|
|
83
|
+
return {
|
|
84
|
+
paths: {
|
|
85
|
+
sourceDir: defaults.src,
|
|
86
|
+
outputDir: defaults.dest,
|
|
87
|
+
},
|
|
88
|
+
outputs: {
|
|
89
|
+
formats: /** @type {BuildFormat[]} */ ([...defaults.formats]),
|
|
90
|
+
patterns: {
|
|
91
|
+
es: defaults.esmPattern,
|
|
92
|
+
cjs: defaults.commonjsPattern,
|
|
93
|
+
umd: defaults.umdPattern,
|
|
94
|
+
},
|
|
95
|
+
umdEntries: [...defaults.umd],
|
|
96
|
+
sourcemaps: /** @type {BuildFormat[]} */ ([...defaults.sourcemaps]),
|
|
97
|
+
},
|
|
98
|
+
transforms: {
|
|
99
|
+
compress: /** @type {BuildFormat[]} */ ([...defaults.compress]),
|
|
100
|
+
preprocess: [...defaults.preprocess],
|
|
101
|
+
includeExternals: defaults.includeExternals,
|
|
102
|
+
removeLegalComments: false,
|
|
103
|
+
},
|
|
104
|
+
packageJson: {
|
|
105
|
+
update: true,
|
|
106
|
+
format: defaults.formatPackageJson,
|
|
107
|
+
exports: true,
|
|
108
|
+
pack: true,
|
|
109
|
+
executables: { mode: 'infer', values: [] },
|
|
110
|
+
},
|
|
111
|
+
typescript: {
|
|
112
|
+
updateConfig: defaults.tsConfig,
|
|
113
|
+
},
|
|
114
|
+
execution: {
|
|
115
|
+
clean: true,
|
|
116
|
+
bundle: true,
|
|
117
|
+
eject: defaults.eject,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {BuildConfigurationDraft} draft
|
|
124
|
+
* @param {PackageJson} packageJson
|
|
125
|
+
*/
|
|
126
|
+
function applyPackageMetadata(draft, packageJson) {
|
|
127
|
+
if (typeof packageJson.umd === 'string') {
|
|
128
|
+
draft.outputs.umdEntries.push('index');
|
|
129
|
+
draft.outputs.formats.push('umd');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {readonly string[]} argv
|
|
135
|
+
* @param {PackageJson} packageJson
|
|
136
|
+
*/
|
|
137
|
+
function parseCli(argv, packageJson) {
|
|
138
|
+
const result = parseArgsPlus(
|
|
139
|
+
{
|
|
140
|
+
args: [...argv],
|
|
141
|
+
name: 'pkgbld',
|
|
142
|
+
version: packageJson.version ?? '<unknown>',
|
|
143
|
+
options: /** @type {any} */ (cliFlags),
|
|
144
|
+
allowNegative: true,
|
|
145
|
+
tokens: true,
|
|
146
|
+
},
|
|
147
|
+
[help, camelCase, customValue]
|
|
148
|
+
);
|
|
149
|
+
/** @type {Record<string, boolean>} */
|
|
150
|
+
const provided = {};
|
|
151
|
+
for (const token of result.tokens ?? []) {
|
|
152
|
+
if (token.kind === 'option') {
|
|
153
|
+
provided[toCamelCase(token.name)] = true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { values: /** @type {ParsedOptions} */ (result.values), provided };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {BuildConfigurationDraft} draft
|
|
161
|
+
* @param {ParsedOptions} flags
|
|
162
|
+
* @param {Record<string, boolean>} provided
|
|
163
|
+
*/
|
|
164
|
+
function applyCli(draft, flags, provided) {
|
|
165
|
+
if (provided.formats) {
|
|
166
|
+
draft.outputs.formats = [.../** @type {BuildFormat[]} */ (flags.formats)];
|
|
167
|
+
if (!draft.outputs.formats.includes('umd') && !provided.umd) {
|
|
168
|
+
draft.outputs.umdEntries = [];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (provided.umd) {
|
|
172
|
+
draft.outputs.umdEntries = [.../** @type {string[]} */ (flags.umd)];
|
|
173
|
+
if (draft.outputs.umdEntries.length === 0 && !provided.formats) {
|
|
174
|
+
draft.outputs.formats = draft.outputs.formats.filter(format => format !== 'umd');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (provided.compress) draft.transforms.compress = [.../** @type {BuildFormat[]} */ (flags.compress)];
|
|
178
|
+
if (provided.sourcemaps) draft.outputs.sourcemaps = [.../** @type {BuildFormat[]} */ (flags.sourcemaps)];
|
|
179
|
+
if (provided.preprocess) draft.transforms.preprocess = [.../** @type {string[]} */ (flags.preprocess)];
|
|
180
|
+
if (provided.dest) draft.paths.outputDir = /** @type {string} */ (flags.dest);
|
|
181
|
+
if (provided.src) draft.paths.sourceDir = /** @type {string} */ (flags.src);
|
|
182
|
+
if (provided.bin) {
|
|
183
|
+
const values = /** @type {string[]} */ (flags.bin);
|
|
184
|
+
draft.packageJson.executables = { mode: values.length > 0 ? 'explicit' : 'disabled', values: [...values] };
|
|
185
|
+
}
|
|
186
|
+
if (provided.includeExternals) {
|
|
187
|
+
draft.transforms.includeExternals = /** @type {boolean | string[]} */ (flags.includeExternals);
|
|
188
|
+
}
|
|
189
|
+
if (provided.eject) draft.execution.eject = /** @type {boolean} */ (flags.eject);
|
|
190
|
+
if (provided.tsConfig) draft.typescript.updateConfig = /** @type {boolean} */ (flags.tsConfig);
|
|
191
|
+
if (provided.updatePackageJson) draft.packageJson.update = /** @type {boolean} */ (flags.updatePackageJson);
|
|
192
|
+
if (provided.commonjsPattern) draft.outputs.patterns.cjs = /** @type {string} */ (flags.commonjsPattern);
|
|
193
|
+
if (provided.esmPattern) draft.outputs.patterns.es = /** @type {string} */ (flags.esmPattern);
|
|
194
|
+
if (provided.umdPattern) draft.outputs.patterns.umd = /** @type {string} */ (flags.umdPattern);
|
|
195
|
+
if (provided.formatPackageJson) draft.packageJson.format = /** @type {boolean} */ (flags.formatPackageJson);
|
|
196
|
+
if (provided.pack) draft.packageJson.pack = /** @type {boolean} */ (flags.pack);
|
|
197
|
+
if (provided.exports) draft.packageJson.exports = /** @type {boolean} */ (flags.exports);
|
|
198
|
+
if (provided.clean) draft.execution.clean = /** @type {boolean} */ (flags.clean);
|
|
199
|
+
if (provided.bundle) draft.execution.bundle = /** @type {boolean} */ (flags.bundle);
|
|
200
|
+
if (provided.removeLegalComments) {
|
|
201
|
+
draft.transforms.removeLegalComments = /** @type {boolean} */ (flags.removeLegalComments);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** @param {BuildConfigurationDraft} draft */
|
|
206
|
+
function normalize(draft) {
|
|
207
|
+
if (Array.isArray(draft.outputs?.formats)) draft.outputs.formats = unique(draft.outputs.formats);
|
|
208
|
+
if (Array.isArray(draft.outputs?.umdEntries)) draft.outputs.umdEntries = unique(draft.outputs.umdEntries);
|
|
209
|
+
if (Array.isArray(draft.outputs?.sourcemaps)) draft.outputs.sourcemaps = unique(draft.outputs.sourcemaps);
|
|
210
|
+
if (Array.isArray(draft.transforms?.compress)) draft.transforms.compress = unique(draft.transforms.compress);
|
|
211
|
+
if (Array.isArray(draft.transforms?.preprocess)) draft.transforms.preprocess = unique(draft.transforms.preprocess);
|
|
212
|
+
if (Array.isArray(draft.transforms.includeExternals)) {
|
|
213
|
+
draft.transforms.includeExternals = unique(draft.transforms.includeExternals);
|
|
214
|
+
}
|
|
215
|
+
if (Array.isArray(draft.packageJson?.executables?.values)) {
|
|
216
|
+
draft.packageJson.executables.values = unique(draft.packageJson.executables.values.filter(Boolean));
|
|
217
|
+
if (draft.packageJson.executables.mode === 'explicit' && draft.packageJson.executables.values.length === 0) {
|
|
218
|
+
draft.packageJson.executables.mode = 'disabled';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (
|
|
222
|
+
Array.isArray(draft.outputs?.umdEntries) &&
|
|
223
|
+
Array.isArray(draft.outputs?.formats) &&
|
|
224
|
+
draft.outputs.umdEntries.length > 0 &&
|
|
225
|
+
!draft.outputs.formats.includes('umd')
|
|
226
|
+
) {
|
|
227
|
+
draft.outputs.formats.push('umd');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {BuildConfigurationDraft} draft
|
|
233
|
+
* @param {PackageJson} packageJson
|
|
234
|
+
*/
|
|
235
|
+
function validate(draft, packageJson) {
|
|
236
|
+
/** @type {{ code: string; path: string; message: string; value?: unknown }[]} */
|
|
237
|
+
const issues = [];
|
|
238
|
+
validateFormats(draft.outputs.formats, 'outputs.formats', issues);
|
|
239
|
+
validateFormats(draft.outputs.sourcemaps, 'outputs.sourcemaps', issues);
|
|
240
|
+
validateFormats(draft.transforms.compress, 'transforms.compress', issues);
|
|
241
|
+
validateStrings(draft.outputs.umdEntries, 'outputs.umdEntries', issues);
|
|
242
|
+
validateStrings(draft.transforms.preprocess, 'transforms.preprocess', issues);
|
|
243
|
+
if (draft.outputs.umdEntries.length > 0 && typeof packageJson.name !== 'string') {
|
|
244
|
+
issues.push({ code: 'PACKAGE_NAME_REQUIRED', path: 'package.name', message: 'a package name is required for UMD entries' });
|
|
245
|
+
}
|
|
246
|
+
for (const [format, pattern] of Object.entries(draft.outputs.patterns)) {
|
|
247
|
+
if (typeof pattern !== 'string' || !pattern.includes('[name]')) {
|
|
248
|
+
issues.push({
|
|
249
|
+
code: 'INVALID_FILE_NAME_PATTERN',
|
|
250
|
+
path: `outputs.patterns.${format}`,
|
|
251
|
+
message: 'must contain [name]',
|
|
252
|
+
value: pattern,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const [path, value] of [
|
|
257
|
+
['paths.sourceDir', draft.paths.sourceDir],
|
|
258
|
+
['paths.outputDir', draft.paths.outputDir],
|
|
259
|
+
]) {
|
|
260
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
261
|
+
issues.push({ code: 'INVALID_PATH', path, message: 'must be a non-empty string', value });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
typeof draft.transforms.includeExternals !== 'boolean' &&
|
|
266
|
+
(!Array.isArray(draft.transforms.includeExternals) ||
|
|
267
|
+
draft.transforms.includeExternals.some(value => typeof value !== 'string' || value.length === 0))
|
|
268
|
+
) {
|
|
269
|
+
issues.push({
|
|
270
|
+
code: 'INVALID_EXTERNALS',
|
|
271
|
+
path: 'transforms.includeExternals',
|
|
272
|
+
message: 'must be a boolean or an array of non-empty strings',
|
|
273
|
+
value: draft.transforms.includeExternals,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (!['infer', 'disabled', 'explicit'].includes(draft.packageJson.executables.mode)) {
|
|
277
|
+
issues.push({
|
|
278
|
+
code: 'INVALID_EXECUTABLE_MODE',
|
|
279
|
+
path: 'packageJson.executables.mode',
|
|
280
|
+
message: 'must be infer, disabled, or explicit',
|
|
281
|
+
value: draft.packageJson.executables.mode,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
validateStrings(draft.packageJson.executables.values, 'packageJson.executables.values', issues);
|
|
285
|
+
return issues;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @param {object} expected
|
|
290
|
+
* @param {unknown} actual
|
|
291
|
+
* @returns {{ code: string; path: string; message: string; value?: unknown }[]}
|
|
292
|
+
*/
|
|
293
|
+
function validateStructure(expected, actual) {
|
|
294
|
+
/** @type {{ code: string; path: string; message: string; value?: unknown }[]} */
|
|
295
|
+
const issues = [];
|
|
296
|
+
visit(expected, actual, '');
|
|
297
|
+
return issues;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @param {unknown} expected
|
|
301
|
+
* @param {unknown} actual
|
|
302
|
+
* @param {string} path
|
|
303
|
+
*/
|
|
304
|
+
function visit(expected, actual, path) {
|
|
305
|
+
if (Array.isArray(expected)) {
|
|
306
|
+
if (!Array.isArray(actual)) {
|
|
307
|
+
issues.push({ code: 'INVALID_CONFIGURATION_SHAPE', path, message: 'must be an array', value: actual });
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (typeof expected !== 'object' || expected == null) {
|
|
312
|
+
const isExternalList = path === 'transforms.includeExternals' && Array.isArray(actual);
|
|
313
|
+
if (!isExternalList && typeof actual !== typeof expected) {
|
|
314
|
+
issues.push({
|
|
315
|
+
code: 'INVALID_CONFIGURATION_SHAPE',
|
|
316
|
+
path,
|
|
317
|
+
message: `must be ${typeof expected}`,
|
|
318
|
+
value: actual,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (typeof actual !== 'object' || actual == null || Array.isArray(actual)) {
|
|
324
|
+
issues.push({ code: 'INVALID_CONFIGURATION_SHAPE', path, message: 'must be an object', value: actual });
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
for (const key of Object.keys(actual)) {
|
|
328
|
+
const keyPath = path ? `${path}.${key}` : key;
|
|
329
|
+
if (!(key in expected)) {
|
|
330
|
+
issues.push({ code: 'UNKNOWN_CONFIGURATION_KEY', path: keyPath, message: 'is not a recognized configuration key' });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const key of Object.keys(expected)) {
|
|
334
|
+
const keyPath = path ? `${path}.${key}` : key;
|
|
335
|
+
if (!(key in actual)) {
|
|
336
|
+
issues.push({ code: 'MISSING_CONFIGURATION_KEY', path: keyPath, message: 'is required' });
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
visit(/** @type {Record<string, unknown>} */ (expected)[key], /** @type {Record<string, unknown>} */ (actual)[key], keyPath);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @param {unknown} values
|
|
346
|
+
* @param {string} path
|
|
347
|
+
* @param {{ code: string; path: string; message: string; value?: unknown }[]} issues
|
|
348
|
+
*/
|
|
349
|
+
function validateFormats(values, path, issues) {
|
|
350
|
+
if (!Array.isArray(values)) {
|
|
351
|
+
issues.push({ code: 'INVALID_FORMATS', path, message: 'must be an array', value: values });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
for (const value of values) {
|
|
355
|
+
if (!formats.has(value)) {
|
|
356
|
+
issues.push({ code: 'UNSUPPORTED_FORMAT', path, message: `unsupported format ${JSON.stringify(value)}`, value });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {unknown} values
|
|
363
|
+
* @param {string} path
|
|
364
|
+
* @param {{ code: string; path: string; message: string; value?: unknown }[]} issues
|
|
365
|
+
*/
|
|
366
|
+
function validateStrings(values, path, issues) {
|
|
367
|
+
if (!Array.isArray(values)) {
|
|
368
|
+
issues.push({ code: 'INVALID_LIST', path, message: 'must be an array', value: values });
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
for (const value of values) {
|
|
372
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
373
|
+
issues.push({ code: 'INVALID_LIST_VALUE', path, message: 'must contain non-empty strings', value });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** @template T @param {T[]} values @returns {T[]} */
|
|
379
|
+
function unique(values) {
|
|
380
|
+
return [...new Set(values)];
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** @param {string} value */
|
|
384
|
+
function toCamelCase(value) {
|
|
385
|
+
return value.replace(/-([a-z])/g, (_, character) => character.toUpperCase());
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** @template T @param {T} value @returns {T} */
|
|
389
|
+
function deepFreeze(value) {
|
|
390
|
+
if (typeof value !== 'object' || value == null || Object.isFrozen(value)) return value;
|
|
391
|
+
for (const nested of Object.values(value)) {
|
|
392
|
+
deepFreeze(nested);
|
|
393
|
+
}
|
|
394
|
+
return Object.freeze(value);
|
|
395
|
+
}
|