pkgbld 1.35.1 → 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 +28 -63
- 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 -78
- package/dist/index.mjs +0 -1617
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pkgbld
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
_Build your libraries with ease_
|
|
4
4
|
|
|
5
5
|
Rollup-based build tool for building libraries based on package.json config and simple CLI options.
|
|
6
6
|
|
|
@@ -15,6 +15,7 @@ It is created to easily build libraries that contain multiple subpath exports (e
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
Using npm:
|
|
18
|
+
|
|
18
19
|
```
|
|
19
20
|
npm install --save-dev pkgbld
|
|
20
21
|
```
|
|
@@ -23,7 +24,7 @@ npm install --save-dev pkgbld
|
|
|
23
24
|
|
|
24
25
|
1. Start by creating package.json using `npm init`
|
|
25
26
|
2. Add pkgbld `npm install --save-dev pkgbld`
|
|
26
|
-
3. Create `src/index.
|
|
27
|
+
3. Create `src/index.js`
|
|
27
28
|
4. Add pkgbld in the 'scripts' field of your package.json like:
|
|
28
29
|
|
|
29
30
|
```json
|
|
@@ -34,6 +35,10 @@ npm install --save-dev pkgbld
|
|
|
34
35
|
|
|
35
36
|
Run `npm run build`.
|
|
36
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
|
+
|
|
37
42
|
## package.json
|
|
38
43
|
|
|
39
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.
|
|
@@ -194,76 +199,36 @@ pkgbld --no-exports
|
|
|
194
199
|
|
|
195
200
|
Do not add exports field in package.json.
|
|
196
201
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
pkgbld prune
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
prune devDependencies and redundant scripts from package.json
|
|
204
|
-
|
|
205
|
-
### prune --profile=<profile>
|
|
206
|
-
|
|
207
|
-
There are two profiles: `library` and `app`. `library` is default.
|
|
208
|
-
|
|
209
|
-
Right now it only affects how `prune` command removes entries in the `scripts` field.
|
|
210
|
-
|
|
211
|
-
For `library` profile it retains: 'preinstall', 'install', 'postinstall', 'prepublish', 'preprepare', 'prepare', 'postprepare'.
|
|
212
|
-
|
|
213
|
-
For `app` profile it retains in addition: 'prestart', 'start', 'poststart', 'prerestart', 'restart', 'postrestart', 'prestop', 'stop', 'poststop', 'pretest', 'test', 'posttest'.
|
|
214
|
-
|
|
215
|
-
### flatten
|
|
216
|
-
|
|
217
|
-
```
|
|
218
|
-
pkgbld prune --flatten=<directory>
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Flattens file structure by moving all files from `dist` or other directory to the root directory and updating package.json.
|
|
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.
|
|
222
204
|
|
|
223
|
-
|
|
205
|
+
## Build plugin interface
|
|
224
206
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
pkgbld prune --remove-sourcemaps
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Removes all sourcemaps from the package. The logic is very simple and removes all files with `.map` extension and references in format `//# sourceMappingURL=<mapFile>`.
|
|
234
|
-
|
|
235
|
-
### optimizeFiles (default)
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
pkgbld prune --optimize-files=false
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Optimizes files by removing all files that are not required for pack at the given moment.
|
|
242
|
-
|
|
243
|
-
You might want to disable this option in some edge cases.
|
|
244
|
-
|
|
245
|
-
### removeLegalComments
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
pkgbld prune --remove-legal-comments --compress=es,cjs
|
|
249
|
-
```
|
|
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-`.
|
|
250
210
|
|
|
251
|
-
|
|
211
|
+
Plugins implement one or more lifecycle methods on the object returned by the plugin module's `create()` function.
|
|
252
212
|
|
|
253
|
-
|
|
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.
|
|
254
214
|
|
|
255
|
-
|
|
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.
|
|
256
216
|
|
|
257
|
-
|
|
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()`.
|
|
258
218
|
|
|
259
219
|
```typescript
|
|
260
220
|
interface PkgbldPlugin {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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>;
|
|
267
232
|
}
|
|
268
233
|
```
|
|
269
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
|
+
}
|