@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.
Files changed (134) hide show
  1. package/LICENSE +43 -0
  2. package/README.md +240 -0
  3. package/dist/child_process-BJOMsZje.js +8233 -0
  4. package/dist/child_process-BJOMsZje.js.map +1 -0
  5. package/dist/child_process-Cj8vOcuc.cjs +7434 -0
  6. package/dist/child_process-Cj8vOcuc.cjs.map +1 -0
  7. package/dist/index-Cb1Cgdnd.js +35308 -0
  8. package/dist/index-Cb1Cgdnd.js.map +1 -0
  9. package/dist/index-DsMGS-xc.cjs +37195 -0
  10. package/dist/index-DsMGS-xc.cjs.map +1 -0
  11. package/dist/index.cjs +65 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.mjs +59 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +95 -0
  16. package/src/__tests__/smoke.test.ts +11 -0
  17. package/src/constants/cdn-urls.ts +18 -0
  18. package/src/constants/config.ts +236 -0
  19. package/src/cross-origin.ts +26 -0
  20. package/src/engine-factory.ts +176 -0
  21. package/src/engine-types.ts +56 -0
  22. package/src/helpers/byte-encoding.ts +39 -0
  23. package/src/helpers/digest.ts +9 -0
  24. package/src/helpers/event-loop.ts +96 -0
  25. package/src/helpers/wasm-cache.ts +133 -0
  26. package/src/iframe-sandbox.ts +141 -0
  27. package/src/index.ts +192 -0
  28. package/src/isolation-helpers.ts +148 -0
  29. package/src/memory-volume.ts +941 -0
  30. package/src/module-transformer.ts +368 -0
  31. package/src/packages/archive-extractor.ts +248 -0
  32. package/src/packages/browser-bundler.ts +284 -0
  33. package/src/packages/installer.ts +396 -0
  34. package/src/packages/registry-client.ts +131 -0
  35. package/src/packages/version-resolver.ts +411 -0
  36. package/src/polyfills/assert.ts +384 -0
  37. package/src/polyfills/async_hooks.ts +144 -0
  38. package/src/polyfills/buffer.ts +628 -0
  39. package/src/polyfills/child_process.ts +2288 -0
  40. package/src/polyfills/chokidar.ts +336 -0
  41. package/src/polyfills/cluster.ts +106 -0
  42. package/src/polyfills/console.ts +136 -0
  43. package/src/polyfills/constants.ts +123 -0
  44. package/src/polyfills/crypto.ts +885 -0
  45. package/src/polyfills/dgram.ts +87 -0
  46. package/src/polyfills/diagnostics_channel.ts +76 -0
  47. package/src/polyfills/dns.ts +134 -0
  48. package/src/polyfills/domain.ts +68 -0
  49. package/src/polyfills/esbuild.ts +854 -0
  50. package/src/polyfills/events.ts +276 -0
  51. package/src/polyfills/fs.ts +2888 -0
  52. package/src/polyfills/fsevents.ts +79 -0
  53. package/src/polyfills/http.ts +1449 -0
  54. package/src/polyfills/http2.ts +199 -0
  55. package/src/polyfills/https.ts +76 -0
  56. package/src/polyfills/inspector.ts +62 -0
  57. package/src/polyfills/lightningcss.ts +105 -0
  58. package/src/polyfills/module.ts +191 -0
  59. package/src/polyfills/net.ts +353 -0
  60. package/src/polyfills/os.ts +238 -0
  61. package/src/polyfills/path.ts +206 -0
  62. package/src/polyfills/perf_hooks.ts +102 -0
  63. package/src/polyfills/process.ts +690 -0
  64. package/src/polyfills/punycode.ts +159 -0
  65. package/src/polyfills/querystring.ts +93 -0
  66. package/src/polyfills/quic.ts +118 -0
  67. package/src/polyfills/readdirp.ts +229 -0
  68. package/src/polyfills/readline.ts +692 -0
  69. package/src/polyfills/repl.ts +134 -0
  70. package/src/polyfills/rollup.ts +119 -0
  71. package/src/polyfills/sea.ts +33 -0
  72. package/src/polyfills/sqlite.ts +78 -0
  73. package/src/polyfills/stream.ts +1620 -0
  74. package/src/polyfills/string_decoder.ts +25 -0
  75. package/src/polyfills/tailwindcss-oxide.ts +309 -0
  76. package/src/polyfills/test.ts +197 -0
  77. package/src/polyfills/timers.ts +32 -0
  78. package/src/polyfills/tls.ts +105 -0
  79. package/src/polyfills/trace_events.ts +50 -0
  80. package/src/polyfills/tty.ts +71 -0
  81. package/src/polyfills/url.ts +174 -0
  82. package/src/polyfills/util.ts +559 -0
  83. package/src/polyfills/v8.ts +126 -0
  84. package/src/polyfills/vm.ts +132 -0
  85. package/src/polyfills/volume-registry.ts +15 -0
  86. package/src/polyfills/wasi.ts +44 -0
  87. package/src/polyfills/worker_threads.ts +326 -0
  88. package/src/polyfills/ws.ts +595 -0
  89. package/src/polyfills/zlib.ts +881 -0
  90. package/src/request-proxy.ts +716 -0
  91. package/src/script-engine.ts +3375 -0
  92. package/src/sdk/nodepod-fs.ts +93 -0
  93. package/src/sdk/nodepod-process.ts +86 -0
  94. package/src/sdk/nodepod-terminal.ts +350 -0
  95. package/src/sdk/nodepod.ts +509 -0
  96. package/src/sdk/types.ts +70 -0
  97. package/src/shell/commands/bun.ts +121 -0
  98. package/src/shell/commands/directory.ts +297 -0
  99. package/src/shell/commands/file-ops.ts +525 -0
  100. package/src/shell/commands/git.ts +2142 -0
  101. package/src/shell/commands/node.ts +80 -0
  102. package/src/shell/commands/npm.ts +198 -0
  103. package/src/shell/commands/pm-types.ts +45 -0
  104. package/src/shell/commands/pnpm.ts +82 -0
  105. package/src/shell/commands/search.ts +264 -0
  106. package/src/shell/commands/shell-env.ts +352 -0
  107. package/src/shell/commands/text-processing.ts +1152 -0
  108. package/src/shell/commands/yarn.ts +84 -0
  109. package/src/shell/shell-builtins.ts +19 -0
  110. package/src/shell/shell-helpers.ts +250 -0
  111. package/src/shell/shell-interpreter.ts +514 -0
  112. package/src/shell/shell-parser.ts +429 -0
  113. package/src/shell/shell-types.ts +85 -0
  114. package/src/syntax-transforms.ts +561 -0
  115. package/src/threading/engine-worker.ts +64 -0
  116. package/src/threading/inline-worker.ts +372 -0
  117. package/src/threading/offload-types.ts +112 -0
  118. package/src/threading/offload-worker.ts +383 -0
  119. package/src/threading/offload.ts +271 -0
  120. package/src/threading/process-context.ts +92 -0
  121. package/src/threading/process-handle.ts +275 -0
  122. package/src/threading/process-manager.ts +956 -0
  123. package/src/threading/process-worker-entry.ts +854 -0
  124. package/src/threading/shared-vfs.ts +352 -0
  125. package/src/threading/sync-channel.ts +135 -0
  126. package/src/threading/task-queue.ts +177 -0
  127. package/src/threading/vfs-bridge.ts +231 -0
  128. package/src/threading/worker-pool.ts +233 -0
  129. package/src/threading/worker-protocol.ts +358 -0
  130. package/src/threading/worker-vfs.ts +218 -0
  131. package/src/types/externals.d.ts +38 -0
  132. package/src/types/fs-streams.ts +142 -0
  133. package/src/types/manifest.ts +17 -0
  134. package/src/worker-sandbox.ts +90 -0
@@ -0,0 +1,854 @@
1
+ // esbuild polyfill -- loads esbuild-wasm from CDN, routes file I/O through MemoryVolume
2
+
3
+ import type { MemoryVolume } from "../memory-volume";
4
+ import { CDN_ESBUILD_BINARY, CDN_ESBUILD_BROWSER, cdnImport } from "../constants/cdn-urls";
5
+ import { stripTopLevelAwait } from "../syntax-transforms";
6
+ import { ESBUILD_LOADER_MAP, RESOLVE_EXTENSIONS } from "../constants/config";
7
+ import { ref, unref } from "../helpers/event-loop";
8
+
9
+ const BUILTIN_MODULES = new Set([
10
+ "assert",
11
+ "async_hooks",
12
+ "buffer",
13
+ "child_process",
14
+ "cluster",
15
+ "console",
16
+ "constants",
17
+ "crypto",
18
+ "dgram",
19
+ "dns",
20
+ "domain",
21
+ "events",
22
+ "fs",
23
+ "http",
24
+ "http2",
25
+ "https",
26
+ "inspector",
27
+ "module",
28
+ "net",
29
+ "os",
30
+ "path",
31
+ "perf_hooks",
32
+ "process",
33
+ "punycode",
34
+ "querystring",
35
+ "readline",
36
+ "stream",
37
+ "string_decoder",
38
+ "sys",
39
+ "timers",
40
+ "tls",
41
+ "tty",
42
+ "url",
43
+ "util",
44
+ "v8",
45
+ "vm",
46
+ "worker_threads",
47
+ "zlib",
48
+ ]);
49
+
50
+ type ExportValue = string | ExportMap;
51
+ interface ExportMap {
52
+ [key: string]: ExportValue;
53
+ }
54
+
55
+ interface PackageJson {
56
+ exports?: ExportMap | ExportValue;
57
+ imports?: ExportMap;
58
+ module?: string;
59
+ main?: string;
60
+ }
61
+
62
+ interface ModuleHit {
63
+ resolvedPath: string;
64
+ fromVolume: boolean;
65
+ }
66
+
67
+ export interface TransformConfig {
68
+ loader?: "js" | "jsx" | "ts" | "tsx" | "json" | "css";
69
+ format?: "iife" | "cjs" | "esm";
70
+ target?: string | string[];
71
+ minify?: boolean;
72
+ sourcemap?: boolean | "inline" | "external";
73
+ jsx?: "transform" | "preserve";
74
+ jsxFactory?: string;
75
+ jsxFragment?: string;
76
+ }
77
+
78
+ export interface TransformOutput {
79
+ code: string;
80
+ map: string;
81
+ warnings: unknown[];
82
+ }
83
+
84
+ export interface BundleConfig {
85
+ entryPoints?: string[];
86
+ stdin?: {
87
+ contents: string;
88
+ resolveDir?: string;
89
+ loader?: "js" | "jsx" | "ts" | "tsx" | "json" | "css";
90
+ };
91
+ bundle?: boolean;
92
+ outdir?: string;
93
+ outfile?: string;
94
+ format?: "iife" | "cjs" | "esm";
95
+ platform?: "browser" | "node" | "neutral";
96
+ target?: string | string[];
97
+ minify?: boolean;
98
+ sourcemap?: boolean | "inline" | "external";
99
+ external?: string[];
100
+ write?: boolean;
101
+ plugins?: unknown[];
102
+ absWorkingDir?: string;
103
+ conditions?: string[];
104
+ mainFields?: string[];
105
+ }
106
+
107
+ export interface BundleOutput {
108
+ errors: unknown[];
109
+ warnings: unknown[];
110
+ outputFiles?: Array<{ path: string; contents: Uint8Array; text: string }>;
111
+ metafile?: {
112
+ inputs?: Record<string, unknown>;
113
+ outputs?: Record<string, unknown>;
114
+ };
115
+ }
116
+
117
+ let engine: typeof import("esbuild-wasm") | null = null;
118
+ let bootPromise: Promise<void> | null = null;
119
+ let wasmBinaryUrl: string = CDN_ESBUILD_BINARY;
120
+ let volumeRef: MemoryVolume | null = null;
121
+
122
+ export function setVolume(vol: MemoryVolume): void {
123
+ volumeRef = vol;
124
+ }
125
+
126
+ export function setWasmUrl(url: string): void {
127
+ wasmBinaryUrl = url;
128
+ }
129
+
130
+ export async function initialize(opts?: { wasmURL?: string }): Promise<void> {
131
+ if (engine) return;
132
+
133
+ if (
134
+ typeof window !== "undefined" &&
135
+ (window as unknown as Record<string, unknown>).__esbuild
136
+ ) {
137
+ engine = (window as unknown as Record<string, unknown>)
138
+ .__esbuild as typeof import("esbuild-wasm");
139
+ return;
140
+ }
141
+
142
+ if (
143
+ typeof window !== "undefined" &&
144
+ (window as unknown as Record<string, unknown>).__esbuildInitPromise
145
+ ) {
146
+ await (window as unknown as Record<string, unknown>).__esbuildInitPromise;
147
+ if ((window as unknown as Record<string, unknown>).__esbuild) {
148
+ engine = (window as unknown as Record<string, unknown>)
149
+ .__esbuild as typeof import("esbuild-wasm");
150
+ return;
151
+ }
152
+ }
153
+
154
+ if (bootPromise) return bootPromise;
155
+
156
+ bootPromise = (async () => {
157
+ try {
158
+ const mod = await cdnImport(CDN_ESBUILD_BROWSER);
159
+ await mod.initialize({ wasmURL: opts?.wasmURL || wasmBinaryUrl });
160
+ engine = mod;
161
+ } catch (err) {
162
+ bootPromise = null;
163
+ throw new Error(`esbuild: initialization failed -- ${err}`);
164
+ }
165
+ })();
166
+
167
+ return bootPromise;
168
+ }
169
+
170
+ export async function transform(
171
+ source: string,
172
+ cfg?: TransformConfig,
173
+ ): Promise<TransformOutput> {
174
+ ref();
175
+ try {
176
+ if (!engine) await initialize();
177
+ if (!engine) throw new Error("esbuild: engine not ready");
178
+ return await engine.transform(source, cfg);
179
+ } finally {
180
+ unref();
181
+ }
182
+ }
183
+
184
+ export async function build(cfg: BundleConfig): Promise<BundleOutput> {
185
+ if (!engine) await initialize();
186
+ if (!engine) throw new Error("esbuild: engine not ready");
187
+
188
+ // keep event loop alive while building (Vite's dep optimizer is async)
189
+ ref();
190
+ try {
191
+ const volumePlugin = createVolumePlugin(cfg.external, cfg.platform, cfg.conditions);
192
+ const allPlugins = [...(cfg.plugins || [])];
193
+ // volume plugin goes last so other plugins' onLoad handlers run first
194
+ if (volumePlugin) allPlugins.push(volumePlugin);
195
+
196
+ let entries = cfg.entryPoints;
197
+ if (entries && volumeRef) {
198
+ const base = cfg.absWorkingDir || resolveWorkingDir();
199
+ entries = entries.map((ep) => toAbsolute(ep, base));
200
+ }
201
+
202
+ const workDir = cfg.absWorkingDir || resolveWorkingDir();
203
+
204
+ // always get outputFiles in memory, then write to VFS if requested
205
+ const shouldWrite = cfg.write !== false;
206
+
207
+ const raw = (await engine.build({
208
+ ...cfg,
209
+ entryPoints: entries,
210
+ plugins: allPlugins,
211
+ write: false,
212
+ absWorkingDir: workDir,
213
+ })) as BundleOutput;
214
+
215
+ stripNamespacePrefixes(raw);
216
+
217
+ if (shouldWrite && raw.outputFiles && volumeRef) {
218
+ for (const f of raw.outputFiles) {
219
+ const outPath = f.path.startsWith("/") ? f.path : workDir + "/" + f.path;
220
+ const dir = outPath.substring(0, outPath.lastIndexOf("/"));
221
+ if (dir && !volumeRef.existsSync(dir)) {
222
+ volumeRef.mkdirSync(dir, { recursive: true });
223
+ }
224
+ volumeRef.writeFileSync(outPath, f.text);
225
+ }
226
+ }
227
+
228
+ return raw;
229
+ } finally {
230
+ unref();
231
+ }
232
+ }
233
+
234
+ export function formatMessages(
235
+ messages: unknown[],
236
+ opts?: { kind?: "error" | "warning"; color?: boolean },
237
+ ): Promise<string[]> {
238
+ if (!engine) throw new Error("esbuild: engine not ready");
239
+ return (
240
+ engine as unknown as {
241
+ formatMessages: (m: unknown[], o?: unknown) => Promise<string[]>;
242
+ }
243
+ ).formatMessages(messages, opts);
244
+ }
245
+
246
+ export const version = "0.21.5";
247
+
248
+ // build context for incremental builds (used by Vite)
249
+ export async function context(cfg: BundleConfig): Promise<{
250
+ rebuild: () => Promise<BundleOutput>;
251
+ watch: (opts?: unknown) => Promise<void>;
252
+ serve: (opts?: unknown) => Promise<{ host: string; port: number }>;
253
+ cancel: () => Promise<void>;
254
+ dispose: () => Promise<void>;
255
+ }> {
256
+ ref();
257
+ try {
258
+ if (!engine) await initialize();
259
+ } finally {
260
+ unref();
261
+ }
262
+
263
+ let disposed = false;
264
+ const ctx = {
265
+ async rebuild(): Promise<BundleOutput> {
266
+ if (disposed) throw new Error("Build context already disposed");
267
+ return build(cfg);
268
+ },
269
+ async watch(_opts?: unknown): Promise<void> {},
270
+ async serve(_opts?: unknown): Promise<{ host: string; port: number }> {
271
+ return { host: "localhost", port: 0 };
272
+ },
273
+ async cancel(): Promise<void> {},
274
+ async dispose(): Promise<void> {
275
+ disposed = true;
276
+ },
277
+ };
278
+ return ctx;
279
+ }
280
+
281
+ export function stop(): void {}
282
+
283
+ export async function analyzeMetafile(
284
+ metafile:
285
+ | string
286
+ | { inputs?: Record<string, unknown>; outputs?: Record<string, unknown> },
287
+ _opts?: { verbose?: boolean; color?: boolean },
288
+ ): Promise<string> {
289
+ const meta = typeof metafile === "string" ? JSON.parse(metafile) : metafile;
290
+ const outputs = meta?.outputs ?? {};
291
+ const lines: string[] = [];
292
+ for (const [name, info] of Object.entries(outputs)) {
293
+ const bytes = (info as any)?.bytes ?? 0;
294
+ lines.push(` ${name} ${(bytes / 1024).toFixed(1)}kb`);
295
+ }
296
+ return lines.join("\n");
297
+ }
298
+
299
+ export function analyzeMetafileSync(
300
+ metafile:
301
+ | string
302
+ | { inputs?: Record<string, unknown>; outputs?: Record<string, unknown> },
303
+ _opts?: { verbose?: boolean; color?: boolean },
304
+ ): string {
305
+ const meta = typeof metafile === "string" ? JSON.parse(metafile) : metafile;
306
+ const outputs = meta?.outputs ?? {};
307
+ const lines: string[] = [];
308
+ for (const [name, info] of Object.entries(outputs)) {
309
+ const bytes = (info as any)?.bytes ?? 0;
310
+ lines.push(` ${name} ${(bytes / 1024).toFixed(1)}kb`);
311
+ }
312
+ return lines.join("\n");
313
+ }
314
+
315
+ // can't truly block for WASM in browser, so just return source unchanged
316
+ export function transformSync(
317
+ source: string,
318
+ cfg?: TransformConfig,
319
+ ): TransformOutput {
320
+ return { code: source, map: "", warnings: [] };
321
+ }
322
+
323
+ export function buildSync(cfg: BundleConfig): BundleOutput {
324
+ return { errors: [], warnings: [], outputFiles: [] };
325
+ }
326
+
327
+ export default {
328
+ initialize,
329
+ transform,
330
+ transformSync,
331
+ build,
332
+ buildSync,
333
+ context,
334
+ stop,
335
+ formatMessages,
336
+ analyzeMetafile,
337
+ analyzeMetafileSync,
338
+ version,
339
+ setVolume,
340
+ setWasmUrl,
341
+ };
342
+
343
+ // Internal helpers
344
+
345
+ const NODE_CONDITION_ORDER = [
346
+ "node",
347
+ "browser",
348
+ "require",
349
+ "module",
350
+ "import",
351
+ "default",
352
+ ] as const;
353
+
354
+ const BROWSER_CONDITION_ORDER = [
355
+ "browser",
356
+ "module",
357
+ "import",
358
+ "node",
359
+ "require",
360
+ "default",
361
+ ] as const;
362
+
363
+ function pickCondition(entry: ExportValue, platform?: string, conditions?: string[]): string | undefined {
364
+ if (typeof entry === "string") return entry;
365
+ if (typeof entry === "object" && entry !== null) {
366
+ if (conditions) {
367
+ for (const cond of conditions) {
368
+ const nested = (entry as ExportMap)[cond];
369
+ if (nested !== undefined) {
370
+ const resolved = pickCondition(nested, platform, conditions);
371
+ if (resolved) return resolved;
372
+ }
373
+ }
374
+ }
375
+ const order = platform === "browser" ? BROWSER_CONDITION_ORDER : NODE_CONDITION_ORDER;
376
+ for (const cond of order) {
377
+ const nested = (entry as ExportMap)[cond];
378
+ if (nested !== undefined) {
379
+ const resolved = pickCondition(nested, platform, conditions);
380
+ if (resolved) return resolved;
381
+ }
382
+ }
383
+ }
384
+ return undefined;
385
+ }
386
+
387
+ function locateModule(
388
+ vol: MemoryVolume,
389
+ specifier: string,
390
+ exts: string[],
391
+ fromDir?: string,
392
+ platform?: string,
393
+ conditions?: string[],
394
+ ): ModuleHit | null {
395
+ const parts = specifier.split("/");
396
+ const scoped = parts[0].startsWith("@");
397
+ const pkgName = scoped ? parts.slice(0, 2).join("/") : parts[0];
398
+ const subPath = scoped ? parts.slice(2).join("/") : parts.slice(1).join("/");
399
+
400
+ const searchRoots: string[] = [];
401
+ if (fromDir) {
402
+ let dir = fromDir;
403
+ while (dir !== "/" && dir) {
404
+ searchRoots.push(dir + "/node_modules/" + pkgName);
405
+ const parent = dir.substring(0, dir.lastIndexOf("/")) || "/";
406
+ if (parent === dir) break;
407
+ dir = parent;
408
+ }
409
+ }
410
+ searchRoots.push("/node_modules/" + pkgName);
411
+ searchRoots.push("/project/node_modules/" + pkgName);
412
+
413
+ let base: string | null = null;
414
+ for (const candidate of searchRoots) {
415
+ if (vol.existsSync(candidate)) {
416
+ base = candidate;
417
+ break;
418
+ }
419
+ }
420
+ if (!base) return null;
421
+
422
+ const pkgJsonPath = base + "/package.json";
423
+ if (!vol.existsSync(pkgJsonPath)) return null;
424
+
425
+ try {
426
+ const raw = vol.readFileSync(pkgJsonPath, "utf8");
427
+ const pkg: PackageJson = JSON.parse(raw);
428
+
429
+ let found: string | null = null;
430
+
431
+ if (subPath) {
432
+ found = resolveSubpath(vol, pkg, base, subPath, exts, platform, conditions);
433
+ } else {
434
+ found = resolveRoot(vol, pkg, base, exts, platform, conditions);
435
+ }
436
+
437
+ if (found) return { resolvedPath: found, fromVolume: true };
438
+ } catch {
439
+ /* parse failure */
440
+ }
441
+
442
+ return null;
443
+ }
444
+
445
+ function resolveSubpath(
446
+ vol: MemoryVolume,
447
+ pkg: PackageJson,
448
+ base: string,
449
+ sub: string,
450
+ exts: string[],
451
+ platform?: string,
452
+ conditions?: string[],
453
+ ): string | null {
454
+ if (pkg.exports && typeof pkg.exports === "object") {
455
+ const key = "./" + sub;
456
+ const mapEntry = (pkg.exports as ExportMap)[key];
457
+ if (mapEntry) {
458
+ const target = pickCondition(mapEntry, platform, conditions);
459
+ if (target) {
460
+ const full = base + "/" + target.replace(/^\.\//, "");
461
+ const hit = probeFile(vol, full, ["", ".js", ".ts", ".mjs"]);
462
+ if (hit) return hit;
463
+ }
464
+ }
465
+ }
466
+ return probeFile(vol, base + "/" + sub, exts);
467
+ }
468
+
469
+ function resolveRoot(
470
+ vol: MemoryVolume,
471
+ pkg: PackageJson,
472
+ base: string,
473
+ exts: string[],
474
+ platform?: string,
475
+ conditions?: string[],
476
+ ): string | null {
477
+ if (pkg.exports) {
478
+ const top =
479
+ typeof pkg.exports === "object" && !Array.isArray(pkg.exports)
480
+ ? (pkg.exports as ExportMap)["."] || pkg.exports
481
+ : pkg.exports;
482
+ const target = pickCondition(top as ExportValue, platform, conditions);
483
+ if (target) {
484
+ const full = base + "/" + target.replace(/^\.\//, "");
485
+ const hit = probeFile(vol, full, ["", ".js", ".ts", ".mjs"]);
486
+ if (hit) return hit;
487
+ }
488
+ }
489
+ const fallback = pkg.module || pkg.main || "index.js";
490
+ return probeFile(vol, base + "/" + fallback.replace(/^\.\//, ""), exts);
491
+ }
492
+
493
+ // resolve #-prefixed subpath imports via package.json "imports" field
494
+ function resolvePackageImport(
495
+ vol: MemoryVolume,
496
+ specifier: string,
497
+ exts: string[],
498
+ fromDir: string,
499
+ platform?: string,
500
+ conditions?: string[],
501
+ ): string | null {
502
+ let dir = fromDir;
503
+ while (dir && dir !== "/") {
504
+ const pkgPath = dir + "/package.json";
505
+ if (vol.existsSync(pkgPath)) {
506
+ try {
507
+ const pkg: PackageJson = JSON.parse(vol.readFileSync(pkgPath, "utf8"));
508
+ if (pkg.imports) {
509
+ const entry = pkg.imports[specifier];
510
+ if (entry !== undefined) {
511
+ const target = pickCondition(entry, platform, conditions);
512
+ if (target) {
513
+ const full = dir + "/" + target.replace(/^\.\//, "");
514
+ const hit = probeFile(vol, full, ["", ...exts]);
515
+ if (hit) return hit;
516
+ }
517
+ }
518
+
519
+ for (const key of Object.keys(pkg.imports)) {
520
+ const starIdx = key.indexOf("*");
521
+ if (starIdx === -1) continue;
522
+ const prefix = key.slice(0, starIdx);
523
+ const suffix = key.slice(starIdx + 1);
524
+ if (
525
+ specifier.startsWith(prefix) &&
526
+ specifier.endsWith(suffix) &&
527
+ specifier.length >= prefix.length + suffix.length
528
+ ) {
529
+ const matched = suffix.length
530
+ ? specifier.slice(prefix.length, -suffix.length)
531
+ : specifier.slice(prefix.length);
532
+ const target = pickCondition(pkg.imports[key], platform, conditions);
533
+ if (target) {
534
+ const resolved = target.replace(/\*/g, matched);
535
+ const full = dir + "/" + resolved.replace(/^\.\//, "");
536
+ const hit = probeFile(vol, full, ["", ...exts]);
537
+ if (hit) return hit;
538
+ }
539
+ }
540
+ }
541
+ }
542
+ } catch {
543
+ /* parse failure — skip */
544
+ }
545
+ }
546
+ const parent = dir.substring(0, dir.lastIndexOf("/")) || "/";
547
+ if (parent === dir) break;
548
+ dir = parent;
549
+ }
550
+ return null;
551
+ }
552
+
553
+ function probeFile(
554
+ vol: MemoryVolume,
555
+ candidate: string,
556
+ exts: string[],
557
+ ): string | null {
558
+ for (const ext of exts) {
559
+ const p = candidate + ext;
560
+ if (vol.existsSync(p)) {
561
+ try {
562
+ if (!vol.statSync(p).isDirectory()) return p;
563
+ } catch {
564
+ return p;
565
+ }
566
+ }
567
+ }
568
+ return null;
569
+ }
570
+
571
+ function resolveWorkingDir(): string {
572
+ if (
573
+ typeof globalThis !== "undefined" &&
574
+ globalThis.process &&
575
+ typeof globalThis.process.cwd === "function"
576
+ ) {
577
+ return globalThis.process.cwd();
578
+ }
579
+ return "/";
580
+ }
581
+
582
+ function toAbsolute(entry: string, base: string): string {
583
+ if (entry.includes("vfs:")) {
584
+ entry = entry.substring(entry.indexOf("vfs:") + 4);
585
+ }
586
+ if (entry.startsWith("/")) return entry;
587
+ if (entry.startsWith("./")) {
588
+ const b = base.endsWith("/") ? base.slice(0, -1) : base;
589
+ return b + "/" + entry.slice(2);
590
+ }
591
+ if (entry.startsWith("../")) {
592
+ const segments = (base.endsWith("/") ? base.slice(0, -1) : base)
593
+ .split("/")
594
+ .filter(Boolean);
595
+ segments.pop();
596
+ return "/" + segments.join("/") + "/" + entry.slice(3);
597
+ }
598
+ return entry;
599
+ }
600
+
601
+ function normalizeParts(raw: string): string {
602
+ const pieces = raw.split("/").filter(Boolean);
603
+ const stack: string[] = [];
604
+ for (const piece of pieces) {
605
+ if (piece === "..") stack.pop();
606
+ else if (piece !== ".") stack.push(piece);
607
+ }
608
+ return "/" + stack.join("/");
609
+ }
610
+
611
+ function stripNamespacePrefixes(output: BundleOutput): void {
612
+ if (output.outputFiles) {
613
+ for (const f of output.outputFiles) {
614
+ if (f.path.includes("vfs:")) f.path = f.path.replace(/vfs:/g, "");
615
+ }
616
+ }
617
+ if (output.metafile) {
618
+ const m = output.metafile as {
619
+ inputs?: Record<string, unknown>;
620
+ outputs?: Record<string, unknown>;
621
+ };
622
+ for (const bucket of ["inputs", "outputs"] as const) {
623
+ const obj = m[bucket];
624
+ if (!obj) continue;
625
+ for (const key of Object.keys(obj)) {
626
+ if (key.includes("vfs:")) {
627
+ obj[key.replace(/vfs:/g, "")] = obj[key];
628
+ delete obj[key];
629
+ }
630
+ }
631
+ }
632
+ }
633
+ }
634
+
635
+ function createVolumePlugin(externals?: string[], platform?: string, conditions?: string[]): unknown {
636
+ if (!volumeRef) return null;
637
+ const vol = volumeRef;
638
+
639
+ return {
640
+ name: "volume-loader",
641
+ setup(api: unknown) {
642
+ const b = api as {
643
+ onResolve: (
644
+ o: { filter: RegExp; namespace?: string },
645
+ cb: (a: { path: string; importer: string; kind: string }) => unknown,
646
+ ) => void;
647
+ onLoad: (
648
+ o: { filter: RegExp; namespace?: string },
649
+ cb: (a: {
650
+ path: string;
651
+ namespace?: string;
652
+ pluginData?: Record<string, unknown>;
653
+ }) => unknown,
654
+ ) => void;
655
+ };
656
+
657
+ const tryExts = ["", ...RESOLVE_EXTENSIONS];
658
+
659
+ const nativeAddonCache = new Map<string, boolean>();
660
+
661
+ function volumeHit(filePath: string) {
662
+ return { path: filePath, pluginData: { fromVolume: true } };
663
+ }
664
+
665
+ b.onResolve({ filter: /.*/ }, (args) => {
666
+ const { path: raw, importer } = args;
667
+
668
+ if (raw.endsWith(".node")) {
669
+ return { external: true };
670
+ }
671
+
672
+ // externalize packages with native bindings (napi/binary/gypfile in package.json)
673
+ if (!raw.startsWith(".") && !raw.startsWith("/") && !raw.startsWith("#")) {
674
+ const parts = raw.split("/");
675
+ const scoped = parts[0].startsWith("@");
676
+ const pkgName = scoped ? parts.slice(0, 2).join("/") : parts[0];
677
+
678
+ const cached = nativeAddonCache.get(pkgName);
679
+ if (cached === true) return { external: true };
680
+ if (cached === undefined) {
681
+ const candidates: string[] = [];
682
+ if (importer) {
683
+ let dir = importer.substring(0, importer.lastIndexOf("/"));
684
+ while (dir && dir !== "/") {
685
+ candidates.push(dir + "/node_modules/" + pkgName);
686
+ dir = dir.substring(0, dir.lastIndexOf("/")) || "/";
687
+ }
688
+ }
689
+ candidates.push("/node_modules/" + pkgName);
690
+ candidates.push("/project/node_modules/" + pkgName);
691
+ let isNative = false;
692
+ for (const candidate of candidates) {
693
+ if (!vol.existsSync(candidate)) continue;
694
+ try {
695
+ const pkgJson = JSON.parse(vol.readFileSync(candidate + "/package.json", "utf8"));
696
+ if (pkgJson.napi || pkgJson.binary || pkgJson.gypfile) {
697
+ isNative = true;
698
+ }
699
+ } catch { /* no package.json — proceed */ }
700
+ break;
701
+ }
702
+ nativeAddonCache.set(pkgName, isNative);
703
+ if (isNative) return { external: true };
704
+ }
705
+ }
706
+
707
+ if (raw.startsWith("node_modules/")) {
708
+ const absPath = "/" + raw;
709
+ const found = probeFile(vol, absPath, tryExts);
710
+ if (found) return volumeHit(found);
711
+ return { external: true };
712
+ }
713
+
714
+ if (raw.startsWith("/")) {
715
+ const found = probeFile(vol, raw, tryExts);
716
+ return found ? volumeHit(found) : { external: true };
717
+ }
718
+
719
+ if (raw.startsWith(".")) {
720
+ let combined = raw;
721
+ if (importer) {
722
+ const dir = importer.substring(0, importer.lastIndexOf("/"));
723
+ combined = dir + "/" + raw;
724
+ }
725
+ const normed = normalizeParts(combined);
726
+
727
+ const found = probeFile(vol, normed, tryExts);
728
+ if (found) return volumeHit(found);
729
+
730
+ for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
731
+ const idx = probeFile(vol, normed + "/index" + ext, [""]);
732
+ if (idx) return volumeHit(idx);
733
+ }
734
+ }
735
+
736
+ if (raw.startsWith("#")) {
737
+ const importerDir = importer
738
+ ? importer.substring(0, importer.lastIndexOf("/"))
739
+ : resolveWorkingDir();
740
+ const resolved = resolvePackageImport(vol, raw, tryExts, importerDir, platform, conditions);
741
+ if (resolved) return volumeHit(resolved);
742
+ return { external: true };
743
+ }
744
+
745
+ if (
746
+ externals &&
747
+ externals.some((e) => raw === e || raw.startsWith(e + "/"))
748
+ ) {
749
+ return { external: true };
750
+ }
751
+
752
+ const importerDir = importer
753
+ ? importer.substring(0, importer.lastIndexOf("/"))
754
+ : resolveWorkingDir();
755
+ const hit = locateModule(vol, raw, tryExts, importerDir, platform, conditions);
756
+ if (hit) {
757
+ return { path: hit.resolvedPath, pluginData: { fromVolume: true } };
758
+ }
759
+
760
+ // builtins resolve at runtime via our module resolver
761
+ const bare = raw.replace(/^node:/, "");
762
+ if (BUILTIN_MODULES.has(bare)) {
763
+ return { external: true };
764
+ }
765
+
766
+ // let other plugins handle virtual module IDs (e.g. Vite dep optimizer)
767
+ if (args.kind === "entry-point") {
768
+ return undefined;
769
+ }
770
+
771
+ return { external: true };
772
+ });
773
+
774
+ b.onLoad({ filter: /.*/, namespace: "builtin-stub" }, () => {
775
+ return { contents: "module.exports = {};", loader: "js" as const };
776
+ });
777
+
778
+ // read from VFS -- matches ALL paths since other plugins may resolve to relative paths
779
+ b.onLoad({ filter: /.*/ }, (args) => {
780
+ if (
781
+ args.namespace &&
782
+ args.namespace !== "file" &&
783
+ args.namespace !== ""
784
+ )
785
+ return null;
786
+
787
+ const fromVolume = args.pluginData?.fromVolume;
788
+
789
+ if (!fromVolume) {
790
+ let tryPath = args.path;
791
+ if (!tryPath.startsWith("/")) {
792
+ tryPath = "/" + tryPath;
793
+ }
794
+ if (!vol.existsSync(tryPath)) return null;
795
+ args = {
796
+ ...args,
797
+ path: tryPath,
798
+ pluginData: { fromVolume: true, actualPath: tryPath },
799
+ };
800
+ }
801
+ try {
802
+ const diskPath =
803
+ (args.pluginData?.actualPath as string | undefined) || args.path;
804
+ let source: string;
805
+ if (vol.existsSync(diskPath)) {
806
+ source = vol.readFileSync(diskPath, "utf8");
807
+ } else {
808
+ throw new Error(`Not found: ${diskPath}`);
809
+ }
810
+
811
+ // stub out files that require .node native addons
812
+ if (diskPath.includes("node_modules") &&
813
+ /require\(.+\.node['"`)]/m.test(source.slice(0, 4096))) {
814
+ return { contents: "module.exports = {};", loader: "js" as const };
815
+ }
816
+
817
+ // strip top-level await so esbuild doesn't reject CJS require() calls
818
+ if (/\bawait\b/.test(source) && !args.path.endsWith(".mjs")) {
819
+ source = stripTopLevelAwait(source, "topLevelOnly");
820
+ }
821
+
822
+ const dot = diskPath.lastIndexOf(".");
823
+ const ext = dot >= 0 ? diskPath.substring(dot) : "";
824
+ const loaderMap = ESBUILD_LOADER_MAP;
825
+ let loader = loaderMap[ext] as
826
+ | "ts"
827
+ | "tsx"
828
+ | "js"
829
+ | "jsx"
830
+ | "json"
831
+ | "css"
832
+ | "text"
833
+ | "file"
834
+ | undefined;
835
+
836
+ // unknown extensions: let other plugins handle (e.g. .html, .svelte, .vue)
837
+ if (!loader) return null;
838
+
839
+ return { contents: source, loader };
840
+ } catch (err) {
841
+ if (args.path.endsWith(".map")) {
842
+ return {
843
+ contents: '{"version":3,"sources":[],"mappings":""}',
844
+ loader: "json" as const,
845
+ };
846
+ }
847
+ return {
848
+ errors: [{ text: `volume-loader: ${args.path} -- ${err}` }],
849
+ };
850
+ }
851
+ });
852
+ },
853
+ };
854
+ }