quickbundle 0.0.0-next-e351a81 → 0.0.0-next-386ba5d
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 +20 -2
- package/dist/index.mjs +135 -20
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
Quickbundle allows you to bundle a library in a **quick**, **fast** and **easy** way:
|
|
12
12
|
|
|
13
13
|
- Fast build and watch mode powered by Rollup[^1] and SWC[^2].
|
|
14
|
-
- Compile mode to
|
|
14
|
+
- Compile mode to compile and cross compile standalone binaries for systems that do not have Node.js installed[^3].
|
|
15
15
|
- Zero configuration: define the build artifacts in your `package.json`, and you're all set!
|
|
16
16
|
- Support of `cjs` & `esm` module formats output.
|
|
17
17
|
- Support of several loaders including JavaScript, TypeScript, JSX, JSON, and Images.
|
|
@@ -113,7 +113,7 @@ yarn add quickbundle
|
|
|
113
113
|
"name": "lib", // Package name
|
|
114
114
|
"source": "./src/index.ts", // Source code entry point. Make sure that it starts with `#!/usr/bin/env node` pragme to make the binary portable for consumers who would like to use it by installing the package instead of using the generated standalone executable.
|
|
115
115
|
"bin": {
|
|
116
|
-
"your-binary-name": "./dist/index.cjs", // Binary information to get the executable name from the key and, from the value, the bundled file to generate from the source code and inject into the executable. The generated executable will be located in the same folder as the bundled file and, dependending on the current operating system running the `compile` command, the executable will be named either `your-binary-name.exe` on Windows or `your-binary-name` on Linux and macOS.
|
|
116
|
+
"your-binary-name": "./dist/index.cjs", // Binary information to get the executable name from the key and, from the value, the bundled file to generate from the source code and inject into the executable. The generated executable will be located in the same folder as the bundled file and, by default, dependending on the current operating system running the `compile` command, the executable will be named either `your-binary-name.exe` on Windows or `your-binary-name` on Linux and macOS. For cross-compilation output, check the `Patterns` section.
|
|
117
117
|
},
|
|
118
118
|
// "bin": "./dist/index.cjs", // Or, if the binary name follows the package name, you can define a string-based `bin` value.
|
|
119
119
|
"scripts": {
|
|
@@ -171,6 +171,24 @@ quickbundle watch --source-maps
|
|
|
171
171
|
|
|
172
172
|
Enabling source map generation is needed only if a build is [obfuscated (minified)](#optimize-the-build-output) for debugging-easing purposes. It generally pairs with the [`minification` flag](#optimize-the-build-output).
|
|
173
173
|
|
|
174
|
+
### Cross compilation to other platforms
|
|
175
|
+
|
|
176
|
+
By default, the `compile` command embeds the runtime at the origin of its execution which means it generates executables compatible only with machines running the same operating system, processor architecture, and Node.js version.
|
|
177
|
+
|
|
178
|
+
However, Quickbundle provides the ability to target a different operating system, processor architecture, or Node.js version (also known as cross-compilation):
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
quickbundle compile --target node-v23.1.0-darwin-arm64 # Embeds Node v23 runtime built for macOS ARM64 target
|
|
182
|
+
quickbundle compile --target node-v23.1.0-darwin-x64 # Embeds Node v23 runtime built for macOS X64 target
|
|
183
|
+
quickbundle compile --target node-v23.1.0-linux-arm64 # Embeds Node v23 runtime built for Linux ARM64 target
|
|
184
|
+
quickbundle compile --target node-v23.1.0-linux-x64 # Embeds Node v23 runtime built for Linux X64 target
|
|
185
|
+
quickbundle compile --target node-v23.1.0-win-arm64 # Embeds Node v23 runtime built for Windows ARM64 target
|
|
186
|
+
quickbundle compile --target node-v23.1.0-win-x64 # Embeds Node v23 runtime built for Windows X64 target
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> [!note]
|
|
190
|
+
> The accepted target input follows the `node-vx.y.z-(darwin|linux|win)-(arm64|x64|x86)` format (for an exhaustive view, check available filenames in [https://nodejs.org/download/release/vx.y.z](https://nodejs.org/download/release/latest/)).
|
|
191
|
+
|
|
174
192
|
<br>
|
|
175
193
|
|
|
176
194
|
## ☑️ Roadmap
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { helpers, termost } from 'termost';
|
|
2
|
-
import {
|
|
2
|
+
import { finished } from 'node:stream/promises';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
3
4
|
import process from 'node:process';
|
|
5
|
+
import { resolve, dirname, join, basename } from 'node:path';
|
|
6
|
+
import { copyFile as copyFile$1, rm, writeFile as writeFile$1, rename, mkdir, readFile as readFile$1 } from 'node:fs/promises';
|
|
7
|
+
import { createWriteStream } from 'node:fs';
|
|
8
|
+
import decompress from 'decompress';
|
|
4
9
|
import { watch as watch$1, rollup } from 'rollup';
|
|
5
|
-
import { join, basename, dirname, resolve } from 'node:path';
|
|
6
10
|
import { createRequire } from 'node:module';
|
|
7
11
|
import { swc } from 'rollup-plugin-swc3';
|
|
8
12
|
import externals from 'rollup-plugin-node-externals';
|
|
@@ -15,11 +19,71 @@ import os from 'node:os';
|
|
|
15
19
|
import { gzipSize } from 'gzip-size';
|
|
16
20
|
|
|
17
21
|
var name = "quickbundle";
|
|
18
|
-
var version = "0.0.0-next-
|
|
22
|
+
var version = "0.0.0-next-386ba5d";
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a relative path from the Quickbundle node modules directory.
|
|
26
|
+
* @param paths - Relative paths.
|
|
27
|
+
* @returns The resolved absolute path.
|
|
28
|
+
* @example
|
|
29
|
+
* resolveFromInternalDirectory("dist", "node");
|
|
30
|
+
*/ const resolveFromInternalDirectory = (...paths)=>{
|
|
31
|
+
return resolve(import.meta.dirname, "../", ...paths);
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a relative path from the current working project directory.
|
|
35
|
+
* @param paths - Relative paths.
|
|
36
|
+
* @returns The resolved absolute path.
|
|
37
|
+
* @example
|
|
38
|
+
* resolveFromExternalDirectory("package.json");
|
|
39
|
+
*/ const resolveFromExternalDirectory = (...paths)=>{
|
|
40
|
+
return resolve(process.cwd(), ...paths);
|
|
41
|
+
};
|
|
42
|
+
const createRegExpMatcher = (regex)=>{
|
|
43
|
+
return (value)=>{
|
|
44
|
+
return regex.exec(value)?.groups;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const createDirectory = async (path)=>{
|
|
48
|
+
await mkdir(path, {
|
|
49
|
+
recursive: true
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const copyFile = async (fromPath, toPath)=>{
|
|
53
|
+
await createDirectory(dirname(toPath));
|
|
54
|
+
await copyFile$1(fromPath, toPath);
|
|
55
|
+
};
|
|
56
|
+
const removePath = async (path)=>{
|
|
57
|
+
await rm(path, {
|
|
58
|
+
force: true,
|
|
59
|
+
recursive: true
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const readFile = async (filePath)=>{
|
|
63
|
+
return readFile$1(filePath);
|
|
64
|
+
};
|
|
65
|
+
const writeFile = async (filePath, content)=>{
|
|
66
|
+
await createDirectory(dirname(filePath));
|
|
67
|
+
await writeFile$1(filePath, content, "utf8");
|
|
68
|
+
};
|
|
69
|
+
const download = async (url, filePath)=>{
|
|
70
|
+
await createDirectory(dirname(filePath));
|
|
71
|
+
const { body } = await fetch(url);
|
|
72
|
+
if (!body) {
|
|
73
|
+
throw new Error(`Empty body while attempting to download file from \`${url}\`.`);
|
|
74
|
+
}
|
|
75
|
+
return finished(Readable.fromWeb(body).pipe(createWriteStream(filePath)));
|
|
76
|
+
};
|
|
77
|
+
const unzip = async (input, output)=>{
|
|
78
|
+
const { targetArchivePath } = input;
|
|
79
|
+
const { directoryPath } = output;
|
|
80
|
+
await decompress(input.path, directoryPath, {
|
|
81
|
+
filter (file) {
|
|
82
|
+
return file.path === targetArchivePath;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
await rename(join(directoryPath, targetArchivePath), join(directoryPath, output.filename));
|
|
86
|
+
};
|
|
23
87
|
const createCommand = (program, input)=>{
|
|
24
88
|
return program.command(input).option({
|
|
25
89
|
key: "minification",
|
|
@@ -88,7 +152,7 @@ const clearLog = (...input)=>{
|
|
|
88
152
|
};
|
|
89
153
|
|
|
90
154
|
const require = createRequire(import.meta.url);
|
|
91
|
-
const PKG = require(
|
|
155
|
+
const PKG = require(resolveFromExternalDirectory("package.json"));
|
|
92
156
|
const createConfiguration = (options = {
|
|
93
157
|
minification: false,
|
|
94
158
|
sourceMaps: false,
|
|
@@ -321,17 +385,21 @@ const build = async (input)=>{
|
|
|
321
385
|
return output;
|
|
322
386
|
};
|
|
323
387
|
|
|
388
|
+
const TEMPORARY_PATH = resolveFromInternalDirectory("dist", "tmp");
|
|
389
|
+
const TEMPORARY_DOWNLOAD_PATH = join(TEMPORARY_PATH, "zip");
|
|
390
|
+
const TEMPORARY_RUNTIME_PATH = join(TEMPORARY_PATH, "runtime");
|
|
324
391
|
const createCompileCommand = (program)=>{
|
|
325
392
|
return program.command({
|
|
326
393
|
name: "compile",
|
|
327
394
|
description: "Compiles the source code into a self-contained executable"
|
|
328
|
-
}).
|
|
329
|
-
key: "
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
395
|
+
}).option({
|
|
396
|
+
key: "targetInput",
|
|
397
|
+
name: {
|
|
398
|
+
long: "target",
|
|
399
|
+
short: "t"
|
|
400
|
+
},
|
|
401
|
+
description: "Set a different cross-compilation target",
|
|
402
|
+
defaultValue: "local"
|
|
335
403
|
}).task({
|
|
336
404
|
key: "config",
|
|
337
405
|
label: "Create configuration",
|
|
@@ -342,6 +410,36 @@ const createCompileCommand = (program)=>{
|
|
|
342
410
|
standalone: true
|
|
343
411
|
});
|
|
344
412
|
}
|
|
413
|
+
}).task({
|
|
414
|
+
key: "osType",
|
|
415
|
+
label ({ targetInput }) {
|
|
416
|
+
return `Get \`${targetInput}\` runtime`;
|
|
417
|
+
},
|
|
418
|
+
async handler ({ targetInput }) {
|
|
419
|
+
if (targetInput === "local") {
|
|
420
|
+
await copyFile(process.execPath, TEMPORARY_RUNTIME_PATH);
|
|
421
|
+
return getOsType(os.type());
|
|
422
|
+
}
|
|
423
|
+
const matchedRuntimeParts = matchRuntimeParts(targetInput);
|
|
424
|
+
if (!matchedRuntimeParts) {
|
|
425
|
+
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)`.");
|
|
426
|
+
}
|
|
427
|
+
const osType = getOsType(matchedRuntimeParts.os);
|
|
428
|
+
const extension = osType === "windows" ? "zip" : "tar.gz";
|
|
429
|
+
await download(`https://nodejs.org/download/release/${matchedRuntimeParts.version}/${targetInput}.${extension}`, TEMPORARY_DOWNLOAD_PATH);
|
|
430
|
+
console.log(TEMPORARY_DOWNLOAD_PATH, {
|
|
431
|
+
directoryPath: dirname(TEMPORARY_RUNTIME_PATH),
|
|
432
|
+
filename: basename(TEMPORARY_RUNTIME_PATH)
|
|
433
|
+
});
|
|
434
|
+
await unzip({
|
|
435
|
+
path: TEMPORARY_DOWNLOAD_PATH,
|
|
436
|
+
targetArchivePath: osType === "windows" ? join(targetInput, "node.exe") : join(targetInput, "bin", "node")
|
|
437
|
+
}, {
|
|
438
|
+
directoryPath: dirname(TEMPORARY_RUNTIME_PATH),
|
|
439
|
+
filename: basename(TEMPORARY_RUNTIME_PATH)
|
|
440
|
+
});
|
|
441
|
+
return osType;
|
|
442
|
+
}
|
|
345
443
|
}).task({
|
|
346
444
|
label: "Build",
|
|
347
445
|
async handler ({ config }) {
|
|
@@ -367,6 +465,22 @@ const createCompileCommand = (program)=>{
|
|
|
367
465
|
}
|
|
368
466
|
});
|
|
369
467
|
};
|
|
468
|
+
const getOsType = (input)=>{
|
|
469
|
+
switch(input){
|
|
470
|
+
case "Windows_NT":
|
|
471
|
+
case "win":
|
|
472
|
+
return "windows";
|
|
473
|
+
case "Darwin":
|
|
474
|
+
case "darwin":
|
|
475
|
+
return "macos";
|
|
476
|
+
case "Linux":
|
|
477
|
+
case "linux":
|
|
478
|
+
return "linux";
|
|
479
|
+
default:
|
|
480
|
+
throw new Error(`Unsupported operating system \`${input}\``);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
const matchRuntimeParts = createRegExpMatcher(/^node-(?<version>v\d+\.\d+\.\d+)-(?<os>darwin|linux|win)-(?<architecture>arm64|x64|x86)$/);
|
|
370
484
|
const compile = async ({ bin, input, osType })=>{
|
|
371
485
|
const inputFileName = basename(input);
|
|
372
486
|
const inputDirectory = dirname(input);
|
|
@@ -382,11 +496,11 @@ const compile = async ({ bin, input, osType })=>{
|
|
|
382
496
|
output: blobFileName,
|
|
383
497
|
useCodeCache: false,
|
|
384
498
|
useSnapshot: false
|
|
385
|
-
})
|
|
499
|
+
}));
|
|
386
500
|
await Promise.all([
|
|
387
|
-
`node --experimental-sea-config ${seaConfigFileName}
|
|
388
|
-
|
|
389
|
-
]
|
|
501
|
+
helpers.exec(`node --experimental-sea-config ${seaConfigFileName}`),
|
|
502
|
+
copyFile(TEMPORARY_RUNTIME_PATH, executableFileName)
|
|
503
|
+
]);
|
|
390
504
|
if (osType === "macos") {
|
|
391
505
|
await helpers.exec(`codesign --remove-signature ${executableFileName}`);
|
|
392
506
|
}
|
|
@@ -396,8 +510,9 @@ const compile = async ({ bin, input, osType })=>{
|
|
|
396
510
|
}
|
|
397
511
|
await Promise.all([
|
|
398
512
|
blobFileName,
|
|
399
|
-
seaConfigFileName
|
|
400
|
-
|
|
513
|
+
seaConfigFileName,
|
|
514
|
+
TEMPORARY_PATH
|
|
515
|
+
].map(async (path)=>removePath(path)));
|
|
401
516
|
};
|
|
402
517
|
|
|
403
518
|
const createBuildCommand = (program)=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickbundle",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-386ba5d",
|
|
4
4
|
"description": "The zero-configuration transpiler and bundler for the web",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ayoub Adib",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
54
54
|
"@rollup/plugin-url": "^8.0.2",
|
|
55
55
|
"@swc/core": "^1.9.1",
|
|
56
|
+
"decompress": "^4.2.1",
|
|
56
57
|
"gzip-size": "^7.0.0",
|
|
57
58
|
"rollup": "^4.24.4",
|
|
58
59
|
"rollup-plugin-dts": "^6.1.1",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"termost": "^1.2.0"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
65
|
+
"@types/decompress": "4.2.7",
|
|
64
66
|
"@types/node": "22.9.0"
|
|
65
67
|
},
|
|
66
68
|
"scripts": {
|