quickbundle 3.0.0-next-9f62eb1 → 3.0.0-next-c70f8cb
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 +1 -0
- package/dist/index.js +34 -118
- package/package.json +1 -6
package/README.md
CHANGED
|
@@ -105,6 +105,7 @@ yarn add quickbundle
|
|
|
105
105
|
> [!warning]
|
|
106
106
|
> The `compile` command relies on the [Node.js SEA (Single Executable Applications feature)](https://nodejs.org/api/single-executable-applications.html). This feature comes with some limitations which Quickbundle attempts to address:
|
|
107
107
|
>
|
|
108
|
+
> - The minimum compatible Node.js version is v25.5.0 (to support `--build-sea` flag).
|
|
108
109
|
> - The source code must not rely on [external dependencies](https://github.com/nodejs/single-executable/discussions/70) 🛑. To partially address this, Quickbundle bundles all dependencies (whatever their types, as long as they're used) and inline dynamic imports by default ✅.
|
|
109
110
|
> - The bundled code must use the CommonJS module system 🛑. To address this, Quickbundle always outputs CJS modules in compile mode ✅.
|
|
110
111
|
|
package/dist/index.js
CHANGED
|
@@ -6,16 +6,11 @@ import url from "@rollup/plugin-url";
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { dts } from "rolldown-plugin-dts";
|
|
8
8
|
import externals from "rollup-plugin-node-externals";
|
|
9
|
-
import
|
|
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";
|
|
9
|
+
import { readFile, rename, rm } from "node:fs/promises";
|
|
15
10
|
|
|
16
11
|
//#region package.json
|
|
17
12
|
var name = "quickbundle";
|
|
18
|
-
var version = "3.0.0-next-
|
|
13
|
+
var version = "3.0.0-next-c70f8cb";
|
|
19
14
|
|
|
20
15
|
//#endregion
|
|
21
16
|
//#region src/bundler/build.ts
|
|
@@ -44,16 +39,6 @@ const build = async (input) => {
|
|
|
44
39
|
//#endregion
|
|
45
40
|
//#region src/helpers.ts
|
|
46
41
|
/**
|
|
47
|
-
* Resolve a relative path from the Quickbundle node modules directory.
|
|
48
|
-
* @param paths - Relative paths.
|
|
49
|
-
* @returns The resolved absolute path.
|
|
50
|
-
* @example
|
|
51
|
-
* resolveFromInternalDirectory("dist", "node");
|
|
52
|
-
*/
|
|
53
|
-
const resolveFromInternalDirectory = (...paths) => {
|
|
54
|
-
return resolve(import.meta.dirname, "../", ...paths);
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
42
|
* Resolve a relative path from the current working project directory.
|
|
58
43
|
* @param paths - Relative paths.
|
|
59
44
|
* @returns The resolved absolute path.
|
|
@@ -68,12 +53,8 @@ const createRegExpMatcher = (regex) => {
|
|
|
68
53
|
return regex.exec(value)?.groups;
|
|
69
54
|
};
|
|
70
55
|
};
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
const copyFile$1 = async (fromPath, toPath) => {
|
|
75
|
-
await createDirectory(dirname(toPath));
|
|
76
|
-
await copyFile(fromPath, toPath);
|
|
56
|
+
const readFile$1 = async (filePath) => {
|
|
57
|
+
return readFile(filePath);
|
|
77
58
|
};
|
|
78
59
|
const removePath = async (path) => {
|
|
79
60
|
await rm(path, {
|
|
@@ -81,28 +62,6 @@ const removePath = async (path) => {
|
|
|
81
62
|
recursive: true
|
|
82
63
|
});
|
|
83
64
|
};
|
|
84
|
-
const readFile$1 = async (filePath) => {
|
|
85
|
-
return readFile(filePath);
|
|
86
|
-
};
|
|
87
|
-
const writeFile$1 = async (filePath, content) => {
|
|
88
|
-
await createDirectory(dirname(filePath));
|
|
89
|
-
await writeFile(filePath, content, "utf8");
|
|
90
|
-
};
|
|
91
|
-
const download = async (url, filePath) => {
|
|
92
|
-
await createDirectory(dirname(filePath));
|
|
93
|
-
const { body, ok, status, statusText } = await fetch(url);
|
|
94
|
-
if (!ok) throw new Error(`An error ocurred while downloading \`${url}\`. Received \`${status}\` status code with the following message \`${statusText}\`.`);
|
|
95
|
-
if (!body) throw new Error(`Empty body received while downloading \`${url}\`.`);
|
|
96
|
-
await finished(Readable.fromWeb(body).pipe(createWriteStream(filePath)));
|
|
97
|
-
};
|
|
98
|
-
const unzip = async (input, output) => {
|
|
99
|
-
const { targetedArchivePath } = input;
|
|
100
|
-
const { directoryPath } = output;
|
|
101
|
-
await decompress(input.path, directoryPath, { filter(file) {
|
|
102
|
-
return file.path === targetedArchivePath;
|
|
103
|
-
} });
|
|
104
|
-
await rename(join(directoryPath, targetedArchivePath), join(directoryPath, output.filename));
|
|
105
|
-
};
|
|
106
65
|
const createCommand = (program, input) => {
|
|
107
66
|
return program.command(input).option({
|
|
108
67
|
defaultValue: false,
|
|
@@ -327,9 +286,6 @@ const formatSize = (bytes) => {
|
|
|
327
286
|
|
|
328
287
|
//#endregion
|
|
329
288
|
//#region src/commands/compile.ts
|
|
330
|
-
const TEMPORARY_PATH = resolveFromInternalDirectory("dist", "tmp");
|
|
331
|
-
const TEMPORARY_DOWNLOAD_PATH = join(TEMPORARY_PATH, "zip");
|
|
332
|
-
const TEMPORARY_RUNTIME_PATH = join(TEMPORARY_PATH, "runtime");
|
|
333
289
|
const createCompileCommand = (program) => {
|
|
334
290
|
return program.command({
|
|
335
291
|
description: "Compiles the source code into a self-contained executable",
|
|
@@ -352,45 +308,42 @@ const createCompileCommand = (program) => {
|
|
|
352
308
|
},
|
|
353
309
|
key: "config",
|
|
354
310
|
label: "Create configuration"
|
|
355
|
-
}).task({
|
|
356
|
-
async handler({ targetInput }) {
|
|
357
|
-
if (targetInput === "local") {
|
|
358
|
-
await copyFile$1(process.execPath, TEMPORARY_RUNTIME_PATH);
|
|
359
|
-
return getOsType(os.type());
|
|
360
|
-
}
|
|
361
|
-
const matchedRuntimeParts = matchRuntimeParts(targetInput);
|
|
362
|
-
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)`.");
|
|
363
|
-
const osType = getOsType(matchedRuntimeParts.os);
|
|
364
|
-
const extension = osType === "windows" ? "zip" : "tar.gz";
|
|
365
|
-
await download(`https://nodejs.org/download/release/${matchedRuntimeParts.version}/${targetInput}.${extension}`, TEMPORARY_DOWNLOAD_PATH);
|
|
366
|
-
await unzip({
|
|
367
|
-
path: TEMPORARY_DOWNLOAD_PATH,
|
|
368
|
-
targetedArchivePath: osType === "windows" ? join(targetInput, "node.exe") : join(targetInput, "bin", "node")
|
|
369
|
-
}, {
|
|
370
|
-
directoryPath: dirname(TEMPORARY_RUNTIME_PATH),
|
|
371
|
-
filename: basename(TEMPORARY_RUNTIME_PATH)
|
|
372
|
-
});
|
|
373
|
-
return osType;
|
|
374
|
-
},
|
|
375
|
-
key: "osType",
|
|
376
|
-
label({ targetInput }) {
|
|
377
|
-
return `Get \`${targetInput}\` runtime`;
|
|
378
|
-
}
|
|
379
311
|
}).task({
|
|
380
312
|
async handler({ config }) {
|
|
381
313
|
await build(config);
|
|
382
314
|
},
|
|
383
315
|
label: "Build"
|
|
384
316
|
}).task({
|
|
385
|
-
async handler({ config,
|
|
386
|
-
|
|
317
|
+
async handler({ config, targetInput }) {
|
|
318
|
+
for (const { bin, require } of config.metadata) {
|
|
387
319
|
if (!require || !bin) return;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
320
|
+
let os = process.platform === "win32" ? "win" : process.platform;
|
|
321
|
+
let architecture = process.arch;
|
|
322
|
+
let version = void 0;
|
|
323
|
+
if (targetInput !== "local") {
|
|
324
|
+
const nodeProperties = getNodeProperties(targetInput);
|
|
325
|
+
if (!nodeProperties) 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)`.");
|
|
326
|
+
architecture = nodeProperties.architecture;
|
|
327
|
+
os = nodeProperties.os;
|
|
328
|
+
version = nodeProperties.version;
|
|
329
|
+
}
|
|
330
|
+
const distributionPath = dirname(require);
|
|
331
|
+
const target = `${os}-${architecture}`;
|
|
332
|
+
const targetPath = join(distributionPath, target);
|
|
333
|
+
const packAppFlags = Object.entries({
|
|
334
|
+
"entry": require,
|
|
335
|
+
"output-dir": distributionPath,
|
|
336
|
+
"output-name": bin,
|
|
337
|
+
"runtime": version ? `node@${version}` : void 0,
|
|
338
|
+
target
|
|
339
|
+
}).map(([flagName, flagValue]) => {
|
|
340
|
+
if (!flagValue) return void 0;
|
|
341
|
+
return `--${flagName} ${flagValue}`;
|
|
342
|
+
}).filter(Boolean).join(" ");
|
|
343
|
+
await helpers.exec(`npx --yes pnpm pack-app ${packAppFlags}`);
|
|
344
|
+
await rename(join(targetPath, bin), join(distributionPath, bin));
|
|
345
|
+
await Promise.all([require, targetPath].map(async (path) => removePath(path)));
|
|
346
|
+
}
|
|
394
347
|
},
|
|
395
348
|
label({ config }) {
|
|
396
349
|
return `Compile ${config.metadata.map(({ bin }) => {
|
|
@@ -400,44 +353,7 @@ const createCompileCommand = (program) => {
|
|
|
400
353
|
}
|
|
401
354
|
});
|
|
402
355
|
};
|
|
403
|
-
const
|
|
404
|
-
switch (input) {
|
|
405
|
-
case "Darwin":
|
|
406
|
-
case "darwin": return "macos";
|
|
407
|
-
case "Linux":
|
|
408
|
-
case "linux": return "linux";
|
|
409
|
-
case "win":
|
|
410
|
-
case "Windows_NT": return "windows";
|
|
411
|
-
default: throw new Error(`Unsupported operating system \`${input}\``);
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
const matchRuntimeParts = createRegExpMatcher(/^node-(?<version>v\d+\.\d+\.\d+)-(?<os>darwin|linux|win)-(?<architecture>arm64|x64|x86)$/);
|
|
415
|
-
const compile = async ({ bin, input, osType }) => {
|
|
416
|
-
const inputFileName = basename(input);
|
|
417
|
-
const inputDirectory = dirname(input);
|
|
418
|
-
const resolveFromInputDirectory = (...paths) => {
|
|
419
|
-
return resolve(inputDirectory, ...paths);
|
|
420
|
-
};
|
|
421
|
-
const blobFileName = resolveFromInputDirectory(`${inputFileName}.blob`);
|
|
422
|
-
const executableFileName = resolveFromInputDirectory(`${bin}${osType === "windows" ? ".exe" : ""}`);
|
|
423
|
-
const seaConfigFileName = resolveFromInputDirectory(`${inputFileName}.sea-config.json`);
|
|
424
|
-
await writeFile$1(seaConfigFileName, JSON.stringify({
|
|
425
|
-
disableExperimentalSEAWarning: true,
|
|
426
|
-
main: input,
|
|
427
|
-
output: blobFileName,
|
|
428
|
-
useCodeCache: false,
|
|
429
|
-
useSnapshot: false
|
|
430
|
-
}));
|
|
431
|
-
await Promise.all([helpers.exec(`node --experimental-sea-config ${seaConfigFileName}`), copyFile$1(TEMPORARY_RUNTIME_PATH, executableFileName)]);
|
|
432
|
-
if (osType === "macos") await helpers.exec(`codesign --remove-signature ${executableFileName}`);
|
|
433
|
-
await helpers.exec(`npx postject ${executableFileName} NODE_SEA_BLOB ${blobFileName} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 ${osType === "macos" ? "--macho-segment-name NODE_SEA" : ""}`);
|
|
434
|
-
if (osType === "macos") await helpers.exec(`codesign --sign - ${executableFileName}`);
|
|
435
|
-
await Promise.all([
|
|
436
|
-
blobFileName,
|
|
437
|
-
seaConfigFileName,
|
|
438
|
-
TEMPORARY_PATH
|
|
439
|
-
].map(async (path) => removePath(path)));
|
|
440
|
-
};
|
|
356
|
+
const getNodeProperties = createRegExpMatcher(/^node-(?<version>v\d+\.\d+\.\d+)-(?<os>darwin|linux|win)-(?<architecture>arm64|x64|x86)$/);
|
|
441
357
|
|
|
442
358
|
//#endregion
|
|
443
359
|
//#region src/bundler/watch.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickbundle",
|
|
3
|
-
"version": "3.0.0-next-
|
|
3
|
+
"version": "3.0.0-next-c70f8cb",
|
|
4
4
|
"description": "The zero-configuration transpiler and bundler for the web",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"library",
|
|
@@ -41,17 +41,12 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@rollup/plugin-url": "^8.0.2",
|
|
44
|
-
"decompress": "^4.2.1",
|
|
45
44
|
"gzip-size": "^7.0.0",
|
|
46
45
|
"rolldown": "^1.2.0",
|
|
47
46
|
"rolldown-plugin-dts": "^0.27.14",
|
|
48
47
|
"rollup-plugin-node-externals": "^9.0.1",
|
|
49
48
|
"termost": "^1.9.1"
|
|
50
49
|
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@types/decompress": "4.2.7",
|
|
53
|
-
"@types/node": "24.13.3"
|
|
54
|
-
},
|
|
55
50
|
"peerDependencies": {
|
|
56
51
|
"typescript": "^4.7.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
57
52
|
},
|