quickbundle 2.15.0 → 2.16.0-next-dfc95be

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.
Files changed (2) hide show
  1. package/dist/index.js +464 -561
  2. package/package.json +9 -14
package/dist/index.js CHANGED
@@ -1,603 +1,506 @@
1
- import { helpers, termost } from 'termost';
2
- import { finished } from 'node:stream/promises';
3
- import { Readable } from 'node:stream';
4
- import { resolve, dirname, join, basename } from 'node:path';
5
- import { copyFile as copyFile$1, rename, mkdir, writeFile as writeFile$1, rm, readFile as readFile$1 } from 'node:fs/promises';
6
- import { createWriteStream } from 'node:fs';
7
- import decompress from 'decompress';
8
- import { watch as watch$1, rollup } from 'rollup';
9
- import { createRequire } from 'node:module';
10
- import { swc } from 'rollup-plugin-swc3';
11
- import externals from 'rollup-plugin-node-externals';
12
- import dts from 'rollup-plugin-dts';
13
- import url from '@rollup/plugin-url';
14
- import { nodeResolve } from '@rollup/plugin-node-resolve';
15
- import json from '@rollup/plugin-json';
16
- import commonjs from '@rollup/plugin-commonjs';
17
- import os from 'node:os';
18
- import { gzipSize } from 'gzip-size';
1
+ import { helpers, termost } from "termost";
2
+ import { gzipSize } from "gzip-size";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { rolldown, watch } from "rolldown";
5
+ import url from "@rollup/plugin-url";
6
+ import { createRequire } from "node:module";
7
+ import { dts } from "rolldown-plugin-dts";
8
+ import externals from "rollup-plugin-node-externals";
9
+ import decompress from "decompress";
10
+ import { createWriteStream } from "node:fs";
11
+ import { copyFile, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
12
+ import { Readable } from "node:stream";
13
+ import { finished } from "node:stream/promises";
14
+ import os from "node:os";
19
15
 
20
- /**
21
- * Resolve a relative path from the Quickbundle node modules directory.
22
- * @param paths - Relative paths.
23
- * @returns The resolved absolute path.
24
- * @example
25
- * resolveFromInternalDirectory("dist", "node");
26
- */ const resolveFromInternalDirectory = (...paths)=>{
27
- return resolve(import.meta.dirname, "../", ...paths);
16
+ //#region package.json
17
+ var name = "quickbundle";
18
+ var version = "2.16.0-next-dfc95be";
19
+
20
+ //#endregion
21
+ //#region src/bundler/build.ts
22
+ const build = async (input) => {
23
+ process.env.NODE_ENV ??= "production";
24
+ const { data: configurations } = input;
25
+ const output = [];
26
+ for (const config of configurations) {
27
+ const initialTime = Date.now();
28
+ const bundle = await rolldown(config);
29
+ if (config.output) {
30
+ const outputEntries = Array.isArray(config.output) ? config.output : [config.output];
31
+ const promises = [];
32
+ for (const outputEntry of outputEntries) {
33
+ const entryFileName = outputEntry.entryFileNames;
34
+ const outputFilePath = join(outputEntry.dir ?? "", typeof entryFileName === "string" ? entryFileName : "");
35
+ if (!outputFilePath) throw new Error("Misconfigured file entry point. Make sure to define an `import`, `require`, or `default` field.");
36
+ promises.push(new Promise((resolve, reject) => {
37
+ bundle.write(outputEntry).then(() => {
38
+ resolve({
39
+ elapsedTime: Date.now() - initialTime,
40
+ filePath: outputFilePath
41
+ });
42
+ }).catch((error) => {
43
+ if (error instanceof Error) reject(error);
44
+ });
45
+ }));
46
+ }
47
+ output.push(...await Promise.all(promises));
48
+ }
49
+ }
50
+ return output;
28
51
  };
52
+
53
+ //#endregion
54
+ //#region src/helpers.ts
29
55
  /**
30
- * Resolve a relative path from the current working project directory.
31
- * @param paths - Relative paths.
32
- * @returns The resolved absolute path.
33
- * @example
34
- * resolveFromExternalDirectory("package.json");
35
- */ const resolveFromExternalDirectory = (...paths)=>{
36
- return resolve(process.cwd(), ...paths);
56
+ * Resolve a relative path from the Quickbundle node modules directory.
57
+ * @param paths - Relative paths.
58
+ * @returns The resolved absolute path.
59
+ * @example
60
+ * resolveFromInternalDirectory("dist", "node");
61
+ */
62
+ const resolveFromInternalDirectory = (...paths) => {
63
+ return resolve(import.meta.dirname, "../", ...paths);
37
64
  };
38
- const createRegExpMatcher = (regex)=>{
39
- return (value)=>{
40
- return regex.exec(value)?.groups;
41
- };
42
- };
43
- const createDirectory = async (path)=>{
44
- await mkdir(path, {
45
- recursive: true
46
- });
65
+ /**
66
+ * Resolve a relative path from the current working project directory.
67
+ * @param paths - Relative paths.
68
+ * @returns The resolved absolute path.
69
+ * @example
70
+ * resolveFromExternalDirectory("package.json");
71
+ */
72
+ const resolveFromExternalDirectory = (...paths) => {
73
+ return resolve(process.cwd(), ...paths);
47
74
  };
48
- const copyFile = async (fromPath, toPath)=>{
49
- await createDirectory(dirname(toPath));
50
- await copyFile$1(fromPath, toPath);
75
+ const createRegExpMatcher = (regex) => {
76
+ return (value) => {
77
+ return regex.exec(value)?.groups;
78
+ };
51
79
  };
52
- const removePath = async (path)=>{
53
- await rm(path, {
54
- force: true,
55
- recursive: true
56
- });
80
+ const createDirectory = async (path) => {
81
+ await mkdir(path, { recursive: true });
57
82
  };
58
- const readFile = async (filePath)=>{
59
- return readFile$1(filePath);
83
+ const copyFile$1 = async (fromPath, toPath) => {
84
+ await createDirectory(dirname(toPath));
85
+ await copyFile(fromPath, toPath);
60
86
  };
61
- const writeFile = async (filePath, content)=>{
62
- await createDirectory(dirname(filePath));
63
- await writeFile$1(filePath, content, "utf8");
87
+ const removePath = async (path) => {
88
+ await rm(path, {
89
+ force: true,
90
+ recursive: true
91
+ });
64
92
  };
65
- const download = async (url, filePath)=>{
66
- await createDirectory(dirname(filePath));
67
- const { body, ok, status, statusText } = await fetch(url);
68
- if (!ok) {
69
- throw new Error(`An error ocurred while downloading \`${url}\`. Received \`${status}\` status code with the following message \`${statusText}\`.`);
70
- }
71
- if (!body) {
72
- throw new Error(`Empty body received while downloading \`${url}\`.`);
73
- }
74
- await finished(Readable.fromWeb(body).pipe(createWriteStream(filePath)));
93
+ const readFile$1 = async (filePath) => {
94
+ return readFile(filePath);
75
95
  };
76
- const unzip = async (input, output)=>{
77
- const { targetedArchivePath } = input;
78
- const { directoryPath } = output;
79
- await decompress(input.path, directoryPath, {
80
- filter (file) {
81
- return file.path === targetedArchivePath;
82
- }
83
- });
84
- await rename(join(directoryPath, targetedArchivePath), join(directoryPath, output.filename));
96
+ const writeFile$1 = async (filePath, content) => {
97
+ await createDirectory(dirname(filePath));
98
+ await writeFile(filePath, content, "utf8");
85
99
  };
86
- const createCommand = (program, input)=>{
87
- return program.command(input).option({
88
- key: "minification",
89
- name: "minification",
90
- description: "Enable minification",
91
- defaultValue: false
92
- }).option({
93
- key: "sourceMaps",
94
- name: "source-maps",
95
- description: "Enable source maps generation",
96
- defaultValue: false
97
- });
100
+ const download = async (url, filePath) => {
101
+ await createDirectory(dirname(filePath));
102
+ const { body, ok, status, statusText } = await fetch(url);
103
+ if (!ok) throw new Error(`An error ocurred while downloading \`${url}\`. Received \`${status}\` status code with the following message \`${statusText}\`.`);
104
+ if (!body) throw new Error(`Empty body received while downloading \`${url}\`.`);
105
+ await finished(Readable.fromWeb(body).pipe(createWriteStream(filePath)));
98
106
  };
99
-
100
- const onLog = (_, log)=>{
101
- if (log.message.includes("Generated an empty chunk")) return;
107
+ const unzip = async (input, output) => {
108
+ const { targetedArchivePath } = input;
109
+ const { directoryPath } = output;
110
+ await decompress(input.path, directoryPath, { filter(file) {
111
+ return file.path === targetedArchivePath;
112
+ } });
113
+ await rename(join(directoryPath, targetedArchivePath), join(directoryPath, output.filename));
102
114
  };
103
- const isRecord = (value)=>{
104
- return typeof value === "object" && value !== null && !Array.isArray(value);
115
+ const createCommand = (program, input) => {
116
+ return program.command(input).option({
117
+ defaultValue: false,
118
+ description: "Enable minification",
119
+ key: "minification",
120
+ name: "minification"
121
+ }).option({
122
+ defaultValue: false,
123
+ description: "Enable source maps generation",
124
+ key: "sourceMaps",
125
+ name: "source-maps"
126
+ });
105
127
  };
106
128
 
107
- const watch = (input)=>{
108
- process.env.NODE_ENV ??= "development";
109
- const watcher = watch$1(input.data.map((configItem)=>({
110
- ...configItem,
111
- onLog
112
- })));
113
- let startDuration;
114
- console.clear();
115
- watcher.on("event", async (event)=>{
116
- switch(event.code){
117
- case "START":
118
- {
119
- startDuration = Date.now();
120
- clearLog("Build in progress…", {
121
- type: "information"
122
- });
123
- return;
124
- }
125
- case "BUNDLE_END":
126
- {
127
- await event.result.close();
128
- break;
129
- }
130
- case "END":
131
- {
132
- const duration = Date.now() - startDuration;
133
- clearLog(`Build done in ${duration}ms (at ${new Date().toLocaleTimeString()})`, {
134
- type: "success"
135
- });
136
- return;
137
- }
138
- case "ERROR":
139
- {
140
- const { error } = event;
141
- clearLog(error.message, {
142
- type: "error"
143
- });
144
- console.error("\n", error);
145
- return;
146
- }
147
- }
148
- });
149
- };
150
- const clearLog = (...input)=>{
151
- console.clear();
152
- helpers.message(...input);
129
+ //#endregion
130
+ //#region src/bundler/helpers.ts
131
+ const isRecord = (value) => {
132
+ return typeof value === "object" && value !== null && !Array.isArray(value);
153
133
  };
154
134
 
155
- const require = createRequire(import.meta.url);
156
- const PKG = require(resolveFromExternalDirectory("package.json"));
135
+ //#endregion
136
+ //#region src/bundler/config.ts
137
+ const PKG = createRequire(import.meta.url)(resolveFromExternalDirectory("package.json"));
157
138
  const DEFAULT_OPTIONS = {
158
- minification: false,
159
- sourceMaps: false,
160
- standalone: false
139
+ minification: false,
140
+ sourceMaps: false,
141
+ standalone: false
161
142
  };
162
- const createConfiguration = (options = DEFAULT_OPTIONS)=>{
163
- const buildableExports = getBuildableExports(options);
164
- return {
165
- data: buildableExports.flatMap((buildableExport)=>{
166
- return [
167
- buildableExport.source && createMainConfig({
168
- ...buildableExport,
169
- source: buildableExport.source
170
- }, options),
171
- buildableExport.source && buildableExport.types && createTypesConfig({
172
- source: buildableExport.source,
173
- types: buildableExport.types
174
- }, options)
175
- ].filter(Boolean);
176
- }),
177
- metadata: buildableExports
178
- };
143
+ const createConfiguration = (options = DEFAULT_OPTIONS) => {
144
+ const buildableExports = getBuildableExports(options);
145
+ return {
146
+ data: buildableExports.flatMap((buildableExport) => {
147
+ return [buildableExport.source && createMainConfig({
148
+ ...buildableExport,
149
+ source: buildableExport.source
150
+ }, options), buildableExport.source && buildableExport.types && createTypesConfig({
151
+ source: buildableExport.source,
152
+ types: buildableExport.types
153
+ }, options)].filter(Boolean);
154
+ }),
155
+ metadata: buildableExports
156
+ };
179
157
  };
180
- // eslint-disable-next-line sonarjs/cyclomatic-complexity
181
- const getBuildableExports = ({ standalone })=>{
182
- if (standalone) {
183
- /**
184
- * Entry-point resolution invariants for standalone target (mostly binaries).
185
- */ if (!PKG.source || !PKG.bin || !PKG.name) {
186
- throw new Error("Invalid package entry points contract. Standalone compilation is enabled but required fields are missing. Make sure to set `name`, `source`, and `bin` fields.");
187
- }
188
- const bin = PKG.bin;
189
- const name = PKG.name;
190
- const source = PKG.source;
191
- if (isRecord(bin)) {
192
- return Object.entries(bin).map((data)=>({
193
- bin: data[0],
194
- require: data[1],
195
- source
196
- }));
197
- }
198
- return [
199
- {
200
- // For scoped packages and if the `bin` is defined with a string value, the [scope name is discarded](the scope name is discarded when creating a binary) when creating a binary.
201
- bin: name.replace(/^(@.*?\/)/, ""),
202
- require: bin,
203
- source
204
- }
205
- ];
206
- }
207
- /**
208
- * Entry-point resolution invariants for non-standalone target (mostly libraries):
209
- * Following the [package entry-point specification](https://nodejs.org/api/packages.html#package-entry-points),
210
- * whenever an export object is defined, it take precedence over other classical entry-point fields
211
- * (such as main, module, and types defined at the root package.json level).
212
- */ if (PKG.main || PKG.module || PKG.types || !PKG.exports) {
213
- throw new Error("Invalid package entry points contract. Use the recommended [`exports` field](https://nodejs.org/api/packages.html#package-entry-points) instead and, for TypeScript-based projects, update the `tsconfig.json` file to resolve it properly (`moduleResolution` must be set to `Bundler` (or `NodeNext`)).");
214
- }
215
- const buildableExportFields = [
216
- "default",
217
- "import",
218
- "require",
219
- "types"
220
- ];
221
- let singleExport = undefined;
222
- const output = Object.entries(PKG.exports).map(([field, value])=>{
223
- if (isRecord(value)) {
224
- return [
225
- field,
226
- value
227
- ];
228
- }
229
- if ([
230
- "source",
231
- ...buildableExportFields
232
- ].includes(field)) {
233
- if (!singleExport) {
234
- singleExport = {};
235
- singleExport[field] = value;
236
- return [
237
- ".",
238
- singleExport
239
- ];
240
- }
241
- singleExport[field] = value;
242
- }
243
- return undefined;
244
- }).reduce((buildableExports, currentExport)=>{
245
- if (!currentExport) return buildableExports;
246
- const [exportField, exportValue] = currentExport;
247
- const conditionalExportFields = Object.keys(exportValue);
248
- if (!conditionalExportFields.includes("source")) return buildableExports;
249
- const hasAtLeastOneRequiredField = buildableExportFields.some((entryPointField)=>conditionalExportFields.includes(entryPointField));
250
- if (hasAtLeastOneRequiredField) {
251
- buildableExports.push(exportValue);
252
- return buildableExports;
253
- }
254
- throw new Error(`A \`source\` field is defined without an output defined for the \`${exportField}\` export. Make sure to define at least one conditional entry point (including ${buildableExportFields.map((field)=>`\`${field}\``).join(", ")})`);
255
- }, []);
256
- if (output.length === 0) {
257
- throw new Error("No `source` field is set for the targeted package. If a build step is necessary, make sure to configure at least one `source` field in the package `exports` contract. If not, do not execute quickbundle on this package.");
258
- }
259
- return output;
158
+ const getBuildableExports = ({ standalone }) => {
159
+ if (standalone) {
160
+ /**
161
+ * Entry-point resolution invariants for standalone target (mostly binaries).
162
+ */
163
+ if (!PKG.source || !PKG.bin || !PKG.name) throw new Error("Invalid package entry points contract. Standalone compilation is enabled but required fields are missing. Make sure to set `name`, `source`, and `bin` fields.");
164
+ const bin = PKG.bin;
165
+ const name = PKG.name;
166
+ const source = PKG.source;
167
+ if (isRecord(bin)) return Object.entries(bin).map((data) => ({
168
+ bin: data[0],
169
+ require: data[1],
170
+ source
171
+ }));
172
+ return [{
173
+ bin: name.replace(/^(@.*?\/)/, ""),
174
+ require: bin,
175
+ source
176
+ }];
177
+ }
178
+ /**
179
+ * Entry-point resolution invariants for non-standalone target (mostly libraries):
180
+ * Following the [package entry-point specification](https://nodejs.org/api/packages.html#package-entry-points),
181
+ * whenever an export object is defined, it take precedence over other classical entry-point fields
182
+ * (such as main, module, and types defined at the root package.json level).
183
+ */
184
+ if (PKG.main || PKG.module || PKG.types || !PKG.exports) throw new Error("Invalid package entry points contract. Use the recommended [`exports` field](https://nodejs.org/api/packages.html#package-entry-points) instead and, for TypeScript-based projects, update the `tsconfig.json` file to resolve it properly (`moduleResolution` must be set to `Bundler` (or `NodeNext`)).");
185
+ const buildableExportFields = [
186
+ "default",
187
+ "import",
188
+ "require",
189
+ "types"
190
+ ];
191
+ let singleExport = void 0;
192
+ const output = Object.entries(PKG.exports).map(([field, value]) => {
193
+ if (isRecord(value)) return [field, value];
194
+ if (["source", ...buildableExportFields].includes(field)) {
195
+ if (!singleExport) {
196
+ singleExport = { [field]: value };
197
+ return [".", singleExport];
198
+ }
199
+ singleExport[field] = value;
200
+ }
201
+ }).reduce((buildableExports, currentExport) => {
202
+ if (!currentExport) return buildableExports;
203
+ const [exportField, exportValue] = currentExport;
204
+ const conditionalExportFields = Object.keys(exportValue);
205
+ if (!conditionalExportFields.includes("source")) return buildableExports;
206
+ if (buildableExportFields.some((entryPointField) => conditionalExportFields.includes(entryPointField))) {
207
+ buildableExports.push(exportValue);
208
+ return buildableExports;
209
+ }
210
+ throw new Error(`A \`source\` field is defined without an output defined for the \`${exportField}\` export. Make sure to define at least one conditional entry point (including ${buildableExportFields.map((field) => `\`${field}\``).join(", ")})`);
211
+ }, []);
212
+ if (output.length === 0) throw new Error("No `source` field is set for the targeted package. If a build step is necessary, make sure to configure at least one `source` field in the package `exports` contract. If not, do not execute quickbundle on this package.");
213
+ return output;
260
214
  };
261
- const getPlugins = (customPlugins, options)=>{
262
- return [
263
- !options.standalone && externals({
264
- builtins: true,
265
- deps: true,
266
- /**
267
- * As they're not installed consumer side, `devDependencies` are declared as internal dependencies (via the `false` value)
268
- * and bundled into the dist if and only if imported and not listed as `peerDependencies` (otherwise, they're considered external).
269
- */ devDeps: false,
270
- optDeps: true,
271
- peerDeps: true
272
- }),
273
- commonjs(),
274
- url(),
275
- json(),
276
- ...customPlugins
277
- ].filter(Boolean);
215
+ const getFileOutput = (filePath) => {
216
+ return {
217
+ dir: dirname(filePath),
218
+ entryFileNames: basename(filePath)
219
+ };
278
220
  };
279
- const createMainConfig = (entryPoints, options)=>{
280
- const { minification, sourceMaps } = options;
281
- const esmInput = entryPoints.import ?? entryPoints.default;
282
- if (entryPoints.import && entryPoints.default && entryPoints.import !== entryPoints.default) {
283
- throw new Error("Both `import` and `default` export fields have been defined but with different values. To preserve proper `default` field resolution on the consumer side (i.e. to target ESM format), make sure to provide the same file path for both fields.");
284
- }
285
- const output = [
286
- entryPoints.require && {
287
- file: entryPoints.require,
288
- format: "cjs",
289
- inlineDynamicImports: Boolean(options.standalone),
290
- sourcemap: sourceMaps
291
- },
292
- esmInput && {
293
- file: esmInput,
294
- format: "es",
295
- sourcemap: sourceMaps
296
- }
297
- ].filter(Boolean);
298
- return {
299
- input: entryPoints.source,
300
- output,
301
- plugins: getPlugins([
302
- nodeResolve(),
303
- swc({
304
- minify: minification,
305
- sourceMaps
306
- })
307
- ], options)
308
- };
221
+ const getPlugins = (options) => {
222
+ const output = [url()];
223
+ if (!options.standalone) output.push(externals({
224
+ builtins: true,
225
+ deps: true,
226
+ /**
227
+ * As they're not installed consumer side, `devDependencies` are declared as internal dependencies (via the `false` value)
228
+ * and bundled into the dist if and only if imported and not listed as `peerDependencies` (otherwise, they're considered external).
229
+ */
230
+ devDeps: false,
231
+ optDeps: true,
232
+ peerDeps: true
233
+ }));
234
+ return output;
309
235
  };
310
- const createTypesConfig = (entryPoints, options)=>{
311
- return {
312
- input: entryPoints.source,
313
- output: [
314
- {
315
- file: entryPoints.types
316
- }
317
- ],
318
- plugins: getPlugins([
319
- nodeResolve({
320
- /**
321
- * The `exports` conditional fields definition order is important in the `package.json file`.
322
- * To be resolved first, `types` field must always come first in the package.json exports definition.
323
- * @see https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#package-json-exports-imports-and-self-referencing.
324
- */ exportConditions: [
325
- "types"
326
- ]
327
- }),
328
- dts({
329
- compilerOptions: {
330
- declaration: true,
331
- emitDeclarationOnly: true,
332
- incremental: false,
333
- preserveSymlinks: false
334
- },
335
- respectExternal: true
336
- })
337
- ], options)
338
- };
236
+ const createMainConfig = (entryPoints, options) => {
237
+ const { minification, sourceMaps } = options;
238
+ const cjsInput = entryPoints.require;
239
+ const esmInput = entryPoints.import ?? entryPoints.default;
240
+ if (entryPoints.import && entryPoints.default && entryPoints.import !== entryPoints.default) throw new Error("Both `import` and `default` export fields have been defined but with different values. To preserve proper `default` field resolution on the consumer side (i.e. to target ESM format), make sure to provide the same file path for both fields.");
241
+ const commonOutputConfig = {
242
+ minify: minification,
243
+ sourcemap: sourceMaps
244
+ };
245
+ const output = [cjsInput && {
246
+ ...commonOutputConfig,
247
+ ...getFileOutput(cjsInput),
248
+ codeSplitting: Boolean(options.standalone),
249
+ format: "cjs"
250
+ }, esmInput && {
251
+ ...commonOutputConfig,
252
+ ...getFileOutput(esmInput),
253
+ format: "es"
254
+ }].filter(Boolean);
255
+ return {
256
+ input: entryPoints.source,
257
+ output,
258
+ plugins: getPlugins(options)
259
+ };
339
260
  };
340
-
341
- const createWatchCommand = (program)=>{
342
- return createCommand(program, {
343
- name: "watch",
344
- description: "Watch and rebuild on any code change (development mode)"
345
- }).task({
346
- handler (context) {
347
- watch(createConfiguration({
348
- minification: context.minification,
349
- sourceMaps: context.sourceMaps,
350
- standalone: false
351
- }));
352
- }
353
- });
261
+ const createTypesConfig = (entryPoints, options) => {
262
+ return {
263
+ input: entryPoints.source,
264
+ output: [getFileOutput(entryPoints.types)],
265
+ plugins: [...getPlugins(options), dts()]
266
+ };
354
267
  };
355
268
 
356
- const build = async (input)=>{
357
- process.env.NODE_ENV ??= "production";
358
- const { data: configurations } = input;
359
- const output = [];
360
- for (const config of configurations){
361
- const initialTime = Date.now();
362
- const bundle = await rollup({
363
- ...config,
364
- onLog
365
- });
366
- if (config.output) {
367
- const outputEntries = Array.isArray(config.output) ? config.output : [
368
- config.output
369
- ];
370
- const promises = [];
371
- for (const outputEntry of outputEntries){
372
- const outputFilePath = outputEntry.file ?? outputEntry.dir;
373
- if (!outputFilePath) {
374
- throw new Error("Misconfigured file entry point. Make sure to define an `import`, `require`, or `default` field.");
375
- }
376
- promises.push(new Promise((resolve, reject)=>{
377
- bundle.write(outputEntry).then(()=>{
378
- resolve({
379
- elapsedTime: Date.now() - initialTime,
380
- filePath: outputFilePath
381
- });
382
- }).catch((error)=>{
383
- reject(error);
384
- });
385
- }));
386
- }
387
- output.push(...await Promise.all(promises));
388
- }
389
- }
390
- return output;
269
+ //#endregion
270
+ //#region src/commands/build.ts
271
+ const createBuildCommand = (program) => {
272
+ return createCommand(program, {
273
+ description: "Build the source code (production mode)",
274
+ name: "build"
275
+ }).task({
276
+ async handler(context) {
277
+ return build(createConfiguration({
278
+ minification: context.minification,
279
+ sourceMaps: context.sourceMaps,
280
+ standalone: false
281
+ }));
282
+ },
283
+ key: "buildOutput",
284
+ label: "Bundle assets 📦"
285
+ }).task({
286
+ async handler(context) {
287
+ return computeBundleSize(context.buildOutput);
288
+ },
289
+ key: "logInput",
290
+ label: "Generate report 📝",
291
+ skip(context) {
292
+ return context.buildOutput.length === 0;
293
+ }
294
+ }).task({
295
+ handler(context) {
296
+ context.logInput.forEach((item) => {
297
+ helpers.message([`${formatSize(item.rawSize)} raw`, `${formatSize(item.compressedSize)} gzip`].map((message, index) => {
298
+ return index === 0 ? message : ` ${message}`;
299
+ }).join("\n"), {
300
+ label: `${item.filePath} (took ${item.elapsedTime}ms)`,
301
+ lineBreak: {
302
+ end: false,
303
+ start: true
304
+ },
305
+ type: "information"
306
+ });
307
+ });
308
+ },
309
+ skip(context) {
310
+ return context.buildOutput.length === 0;
311
+ }
312
+ });
313
+ };
314
+ const computeBundleSize = async (buildOutput) => {
315
+ const computeFileSize = async (buildItemOutput) => {
316
+ const content = await readFile$1(buildItemOutput.filePath);
317
+ const gzSize = await gzipSize(content);
318
+ return {
319
+ ...buildItemOutput,
320
+ compressedSize: gzSize,
321
+ rawSize: content.byteLength
322
+ };
323
+ };
324
+ return Promise.all(buildOutput.map(async (item) => computeFileSize(item)));
325
+ };
326
+ const formatSize = (bytes) => {
327
+ const kiloBytes = bytes / 1e3;
328
+ return kiloBytes < 1 ? `${bytes} B` : `${kiloBytes.toFixed(2)} kB`;
391
329
  };
392
330
 
331
+ //#endregion
332
+ //#region src/commands/compile.ts
393
333
  const TEMPORARY_PATH = resolveFromInternalDirectory("dist", "tmp");
394
334
  const TEMPORARY_DOWNLOAD_PATH = join(TEMPORARY_PATH, "zip");
395
335
  const TEMPORARY_RUNTIME_PATH = join(TEMPORARY_PATH, "runtime");
396
- const createCompileCommand = (program)=>{
397
- return program.command({
398
- name: "compile",
399
- description: "Compiles the source code into a self-contained executable"
400
- }).option({
401
- key: "targetInput",
402
- name: {
403
- long: "target",
404
- short: "t"
405
- },
406
- description: "Set a different cross-compilation target",
407
- defaultValue: "local"
408
- }).task({
409
- key: "config",
410
- label: "Create configuration",
411
- handler () {
412
- return createConfiguration({
413
- minification: true,
414
- sourceMaps: false,
415
- standalone: true
416
- });
417
- }
418
- }).task({
419
- key: "osType",
420
- label ({ targetInput }) {
421
- return `Get \`${targetInput}\` runtime`;
422
- },
423
- async handler ({ targetInput }) {
424
- if (targetInput === "local") {
425
- await copyFile(process.execPath, TEMPORARY_RUNTIME_PATH);
426
- return getOsType(os.type());
427
- }
428
- const matchedRuntimeParts = matchRuntimeParts(targetInput);
429
- if (!matchedRuntimeParts) {
430
- throw new Error("Invalid `runtime` flag input. The accepted targets are the one listed in https://nodejs.org/download/release/ with the following format `node-vx.y.z-(darwin|linux|win)-(arm64|x64|x86)`.");
431
- }
432
- const osType = getOsType(matchedRuntimeParts.os);
433
- const extension = osType === "windows" ? "zip" : "tar.gz";
434
- await download(`https://nodejs.org/download/release/${matchedRuntimeParts.version}/${targetInput}.${extension}`, TEMPORARY_DOWNLOAD_PATH);
435
- await unzip({
436
- path: TEMPORARY_DOWNLOAD_PATH,
437
- targetedArchivePath: osType === "windows" ? join(targetInput, "node.exe") : join(targetInput, "bin", "node")
438
- }, {
439
- directoryPath: dirname(TEMPORARY_RUNTIME_PATH),
440
- filename: basename(TEMPORARY_RUNTIME_PATH)
441
- });
442
- return osType;
443
- }
444
- }).task({
445
- label: "Build",
446
- async handler ({ config }) {
447
- await build(config);
448
- }
449
- }).task({
450
- label ({ config }) {
451
- const binaries = config.metadata.map(({ bin })=>{
452
- if (!bin) return undefined;
453
- return `\`${bin}\``;
454
- }).filter(Boolean).join(", ");
455
- return `Compile ${binaries}`;
456
- },
457
- async handler ({ config, osType }) {
458
- await Promise.all(config.metadata.map(async ({ bin, require })=>{
459
- if (!require || !bin) return;
460
- return compile({
461
- bin,
462
- input: require,
463
- osType
464
- });
465
- }));
466
- }
467
- });
336
+ const createCompileCommand = (program) => {
337
+ return program.command({
338
+ description: "Compiles the source code into a self-contained executable",
339
+ name: "compile"
340
+ }).option({
341
+ defaultValue: "local",
342
+ description: "Set a different cross-compilation target",
343
+ key: "targetInput",
344
+ name: {
345
+ long: "target",
346
+ short: "t"
347
+ }
348
+ }).task({
349
+ handler() {
350
+ return createConfiguration({
351
+ minification: true,
352
+ sourceMaps: false,
353
+ standalone: true
354
+ });
355
+ },
356
+ key: "config",
357
+ label: "Create configuration"
358
+ }).task({
359
+ async handler({ targetInput }) {
360
+ if (targetInput === "local") {
361
+ await copyFile$1(process.execPath, TEMPORARY_RUNTIME_PATH);
362
+ return getOsType(os.type());
363
+ }
364
+ const matchedRuntimeParts = matchRuntimeParts(targetInput);
365
+ if (!matchedRuntimeParts) throw new Error("Invalid `runtime` flag input. The accepted targets are the one listed in https://nodejs.org/download/release/ with the following format `node-vx.y.z-(darwin|linux|win)-(arm64|x64|x86)`.");
366
+ const osType = getOsType(matchedRuntimeParts.os);
367
+ const extension = osType === "windows" ? "zip" : "tar.gz";
368
+ await download(`https://nodejs.org/download/release/${matchedRuntimeParts.version}/${targetInput}.${extension}`, TEMPORARY_DOWNLOAD_PATH);
369
+ await unzip({
370
+ path: TEMPORARY_DOWNLOAD_PATH,
371
+ targetedArchivePath: osType === "windows" ? join(targetInput, "node.exe") : join(targetInput, "bin", "node")
372
+ }, {
373
+ directoryPath: dirname(TEMPORARY_RUNTIME_PATH),
374
+ filename: basename(TEMPORARY_RUNTIME_PATH)
375
+ });
376
+ return osType;
377
+ },
378
+ key: "osType",
379
+ label({ targetInput }) {
380
+ return `Get \`${targetInput}\` runtime`;
381
+ }
382
+ }).task({
383
+ async handler({ config }) {
384
+ await build(config);
385
+ },
386
+ label: "Build"
387
+ }).task({
388
+ async handler({ config, osType }) {
389
+ await Promise.all(config.metadata.map(async ({ bin, require }) => {
390
+ if (!require || !bin) return;
391
+ return compile({
392
+ bin,
393
+ input: require,
394
+ osType
395
+ });
396
+ }));
397
+ },
398
+ label({ config }) {
399
+ return `Compile ${config.metadata.map(({ bin }) => {
400
+ if (!bin) return void 0;
401
+ return `\`${bin}\``;
402
+ }).filter(Boolean).join(", ")}`;
403
+ }
404
+ });
468
405
  };
469
- const getOsType = (input)=>{
470
- switch(input){
471
- case "Windows_NT":
472
- case "win":
473
- {
474
- return "windows";
475
- }
476
- case "Darwin":
477
- case "darwin":
478
- {
479
- return "macos";
480
- }
481
- case "Linux":
482
- case "linux":
483
- {
484
- return "linux";
485
- }
486
- default:
487
- {
488
- throw new Error(`Unsupported operating system \`${input}\``);
489
- }
490
- }
406
+ const getOsType = (input) => {
407
+ switch (input) {
408
+ case "Darwin":
409
+ case "darwin": return "macos";
410
+ case "Linux":
411
+ case "linux": return "linux";
412
+ case "win":
413
+ case "Windows_NT": return "windows";
414
+ default: throw new Error(`Unsupported operating system \`${input}\``);
415
+ }
491
416
  };
492
417
  const matchRuntimeParts = createRegExpMatcher(/^node-(?<version>v\d+\.\d+\.\d+)-(?<os>darwin|linux|win)-(?<architecture>arm64|x64|x86)$/);
493
- const compile = async ({ bin, input, osType })=>{
494
- const inputFileName = basename(input);
495
- const inputDirectory = dirname(input);
496
- const resolveFromInputDirectory = (...paths)=>{
497
- return resolve(inputDirectory, ...paths);
498
- };
499
- const blobFileName = resolveFromInputDirectory(`${inputFileName}.blob`);
500
- const executableFileName = resolveFromInputDirectory(`${bin}${osType === "windows" ? ".exe" : ""}`);
501
- const seaConfigFileName = resolveFromInputDirectory(`${inputFileName}.sea-config.json`);
502
- await writeFile(seaConfigFileName, JSON.stringify({
503
- disableExperimentalSEAWarning: true,
504
- main: input,
505
- output: blobFileName,
506
- useCodeCache: false,
507
- useSnapshot: false
508
- }));
509
- await Promise.all([
510
- helpers.exec(`node --experimental-sea-config ${seaConfigFileName}`),
511
- copyFile(TEMPORARY_RUNTIME_PATH, executableFileName)
512
- ]);
513
- if (osType === "macos") {
514
- await helpers.exec(`codesign --remove-signature ${executableFileName}`);
515
- }
516
- await helpers.exec(`npx postject ${executableFileName} NODE_SEA_BLOB ${blobFileName} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 ${osType === "macos" ? "--macho-segment-name NODE_SEA" : ""}`);
517
- if (osType === "macos") {
518
- await helpers.exec(`codesign --sign - ${executableFileName}`);
519
- }
520
- await Promise.all([
521
- blobFileName,
522
- seaConfigFileName,
523
- TEMPORARY_PATH
524
- ].map(async (path)=>removePath(path)));
418
+ const compile = async ({ bin, input, osType }) => {
419
+ const inputFileName = basename(input);
420
+ const inputDirectory = dirname(input);
421
+ const resolveFromInputDirectory = (...paths) => {
422
+ return resolve(inputDirectory, ...paths);
423
+ };
424
+ const blobFileName = resolveFromInputDirectory(`${inputFileName}.blob`);
425
+ const executableFileName = resolveFromInputDirectory(`${bin}${osType === "windows" ? ".exe" : ""}`);
426
+ const seaConfigFileName = resolveFromInputDirectory(`${inputFileName}.sea-config.json`);
427
+ await writeFile$1(seaConfigFileName, JSON.stringify({
428
+ disableExperimentalSEAWarning: true,
429
+ main: input,
430
+ output: blobFileName,
431
+ useCodeCache: false,
432
+ useSnapshot: false
433
+ }));
434
+ await Promise.all([helpers.exec(`node --experimental-sea-config ${seaConfigFileName}`), copyFile$1(TEMPORARY_RUNTIME_PATH, executableFileName)]);
435
+ if (osType === "macos") await helpers.exec(`codesign --remove-signature ${executableFileName}`);
436
+ await helpers.exec(`npx postject ${executableFileName} NODE_SEA_BLOB ${blobFileName} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 ${osType === "macos" ? "--macho-segment-name NODE_SEA" : ""}`);
437
+ if (osType === "macos") await helpers.exec(`codesign --sign - ${executableFileName}`);
438
+ await Promise.all([
439
+ blobFileName,
440
+ seaConfigFileName,
441
+ TEMPORARY_PATH
442
+ ].map(async (path) => removePath(path)));
525
443
  };
526
444
 
527
- const createBuildCommand = (program)=>{
528
- return createCommand(program, {
529
- name: "build",
530
- description: "Build the source code (production mode)"
531
- }).task({
532
- key: "buildOutput",
533
- label: "Bundle assets 📦",
534
- async handler (context) {
535
- return build(createConfiguration({
536
- minification: context.minification,
537
- sourceMaps: context.sourceMaps,
538
- standalone: false
539
- }));
540
- }
541
- }).task({
542
- key: "logInput",
543
- label: "Generate report 📝",
544
- async handler (context) {
545
- return computeBundleSize(context.buildOutput);
546
- },
547
- skip (context) {
548
- return context.buildOutput.length === 0;
549
- }
550
- }).task({
551
- handler (context) {
552
- context.logInput.forEach((item)=>{
553
- helpers.message([
554
- `${formatSize(item.rawSize)} raw`,
555
- `${formatSize(item.compressedSize)} gzip`
556
- ].map((message, index)=>{
557
- return index === 0 ? message : ` ${message}`;
558
- }).join("\n"), {
559
- label: `${item.filePath} (took ${item.elapsedTime}ms)`,
560
- lineBreak: {
561
- end: false,
562
- start: true
563
- },
564
- type: "information"
565
- });
566
- });
567
- },
568
- skip (context) {
569
- return context.buildOutput.length === 0;
570
- }
571
- });
572
- };
573
- const computeBundleSize = async (buildOutput)=>{
574
- const computeFileSize = async (buildItemOutput)=>{
575
- const content = await readFile(buildItemOutput.filePath);
576
- const gzSize = await gzipSize(content);
577
- return {
578
- ...buildItemOutput,
579
- compressedSize: gzSize,
580
- rawSize: content.byteLength
581
- };
582
- };
583
- return Promise.all(buildOutput.map(async (item)=>computeFileSize(item)));
445
+ //#endregion
446
+ //#region src/bundler/watch.ts
447
+ const watch$1 = (input) => {
448
+ process.env.NODE_ENV ??= "development";
449
+ const watcher = watch(input.data);
450
+ let startDuration;
451
+ console.clear();
452
+ watcher.on("event", async (event) => {
453
+ switch (event.code) {
454
+ case "BUNDLE_END":
455
+ await event.result.close();
456
+ break;
457
+ case "END":
458
+ clearLog(`Build done in ${Date.now() - startDuration}ms (at ${(/* @__PURE__ */ new Date()).toLocaleTimeString()})`, { type: "success" });
459
+ return;
460
+ case "ERROR": {
461
+ const { error } = event;
462
+ clearLog(error.message, { type: "error" });
463
+ console.error("\n", error);
464
+ return;
465
+ }
466
+ case "START":
467
+ startDuration = Date.now();
468
+ clearLog("Build in progress…", { type: "information" });
469
+ return;
470
+ default: break;
471
+ }
472
+ });
584
473
  };
585
- const formatSize = (bytes)=>{
586
- const kiloBytes = bytes / 1000;
587
- return kiloBytes < 1 ? `${bytes} B` : `${kiloBytes.toFixed(2)} kB`;
474
+ const clearLog = (...input) => {
475
+ console.clear();
476
+ helpers.message(...input);
588
477
  };
589
478
 
590
- var name = "quickbundle";
591
- var version = "2.15.0";
479
+ //#endregion
480
+ //#region src/commands/watch.ts
481
+ const createWatchCommand = (program) => {
482
+ return createCommand(program, {
483
+ description: "Watch and rebuild on any code change (development mode)",
484
+ name: "watch"
485
+ }).task({ handler(context) {
486
+ watch$1(createConfiguration({
487
+ minification: context.minification,
488
+ sourceMaps: context.sourceMaps,
489
+ standalone: false
490
+ }));
491
+ } });
492
+ };
592
493
 
593
- const createProgram = (...commandBuilders)=>{
594
- const program = termost({
595
- name,
596
- description: "The zero-configuration transpiler and bundler for the web",
597
- version
598
- });
599
- for (const commandBuilder of commandBuilders){
600
- commandBuilder(program);
601
- }
494
+ //#endregion
495
+ //#region src/index.ts
496
+ const createProgram = (...commandBuilders) => {
497
+ const program = termost({
498
+ description: "The zero-configuration transpiler and bundler for the web",
499
+ name,
500
+ version
501
+ });
502
+ for (const commandBuilder of commandBuilders) commandBuilder(program);
602
503
  };
603
504
  createProgram(createBuildCommand, createWatchCommand, createCompileCommand);
505
+
506
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickbundle",
3
- "version": "2.15.0",
3
+ "version": "2.16.0-next-dfc95be",
4
4
  "description": "The zero-configuration transpiler and bundler for the web",
5
5
  "keywords": [
6
6
  "library",
@@ -40,25 +40,20 @@
40
40
  "dist"
41
41
  ],
42
42
  "dependencies": {
43
- "@rollup/plugin-commonjs": "^28.0.8",
44
- "@rollup/plugin-json": "^6.1.0",
45
- "@rollup/plugin-node-resolve": "^16.0.3",
46
43
  "@rollup/plugin-url": "^8.0.2",
47
- "@swc/core": "^1.13.5",
48
44
  "decompress": "^4.2.1",
49
45
  "gzip-size": "^7.0.0",
50
- "rollup": "^4.52.4",
51
- "rollup-plugin-dts": "^6.2.3",
52
- "rollup-plugin-node-externals": "^8.1.1",
53
- "rollup-plugin-swc3": "^0.12.1",
54
- "termost": "^1.8.0"
46
+ "rolldown": "^1.0.1",
47
+ "rolldown-plugin-dts": "^0.25.1",
48
+ "rollup-plugin-node-externals": "^9.0.1",
49
+ "termost": "^1.9.0"
55
50
  },
56
51
  "devDependencies": {
57
52
  "@types/decompress": "4.2.7",
58
- "@types/node": "22.18.11"
53
+ "@types/node": "24.12.4"
59
54
  },
60
55
  "peerDependencies": {
61
- "typescript": "^4.7.0 || ^5.0.0"
56
+ "typescript": "^4.7.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
62
57
  },
63
58
  "peerDependenciesMeta": {
64
59
  "typescript": {
@@ -66,8 +61,8 @@
66
61
  }
67
62
  },
68
63
  "scripts": {
69
- "build": "rollup --config ./bundler.config.ts --configPlugin rollup-plugin-swc3",
64
+ "build": "rolldown --config ./bundler.config.ts",
70
65
  "test": "exit 0",
71
- "watch": "rollup --watch --config ./bundler.config.ts --configPlugin rollup-plugin-swc3"
66
+ "watch": "rolldown --watch --config ./bundler.config.ts"
72
67
  }
73
68
  }