@yao-pkg/pkg 6.12.0 → 6.13.1

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 CHANGED
@@ -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
- Executables created with `pkg` are based on a Node.js binary and, by default,
539
- inherit its embedded metadata such as version number, product name, company
540
- name, icon, and description. This can be misleading or unpolished in
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 [`resedit`](https://www.npmjs.com/package/resedit).
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 "resedit";
565
- import * as fs from "fs";
566
- import * as path from "path";
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 = "dist/my-tool.exe"; // Path to the generated executable
570
- const outputPath = exePath; // Overwrite or use a different path
571
- const version = "1.2.3"; // Your application 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; // en-US
574
- const codepage = 1200; // Unicode
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({ lang, codepage }, {
588
- FileDescription: "ACME CLI Tool",
589
- ProductName: "ACME Application",
590
- CompanyName: "ACME Corporation",
591
- ProductVersion: version,
592
- FileVersion: version,
593
- OriginalFilename: path.basename(exePath),
594
- LegalCopyright: `© ${new Date().getFullYear()} ACME Corporation`
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/common.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.toNormalizedRealPath = exports.removeUplevels = exports.stripSnapshot = exports.insideSnapshot = exports.snapshotify = exports.substituteDenominator = exports.retrieveDenominator = exports.isDotNODE = exports.isDotJSON = exports.isDotJS = exports.isPackageJson = exports.normalizePath = exports.isRootPath = exports.ALIAS_AS_RESOLVABLE = exports.ALIAS_AS_RELATIVE = exports.STORE_STAT = exports.STORE_LINKS = exports.STORE_CONTENT = exports.STORE_BLOB = void 0;
6
+ exports.isESMFile = exports.isESMPackage = exports.toNormalizedRealPath = exports.removeUplevels = exports.stripSnapshot = exports.insideSnapshot = exports.snapshotify = exports.substituteDenominator = exports.retrieveDenominator = exports.unlikelyJavascript = exports.isDotNODE = exports.isDotJSON = exports.isDotJS = exports.isPackageJson = exports.normalizePath = exports.isRootPath = exports.ALIAS_AS_RESOLVABLE = exports.ALIAS_AS_RELATIVE = exports.STORE_STAT = exports.STORE_LINKS = exports.STORE_CONTENT = exports.STORE_BLOB = void 0;
7
7
  const assert_1 = __importDefault(require("assert"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
@@ -93,6 +93,19 @@ function isDotNODE(file) {
93
93
  return path_1.default.extname(file) === '.node';
94
94
  }
95
95
  exports.isDotNODE = isDotNODE;
96
+ function unlikelyJavascript(file) {
97
+ const ext = path_1.default.extname(file);
98
+ // Check single extensions
99
+ if (['.css', '.html', '.json', '.vue'].includes(ext)) {
100
+ return true;
101
+ }
102
+ // Check for .d.ts files (compound extension)
103
+ if (file.endsWith('.d.ts')) {
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ exports.unlikelyJavascript = unlikelyJavascript;
96
109
  function replaceSlashes(file, slash) {
97
110
  if (/^.:\\/.test(file)) {
98
111
  if (slash === '/') {
@@ -232,4 +245,84 @@ function toNormalizedRealPath(requestPath) {
232
245
  return file;
233
246
  }
234
247
  exports.toNormalizedRealPath = toNormalizedRealPath;
248
+ /**
249
+ * Find the nearest package.json file by walking up the directory tree
250
+ * @param filePath - Starting file path
251
+ * @returns Path to package.json or null if not found
252
+ */
253
+ function findNearestPackageJson(filePath) {
254
+ let dir = path_1.default.dirname(filePath);
255
+ const { root } = path_1.default.parse(dir);
256
+ while (dir !== root) {
257
+ const packageJsonPath = path_1.default.join(dir, 'package.json');
258
+ if (fs_1.default.existsSync(packageJsonPath)) {
259
+ return packageJsonPath;
260
+ }
261
+ dir = path_1.default.dirname(dir);
262
+ }
263
+ return null;
264
+ }
265
+ // Caches for ESM detection performance optimization
266
+ const packageJsonCache = new Map();
267
+ const esmPackageCache = new Map();
268
+ /**
269
+ * Check if a package.json indicates an ESM package
270
+ * @param packageJsonPath - Path to package.json
271
+ * @returns true if "type": "module" is set
272
+ */
273
+ function isESMPackage(packageJsonPath) {
274
+ // Check cache first
275
+ if (esmPackageCache.has(packageJsonPath)) {
276
+ return esmPackageCache.get(packageJsonPath);
277
+ }
278
+ try {
279
+ const content = fs_1.default.readFileSync(packageJsonPath, 'utf8');
280
+ const pkg = JSON.parse(content);
281
+ const result = pkg.type === 'module';
282
+ esmPackageCache.set(packageJsonPath, result);
283
+ return result;
284
+ }
285
+ catch {
286
+ esmPackageCache.set(packageJsonPath, false);
287
+ return false;
288
+ }
289
+ }
290
+ exports.isESMPackage = isESMPackage;
291
+ /**
292
+ * Determine if a file should be treated as ESM
293
+ * Based on file extension and nearest package.json "type" field
294
+ *
295
+ * @param filePath - The file path to check
296
+ * @returns true if file should be treated as ESM
297
+ */
298
+ function isESMFile(filePath) {
299
+ // .mjs files are always ESM
300
+ if (filePath.endsWith('.mjs')) {
301
+ return true;
302
+ }
303
+ // .cjs files are never ESM
304
+ if (filePath.endsWith('.cjs')) {
305
+ return false;
306
+ }
307
+ // For .js files, check nearest package.json for "type": "module"
308
+ if (filePath.endsWith('.js')) {
309
+ const dir = path_1.default.dirname(filePath);
310
+ // Check cache first
311
+ if (packageJsonCache.has(dir)) {
312
+ const cached = packageJsonCache.get(dir);
313
+ if (cached) {
314
+ return isESMPackage(cached);
315
+ }
316
+ return false;
317
+ }
318
+ // Compute and cache
319
+ const packageJsonPath = findNearestPackageJson(filePath);
320
+ packageJsonCache.set(dir, packageJsonPath);
321
+ if (packageJsonPath) {
322
+ return isESMPackage(packageJsonPath);
323
+ }
324
+ }
325
+ return false;
326
+ }
327
+ exports.isESMFile = isESMFile;
235
328
  //# sourceMappingURL=common.js.map
@@ -86,6 +86,7 @@ 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
89
90
  let v = (0, generator_1.default)(node, { comments: false }).code.replace(/\n/g, '');
90
91
  let v2;
91
92
  while (true) {
@@ -403,13 +404,14 @@ function parse(body) {
403
404
  });
404
405
  }
405
406
  exports.parse = parse;
406
- function detect(body, visitor) {
407
+ function detect(body, visitor, file) {
407
408
  let json;
408
409
  try {
409
410
  json = parse(body);
410
411
  }
411
412
  catch (error) {
412
- log_1.log.warn(`Babel parse has failed: ${error.message}`);
413
+ const fileInfo = file ? ` in ${file}` : '';
414
+ log_1.log.warn(`Babel parse has failed: ${error.message}${fileInfo}`);
413
415
  }
414
416
  if (!json) {
415
417
  return;
@@ -0,0 +1,366 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.transformESMtoCJS = void 0;
30
+ const babel = __importStar(require("@babel/parser"));
31
+ const traverse_1 = __importDefault(require("@babel/traverse"));
32
+ const esbuild = __importStar(require("esbuild"));
33
+ const log_1 = require("./log");
34
+ const common_1 = require("./common");
35
+ /**
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
45
+ *
46
+ * @param code - The ESM source code to check
47
+ * @returns true if import.meta is used, false otherwise
48
+ */
49
+ function hasImportMeta(code) {
50
+ try {
51
+ const ast = babel.parse(code, {
52
+ sourceType: 'module',
53
+ plugins: [],
54
+ });
55
+ if (!ast) {
56
+ return false;
57
+ }
58
+ let found = false;
59
+ // @ts-expect-error Type mismatch due to @babel/types version in @types/babel__traverse
60
+ (0, traverse_1.default)(ast, {
61
+ // Detect import.meta usage
62
+ MetaProperty(path) {
63
+ if (path.node.meta.name === 'import' &&
64
+ path.node.property.name === 'meta') {
65
+ found = true;
66
+ path.stop(); // Stop traversal once found
67
+ }
68
+ },
69
+ });
70
+ return found;
71
+ }
72
+ catch (error) {
73
+ // If we can't parse, assume no import.meta
74
+ return false;
75
+ }
76
+ }
77
+ /**
78
+ * Detect ESM features that require special handling or cannot be transformed
79
+ * These include:
80
+ * - Top-level await (can be handled with async IIFE wrapper)
81
+ *
82
+ * Note: import.meta is now supported via polyfills and is no longer in the unsupported list
83
+ *
84
+ * @param code - The ESM source code to check
85
+ * @param filename - The filename for error reporting
86
+ * @returns Object with arrays of features requiring special handling
87
+ */
88
+ function detectESMFeatures(code, filename) {
89
+ try {
90
+ const ast = babel.parse(code, {
91
+ sourceType: 'module',
92
+ plugins: [],
93
+ });
94
+ if (!ast) {
95
+ return null;
96
+ }
97
+ const topLevelAwait = [];
98
+ const unsupportedFeatures = [];
99
+ // @ts-expect-error Type mismatch due to @babel/types version in @types/babel__traverse
100
+ (0, traverse_1.default)(ast, {
101
+ // Detect top-level await - can be handled with async IIFE wrapper
102
+ AwaitExpression(path) {
103
+ // Check if await is at top level (not inside a function)
104
+ let parent = path.parentPath;
105
+ let isTopLevel = true;
106
+ while (parent) {
107
+ if (parent.isFunctionDeclaration() ||
108
+ parent.isFunctionExpression() ||
109
+ parent.isArrowFunctionExpression() ||
110
+ parent.isObjectMethod() ||
111
+ parent.isClassMethod()) {
112
+ isTopLevel = false;
113
+ break;
114
+ }
115
+ parent = parent.parentPath;
116
+ }
117
+ if (isTopLevel) {
118
+ topLevelAwait.push({
119
+ feature: 'top-level await',
120
+ line: path.node.loc?.start.line ?? null,
121
+ column: path.node.loc?.start.column ?? null,
122
+ });
123
+ }
124
+ },
125
+ // Detect for-await-of at top level - can be handled with async IIFE wrapper
126
+ ForOfStatement(path) {
127
+ if (path.node.await) {
128
+ let parent = path.parentPath;
129
+ let isTopLevel = true;
130
+ while (parent) {
131
+ if (parent.isFunctionDeclaration() ||
132
+ parent.isFunctionExpression() ||
133
+ parent.isArrowFunctionExpression() ||
134
+ parent.isObjectMethod() ||
135
+ parent.isClassMethod()) {
136
+ isTopLevel = false;
137
+ break;
138
+ }
139
+ parent = parent.parentPath;
140
+ }
141
+ if (isTopLevel) {
142
+ topLevelAwait.push({
143
+ feature: 'top-level for-await-of',
144
+ line: path.node.loc?.start.line ?? null,
145
+ column: path.node.loc?.start.column ?? null,
146
+ });
147
+ }
148
+ }
149
+ },
150
+ });
151
+ return { topLevelAwait, unsupportedFeatures };
152
+ }
153
+ catch (error) {
154
+ // If we can't parse, return null to let the transform attempt proceed
155
+ log_1.log.debug(`Could not parse ${filename} to detect ESM features: ${error instanceof Error ? error.message : String(error)}`);
156
+ return null;
157
+ }
158
+ }
159
+ /**
160
+ * Replace esbuild's empty import_meta object with a proper implementation
161
+ *
162
+ * When esbuild transforms ESM to CJS, it converts `import.meta` to a `const import_meta = {}`.
163
+ * This function replaces that empty object with a proper implementation of import.meta properties.
164
+ *
165
+ * Shims provided:
166
+ * - import.meta.url: File URL of the current module
167
+ * - import.meta.dirname: Directory path of the current module (Node.js 20.11+)
168
+ * - import.meta.filename: File path of the current module (Node.js 20.11+)
169
+ *
170
+ * Based on approach from tsup and esbuild discussions
171
+ * @see https://github.com/egoist/tsup/blob/main/assets/cjs_shims.js
172
+ * @see https://github.com/evanw/esbuild/issues/3839
173
+ *
174
+ * @param code - The transformed CJS code from esbuild
175
+ * @returns Code with import_meta properly implemented
176
+ */
177
+ function replaceImportMetaObject(code) {
178
+ // esbuild generates: const import_meta = {};
179
+ // We need to replace this with a proper implementation
180
+ // Note: We use getters to ensure values are computed at runtime in the correct context
181
+ const shimImplementation = `const import_meta = {
182
+ get url() {
183
+ return require('url').pathToFileURL(__filename).href;
184
+ },
185
+ get dirname() {
186
+ return __dirname;
187
+ },
188
+ get filename() {
189
+ return __filename;
190
+ }
191
+ };`;
192
+ // Replace esbuild's empty import_meta object with our implementation
193
+ // Match: const import_meta = {};
194
+ return code.replace(/const import_meta\s*=\s*\{\s*\};/, shimImplementation);
195
+ }
196
+ /**
197
+ * Transform ESM code to CommonJS using esbuild
198
+ * This allows ESM modules to be compiled to bytecode via vm.Script
199
+ * Uses Babel parser for detecting unsupported ESM features, then esbuild for fast transformation
200
+ *
201
+ * @param code - The ESM source code to transform
202
+ * @param filename - The filename for error reporting
203
+ * @returns Object with transformed code and success flag
204
+ */
205
+ function transformESMtoCJS(code, filename) {
206
+ // Skip files that are unlikely to be JavaScript (e.g., .d.ts, .json, .css)
207
+ // to avoid Babel parse errors
208
+ if ((0, common_1.unlikelyJavascript)(filename)) {
209
+ return {
210
+ code,
211
+ isTransformed: false,
212
+ };
213
+ }
214
+ // First, check for ESM features that need special handling
215
+ const esmFeatures = detectESMFeatures(code, filename);
216
+ // Handle truly unsupported features (import.meta)
217
+ if (esmFeatures &&
218
+ esmFeatures.unsupportedFeatures &&
219
+ esmFeatures.unsupportedFeatures.length > 0) {
220
+ const featureList = esmFeatures.unsupportedFeatures
221
+ .map((f) => {
222
+ const location = f.line !== null ? ` at line ${f.line}` : '';
223
+ return ` - ${f.feature}${location}`;
224
+ })
225
+ .join('\n');
226
+ const errorMessage = [
227
+ `Cannot transform ESM module ${filename} to CommonJS:`,
228
+ `The following ESM features have no CommonJS equivalent:`,
229
+ featureList,
230
+ '',
231
+ 'These features are not supported when compiling to bytecode.',
232
+ 'Consider one of the following:',
233
+ ' 1. Refactor to avoid these features',
234
+ ' 2. Use --no-bytecode flag to keep the module as source code',
235
+ ' 3. Mark the package as public to distribute with sources',
236
+ ].join('\n');
237
+ log_1.log.warn(errorMessage);
238
+ // Return untransformed code rather than throwing
239
+ // This allows the file to be included as content instead of bytecode
240
+ return {
241
+ code,
242
+ isTransformed: false,
243
+ };
244
+ }
245
+ // Check if we need to wrap in async IIFE for top-level await
246
+ const hasTopLevelAwait = esmFeatures &&
247
+ esmFeatures.topLevelAwait &&
248
+ esmFeatures.topLevelAwait.length > 0;
249
+ let codeToTransform = code;
250
+ // If top-level await is detected, we need to wrap in async IIFE
251
+ // But we must handle imports and exports specially
252
+ if (hasTopLevelAwait) {
253
+ try {
254
+ // Parse the code to check for exports and collect imports
255
+ const ast = babel.parse(code, {
256
+ sourceType: 'module',
257
+ plugins: [],
258
+ });
259
+ let hasExports = false;
260
+ const codeLines = code.split('\n');
261
+ const importLineIndices = new Set();
262
+ // @ts-expect-error Type mismatch due to @babel/types version
263
+ (0, traverse_1.default)(ast, {
264
+ ExportNamedDeclaration() {
265
+ hasExports = true;
266
+ },
267
+ ExportDefaultDeclaration() {
268
+ hasExports = true;
269
+ },
270
+ ExportAllDeclaration() {
271
+ hasExports = true;
272
+ },
273
+ ImportDeclaration(path) {
274
+ // Track import statements by line number
275
+ const { loc } = path.node;
276
+ if (loc) {
277
+ const { start, end } = loc;
278
+ for (let i = start.line; i <= end.line; i += 1) {
279
+ importLineIndices.add(i - 1); // Convert to 0-based index
280
+ }
281
+ }
282
+ },
283
+ });
284
+ if (hasExports) {
285
+ // If the file has exports, we can't wrap it in an IIFE
286
+ // because exports need to be synchronous and at the top level.
287
+ log_1.log.warn(`Module ${filename} has both top-level await and export statements. ` +
288
+ `This combination cannot be safely transformed to CommonJS in pkg's ESM transformer. ` +
289
+ `The original source code will be used as-is; depending on the package visibility and build configuration, ` +
290
+ `bytecode compilation may fail and the module may need to be loaded from source or be skipped.`);
291
+ return {
292
+ code,
293
+ isTransformed: false,
294
+ };
295
+ }
296
+ // If there are imports, extract them to keep outside the async IIFE
297
+ if (importLineIndices.size > 0) {
298
+ const imports = [];
299
+ const rest = [];
300
+ codeLines.forEach((line, index) => {
301
+ if (importLineIndices.has(index)) {
302
+ imports.push(line);
303
+ }
304
+ else {
305
+ rest.push(line);
306
+ }
307
+ });
308
+ // Reconstruct: imports at top, then async IIFE wrapping the rest
309
+ codeToTransform = `${imports.join('\n')}\n${ASYNC_IIFE_WRAPPER.prefix}${rest.join('\n')}${ASYNC_IIFE_WRAPPER.suffix}`;
310
+ log_1.log.debug(`Wrapping ${filename} in async IIFE with imports extracted to top level`);
311
+ }
312
+ else {
313
+ // No imports, wrap everything
314
+ codeToTransform =
315
+ ASYNC_IIFE_WRAPPER.prefix + code + ASYNC_IIFE_WRAPPER.suffix;
316
+ log_1.log.debug(`Wrapping ${filename} in async IIFE to support top-level await`);
317
+ }
318
+ }
319
+ catch (parseError) {
320
+ // If we can't parse, wrap everything and hope for the best
321
+ codeToTransform =
322
+ ASYNC_IIFE_WRAPPER.prefix + code + ASYNC_IIFE_WRAPPER.suffix;
323
+ log_1.log.warn(`Could not parse ${filename} to detect exports/imports (${parseError instanceof Error ? parseError.message : String(parseError)}). ` +
324
+ `Wrapping entire code in async IIFE - this may fail if the module has export or import statements.`);
325
+ }
326
+ }
327
+ // Check if code uses import.meta before transformation
328
+ const usesImportMeta = hasImportMeta(code);
329
+ try {
330
+ // Build esbuild options
331
+ const esbuildOptions = {
332
+ loader: 'js',
333
+ format: 'cjs',
334
+ target: 'node18',
335
+ sourcemap: false,
336
+ minify: false,
337
+ keepNames: true,
338
+ };
339
+ const result = esbuild.transformSync(codeToTransform, esbuildOptions);
340
+ if (!result || !result.code) {
341
+ log_1.log.warn(`esbuild transform returned no code for ${filename}`);
342
+ return {
343
+ code,
344
+ isTransformed: false,
345
+ };
346
+ }
347
+ // Inject import.meta shims after esbuild transformation if needed
348
+ let finalCode = result.code;
349
+ if (usesImportMeta) {
350
+ finalCode = replaceImportMetaObject(result.code);
351
+ }
352
+ return {
353
+ code: finalCode,
354
+ isTransformed: true,
355
+ };
356
+ }
357
+ catch (error) {
358
+ log_1.log.warn(`Failed to transform ESM to CJS for ${filename}: ${error instanceof Error ? error.message : String(error)}`);
359
+ return {
360
+ code,
361
+ isTransformed: false,
362
+ };
363
+ }
364
+ }
365
+ exports.transformESMtoCJS = transformESMtoCJS;
366
+ //# sourceMappingURL=esm-transformer.js.map