@scelar/nodepod 1.0.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/LICENSE +43 -0
- package/README.md +240 -0
- package/dist/child_process-BJOMsZje.js +8233 -0
- package/dist/child_process-BJOMsZje.js.map +1 -0
- package/dist/child_process-Cj8vOcuc.cjs +7434 -0
- package/dist/child_process-Cj8vOcuc.cjs.map +1 -0
- package/dist/index-Cb1Cgdnd.js +35308 -0
- package/dist/index-Cb1Cgdnd.js.map +1 -0
- package/dist/index-DsMGS-xc.cjs +37195 -0
- package/dist/index-DsMGS-xc.cjs.map +1 -0
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +59 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +95 -0
- package/src/__tests__/smoke.test.ts +11 -0
- package/src/constants/cdn-urls.ts +18 -0
- package/src/constants/config.ts +236 -0
- package/src/cross-origin.ts +26 -0
- package/src/engine-factory.ts +176 -0
- package/src/engine-types.ts +56 -0
- package/src/helpers/byte-encoding.ts +39 -0
- package/src/helpers/digest.ts +9 -0
- package/src/helpers/event-loop.ts +96 -0
- package/src/helpers/wasm-cache.ts +133 -0
- package/src/iframe-sandbox.ts +141 -0
- package/src/index.ts +192 -0
- package/src/isolation-helpers.ts +148 -0
- package/src/memory-volume.ts +941 -0
- package/src/module-transformer.ts +368 -0
- package/src/packages/archive-extractor.ts +248 -0
- package/src/packages/browser-bundler.ts +284 -0
- package/src/packages/installer.ts +396 -0
- package/src/packages/registry-client.ts +131 -0
- package/src/packages/version-resolver.ts +411 -0
- package/src/polyfills/assert.ts +384 -0
- package/src/polyfills/async_hooks.ts +144 -0
- package/src/polyfills/buffer.ts +628 -0
- package/src/polyfills/child_process.ts +2288 -0
- package/src/polyfills/chokidar.ts +336 -0
- package/src/polyfills/cluster.ts +106 -0
- package/src/polyfills/console.ts +136 -0
- package/src/polyfills/constants.ts +123 -0
- package/src/polyfills/crypto.ts +885 -0
- package/src/polyfills/dgram.ts +87 -0
- package/src/polyfills/diagnostics_channel.ts +76 -0
- package/src/polyfills/dns.ts +134 -0
- package/src/polyfills/domain.ts +68 -0
- package/src/polyfills/esbuild.ts +854 -0
- package/src/polyfills/events.ts +276 -0
- package/src/polyfills/fs.ts +2888 -0
- package/src/polyfills/fsevents.ts +79 -0
- package/src/polyfills/http.ts +1449 -0
- package/src/polyfills/http2.ts +199 -0
- package/src/polyfills/https.ts +76 -0
- package/src/polyfills/inspector.ts +62 -0
- package/src/polyfills/lightningcss.ts +105 -0
- package/src/polyfills/module.ts +191 -0
- package/src/polyfills/net.ts +353 -0
- package/src/polyfills/os.ts +238 -0
- package/src/polyfills/path.ts +206 -0
- package/src/polyfills/perf_hooks.ts +102 -0
- package/src/polyfills/process.ts +690 -0
- package/src/polyfills/punycode.ts +159 -0
- package/src/polyfills/querystring.ts +93 -0
- package/src/polyfills/quic.ts +118 -0
- package/src/polyfills/readdirp.ts +229 -0
- package/src/polyfills/readline.ts +692 -0
- package/src/polyfills/repl.ts +134 -0
- package/src/polyfills/rollup.ts +119 -0
- package/src/polyfills/sea.ts +33 -0
- package/src/polyfills/sqlite.ts +78 -0
- package/src/polyfills/stream.ts +1620 -0
- package/src/polyfills/string_decoder.ts +25 -0
- package/src/polyfills/tailwindcss-oxide.ts +309 -0
- package/src/polyfills/test.ts +197 -0
- package/src/polyfills/timers.ts +32 -0
- package/src/polyfills/tls.ts +105 -0
- package/src/polyfills/trace_events.ts +50 -0
- package/src/polyfills/tty.ts +71 -0
- package/src/polyfills/url.ts +174 -0
- package/src/polyfills/util.ts +559 -0
- package/src/polyfills/v8.ts +126 -0
- package/src/polyfills/vm.ts +132 -0
- package/src/polyfills/volume-registry.ts +15 -0
- package/src/polyfills/wasi.ts +44 -0
- package/src/polyfills/worker_threads.ts +326 -0
- package/src/polyfills/ws.ts +595 -0
- package/src/polyfills/zlib.ts +881 -0
- package/src/request-proxy.ts +716 -0
- package/src/script-engine.ts +3375 -0
- package/src/sdk/nodepod-fs.ts +93 -0
- package/src/sdk/nodepod-process.ts +86 -0
- package/src/sdk/nodepod-terminal.ts +350 -0
- package/src/sdk/nodepod.ts +509 -0
- package/src/sdk/types.ts +70 -0
- package/src/shell/commands/bun.ts +121 -0
- package/src/shell/commands/directory.ts +297 -0
- package/src/shell/commands/file-ops.ts +525 -0
- package/src/shell/commands/git.ts +2142 -0
- package/src/shell/commands/node.ts +80 -0
- package/src/shell/commands/npm.ts +198 -0
- package/src/shell/commands/pm-types.ts +45 -0
- package/src/shell/commands/pnpm.ts +82 -0
- package/src/shell/commands/search.ts +264 -0
- package/src/shell/commands/shell-env.ts +352 -0
- package/src/shell/commands/text-processing.ts +1152 -0
- package/src/shell/commands/yarn.ts +84 -0
- package/src/shell/shell-builtins.ts +19 -0
- package/src/shell/shell-helpers.ts +250 -0
- package/src/shell/shell-interpreter.ts +514 -0
- package/src/shell/shell-parser.ts +429 -0
- package/src/shell/shell-types.ts +85 -0
- package/src/syntax-transforms.ts +561 -0
- package/src/threading/engine-worker.ts +64 -0
- package/src/threading/inline-worker.ts +372 -0
- package/src/threading/offload-types.ts +112 -0
- package/src/threading/offload-worker.ts +383 -0
- package/src/threading/offload.ts +271 -0
- package/src/threading/process-context.ts +92 -0
- package/src/threading/process-handle.ts +275 -0
- package/src/threading/process-manager.ts +956 -0
- package/src/threading/process-worker-entry.ts +854 -0
- package/src/threading/shared-vfs.ts +352 -0
- package/src/threading/sync-channel.ts +135 -0
- package/src/threading/task-queue.ts +177 -0
- package/src/threading/vfs-bridge.ts +231 -0
- package/src/threading/worker-pool.ts +233 -0
- package/src/threading/worker-protocol.ts +358 -0
- package/src/threading/worker-vfs.ts +218 -0
- package/src/types/externals.d.ts +38 -0
- package/src/types/fs-streams.ts +142 -0
- package/src/types/manifest.ts +17 -0
- package/src/worker-sandbox.ts +90 -0
|
@@ -0,0 +1,3375 @@
|
|
|
1
|
+
// ScriptEngine — JS execution engine with require(), module resolution,
|
|
2
|
+
// ESM-to-CJS conversion, and Node.js polyfills. Runs in the browser.
|
|
3
|
+
|
|
4
|
+
import { MemoryVolume } from "./memory-volume";
|
|
5
|
+
import type {
|
|
6
|
+
IScriptEngine,
|
|
7
|
+
ExecutionOutcome,
|
|
8
|
+
EngineConfig,
|
|
9
|
+
LoadedModule,
|
|
10
|
+
} from "./engine-types";
|
|
11
|
+
import type { PackageManifest } from "./types/manifest";
|
|
12
|
+
import { quickDigest } from "./helpers/digest";
|
|
13
|
+
import { bytesToBase64, bytesToHex } from "./helpers/byte-encoding";
|
|
14
|
+
import { buildFileSystemBridge, FsBridge } from "./polyfills/fs";
|
|
15
|
+
import * as pathPolyfill from "./polyfills/path";
|
|
16
|
+
import {
|
|
17
|
+
RESOLVE_EXTENSIONS,
|
|
18
|
+
MAIN_FIELD_EXTENSIONS,
|
|
19
|
+
INDEX_FILES,
|
|
20
|
+
IMPORTS_FIELD_EXTENSIONS,
|
|
21
|
+
LIMITS,
|
|
22
|
+
} from "./constants/config";
|
|
23
|
+
import { buildProcessEnv, ProcessObject } from "./polyfills/process";
|
|
24
|
+
import * as httpPolyfill from "./polyfills/http";
|
|
25
|
+
import * as httpsPolyfill from "./polyfills/https";
|
|
26
|
+
import * as tcpPolyfill from "./polyfills/net";
|
|
27
|
+
import eventBusPolyfill from "./polyfills/events";
|
|
28
|
+
import streamPolyfill from "./polyfills/stream";
|
|
29
|
+
import * as urlPolyfill from "./polyfills/url";
|
|
30
|
+
import * as qsPolyfill from "./polyfills/querystring";
|
|
31
|
+
import * as helpersPolyfill from "./polyfills/util";
|
|
32
|
+
import * as ttyPolyfill from "./polyfills/tty";
|
|
33
|
+
import * as osPolyfill from "./polyfills/os";
|
|
34
|
+
import * as hashingPolyfill from "./polyfills/crypto";
|
|
35
|
+
import * as compressionPolyfill from "./polyfills/zlib";
|
|
36
|
+
import * as dnsPolyfill from "./polyfills/dns";
|
|
37
|
+
import bufferPolyfill from "./polyfills/buffer";
|
|
38
|
+
// child_process is lazy-loaded to avoid pulling in the shell at import time
|
|
39
|
+
let _shellExecPolyfill: any = null;
|
|
40
|
+
let _initShellExec: ((vol: any) => void) | null = null;
|
|
41
|
+
const shellExecProxy = new Proxy({} as any, {
|
|
42
|
+
get(_target, prop) {
|
|
43
|
+
if (!_shellExecPolyfill) return undefined;
|
|
44
|
+
return _shellExecPolyfill[prop];
|
|
45
|
+
},
|
|
46
|
+
ownKeys() {
|
|
47
|
+
if (!_shellExecPolyfill) return [];
|
|
48
|
+
return Reflect.ownKeys(_shellExecPolyfill);
|
|
49
|
+
},
|
|
50
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
51
|
+
if (!_shellExecPolyfill) return undefined;
|
|
52
|
+
return Object.getOwnPropertyDescriptor(_shellExecPolyfill, prop);
|
|
53
|
+
},
|
|
54
|
+
has(_target, prop) {
|
|
55
|
+
if (!_shellExecPolyfill) return false;
|
|
56
|
+
return prop in _shellExecPolyfill;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Set eagerly so require('child_process') works sync in workers.
|
|
61
|
+
// The async .then() in the constructor fires too late for top-level require() calls.
|
|
62
|
+
export function setChildProcessPolyfill(mod: any): void {
|
|
63
|
+
_shellExecPolyfill = mod;
|
|
64
|
+
_initShellExec = mod.initShellExec;
|
|
65
|
+
}
|
|
66
|
+
import { getProxyInstance } from "./request-proxy";
|
|
67
|
+
import * as watcherPolyfill from "./polyfills/chokidar";
|
|
68
|
+
import * as wsPolyfill from "./polyfills/ws";
|
|
69
|
+
import * as macEventsPolyfill from "./polyfills/fsevents";
|
|
70
|
+
import * as scannerPolyfill from "./polyfills/readdirp";
|
|
71
|
+
import * as moduleSysPolyfill from "./polyfills/module";
|
|
72
|
+
import * as perfPolyfill from "./polyfills/perf_hooks";
|
|
73
|
+
import * as threadPoolPolyfill from "./polyfills/worker_threads";
|
|
74
|
+
import * as esbuildPolyfill from "./polyfills/esbuild";
|
|
75
|
+
import * as rollupPolyfill from "./polyfills/rollup";
|
|
76
|
+
import * as v8Polyfill from "./polyfills/v8";
|
|
77
|
+
import * as lineReaderPolyfill from "./polyfills/readline";
|
|
78
|
+
import * as tlsPolyfill from "./polyfills/tls";
|
|
79
|
+
import * as http2Polyfill from "./polyfills/http2";
|
|
80
|
+
import * as clusterPolyfill from "./polyfills/cluster";
|
|
81
|
+
import * as udpPolyfill from "./polyfills/dgram";
|
|
82
|
+
import * as vmPolyfill from "./polyfills/vm";
|
|
83
|
+
import * as debugPolyfill from "./polyfills/inspector";
|
|
84
|
+
import * as asyncCtxPolyfill from "./polyfills/async_hooks";
|
|
85
|
+
import * as domainPolyfill from "./polyfills/domain";
|
|
86
|
+
import * as tracePolyfill from "./polyfills/diagnostics_channel";
|
|
87
|
+
import * as consolePolyfill from "./polyfills/console";
|
|
88
|
+
import * as replPolyfill from "./polyfills/repl";
|
|
89
|
+
import * as testPolyfill from "./polyfills/test";
|
|
90
|
+
import * as traceEventsPolyfill from "./polyfills/trace_events";
|
|
91
|
+
import * as wasiPolyfill from "./polyfills/wasi";
|
|
92
|
+
import * as seaPolyfill from "./polyfills/sea";
|
|
93
|
+
import * as sqlitePolyfill from "./polyfills/sqlite";
|
|
94
|
+
import * as quicPolyfill from "./polyfills/quic";
|
|
95
|
+
import * as lightningcssPolyfill from "./polyfills/lightningcss";
|
|
96
|
+
import * as tailwindOxidePolyfill from "./polyfills/tailwindcss-oxide";
|
|
97
|
+
import {
|
|
98
|
+
promises as streamPromises,
|
|
99
|
+
Readable,
|
|
100
|
+
Writable,
|
|
101
|
+
Duplex,
|
|
102
|
+
Transform,
|
|
103
|
+
PassThrough,
|
|
104
|
+
} from "./polyfills/stream";
|
|
105
|
+
import { promises as dnsPromises } from "./polyfills/dns";
|
|
106
|
+
import { promises as readlinePromises } from "./polyfills/readline";
|
|
107
|
+
|
|
108
|
+
import assertPolyfill from "./polyfills/assert";
|
|
109
|
+
import stringDecoderPolyfill from "./polyfills/string_decoder";
|
|
110
|
+
import timersPolyfill from "./polyfills/timers";
|
|
111
|
+
import { promises as timersPromises } from "./polyfills/timers";
|
|
112
|
+
import * as punycodePolyfill from "./polyfills/punycode";
|
|
113
|
+
import constantsPolyfill from "./polyfills/constants";
|
|
114
|
+
import {
|
|
115
|
+
resolve as resolveExports,
|
|
116
|
+
imports as resolveImports,
|
|
117
|
+
} from "resolve.exports";
|
|
118
|
+
import {
|
|
119
|
+
esmToCjs,
|
|
120
|
+
stripTopLevelAwait,
|
|
121
|
+
hasTopLevelAwait,
|
|
122
|
+
} from "./syntax-transforms";
|
|
123
|
+
import { getCachedModule, precompileWasm, compileWasmInWorker } from "./helpers/wasm-cache";
|
|
124
|
+
import * as acorn from "acorn";
|
|
125
|
+
|
|
126
|
+
// ── TypeScript type stripper ──
|
|
127
|
+
// Regex-based stripping of TS syntax so acorn/eval can handle it at runtime.
|
|
128
|
+
|
|
129
|
+
function stripTypeScript(source: string): string {
|
|
130
|
+
let s = source;
|
|
131
|
+
|
|
132
|
+
s = s.replace(
|
|
133
|
+
/^\s*declare\s+(module|namespace|global)\s+[^{]*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/gm,
|
|
134
|
+
"",
|
|
135
|
+
);
|
|
136
|
+
s = s.replace(
|
|
137
|
+
/^\s*declare\s+(?:const|let|var|function|class|enum|type|interface)\s+[^;\n]+[;\n]/gm,
|
|
138
|
+
"",
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
s = s.replace(
|
|
142
|
+
/^\s*(?:export\s+)?interface\s+\w+(?:\s+extends\s+[^{]+)?\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/gm,
|
|
143
|
+
"",
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
s = s.replace(/^\s*(?:export\s+)?type\s+\w+(?:<[^>]*>)?\s*=\s*[^;]+;/gm, "");
|
|
147
|
+
|
|
148
|
+
s = s.replace(
|
|
149
|
+
/^\s*export\s+type\s*\{[^}]*\}\s*(?:from\s*['"][^'"]*['"])?\s*;?/gm,
|
|
150
|
+
"",
|
|
151
|
+
);
|
|
152
|
+
s = s.replace(
|
|
153
|
+
/^\s*import\s+type\s+(?:\{[^}]*\}|\w+)\s*(?:from\s*['"][^'"]*['"])?\s*;?/gm,
|
|
154
|
+
"",
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
s = s.replace(/\bas\s+const\b/g, "");
|
|
158
|
+
s = s.replace(/\s+as\s+(?:[A-Z][\w.<>,\s|&\[\]]*)/g, "");
|
|
159
|
+
|
|
160
|
+
s = s.replace(
|
|
161
|
+
/(function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:function\s*)?)\s*<[^(]*?>\s*\(/g,
|
|
162
|
+
"$1(",
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Strip type annotations from params and return types
|
|
166
|
+
s = s.replace(
|
|
167
|
+
/:\s*(?:readonly\s+)?(?:[A-Z][\w.<>,\s|&\[\]]*|string|number|boolean|void|any|never|unknown|null|undefined|object|bigint)(?:\s*\|\s*(?:[A-Z][\w.<>,\s|&\[\]]*|string|number|boolean|void|any|never|unknown|null|undefined|object|bigint))*/g,
|
|
168
|
+
(match, offset) => {
|
|
169
|
+
// Heuristic: only strip if we're in a signature context, not object literals
|
|
170
|
+
const before = s.slice(Math.max(0, offset - 40), offset);
|
|
171
|
+
if (/[,(?]\s*\w+\s*\??$/.test(before)) return "";
|
|
172
|
+
if (/\)\s*$/.test(before)) return "";
|
|
173
|
+
if (/\(\s*\w+\s*\??$/.test(before)) return "";
|
|
174
|
+
if (/\b(?:const|let|var)\s+\w+$/.test(before)) return "";
|
|
175
|
+
return match;
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
s = s.replace(/(\w)!\./g, "$1.");
|
|
180
|
+
s = s.replace(/(\w)!\)/g, "$1)");
|
|
181
|
+
s = s.replace(/(\w)!\,/g, "$1,");
|
|
182
|
+
|
|
183
|
+
s = s.replace(
|
|
184
|
+
/(?<!=)\s*<(?:string|number|boolean|any|unknown|never|void|object|bigint|Record|Partial|Required|Readonly|Pick|Omit|Extract|Exclude|Array|Promise|Set|Map)\b[^>]*>/g,
|
|
185
|
+
"",
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Convert enums to plain objects
|
|
189
|
+
s = s.replace(
|
|
190
|
+
/^\s*(?:export\s+)?(?:const\s+)?enum\s+(\w+)\s*\{([^}]*)\}/gm,
|
|
191
|
+
(_, name, body) => {
|
|
192
|
+
const entries = body
|
|
193
|
+
.split(",")
|
|
194
|
+
.map((e: string) => e.trim())
|
|
195
|
+
.filter(Boolean);
|
|
196
|
+
const obj: string[] = [];
|
|
197
|
+
let autoVal = 0;
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
const eqIdx = entry.indexOf("=");
|
|
200
|
+
if (eqIdx !== -1) {
|
|
201
|
+
const key = entry.slice(0, eqIdx).trim();
|
|
202
|
+
const val = entry.slice(eqIdx + 1).trim();
|
|
203
|
+
obj.push(`${JSON.stringify(key)}: ${val}`);
|
|
204
|
+
const numVal = Number(val);
|
|
205
|
+
if (!isNaN(numVal)) autoVal = numVal + 1;
|
|
206
|
+
} else {
|
|
207
|
+
obj.push(`${JSON.stringify(entry)}: ${autoVal}`);
|
|
208
|
+
autoVal++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return `var ${name} = {${obj.join(", ")}};`;
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
s = s.replace(/^\s*(public|private|protected)?\s*readonly\s+/gm, "$1 ");
|
|
216
|
+
s = s.replace(/\b(public|private|protected)\s+(?=\w+[\s,):])/g, "");
|
|
217
|
+
s = s.replace(/\babstract\s+(class|extends)/g, "$1");
|
|
218
|
+
s = s.replace(/\bimplements\s+[\w.,\s<>]+(?=\s*\{)/g, "");
|
|
219
|
+
s = s.replace(/\boverride\s+(?=\w)/g, "");
|
|
220
|
+
|
|
221
|
+
return s;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function isTypeScriptFile(filename: string): boolean {
|
|
225
|
+
const clean = filename.split("?")[0];
|
|
226
|
+
if (
|
|
227
|
+
clean.endsWith(".ts") ||
|
|
228
|
+
clean.endsWith(".tsx") ||
|
|
229
|
+
clean.endsWith(".mts")
|
|
230
|
+
) return true;
|
|
231
|
+
// Vite SFC query params like ?type=script&lang.ts
|
|
232
|
+
if (filename.includes("lang.ts") || filename.includes("lang=ts")) return true;
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// CSS files must never go through stripTypeScript
|
|
237
|
+
function isCSSFile(filename: string): boolean {
|
|
238
|
+
const clean = filename.split("?")[0];
|
|
239
|
+
if (
|
|
240
|
+
clean.endsWith(".css") ||
|
|
241
|
+
clean.endsWith(".scss") ||
|
|
242
|
+
clean.endsWith(".sass") ||
|
|
243
|
+
clean.endsWith(".less") ||
|
|
244
|
+
clean.endsWith(".styl") ||
|
|
245
|
+
clean.endsWith(".stylus") ||
|
|
246
|
+
clean.endsWith(".postcss")
|
|
247
|
+
) return true;
|
|
248
|
+
if (filename.includes("type=style")) return true;
|
|
249
|
+
if (/lang[.=](?:css|scss|sass|less|styl|stylus|postcss)/.test(filename)) return true;
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Fallback heuristic when filename doesn't indicate TS
|
|
254
|
+
function looksLikeTypeScript(source: string): boolean {
|
|
255
|
+
return (
|
|
256
|
+
/\b(?:interface|type)\s+\w+/.test(source) ||
|
|
257
|
+
/:\s*(?:string|number|boolean|void|any|never|unknown|Record|Array|Promise)\b/.test(source) ||
|
|
258
|
+
/(?:as\s+(?:string|number|boolean|any|const)\b)/.test(source)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ── AST walk helper ──
|
|
263
|
+
function traverseAst(node: any, visitor: (n: any) => void): void {
|
|
264
|
+
if (!node || typeof node !== "object") return;
|
|
265
|
+
if (typeof node.type === "string") visitor(node);
|
|
266
|
+
for (const key of Object.keys(node)) {
|
|
267
|
+
if (
|
|
268
|
+
key === "type" ||
|
|
269
|
+
key === "start" ||
|
|
270
|
+
key === "end" ||
|
|
271
|
+
key === "loc" ||
|
|
272
|
+
key === "range"
|
|
273
|
+
)
|
|
274
|
+
continue;
|
|
275
|
+
const val = node[key];
|
|
276
|
+
if (val && typeof val === "object") {
|
|
277
|
+
if (Array.isArray(val)) {
|
|
278
|
+
for (const item of val) {
|
|
279
|
+
if (item && typeof item === "object" && typeof item.type === "string")
|
|
280
|
+
traverseAst(item, visitor);
|
|
281
|
+
}
|
|
282
|
+
} else if (typeof val.type === "string") {
|
|
283
|
+
traverseAst(val, visitor);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ── Dynamic import regex fallback ──
|
|
290
|
+
function rewriteDynamicImportsRegex(source: string): string {
|
|
291
|
+
return source.replace(/(?<![.$\w])import\s*\(/g, "__asyncLoad(");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ── ESM → CJS conversion ──
|
|
295
|
+
function convertModuleSyntax(source: string, filePath: string): string {
|
|
296
|
+
if (!/\bimport\b|\bexport\b/.test(source)) return source;
|
|
297
|
+
try {
|
|
298
|
+
return convertViaAst(source, filePath);
|
|
299
|
+
} catch (astErr) {
|
|
300
|
+
_nativeConsole.warn("[convertModuleSyntax] AST parse failed for", filePath, "falling back to regex:", astErr instanceof Error ? astErr.message : String(astErr));
|
|
301
|
+
return convertViaRegex(source, filePath);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function convertViaAst(source: string, filePath: string): string {
|
|
306
|
+
const ast = acorn.parse(source, {
|
|
307
|
+
ecmaVersion: "latest",
|
|
308
|
+
sourceType: "module",
|
|
309
|
+
}) as any;
|
|
310
|
+
const patches: Array<[number, number, string]> = [];
|
|
311
|
+
|
|
312
|
+
traverseAst(ast, (node: any) => {
|
|
313
|
+
if (
|
|
314
|
+
node.type === "MetaProperty" &&
|
|
315
|
+
node.meta?.name === "import" &&
|
|
316
|
+
node.property?.name === "meta"
|
|
317
|
+
) {
|
|
318
|
+
patches.push([node.start, node.end, "import_meta"]);
|
|
319
|
+
}
|
|
320
|
+
if (node.type === "ImportExpression") {
|
|
321
|
+
patches.push([node.start, node.start + 6, "__asyncLoad"]);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const hasImportDecl = ast.body.some(
|
|
326
|
+
(n: any) => n.type === "ImportDeclaration",
|
|
327
|
+
);
|
|
328
|
+
const hasExportDecl = ast.body.some((n: any) => n.type?.startsWith("Export"));
|
|
329
|
+
|
|
330
|
+
let output = source;
|
|
331
|
+
patches.sort((a, b) => b[0] - a[0]);
|
|
332
|
+
for (const [s, e, r] of patches)
|
|
333
|
+
output = output.slice(0, s) + r + output.slice(e);
|
|
334
|
+
|
|
335
|
+
if (hasImportDecl || hasExportDecl) {
|
|
336
|
+
output = esmToCjs(output);
|
|
337
|
+
if (hasExportDecl) {
|
|
338
|
+
output =
|
|
339
|
+
'Object.defineProperty(exports, "__esModule", { value: true });\n' +
|
|
340
|
+
output;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// .mjs files with `const require = createRequire(...)` hit TDZ after esmToCjs
|
|
345
|
+
output = demoteLexicalRequire(output);
|
|
346
|
+
|
|
347
|
+
return output;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Demote `const/let require =` to plain assignment to avoid TDZ with esmToCjs-generated require() calls
|
|
351
|
+
function demoteLexicalRequire(code: string): string {
|
|
352
|
+
if (!/\b(?:const|let)\s+require\s*=/.test(code)) return code;
|
|
353
|
+
return code.replace(
|
|
354
|
+
/\b(const|let)\s+(require)\s*=/g,
|
|
355
|
+
"require =",
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Builds the IIFE wrapper that sandboxes user code with shimmed globals
|
|
360
|
+
function buildModuleWrapper(
|
|
361
|
+
code: string,
|
|
362
|
+
opts: {
|
|
363
|
+
async?: boolean;
|
|
364
|
+
useNativePromise?: boolean;
|
|
365
|
+
includeViteVars?: boolean;
|
|
366
|
+
hideBrowserGlobals?: boolean;
|
|
367
|
+
wasmHelpers?: boolean;
|
|
368
|
+
} = {},
|
|
369
|
+
): string {
|
|
370
|
+
const {
|
|
371
|
+
async: isAsync = false,
|
|
372
|
+
useNativePromise = false,
|
|
373
|
+
includeViteVars = true,
|
|
374
|
+
hideBrowserGlobals = true,
|
|
375
|
+
wasmHelpers = false,
|
|
376
|
+
} = opts;
|
|
377
|
+
|
|
378
|
+
const promiseVar = useNativePromise ? "globalThis.Promise" : "$SyncPromise";
|
|
379
|
+
const fnKeyword = isAsync ? "async function" : "function";
|
|
380
|
+
|
|
381
|
+
let vars = `var exports = $exports;
|
|
382
|
+
var require = $require;
|
|
383
|
+
var module = $module;
|
|
384
|
+
var __filename = $filename;
|
|
385
|
+
var __dirname = $dirname;
|
|
386
|
+
`;
|
|
387
|
+
if (includeViteVars) {
|
|
388
|
+
vars += `var __vite_injected_original_filename = $filename;
|
|
389
|
+
var __vite_injected_original_dirname = $dirname;
|
|
390
|
+
var __vite_injected_original_import_meta_url = "file://" + $filename;
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
vars += `var process = $process;
|
|
394
|
+
var console = $console;
|
|
395
|
+
var import_meta = $importMeta;
|
|
396
|
+
var __asyncLoad = $asyncLoad;
|
|
397
|
+
var __syncAwait = $syncAwait;
|
|
398
|
+
var Promise = ${promiseVar};
|
|
399
|
+
var global = globalThis;
|
|
400
|
+
`;
|
|
401
|
+
if (hideBrowserGlobals) {
|
|
402
|
+
vars += `var document = undefined;
|
|
403
|
+
var window = undefined;
|
|
404
|
+
var HTMLElement = undefined;
|
|
405
|
+
`;
|
|
406
|
+
}
|
|
407
|
+
vars += `globalThis.process = $process;
|
|
408
|
+
globalThis.console = $console;
|
|
409
|
+
globalThis.require = $require;
|
|
410
|
+
global.process = $process;
|
|
411
|
+
global.console = $console;
|
|
412
|
+
global.require = $require;
|
|
413
|
+
`;
|
|
414
|
+
if (wasmHelpers) {
|
|
415
|
+
vars += `async function __wasmCompile(bytes) { return WebAssembly.compile(bytes); }
|
|
416
|
+
async function __wasmInstantiate(moduleOrBytes, imports) {
|
|
417
|
+
var mod = moduleOrBytes;
|
|
418
|
+
if (moduleOrBytes instanceof ArrayBuffer || ArrayBuffer.isView(moduleOrBytes)) {
|
|
419
|
+
mod = await WebAssembly.compile(moduleOrBytes);
|
|
420
|
+
}
|
|
421
|
+
var result = await WebAssembly.instantiate(mod, imports);
|
|
422
|
+
return result.instance || result;
|
|
423
|
+
}
|
|
424
|
+
`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return `(function($exports, $require, $module, $filename, $dirname, $process, $console, $importMeta, $asyncLoad, $syncAwait, $SyncPromise) {
|
|
428
|
+
${vars}return (${fnKeyword}() {
|
|
429
|
+
${code}
|
|
430
|
+
}).call(this);
|
|
431
|
+
})`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function convertViaRegex(source: string, filePath: string): string {
|
|
435
|
+
let output = source;
|
|
436
|
+
output = output.replace(/\bimport\.meta\.url\b/g, `"file://${filePath}"`);
|
|
437
|
+
output = output.replace(
|
|
438
|
+
/\bimport\.meta\.dirname\b/g,
|
|
439
|
+
`"${pathPolyfill.dirname(filePath)}"`,
|
|
440
|
+
);
|
|
441
|
+
output = output.replace(/\bimport\.meta\.filename\b/g, `"${filePath}"`);
|
|
442
|
+
output = output.replace(
|
|
443
|
+
/\bimport\.meta\b/g,
|
|
444
|
+
`({ url: "file://${filePath}", dirname: "${pathPolyfill.dirname(filePath)}", filename: "${filePath}" })`,
|
|
445
|
+
);
|
|
446
|
+
output = rewriteDynamicImportsRegex(output);
|
|
447
|
+
|
|
448
|
+
const hasImport = /\bimport\s+[\w{*'"]/m.test(source);
|
|
449
|
+
const hasExport =
|
|
450
|
+
/\bexport\s+(?:default|const|let|var|function|class|{|\*)/m.test(source);
|
|
451
|
+
if (hasImport || hasExport) {
|
|
452
|
+
output = esmToCjs(output);
|
|
453
|
+
if (hasExport) {
|
|
454
|
+
output =
|
|
455
|
+
'Object.defineProperty(exports, "__esModule", { value: true });\n' +
|
|
456
|
+
output;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
output = demoteLexicalRequire(output);
|
|
461
|
+
|
|
462
|
+
return output;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ── Sync promise infrastructure ──
|
|
466
|
+
// SyncThenable and SyncPromise let __syncAwait unwrap values without hitting
|
|
467
|
+
// the microtask queue. This is how require() can work synchronously even when
|
|
468
|
+
// modules use async patterns internally.
|
|
469
|
+
|
|
470
|
+
// .then() fires synchronously when value is already resolved
|
|
471
|
+
class SyncThenable<T> {
|
|
472
|
+
private _value: T;
|
|
473
|
+
constructor(value: T) {
|
|
474
|
+
this._value = value;
|
|
475
|
+
}
|
|
476
|
+
then<R>(
|
|
477
|
+
onFulfilled?: ((v: T) => R) | null,
|
|
478
|
+
_onRejected?: ((e: any) => R) | null,
|
|
479
|
+
): SyncThenable<R> | this {
|
|
480
|
+
if (onFulfilled) return new SyncThenable(onFulfilled(this._value));
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
catch(_onRejected?: ((e: any) => unknown) | null): this {
|
|
484
|
+
return this;
|
|
485
|
+
}
|
|
486
|
+
finally(onFinally?: (() => void) | null): this {
|
|
487
|
+
if (onFinally) onFinally();
|
|
488
|
+
return this;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Try to synchronously unwrap a thenable. Returns the value if .then() fires sync,
|
|
493
|
+
// otherwise returns the original value (possibly a native Promise).
|
|
494
|
+
function syncAwait(val: unknown): unknown {
|
|
495
|
+
if (val && typeof (val as any).then === "function") {
|
|
496
|
+
let resolved: unknown;
|
|
497
|
+
let gotSync = false;
|
|
498
|
+
(val as any).then((v: unknown) => {
|
|
499
|
+
resolved = v;
|
|
500
|
+
gotSync = true;
|
|
501
|
+
});
|
|
502
|
+
if (gotSync) return resolved;
|
|
503
|
+
}
|
|
504
|
+
return val;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Promise subclass that resolves .then() synchronously when the executor resolves sync.
|
|
508
|
+
// Needed because async functions always return native Promises, but when their body
|
|
509
|
+
// resolves synchronously we want __syncAwait to unwrap the result.
|
|
510
|
+
// Injected as `Promise` inside module wrappers.
|
|
511
|
+
function createSyncPromise(): typeof Promise {
|
|
512
|
+
const NativePromise = Promise;
|
|
513
|
+
|
|
514
|
+
class SyncPromise<T> extends NativePromise<T> {
|
|
515
|
+
private _syncValue: T | undefined;
|
|
516
|
+
private _syncResolved = false;
|
|
517
|
+
private _syncRejected = false;
|
|
518
|
+
private _syncError: any;
|
|
519
|
+
|
|
520
|
+
constructor(
|
|
521
|
+
executor: (
|
|
522
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
523
|
+
reject: (reason?: any) => void,
|
|
524
|
+
) => void,
|
|
525
|
+
) {
|
|
526
|
+
let syncVal: T | undefined;
|
|
527
|
+
let syncResolved = false;
|
|
528
|
+
let syncRejected = false;
|
|
529
|
+
let syncErr: any;
|
|
530
|
+
|
|
531
|
+
super((resolve, reject) => {
|
|
532
|
+
executor(
|
|
533
|
+
(value) => {
|
|
534
|
+
// Try sync unwrap. If it can't resolve sync, let native handle it.
|
|
535
|
+
// Without this, p-limit's resolve(asyncPromise) gets treated as
|
|
536
|
+
// sync-resolved with the Promise object as the value.
|
|
537
|
+
if (
|
|
538
|
+
value &&
|
|
539
|
+
typeof value === "object" &&
|
|
540
|
+
typeof (value as any).then === "function"
|
|
541
|
+
) {
|
|
542
|
+
let innerResolved = false;
|
|
543
|
+
let innerVal: T | undefined;
|
|
544
|
+
let innerRejected = false;
|
|
545
|
+
let innerErr: any;
|
|
546
|
+
(value as any).then(
|
|
547
|
+
(v: T) => {
|
|
548
|
+
innerVal = v;
|
|
549
|
+
innerResolved = true;
|
|
550
|
+
},
|
|
551
|
+
(e: any) => {
|
|
552
|
+
innerErr = e;
|
|
553
|
+
innerRejected = true;
|
|
554
|
+
},
|
|
555
|
+
);
|
|
556
|
+
if (innerResolved) {
|
|
557
|
+
syncVal = innerVal;
|
|
558
|
+
syncResolved = true;
|
|
559
|
+
resolve(innerVal!);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (innerRejected) {
|
|
563
|
+
syncRejected = true;
|
|
564
|
+
syncErr = innerErr;
|
|
565
|
+
reject(innerErr);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
resolve(value);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
syncVal = value as T;
|
|
572
|
+
syncResolved = true;
|
|
573
|
+
resolve(value);
|
|
574
|
+
},
|
|
575
|
+
(reason) => {
|
|
576
|
+
syncRejected = true;
|
|
577
|
+
syncErr = reason;
|
|
578
|
+
reject(reason);
|
|
579
|
+
},
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
this._syncValue = syncVal;
|
|
584
|
+
this._syncResolved = syncResolved;
|
|
585
|
+
this._syncRejected = syncRejected;
|
|
586
|
+
this._syncError = syncErr;
|
|
587
|
+
|
|
588
|
+
// Suppress native unhandledrejection — our .then() handles these sync
|
|
589
|
+
if (syncRejected) {
|
|
590
|
+
NativePromise.prototype.catch.call(this, () => {});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
then<TResult1 = T, TResult2 = never>(
|
|
595
|
+
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
|
596
|
+
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
|
|
597
|
+
): Promise<TResult1 | TResult2> {
|
|
598
|
+
if (this._syncResolved && onFulfilled) {
|
|
599
|
+
try {
|
|
600
|
+
const result = onFulfilled(this._syncValue as T);
|
|
601
|
+
if (
|
|
602
|
+
result &&
|
|
603
|
+
typeof result === "object" &&
|
|
604
|
+
typeof (result as any).then === "function"
|
|
605
|
+
) {
|
|
606
|
+
let innerVal: any;
|
|
607
|
+
let innerResolved = false;
|
|
608
|
+
let innerRejected = false;
|
|
609
|
+
let innerErr: any;
|
|
610
|
+
(result as any).then(
|
|
611
|
+
(v: any) => {
|
|
612
|
+
innerVal = v;
|
|
613
|
+
innerResolved = true;
|
|
614
|
+
},
|
|
615
|
+
(e: any) => {
|
|
616
|
+
innerErr = e;
|
|
617
|
+
innerRejected = true;
|
|
618
|
+
},
|
|
619
|
+
);
|
|
620
|
+
if (innerResolved) {
|
|
621
|
+
return new SyncPromise<TResult1>((res) => res(innerVal)) as any;
|
|
622
|
+
}
|
|
623
|
+
if (innerRejected) {
|
|
624
|
+
if (onRejected) {
|
|
625
|
+
return new SyncPromise<TResult2>((res) =>
|
|
626
|
+
res(onRejected(innerErr) as TResult2),
|
|
627
|
+
) as any;
|
|
628
|
+
}
|
|
629
|
+
return new SyncPromise<TResult2>((_, rej) => rej(innerErr)) as any;
|
|
630
|
+
}
|
|
631
|
+
return NativePromise.resolve(result).then(null, onRejected) as any;
|
|
632
|
+
}
|
|
633
|
+
return new SyncPromise<TResult1>((res) =>
|
|
634
|
+
res(result as TResult1),
|
|
635
|
+
) as any;
|
|
636
|
+
} catch (e) {
|
|
637
|
+
if (onRejected) {
|
|
638
|
+
return new SyncPromise<TResult2>((res) =>
|
|
639
|
+
res(onRejected(e) as TResult2),
|
|
640
|
+
) as any;
|
|
641
|
+
}
|
|
642
|
+
// Must be SyncPromise so downstream .catch() fires sync (p-locate depends on this)
|
|
643
|
+
return new SyncPromise<TResult2>((_, rej) => rej(e)) as any;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (this._syncRejected && onRejected) {
|
|
647
|
+
try {
|
|
648
|
+
const result = onRejected(this._syncError);
|
|
649
|
+
return new SyncPromise<TResult2>((res) =>
|
|
650
|
+
res(result as TResult2),
|
|
651
|
+
) as any;
|
|
652
|
+
} catch (e) {
|
|
653
|
+
return new SyncPromise<TResult2>((_, rej) => rej(e)) as any;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (this._syncRejected && !onRejected) {
|
|
657
|
+
return new SyncPromise<TResult2>((_, rej) =>
|
|
658
|
+
rej(this._syncError),
|
|
659
|
+
) as any;
|
|
660
|
+
}
|
|
661
|
+
return super.then(onFulfilled, onRejected);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// instanceof must work for native Promises too since we inject SyncPromise as `Promise`
|
|
666
|
+
Object.defineProperty(SyncPromise, Symbol.hasInstance, {
|
|
667
|
+
value: (instance: any) =>
|
|
668
|
+
instance instanceof NativePromise,
|
|
669
|
+
configurable: true,
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
(SyncPromise as any).resolve = (value: any) => {
|
|
673
|
+
if (
|
|
674
|
+
value &&
|
|
675
|
+
typeof value === "object" &&
|
|
676
|
+
typeof (value as any).then === "function"
|
|
677
|
+
) {
|
|
678
|
+
return new SyncPromise((res) => res(value));
|
|
679
|
+
}
|
|
680
|
+
return new SyncPromise((res) => res(value));
|
|
681
|
+
};
|
|
682
|
+
(SyncPromise as any).reject = (reason: any) =>
|
|
683
|
+
new SyncPromise((_, rej) => rej(reason));
|
|
684
|
+
|
|
685
|
+
// all/race/allSettled/any return SyncPromise so __syncAwait can unwrap them
|
|
686
|
+
(SyncPromise as any).all = (iterable: Iterable<any>) => {
|
|
687
|
+
const arr = Array.from(iterable);
|
|
688
|
+
const results: any[] = new Array(arr.length);
|
|
689
|
+
let allSync = true;
|
|
690
|
+
for (let i = 0; i < arr.length; i++) {
|
|
691
|
+
const v = arr[i];
|
|
692
|
+
if (v instanceof SyncPromise) {
|
|
693
|
+
if ((v as any)._syncResolved) {
|
|
694
|
+
results[i] = (v as any)._syncValue;
|
|
695
|
+
} else if ((v as any)._syncRejected) {
|
|
696
|
+
return new SyncPromise((_, rej) => rej((v as any)._syncError));
|
|
697
|
+
} else {
|
|
698
|
+
allSync = false;
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
} else if (
|
|
702
|
+
v && typeof v === "object" && typeof v.then === "function"
|
|
703
|
+
) {
|
|
704
|
+
let probed = false, pVal: any;
|
|
705
|
+
v.then((x: any) => { pVal = x; probed = true; });
|
|
706
|
+
if (probed) {
|
|
707
|
+
results[i] = pVal;
|
|
708
|
+
} else {
|
|
709
|
+
allSync = false;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
results[i] = v;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (allSync) {
|
|
717
|
+
return new SyncPromise((res: any) => res(results));
|
|
718
|
+
}
|
|
719
|
+
return new SyncPromise((res: any, rej: any) => {
|
|
720
|
+
NativePromise.all(arr).then(res, rej);
|
|
721
|
+
});
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
(SyncPromise as any).allSettled = (iterable: Iterable<any>) => {
|
|
725
|
+
const arr = Array.from(iterable);
|
|
726
|
+
const results: any[] = new Array(arr.length);
|
|
727
|
+
let allSync = true;
|
|
728
|
+
for (let i = 0; i < arr.length; i++) {
|
|
729
|
+
const v = arr[i];
|
|
730
|
+
if (v instanceof SyncPromise) {
|
|
731
|
+
if ((v as any)._syncResolved) {
|
|
732
|
+
results[i] = { status: "fulfilled", value: (v as any)._syncValue };
|
|
733
|
+
} else if ((v as any)._syncRejected) {
|
|
734
|
+
results[i] = { status: "rejected", reason: (v as any)._syncError };
|
|
735
|
+
} else {
|
|
736
|
+
allSync = false;
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
} else if (
|
|
740
|
+
v && typeof v === "object" && typeof v.then === "function"
|
|
741
|
+
) {
|
|
742
|
+
allSync = false;
|
|
743
|
+
break;
|
|
744
|
+
} else {
|
|
745
|
+
results[i] = { status: "fulfilled", value: v };
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (allSync) {
|
|
749
|
+
return new SyncPromise((res: any) => res(results));
|
|
750
|
+
}
|
|
751
|
+
return new SyncPromise((res: any, rej: any) => {
|
|
752
|
+
NativePromise.allSettled(arr).then(res, rej);
|
|
753
|
+
});
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
(SyncPromise as any).race = (iterable: Iterable<any>) => {
|
|
757
|
+
const arr = Array.from(iterable);
|
|
758
|
+
for (const v of arr) {
|
|
759
|
+
if (v instanceof SyncPromise) {
|
|
760
|
+
if ((v as any)._syncResolved) {
|
|
761
|
+
return new SyncPromise((res: any) => res((v as any)._syncValue));
|
|
762
|
+
}
|
|
763
|
+
if ((v as any)._syncRejected) {
|
|
764
|
+
return new SyncPromise((_, rej: any) => rej((v as any)._syncError));
|
|
765
|
+
}
|
|
766
|
+
} else if (
|
|
767
|
+
!(v && typeof v === "object" && typeof v.then === "function")
|
|
768
|
+
) {
|
|
769
|
+
return new SyncPromise((res: any) => res(v));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return new SyncPromise((res: any, rej: any) => {
|
|
773
|
+
NativePromise.race(arr).then(res, rej);
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
(SyncPromise as any).any = (iterable: Iterable<any>) => {
|
|
778
|
+
const arr = Array.from(iterable);
|
|
779
|
+
for (const v of arr) {
|
|
780
|
+
if (v instanceof SyncPromise && (v as any)._syncResolved) {
|
|
781
|
+
return new SyncPromise((res: any) => res((v as any)._syncValue));
|
|
782
|
+
}
|
|
783
|
+
if (
|
|
784
|
+
!(v && typeof v === "object" && typeof v.then === "function")
|
|
785
|
+
) {
|
|
786
|
+
return new SyncPromise((res: any) => res(v));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
let allSyncRejected = true;
|
|
790
|
+
const errors: any[] = [];
|
|
791
|
+
for (const v of arr) {
|
|
792
|
+
if (v instanceof SyncPromise && (v as any)._syncRejected) {
|
|
793
|
+
errors.push((v as any)._syncError);
|
|
794
|
+
} else {
|
|
795
|
+
allSyncRejected = false;
|
|
796
|
+
break;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (allSyncRejected && arr.length > 0) {
|
|
800
|
+
return new SyncPromise((_, rej: any) =>
|
|
801
|
+
rej(new AggregateError(errors, "All promises were rejected")),
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
return new SyncPromise((res: any, rej: any) => {
|
|
805
|
+
NativePromise.any(arr).then(res, rej);
|
|
806
|
+
});
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
return SyncPromise as any;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const SyncPromiseClass = createSyncPromise();
|
|
813
|
+
|
|
814
|
+
function makeDynamicLoader(
|
|
815
|
+
resolver: ResolverFn,
|
|
816
|
+
): (specifier: string) => SyncThenable<unknown> {
|
|
817
|
+
return (specifier: string): SyncThenable<unknown> => {
|
|
818
|
+
const loaded = resolver(specifier);
|
|
819
|
+
// Functions can have named exports as properties (e.g. Module.createRequire)
|
|
820
|
+
if (
|
|
821
|
+
loaded &&
|
|
822
|
+
(typeof loaded === "object" || typeof loaded === "function") &&
|
|
823
|
+
("default" in (loaded as object) || "__esModule" in (loaded as object))
|
|
824
|
+
) {
|
|
825
|
+
return new SyncThenable(loaded);
|
|
826
|
+
}
|
|
827
|
+
const spread = loaded && (typeof loaded === "object" || typeof loaded === "function")
|
|
828
|
+
? Object.getOwnPropertyNames(loaded as object).reduce((acc, key) => {
|
|
829
|
+
acc[key] = (loaded as any)[key];
|
|
830
|
+
return acc;
|
|
831
|
+
}, {} as Record<string, unknown>)
|
|
832
|
+
: {};
|
|
833
|
+
return new SyncThenable({
|
|
834
|
+
default: loaded,
|
|
835
|
+
...spread,
|
|
836
|
+
});
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// ── Types ──
|
|
841
|
+
export interface ModuleRecord {
|
|
842
|
+
id: string;
|
|
843
|
+
filename: string;
|
|
844
|
+
exports: unknown;
|
|
845
|
+
loaded: boolean;
|
|
846
|
+
children: ModuleRecord[];
|
|
847
|
+
paths: string[];
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
export interface EngineOptions {
|
|
851
|
+
cwd?: string;
|
|
852
|
+
env?: Record<string, string>;
|
|
853
|
+
onConsole?: (method: string, args: unknown[]) => void;
|
|
854
|
+
onStdout?: (data: string) => void;
|
|
855
|
+
onStderr?: (data: string) => void;
|
|
856
|
+
workerThreadsOverride?: {
|
|
857
|
+
isMainThread: boolean;
|
|
858
|
+
parentPort: unknown;
|
|
859
|
+
workerData: unknown;
|
|
860
|
+
threadId: number;
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
export interface ResolverFn {
|
|
865
|
+
(id: string): unknown;
|
|
866
|
+
resolve: (id: string, options?: { paths?: string[] }) => string;
|
|
867
|
+
cache: Record<string, ModuleRecord>;
|
|
868
|
+
extensions: Record<string, unknown>;
|
|
869
|
+
main: ModuleRecord | null;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// Mutable copy so packages can monkey-patch frozen polyfill namespaces
|
|
873
|
+
function shallowCopy(source: Record<string, unknown>): Record<string, unknown> {
|
|
874
|
+
const copy: Record<string, unknown> = {};
|
|
875
|
+
for (const k of Object.keys(source)) copy[k] = source[k];
|
|
876
|
+
return copy;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// ── Core module registry ──
|
|
880
|
+
const CORE_MODULES: Record<string, unknown> = {
|
|
881
|
+
path: pathPolyfill,
|
|
882
|
+
http: shallowCopy(httpPolyfill as unknown as Record<string, unknown>),
|
|
883
|
+
https: shallowCopy(httpsPolyfill as unknown as Record<string, unknown>),
|
|
884
|
+
net: tcpPolyfill,
|
|
885
|
+
events: eventBusPolyfill,
|
|
886
|
+
stream: streamPolyfill,
|
|
887
|
+
buffer: bufferPolyfill,
|
|
888
|
+
url: urlPolyfill,
|
|
889
|
+
querystring: qsPolyfill,
|
|
890
|
+
util: helpersPolyfill,
|
|
891
|
+
tty: ttyPolyfill,
|
|
892
|
+
os: osPolyfill,
|
|
893
|
+
crypto: shallowCopy(hashingPolyfill as unknown as Record<string, unknown>),
|
|
894
|
+
zlib: compressionPolyfill,
|
|
895
|
+
dns: dnsPolyfill,
|
|
896
|
+
child_process: shellExecProxy,
|
|
897
|
+
assert: assertPolyfill,
|
|
898
|
+
string_decoder: stringDecoderPolyfill,
|
|
899
|
+
timers: timersPolyfill,
|
|
900
|
+
constants: constantsPolyfill,
|
|
901
|
+
punycode: punycodePolyfill,
|
|
902
|
+
_http_common: {},
|
|
903
|
+
_http_incoming: {},
|
|
904
|
+
_http_outgoing: {},
|
|
905
|
+
chokidar: watcherPolyfill,
|
|
906
|
+
ws: wsPolyfill,
|
|
907
|
+
fsevents: macEventsPolyfill,
|
|
908
|
+
readdirp: scannerPolyfill,
|
|
909
|
+
module: moduleSysPolyfill.Module,
|
|
910
|
+
perf_hooks: perfPolyfill,
|
|
911
|
+
worker_threads: threadPoolPolyfill,
|
|
912
|
+
esbuild: esbuildPolyfill,
|
|
913
|
+
rollup: rollupPolyfill,
|
|
914
|
+
v8: v8Polyfill,
|
|
915
|
+
readline: lineReaderPolyfill,
|
|
916
|
+
tls: tlsPolyfill,
|
|
917
|
+
http2: http2Polyfill,
|
|
918
|
+
cluster: clusterPolyfill,
|
|
919
|
+
dgram: udpPolyfill,
|
|
920
|
+
vm: vmPolyfill,
|
|
921
|
+
inspector: debugPolyfill,
|
|
922
|
+
"inspector/promises": debugPolyfill,
|
|
923
|
+
async_hooks: asyncCtxPolyfill,
|
|
924
|
+
domain: domainPolyfill,
|
|
925
|
+
diagnostics_channel: tracePolyfill,
|
|
926
|
+
console: { ...console, Console: consolePolyfill.Console },
|
|
927
|
+
repl: replPolyfill,
|
|
928
|
+
test: testPolyfill,
|
|
929
|
+
trace_events: traceEventsPolyfill,
|
|
930
|
+
wasi: wasiPolyfill,
|
|
931
|
+
sea: seaPolyfill,
|
|
932
|
+
sqlite: sqlitePolyfill,
|
|
933
|
+
quic: quicPolyfill,
|
|
934
|
+
lightningcss: lightningcssPolyfill,
|
|
935
|
+
"@tailwindcss/oxide": tailwindOxidePolyfill,
|
|
936
|
+
sys: helpersPolyfill,
|
|
937
|
+
"util/types": helpersPolyfill.types,
|
|
938
|
+
"path/posix": pathPolyfill,
|
|
939
|
+
"path/win32": pathPolyfill.win32,
|
|
940
|
+
"timers/promises": timersPromises,
|
|
941
|
+
"stream/promises": streamPromises,
|
|
942
|
+
"stream/web": {
|
|
943
|
+
ReadableStream: globalThis.ReadableStream,
|
|
944
|
+
WritableStream: globalThis.WritableStream,
|
|
945
|
+
TransformStream: globalThis.TransformStream,
|
|
946
|
+
ByteLengthQueuingStrategy: globalThis.ByteLengthQueuingStrategy,
|
|
947
|
+
CountQueuingStrategy: globalThis.CountQueuingStrategy,
|
|
948
|
+
},
|
|
949
|
+
"stream/consumers": {
|
|
950
|
+
async arrayBuffer(stream: any): Promise<ArrayBuffer> {
|
|
951
|
+
const chunks: Uint8Array[] = [];
|
|
952
|
+
for await (const chunk of stream) {
|
|
953
|
+
chunks.push(
|
|
954
|
+
typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk,
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
let len = 0;
|
|
958
|
+
for (const c of chunks) len += c.byteLength;
|
|
959
|
+
const buf = new Uint8Array(len);
|
|
960
|
+
let off = 0;
|
|
961
|
+
for (const c of chunks) {
|
|
962
|
+
buf.set(c, off);
|
|
963
|
+
off += c.byteLength;
|
|
964
|
+
}
|
|
965
|
+
return buf.buffer;
|
|
966
|
+
},
|
|
967
|
+
async blob(stream: any): Promise<Blob> {
|
|
968
|
+
const chunks: Uint8Array[] = [];
|
|
969
|
+
for await (const chunk of stream) {
|
|
970
|
+
chunks.push(
|
|
971
|
+
typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk,
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
return new Blob(chunks as unknown as BlobPart[]);
|
|
975
|
+
},
|
|
976
|
+
async buffer(stream: any): Promise<Uint8Array> {
|
|
977
|
+
const chunks: Uint8Array[] = [];
|
|
978
|
+
for await (const chunk of stream) {
|
|
979
|
+
chunks.push(
|
|
980
|
+
typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk,
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
let len = 0;
|
|
984
|
+
for (const c of chunks) len += c.byteLength;
|
|
985
|
+
const buf = new Uint8Array(len);
|
|
986
|
+
let off = 0;
|
|
987
|
+
for (const c of chunks) {
|
|
988
|
+
buf.set(c, off);
|
|
989
|
+
off += c.byteLength;
|
|
990
|
+
}
|
|
991
|
+
return buf;
|
|
992
|
+
},
|
|
993
|
+
async json(stream: any): Promise<unknown> {
|
|
994
|
+
const chunks: string[] = [];
|
|
995
|
+
for await (const chunk of stream) {
|
|
996
|
+
chunks.push(
|
|
997
|
+
typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk),
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return JSON.parse(chunks.join(""));
|
|
1001
|
+
},
|
|
1002
|
+
async text(stream: any): Promise<string> {
|
|
1003
|
+
const chunks: string[] = [];
|
|
1004
|
+
for await (const chunk of stream) {
|
|
1005
|
+
chunks.push(
|
|
1006
|
+
typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk),
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
return chunks.join("");
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
1012
|
+
"dns/promises": dnsPromises,
|
|
1013
|
+
"assert/strict": assertPolyfill,
|
|
1014
|
+
"readline/promises": readlinePromises,
|
|
1015
|
+
_stream_readable: Readable,
|
|
1016
|
+
_stream_writable: Writable,
|
|
1017
|
+
_stream_duplex: Duplex,
|
|
1018
|
+
_stream_transform: Transform,
|
|
1019
|
+
_stream_passthrough: PassThrough,
|
|
1020
|
+
// Vite imports rollup/parseAst which normally uses native bindings
|
|
1021
|
+
"rollup/parseAst": {
|
|
1022
|
+
parseAst: rollupPolyfill.parseAst,
|
|
1023
|
+
parseAstAsync: rollupPolyfill.parseAstAsync,
|
|
1024
|
+
},
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
// ── Console wrapper ──
|
|
1028
|
+
// Captured at module load time to avoid infinite recursion when globalThis.console is overridden
|
|
1029
|
+
const _nativeConsole = console;
|
|
1030
|
+
|
|
1031
|
+
function wrapConsole(
|
|
1032
|
+
onConsole?: (method: string, args: unknown[]) => void,
|
|
1033
|
+
): Console {
|
|
1034
|
+
// Route through onConsole callback exclusively when provided, else fall back to browser console
|
|
1035
|
+
const nc = _nativeConsole;
|
|
1036
|
+
const wrapped = {
|
|
1037
|
+
log: (...args: unknown[]) => {
|
|
1038
|
+
if (onConsole) onConsole("log", args);
|
|
1039
|
+
else nc.log(...args);
|
|
1040
|
+
},
|
|
1041
|
+
error: (...args: unknown[]) => {
|
|
1042
|
+
if (onConsole) onConsole("error", args);
|
|
1043
|
+
else nc.error(...args);
|
|
1044
|
+
},
|
|
1045
|
+
warn: (...args: unknown[]) => {
|
|
1046
|
+
if (onConsole) onConsole("warn", args);
|
|
1047
|
+
else nc.warn(...args);
|
|
1048
|
+
},
|
|
1049
|
+
info: (...args: unknown[]) => {
|
|
1050
|
+
if (onConsole) onConsole("info", args);
|
|
1051
|
+
else nc.info(...args);
|
|
1052
|
+
},
|
|
1053
|
+
debug: (...args: unknown[]) => {
|
|
1054
|
+
if (onConsole) onConsole("debug", args);
|
|
1055
|
+
else nc.debug(...args);
|
|
1056
|
+
},
|
|
1057
|
+
trace: (...args: unknown[]) => {
|
|
1058
|
+
if (onConsole) onConsole("trace", args);
|
|
1059
|
+
else nc.trace(...args);
|
|
1060
|
+
},
|
|
1061
|
+
dir: (obj: unknown) => {
|
|
1062
|
+
if (onConsole) onConsole("dir", [obj]);
|
|
1063
|
+
else nc.dir(obj);
|
|
1064
|
+
},
|
|
1065
|
+
time: nc.time.bind(nc),
|
|
1066
|
+
timeEnd: nc.timeEnd.bind(nc),
|
|
1067
|
+
timeLog: nc.timeLog.bind(nc),
|
|
1068
|
+
assert: (...args: unknown[]) => {
|
|
1069
|
+
const [v, ...rest] = args;
|
|
1070
|
+
if (!v) {
|
|
1071
|
+
if (onConsole) onConsole("error", ["Assertion failed:", ...rest]);
|
|
1072
|
+
else nc.assert(v as boolean, ...rest);
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
clear: nc.clear.bind(nc),
|
|
1076
|
+
count: nc.count.bind(nc),
|
|
1077
|
+
countReset: nc.countReset.bind(nc),
|
|
1078
|
+
group: nc.group.bind(nc),
|
|
1079
|
+
groupCollapsed: nc.groupCollapsed.bind(nc),
|
|
1080
|
+
groupEnd: nc.groupEnd.bind(nc),
|
|
1081
|
+
table: (...args: unknown[]) => {
|
|
1082
|
+
if (onConsole) onConsole("log", args);
|
|
1083
|
+
else nc.table(...args);
|
|
1084
|
+
},
|
|
1085
|
+
timeStamp: nc.timeStamp ? nc.timeStamp.bind(nc) : () => {},
|
|
1086
|
+
profile: nc.profile ? nc.profile.bind(nc) : () => {},
|
|
1087
|
+
profileEnd: nc.profileEnd ? nc.profileEnd.bind(nc) : () => {},
|
|
1088
|
+
};
|
|
1089
|
+
return wrapped as unknown as Console;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// ── Module resolver & loader ──
|
|
1093
|
+
function buildResolver(
|
|
1094
|
+
vol: MemoryVolume,
|
|
1095
|
+
fsBridge: FsBridge,
|
|
1096
|
+
proc: ProcessObject,
|
|
1097
|
+
baseDir: string,
|
|
1098
|
+
cache: Record<string, ModuleRecord>,
|
|
1099
|
+
opts: EngineOptions,
|
|
1100
|
+
codeCache?: Map<string, string>,
|
|
1101
|
+
deAsyncImports = false,
|
|
1102
|
+
): ResolverFn {
|
|
1103
|
+
const resolveCache = new Map<string, string | null>();
|
|
1104
|
+
const manifestCache = new Map<string, PackageManifest | null>();
|
|
1105
|
+
// Shared across all resolvers — deduplicates same-version packages from nested node_modules
|
|
1106
|
+
const _pkgIdentityMap: Record<string, string> =
|
|
1107
|
+
(cache as any).__pkgIdentityMap ?? ((cache as any).__pkgIdentityMap = {});
|
|
1108
|
+
|
|
1109
|
+
const readManifest = (manifestPath: string): PackageManifest | null => {
|
|
1110
|
+
if (manifestCache.has(manifestPath))
|
|
1111
|
+
return manifestCache.get(manifestPath)!;
|
|
1112
|
+
try {
|
|
1113
|
+
const raw = vol.readFileSync(manifestPath, "utf8");
|
|
1114
|
+
const parsed = JSON.parse(raw) as PackageManifest;
|
|
1115
|
+
manifestCache.set(manifestPath, parsed);
|
|
1116
|
+
return parsed;
|
|
1117
|
+
} catch {
|
|
1118
|
+
manifestCache.set(manifestPath, null);
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
const resolveId = (
|
|
1124
|
+
id: string,
|
|
1125
|
+
fromDir: string,
|
|
1126
|
+
preferEsm: boolean = false,
|
|
1127
|
+
): string => {
|
|
1128
|
+
if (typeof id !== "string") {
|
|
1129
|
+
throw new TypeError(
|
|
1130
|
+
`The "id" argument must be of type string. Received ${typeof id}`,
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
if (id.startsWith("node:")) id = id.slice(5);
|
|
1134
|
+
|
|
1135
|
+
if (id.startsWith("file:///")) {
|
|
1136
|
+
id = decodeURIComponent(id.slice(7));
|
|
1137
|
+
if (/^[A-Za-z]:[\\/]/.test(id)) {
|
|
1138
|
+
id = "/" + id.slice(2).replace(/\\/g, "/");
|
|
1139
|
+
}
|
|
1140
|
+
} else if (id.startsWith("file://")) {
|
|
1141
|
+
id = decodeURIComponent(id.slice(7));
|
|
1142
|
+
if (/^[A-Za-z]:[\\/]/.test(id)) {
|
|
1143
|
+
id = "/" + id.slice(2).replace(/\\/g, "/");
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const qIdx = id.indexOf("?");
|
|
1148
|
+
if (qIdx !== -1) id = id.slice(0, qIdx);
|
|
1149
|
+
|
|
1150
|
+
const hashIdx = id.indexOf("#");
|
|
1151
|
+
if (hashIdx !== -1 && !id.startsWith("#")) id = id.slice(0, hashIdx);
|
|
1152
|
+
|
|
1153
|
+
if (id.includes("\\")) id = id.replace(/\\/g, "/");
|
|
1154
|
+
|
|
1155
|
+
if (
|
|
1156
|
+
CORE_MODULES[id] ||
|
|
1157
|
+
id === "fs" ||
|
|
1158
|
+
id === "process" ||
|
|
1159
|
+
id === "url" ||
|
|
1160
|
+
id === "querystring" ||
|
|
1161
|
+
id === "util"
|
|
1162
|
+
) {
|
|
1163
|
+
return id;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// Native Rust bindings can't run in browser — provide JS stubs
|
|
1167
|
+
if (id.startsWith("@rollup/rollup-")) {
|
|
1168
|
+
if (!CORE_MODULES[id]) {
|
|
1169
|
+
CORE_MODULES[id] = {
|
|
1170
|
+
parse: rollupPolyfill.parseAst,
|
|
1171
|
+
parseAsync: rollupPolyfill.parseAstAsync,
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
return id;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if (id.startsWith("@rolldown/binding-")) {
|
|
1178
|
+
if (!CORE_MODULES[id]) {
|
|
1179
|
+
const makeParseResult = (source: string, opts?: any) => {
|
|
1180
|
+
const lang = opts?.lang || "js";
|
|
1181
|
+
const useJsx = lang === "jsx" || lang === "tsx";
|
|
1182
|
+
const ast = rollupPolyfill.parseAst(source, { jsx: useJsx });
|
|
1183
|
+
return {
|
|
1184
|
+
program: JSON.stringify({ node: ast, fixes: [] }),
|
|
1185
|
+
module: {
|
|
1186
|
+
hasModuleSyntax: false,
|
|
1187
|
+
staticImports: [],
|
|
1188
|
+
staticExports: [],
|
|
1189
|
+
dynamicImports: [],
|
|
1190
|
+
importMetas: [],
|
|
1191
|
+
},
|
|
1192
|
+
comments: [],
|
|
1193
|
+
errors: [],
|
|
1194
|
+
};
|
|
1195
|
+
};
|
|
1196
|
+
const applyDefineReplacements = (
|
|
1197
|
+
code: string,
|
|
1198
|
+
define?: Record<string, string>,
|
|
1199
|
+
): string => {
|
|
1200
|
+
if (!define || typeof define !== "object") return code;
|
|
1201
|
+
for (const [key, value] of Object.entries(define)) {
|
|
1202
|
+
if (!key) continue;
|
|
1203
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1204
|
+
const re = new RegExp(`\\b${escaped}\\b`, "g");
|
|
1205
|
+
code = code.replace(re, value);
|
|
1206
|
+
}
|
|
1207
|
+
return code;
|
|
1208
|
+
};
|
|
1209
|
+
const noop = () => {};
|
|
1210
|
+
const noopAsync = async () => {};
|
|
1211
|
+
CORE_MODULES[id] = {
|
|
1212
|
+
// Parser
|
|
1213
|
+
parseSync: (_filename: string, source: string, opts?: any) =>
|
|
1214
|
+
makeParseResult(source, opts),
|
|
1215
|
+
parse: async (_filename: string, source: string, opts?: any) =>
|
|
1216
|
+
makeParseResult(source, opts),
|
|
1217
|
+
initTraceSubscriber: noop,
|
|
1218
|
+
shutdownAsyncRuntime: noop,
|
|
1219
|
+
startAsyncRuntime: noop,
|
|
1220
|
+
createTokioRuntime: noop,
|
|
1221
|
+
registerPlugins: noop,
|
|
1222
|
+
rawTransferSupported: () => false,
|
|
1223
|
+
sync: noop,
|
|
1224
|
+
transform: async (filename: string, code: string, opts?: any) => {
|
|
1225
|
+
code = applyDefineReplacements(code, opts?.define);
|
|
1226
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1227
|
+
code = stripTypeScript(code);
|
|
1228
|
+
return { code, map: null, errors: [], warnings: [] };
|
|
1229
|
+
},
|
|
1230
|
+
transformSync: (filename: string, code: string, opts?: any) => {
|
|
1231
|
+
code = applyDefineReplacements(code, opts?.define);
|
|
1232
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1233
|
+
code = stripTypeScript(code);
|
|
1234
|
+
return { code, map: null, errors: [], warnings: [] };
|
|
1235
|
+
},
|
|
1236
|
+
enhancedTransform: async (
|
|
1237
|
+
filename: string,
|
|
1238
|
+
code: string,
|
|
1239
|
+
opts?: any,
|
|
1240
|
+
) => {
|
|
1241
|
+
code = applyDefineReplacements(code, opts?.define);
|
|
1242
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1243
|
+
code = stripTypeScript(code);
|
|
1244
|
+
return {
|
|
1245
|
+
code,
|
|
1246
|
+
map: null,
|
|
1247
|
+
errors: [],
|
|
1248
|
+
warnings: [],
|
|
1249
|
+
helpersUsed: {},
|
|
1250
|
+
tsconfigFilePaths: [],
|
|
1251
|
+
};
|
|
1252
|
+
},
|
|
1253
|
+
enhancedTransformSync: (
|
|
1254
|
+
filename: string,
|
|
1255
|
+
code: string,
|
|
1256
|
+
opts?: any,
|
|
1257
|
+
) => {
|
|
1258
|
+
code = applyDefineReplacements(code, opts?.define);
|
|
1259
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1260
|
+
code = stripTypeScript(code);
|
|
1261
|
+
return {
|
|
1262
|
+
code,
|
|
1263
|
+
map: null,
|
|
1264
|
+
errors: [],
|
|
1265
|
+
warnings: [],
|
|
1266
|
+
helpersUsed: {},
|
|
1267
|
+
tsconfigFilePaths: [],
|
|
1268
|
+
};
|
|
1269
|
+
},
|
|
1270
|
+
minify: async (_filename: string, code: string) => ({
|
|
1271
|
+
code,
|
|
1272
|
+
map: null,
|
|
1273
|
+
errors: [],
|
|
1274
|
+
warnings: [],
|
|
1275
|
+
}),
|
|
1276
|
+
minifySync: (_filename: string, code: string) => ({
|
|
1277
|
+
code,
|
|
1278
|
+
map: null,
|
|
1279
|
+
errors: [],
|
|
1280
|
+
warnings: [],
|
|
1281
|
+
}),
|
|
1282
|
+
isolatedDeclaration: async (_filename: string, code: string) => ({
|
|
1283
|
+
code,
|
|
1284
|
+
map: null,
|
|
1285
|
+
errors: [],
|
|
1286
|
+
warnings: [],
|
|
1287
|
+
}),
|
|
1288
|
+
isolatedDeclarationSync: (_filename: string, code: string) => ({
|
|
1289
|
+
code,
|
|
1290
|
+
map: null,
|
|
1291
|
+
errors: [],
|
|
1292
|
+
warnings: [],
|
|
1293
|
+
}),
|
|
1294
|
+
moduleRunnerTransform: async (filename: string, code: string) => {
|
|
1295
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1296
|
+
code = stripTypeScript(code);
|
|
1297
|
+
return { code, map: null, deps: [], dynamicDeps: [], errors: [] };
|
|
1298
|
+
},
|
|
1299
|
+
moduleRunnerTransformSync: (filename: string, code: string) => {
|
|
1300
|
+
if (!isCSSFile(filename) && (isTypeScriptFile(filename) || looksLikeTypeScript(code)))
|
|
1301
|
+
code = stripTypeScript(code);
|
|
1302
|
+
return { code, map: null, deps: [], dynamicDeps: [], errors: [] };
|
|
1303
|
+
},
|
|
1304
|
+
Severity: { Error: "Error", Warning: "Warning", Advice: "Advice" },
|
|
1305
|
+
ParseResult: class {},
|
|
1306
|
+
ResolverFactory: class {},
|
|
1307
|
+
EnforceExtension: { Auto: 0, Enabled: 1, Disabled: 2 },
|
|
1308
|
+
ModuleType: {},
|
|
1309
|
+
HelperMode: {},
|
|
1310
|
+
TraceSubscriberGuard: class {},
|
|
1311
|
+
BindingBundler: class {
|
|
1312
|
+
closed = false;
|
|
1313
|
+
#watchFiles: string[] = [];
|
|
1314
|
+
async generate(opts: any) {
|
|
1315
|
+
return this.#bundle(opts);
|
|
1316
|
+
}
|
|
1317
|
+
async write(opts: any) {
|
|
1318
|
+
const result = await this.#bundle(opts);
|
|
1319
|
+
const outputOpts = opts?.outputOptions ?? {};
|
|
1320
|
+
const dir = outputOpts.dir || "/dist";
|
|
1321
|
+
try {
|
|
1322
|
+
if (!vol.existsSync(dir)) {
|
|
1323
|
+
vol.mkdirSync(dir, { recursive: true });
|
|
1324
|
+
}
|
|
1325
|
+
} catch {
|
|
1326
|
+
/* best effort */
|
|
1327
|
+
}
|
|
1328
|
+
for (const chunk of result.chunks) {
|
|
1329
|
+
const outPath = pathPolyfill.join(dir, chunk.getFileName());
|
|
1330
|
+
try {
|
|
1331
|
+
const outDir = pathPolyfill.dirname(outPath);
|
|
1332
|
+
if (!vol.existsSync(outDir)) {
|
|
1333
|
+
vol.mkdirSync(outDir, { recursive: true });
|
|
1334
|
+
}
|
|
1335
|
+
const code = chunk.getCode();
|
|
1336
|
+
vol.writeFileSync(outPath, code);
|
|
1337
|
+
} catch {
|
|
1338
|
+
/* best effort */
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return result;
|
|
1342
|
+
}
|
|
1343
|
+
// Dependency scanning for Vite's dep optimizer — invokes plugin resolveId hooks
|
|
1344
|
+
// so Vite's scan plugin discovers bare imports to pre-bundle
|
|
1345
|
+
async scan(opts?: any) {
|
|
1346
|
+
try {
|
|
1347
|
+
const inputOpts = opts?.inputOptions ?? {};
|
|
1348
|
+
const plugins: any[] = (inputOpts.plugins ?? []).filter(
|
|
1349
|
+
Boolean,
|
|
1350
|
+
);
|
|
1351
|
+
const entries: { name?: string; import: string }[] =
|
|
1352
|
+
Array.isArray(inputOpts.input) ? inputOpts.input : [];
|
|
1353
|
+
const cwd = inputOpts.cwd || "/";
|
|
1354
|
+
if (!entries.length || !plugins.length) return;
|
|
1355
|
+
|
|
1356
|
+
const mockCtx = {
|
|
1357
|
+
resolve: async () => null,
|
|
1358
|
+
load: async () => ({}),
|
|
1359
|
+
emitFile: () => "",
|
|
1360
|
+
getFileName: () => "",
|
|
1361
|
+
getModuleInfo: () => null,
|
|
1362
|
+
getModuleIds: () => [],
|
|
1363
|
+
addWatchFile: () => {},
|
|
1364
|
+
parse: (code: string) =>
|
|
1365
|
+
acorn.parse(code, {
|
|
1366
|
+
ecmaVersion: "latest" as any,
|
|
1367
|
+
sourceType: "module",
|
|
1368
|
+
}),
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
const extractImports = (code: string): string[] => {
|
|
1372
|
+
const specs: string[] = [];
|
|
1373
|
+
try {
|
|
1374
|
+
const ast: any = acorn.parse(code, {
|
|
1375
|
+
ecmaVersion: "latest" as any,
|
|
1376
|
+
sourceType: "module",
|
|
1377
|
+
allowImportExportEverywhere: true,
|
|
1378
|
+
});
|
|
1379
|
+
for (const node of ast.body) {
|
|
1380
|
+
if (
|
|
1381
|
+
node.type === "ImportDeclaration" &&
|
|
1382
|
+
node.source?.value
|
|
1383
|
+
)
|
|
1384
|
+
specs.push(node.source.value);
|
|
1385
|
+
if (
|
|
1386
|
+
node.type === "ExportNamedDeclaration" &&
|
|
1387
|
+
node.source?.value
|
|
1388
|
+
)
|
|
1389
|
+
specs.push(node.source.value);
|
|
1390
|
+
if (
|
|
1391
|
+
node.type === "ExportAllDeclaration" &&
|
|
1392
|
+
node.source?.value
|
|
1393
|
+
)
|
|
1394
|
+
specs.push(node.source.value);
|
|
1395
|
+
}
|
|
1396
|
+
} catch { /* fallback to regex */ }
|
|
1397
|
+
const dynRe = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1398
|
+
let m;
|
|
1399
|
+
while ((m = dynRe.exec(code)) !== null) specs.push(m[1]);
|
|
1400
|
+
return [...new Set(specs)];
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
for (const p of plugins) {
|
|
1404
|
+
if (p.buildStart) {
|
|
1405
|
+
try {
|
|
1406
|
+
await p.buildStart(mockCtx, {});
|
|
1407
|
+
} catch (hookErr) {
|
|
1408
|
+
const msg = hookErr instanceof Error ? hookErr.message : String(hookErr);
|
|
1409
|
+
_nativeConsole.warn("[rolldown scan] buildStart hook error:", msg);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// BFS: load files, extract imports, call resolveId, follow local files
|
|
1415
|
+
const visitedSpecs = new Set<string>();
|
|
1416
|
+
const visitedFiles = new Set<string>();
|
|
1417
|
+
const queue: string[] = [];
|
|
1418
|
+
|
|
1419
|
+
for (const entry of entries) {
|
|
1420
|
+
const ep = entry.import?.startsWith("/")
|
|
1421
|
+
? entry.import
|
|
1422
|
+
: pathPolyfill.resolve(cwd, entry.import);
|
|
1423
|
+
queue.push(ep);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
const loadFile = async (
|
|
1427
|
+
filePath: string,
|
|
1428
|
+
usePlugins: boolean,
|
|
1429
|
+
): Promise<string> => {
|
|
1430
|
+
if (usePlugins) {
|
|
1431
|
+
for (const p of plugins) {
|
|
1432
|
+
if (p.load) {
|
|
1433
|
+
try {
|
|
1434
|
+
const r = await p.load(mockCtx, filePath);
|
|
1435
|
+
if (r && typeof r === "object" && r.code)
|
|
1436
|
+
return r.code;
|
|
1437
|
+
} catch (loadErr) {
|
|
1438
|
+
const msg = loadErr instanceof Error ? loadErr.message : String(loadErr);
|
|
1439
|
+
_nativeConsole.warn("[rolldown scan] load hook error:", filePath, msg);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return vol.readFileSync(filePath, "utf8");
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
const MAX_DEPTH = LIMITS.MAX_RESOLVE_DEPTH;
|
|
1448
|
+
let processed = 0;
|
|
1449
|
+
const entryPaths = new Set(queue);
|
|
1450
|
+
while (queue.length > 0 && processed < MAX_DEPTH) {
|
|
1451
|
+
const filePath = queue.shift()!;
|
|
1452
|
+
if (visitedFiles.has(filePath)) continue;
|
|
1453
|
+
visitedFiles.add(filePath);
|
|
1454
|
+
processed++;
|
|
1455
|
+
|
|
1456
|
+
let code: string;
|
|
1457
|
+
try {
|
|
1458
|
+
code = await loadFile(filePath, entryPaths.has(filePath));
|
|
1459
|
+
} catch {
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const specifiers = extractImports(code);
|
|
1464
|
+
|
|
1465
|
+
for (const spec of specifiers) {
|
|
1466
|
+
const key = `${spec}\0${filePath}`;
|
|
1467
|
+
if (visitedSpecs.has(key)) continue;
|
|
1468
|
+
visitedSpecs.add(key);
|
|
1469
|
+
|
|
1470
|
+
for (const p of plugins) {
|
|
1471
|
+
if (p.resolveId) {
|
|
1472
|
+
try {
|
|
1473
|
+
await p.resolveId(mockCtx, spec, filePath, {
|
|
1474
|
+
isEntry: false,
|
|
1475
|
+
kind: "import-statement",
|
|
1476
|
+
custom: undefined,
|
|
1477
|
+
});
|
|
1478
|
+
} catch (resolveErr) {
|
|
1479
|
+
const msg = resolveErr instanceof Error ? resolveErr.message : String(resolveErr);
|
|
1480
|
+
_nativeConsole.warn("[rolldown scan] resolveId hook error:", spec, msg);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Follow local files to discover deeper imports
|
|
1486
|
+
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
1487
|
+
const dir = pathPolyfill.dirname(filePath);
|
|
1488
|
+
let resolved = "";
|
|
1489
|
+
try {
|
|
1490
|
+
resolved = resolveId(spec, dir, true);
|
|
1491
|
+
} catch {
|
|
1492
|
+
/* not found */
|
|
1493
|
+
}
|
|
1494
|
+
// Vite treats absolute paths as project-root-relative
|
|
1495
|
+
if (!resolved && spec.startsWith("/")) {
|
|
1496
|
+
try {
|
|
1497
|
+
const cwdSpec = pathPolyfill.join(cwd, spec);
|
|
1498
|
+
resolved = resolveId(cwdSpec, cwd, true);
|
|
1499
|
+
} catch {
|
|
1500
|
+
/* not found */
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
if (
|
|
1504
|
+
resolved &&
|
|
1505
|
+
!resolved.includes("/node_modules/") &&
|
|
1506
|
+
!visitedFiles.has(resolved)
|
|
1507
|
+
) {
|
|
1508
|
+
queue.push(resolved);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
for (const p of plugins) {
|
|
1515
|
+
if (p.buildEnd) {
|
|
1516
|
+
try {
|
|
1517
|
+
await p.buildEnd(mockCtx);
|
|
1518
|
+
} catch (endErr) {
|
|
1519
|
+
const msg = endErr instanceof Error ? endErr.message : String(endErr);
|
|
1520
|
+
_nativeConsole.warn("[rolldown scan] buildEnd hook error:", msg);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
} catch (scanErr) {
|
|
1525
|
+
const msg = scanErr instanceof Error ? scanErr.message : String(scanErr);
|
|
1526
|
+
const detail = scanErr instanceof Error && scanErr.stack ? `\n${scanErr.stack}` : "";
|
|
1527
|
+
_nativeConsole.warn("[rolldown scan]", msg);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
async close() {
|
|
1531
|
+
this.closed = true;
|
|
1532
|
+
}
|
|
1533
|
+
getWatchFiles() {
|
|
1534
|
+
return this.#watchFiles;
|
|
1535
|
+
}
|
|
1536
|
+
async #bundle(opts: any) {
|
|
1537
|
+
const inputOpts = opts?.inputOptions ?? {};
|
|
1538
|
+
const entries: { name: string; import: string }[] = Array.isArray(
|
|
1539
|
+
inputOpts.input,
|
|
1540
|
+
)
|
|
1541
|
+
? inputOpts.input
|
|
1542
|
+
: [];
|
|
1543
|
+
const cwd = inputOpts.cwd || "/";
|
|
1544
|
+
const outputOpts = opts?.outputOptions ?? {};
|
|
1545
|
+
const format = outputOpts.format ?? "esm";
|
|
1546
|
+
const entryFileNames = outputOpts.entryFileNames || "[name].js";
|
|
1547
|
+
|
|
1548
|
+
const extractExports = (src: string): string[] => {
|
|
1549
|
+
try {
|
|
1550
|
+
const ast = acorn.parse(src, {
|
|
1551
|
+
ecmaVersion: "latest",
|
|
1552
|
+
sourceType: "module",
|
|
1553
|
+
allowImportExportEverywhere: true,
|
|
1554
|
+
}) as any;
|
|
1555
|
+
const names: string[] = [];
|
|
1556
|
+
for (const node of ast.body) {
|
|
1557
|
+
if (
|
|
1558
|
+
node.type === "ExportNamedDeclaration" &&
|
|
1559
|
+
node.specifiers
|
|
1560
|
+
) {
|
|
1561
|
+
for (const s of node.specifiers) {
|
|
1562
|
+
if (s.exported?.name) names.push(s.exported.name);
|
|
1563
|
+
}
|
|
1564
|
+
if (node.declaration) {
|
|
1565
|
+
if (node.declaration.id?.name) {
|
|
1566
|
+
names.push(node.declaration.id.name);
|
|
1567
|
+
} else if (node.declaration.declarations) {
|
|
1568
|
+
for (const d of node.declaration.declarations) {
|
|
1569
|
+
if (d.id?.name) names.push(d.id.name);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
1574
|
+
names.push("default");
|
|
1575
|
+
} else if (node.type === "ExportAllDeclaration") {
|
|
1576
|
+
// Can't statically enumerate re-exports
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return names;
|
|
1580
|
+
} catch {
|
|
1581
|
+
return [];
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1585
|
+
// Re-resolve using browser ESM conditions when esbuild produces empty output
|
|
1586
|
+
// (happens with thin ESM re-exports pointing at CJS like vue/index.mjs)
|
|
1587
|
+
const reResolveEntry = (origPath: string): string | null => {
|
|
1588
|
+
// Only applicable inside node_modules
|
|
1589
|
+
const nmIdx = origPath.lastIndexOf("/node_modules/");
|
|
1590
|
+
if (nmIdx === -1) return null;
|
|
1591
|
+
const afterNm = origPath.slice(nmIdx + "/node_modules/".length);
|
|
1592
|
+
const parts = afterNm.split("/");
|
|
1593
|
+
const scoped = parts[0].startsWith("@");
|
|
1594
|
+
const pkgName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
1595
|
+
const pkgRoot = origPath.slice(0, nmIdx) + "/node_modules/" + pkgName;
|
|
1596
|
+
const pkgJsonPath = pkgRoot + "/package.json";
|
|
1597
|
+
try {
|
|
1598
|
+
if (!vol.existsSync(pkgJsonPath)) return null;
|
|
1599
|
+
const manifest = JSON.parse(vol.readFileSync(pkgJsonPath, "utf8"));
|
|
1600
|
+
for (const conds of [
|
|
1601
|
+
{ browser: true, import: true },
|
|
1602
|
+
{ import: true },
|
|
1603
|
+
] as const) {
|
|
1604
|
+
try {
|
|
1605
|
+
const resolved = resolveExports(manifest, ".", conds);
|
|
1606
|
+
if (resolved?.length) {
|
|
1607
|
+
const full = pathPolyfill.join(pkgRoot, resolved[0]);
|
|
1608
|
+
if (vol.existsSync(full) && full !== origPath) return full;
|
|
1609
|
+
}
|
|
1610
|
+
} catch { /* try next */ }
|
|
1611
|
+
}
|
|
1612
|
+
} catch { /* can't re-resolve */ }
|
|
1613
|
+
return null;
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
const esbuildMod = CORE_MODULES["esbuild"] as any;
|
|
1617
|
+
const chunks: any[] = [];
|
|
1618
|
+
for (const entry of entries) {
|
|
1619
|
+
let entryPath = entry.import.startsWith("/")
|
|
1620
|
+
? entry.import
|
|
1621
|
+
: pathPolyfill.resolve(cwd, entry.import);
|
|
1622
|
+
let code: string;
|
|
1623
|
+
const esbuildPlatform =
|
|
1624
|
+
inputOpts.platform === "browser" ? "browser" : "node";
|
|
1625
|
+
const esbuildFormat = format === "cjs" ? "cjs" : "esm";
|
|
1626
|
+
|
|
1627
|
+
const doBuild = async (ep: string) => {
|
|
1628
|
+
const result = await esbuildMod.build({
|
|
1629
|
+
entryPoints: [ep],
|
|
1630
|
+
bundle: true,
|
|
1631
|
+
write: false,
|
|
1632
|
+
format: esbuildFormat,
|
|
1633
|
+
platform: esbuildPlatform,
|
|
1634
|
+
sourcemap: outputOpts.sourcemap ? "inline" : false,
|
|
1635
|
+
target: "esnext",
|
|
1636
|
+
logLevel: "warning",
|
|
1637
|
+
});
|
|
1638
|
+
return result.outputFiles?.[0]?.text ?? vol.readFileSync(ep, "utf8");
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
try {
|
|
1642
|
+
code = await doBuild(entryPath);
|
|
1643
|
+
|
|
1644
|
+
// Empty output = thin ESM re-export of CJS; try browser-ESM re-resolve
|
|
1645
|
+
if (
|
|
1646
|
+
esbuildFormat === "esm" &&
|
|
1647
|
+
esbuildPlatform === "browser" &&
|
|
1648
|
+
code.length < 300 &&
|
|
1649
|
+
extractExports(code).length === 0
|
|
1650
|
+
) {
|
|
1651
|
+
const alt = reResolveEntry(entryPath);
|
|
1652
|
+
if (alt) {
|
|
1653
|
+
const altCode = await doBuild(alt);
|
|
1654
|
+
if (altCode.length > code.length) {
|
|
1655
|
+
code = altCode;
|
|
1656
|
+
entryPath = alt;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
} catch (buildErr) {
|
|
1661
|
+
const buildMsg = buildErr instanceof Error ? buildErr.message : String(buildErr);
|
|
1662
|
+
_nativeConsole.warn("[rolldown bundle] esbuild failed for", entryPath, buildMsg);
|
|
1663
|
+
try {
|
|
1664
|
+
code = vol.readFileSync(entryPath, "utf8");
|
|
1665
|
+
} catch {
|
|
1666
|
+
code = "";
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
const name =
|
|
1670
|
+
entry.name ||
|
|
1671
|
+
pathPolyfill.basename(
|
|
1672
|
+
entryPath,
|
|
1673
|
+
pathPolyfill.extname(entryPath),
|
|
1674
|
+
);
|
|
1675
|
+
const fileName =
|
|
1676
|
+
typeof entryFileNames === "string"
|
|
1677
|
+
? entryFileNames.replace("[name]", name)
|
|
1678
|
+
: name + ".js";
|
|
1679
|
+
const exports = extractExports(code);
|
|
1680
|
+
this.#watchFiles.push(entryPath);
|
|
1681
|
+
chunks.push({
|
|
1682
|
+
dropInner: () => ({ freed: true }),
|
|
1683
|
+
getFileName: () => fileName,
|
|
1684
|
+
getName: () => name,
|
|
1685
|
+
getCode: () => code,
|
|
1686
|
+
getExports: () => exports,
|
|
1687
|
+
getIsEntry: () => true,
|
|
1688
|
+
getIsDynamicEntry: () => false,
|
|
1689
|
+
getFacadeModuleId: () => entryPath,
|
|
1690
|
+
getSourcemapFileName: () => null,
|
|
1691
|
+
getPreliminaryFileName: () => fileName,
|
|
1692
|
+
getModules: () => ({ values: [], keys: [] }),
|
|
1693
|
+
getImports: () => [],
|
|
1694
|
+
getDynamicImports: () => [],
|
|
1695
|
+
getModuleIds: () => [entryPath],
|
|
1696
|
+
getMap: () => null,
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
return { chunks, assets: [] };
|
|
1700
|
+
}
|
|
1701
|
+
},
|
|
1702
|
+
// Constructor (not class) so methods are own-enumerable — Vite iterates with for...in
|
|
1703
|
+
BindingCallableBuiltinPlugin: function (this: any, _opts: any) {
|
|
1704
|
+
this.resolveId = async function (
|
|
1705
|
+
specifier: string,
|
|
1706
|
+
importer?: string | null,
|
|
1707
|
+
) {
|
|
1708
|
+
if (!specifier || typeof specifier !== "string") return null;
|
|
1709
|
+
if (specifier.startsWith("\0")) return null;
|
|
1710
|
+
if (
|
|
1711
|
+
specifier.includes(":") &&
|
|
1712
|
+
!specifier.startsWith("/") &&
|
|
1713
|
+
!specifier.startsWith(".")
|
|
1714
|
+
)
|
|
1715
|
+
return null;
|
|
1716
|
+
|
|
1717
|
+
// Preserve query/hash (Vue SFC virtual modules need them)
|
|
1718
|
+
let cleanSpec = specifier;
|
|
1719
|
+
let querySuffix = "";
|
|
1720
|
+
const qIdx = cleanSpec.indexOf("?");
|
|
1721
|
+
if (qIdx !== -1) {
|
|
1722
|
+
querySuffix = cleanSpec.slice(qIdx);
|
|
1723
|
+
cleanSpec = cleanSpec.slice(0, qIdx);
|
|
1724
|
+
}
|
|
1725
|
+
const hIdx = cleanSpec.indexOf("#");
|
|
1726
|
+
if (hIdx !== -1) {
|
|
1727
|
+
querySuffix = cleanSpec.slice(hIdx) + querySuffix;
|
|
1728
|
+
cleanSpec = cleanSpec.slice(0, hIdx);
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// Strip Vite dev server prefixes like /@fs/ and /@id/
|
|
1732
|
+
let stripped = cleanSpec;
|
|
1733
|
+
if (
|
|
1734
|
+
stripped.startsWith("/@") &&
|
|
1735
|
+
stripped.length > 2 &&
|
|
1736
|
+
stripped[2] !== "/"
|
|
1737
|
+
) {
|
|
1738
|
+
const secondSlash = stripped.indexOf("/", 2);
|
|
1739
|
+
if (secondSlash !== -1) {
|
|
1740
|
+
stripped = stripped.slice(secondSlash);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
const fromDir = importer
|
|
1745
|
+
? pathPolyfill.dirname(
|
|
1746
|
+
importer.startsWith("/@")
|
|
1747
|
+
? importer.slice(importer.indexOf("/", 2))
|
|
1748
|
+
: importer,
|
|
1749
|
+
)
|
|
1750
|
+
: (typeof globalThis !== "undefined" &&
|
|
1751
|
+
(globalThis as any).process?.cwd?.()) ||
|
|
1752
|
+
"/";
|
|
1753
|
+
|
|
1754
|
+
for (const candidate of stripped !== cleanSpec
|
|
1755
|
+
? [stripped, cleanSpec]
|
|
1756
|
+
: [cleanSpec]) {
|
|
1757
|
+
try {
|
|
1758
|
+
const resolved = resolveId(candidate, fromDir, true);
|
|
1759
|
+
if (resolved) {
|
|
1760
|
+
return { id: resolved + querySuffix };
|
|
1761
|
+
}
|
|
1762
|
+
} catch (_e) { /* not found */ }
|
|
1763
|
+
|
|
1764
|
+
if (candidate.startsWith("/")) {
|
|
1765
|
+
if (vol.existsSync(candidate)) return { id: candidate + querySuffix };
|
|
1766
|
+
const cwd =
|
|
1767
|
+
(typeof globalThis !== "undefined" &&
|
|
1768
|
+
(globalThis as any).process?.cwd?.()) ||
|
|
1769
|
+
"/";
|
|
1770
|
+
if (cwd !== "/") {
|
|
1771
|
+
const abs = cwd + candidate;
|
|
1772
|
+
if (vol.existsSync(abs)) return { id: abs + querySuffix };
|
|
1773
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
1774
|
+
if (vol.existsSync(abs + ext)) return { id: abs + ext + querySuffix };
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return null;
|
|
1780
|
+
};
|
|
1781
|
+
this.load = async function () {
|
|
1782
|
+
return null;
|
|
1783
|
+
};
|
|
1784
|
+
this.transform = async function () {
|
|
1785
|
+
return null;
|
|
1786
|
+
};
|
|
1787
|
+
this.watchChange = async function () {};
|
|
1788
|
+
this.buildStart = function () {};
|
|
1789
|
+
this.buildEnd = function () {};
|
|
1790
|
+
this.renderChunk = function () {
|
|
1791
|
+
return null;
|
|
1792
|
+
};
|
|
1793
|
+
this.generateBundle = function () {};
|
|
1794
|
+
this.moduleParsed = function () {};
|
|
1795
|
+
},
|
|
1796
|
+
resolveTsconfig: () => ({
|
|
1797
|
+
tsconfig: {
|
|
1798
|
+
compilerOptions: {},
|
|
1799
|
+
},
|
|
1800
|
+
tsconfigFilePaths: [],
|
|
1801
|
+
}),
|
|
1802
|
+
collapseSourcemaps: () => null,
|
|
1803
|
+
freeExternalMemory: noop,
|
|
1804
|
+
scan: noopAsync,
|
|
1805
|
+
BindingWatcher: class {
|
|
1806
|
+
constructor(_options?: any, _notifyOption?: any) {}
|
|
1807
|
+
async close() {}
|
|
1808
|
+
async start(_listener: any) {}
|
|
1809
|
+
},
|
|
1810
|
+
BindingWatcherBundler: class {
|
|
1811
|
+
async close() {}
|
|
1812
|
+
},
|
|
1813
|
+
BindingDevEngine: class {
|
|
1814
|
+
constructor(_options?: any, _devOptions?: any) {}
|
|
1815
|
+
async run() {}
|
|
1816
|
+
async ensureCurrentBuildFinish() {}
|
|
1817
|
+
async getBundleState() {
|
|
1818
|
+
return { lastFullBuildFailed: false, hasStaleOutput: false };
|
|
1819
|
+
}
|
|
1820
|
+
async ensureLatestBuildOutput() {}
|
|
1821
|
+
async invalidate() {
|
|
1822
|
+
return [];
|
|
1823
|
+
}
|
|
1824
|
+
async registerModules() {}
|
|
1825
|
+
async removeClient() {}
|
|
1826
|
+
async close() {}
|
|
1827
|
+
async compileEntry() {
|
|
1828
|
+
return "";
|
|
1829
|
+
}
|
|
1830
|
+
},
|
|
1831
|
+
BindingPluginContext: class {},
|
|
1832
|
+
BindingTransformPluginContext: class {},
|
|
1833
|
+
BindingLoadPluginContext: class {},
|
|
1834
|
+
BindingChunkingContext: class {},
|
|
1835
|
+
BindingOutputChunk: class {},
|
|
1836
|
+
BindingOutputAsset: class {},
|
|
1837
|
+
BindingRenderedChunk: class {},
|
|
1838
|
+
BindingRenderedChunkMeta: class {},
|
|
1839
|
+
BindingRenderedModule: class {},
|
|
1840
|
+
BindingModuleInfo: class {},
|
|
1841
|
+
BindingNormalizedOptions: class {},
|
|
1842
|
+
BindingSourceMap: class {},
|
|
1843
|
+
BindingDecodedMap: class {},
|
|
1844
|
+
BindingBundleEndEventData: class {},
|
|
1845
|
+
BindingBundleErrorEventData: class {},
|
|
1846
|
+
BindingWatcherChangeData: class {},
|
|
1847
|
+
BindingWatcherEvent: class {},
|
|
1848
|
+
TsconfigCache: class {},
|
|
1849
|
+
ParallelJsPluginRegistry: class {},
|
|
1850
|
+
ScheduledBuild: class {},
|
|
1851
|
+
ExportExportNameKind: {},
|
|
1852
|
+
ExportImportNameKind: {},
|
|
1853
|
+
ExportLocalNameKind: {},
|
|
1854
|
+
ImportNameKind: {},
|
|
1855
|
+
FilterTokenKind: {},
|
|
1856
|
+
BindingChunkModuleOrderBy: { ModuleId: 0, ExecOrder: 1 },
|
|
1857
|
+
BindingPluginOrder: { Pre: 0, Post: 1 },
|
|
1858
|
+
BindingAttachDebugInfo: { None: 0, Simple: 1, Full: 2 },
|
|
1859
|
+
BindingLogLevel: { Silent: 0, Debug: 1, Warn: 2, Info: 3 },
|
|
1860
|
+
BindingPropertyReadSideEffects: { Always: 0, False: 1 },
|
|
1861
|
+
BindingPropertyWriteSideEffects: { Always: 0, False: 1 },
|
|
1862
|
+
BindingBuiltinPluginName: {},
|
|
1863
|
+
BindingRebuildStrategy: { Always: 0, Auto: 1, Never: 2 },
|
|
1864
|
+
BindingMagicString: class {
|
|
1865
|
+
_str: string;
|
|
1866
|
+
_original: string;
|
|
1867
|
+
_changed: boolean;
|
|
1868
|
+
_filename: string | null;
|
|
1869
|
+
constructor(source?: string, opts?: any) {
|
|
1870
|
+
this._str = source ?? "";
|
|
1871
|
+
this._original = this._str;
|
|
1872
|
+
this._changed = false;
|
|
1873
|
+
this._filename = opts?.filename ?? null;
|
|
1874
|
+
}
|
|
1875
|
+
get filename() {
|
|
1876
|
+
return this._filename;
|
|
1877
|
+
}
|
|
1878
|
+
replace(from: string, to: string) {
|
|
1879
|
+
this._str = this._str.replace(from, to);
|
|
1880
|
+
if (this._str !== this._original) this._changed = true;
|
|
1881
|
+
return this;
|
|
1882
|
+
}
|
|
1883
|
+
replaceAll(from: string, to: string) {
|
|
1884
|
+
this._str = this._str.split(from).join(to);
|
|
1885
|
+
if (this._str !== this._original) this._changed = true;
|
|
1886
|
+
return this;
|
|
1887
|
+
}
|
|
1888
|
+
prepend(content: string) {
|
|
1889
|
+
this._str = content + this._str;
|
|
1890
|
+
this._changed = true;
|
|
1891
|
+
return this;
|
|
1892
|
+
}
|
|
1893
|
+
append(content: string) {
|
|
1894
|
+
this._str += content;
|
|
1895
|
+
this._changed = true;
|
|
1896
|
+
return this;
|
|
1897
|
+
}
|
|
1898
|
+
prependLeft(idx: number, content: string) {
|
|
1899
|
+
this._str =
|
|
1900
|
+
this._str.slice(0, idx) + content + this._str.slice(idx);
|
|
1901
|
+
this._changed = true;
|
|
1902
|
+
return this;
|
|
1903
|
+
}
|
|
1904
|
+
prependRight(idx: number, content: string) {
|
|
1905
|
+
this._str =
|
|
1906
|
+
this._str.slice(0, idx) + content + this._str.slice(idx);
|
|
1907
|
+
this._changed = true;
|
|
1908
|
+
return this;
|
|
1909
|
+
}
|
|
1910
|
+
appendLeft(idx: number, content: string) {
|
|
1911
|
+
this._str =
|
|
1912
|
+
this._str.slice(0, idx) + content + this._str.slice(idx);
|
|
1913
|
+
this._changed = true;
|
|
1914
|
+
return this;
|
|
1915
|
+
}
|
|
1916
|
+
appendRight(idx: number, content: string) {
|
|
1917
|
+
this._str =
|
|
1918
|
+
this._str.slice(0, idx) + content + this._str.slice(idx);
|
|
1919
|
+
this._changed = true;
|
|
1920
|
+
return this;
|
|
1921
|
+
}
|
|
1922
|
+
overwrite(start: number, end: number, content: string) {
|
|
1923
|
+
this._str =
|
|
1924
|
+
this._str.slice(0, start) + content + this._str.slice(end);
|
|
1925
|
+
this._changed = true;
|
|
1926
|
+
return this;
|
|
1927
|
+
}
|
|
1928
|
+
update(start: number, end: number, content: string) {
|
|
1929
|
+
return this.overwrite(start, end, content);
|
|
1930
|
+
}
|
|
1931
|
+
remove(start: number, end: number) {
|
|
1932
|
+
return this.overwrite(start, end, "");
|
|
1933
|
+
}
|
|
1934
|
+
toString() {
|
|
1935
|
+
return this._str;
|
|
1936
|
+
}
|
|
1937
|
+
hasChanged() {
|
|
1938
|
+
return this._changed;
|
|
1939
|
+
}
|
|
1940
|
+
length() {
|
|
1941
|
+
return this._str.length;
|
|
1942
|
+
}
|
|
1943
|
+
isEmpty() {
|
|
1944
|
+
return this._str.length === 0;
|
|
1945
|
+
}
|
|
1946
|
+
insert(_index: number, _content: string): never {
|
|
1947
|
+
throw new Error(
|
|
1948
|
+
"magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...) instead",
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
indent(indentor?: string) {
|
|
1952
|
+
const ind = indentor ?? "\t";
|
|
1953
|
+
this._str = this._str.replace(/^/gm, ind);
|
|
1954
|
+
this._changed = true;
|
|
1955
|
+
return this;
|
|
1956
|
+
}
|
|
1957
|
+
trim(_charType?: string) {
|
|
1958
|
+
this._str = this._str.trim();
|
|
1959
|
+
if (this._str !== this._original) this._changed = true;
|
|
1960
|
+
return this;
|
|
1961
|
+
}
|
|
1962
|
+
trimStart(_charType?: string) {
|
|
1963
|
+
this._str = this._str.trimStart();
|
|
1964
|
+
if (this._str !== this._original) this._changed = true;
|
|
1965
|
+
return this;
|
|
1966
|
+
}
|
|
1967
|
+
trimEnd(_charType?: string) {
|
|
1968
|
+
this._str = this._str.trimEnd();
|
|
1969
|
+
if (this._str !== this._original) this._changed = true;
|
|
1970
|
+
return this;
|
|
1971
|
+
}
|
|
1972
|
+
trimLines() {
|
|
1973
|
+
this._str = this._str.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
1974
|
+
if (this._str !== this._original) this._changed = true;
|
|
1975
|
+
return this;
|
|
1976
|
+
}
|
|
1977
|
+
clone() {
|
|
1978
|
+
const c = new (this.constructor as any)(this._str);
|
|
1979
|
+
c._original = this._original;
|
|
1980
|
+
c._changed = this._changed;
|
|
1981
|
+
return c;
|
|
1982
|
+
}
|
|
1983
|
+
lastChar() {
|
|
1984
|
+
return this._str[this._str.length - 1] || "";
|
|
1985
|
+
}
|
|
1986
|
+
lastLine() {
|
|
1987
|
+
const idx = this._str.lastIndexOf("\n");
|
|
1988
|
+
return idx === -1 ? this._str : this._str.slice(idx + 1);
|
|
1989
|
+
}
|
|
1990
|
+
snip(start: number, end: number) {
|
|
1991
|
+
return new (this.constructor as any)(this._str.slice(start, end));
|
|
1992
|
+
}
|
|
1993
|
+
reset(start: number, end: number) {
|
|
1994
|
+
const s = start < 0 ? this._original.length + start : start;
|
|
1995
|
+
const e = end < 0 ? this._original.length + end : end;
|
|
1996
|
+
const original = this._original.slice(s, e);
|
|
1997
|
+
this._str = this._str.slice(0, s) + original + this._str.slice(e);
|
|
1998
|
+
return this;
|
|
1999
|
+
}
|
|
2000
|
+
slice(start?: number, end?: number) {
|
|
2001
|
+
return this._str.slice(start, end);
|
|
2002
|
+
}
|
|
2003
|
+
relocate(start: number, end: number, to: number) {
|
|
2004
|
+
const chunk = this._str.slice(start, end);
|
|
2005
|
+
const without = this._str.slice(0, start) + this._str.slice(end);
|
|
2006
|
+
const adjustedTo = to > end ? to - (end - start) : to;
|
|
2007
|
+
this._str =
|
|
2008
|
+
without.slice(0, adjustedTo) +
|
|
2009
|
+
chunk +
|
|
2010
|
+
without.slice(adjustedTo);
|
|
2011
|
+
this._changed = true;
|
|
2012
|
+
return this;
|
|
2013
|
+
}
|
|
2014
|
+
move(start: number, end: number, index: number) {
|
|
2015
|
+
return this.relocate(start, end, index);
|
|
2016
|
+
}
|
|
2017
|
+
generateMap(_opts?: any) {
|
|
2018
|
+
return { version: 3, sources: [], mappings: "", names: [] };
|
|
2019
|
+
}
|
|
2020
|
+
generateDecodedMap(_opts?: any) {
|
|
2021
|
+
return { version: 3, sources: [], mappings: [], names: [] };
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
return id;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
if (id.startsWith("#")) {
|
|
2030
|
+
let dir = fromDir;
|
|
2031
|
+
while (dir !== "/" && dir) {
|
|
2032
|
+
const mf = readManifest(pathPolyfill.join(dir, "package.json"));
|
|
2033
|
+
if (mf?.imports) {
|
|
2034
|
+
for (const conds of [
|
|
2035
|
+
{ browser: true, require: true },
|
|
2036
|
+
{ require: true },
|
|
2037
|
+
{ browser: true, import: true },
|
|
2038
|
+
{},
|
|
2039
|
+
] as const) {
|
|
2040
|
+
try {
|
|
2041
|
+
const resolved = resolveImports(mf, id, conds);
|
|
2042
|
+
if (resolved?.length) {
|
|
2043
|
+
const full = pathPolyfill.join(dir, resolved[0]);
|
|
2044
|
+
if (vol.existsSync(full)) return full;
|
|
2045
|
+
for (const ext of IMPORTS_FIELD_EXTENSIONS) {
|
|
2046
|
+
if (vol.existsSync(full + ext)) return full + ext;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
} catch {
|
|
2050
|
+
/* try next condition set */
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
const parent = pathPolyfill.dirname(dir);
|
|
2055
|
+
if (parent === dir) break;
|
|
2056
|
+
dir = parent;
|
|
2057
|
+
}
|
|
2058
|
+
// Unresolvable # imports get a stub (many are just feature-detection flags)
|
|
2059
|
+
const stubPath = `/node_modules/.nodepod-stubs/${id.slice(1)}.js`;
|
|
2060
|
+
if (!vol.existsSync(stubPath)) {
|
|
2061
|
+
vol.mkdirSync(pathPolyfill.dirname(stubPath), { recursive: true });
|
|
2062
|
+
vol.writeFileSync(stubPath, "module.exports = {};");
|
|
2063
|
+
}
|
|
2064
|
+
return stubPath;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
const cacheKey = `${fromDir}|${id}`;
|
|
2068
|
+
const cached = resolveCache.get(cacheKey);
|
|
2069
|
+
if (cached !== undefined) {
|
|
2070
|
+
if (cached === null) {
|
|
2071
|
+
const e = new Error(`Cannot find module '${id}'`) as Error & { code: string };
|
|
2072
|
+
e.code = "MODULE_NOT_FOUND";
|
|
2073
|
+
throw e;
|
|
2074
|
+
}
|
|
2075
|
+
return cached;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
const tryFile = (base: string): string | null => {
|
|
2079
|
+
if (vol.existsSync(base)) {
|
|
2080
|
+
const s = vol.statSync(base);
|
|
2081
|
+
if (s.isFile()) return base;
|
|
2082
|
+
const localMf = readManifest(pathPolyfill.join(base, "package.json"));
|
|
2083
|
+
if (localMf?.main) {
|
|
2084
|
+
const mainPath = pathPolyfill.join(base, localMf.main);
|
|
2085
|
+
if (vol.existsSync(mainPath)) {
|
|
2086
|
+
const ms = vol.statSync(mainPath);
|
|
2087
|
+
if (ms.isFile()) return mainPath;
|
|
2088
|
+
}
|
|
2089
|
+
for (const ext of MAIN_FIELD_EXTENSIONS) {
|
|
2090
|
+
const withExt = mainPath + ext;
|
|
2091
|
+
if (vol.existsSync(withExt)) return withExt;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
for (const idx of INDEX_FILES) {
|
|
2095
|
+
const idxPath = pathPolyfill.join(base, idx);
|
|
2096
|
+
if (vol.existsSync(idxPath)) return idxPath;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
for (const ext of [...MAIN_FIELD_EXTENSIONS, ".node"]) {
|
|
2100
|
+
const withExt = base + ext;
|
|
2101
|
+
if (vol.existsSync(withExt)) return withExt;
|
|
2102
|
+
}
|
|
2103
|
+
return null;
|
|
2104
|
+
};
|
|
2105
|
+
|
|
2106
|
+
if (id === "." || id === "..") id = id + "/";
|
|
2107
|
+
if (id.startsWith("./") || id.startsWith("../") || id.startsWith("/")) {
|
|
2108
|
+
const abs = id.startsWith("/") ? id : pathPolyfill.resolve(fromDir, id);
|
|
2109
|
+
const found = tryFile(abs);
|
|
2110
|
+
if (found) {
|
|
2111
|
+
resolveCache.set(cacheKey, found);
|
|
2112
|
+
return found;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
resolveCache.set(cacheKey, null);
|
|
2116
|
+
const e = new Error(`Cannot find module '${id}' from '${fromDir}'`) as Error & { code: string };
|
|
2117
|
+
e.code = "MODULE_NOT_FOUND";
|
|
2118
|
+
throw e;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
const applyBrowserRemap = (
|
|
2122
|
+
resolved: string,
|
|
2123
|
+
manifest: PackageManifest,
|
|
2124
|
+
pkgRoot: string,
|
|
2125
|
+
): string | null => {
|
|
2126
|
+
if (!manifest.browser || typeof manifest.browser !== "object")
|
|
2127
|
+
return resolved;
|
|
2128
|
+
const map = manifest.browser as Record<string, string | false>;
|
|
2129
|
+
const rel = "./" + pathPolyfill.relative(pkgRoot, resolved);
|
|
2130
|
+
const relNoExt = rel.replace(/\.(js|json|cjs|mjs)$/, "");
|
|
2131
|
+
for (const k of [rel, relNoExt]) {
|
|
2132
|
+
if (k in map) {
|
|
2133
|
+
if (map[k] === false) return null;
|
|
2134
|
+
return tryFile(pathPolyfill.join(pkgRoot, map[k] as string));
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
return resolved;
|
|
2138
|
+
};
|
|
2139
|
+
|
|
2140
|
+
const tryNodeModules = (nmDir: string, moduleId: string): string | null => {
|
|
2141
|
+
const parts = moduleId.split("/");
|
|
2142
|
+
const pkgName =
|
|
2143
|
+
parts[0].startsWith("@") && parts.length > 1
|
|
2144
|
+
? `${parts[0]}/${parts[1]}`
|
|
2145
|
+
: parts[0];
|
|
2146
|
+
|
|
2147
|
+
const pkgRoot = pathPolyfill.join(nmDir, pkgName);
|
|
2148
|
+
const mfPath = pathPolyfill.join(pkgRoot, "package.json");
|
|
2149
|
+
const manifest = readManifest(mfPath);
|
|
2150
|
+
|
|
2151
|
+
if (manifest) {
|
|
2152
|
+
let exportsResolved = false;
|
|
2153
|
+
if (manifest.exports) {
|
|
2154
|
+
const subpath =
|
|
2155
|
+
moduleId === pkgName
|
|
2156
|
+
? "."
|
|
2157
|
+
: "./" + moduleId.slice(pkgName.length + 1);
|
|
2158
|
+
|
|
2159
|
+
// Custom export conditions from --conditions flag / NODE_OPTIONS
|
|
2160
|
+
const extraConditions: string[] = [];
|
|
2161
|
+
const execArgv: string[] = proc.execArgv || [];
|
|
2162
|
+
for (let ai = 0; ai < execArgv.length; ai++) {
|
|
2163
|
+
const arg = execArgv[ai];
|
|
2164
|
+
if (arg === "--conditions" || arg === "-C") {
|
|
2165
|
+
if (ai + 1 < execArgv.length) extraConditions.push(execArgv[++ai]);
|
|
2166
|
+
} else if (arg.startsWith("--conditions=")) {
|
|
2167
|
+
extraConditions.push(arg.slice("--conditions=".length));
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
const nodeOpts = proc.env?.NODE_OPTIONS || "";
|
|
2171
|
+
const condMatch = nodeOpts.matchAll(/(?:--conditions[= ]|-C )(\S+)/g);
|
|
2172
|
+
for (const m of condMatch) extraConditions.push(m[1]);
|
|
2173
|
+
const condExtra = extraConditions.length > 0
|
|
2174
|
+
? { conditions: extraConditions }
|
|
2175
|
+
: {};
|
|
2176
|
+
|
|
2177
|
+
const baseSets: Record<string, unknown>[] = preferEsm
|
|
2178
|
+
? [
|
|
2179
|
+
{ node: true, import: true, ...condExtra },
|
|
2180
|
+
{ browser: true, import: true, ...condExtra },
|
|
2181
|
+
{ import: true, ...condExtra },
|
|
2182
|
+
{ node: true, require: true, ...condExtra },
|
|
2183
|
+
{ browser: true, require: true, ...condExtra },
|
|
2184
|
+
{ require: true, ...condExtra },
|
|
2185
|
+
]
|
|
2186
|
+
: [
|
|
2187
|
+
{ node: true, require: true, ...condExtra },
|
|
2188
|
+
{ browser: true, require: true, ...condExtra },
|
|
2189
|
+
{ require: true, ...condExtra },
|
|
2190
|
+
{ node: true, import: true, ...condExtra },
|
|
2191
|
+
{ browser: true, import: true, ...condExtra },
|
|
2192
|
+
{ import: true, ...condExtra },
|
|
2193
|
+
];
|
|
2194
|
+
|
|
2195
|
+
for (const conds of baseSets) {
|
|
2196
|
+
try {
|
|
2197
|
+
const resolved = resolveExports(manifest, subpath, conds);
|
|
2198
|
+
if (resolved?.length) {
|
|
2199
|
+
const full = pathPolyfill.join(pkgRoot, resolved[0]);
|
|
2200
|
+
const found = tryFile(full);
|
|
2201
|
+
if (found) {
|
|
2202
|
+
if (found.endsWith(".cjs")) {
|
|
2203
|
+
try {
|
|
2204
|
+
const content = vol.readFileSync(found, "utf8");
|
|
2205
|
+
if (content.trimStart().startsWith("throw ")) continue;
|
|
2206
|
+
} catch {
|
|
2207
|
+
/* proceed */
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
exportsResolved = true;
|
|
2211
|
+
return found;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
} catch {
|
|
2215
|
+
/* try next */
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
if (!exportsResolved && pkgName === moduleId) {
|
|
2221
|
+
let entry: string | undefined;
|
|
2222
|
+
if (typeof manifest.browser === "string") entry = manifest.browser;
|
|
2223
|
+
if (!entry && manifest.module) entry = manifest.module as string;
|
|
2224
|
+
if (!entry) entry = manifest.main || "index.js";
|
|
2225
|
+
const found = tryFile(pathPolyfill.join(pkgRoot, entry));
|
|
2226
|
+
if (found) return found;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
const directPath = pathPolyfill.join(nmDir, moduleId);
|
|
2231
|
+
return tryFile(directPath);
|
|
2232
|
+
};
|
|
2233
|
+
|
|
2234
|
+
let searchDir = fromDir;
|
|
2235
|
+
while (searchDir !== "/") {
|
|
2236
|
+
const nmDir = pathPolyfill.join(searchDir, "node_modules");
|
|
2237
|
+
const found = tryNodeModules(nmDir, id);
|
|
2238
|
+
if (found) {
|
|
2239
|
+
resolveCache.set(cacheKey, found);
|
|
2240
|
+
return found;
|
|
2241
|
+
}
|
|
2242
|
+
searchDir = pathPolyfill.dirname(searchDir);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
const rootFound = tryNodeModules("/node_modules", id);
|
|
2246
|
+
if (rootFound) {
|
|
2247
|
+
resolveCache.set(cacheKey, rootFound);
|
|
2248
|
+
return rootFound;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// Fallback: resolve from cwd (handles modules loaded from temp/bundled locations)
|
|
2252
|
+
const cwd = proc.cwd();
|
|
2253
|
+
if (cwd !== fromDir && cwd !== "/") {
|
|
2254
|
+
let fallbackDir = cwd;
|
|
2255
|
+
while (fallbackDir !== "/" && fallbackDir !== fromDir) {
|
|
2256
|
+
const nmDir = pathPolyfill.join(fallbackDir, "node_modules");
|
|
2257
|
+
const found = tryNodeModules(nmDir, id);
|
|
2258
|
+
if (found) {
|
|
2259
|
+
resolveCache.set(cacheKey, found);
|
|
2260
|
+
return found;
|
|
2261
|
+
}
|
|
2262
|
+
fallbackDir = pathPolyfill.dirname(fallbackDir);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
resolveCache.set(cacheKey, null);
|
|
2267
|
+
const e = new Error(`Cannot find module '${id}' from '${fromDir}'`) as Error & { code: string };
|
|
2268
|
+
e.code = "MODULE_NOT_FOUND";
|
|
2269
|
+
throw e;
|
|
2270
|
+
};
|
|
2271
|
+
|
|
2272
|
+
const loadModule = (resolved: string): ModuleRecord => {
|
|
2273
|
+
if (cache[resolved]) return cache[resolved];
|
|
2274
|
+
|
|
2275
|
+
// Package dedup: reuse first instance of name@version:path to prevent
|
|
2276
|
+
// "Cannot use X from another module or realm" errors
|
|
2277
|
+
const nmIdx = resolved.lastIndexOf("/node_modules/");
|
|
2278
|
+
if (nmIdx !== -1) {
|
|
2279
|
+
const afterNm = resolved.slice(nmIdx + "/node_modules/".length);
|
|
2280
|
+
const parts = afterNm.split("/");
|
|
2281
|
+
const pkgName = parts[0].startsWith("@") ? parts[0] + "/" + parts[1] : parts[0];
|
|
2282
|
+
const pkgDir = resolved.slice(0, nmIdx) + "/node_modules/" + pkgName;
|
|
2283
|
+
try {
|
|
2284
|
+
const pkgJson = JSON.parse(vol.readFileSync(pkgDir + "/package.json", "utf8"));
|
|
2285
|
+
// Include file path so different subpath exports (svelte vs svelte/compiler) aren't deduped
|
|
2286
|
+
const identity = pkgName + "@" + (pkgJson.version || "0.0.0") + ":" + afterNm;
|
|
2287
|
+
if (!_pkgIdentityMap[identity]) {
|
|
2288
|
+
_pkgIdentityMap[identity] = resolved;
|
|
2289
|
+
} else if (_pkgIdentityMap[identity] !== resolved) {
|
|
2290
|
+
// Only reuse fully-loaded modules — returning mid-execution ones causes "X is not a function"
|
|
2291
|
+
const canonical = _pkgIdentityMap[identity];
|
|
2292
|
+
if (cache[canonical] && cache[canonical].loaded) {
|
|
2293
|
+
cache[resolved] = cache[canonical];
|
|
2294
|
+
return cache[canonical];
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
} catch { /* no package.json */ }
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
const _loadDepth = ((globalThis as any).__loadModuleDepth ?? 0) + 1;
|
|
2301
|
+
(globalThis as any).__loadModuleDepth = _loadDepth;
|
|
2302
|
+
|
|
2303
|
+
const record: ModuleRecord = {
|
|
2304
|
+
id: resolved,
|
|
2305
|
+
filename: resolved,
|
|
2306
|
+
exports: {},
|
|
2307
|
+
loaded: false,
|
|
2308
|
+
children: [],
|
|
2309
|
+
paths: [],
|
|
2310
|
+
};
|
|
2311
|
+
|
|
2312
|
+
cache[resolved] = record;
|
|
2313
|
+
|
|
2314
|
+
const keys = Object.keys(cache);
|
|
2315
|
+
if (keys.length > LIMITS.MODULE_CACHE_MAX) delete cache[keys[0]];
|
|
2316
|
+
|
|
2317
|
+
if (resolved.endsWith(".json")) {
|
|
2318
|
+
const raw = vol.readFileSync(resolved, "utf8");
|
|
2319
|
+
record.exports = JSON.parse(raw);
|
|
2320
|
+
record.loaded = true;
|
|
2321
|
+
return record;
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// Native addons can't run in browser — return empty module for fallback
|
|
2325
|
+
if (resolved.endsWith(".node")) {
|
|
2326
|
+
record.exports = {};
|
|
2327
|
+
record.loaded = true;
|
|
2328
|
+
return record;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
const rawSource = vol.readFileSync(resolved, "utf8");
|
|
2332
|
+
const dir = pathPolyfill.dirname(resolved);
|
|
2333
|
+
|
|
2334
|
+
const codeCacheKey = `${resolved}|${quickDigest(rawSource)}`;
|
|
2335
|
+
let processedCode = codeCache?.get(codeCacheKey);
|
|
2336
|
+
|
|
2337
|
+
if (!processedCode) {
|
|
2338
|
+
processedCode = rawSource;
|
|
2339
|
+
if (processedCode.startsWith("#!")) {
|
|
2340
|
+
processedCode = processedCode.slice(processedCode.indexOf("\n") + 1);
|
|
2341
|
+
}
|
|
2342
|
+
if (isTypeScriptFile(resolved)) {
|
|
2343
|
+
processedCode = stripTypeScript(processedCode);
|
|
2344
|
+
}
|
|
2345
|
+
if (resolved.endsWith(".cjs")) {
|
|
2346
|
+
// CJS: only rewrite import()/import.meta via AST, skip full ESM conversion
|
|
2347
|
+
try {
|
|
2348
|
+
const cjsAst = acorn.parse(processedCode, {
|
|
2349
|
+
ecmaVersion: "latest",
|
|
2350
|
+
sourceType: "script",
|
|
2351
|
+
allowImportExportEverywhere: true,
|
|
2352
|
+
});
|
|
2353
|
+
const cjsPatches: Array<[number, number, string]> = [];
|
|
2354
|
+
const walkCjs = (node: any) => {
|
|
2355
|
+
if (!node || typeof node !== "object") return;
|
|
2356
|
+
if (Array.isArray(node)) { for (const c of node) walkCjs(c); return; }
|
|
2357
|
+
if (typeof node.type !== "string") return;
|
|
2358
|
+
if (node.type === "ImportExpression") {
|
|
2359
|
+
cjsPatches.push([node.start, node.start + 6, "__asyncLoad"]);
|
|
2360
|
+
}
|
|
2361
|
+
if (node.type === "MetaProperty" && node.meta?.name === "import" && node.property?.name === "meta") {
|
|
2362
|
+
cjsPatches.push([
|
|
2363
|
+
node.start,
|
|
2364
|
+
node.end,
|
|
2365
|
+
`({ url: "file://${resolved}", dirname: "${pathPolyfill.dirname(resolved)}", filename: "${resolved}" })`,
|
|
2366
|
+
]);
|
|
2367
|
+
}
|
|
2368
|
+
for (const key of Object.keys(node)) {
|
|
2369
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
2370
|
+
const val = node[key];
|
|
2371
|
+
if (val && typeof val === "object") walkCjs(val);
|
|
2372
|
+
}
|
|
2373
|
+
};
|
|
2374
|
+
walkCjs(cjsAst);
|
|
2375
|
+
if (cjsPatches.length > 0) {
|
|
2376
|
+
cjsPatches.sort((a, b) => b[0] - a[0]);
|
|
2377
|
+
for (const [start, end, replacement] of cjsPatches) {
|
|
2378
|
+
processedCode = processedCode.slice(0, start) + replacement + processedCode.slice(end);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
} catch { /* can't parse — leave untransformed */ }
|
|
2382
|
+
} else {
|
|
2383
|
+
processedCode = convertModuleSyntax(processedCode, resolved);
|
|
2384
|
+
}
|
|
2385
|
+
codeCache?.set(codeCacheKey, processedCode);
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
// "full" de-async strips async from all functions so cross-module calls work with __syncAwait
|
|
2389
|
+
// "topLevelOnly" preserves genuine async behavior for non-TLA modules
|
|
2390
|
+
const moduleHasTLA = resolved.endsWith(".cjs") ? false : hasTopLevelAwait(processedCode);
|
|
2391
|
+
const useFullDeAsync = deAsyncImports || moduleHasTLA;
|
|
2392
|
+
|
|
2393
|
+
const childResolver = buildResolver(
|
|
2394
|
+
vol,
|
|
2395
|
+
fsBridge,
|
|
2396
|
+
proc,
|
|
2397
|
+
dir,
|
|
2398
|
+
cache,
|
|
2399
|
+
opts,
|
|
2400
|
+
codeCache,
|
|
2401
|
+
useFullDeAsync,
|
|
2402
|
+
);
|
|
2403
|
+
childResolver.cache = cache;
|
|
2404
|
+
|
|
2405
|
+
const wrappedConsole = wrapConsole(opts.onConsole);
|
|
2406
|
+
|
|
2407
|
+
try {
|
|
2408
|
+
const metaUrl = "file://" + resolved;
|
|
2409
|
+
// CJS files: skip — would corrupt await inside async functions
|
|
2410
|
+
if (!resolved.endsWith(".cjs")) {
|
|
2411
|
+
processedCode = stripTopLevelAwait(
|
|
2412
|
+
processedCode,
|
|
2413
|
+
useFullDeAsync ? "full" : "topLevelOnly",
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
const wrapper = buildModuleWrapper(processedCode);
|
|
2417
|
+
|
|
2418
|
+
let fn;
|
|
2419
|
+
try {
|
|
2420
|
+
fn = (0, eval)(wrapper);
|
|
2421
|
+
} catch (syntaxErr) {
|
|
2422
|
+
const msg =
|
|
2423
|
+
syntaxErr instanceof Error ? syntaxErr.message : String(syntaxErr);
|
|
2424
|
+
throw new SyntaxError(`${msg} (in ${resolved})`);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
{
|
|
2428
|
+
const srcMap = (globalThis as any).__dbgSrcMap || ((globalThis as any).__dbgSrcMap = new Map());
|
|
2429
|
+
srcMap.set(resolved, wrapper);
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
const asyncLoader = makeDynamicLoader(childResolver);
|
|
2433
|
+
fn(
|
|
2434
|
+
record.exports,
|
|
2435
|
+
childResolver,
|
|
2436
|
+
record,
|
|
2437
|
+
resolved,
|
|
2438
|
+
dir,
|
|
2439
|
+
proc,
|
|
2440
|
+
wrappedConsole,
|
|
2441
|
+
{ url: metaUrl, dirname: dir, filename: resolved },
|
|
2442
|
+
asyncLoader,
|
|
2443
|
+
syncAwait,
|
|
2444
|
+
SyncPromiseClass,
|
|
2445
|
+
);
|
|
2446
|
+
|
|
2447
|
+
record.loaded = true;
|
|
2448
|
+
(globalThis as any).__loadModuleDepth = _loadDepth - 1;
|
|
2449
|
+
} catch (err) {
|
|
2450
|
+
(globalThis as any).__loadModuleDepth = _loadDepth - 1;
|
|
2451
|
+
delete cache[resolved];
|
|
2452
|
+
// >8MB WASM: retry with async compilation APIs
|
|
2453
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2454
|
+
if (
|
|
2455
|
+
errMsg.includes("disallowed on the main thread") ||
|
|
2456
|
+
errMsg.includes("buffer size is larger than") ||
|
|
2457
|
+
errMsg.includes("__WASM_COMPILE_PENDING__")
|
|
2458
|
+
) {
|
|
2459
|
+
let asyncCode = processedCode!;
|
|
2460
|
+
asyncCode = asyncCode.replace(
|
|
2461
|
+
/new\s+WebAssembly\.Module\b/g,
|
|
2462
|
+
"await __wasmCompile",
|
|
2463
|
+
);
|
|
2464
|
+
asyncCode = asyncCode.replace(
|
|
2465
|
+
/new\s+WebAssembly\.Instance\b/g,
|
|
2466
|
+
"await __wasmInstantiate",
|
|
2467
|
+
);
|
|
2468
|
+
|
|
2469
|
+
const asyncWrapper = buildModuleWrapper(asyncCode, {
|
|
2470
|
+
async: true,
|
|
2471
|
+
useNativePromise: true,
|
|
2472
|
+
includeViteVars: false,
|
|
2473
|
+
hideBrowserGlobals: false,
|
|
2474
|
+
wasmHelpers: true,
|
|
2475
|
+
});
|
|
2476
|
+
try {
|
|
2477
|
+
const asyncFn = (0, eval)(asyncWrapper);
|
|
2478
|
+
const asyncLoader = makeDynamicLoader(childResolver);
|
|
2479
|
+
const wasmReady = asyncFn(
|
|
2480
|
+
record.exports,
|
|
2481
|
+
childResolver,
|
|
2482
|
+
record,
|
|
2483
|
+
resolved,
|
|
2484
|
+
dir,
|
|
2485
|
+
proc,
|
|
2486
|
+
wrappedConsole,
|
|
2487
|
+
{ url: "file://" + resolved, dirname: dir, filename: resolved },
|
|
2488
|
+
asyncLoader,
|
|
2489
|
+
syncAwait,
|
|
2490
|
+
SyncPromiseClass,
|
|
2491
|
+
);
|
|
2492
|
+
record.loaded = true;
|
|
2493
|
+
(record as any).__wasmReady = wasmReady;
|
|
2494
|
+
cache[resolved] = record;
|
|
2495
|
+
} catch (retryErr) {
|
|
2496
|
+
if (err instanceof Error && !err.message.includes("(in /")) {
|
|
2497
|
+
err.message = `${err.message} (in ${resolved})`;
|
|
2498
|
+
}
|
|
2499
|
+
throw err;
|
|
2500
|
+
}
|
|
2501
|
+
return record;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
if (err instanceof Error && !err.message.includes("(in /")) {
|
|
2505
|
+
err.message = `${err.message} (in ${resolved})`;
|
|
2506
|
+
}
|
|
2507
|
+
throw err;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
return record;
|
|
2511
|
+
};
|
|
2512
|
+
|
|
2513
|
+
const resolver: ResolverFn = (id: string): unknown => {
|
|
2514
|
+
if (typeof id !== "string") {
|
|
2515
|
+
// Match real Node.js error: TypeError with ERR_INVALID_ARG_TYPE code
|
|
2516
|
+
const err: any = new TypeError(
|
|
2517
|
+
`The "id" argument must be of type string. Received ${id === null ? "null" : typeof id}`
|
|
2518
|
+
);
|
|
2519
|
+
err.code = "ERR_INVALID_ARG_TYPE";
|
|
2520
|
+
throw err;
|
|
2521
|
+
}
|
|
2522
|
+
if (id.startsWith("node:")) id = id.slice(5);
|
|
2523
|
+
|
|
2524
|
+
if (id === "fs") return fsBridge;
|
|
2525
|
+
if (id === "fs/promises") return fsBridge.promises;
|
|
2526
|
+
if (id === "process") return proc;
|
|
2527
|
+
if (id === "console") return wrapConsole(opts.onConsole);
|
|
2528
|
+
if (id === "worker_threads") {
|
|
2529
|
+
if (opts.workerThreadsOverride) {
|
|
2530
|
+
const base = CORE_MODULES["worker_threads"];
|
|
2531
|
+
const override = Object.assign(Object.create(null), base, opts.workerThreadsOverride);
|
|
2532
|
+
override.default = override;
|
|
2533
|
+
return override;
|
|
2534
|
+
}
|
|
2535
|
+
return CORE_MODULES["worker_threads"];
|
|
2536
|
+
}
|
|
2537
|
+
if (id === "module") {
|
|
2538
|
+
// Per-engine Module so fork() doesn't clobber parent's _resolveFilename
|
|
2539
|
+
const OrigModule = moduleSysPolyfill.Module;
|
|
2540
|
+
|
|
2541
|
+
function PerEngineModule(this: any, mid?: string, parent?: any) {
|
|
2542
|
+
OrigModule.call(this, mid, parent);
|
|
2543
|
+
}
|
|
2544
|
+
PerEngineModule.prototype = OrigModule.prototype;
|
|
2545
|
+
|
|
2546
|
+
const liveCreateRequire = (from: string) => {
|
|
2547
|
+
let fromPath = from;
|
|
2548
|
+
if (from.startsWith("file://")) {
|
|
2549
|
+
fromPath = decodeURIComponent(from.slice(7));
|
|
2550
|
+
if (fromPath.startsWith("/") && fromPath[2] === ":")
|
|
2551
|
+
fromPath = fromPath.slice(1);
|
|
2552
|
+
}
|
|
2553
|
+
const childDir = pathPolyfill.dirname(fromPath);
|
|
2554
|
+
const child = buildResolver(vol, fsBridge, proc, childDir, cache, opts, codeCache, deAsyncImports);
|
|
2555
|
+
child.cache = cache;
|
|
2556
|
+
return child;
|
|
2557
|
+
};
|
|
2558
|
+
|
|
2559
|
+
PerEngineModule.createRequire = liveCreateRequire;
|
|
2560
|
+
PerEngineModule._cache = cache;
|
|
2561
|
+
PerEngineModule._resolveFilename = (
|
|
2562
|
+
request: string,
|
|
2563
|
+
parent?: any,
|
|
2564
|
+
isMain?: boolean,
|
|
2565
|
+
options?: any,
|
|
2566
|
+
) => {
|
|
2567
|
+
if (typeof request !== "string") {
|
|
2568
|
+
const err: any = new Error(
|
|
2569
|
+
`Cannot find module '${request}'`,
|
|
2570
|
+
);
|
|
2571
|
+
err.code = "MODULE_NOT_FOUND";
|
|
2572
|
+
throw err;
|
|
2573
|
+
}
|
|
2574
|
+
if (options?.paths && Array.isArray(options.paths)) {
|
|
2575
|
+
for (const p of options.paths) {
|
|
2576
|
+
try {
|
|
2577
|
+
return resolveId(request, p);
|
|
2578
|
+
} catch {
|
|
2579
|
+
/* try next */
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
if (parent?.paths && Array.isArray(parent.paths)) {
|
|
2584
|
+
for (const p of parent.paths) {
|
|
2585
|
+
try {
|
|
2586
|
+
const dir = p.endsWith("/node_modules")
|
|
2587
|
+
? pathPolyfill.dirname(p)
|
|
2588
|
+
: p;
|
|
2589
|
+
return resolveId(request, dir);
|
|
2590
|
+
} catch {
|
|
2591
|
+
/* try next */
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
const fromDir = parent?.filename
|
|
2596
|
+
? pathPolyfill.dirname(parent.filename)
|
|
2597
|
+
: baseDir;
|
|
2598
|
+
try {
|
|
2599
|
+
return resolveId(request, fromDir);
|
|
2600
|
+
} catch {
|
|
2601
|
+
const err: any = new Error(
|
|
2602
|
+
`Cannot find module '${request}'`,
|
|
2603
|
+
);
|
|
2604
|
+
err.code = "MODULE_NOT_FOUND";
|
|
2605
|
+
throw err;
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
PerEngineModule._load = (request: string, parent?: any, isMain?: boolean) => {
|
|
2609
|
+
try {
|
|
2610
|
+
return resolver(request);
|
|
2611
|
+
} catch {
|
|
2612
|
+
return moduleSysPolyfill._load(request, parent, isMain);
|
|
2613
|
+
}
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2616
|
+
PerEngineModule.builtinModules = moduleSysPolyfill.builtinModules;
|
|
2617
|
+
PerEngineModule.isBuiltin = moduleSysPolyfill.isBuiltin;
|
|
2618
|
+
PerEngineModule._extensions = moduleSysPolyfill._extensions;
|
|
2619
|
+
PerEngineModule._pathCache = moduleSysPolyfill._pathCache;
|
|
2620
|
+
PerEngineModule._nodeModulePaths = moduleSysPolyfill._nodeModulePaths;
|
|
2621
|
+
PerEngineModule._findPath = moduleSysPolyfill._findPath;
|
|
2622
|
+
PerEngineModule.syncBuiltinESMExports =
|
|
2623
|
+
moduleSysPolyfill.syncBuiltinESMExports;
|
|
2624
|
+
PerEngineModule.wrap = moduleSysPolyfill.wrap;
|
|
2625
|
+
PerEngineModule.wrapper = moduleSysPolyfill.wrapper;
|
|
2626
|
+
PerEngineModule.Module = PerEngineModule;
|
|
2627
|
+
PerEngineModule.runMain = OrigModule.runMain;
|
|
2628
|
+
PerEngineModule._preloadModules = OrigModule._preloadModules;
|
|
2629
|
+
PerEngineModule._initPaths = OrigModule._initPaths;
|
|
2630
|
+
PerEngineModule.globalPaths = OrigModule.globalPaths;
|
|
2631
|
+
(PerEngineModule as any).default = PerEngineModule;
|
|
2632
|
+
return PerEngineModule;
|
|
2633
|
+
}
|
|
2634
|
+
if (CORE_MODULES[id]) return CORE_MODULES[id];
|
|
2635
|
+
|
|
2636
|
+
const resolved = resolveId(id, baseDir);
|
|
2637
|
+
if (CORE_MODULES[resolved]) return CORE_MODULES[resolved];
|
|
2638
|
+
|
|
2639
|
+
const rec = loadModule(resolved);
|
|
2640
|
+
// Proxy for async WASM — reads from rec.exports at access time so
|
|
2641
|
+
// reassigned module.exports is picked up after compilation finishes
|
|
2642
|
+
if ((rec as any).__wasmReady) {
|
|
2643
|
+
return new Proxy(Object.create(null), {
|
|
2644
|
+
get(_t, prop) {
|
|
2645
|
+
const ex = rec.exports as any;
|
|
2646
|
+
if (ex && prop in ex) return ex[prop];
|
|
2647
|
+
return undefined;
|
|
2648
|
+
},
|
|
2649
|
+
set(_t, prop, val) {
|
|
2650
|
+
(rec.exports as any)[prop] = val;
|
|
2651
|
+
return true;
|
|
2652
|
+
},
|
|
2653
|
+
has(_t, prop) {
|
|
2654
|
+
return rec.exports ? prop in (rec.exports as any) : false;
|
|
2655
|
+
},
|
|
2656
|
+
ownKeys() {
|
|
2657
|
+
return rec.exports ? Reflect.ownKeys(rec.exports as any) : [];
|
|
2658
|
+
},
|
|
2659
|
+
getOwnPropertyDescriptor(_t, prop) {
|
|
2660
|
+
if (!rec.exports) return undefined;
|
|
2661
|
+
return Object.getOwnPropertyDescriptor(rec.exports as any, prop);
|
|
2662
|
+
},
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
return rec.exports;
|
|
2666
|
+
};
|
|
2667
|
+
|
|
2668
|
+
resolver.resolve = (id: string, options?: { paths?: string[] }): string => {
|
|
2669
|
+
if (id === "fs" || id === "process" || CORE_MODULES[id]) return id;
|
|
2670
|
+
if (options?.paths && Array.isArray(options.paths)) {
|
|
2671
|
+
for (const p of options.paths) {
|
|
2672
|
+
try {
|
|
2673
|
+
return resolveId(id, p);
|
|
2674
|
+
} catch {
|
|
2675
|
+
/* try next */
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
return resolveId(id, baseDir);
|
|
2680
|
+
};
|
|
2681
|
+
|
|
2682
|
+
resolver.cache = cache;
|
|
2683
|
+
resolver.extensions = {
|
|
2684
|
+
".js": () => {},
|
|
2685
|
+
".json": () => {},
|
|
2686
|
+
".node": () => {},
|
|
2687
|
+
".ts": () => {},
|
|
2688
|
+
".tsx": () => {},
|
|
2689
|
+
".mjs": () => {},
|
|
2690
|
+
".cjs": () => {},
|
|
2691
|
+
};
|
|
2692
|
+
resolver.main = null;
|
|
2693
|
+
return resolver;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// ── ScriptEngine class ──
|
|
2697
|
+
export class ScriptEngine {
|
|
2698
|
+
private vol: MemoryVolume;
|
|
2699
|
+
private fsBridge: FsBridge;
|
|
2700
|
+
private proc: ProcessObject;
|
|
2701
|
+
private moduleRegistry: Record<string, ModuleRecord> = {};
|
|
2702
|
+
private opts: EngineOptions;
|
|
2703
|
+
private transformCache: Map<string, string> = new Map();
|
|
2704
|
+
|
|
2705
|
+
constructor(vol: MemoryVolume, opts: EngineOptions = {}) {
|
|
2706
|
+
this.vol = vol;
|
|
2707
|
+
this.proc = buildProcessEnv({
|
|
2708
|
+
cwd: opts.cwd || "/",
|
|
2709
|
+
env: opts.env,
|
|
2710
|
+
onStdout: opts.onStdout,
|
|
2711
|
+
onStderr: opts.onStderr,
|
|
2712
|
+
});
|
|
2713
|
+
this.fsBridge = buildFileSystemBridge(vol, () => this.proc.cwd());
|
|
2714
|
+
this.opts = opts;
|
|
2715
|
+
|
|
2716
|
+
// Don't call initShellExec here — Nodepod.boot() sets up the shell with correct cwd
|
|
2717
|
+
import("./polyfills/child_process")
|
|
2718
|
+
.then((mod) => {
|
|
2719
|
+
_shellExecPolyfill = mod;
|
|
2720
|
+
_initShellExec = mod.initShellExec;
|
|
2721
|
+
})
|
|
2722
|
+
.catch(() => {
|
|
2723
|
+
/* shell unavailable in this environment */
|
|
2724
|
+
});
|
|
2725
|
+
watcherPolyfill.setVolume(vol);
|
|
2726
|
+
scannerPolyfill.setVolume(vol);
|
|
2727
|
+
esbuildPolyfill.setVolume(vol);
|
|
2728
|
+
|
|
2729
|
+
(globalThis as any).__nodepodVolume = vol;
|
|
2730
|
+
|
|
2731
|
+
if (typeof globalThis.setImmediate === "undefined") {
|
|
2732
|
+
(globalThis as any).setImmediate = (
|
|
2733
|
+
fn: (...a: unknown[]) => void,
|
|
2734
|
+
...a: unknown[]
|
|
2735
|
+
) => setTimeout(fn, 0, ...a);
|
|
2736
|
+
(globalThis as any).clearImmediate = (id: number) => clearTimeout(id);
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
// Browsers disallow sync WebAssembly.Module() for >8MB buffers — serve from cache
|
|
2740
|
+
if (
|
|
2741
|
+
typeof WebAssembly !== "undefined" &&
|
|
2742
|
+
!(WebAssembly.Module as any).__nodepodPatched
|
|
2743
|
+
) {
|
|
2744
|
+
const OrigModule = WebAssembly.Module;
|
|
2745
|
+
const PatchedModule = function WebAssemblyModule(
|
|
2746
|
+
this: any,
|
|
2747
|
+
bytes: BufferSource,
|
|
2748
|
+
) {
|
|
2749
|
+
const cached = getCachedModule(bytes);
|
|
2750
|
+
if (cached) return cached;
|
|
2751
|
+
try {
|
|
2752
|
+
return new OrigModule(bytes);
|
|
2753
|
+
} catch (e: any) {
|
|
2754
|
+
if (
|
|
2755
|
+
e &&
|
|
2756
|
+
(e.message?.includes("disallowed on the main thread") ||
|
|
2757
|
+
e.message?.includes("buffer size is larger than"))
|
|
2758
|
+
) {
|
|
2759
|
+
const cached2 = getCachedModule(bytes);
|
|
2760
|
+
if (cached2) return cached2;
|
|
2761
|
+
const compilePromise = compileWasmInWorker(
|
|
2762
|
+
bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes as Uint8Array,
|
|
2763
|
+
);
|
|
2764
|
+
(globalThis as any).__wasmCompilePromise = compilePromise;
|
|
2765
|
+
}
|
|
2766
|
+
throw e;
|
|
2767
|
+
}
|
|
2768
|
+
} as any;
|
|
2769
|
+
PatchedModule.prototype = OrigModule.prototype;
|
|
2770
|
+
PatchedModule.__nodepodPatched = true;
|
|
2771
|
+
PatchedModule.imports = OrigModule.imports?.bind(OrigModule);
|
|
2772
|
+
PatchedModule.exports = OrigModule.exports?.bind(OrigModule);
|
|
2773
|
+
PatchedModule.customSections =
|
|
2774
|
+
OrigModule.customSections?.bind(OrigModule);
|
|
2775
|
+
(globalThis as any).WebAssembly.Module = PatchedModule;
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
// Timers need .ref()/.unref() to match Node.js API
|
|
2779
|
+
if (!(globalThis.setTimeout as any).__nodepodPatched) {
|
|
2780
|
+
const origST = globalThis.setTimeout.bind(globalThis);
|
|
2781
|
+
const origSI = globalThis.setInterval.bind(globalThis);
|
|
2782
|
+
const origCT = globalThis.clearTimeout.bind(globalThis);
|
|
2783
|
+
const origCI = globalThis.clearInterval.bind(globalThis);
|
|
2784
|
+
|
|
2785
|
+
const wrapTimeout = (id: ReturnType<typeof origST>) => {
|
|
2786
|
+
const obj = {
|
|
2787
|
+
_id: id,
|
|
2788
|
+
_ref: true,
|
|
2789
|
+
ref() { obj._ref = true; return obj; },
|
|
2790
|
+
unref() { obj._ref = false; return obj; },
|
|
2791
|
+
hasRef() { return obj._ref; },
|
|
2792
|
+
refresh() { return obj; },
|
|
2793
|
+
[Symbol.toPrimitive]() { return id; },
|
|
2794
|
+
};
|
|
2795
|
+
return obj;
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2798
|
+
const wrapInterval = (id: ReturnType<typeof origSI>) => {
|
|
2799
|
+
const obj = {
|
|
2800
|
+
_id: id,
|
|
2801
|
+
_ref: true,
|
|
2802
|
+
ref() { obj._ref = true; return obj; },
|
|
2803
|
+
unref() { obj._ref = false; return obj; },
|
|
2804
|
+
hasRef() { return obj._ref; },
|
|
2805
|
+
refresh() { return obj; },
|
|
2806
|
+
[Symbol.toPrimitive]() { return id; },
|
|
2807
|
+
};
|
|
2808
|
+
return obj;
|
|
2809
|
+
};
|
|
2810
|
+
|
|
2811
|
+
(globalThis as any).setTimeout = Object.assign(
|
|
2812
|
+
(...a: Parameters<typeof origST>) => wrapTimeout(origST(...a)),
|
|
2813
|
+
{ __nodepodPatched: true },
|
|
2814
|
+
);
|
|
2815
|
+
(globalThis as any).setInterval = Object.assign(
|
|
2816
|
+
(...a: Parameters<typeof origSI>) => wrapInterval(origSI(...a)),
|
|
2817
|
+
{ __nodepodPatched: true },
|
|
2818
|
+
);
|
|
2819
|
+
(globalThis as any).clearTimeout = (t: any) => origCT(t?._id ?? t);
|
|
2820
|
+
(globalThis as any).clearInterval = (t: any) => origCI(t?._id ?? t);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
this.patchStackTraceApi();
|
|
2824
|
+
this.patchTextDecoder();
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
private patchTextDecoder(): void {
|
|
2828
|
+
const Original = globalThis.TextDecoder;
|
|
2829
|
+
|
|
2830
|
+
class ExtendedDecoder {
|
|
2831
|
+
private enc: string;
|
|
2832
|
+
private inner: TextDecoder | null = null;
|
|
2833
|
+
|
|
2834
|
+
constructor(encoding: string = "utf-8", options?: TextDecoderOptions) {
|
|
2835
|
+
this.enc = encoding.toLowerCase();
|
|
2836
|
+
const textEncodings = [
|
|
2837
|
+
"utf-8",
|
|
2838
|
+
"utf8",
|
|
2839
|
+
"utf-16le",
|
|
2840
|
+
"utf-16be",
|
|
2841
|
+
"utf-16",
|
|
2842
|
+
"ascii",
|
|
2843
|
+
"iso-8859-1",
|
|
2844
|
+
"latin1",
|
|
2845
|
+
"windows-1252",
|
|
2846
|
+
];
|
|
2847
|
+
if (textEncodings.includes(this.enc)) {
|
|
2848
|
+
try {
|
|
2849
|
+
this.inner = new Original(encoding, options);
|
|
2850
|
+
} catch {
|
|
2851
|
+
this.inner = new Original("utf-8", options);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
decode(input?: BufferSource, options?: TextDecodeOptions): string {
|
|
2857
|
+
if (this.inner) return this.inner.decode(input, options);
|
|
2858
|
+
if (!input) return "";
|
|
2859
|
+
const bytes =
|
|
2860
|
+
input instanceof ArrayBuffer
|
|
2861
|
+
? new Uint8Array(input)
|
|
2862
|
+
: new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
|
|
2863
|
+
|
|
2864
|
+
if (this.enc === "base64") return bytesToBase64(bytes);
|
|
2865
|
+
if (this.enc === "base64url")
|
|
2866
|
+
return bytesToBase64(bytes)
|
|
2867
|
+
.replace(/\+/g, "-")
|
|
2868
|
+
.replace(/\//g, "_")
|
|
2869
|
+
.replace(/=/g, "");
|
|
2870
|
+
if (this.enc === "hex") return bytesToHex(bytes);
|
|
2871
|
+
return new Original("utf-8").decode(input, options);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
get fatal(): boolean {
|
|
2875
|
+
return this.inner?.fatal ?? false;
|
|
2876
|
+
}
|
|
2877
|
+
get ignoreBOM(): boolean {
|
|
2878
|
+
return this.inner?.ignoreBOM ?? false;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
globalThis.TextDecoder = ExtendedDecoder as unknown as typeof TextDecoder;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// Override even in Chrome — eval produces stack frames the native V8 API can't map to VFS paths
|
|
2886
|
+
private patchStackTraceApi(): void {
|
|
2887
|
+
if ((Error as any).stackTraceLimit === undefined)
|
|
2888
|
+
(Error as any).stackTraceLimit = 10;
|
|
2889
|
+
|
|
2890
|
+
function parseFrames(stack: string) {
|
|
2891
|
+
if (!stack) return [];
|
|
2892
|
+
const frames: Array<{
|
|
2893
|
+
fn: string;
|
|
2894
|
+
file: string;
|
|
2895
|
+
line: number;
|
|
2896
|
+
col: number;
|
|
2897
|
+
}> = [];
|
|
2898
|
+
for (const raw of stack.split("\n")) {
|
|
2899
|
+
const trimmed = raw.trim();
|
|
2900
|
+
if (!trimmed) continue;
|
|
2901
|
+
if (/^\w*Error\b/.test(trimmed) && !trimmed.startsWith("at ")) continue;
|
|
2902
|
+
|
|
2903
|
+
const safari = trimmed.match(/^(.*)@(.*?):(\d+):(\d+)$/);
|
|
2904
|
+
if (safari) {
|
|
2905
|
+
frames.push({
|
|
2906
|
+
fn: safari[1] || "",
|
|
2907
|
+
file: safari[2],
|
|
2908
|
+
line: +safari[3],
|
|
2909
|
+
col: +safari[4],
|
|
2910
|
+
});
|
|
2911
|
+
continue;
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
const chrome = trimmed.match(
|
|
2915
|
+
/^at\s+(?:(.+?)\s+\()?(.*?):(\d+):(\d+)\)?$/,
|
|
2916
|
+
);
|
|
2917
|
+
if (chrome) {
|
|
2918
|
+
frames.push({
|
|
2919
|
+
fn: chrome[1] || "",
|
|
2920
|
+
file: chrome[2],
|
|
2921
|
+
line: +chrome[3],
|
|
2922
|
+
col: +chrome[4],
|
|
2923
|
+
});
|
|
2924
|
+
continue;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
const chromePlain = trimmed.match(/^at\s+(?:(.+?)\s+\()?(.*?)\)?$/);
|
|
2928
|
+
if (chromePlain) {
|
|
2929
|
+
frames.push({
|
|
2930
|
+
fn: chromePlain[1] || "",
|
|
2931
|
+
file: chromePlain[2] || "<anonymous>",
|
|
2932
|
+
line: 0,
|
|
2933
|
+
col: 0,
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return frames;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
function makeCallSite(f: {
|
|
2941
|
+
fn: string;
|
|
2942
|
+
file: string;
|
|
2943
|
+
line: number;
|
|
2944
|
+
col: number;
|
|
2945
|
+
}) {
|
|
2946
|
+
return {
|
|
2947
|
+
getFileName: () => f.file || null,
|
|
2948
|
+
getLineNumber: () => f.line || null,
|
|
2949
|
+
getColumnNumber: () => f.col || null,
|
|
2950
|
+
getFunctionName: () => f.fn || null,
|
|
2951
|
+
getMethodName: () => f.fn || null,
|
|
2952
|
+
getTypeName: () => null,
|
|
2953
|
+
getThis: () => undefined,
|
|
2954
|
+
getFunction: () => undefined,
|
|
2955
|
+
getEvalOrigin: () => undefined,
|
|
2956
|
+
isNative: () => false,
|
|
2957
|
+
isConstructor: () => false,
|
|
2958
|
+
isToplevel: () => !f.fn,
|
|
2959
|
+
isEval: () => false,
|
|
2960
|
+
toString: () =>
|
|
2961
|
+
f.fn
|
|
2962
|
+
? `${f.fn} (${f.file}:${f.line}:${f.col})`
|
|
2963
|
+
: `${f.file}:${f.line}:${f.col}`,
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
function buildSites(stack: string, ctorOpt?: Function) {
|
|
2968
|
+
const frames = parseFrames(stack);
|
|
2969
|
+
let start = 0;
|
|
2970
|
+
if (ctorOpt?.name) {
|
|
2971
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2972
|
+
if (frames[i].fn === ctorOpt.name) {
|
|
2973
|
+
start = i + 1;
|
|
2974
|
+
break;
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
return frames.slice(start).map(makeCallSite);
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
const sym = Symbol("rawStack");
|
|
2982
|
+
const symProcessed = Symbol("stackProcessed");
|
|
2983
|
+
|
|
2984
|
+
const nativeCapture = (Error as any).captureStackTrace;
|
|
2985
|
+
|
|
2986
|
+
Object.defineProperty(Error.prototype, "stack", {
|
|
2987
|
+
get() {
|
|
2988
|
+
if ((this as any)[symProcessed]) return (this as any)[sym];
|
|
2989
|
+
const raw = (this as any)[sym];
|
|
2990
|
+
if (
|
|
2991
|
+
typeof raw === "string" &&
|
|
2992
|
+
typeof (Error as any).prepareStackTrace === "function"
|
|
2993
|
+
) {
|
|
2994
|
+
try {
|
|
2995
|
+
const sites = buildSites(raw);
|
|
2996
|
+
if (sites.length > 0) {
|
|
2997
|
+
return (Error as any).prepareStackTrace(this, sites);
|
|
2998
|
+
}
|
|
2999
|
+
} catch {
|
|
3000
|
+
return raw;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return raw;
|
|
3004
|
+
},
|
|
3005
|
+
set(val: any) {
|
|
3006
|
+
(this as any)[sym] = val;
|
|
3007
|
+
},
|
|
3008
|
+
configurable: true,
|
|
3009
|
+
enumerable: false,
|
|
3010
|
+
});
|
|
3011
|
+
|
|
3012
|
+
(Error as any).captureStackTrace = function (
|
|
3013
|
+
target: any,
|
|
3014
|
+
ctorOpt?: Function,
|
|
3015
|
+
) {
|
|
3016
|
+
const saved = (Error as any).prepareStackTrace;
|
|
3017
|
+
(Error as any).prepareStackTrace = undefined;
|
|
3018
|
+
|
|
3019
|
+
let raw: string;
|
|
3020
|
+
if (nativeCapture) {
|
|
3021
|
+
const tmp = {} as any;
|
|
3022
|
+
nativeCapture(tmp);
|
|
3023
|
+
raw = tmp.stack || "";
|
|
3024
|
+
} else {
|
|
3025
|
+
raw = new Error().stack || "";
|
|
3026
|
+
}
|
|
3027
|
+
(Error as any).prepareStackTrace = saved;
|
|
3028
|
+
|
|
3029
|
+
if (typeof saved === "function") {
|
|
3030
|
+
try {
|
|
3031
|
+
const result = saved(target, buildSites(raw, ctorOpt));
|
|
3032
|
+
(target as any)[symProcessed] = true;
|
|
3033
|
+
target.stack = result;
|
|
3034
|
+
} catch {
|
|
3035
|
+
target.stack = raw;
|
|
3036
|
+
}
|
|
3037
|
+
} else {
|
|
3038
|
+
target.stack = raw;
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
execute(
|
|
3044
|
+
code: string,
|
|
3045
|
+
filename: string = "/index.js",
|
|
3046
|
+
): { exports: unknown; module: ModuleRecord } {
|
|
3047
|
+
const dir = pathPolyfill.dirname(filename);
|
|
3048
|
+
this.vol.writeFileSync(filename, code);
|
|
3049
|
+
|
|
3050
|
+
const mod: ModuleRecord = {
|
|
3051
|
+
id: filename,
|
|
3052
|
+
filename,
|
|
3053
|
+
exports: {},
|
|
3054
|
+
loaded: false,
|
|
3055
|
+
children: [],
|
|
3056
|
+
paths: [],
|
|
3057
|
+
};
|
|
3058
|
+
this.moduleRegistry[filename] = mod;
|
|
3059
|
+
|
|
3060
|
+
const consoleProxy = wrapConsole(this.opts.onConsole);
|
|
3061
|
+
|
|
3062
|
+
let processed = code;
|
|
3063
|
+
if (processed.startsWith("#!"))
|
|
3064
|
+
processed = processed.slice(processed.indexOf("\n") + 1);
|
|
3065
|
+
if (isTypeScriptFile(filename)) {
|
|
3066
|
+
processed = stripTypeScript(processed);
|
|
3067
|
+
}
|
|
3068
|
+
if (filename.endsWith(".cjs")) {
|
|
3069
|
+
try {
|
|
3070
|
+
const cjsAst = acorn.parse(processed, {
|
|
3071
|
+
ecmaVersion: "latest",
|
|
3072
|
+
sourceType: "script",
|
|
3073
|
+
allowImportExportEverywhere: true,
|
|
3074
|
+
});
|
|
3075
|
+
const cjsPatches: Array<[number, number, string]> = [];
|
|
3076
|
+
const walkCjs = (node: any) => {
|
|
3077
|
+
if (!node || typeof node !== "object") return;
|
|
3078
|
+
if (Array.isArray(node)) { for (const c of node) walkCjs(c); return; }
|
|
3079
|
+
if (typeof node.type !== "string") return;
|
|
3080
|
+
if (node.type === "ImportExpression") {
|
|
3081
|
+
cjsPatches.push([node.start, node.start + 6, "__asyncLoad"]);
|
|
3082
|
+
}
|
|
3083
|
+
if (node.type === "MetaProperty" && node.meta?.name === "import" && node.property?.name === "meta") {
|
|
3084
|
+
cjsPatches.push([
|
|
3085
|
+
node.start,
|
|
3086
|
+
node.end,
|
|
3087
|
+
`({ url: "file://${filename}", dirname: "${pathPolyfill.dirname(filename)}", filename: "${filename}" })`,
|
|
3088
|
+
]);
|
|
3089
|
+
}
|
|
3090
|
+
for (const key of Object.keys(node)) {
|
|
3091
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
3092
|
+
const val = node[key];
|
|
3093
|
+
if (val && typeof val === "object") walkCjs(val);
|
|
3094
|
+
}
|
|
3095
|
+
};
|
|
3096
|
+
walkCjs(cjsAst);
|
|
3097
|
+
if (cjsPatches.length > 0) {
|
|
3098
|
+
cjsPatches.sort((a, b) => b[0] - a[0]);
|
|
3099
|
+
for (const [start, end, replacement] of cjsPatches) {
|
|
3100
|
+
processed = processed.slice(0, start) + replacement + processed.slice(end);
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
} catch { /* can't parse */ }
|
|
3104
|
+
} else {
|
|
3105
|
+
processed = convertModuleSyntax(processed, filename);
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
const fileHasTLA = filename.endsWith(".cjs") ? false : hasTopLevelAwait(processed);
|
|
3109
|
+
const resolver = buildResolver(
|
|
3110
|
+
this.vol,
|
|
3111
|
+
this.fsBridge,
|
|
3112
|
+
this.proc,
|
|
3113
|
+
dir,
|
|
3114
|
+
this.moduleRegistry,
|
|
3115
|
+
this.opts,
|
|
3116
|
+
this.transformCache,
|
|
3117
|
+
fileHasTLA,
|
|
3118
|
+
);
|
|
3119
|
+
|
|
3120
|
+
try {
|
|
3121
|
+
const metaUrl = "file://" + filename;
|
|
3122
|
+
if (!filename.endsWith(".cjs")) {
|
|
3123
|
+
processed = stripTopLevelAwait(
|
|
3124
|
+
processed,
|
|
3125
|
+
fileHasTLA ? "full" : "topLevelOnly",
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3128
|
+
const wrapper = buildModuleWrapper(processed);
|
|
3129
|
+
|
|
3130
|
+
const asyncLoader = makeDynamicLoader(resolver);
|
|
3131
|
+
let fn;
|
|
3132
|
+
try {
|
|
3133
|
+
fn = (0, eval)(wrapper);
|
|
3134
|
+
} catch (syntaxErr) {
|
|
3135
|
+
throw syntaxErr;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
fn(
|
|
3139
|
+
mod.exports,
|
|
3140
|
+
resolver,
|
|
3141
|
+
mod,
|
|
3142
|
+
filename,
|
|
3143
|
+
dir,
|
|
3144
|
+
this.proc,
|
|
3145
|
+
consoleProxy,
|
|
3146
|
+
{ url: metaUrl, dirname: dir, filename },
|
|
3147
|
+
asyncLoader,
|
|
3148
|
+
syncAwait,
|
|
3149
|
+
SyncPromiseClass,
|
|
3150
|
+
);
|
|
3151
|
+
|
|
3152
|
+
mod.loaded = true;
|
|
3153
|
+
} catch (err) {
|
|
3154
|
+
delete this.moduleRegistry[filename];
|
|
3155
|
+
throw err;
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
return { exports: mod.exports, module: mod };
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
executeSync = this.execute;
|
|
3162
|
+
|
|
3163
|
+
async executeAsync(
|
|
3164
|
+
code: string,
|
|
3165
|
+
filename: string = "/index.js",
|
|
3166
|
+
): Promise<ExecutionOutcome> {
|
|
3167
|
+
return Promise.resolve(this.execute(code, filename));
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
runFile(filename: string): { exports: unknown; module: ModuleRecord } {
|
|
3171
|
+
const source = this.vol.readFileSync(filename, "utf8");
|
|
3172
|
+
return this.execute(source, filename);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
runFileSync = this.runFile;
|
|
3176
|
+
|
|
3177
|
+
// Wraps in async IIFE when TLA is detected, falls back to sync otherwise
|
|
3178
|
+
async runFileTLA(
|
|
3179
|
+
filename: string,
|
|
3180
|
+
): Promise<{ exports: unknown; module: ModuleRecord }> {
|
|
3181
|
+
const source = this.vol.readFileSync(filename, "utf8");
|
|
3182
|
+
const dir = pathPolyfill.dirname(filename);
|
|
3183
|
+
this.vol.writeFileSync(filename, source);
|
|
3184
|
+
|
|
3185
|
+
const mod: ModuleRecord = {
|
|
3186
|
+
id: filename,
|
|
3187
|
+
filename,
|
|
3188
|
+
exports: {},
|
|
3189
|
+
loaded: false,
|
|
3190
|
+
children: [],
|
|
3191
|
+
paths: [],
|
|
3192
|
+
};
|
|
3193
|
+
this.moduleRegistry[filename] = mod;
|
|
3194
|
+
|
|
3195
|
+
const consoleProxy = wrapConsole(this.opts.onConsole);
|
|
3196
|
+
|
|
3197
|
+
let processed = source as string;
|
|
3198
|
+
if (processed.startsWith("#!"))
|
|
3199
|
+
processed = processed.slice(processed.indexOf("\n") + 1);
|
|
3200
|
+
if (isTypeScriptFile(filename)) {
|
|
3201
|
+
processed = stripTypeScript(processed);
|
|
3202
|
+
}
|
|
3203
|
+
if (filename.endsWith(".cjs")) {
|
|
3204
|
+
processed = rewriteDynamicImportsRegex(processed);
|
|
3205
|
+
processed = processed.replace(
|
|
3206
|
+
/\bimport\.meta\.url\b/g,
|
|
3207
|
+
`"file://${filename}"`,
|
|
3208
|
+
);
|
|
3209
|
+
processed = processed.replace(
|
|
3210
|
+
/\bimport\.meta\b/g,
|
|
3211
|
+
`({ url: "file://${filename}" })`,
|
|
3212
|
+
);
|
|
3213
|
+
} else {
|
|
3214
|
+
processed = convertModuleSyntax(processed, filename);
|
|
3215
|
+
}
|
|
3216
|
+
const tla = hasTopLevelAwait(processed);
|
|
3217
|
+
|
|
3218
|
+
// Don't propagate deAsyncImports from entry — it uses native await (async IIFE),
|
|
3219
|
+
// so deps don't need de-async. loadModule handles individual TLA modules.
|
|
3220
|
+
const resolver = buildResolver(
|
|
3221
|
+
this.vol,
|
|
3222
|
+
this.fsBridge,
|
|
3223
|
+
this.proc,
|
|
3224
|
+
dir,
|
|
3225
|
+
this.moduleRegistry,
|
|
3226
|
+
this.opts,
|
|
3227
|
+
this.transformCache,
|
|
3228
|
+
false,
|
|
3229
|
+
);
|
|
3230
|
+
|
|
3231
|
+
if (!tla) {
|
|
3232
|
+
try {
|
|
3233
|
+
processed = stripTopLevelAwait(processed);
|
|
3234
|
+
const wrapper = buildModuleWrapper(processed);
|
|
3235
|
+
const asyncLoader = makeDynamicLoader(resolver);
|
|
3236
|
+
const fn = (0, eval)(wrapper);
|
|
3237
|
+
|
|
3238
|
+
fn(
|
|
3239
|
+
mod.exports,
|
|
3240
|
+
resolver,
|
|
3241
|
+
mod,
|
|
3242
|
+
filename,
|
|
3243
|
+
dir,
|
|
3244
|
+
this.proc,
|
|
3245
|
+
consoleProxy,
|
|
3246
|
+
{ url: "file://" + filename, dirname: dir, filename },
|
|
3247
|
+
asyncLoader,
|
|
3248
|
+
syncAwait,
|
|
3249
|
+
SyncPromiseClass,
|
|
3250
|
+
);
|
|
3251
|
+
mod.loaded = true;
|
|
3252
|
+
} catch (err) {
|
|
3253
|
+
delete this.moduleRegistry[filename];
|
|
3254
|
+
throw err;
|
|
3255
|
+
}
|
|
3256
|
+
return { exports: mod.exports, module: mod };
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
try {
|
|
3260
|
+
const metaUrl = "file://" + filename;
|
|
3261
|
+
const wrapper = buildModuleWrapper(processed, { async: true });
|
|
3262
|
+
const asyncLoader = makeDynamicLoader(resolver);
|
|
3263
|
+
const fn = (0, eval)(wrapper);
|
|
3264
|
+
await fn(
|
|
3265
|
+
mod.exports,
|
|
3266
|
+
resolver,
|
|
3267
|
+
mod,
|
|
3268
|
+
filename,
|
|
3269
|
+
dir,
|
|
3270
|
+
this.proc,
|
|
3271
|
+
consoleProxy,
|
|
3272
|
+
{ url: metaUrl, dirname: dir, filename },
|
|
3273
|
+
asyncLoader,
|
|
3274
|
+
syncAwait,
|
|
3275
|
+
SyncPromiseClass,
|
|
3276
|
+
);
|
|
3277
|
+
mod.loaded = true;
|
|
3278
|
+
} catch (err) {
|
|
3279
|
+
delete this.moduleRegistry[filename];
|
|
3280
|
+
throw err;
|
|
3281
|
+
}
|
|
3282
|
+
return { exports: mod.exports, module: mod };
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
async runFileAsync(filename: string): Promise<ExecutionOutcome> {
|
|
3286
|
+
return Promise.resolve(this.runFile(filename));
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
clearCache(): void {
|
|
3290
|
+
for (const k of Object.keys(this.moduleRegistry))
|
|
3291
|
+
delete this.moduleRegistry[k];
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
getVolume(): MemoryVolume {
|
|
3295
|
+
return this.vol;
|
|
3296
|
+
}
|
|
3297
|
+
getProcess(): ProcessObject {
|
|
3298
|
+
return this.proc;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
createREPL(): { eval: (code: string) => unknown } {
|
|
3302
|
+
const resolver = buildResolver(
|
|
3303
|
+
this.vol,
|
|
3304
|
+
this.fsBridge,
|
|
3305
|
+
this.proc,
|
|
3306
|
+
"/",
|
|
3307
|
+
this.moduleRegistry,
|
|
3308
|
+
this.opts,
|
|
3309
|
+
this.transformCache,
|
|
3310
|
+
);
|
|
3311
|
+
const consoleProxy = wrapConsole(this.opts.onConsole);
|
|
3312
|
+
const proc = this.proc;
|
|
3313
|
+
const buf = bufferPolyfill.Buffer;
|
|
3314
|
+
|
|
3315
|
+
const GenFn = Object.getPrototypeOf(function* () {}).constructor;
|
|
3316
|
+
const gen = new GenFn(
|
|
3317
|
+
"require",
|
|
3318
|
+
"console",
|
|
3319
|
+
"process",
|
|
3320
|
+
"Buffer",
|
|
3321
|
+
`var __code, __result;
|
|
3322
|
+
while (true) {
|
|
3323
|
+
__code = yield;
|
|
3324
|
+
try {
|
|
3325
|
+
__result = eval(__code);
|
|
3326
|
+
yield { value: __result, error: null };
|
|
3327
|
+
} catch (e) {
|
|
3328
|
+
yield { value: undefined, error: e };
|
|
3329
|
+
}
|
|
3330
|
+
}`,
|
|
3331
|
+
)(resolver, consoleProxy, proc, buf);
|
|
3332
|
+
gen.next();
|
|
3333
|
+
|
|
3334
|
+
return {
|
|
3335
|
+
eval(code: string): unknown {
|
|
3336
|
+
const normalized = code.replace(/^\s*(const|let)\s+/gm, "var ");
|
|
3337
|
+
const exprResult = gen.next("(" + normalized + ")").value as {
|
|
3338
|
+
value: unknown;
|
|
3339
|
+
error: unknown;
|
|
3340
|
+
};
|
|
3341
|
+
if (!exprResult.error) {
|
|
3342
|
+
gen.next();
|
|
3343
|
+
return exprResult.value;
|
|
3344
|
+
}
|
|
3345
|
+
gen.next();
|
|
3346
|
+
const stmtResult = gen.next(normalized).value as {
|
|
3347
|
+
value: unknown;
|
|
3348
|
+
error: unknown;
|
|
3349
|
+
};
|
|
3350
|
+
if (stmtResult.error) {
|
|
3351
|
+
gen.next();
|
|
3352
|
+
throw stmtResult.error;
|
|
3353
|
+
}
|
|
3354
|
+
gen.next();
|
|
3355
|
+
return stmtResult.value;
|
|
3356
|
+
},
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
export function executeCode(
|
|
3362
|
+
code: string,
|
|
3363
|
+
vol: MemoryVolume,
|
|
3364
|
+
opts?: EngineOptions,
|
|
3365
|
+
): { exports: unknown; module: ModuleRecord } {
|
|
3366
|
+
const engine = new ScriptEngine(vol, opts);
|
|
3367
|
+
return engine.execute(code);
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
export type {
|
|
3371
|
+
IScriptEngine,
|
|
3372
|
+
ExecutionOutcome,
|
|
3373
|
+
EngineConfig,
|
|
3374
|
+
} from "./engine-types";
|
|
3375
|
+
export default ScriptEngine;
|