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.
@@ -0,0 +1,237 @@
1
+ import path from 'node:path';
2
+
3
+ import { createLogger, LogLevel } from '@niceties/logger';
4
+
5
+ import { resolveBuildEntries } from './build-entries.js';
6
+
7
+ /**
8
+ * @typedef {import('type-fest').JsonObject} JsonObject
9
+ * @typedef {import('type-fest').JsonValue} JsonValue
10
+ * @typedef {import('type-fest').PackageJson} PackageJson
11
+ * @typedef {import('./types.js').BuildConfiguration} BuildConfiguration
12
+ * @typedef {import('./types.js').PackageProcessingResult} PackageProcessingResult
13
+ * @typedef {ReturnType<typeof import('./build-plugin-lifecycle.js').createBuildPluginLifecycle>} BuildPluginLifecycle
14
+ */
15
+
16
+ /** @type {Set<string>} */
17
+ const emptySet = new Set();
18
+ /**
19
+ * @param {JsonObject} pkg
20
+ * @param {BuildConfiguration} configuration
21
+ * @param {BuildPluginLifecycle} pluginLifecycle
22
+ * @returns {Promise<PackageProcessingResult>}
23
+ */
24
+ export async function processPackage(pkg, configuration, pluginLifecycle) {
25
+ const indexId = 'index';
26
+ const { outputs, packageJson, paths } = configuration;
27
+
28
+ const logger = createLogger();
29
+ /** @type {string[]} */
30
+ let executableOutputs = [];
31
+ const allowEsm = outputs.formats.includes('es');
32
+ const allowCjs = outputs.formats.includes('cjs');
33
+ const allowUmd = outputs.formats.includes('umd');
34
+
35
+ if (typeof pkg !== 'object' || Array.isArray(pkg) || pkg == null) {
36
+ logger.finish('expecting object on top level of package.json', LogLevel.error);
37
+ process.exit(-1);
38
+ }
39
+
40
+ if (typeof pkg.name !== 'string' && outputs.umdEntries.length > 0) {
41
+ logger.finish('expecting name to be a string in package.json', LogLevel.error);
42
+ process.exit(-1);
43
+ }
44
+
45
+ if (!Array.isArray(pkg.files)) {
46
+ pkg.files = [];
47
+ }
48
+
49
+ if (!pkg.files.includes(paths.outputDir)) {
50
+ /** @type {string[]} */ (pkg.files).push(paths.outputDir);
51
+ }
52
+
53
+ if (typeof pkg.scripts !== 'object' && pkg.scripts !== null) {
54
+ pkg.scripts = {};
55
+ }
56
+
57
+ if (packageJson.pack && !('prepack' in /** @type {Record<string, JsonObject>} */ (pkg.scripts))) {
58
+ /** @type {Record<string, JsonValue>} */ (pkg.scripts).prepack = 'pkgprn';
59
+ }
60
+
61
+ /** @type {string[]} */
62
+ const entryNames = [];
63
+ if (packageJson.exports) {
64
+ if (typeof pkg.exports === 'object' && pkg.exports != null && !Array.isArray(pkg.exports)) {
65
+ for (const id in pkg.exports) {
66
+ if (id !== './package.json') entryNames.push(exportIdToEntryName(id));
67
+ }
68
+ }
69
+ if (!entryNames.includes(indexId)) entryNames.unshift(indexId);
70
+ } else {
71
+ entryNames.push(indexId);
72
+ }
73
+
74
+ const entries = await resolveBuildEntries(entryNames, configuration, contributions =>
75
+ pluginLifecycle.contributeEntries(contributions, configuration)
76
+ );
77
+ const indexEntry = entries.require(indexId);
78
+
79
+ if (allowEsm && !allowCjs && typeof pkg.type !== 'string') {
80
+ pkg.type = 'module';
81
+ }
82
+
83
+ const exportsFields = new Set([
84
+ 'svelte',
85
+ pkg.type === 'module' ? 'require' : 'import',
86
+ pkg.type === 'module' ? 'import' : 'require',
87
+ 'default',
88
+ ]);
89
+
90
+ if (allowUmd && typeof pkg.umd === 'string') {
91
+ if (outputs.umdEntries.includes(indexId)) {
92
+ pkg.umd = /** @type {string} */ (indexEntry.outputPaths.umd);
93
+ }
94
+ }
95
+
96
+ if (allowCjs) {
97
+ pkg.main = /** @type {string} */ (indexEntry.outputPaths.cjs);
98
+ }
99
+
100
+ if (allowEsm && !allowCjs) {
101
+ pkg.main = /** @type {string} */ (indexEntry.outputPaths.es);
102
+ }
103
+
104
+ if (allowCjs && allowEsm && typeof pkg.module !== 'string') {
105
+ pkg.module = /** @type {string} */ (indexEntry.outputPaths.es);
106
+ }
107
+
108
+ if (allowUmd && outputs.umdEntries.includes(indexId)) {
109
+ pkg.unpkg = /** @type {string} */ (indexEntry.outputPaths.umd);
110
+ }
111
+
112
+ if (packageJson.exports) {
113
+ if (typeof pkg.exports !== 'object' || pkg.exports == null || Array.isArray(pkg.exports)) {
114
+ pkg.exports = {};
115
+ }
116
+ if (/** @type {Record<string, JsonValue>} */ (pkg.exports)['.'] == null) {
117
+ /** @type {Record<string, JsonValue>} */ (pkg.exports)['.'] = {};
118
+ }
119
+ /** @type {Record<string, JsonValue>} */ (pkg.exports)['./package.json'] = './package.json';
120
+
121
+ if (
122
+ allowCjs &&
123
+ pkg.main !== /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)['.']).require
124
+ ) {
125
+ /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)['.']).require =
126
+ /** @type {JsonValue} */ (pkg.main);
127
+ }
128
+
129
+ if (
130
+ pkg.module !== /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)['.'])?.default
131
+ ) {
132
+ /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)['.']).default =
133
+ /** @type {JsonValue} */ (pkg.module);
134
+ }
135
+
136
+ for (const id in /** @type {object} */ (pkg.exports)) {
137
+ if (id === './package.json') continue;
138
+
139
+ const basename = exportIdToEntryName(id);
140
+ const entry = entries.require(basename);
141
+
142
+ if (typeof (/** @type {Record<string, JsonValue>} */ (pkg.exports)[id]) !== 'object') {
143
+ /** @type {Record<string, JsonValue>} */ (pkg.exports)[id] = {};
144
+ }
145
+
146
+ const cjsFieldName = pkg.type === 'module' ? 'require' : 'default';
147
+ const esmFieldName = pkg.type === 'module' ? 'default' : 'import';
148
+
149
+ if (allowEsm) {
150
+ /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)[id])[esmFieldName] =
151
+ /** @type {string} */ (entry.outputPaths.es);
152
+ }
153
+
154
+ if (allowCjs) {
155
+ /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)[id])[cjsFieldName] =
156
+ /** @type {string} */ (entry.outputPaths.cjs);
157
+ }
158
+
159
+ /** @type {Record<string, JsonValue>} */ (pkg.exports)[id] = orderFields(
160
+ exportsFields,
161
+ /** @type {Record<string, JsonValue>} */ (/** @type {Record<string, JsonValue>} */ (pkg.exports)[id])
162
+ );
163
+ }
164
+ }
165
+
166
+ pluginLifecycle.processPackageJson(/** @type {PackageJson} */ (pkg), entries, configuration);
167
+
168
+ if (packageJson.executables.mode === 'explicit') {
169
+ executableOutputs = [...packageJson.executables.values];
170
+ if (executableOutputs.length > 0) {
171
+ pkg.bin = /** @type {string} */ (executableOutputs[0]);
172
+ }
173
+ } else if (packageJson.executables.mode === 'infer' && allowCjs && entries.values.length > 0) {
174
+ if (typeof pkg.bin === 'string') {
175
+ if (entries.values.some(entry => pkg.bin === entry.outputPaths.cjs)) {
176
+ executableOutputs = [pkg.bin];
177
+ }
178
+ } else if (typeof pkg.bin === 'object' && pkg.bin !== null) {
179
+ executableOutputs = /** @type {string[]} */ (
180
+ Object.values(pkg.bin).filter(
181
+ value => typeof value === 'string' && entries.values.some(entry => value === entry.outputPaths.cjs)
182
+ )
183
+ );
184
+ }
185
+ if (
186
+ typeof pkg.directories === 'object' &&
187
+ pkg.directories != null &&
188
+ 'bin' in pkg.directories &&
189
+ typeof pkg.directories.bin === 'string'
190
+ ) {
191
+ if (path.resolve(pkg.directories.bin) === path.resolve(paths.outputDir)) {
192
+ executableOutputs.push(...entries.values.flatMap(entry => (entry.outputPaths.cjs == null ? [] : [entry.outputPaths.cjs])));
193
+ executableOutputs = Array.from(new Set(executableOutputs));
194
+ }
195
+ }
196
+ }
197
+
198
+ return { entries, executableOutputs };
199
+ }
200
+
201
+ /**
202
+ * @param {string} id
203
+ */
204
+ function exportIdToEntryName(id) {
205
+ return id === '.' ? 'index' : id.replace(/^\.\//, '').replaceAll('\\', '/');
206
+ }
207
+
208
+ /**
209
+ * @template {object} T
210
+ * @param {Set<string>} firstFields
211
+ * @param {T} exports
212
+ * @param {Set<string>} [lastFields]
213
+ * @returns {T}
214
+ */
215
+ function orderFields(firstFields, exports, lastFields = emptySet) {
216
+ const ordered = /** @type {T} */ ({});
217
+
218
+ for (const key of firstFields) {
219
+ if (/** @type {keyof T} */ (key) in exports) {
220
+ /** @type {Record<string, unknown>} */ (ordered)[key] = /** @type {Record<string, unknown>} */ (exports)[key];
221
+ }
222
+ }
223
+
224
+ for (const key in exports) {
225
+ if (!firstFields.has(key) && !lastFields.has(key)) {
226
+ /** @type {Record<string, unknown>} */ (ordered)[key] = /** @type {Record<string, unknown>} */ (exports)[key];
227
+ }
228
+ }
229
+
230
+ for (const key of lastFields) {
231
+ if (/** @type {keyof T} */ (key) in exports) {
232
+ /** @type {Record<string, unknown>} */ (ordered)[key] = /** @type {Record<string, unknown>} */ (exports)[key];
233
+ }
234
+ }
235
+
236
+ return ordered;
237
+ }
@@ -0,0 +1,80 @@
1
+ import path from 'node:path';
2
+
3
+ import { fastIsEqual } from 'fast-is-equal';
4
+
5
+ import { getJson } from './get-json.js';
6
+ import { writeJson } from './write-json.js';
7
+
8
+ /**
9
+ * @typedef {import('@niceties/logger').Logger} Logger
10
+ * @typedef {import('type-fest').JsonObject} JsonObject
11
+ * @typedef {import('./types.js').BuildConfiguration} BuildConfiguration
12
+ * @typedef {ReturnType<typeof import('./build-plugin-lifecycle.js').createBuildPluginLifecycle>} BuildPluginLifecycle
13
+ */
14
+
15
+ /**
16
+ * @param {string} sourceDir
17
+ * @returns {Record<string, unknown>}
18
+ */
19
+ function createDefaultTsConfig(sourceDir) {
20
+ return {
21
+ include: [sourceDir, 'types'],
22
+ compilerOptions: {
23
+ lib: ['dom', 'esnext'],
24
+ target: 'esnext',
25
+ module: 'esnext',
26
+ esModuleInterop: true,
27
+ allowJs: true,
28
+ skipLibCheck: true,
29
+ strict: true,
30
+ sourceMap: true,
31
+ noUncheckedIndexedAccess: true,
32
+ declaration: true,
33
+ moduleResolution: 'bundler',
34
+ rootDir: `./${sourceDir}`,
35
+ },
36
+ };
37
+ }
38
+
39
+ /**
40
+ * @param {BuildConfiguration} configuration
41
+ * @param {Logger} mainLogger
42
+ * @param {BuildPluginLifecycle} pluginLifecycle
43
+ * @returns {Promise<JsonObject | undefined>}
44
+ */
45
+ export async function checkTsConfig(configuration, mainLogger, pluginLifecycle) {
46
+ if (!configuration.typescript.updateConfig) {
47
+ return;
48
+ }
49
+ /** @type {JsonObject | undefined} */
50
+ let config,
51
+ needWrite = false;
52
+ try {
53
+ [, config] = await getJson('tsconfig.json');
54
+ } catch {
55
+ /*ignore*/
56
+ }
57
+ try {
58
+ [, config] = await getJson('jsconfig.json');
59
+ if (config && typeof config === 'object' && !Array.isArray(config)) {
60
+ config.allowJs = true;
61
+ }
62
+ } catch {
63
+ /*ignore*/
64
+ }
65
+ if (!config) {
66
+ config = /** @type {JsonObject} */ (createDefaultTsConfig(configuration.paths.sourceDir || 'src'));
67
+ needWrite = true;
68
+ }
69
+ const originalConfig = structuredClone(config);
70
+ pluginLifecycle.processTsConfig(config, configuration);
71
+ if (!fastIsEqual(originalConfig, config)) {
72
+ needWrite = true;
73
+ }
74
+ if (needWrite) {
75
+ mainLogger('no tsconfig.json or jsconfig.json and --no-ts-config not specified, writing tsconfig...');
76
+ await writeJson(path.resolve('tsconfig.json'), config);
77
+ mainLogger('done');
78
+ }
79
+ return config;
80
+ }
@@ -0,0 +1 @@
1
+ declare module 'rollup-plugin-preprocess';
package/src/types.js ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @typedef {import('rollup').InternalModuleFormat} InternalModuleFormat
3
+ * @typedef {import('rollup').OutputOptions} OutputOptions
4
+ * @typedef {import('rollup').Plugin} Plugin
5
+ * @typedef {import('type-fest').JsonObject} JsonObject
6
+ * @typedef {import('type-fest').PackageJson} PackageJson
7
+ */
8
+
9
+ /** @typedef {null | string | number | boolean | JsonArray | JsonRecord} Json */
10
+ /** @typedef {Json[]} JsonArray */
11
+ /** @typedef {{ [name: string]: Json }} JsonRecord */
12
+
13
+ /**
14
+ * @typedef {(
15
+ * factory: () => Plugin,
16
+ * priority: number,
17
+ * options?: {
18
+ * format?: InternalModuleFormat | InternalModuleFormat[];
19
+ * inputs?: string[];
20
+ * outputPlugin?: true;
21
+ * }
22
+ * ) => void} ProvideFunction
23
+ */
24
+
25
+ /**
26
+ * @typedef {{
27
+ * provide: ProvideFunction;
28
+ * import: (module: string, exportName?: string) => Promise<(...args: unknown[]) => Plugin>;
29
+ * globalImport: (module: string, exportName?: string | string[]) => void;
30
+ * globalSetup: (code: ((...args: any[]) => any) | string) => ((...args: any[]) => any) | undefined;
31
+ * }} Provider
32
+ */
33
+
34
+ /**
35
+ * @typedef {{
36
+ * plugin: () => Plugin;
37
+ * priority: number;
38
+ * format?: InternalModuleFormat | InternalModuleFormat[];
39
+ * inputs?: string[];
40
+ * outputPlugin?: true;
41
+ * }} PkgbldRollupPlugin
42
+ */
43
+
44
+ /** @typedef {'infer' | 'disabled' | 'explicit'} ExecutableMode */
45
+ /** @typedef {'es' | 'cjs' | 'umd'} BuildFormat */
46
+ /** @typedef {'ts' | 'tsx' | 'js' | 'jsx' | 'cjs' | 'mjs'} BuildEntryExtension */
47
+
48
+ /**
49
+ * @typedef {{
50
+ * paths: { sourceDir: string; outputDir: string };
51
+ * outputs: {
52
+ * formats: BuildFormat[];
53
+ * patterns: { es: string; cjs: string; umd: string };
54
+ * umdEntries: string[];
55
+ * sourcemaps: BuildFormat[];
56
+ * };
57
+ * transforms: {
58
+ * compress: BuildFormat[];
59
+ * preprocess: string[];
60
+ * includeExternals: boolean | string[];
61
+ * removeLegalComments: boolean;
62
+ * };
63
+ * packageJson: {
64
+ * update: boolean;
65
+ * format: boolean;
66
+ * exports: boolean;
67
+ * pack: boolean;
68
+ * executables: { mode: ExecutableMode; values: string[] };
69
+ * };
70
+ * typescript: { updateConfig: boolean };
71
+ * execution: { clean: boolean; bundle: boolean; eject: boolean };
72
+ * }} BuildConfigurationDraft
73
+ */
74
+
75
+ /**
76
+ * @typedef {Readonly<{
77
+ * paths: Readonly<{ sourceDir: string; outputDir: string }>;
78
+ * outputs: Readonly<{
79
+ * formats: readonly BuildFormat[];
80
+ * patterns: Readonly<{ es: string; cjs: string; umd: string }>;
81
+ * umdEntries: readonly string[];
82
+ * sourcemaps: readonly BuildFormat[];
83
+ * }>;
84
+ * transforms: Readonly<{
85
+ * compress: readonly BuildFormat[];
86
+ * preprocess: readonly string[];
87
+ * includeExternals: boolean | readonly string[];
88
+ * removeLegalComments: boolean;
89
+ * }>;
90
+ * packageJson: Readonly<{
91
+ * update: boolean;
92
+ * format: boolean;
93
+ * exports: boolean;
94
+ * pack: boolean;
95
+ * executables: Readonly<{ mode: ExecutableMode; values: readonly string[] }>;
96
+ * }>;
97
+ * typescript: Readonly<{ updateConfig: boolean }>;
98
+ * execution: Readonly<{ clean: boolean; bundle: boolean; eject: boolean }>;
99
+ * }>} BuildConfiguration
100
+ */
101
+
102
+ /** @typedef {Record<string, string | number | string[] | number[] | boolean | undefined>} ParsedOptions */
103
+
104
+ /**
105
+ * @typedef {Readonly<{
106
+ * defaults: BuildConfiguration;
107
+ * package: Readonly<PackageJson>;
108
+ * cli: Readonly<{
109
+ * values: Readonly<ParsedOptions>;
110
+ * provided: Readonly<Record<string, boolean>>;
111
+ * }>;
112
+ * }>} BuildConfigurationSources
113
+ */
114
+
115
+ /** @typedef {Map<unknown, unknown>} PluginSharedState */
116
+
117
+ /**
118
+ * @typedef {Readonly<{
119
+ * name: string;
120
+ * sourcePath: string;
121
+ * extension: BuildEntryExtension;
122
+ * outputPaths: Readonly<Partial<Record<BuildFormat, string>>>;
123
+ * }>} BuildEntry
124
+ */
125
+
126
+ /** @typedef {{ name: string; sourcePath?: string }} BuildEntryContribution */
127
+ /** @typedef {{ add(contribution: BuildEntryContribution): void }} BuildEntryContributions */
128
+ /** @typedef {{ code: 'INVALID_BUILD_ENTRY_NAME' | 'DUPLICATE_BUILD_ENTRY' | 'SOURCE_NOT_FOUND' | 'SELECTED_BUILD_ENTRY_NOT_FOUND' | 'OUTPUT_PATH_COLLISION'; path: string; message: string; name?: string }} BuildEntryIssue */
129
+
130
+ /**
131
+ * @typedef {Readonly<{
132
+ * values: readonly BuildEntry[];
133
+ * require(name: string): BuildEntry;
134
+ * }>} BuildEntries
135
+ */
136
+
137
+ /**
138
+ * @typedef {Readonly<{
139
+ * entries: BuildEntries;
140
+ * executableOutputs: readonly string[];
141
+ * }>} PackageProcessingResult
142
+ */
143
+
144
+ /** @typedef {{ draft: BuildConfigurationDraft; sources: BuildConfigurationSources; shared: PluginSharedState }} PluginConfigureContext */
145
+ /** @typedef {{ entries: BuildEntryContributions; configuration: BuildConfiguration; shared: PluginSharedState }} PluginContributeEntriesContext */
146
+ /** @typedef {{ packageJson: PackageJson; entries: BuildEntries; configuration: BuildConfiguration; shared: PluginSharedState }} PluginPackageContext */
147
+ /** @typedef {{ config: JsonObject; configuration: BuildConfiguration; shared: PluginSharedState }} PluginTsConfigContext */
148
+ /** @typedef {{ provider: Provider; configuration: BuildConfiguration; packageResult: PackageProcessingResult; shared: PluginSharedState }} PluginRollupContext */
149
+ /** @typedef {{ format: InternalModuleFormat; inputs: string[]; configuration: BuildConfiguration; shared: PluginSharedState }} PluginOutputContext */
150
+ /** @typedef {{ configuration: BuildConfiguration; shared: PluginSharedState }} PluginBuildEndContext */
151
+
152
+ /**
153
+ * @typedef {{
154
+ * create(): Promise<Partial<PkgbldPlugin>>;
155
+ * }} PkgbldPluginFactory
156
+ */
157
+
158
+ /**
159
+ * @typedef {{
160
+ * configure(context: PluginConfigureContext): void;
161
+ * contributeEntries(context: PluginContributeEntriesContext): void;
162
+ * processPackageJson(context: PluginPackageContext): void;
163
+ * processTsConfig(context: PluginTsConfigContext): void;
164
+ * providePlugins(context: PluginRollupContext): Promise<void>;
165
+ * getExtraOutputSettings(context: PluginOutputContext): Partial<OutputOptions>;
166
+ * buildEnd(context: PluginBuildEndContext): Promise<void>;
167
+ * }} PkgbldPlugin
168
+ */
169
+
170
+ export {};
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs/promises';
2
+
3
+ import { toFormattedJson } from './options/index.js';
4
+
5
+ /**
6
+ * @typedef {import('type-fest').JsonObject} JsonObject
7
+ */
8
+
9
+ /**
10
+ * @param {string} path
11
+ * @param {JsonObject} json
12
+ */
13
+ export async function writeJson(path, json) {
14
+ let current;
15
+ try {
16
+ current = await fs.readFile(path, 'utf8');
17
+ } catch (/** @type {any} */ error) {
18
+ if (error.code !== 'ENOENT') throw error;
19
+ }
20
+ await fs.writeFile(path, toFormattedJson(json, current));
21
+ }