@yao-pkg/pkg 6.13.0 → 6.14.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.
- package/README.md +65 -28
- package/lib-es5/detector.js +0 -1
- package/lib-es5/esm-transformer.js +190 -29
- package/lib-es5/fabricator.js +0 -4
- package/lib-es5/follow.js +1 -1
- package/lib-es5/help.js +1 -2
- package/lib-es5/index.js +1 -5
- package/lib-es5/packer.js +10 -2
- package/lib-es5/resolver.js +1 -1
- package/lib-es5/walker.js +6 -4
- package/package.json +29 -18
- package/prelude/bootstrap.js +0 -6
- package/prelude/diagnostic.js +0 -2
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ After installing it, run `pkg --help` without arguments to see list of options:
|
|
|
53
53
|
– Makes executable for particular target machine
|
|
54
54
|
$ pkg -t node14-win-arm64 index.js
|
|
55
55
|
– Makes executables for target machines of your choice
|
|
56
|
-
$ pkg -t
|
|
56
|
+
$ pkg -t node20-linux,node22-linux,node22-win index.js
|
|
57
57
|
– Bakes '--expose-gc' and '--max-heap-size=34' into executable
|
|
58
58
|
$ pkg --options "expose-gc,max-heap-size=34" index.js
|
|
59
59
|
– Consider packageA and packageB to be public
|
|
@@ -82,7 +82,7 @@ The entrypoint of your project is a mandatory CLI argument. It may be:
|
|
|
82
82
|
`pkg` can generate executables for several target machines at a
|
|
83
83
|
time. You can specify a comma-separated list of targets via `--targets`
|
|
84
84
|
option. A canonical target consists of 3 elements, separated by
|
|
85
|
-
dashes, for example `
|
|
85
|
+
dashes, for example `node20-macos-x64` or `node22-linux-arm64`:
|
|
86
86
|
|
|
87
87
|
- **nodeRange** (node8), node10, node12, node14, node16 or latest
|
|
88
88
|
- **platform** alpine, linux, linuxstatic, win, macos, (freebsd)
|
|
@@ -422,6 +422,37 @@ await exec(['app.js', '--target', 'host', '--output', 'app.exe']);
|
|
|
422
422
|
// do something with app.exe, run, test, upload, deploy, etc
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
+
## ECMAScript Modules (ESM) Support
|
|
426
|
+
|
|
427
|
+
Starting from version **6.13.0**, pkg has improved support for ECMAScript Modules (ESM). Most ESM features are now automatically transformed to CommonJS during the packaging process.
|
|
428
|
+
|
|
429
|
+
### Supported ESM Features
|
|
430
|
+
|
|
431
|
+
The following ESM features are now supported and will work in your packaged executables:
|
|
432
|
+
|
|
433
|
+
- **`import` and `export` statements** - Automatically transformed to `require()` and `module.exports`
|
|
434
|
+
- **Top-level `await`** - Wrapped in an async IIFE to work in CommonJS context
|
|
435
|
+
- **Top-level `for await...of`** - Wrapped in an async IIFE to work in CommonJS context
|
|
436
|
+
- **`import.meta.url`** - Polyfilled to provide the file URL of the current module
|
|
437
|
+
- **`import.meta.dirname`** - Polyfilled to provide the directory path (Node.js 20.11+ property)
|
|
438
|
+
- **`import.meta.filename`** - Polyfilled to provide the file path (Node.js 20.11+ property)
|
|
439
|
+
|
|
440
|
+
### Known Limitations
|
|
441
|
+
|
|
442
|
+
While most ESM features work, there are some limitations to be aware of:
|
|
443
|
+
|
|
444
|
+
1. **Modules with both top-level await and exports**: Modules that use `export` statements alongside top-level `await` cannot be wrapped in an async IIFE and will not be transformed to bytecode. These modules will be included as source code instead.
|
|
445
|
+
|
|
446
|
+
2. **`import.meta.main`** and other custom properties: Only the standard `import.meta` properties listed above are polyfilled. Custom properties added by your code or other tools may not work as expected.
|
|
447
|
+
|
|
448
|
+
3. **Dynamic imports**: `import()` expressions work but may have limitations depending on the module being imported.
|
|
449
|
+
|
|
450
|
+
### Best Practices
|
|
451
|
+
|
|
452
|
+
- For entry point scripts (the main file you're packaging), feel free to use top-level await
|
|
453
|
+
- For library modules that will be imported by other code, avoid using both exports and top-level await together
|
|
454
|
+
- Test your packaged executable to ensure all ESM features work as expected in your specific use case
|
|
455
|
+
|
|
425
456
|
## Use custom Node.js binary
|
|
426
457
|
|
|
427
458
|
In case you want to use custom node binary, you can set `PKG_NODE_PATH` environment variable to the path of the node binary you want to use and `pkg` will use it instead of the default one.
|
|
@@ -535,43 +566,45 @@ or
|
|
|
535
566
|
Note: make sure not to use --debug flag in production.
|
|
536
567
|
|
|
537
568
|
### Injecting Windows Executable Metadata After Packaging
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
569
|
+
|
|
570
|
+
Executables created with `pkg` are based on a Node.js binary and, by default,
|
|
571
|
+
inherit its embedded metadata – such as version number, product name, company
|
|
572
|
+
name, icon, and description. This can be misleading or unpolished in
|
|
541
573
|
distributed applications.
|
|
542
574
|
|
|
543
575
|
There are two ways to customize the metadata of the resulting `.exe`:
|
|
576
|
+
|
|
544
577
|
1. **Use a custom Node.js binary** with your own metadata already embedded.
|
|
545
578
|
See: [Use Custom Node.js Binary](#use-custom-nodejs-binary)
|
|
546
579
|
|
|
547
|
-
2. **Post-process the generated executable** using
|
|
548
|
-
[`resedit`](https://www.npmjs.com/package/resedit), a Node.js-compatible
|
|
549
|
-
tool for modifying Windows executable resources. This allows injecting
|
|
580
|
+
2. **Post-process the generated executable** using
|
|
581
|
+
[`resedit`](https://www.npmjs.com/package/resedit), a Node.js-compatible
|
|
582
|
+
tool for modifying Windows executable resources. This allows injecting
|
|
550
583
|
correct version info, icons, copyright,
|
|
551
584
|
and more.
|
|
552
585
|
|
|
553
586
|
This section focuses on the second approach: post-processing the packaged
|
|
554
|
-
binary using
|
|
587
|
+
binary using [`resedit`](https://www.npmjs.com/package/resedit).
|
|
555
588
|
|
|
556
589
|
> ⚠️ Other tools may corrupt the executable, resulting in runtime errors such as
|
|
557
|
-
> `Pkg: Error reading from file.` –
|
|
590
|
+
> `Pkg: Error reading from file.` –
|
|
558
591
|
> [`resedit`](https://www.npmjs.com/package/resedit) has proven to work reliably
|
|
559
592
|
> with `pkg`-generated binaries.
|
|
560
593
|
|
|
561
594
|
Below is a working example for post-processing an `.exe` file using the Node.js API of [`resedit`](https://www.npmjs.com/package/resedit):
|
|
562
595
|
|
|
563
596
|
```ts
|
|
564
|
-
import * as ResEdit from
|
|
565
|
-
import * as fs from
|
|
566
|
-
import * as path from
|
|
597
|
+
import * as ResEdit from 'resedit';
|
|
598
|
+
import * as fs from 'fs';
|
|
599
|
+
import * as path from 'path';
|
|
567
600
|
|
|
568
601
|
// Set your inputs:
|
|
569
|
-
const exePath =
|
|
570
|
-
const outputPath = exePath;
|
|
571
|
-
const version =
|
|
602
|
+
const exePath = 'dist/my-tool.exe'; // Path to the generated executable
|
|
603
|
+
const outputPath = exePath; // Overwrite or use a different path
|
|
604
|
+
const version = '1.2.3'; // Your application version
|
|
572
605
|
|
|
573
|
-
const lang = 1033;
|
|
574
|
-
const codepage = 1200;
|
|
606
|
+
const lang = 1033; // en-US
|
|
607
|
+
const codepage = 1200; // Unicode
|
|
575
608
|
|
|
576
609
|
const exeData = fs.readFileSync(exePath);
|
|
577
610
|
const exe = ResEdit.NtExecutable.from(exeData);
|
|
@@ -580,19 +613,22 @@ const res = ResEdit.NtExecutableResource.from(exe);
|
|
|
580
613
|
const viList = ResEdit.Resource.VersionInfo.fromEntries(res.entries);
|
|
581
614
|
const vi = viList[0];
|
|
582
615
|
|
|
583
|
-
const [major, minor, patch] = version.split(
|
|
616
|
+
const [major, minor, patch] = version.split('.');
|
|
584
617
|
vi.setFileVersion(Number(major), Number(minor), Number(patch), 0, lang);
|
|
585
618
|
vi.setProductVersion(Number(major), Number(minor), Number(patch), 0, lang);
|
|
586
619
|
|
|
587
|
-
vi.setStringValues(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
620
|
+
vi.setStringValues(
|
|
621
|
+
{ lang, codepage },
|
|
622
|
+
{
|
|
623
|
+
FileDescription: 'ACME CLI Tool',
|
|
624
|
+
ProductName: 'ACME Application',
|
|
625
|
+
CompanyName: 'ACME Corporation',
|
|
626
|
+
ProductVersion: version,
|
|
627
|
+
FileVersion: version,
|
|
628
|
+
OriginalFilename: path.basename(exePath),
|
|
629
|
+
LegalCopyright: `© ${new Date().getFullYear()} ACME Corporation`,
|
|
630
|
+
},
|
|
631
|
+
);
|
|
596
632
|
|
|
597
633
|
vi.outputToResourceEntries(res.entries);
|
|
598
634
|
res.outputResource(exe);
|
|
@@ -610,6 +646,7 @@ The following command examples inject an icon and metadata into the executable
|
|
|
610
646
|
`dist/bin/app.exe`.
|
|
611
647
|
|
|
612
648
|
- **Example (PowerShell on Windows)**
|
|
649
|
+
|
|
613
650
|
```powershell
|
|
614
651
|
npx resedit dist/bin/app.exe dist/bin/app_with_metadata.exe `
|
|
615
652
|
--icon 1,dist/favicon.ico `
|
package/lib-es5/detector.js
CHANGED
|
@@ -86,7 +86,6 @@ function reconstructSpecifiers(specs) {
|
|
|
86
86
|
return defaults.join(', ');
|
|
87
87
|
}
|
|
88
88
|
function reconstruct(node) {
|
|
89
|
-
// @ts-expect-error Type mismatch due to @babel/types version in @types/babel__generator
|
|
90
89
|
let v = (0, generator_1.default)(node, { comments: false }).code.replace(/\n/g, '');
|
|
91
90
|
let v2;
|
|
92
91
|
while (true) {
|
|
@@ -33,39 +33,70 @@ const esbuild = __importStar(require("esbuild"));
|
|
|
33
33
|
const log_1 = require("./log");
|
|
34
34
|
const common_1 = require("./common");
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
* Wrapper for top-level await support
|
|
37
|
+
* Wraps code in an async IIFE to allow top-level await in CommonJS
|
|
38
|
+
*/
|
|
39
|
+
const ASYNC_IIFE_WRAPPER = {
|
|
40
|
+
prefix: '(async () => {\n',
|
|
41
|
+
suffix: '\n})()',
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Check if code contains import.meta usage
|
|
40
45
|
*
|
|
41
46
|
* @param code - The ESM source code to check
|
|
42
|
-
* @
|
|
43
|
-
* @returns Array of unsupported features found, or null if parse fails
|
|
47
|
+
* @returns true if import.meta is used, false otherwise
|
|
44
48
|
*/
|
|
45
|
-
function
|
|
49
|
+
function hasImportMeta(code) {
|
|
46
50
|
try {
|
|
47
51
|
const ast = babel.parse(code, {
|
|
48
52
|
sourceType: 'module',
|
|
49
53
|
plugins: [],
|
|
50
54
|
});
|
|
51
55
|
if (!ast) {
|
|
52
|
-
return
|
|
56
|
+
return false;
|
|
53
57
|
}
|
|
54
|
-
|
|
55
|
-
// @ts-expect-error Type mismatch due to @babel/types version in @types/babel__traverse
|
|
58
|
+
let found = false;
|
|
56
59
|
(0, traverse_1.default)(ast, {
|
|
57
60
|
// Detect import.meta usage
|
|
58
61
|
MetaProperty(path) {
|
|
59
62
|
if (path.node.meta.name === 'import' &&
|
|
60
63
|
path.node.property.name === 'meta') {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
line: path.node.loc?.start.line ?? null,
|
|
64
|
-
column: path.node.loc?.start.column ?? null,
|
|
65
|
-
});
|
|
64
|
+
found = true;
|
|
65
|
+
path.stop(); // Stop traversal once found
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
|
-
|
|
68
|
+
});
|
|
69
|
+
return found;
|
|
70
|
+
}
|
|
71
|
+
catch (_error) {
|
|
72
|
+
// If we can't parse, assume no import.meta
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Detect ESM features that require special handling or cannot be transformed
|
|
78
|
+
* These include:
|
|
79
|
+
* - Top-level await (can be handled with async IIFE wrapper)
|
|
80
|
+
*
|
|
81
|
+
* Note: import.meta is now supported via polyfills and is no longer in the unsupported list
|
|
82
|
+
*
|
|
83
|
+
* @param code - The ESM source code to check
|
|
84
|
+
* @param filename - The filename for error reporting
|
|
85
|
+
* @returns Object with arrays of features requiring special handling
|
|
86
|
+
*/
|
|
87
|
+
function detectESMFeatures(code, filename) {
|
|
88
|
+
try {
|
|
89
|
+
const ast = babel.parse(code, {
|
|
90
|
+
sourceType: 'module',
|
|
91
|
+
plugins: [],
|
|
92
|
+
});
|
|
93
|
+
if (!ast) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const topLevelAwait = [];
|
|
97
|
+
const unsupportedFeatures = [];
|
|
98
|
+
(0, traverse_1.default)(ast, {
|
|
99
|
+
// Detect top-level await - can be handled with async IIFE wrapper
|
|
69
100
|
AwaitExpression(path) {
|
|
70
101
|
// Check if await is at top level (not inside a function)
|
|
71
102
|
let parent = path.parentPath;
|
|
@@ -82,14 +113,14 @@ function detectUnsupportedESMFeatures(code, filename) {
|
|
|
82
113
|
parent = parent.parentPath;
|
|
83
114
|
}
|
|
84
115
|
if (isTopLevel) {
|
|
85
|
-
|
|
116
|
+
topLevelAwait.push({
|
|
86
117
|
feature: 'top-level await',
|
|
87
118
|
line: path.node.loc?.start.line ?? null,
|
|
88
119
|
column: path.node.loc?.start.column ?? null,
|
|
89
120
|
});
|
|
90
121
|
}
|
|
91
122
|
},
|
|
92
|
-
// Detect for-await-of at top level
|
|
123
|
+
// Detect for-await-of at top level - can be handled with async IIFE wrapper
|
|
93
124
|
ForOfStatement(path) {
|
|
94
125
|
if (path.node.await) {
|
|
95
126
|
let parent = path.parentPath;
|
|
@@ -106,7 +137,7 @@ function detectUnsupportedESMFeatures(code, filename) {
|
|
|
106
137
|
parent = parent.parentPath;
|
|
107
138
|
}
|
|
108
139
|
if (isTopLevel) {
|
|
109
|
-
|
|
140
|
+
topLevelAwait.push({
|
|
110
141
|
feature: 'top-level for-await-of',
|
|
111
142
|
line: path.node.loc?.start.line ?? null,
|
|
112
143
|
column: path.node.loc?.start.column ?? null,
|
|
@@ -115,14 +146,51 @@ function detectUnsupportedESMFeatures(code, filename) {
|
|
|
115
146
|
}
|
|
116
147
|
},
|
|
117
148
|
});
|
|
118
|
-
return unsupportedFeatures;
|
|
149
|
+
return { topLevelAwait, unsupportedFeatures };
|
|
119
150
|
}
|
|
120
151
|
catch (error) {
|
|
121
152
|
// If we can't parse, return null to let the transform attempt proceed
|
|
122
|
-
log_1.log.debug(`Could not parse ${filename} to detect
|
|
153
|
+
log_1.log.debug(`Could not parse ${filename} to detect ESM features: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
154
|
return null;
|
|
124
155
|
}
|
|
125
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Replace esbuild's empty import_meta object with a proper implementation
|
|
159
|
+
*
|
|
160
|
+
* When esbuild transforms ESM to CJS, it converts `import.meta` to a `const import_meta = {}`.
|
|
161
|
+
* This function replaces that empty object with a proper implementation of import.meta properties.
|
|
162
|
+
*
|
|
163
|
+
* Shims provided:
|
|
164
|
+
* - import.meta.url: File URL of the current module
|
|
165
|
+
* - import.meta.dirname: Directory path of the current module (Node.js 20.11+)
|
|
166
|
+
* - import.meta.filename: File path of the current module (Node.js 20.11+)
|
|
167
|
+
*
|
|
168
|
+
* Based on approach from tsup and esbuild discussions
|
|
169
|
+
* @see https://github.com/egoist/tsup/blob/main/assets/cjs_shims.js
|
|
170
|
+
* @see https://github.com/evanw/esbuild/issues/3839
|
|
171
|
+
*
|
|
172
|
+
* @param code - The transformed CJS code from esbuild
|
|
173
|
+
* @returns Code with import_meta properly implemented
|
|
174
|
+
*/
|
|
175
|
+
function replaceImportMetaObject(code) {
|
|
176
|
+
// esbuild generates: const import_meta = {};
|
|
177
|
+
// We need to replace this with a proper implementation
|
|
178
|
+
// Note: We use getters to ensure values are computed at runtime in the correct context
|
|
179
|
+
const shimImplementation = `const import_meta = {
|
|
180
|
+
get url() {
|
|
181
|
+
return require('url').pathToFileURL(__filename).href;
|
|
182
|
+
},
|
|
183
|
+
get dirname() {
|
|
184
|
+
return __dirname;
|
|
185
|
+
},
|
|
186
|
+
get filename() {
|
|
187
|
+
return __filename;
|
|
188
|
+
}
|
|
189
|
+
};`;
|
|
190
|
+
// Replace esbuild's empty import_meta object with our implementation
|
|
191
|
+
// Match: const import_meta = {};
|
|
192
|
+
return code.replace(/const import_meta\s*=\s*\{\s*\};/, shimImplementation);
|
|
193
|
+
}
|
|
126
194
|
/**
|
|
127
195
|
* Transform ESM code to CommonJS using esbuild
|
|
128
196
|
* This allows ESM modules to be compiled to bytecode via vm.Script
|
|
@@ -141,10 +209,13 @@ function transformESMtoCJS(code, filename) {
|
|
|
141
209
|
isTransformed: false,
|
|
142
210
|
};
|
|
143
211
|
}
|
|
144
|
-
// First, check for
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
212
|
+
// First, check for ESM features that need special handling
|
|
213
|
+
const esmFeatures = detectESMFeatures(code, filename);
|
|
214
|
+
// Handle truly unsupported features (import.meta)
|
|
215
|
+
if (esmFeatures &&
|
|
216
|
+
esmFeatures.unsupportedFeatures &&
|
|
217
|
+
esmFeatures.unsupportedFeatures.length > 0) {
|
|
218
|
+
const featureList = esmFeatures.unsupportedFeatures
|
|
148
219
|
.map((f) => {
|
|
149
220
|
const location = f.line !== null ? ` at line ${f.line}` : '';
|
|
150
221
|
return ` - ${f.feature}${location}`;
|
|
@@ -169,15 +240,100 @@ function transformESMtoCJS(code, filename) {
|
|
|
169
240
|
isTransformed: false,
|
|
170
241
|
};
|
|
171
242
|
}
|
|
243
|
+
// Check if we need to wrap in async IIFE for top-level await
|
|
244
|
+
const hasTopLevelAwait = esmFeatures &&
|
|
245
|
+
esmFeatures.topLevelAwait &&
|
|
246
|
+
esmFeatures.topLevelAwait.length > 0;
|
|
247
|
+
let codeToTransform = code;
|
|
248
|
+
// If top-level await is detected, we need to wrap in async IIFE
|
|
249
|
+
// But we must handle imports and exports specially
|
|
250
|
+
if (hasTopLevelAwait) {
|
|
251
|
+
try {
|
|
252
|
+
// Parse the code to check for exports and collect imports
|
|
253
|
+
const ast = babel.parse(code, {
|
|
254
|
+
sourceType: 'module',
|
|
255
|
+
plugins: [],
|
|
256
|
+
});
|
|
257
|
+
let hasExports = false;
|
|
258
|
+
const codeLines = code.split('\n');
|
|
259
|
+
const importLineIndices = new Set();
|
|
260
|
+
(0, traverse_1.default)(ast, {
|
|
261
|
+
ExportNamedDeclaration() {
|
|
262
|
+
hasExports = true;
|
|
263
|
+
},
|
|
264
|
+
ExportDefaultDeclaration() {
|
|
265
|
+
hasExports = true;
|
|
266
|
+
},
|
|
267
|
+
ExportAllDeclaration() {
|
|
268
|
+
hasExports = true;
|
|
269
|
+
},
|
|
270
|
+
ImportDeclaration(path) {
|
|
271
|
+
// Track import statements by line number
|
|
272
|
+
const { loc } = path.node;
|
|
273
|
+
if (loc) {
|
|
274
|
+
const { start, end } = loc;
|
|
275
|
+
for (let i = start.line; i <= end.line; i += 1) {
|
|
276
|
+
importLineIndices.add(i - 1); // Convert to 0-based index
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
if (hasExports) {
|
|
282
|
+
// If the file has exports, we can't wrap it in an IIFE
|
|
283
|
+
// because exports need to be synchronous and at the top level.
|
|
284
|
+
log_1.log.warn(`Module ${filename} has both top-level await and export statements. ` +
|
|
285
|
+
`This combination cannot be safely transformed to CommonJS in pkg's ESM transformer. ` +
|
|
286
|
+
`The original source code will be used as-is; depending on the package visibility and build configuration, ` +
|
|
287
|
+
`bytecode compilation may fail and the module may need to be loaded from source or be skipped.`);
|
|
288
|
+
return {
|
|
289
|
+
code,
|
|
290
|
+
isTransformed: false,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// If there are imports, extract them to keep outside the async IIFE
|
|
294
|
+
if (importLineIndices.size > 0) {
|
|
295
|
+
const imports = [];
|
|
296
|
+
const rest = [];
|
|
297
|
+
codeLines.forEach((line, index) => {
|
|
298
|
+
if (importLineIndices.has(index)) {
|
|
299
|
+
imports.push(line);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
rest.push(line);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
// Reconstruct: imports at top, then async IIFE wrapping the rest
|
|
306
|
+
codeToTransform = `${imports.join('\n')}\n${ASYNC_IIFE_WRAPPER.prefix}${rest.join('\n')}${ASYNC_IIFE_WRAPPER.suffix}`;
|
|
307
|
+
log_1.log.debug(`Wrapping ${filename} in async IIFE with imports extracted to top level`);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// No imports, wrap everything
|
|
311
|
+
codeToTransform =
|
|
312
|
+
ASYNC_IIFE_WRAPPER.prefix + code + ASYNC_IIFE_WRAPPER.suffix;
|
|
313
|
+
log_1.log.debug(`Wrapping ${filename} in async IIFE to support top-level await`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch (parseError) {
|
|
317
|
+
// If we can't parse, wrap everything and hope for the best
|
|
318
|
+
codeToTransform =
|
|
319
|
+
ASYNC_IIFE_WRAPPER.prefix + code + ASYNC_IIFE_WRAPPER.suffix;
|
|
320
|
+
log_1.log.warn(`Could not parse ${filename} to detect exports/imports (${parseError instanceof Error ? parseError.message : String(parseError)}). ` +
|
|
321
|
+
`Wrapping entire code in async IIFE - this may fail if the module has export or import statements.`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// Check if code uses import.meta before transformation
|
|
325
|
+
const usesImportMeta = hasImportMeta(code);
|
|
172
326
|
try {
|
|
173
|
-
|
|
327
|
+
// Build esbuild options
|
|
328
|
+
const esbuildOptions = {
|
|
174
329
|
loader: 'js',
|
|
175
330
|
format: 'cjs',
|
|
176
|
-
target: '
|
|
331
|
+
target: 'node20',
|
|
177
332
|
sourcemap: false,
|
|
178
333
|
minify: false,
|
|
179
334
|
keepNames: true,
|
|
180
|
-
}
|
|
335
|
+
};
|
|
336
|
+
const result = esbuild.transformSync(codeToTransform, esbuildOptions);
|
|
181
337
|
if (!result || !result.code) {
|
|
182
338
|
log_1.log.warn(`esbuild transform returned no code for ${filename}`);
|
|
183
339
|
return {
|
|
@@ -185,8 +341,13 @@ function transformESMtoCJS(code, filename) {
|
|
|
185
341
|
isTransformed: false,
|
|
186
342
|
};
|
|
187
343
|
}
|
|
344
|
+
// Inject import.meta shims after esbuild transformation if needed
|
|
345
|
+
let finalCode = result.code;
|
|
346
|
+
if (usesImportMeta) {
|
|
347
|
+
finalCode = replaceImportMetaObject(result.code);
|
|
348
|
+
}
|
|
188
349
|
return {
|
|
189
|
-
code:
|
|
350
|
+
code: finalCode,
|
|
190
351
|
isTransformed: true,
|
|
191
352
|
};
|
|
192
353
|
}
|
package/lib-es5/fabricator.js
CHANGED
|
@@ -64,19 +64,16 @@ function fabricate(bakes, fabricator, snap, body, cb) {
|
|
|
64
64
|
}
|
|
65
65
|
let stdout = Buffer.alloc(0);
|
|
66
66
|
function onError(error) {
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
68
67
|
removeListeners();
|
|
69
68
|
kill();
|
|
70
69
|
cb(new Error(`Failed to make bytecode ${fabricator.nodeRange}-${fabricator.arch} for file ${snap} error (${error.message})`));
|
|
71
70
|
}
|
|
72
71
|
function onClose(code) {
|
|
73
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
74
72
|
removeListeners();
|
|
75
73
|
kill();
|
|
76
74
|
if (code !== 0) {
|
|
77
75
|
return cb(new Error(`Failed to make bytecode ${fabricator.nodeRange}-${fabricator.arch} for file ${snap}`));
|
|
78
76
|
}
|
|
79
|
-
// eslint-disable-next-line no-console
|
|
80
77
|
console.log(stdout.toString());
|
|
81
78
|
return cb(new Error(`${cmd} closed unexpectedly`));
|
|
82
79
|
}
|
|
@@ -87,7 +84,6 @@ function fabricate(bakes, fabricator, snap, body, cb) {
|
|
|
87
84
|
if (stdout.length >= 4 + sizeOfBlob) {
|
|
88
85
|
const blob = Buffer.alloc(sizeOfBlob);
|
|
89
86
|
stdout.copy(blob, 0, 4, 4 + sizeOfBlob);
|
|
90
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
91
87
|
removeListeners();
|
|
92
88
|
return cb(undefined, blob);
|
|
93
89
|
}
|
package/lib-es5/follow.js
CHANGED
|
@@ -114,7 +114,7 @@ function follow(x, opts) {
|
|
|
114
114
|
// CJS package - fall through to standard CommonJS resolution
|
|
115
115
|
// to handle all callbacks properly
|
|
116
116
|
}
|
|
117
|
-
catch (
|
|
117
|
+
catch (_error) {
|
|
118
118
|
// ESM resolution failed - fall through to standard CommonJS resolution
|
|
119
119
|
}
|
|
120
120
|
}
|
package/lib-es5/help.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const colors_1 = require("./colors");
|
|
4
4
|
function help() {
|
|
5
|
-
// eslint-disable-next-line no-console
|
|
6
5
|
console.log(`
|
|
7
6
|
${colors_1.pc.bold('pkg')} [options] <input>
|
|
8
7
|
|
|
@@ -34,7 +33,7 @@ function help() {
|
|
|
34
33
|
${colors_1.pc.gray('–')} Makes executable for particular target machine
|
|
35
34
|
${colors_1.pc.cyan('$ pkg -t node14-win-arm64 index.js')}
|
|
36
35
|
${colors_1.pc.gray('–')} Makes executables for target machines of your choice
|
|
37
|
-
${colors_1.pc.cyan('$ pkg -t
|
|
36
|
+
${colors_1.pc.cyan('$ pkg -t node22-linux,node24-linux,node24-win index.js')}
|
|
38
37
|
${colors_1.pc.gray('–')} Bakes '--expose-gc' and '--max-heap-size=34' into executable
|
|
39
38
|
${colors_1.pc.cyan('$ pkg --options "expose-gc,max-heap-size=34" index.js')}
|
|
40
39
|
${colors_1.pc.gray('–')} Consider packageA and packageB to be public
|
package/lib-es5/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable require-atomic-updates */
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -30,7 +29,6 @@ function isConfiguration(file) {
|
|
|
30
29
|
}
|
|
31
30
|
// http://www.openwall.com/lists/musl/2012/12/08/4
|
|
32
31
|
const { hostArch, hostPlatform, isValidNodeRange, knownArchs, knownPlatforms, toFancyArch, toFancyPlatform, } = pkg_fetch_1.system;
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
34
32
|
const hostNodeRange = `node${process.version.match(/^v(\d+)/)[1]}`;
|
|
35
33
|
function parseTargets(items) {
|
|
36
34
|
// [ 'node6-macos-x64', 'node6-linux-x64' ]
|
|
@@ -191,7 +189,6 @@ async function exec(argv2) {
|
|
|
191
189
|
}
|
|
192
190
|
// version
|
|
193
191
|
if (argv.v || argv.version) {
|
|
194
|
-
// eslint-disable-next-line no-console
|
|
195
192
|
console.log(version);
|
|
196
193
|
return;
|
|
197
194
|
}
|
|
@@ -218,7 +215,6 @@ async function exec(argv2) {
|
|
|
218
215
|
throw (0, log_1.wasReported)(`Invalid compression algorithm ${algo} ( should be None, Brotli or Gzip)`);
|
|
219
216
|
}
|
|
220
217
|
if (doCompress !== compress_type_1.CompressType.None) {
|
|
221
|
-
// eslint-disable-next-line no-console
|
|
222
218
|
console.log('compression: ', compress_type_1.CompressType[doCompress]);
|
|
223
219
|
}
|
|
224
220
|
// _
|
|
@@ -288,7 +284,7 @@ async function exec(argv2) {
|
|
|
288
284
|
if (!(0, fs_1.existsSync)(config)) {
|
|
289
285
|
throw (0, log_1.wasReported)('Config file does not exist', [config]);
|
|
290
286
|
}
|
|
291
|
-
// eslint-disable-next-line
|
|
287
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
292
288
|
configJson = require(config); // may be either json or js
|
|
293
289
|
if (!configJson.name &&
|
|
294
290
|
!configJson.files &&
|
package/lib-es5/packer.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable complexity */
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -27,13 +26,22 @@ function hasAnyStore(record) {
|
|
|
27
26
|
}
|
|
28
27
|
function packer({ records, entrypoint, bytecode, }) {
|
|
29
28
|
const stripes = [];
|
|
30
|
-
|
|
29
|
+
// If the entrypoint was a .mjs file that got transformed, update its extension
|
|
30
|
+
if (records[entrypoint]?.wasTransformed && entrypoint.endsWith('.mjs')) {
|
|
31
|
+
entrypoint = `${entrypoint.slice(0, -4)}.js`;
|
|
32
|
+
}
|
|
33
|
+
for (let snap in records) {
|
|
31
34
|
if (records[snap]) {
|
|
32
35
|
const record = records[snap];
|
|
33
36
|
const { file } = record;
|
|
34
37
|
if (!hasAnyStore(record)) {
|
|
35
38
|
continue;
|
|
36
39
|
}
|
|
40
|
+
// If .mjs file was transformed to CJS, rename it to .js in the snapshot
|
|
41
|
+
// This prevents Node.js from treating it as an ES module
|
|
42
|
+
if (record.wasTransformed && snap.endsWith('.mjs')) {
|
|
43
|
+
snap = `${snap.slice(0, -4)}.js`;
|
|
44
|
+
}
|
|
37
45
|
(0, assert_1.default)(record[common_1.STORE_STAT], 'packer: no STORE_STAT');
|
|
38
46
|
(0, assert_1.default)(record[common_1.STORE_BLOB] ||
|
|
39
47
|
record[common_1.STORE_CONTENT] ||
|
package/lib-es5/resolver.js
CHANGED
package/lib-es5/walker.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable require-atomic-updates */
|
|
3
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
3
|
if (k2 === undefined) k2 = k;
|
|
5
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -838,7 +837,7 @@ class Walker {
|
|
|
838
837
|
record.body = Buffer.from(JSON.stringify(pkgContent, null, 2), 'utf8');
|
|
839
838
|
}
|
|
840
839
|
}
|
|
841
|
-
catch (
|
|
840
|
+
catch (_error) {
|
|
842
841
|
// Ignore JSON parsing errors
|
|
843
842
|
}
|
|
844
843
|
}
|
|
@@ -852,6 +851,10 @@ class Walker {
|
|
|
852
851
|
const result = (0, esm_transformer_1.transformESMtoCJS)(record.body.toString('utf8'), record.file);
|
|
853
852
|
if (result.isTransformed) {
|
|
854
853
|
record.body = Buffer.from(result.code, 'utf8');
|
|
854
|
+
// Mark .mjs files as transformed so packer can rename them to .js
|
|
855
|
+
if (record.file.endsWith('.mjs')) {
|
|
856
|
+
record.wasTransformed = true;
|
|
857
|
+
}
|
|
855
858
|
}
|
|
856
859
|
}
|
|
857
860
|
catch (error) {
|
|
@@ -927,7 +930,6 @@ class Walker {
|
|
|
927
930
|
switch (store) {
|
|
928
931
|
case common_1.STORE_BLOB:
|
|
929
932
|
case common_1.STORE_CONTENT:
|
|
930
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
931
933
|
await this.step_STORE_ANY(record, task.marker, store);
|
|
932
934
|
break;
|
|
933
935
|
case common_1.STORE_LINKS:
|
|
@@ -952,7 +954,7 @@ class Walker {
|
|
|
952
954
|
if (this.params.noDictionary?.includes(file)) {
|
|
953
955
|
continue;
|
|
954
956
|
}
|
|
955
|
-
// eslint-disable-next-line
|
|
957
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
956
958
|
const config = require(path_1.default.join(dd, file));
|
|
957
959
|
this.dictionary[name] = config;
|
|
958
960
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yao-pkg/pkg",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"description": "Package your Node.js project into an executable",
|
|
5
5
|
"main": "lib-es5/index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@babel/parser": "^7.23.0",
|
|
27
27
|
"@babel/traverse": "^7.23.0",
|
|
28
28
|
"@babel/types": "^7.23.0",
|
|
29
|
-
"esbuild": "^0.24.0",
|
|
30
29
|
"@yao-pkg/pkg-fetch": "3.5.32",
|
|
31
|
-
"
|
|
30
|
+
"esbuild": "^0.27.3",
|
|
31
|
+
"into-stream": "^9.1.0",
|
|
32
32
|
"minimist": "^1.2.6",
|
|
33
33
|
"multistream": "^4.1.0",
|
|
34
34
|
"picocolors": "^1.1.0",
|
|
@@ -37,35 +37,36 @@
|
|
|
37
37
|
"resolve": "^1.22.10",
|
|
38
38
|
"resolve.exports": "^2.0.3",
|
|
39
39
|
"stream-meter": "^1.0.4",
|
|
40
|
-
"tar": "^7.5.
|
|
40
|
+
"tar": "^7.5.7",
|
|
41
41
|
"tinyglobby": "^0.2.11",
|
|
42
42
|
"unzipper": "^0.12.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@release-it/conventional-changelog": "^
|
|
45
|
+
"@release-it/conventional-changelog": "^10.0.5",
|
|
46
46
|
"@types/babel__generator": "^7.6.5",
|
|
47
47
|
"@types/babel__traverse": "^7.20.3",
|
|
48
48
|
"@types/minimist": "^1.2.2",
|
|
49
49
|
"@types/multistream": "^4.1.0",
|
|
50
|
-
"@types/node": "^
|
|
50
|
+
"@types/node": "^20.0.0",
|
|
51
51
|
"@types/picomatch": "^3.0.1",
|
|
52
52
|
"@types/resolve": "^1.20.2",
|
|
53
53
|
"@types/stream-meter": "^0.0.22",
|
|
54
54
|
"@types/tar": "^6.1.13",
|
|
55
55
|
"@types/unzipper": "^0.10.10",
|
|
56
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
57
|
-
"@typescript-eslint/parser": "^
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
57
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
58
58
|
"esbuild-register": "^3.6.0",
|
|
59
|
-
"eslint": "^
|
|
59
|
+
"eslint": "^9.0.0",
|
|
60
60
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
61
|
-
"eslint-config-airbnb-typescript": "^
|
|
62
|
-
"eslint-config-prettier": "^
|
|
63
|
-
"eslint-plugin-import": "^2.
|
|
61
|
+
"eslint-config-airbnb-typescript": "^18.0.0",
|
|
62
|
+
"eslint-config-prettier": "^10.0.0",
|
|
63
|
+
"eslint-plugin-import": "^2.31.0",
|
|
64
|
+
"globals": "^17.3.0",
|
|
64
65
|
"json-stable-stringify": "^1.0.1",
|
|
65
|
-
"lint-staged": "^15.
|
|
66
|
+
"lint-staged": "^15.0.0",
|
|
66
67
|
"prettier": "^3.0.3",
|
|
67
|
-
"release-it": "^
|
|
68
|
-
"rimraf": "^
|
|
68
|
+
"release-it": "^19.2.4",
|
|
69
|
+
"rimraf": "^6.1.2",
|
|
69
70
|
"simple-git-hooks": "^2.11.1",
|
|
70
71
|
"typescript": "^4.7.2"
|
|
71
72
|
},
|
|
@@ -79,9 +80,10 @@
|
|
|
79
80
|
"fix": "npm run lint:style -- -w && npm run lint:code -- --fix",
|
|
80
81
|
"prepare": "npm run build",
|
|
81
82
|
"prepublishOnly": "npm run lint",
|
|
82
|
-
"test": "npm run build && npm run test:host && npm run test:
|
|
83
|
+
"test": "npm run build && npm run test:host && npm run test:20 && npm run test:22 && npm run test:24",
|
|
83
84
|
"test:20": "node test/test.js node20 no-npm",
|
|
84
|
-
"test:
|
|
85
|
+
"test:22": "node test/test.js node22 no-npm",
|
|
86
|
+
"test:24": "node test/test.js node24 no-npm",
|
|
85
87
|
"test:host": "node test/test.js host only-npm",
|
|
86
88
|
"bump:fetch": "yarn add @yao-pkg/pkg-fetch --exact",
|
|
87
89
|
"release": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it"
|
|
@@ -97,6 +99,15 @@
|
|
|
97
99
|
},
|
|
98
100
|
"packageManager": "yarn@1.22.22",
|
|
99
101
|
"engines": {
|
|
100
|
-
"node": ">=
|
|
102
|
+
"node": ">=20.0.0"
|
|
103
|
+
},
|
|
104
|
+
"resolutions": {
|
|
105
|
+
"lodash": "^4.17.23",
|
|
106
|
+
"@octokit/request-error": "^5.1.1",
|
|
107
|
+
"@octokit/plugin-paginate-rest": "^9.2.2",
|
|
108
|
+
"@octokit/request": "^8.4.1",
|
|
109
|
+
"js-yaml": "^4.1.1",
|
|
110
|
+
"brace-expansion": "^2.0.2",
|
|
111
|
+
"tmp": "^0.2.4"
|
|
101
112
|
}
|
|
102
113
|
}
|
package/prelude/bootstrap.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/no-unresolved */
|
|
2
|
-
/* eslint-disable global-require */
|
|
3
|
-
/* eslint-disable no-underscore-dangle */
|
|
4
|
-
/* eslint-disable prefer-rest-params */
|
|
5
|
-
/* eslint-disable prefer-spread */
|
|
6
|
-
|
|
7
1
|
/* global EXECPATH_FD */
|
|
8
2
|
/* global PAYLOAD_POSITION */
|
|
9
3
|
/* global PAYLOAD_SIZE */
|