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