sandlot 0.2.2 → 0.2.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/browser/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAuClB,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,GAAE,yBAA8B;IAWnD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IA8B1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B,OAAO,CAAC,yBAAyB;IAc3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAoG5D"}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/browser/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAmCtE,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,GAAE,yBAA8B;IAWnD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IA8B1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B,OAAO,CAAC,yBAAyB;IAc3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAU5D"}
@@ -649,6 +649,78 @@ function generateSharedModuleCode(moduleId, registry) {
649
649
  }
650
650
  return code;
651
651
  }
652
+ async function executeBundleWithEsbuild(options) {
653
+ const { esbuild, bundleOptions, cdnBaseUrl, bundleCdnImports } = options;
654
+ const {
655
+ fs,
656
+ entryPoint,
657
+ installedPackages = {},
658
+ sharedModules = [],
659
+ sharedModuleRegistry,
660
+ external = [],
661
+ format = "esm",
662
+ minify = false,
663
+ sourcemap = false,
664
+ target = ["es2020"]
665
+ } = bundleOptions;
666
+ const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
667
+ if (!fs.exists(normalizedEntry)) {
668
+ return {
669
+ success: false,
670
+ errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
671
+ warnings: []
672
+ };
673
+ }
674
+ const includedFiles = new Set;
675
+ const plugin = createVfsPlugin({
676
+ fs,
677
+ entryPoint: normalizedEntry,
678
+ installedPackages,
679
+ sharedModules: new Set(sharedModules),
680
+ sharedModuleRegistry: sharedModuleRegistry ?? null,
681
+ cdnBaseUrl,
682
+ includedFiles,
683
+ bundleCdnImports
684
+ });
685
+ try {
686
+ const result = await esbuild.build({
687
+ entryPoints: [normalizedEntry],
688
+ bundle: true,
689
+ write: false,
690
+ format,
691
+ minify,
692
+ sourcemap: sourcemap ? "inline" : false,
693
+ target,
694
+ external,
695
+ plugins: [plugin],
696
+ jsx: "automatic"
697
+ });
698
+ const code = result.outputFiles?.[0]?.text ?? "";
699
+ const warnings = result.warnings.map((w) => convertEsbuildMessage(w));
700
+ return {
701
+ success: true,
702
+ code,
703
+ warnings,
704
+ includedFiles: Array.from(includedFiles)
705
+ };
706
+ } catch (err) {
707
+ if (isEsbuildBuildFailure(err)) {
708
+ const errors = err.errors.map((e) => convertEsbuildMessage(e));
709
+ const warnings = err.warnings.map((w) => convertEsbuildMessage(w));
710
+ return {
711
+ success: false,
712
+ errors,
713
+ warnings
714
+ };
715
+ }
716
+ const message = err instanceof Error ? err.message : String(err);
717
+ return {
718
+ success: false,
719
+ errors: [{ text: message }],
720
+ warnings: []
721
+ };
722
+ }
723
+ }
652
724
 
653
725
  // src/browser/bundler.ts
654
726
  var ESBUILD_VERSION = "0.27.2";
@@ -729,75 +801,12 @@ To enable, add these headers to your server:
729
801
  }
730
802
  async bundle(options) {
731
803
  await this.initialize();
732
- const esbuild = this.getEsbuild();
733
- const {
734
- fs,
735
- entryPoint,
736
- installedPackages = {},
737
- sharedModules = [],
738
- sharedModuleRegistry,
739
- external = [],
740
- format = "esm",
741
- minify = false,
742
- sourcemap = false,
743
- target = ["es2020"]
744
- } = options;
745
- const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
746
- if (!fs.exists(normalizedEntry)) {
747
- return {
748
- success: false,
749
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
750
- warnings: []
751
- };
752
- }
753
- const includedFiles = new Set;
754
- const plugin = createVfsPlugin({
755
- fs,
756
- entryPoint: normalizedEntry,
757
- installedPackages,
758
- sharedModules: new Set(sharedModules),
759
- sharedModuleRegistry: sharedModuleRegistry ?? null,
804
+ return executeBundleWithEsbuild({
805
+ esbuild: this.getEsbuild(),
806
+ bundleOptions: options,
760
807
  cdnBaseUrl: this.options.cdnBaseUrl,
761
- includedFiles
808
+ bundleCdnImports: false
762
809
  });
763
- try {
764
- const result = await esbuild.build({
765
- entryPoints: [normalizedEntry],
766
- bundle: true,
767
- write: false,
768
- format,
769
- minify,
770
- sourcemap: sourcemap ? "inline" : false,
771
- target,
772
- external,
773
- plugins: [plugin],
774
- jsx: "automatic"
775
- });
776
- const code = result.outputFiles?.[0]?.text ?? "";
777
- const warnings = result.warnings.map((w) => convertEsbuildMessage(w));
778
- return {
779
- success: true,
780
- code,
781
- warnings,
782
- includedFiles: Array.from(includedFiles)
783
- };
784
- } catch (err) {
785
- if (isEsbuildBuildFailure(err)) {
786
- const errors = err.errors.map((e) => convertEsbuildMessage(e));
787
- const warnings = err.warnings.map((w) => convertEsbuildMessage(w));
788
- return {
789
- success: false,
790
- errors,
791
- warnings
792
- };
793
- }
794
- const message = err instanceof Error ? err.message : String(err);
795
- return {
796
- success: false,
797
- errors: [{ text: message }],
798
- warnings: []
799
- };
800
- }
801
810
  }
802
811
  }
803
812
  // src/core/executor.ts
@@ -2369,6 +2378,64 @@ async function createSandboxImpl(fs, options, context) {
2369
2378
  if (!fs.exists(TSCONFIG_PATH)) {
2370
2379
  fs.writeFile(TSCONFIG_PATH, JSON.stringify(DEFAULT_TSCONFIG, null, 2));
2371
2380
  }
2381
+ if (sharedModuleRegistry && typesResolver) {
2382
+ const sharedModuleIds = sharedModuleRegistry.list();
2383
+ const typesFetches = sharedModuleIds.map(async (moduleId) => {
2384
+ try {
2385
+ const typeFiles = await typesResolver.resolveTypes(moduleId);
2386
+ return { moduleId, typeFiles, error: null };
2387
+ } catch (err) {
2388
+ console.warn(`[sandlot] Failed to fetch types for shared module "${moduleId}":`, err);
2389
+ return { moduleId, typeFiles: {}, error: err };
2390
+ }
2391
+ });
2392
+ const results = await Promise.all(typesFetches);
2393
+ for (const { moduleId, typeFiles } of results) {
2394
+ if (Object.keys(typeFiles).length === 0)
2395
+ continue;
2396
+ let packageName = moduleId;
2397
+ let subpath;
2398
+ if (moduleId.startsWith("@")) {
2399
+ const parts = moduleId.split("/");
2400
+ if (parts.length >= 2) {
2401
+ packageName = `${parts[0]}/${parts[1]}`;
2402
+ subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
2403
+ }
2404
+ } else {
2405
+ const slashIndex = moduleId.indexOf("/");
2406
+ if (slashIndex !== -1) {
2407
+ packageName = moduleId.slice(0, slashIndex);
2408
+ subpath = moduleId.slice(slashIndex + 1);
2409
+ }
2410
+ }
2411
+ const packageDir = `/node_modules/${packageName}`;
2412
+ let typesEntry = null;
2413
+ let fallbackEntry = null;
2414
+ for (const [filePath, content] of Object.entries(typeFiles)) {
2415
+ const fullPath = filePath.startsWith("/") ? filePath : `${packageDir}/${filePath}`;
2416
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
2417
+ ensureDir(fs, dir);
2418
+ fs.writeFile(fullPath, content);
2419
+ const relativePath = fullPath.replace(`${packageDir}/`, "");
2420
+ if (relativePath === "index.d.ts") {
2421
+ typesEntry = "index.d.ts";
2422
+ } else if (!fallbackEntry && relativePath.endsWith(".d.ts") && !relativePath.includes("/")) {
2423
+ fallbackEntry = relativePath;
2424
+ }
2425
+ }
2426
+ const finalTypesEntry = typesEntry ?? fallbackEntry ?? "index.d.ts";
2427
+ const pkgJsonPath = `${packageDir}/package.json`;
2428
+ if (!fs.exists(pkgJsonPath)) {
2429
+ const pkgJson = {
2430
+ name: packageName,
2431
+ version: "shared",
2432
+ types: finalTypesEntry,
2433
+ main: finalTypesEntry.replace(/\.d\.ts$/, ".js")
2434
+ };
2435
+ fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
2436
+ }
2437
+ }
2438
+ }
2372
2439
  async function install(packageSpec) {
2373
2440
  const { name, version } = parsePackageSpec(packageSpec);
2374
2441
  let resolvedVersion = version ?? "latest";
@@ -4,7 +4,7 @@
4
4
  * This module contains the VFS plugin, path resolution, and shared module
5
5
  * code generation logic that is common to both esbuild and esbuild-wasm.
6
6
  */
7
- import type { ISharedModuleRegistry, BundleWarning, BundleError, Filesystem } from "../types";
7
+ import type { ISharedModuleRegistry, BundleOptions, BundleResult, BundleWarning, BundleError, Filesystem } from "../types";
8
8
  /**
9
9
  * Minimal esbuild types needed for the shared utilities.
10
10
  * These are compatible with both esbuild and esbuild-wasm.
@@ -139,4 +139,46 @@ export declare function getLoader(path: string): EsbuildLoader;
139
139
  * Generate JavaScript code that accesses a shared module at runtime.
140
140
  */
141
141
  export declare function generateSharedModuleCode(moduleId: string, registry: ISharedModuleRegistry | null): string;
142
+ /**
143
+ * Minimal esbuild interface needed for bundling.
144
+ * Compatible with both esbuild and esbuild-wasm.
145
+ *
146
+ * Uses a loose `Record<string, unknown>` for build options to avoid
147
+ * type conflicts between the various esbuild module signatures.
148
+ */
149
+ export interface EsbuildInstance {
150
+ build(options: Record<string, unknown>): Promise<{
151
+ outputFiles?: Array<{
152
+ text: string;
153
+ }>;
154
+ warnings: EsbuildMessage[];
155
+ }>;
156
+ }
157
+ /**
158
+ * Options for the shared bundle execution helper.
159
+ */
160
+ export interface ExecuteBundleOptions {
161
+ /** The esbuild instance to use */
162
+ esbuild: EsbuildInstance;
163
+ /** Bundle options from the IBundler interface */
164
+ bundleOptions: BundleOptions;
165
+ /** Base URL for CDN imports */
166
+ cdnBaseUrl: string;
167
+ /**
168
+ * Whether to bundle CDN imports inline.
169
+ * - Browser: false (external) - browser can fetch at runtime
170
+ * - Node/Bun: true (bundle) - esbuild fetches during build
171
+ */
172
+ bundleCdnImports: boolean;
173
+ }
174
+ /**
175
+ * Execute a bundle using esbuild with the VFS plugin.
176
+ *
177
+ * This is the shared implementation used by both browser and node WASM bundlers.
178
+ * It handles entry point normalization, VFS plugin creation, and error handling.
179
+ *
180
+ * @param options - Bundle execution options
181
+ * @returns Bundle result with code or errors
182
+ */
183
+ export declare function executeBundleWithEsbuild(options: ExecuteBundleOptions): Promise<BundleResult>;
142
184
  //# sourceMappingURL=bundler-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundler-utils.d.ts","sourceRoot":"","sources":["../../src/core/bundler-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bundler-utils.d.ts","sourceRoot":"","sources":["../../src/core/bundler-utils.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,OAAO,EACP,cAAc,EAYf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAA6B,MAAM,MAAM,CAAC;AA2J7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,QAAQ,CAAC;IAClB,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,oBAAoB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAMD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,CA2ZlB"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,OAAO,EACP,cAAc,EAYf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAA6B,MAAM,MAAM,CAAC;AA2J7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,QAAQ,CAAC;IAClB,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,oBAAoB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAMD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,CA6elB"}
package/dist/index.js CHANGED
@@ -1283,6 +1283,64 @@ async function createSandboxImpl(fs, options, context) {
1283
1283
  if (!fs.exists(TSCONFIG_PATH)) {
1284
1284
  fs.writeFile(TSCONFIG_PATH, JSON.stringify(DEFAULT_TSCONFIG, null, 2));
1285
1285
  }
1286
+ if (sharedModuleRegistry && typesResolver) {
1287
+ const sharedModuleIds = sharedModuleRegistry.list();
1288
+ const typesFetches = sharedModuleIds.map(async (moduleId) => {
1289
+ try {
1290
+ const typeFiles = await typesResolver.resolveTypes(moduleId);
1291
+ return { moduleId, typeFiles, error: null };
1292
+ } catch (err) {
1293
+ console.warn(`[sandlot] Failed to fetch types for shared module "${moduleId}":`, err);
1294
+ return { moduleId, typeFiles: {}, error: err };
1295
+ }
1296
+ });
1297
+ const results = await Promise.all(typesFetches);
1298
+ for (const { moduleId, typeFiles } of results) {
1299
+ if (Object.keys(typeFiles).length === 0)
1300
+ continue;
1301
+ let packageName = moduleId;
1302
+ let subpath;
1303
+ if (moduleId.startsWith("@")) {
1304
+ const parts = moduleId.split("/");
1305
+ if (parts.length >= 2) {
1306
+ packageName = `${parts[0]}/${parts[1]}`;
1307
+ subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
1308
+ }
1309
+ } else {
1310
+ const slashIndex = moduleId.indexOf("/");
1311
+ if (slashIndex !== -1) {
1312
+ packageName = moduleId.slice(0, slashIndex);
1313
+ subpath = moduleId.slice(slashIndex + 1);
1314
+ }
1315
+ }
1316
+ const packageDir = `/node_modules/${packageName}`;
1317
+ let typesEntry = null;
1318
+ let fallbackEntry = null;
1319
+ for (const [filePath, content] of Object.entries(typeFiles)) {
1320
+ const fullPath = filePath.startsWith("/") ? filePath : `${packageDir}/${filePath}`;
1321
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
1322
+ ensureDir(fs, dir);
1323
+ fs.writeFile(fullPath, content);
1324
+ const relativePath = fullPath.replace(`${packageDir}/`, "");
1325
+ if (relativePath === "index.d.ts") {
1326
+ typesEntry = "index.d.ts";
1327
+ } else if (!fallbackEntry && relativePath.endsWith(".d.ts") && !relativePath.includes("/")) {
1328
+ fallbackEntry = relativePath;
1329
+ }
1330
+ }
1331
+ const finalTypesEntry = typesEntry ?? fallbackEntry ?? "index.d.ts";
1332
+ const pkgJsonPath = `${packageDir}/package.json`;
1333
+ if (!fs.exists(pkgJsonPath)) {
1334
+ const pkgJson = {
1335
+ name: packageName,
1336
+ version: "shared",
1337
+ types: finalTypesEntry,
1338
+ main: finalTypesEntry.replace(/\.d\.ts$/, ".js")
1339
+ };
1340
+ fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
1341
+ }
1342
+ }
1343
+ }
1286
1344
  async function install(packageSpec) {
1287
1345
  const { name, version } = parsePackageSpec(packageSpec);
1288
1346
  let resolvedVersion = version ?? "latest";
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/node/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAOlB,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IACnD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,OAAO,GAAE,2BAAgC;IAOrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,OAAO,CAAC,UAAU;IAOlB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA0G5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,2BAA2B,GACpC,oBAAoB,CAEtB"}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/node/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtE,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IACnD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,OAAO,GAAE,2BAAgC;IAOrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,OAAO,CAAC,UAAU;IAOlB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAY5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,2BAA2B,GACpC,oBAAoB,CAEtB"}
@@ -319,6 +319,78 @@ function generateSharedModuleCode(moduleId, registry) {
319
319
  }
320
320
  return code;
321
321
  }
322
+ async function executeBundleWithEsbuild(options) {
323
+ const { esbuild, bundleOptions, cdnBaseUrl, bundleCdnImports } = options;
324
+ const {
325
+ fs,
326
+ entryPoint,
327
+ installedPackages = {},
328
+ sharedModules = [],
329
+ sharedModuleRegistry,
330
+ external = [],
331
+ format = "esm",
332
+ minify = false,
333
+ sourcemap = false,
334
+ target = ["es2020"]
335
+ } = bundleOptions;
336
+ const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
337
+ if (!fs.exists(normalizedEntry)) {
338
+ return {
339
+ success: false,
340
+ errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
341
+ warnings: []
342
+ };
343
+ }
344
+ const includedFiles = new Set;
345
+ const plugin = createVfsPlugin({
346
+ fs,
347
+ entryPoint: normalizedEntry,
348
+ installedPackages,
349
+ sharedModules: new Set(sharedModules),
350
+ sharedModuleRegistry: sharedModuleRegistry ?? null,
351
+ cdnBaseUrl,
352
+ includedFiles,
353
+ bundleCdnImports
354
+ });
355
+ try {
356
+ const result = await esbuild.build({
357
+ entryPoints: [normalizedEntry],
358
+ bundle: true,
359
+ write: false,
360
+ format,
361
+ minify,
362
+ sourcemap: sourcemap ? "inline" : false,
363
+ target,
364
+ external,
365
+ plugins: [plugin],
366
+ jsx: "automatic"
367
+ });
368
+ const code = result.outputFiles?.[0]?.text ?? "";
369
+ const warnings = result.warnings.map((w) => convertEsbuildMessage(w));
370
+ return {
371
+ success: true,
372
+ code,
373
+ warnings,
374
+ includedFiles: Array.from(includedFiles)
375
+ };
376
+ } catch (err) {
377
+ if (isEsbuildBuildFailure(err)) {
378
+ const errors = err.errors.map((e) => convertEsbuildMessage(e));
379
+ const warnings = err.warnings.map((w) => convertEsbuildMessage(w));
380
+ return {
381
+ success: false,
382
+ errors,
383
+ warnings
384
+ };
385
+ }
386
+ const message = err instanceof Error ? err.message : String(err);
387
+ return {
388
+ success: false,
389
+ errors: [{ text: message }],
390
+ warnings: []
391
+ };
392
+ }
393
+ }
322
394
 
323
395
  // src/node/bundler.ts
324
396
  class EsbuildNativeBundler {
@@ -350,76 +422,12 @@ class EsbuildNativeBundler {
350
422
  }
351
423
  async bundle(options) {
352
424
  await this.initialize();
353
- const esbuild = this.getEsbuild();
354
- const {
355
- fs,
356
- entryPoint,
357
- installedPackages = {},
358
- sharedModules = [],
359
- sharedModuleRegistry,
360
- external = [],
361
- format = "esm",
362
- minify = false,
363
- sourcemap = false,
364
- target = ["es2020"]
365
- } = options;
366
- const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
367
- if (!fs.exists(normalizedEntry)) {
368
- return {
369
- success: false,
370
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
371
- warnings: []
372
- };
373
- }
374
- const includedFiles = new Set;
375
- const plugin = createVfsPlugin({
376
- fs,
377
- entryPoint: normalizedEntry,
378
- installedPackages,
379
- sharedModules: new Set(sharedModules),
380
- sharedModuleRegistry: sharedModuleRegistry ?? null,
425
+ return executeBundleWithEsbuild({
426
+ esbuild: this.getEsbuild(),
427
+ bundleOptions: options,
381
428
  cdnBaseUrl: this.options.cdnBaseUrl,
382
- includedFiles,
383
429
  bundleCdnImports: true
384
430
  });
385
- try {
386
- const result = await esbuild.build({
387
- entryPoints: [normalizedEntry],
388
- bundle: true,
389
- write: false,
390
- format,
391
- minify,
392
- sourcemap: sourcemap ? "inline" : false,
393
- target,
394
- external,
395
- plugins: [plugin],
396
- jsx: "automatic"
397
- });
398
- const code = result.outputFiles?.[0]?.text ?? "";
399
- const warnings = result.warnings.map((w) => convertEsbuildMessage(w));
400
- return {
401
- success: true,
402
- code,
403
- warnings,
404
- includedFiles: Array.from(includedFiles)
405
- };
406
- } catch (err) {
407
- if (isEsbuildBuildFailure(err)) {
408
- const errors = err.errors.map((e) => convertEsbuildMessage(e));
409
- const warnings = err.warnings.map((w) => convertEsbuildMessage(w));
410
- return {
411
- success: false,
412
- errors,
413
- warnings
414
- };
415
- }
416
- const message = err instanceof Error ? err.message : String(err);
417
- return {
418
- success: false,
419
- errors: [{ text: message }],
420
- warnings: []
421
- };
422
- }
423
431
  }
424
432
  }
425
433
  function createEsbuildNativeBundler(options) {
@@ -486,76 +494,12 @@ class EsbuildWasmNodeBundler {
486
494
  }
487
495
  async bundle(options) {
488
496
  await this.initialize();
489
- const esbuild = this.getEsbuild();
490
- const {
491
- fs,
492
- entryPoint,
493
- installedPackages = {},
494
- sharedModules = [],
495
- sharedModuleRegistry,
496
- external = [],
497
- format = "esm",
498
- minify = false,
499
- sourcemap = false,
500
- target = ["es2020"]
501
- } = options;
502
- const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
503
- if (!fs.exists(normalizedEntry)) {
504
- return {
505
- success: false,
506
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
507
- warnings: []
508
- };
509
- }
510
- const includedFiles = new Set;
511
- const plugin = createVfsPlugin({
512
- fs,
513
- entryPoint: normalizedEntry,
514
- installedPackages,
515
- sharedModules: new Set(sharedModules),
516
- sharedModuleRegistry: sharedModuleRegistry ?? null,
497
+ return executeBundleWithEsbuild({
498
+ esbuild: this.getEsbuild(),
499
+ bundleOptions: options,
517
500
  cdnBaseUrl: this.options.cdnBaseUrl,
518
- includedFiles,
519
501
  bundleCdnImports: true
520
502
  });
521
- try {
522
- const result = await esbuild.build({
523
- entryPoints: [normalizedEntry],
524
- bundle: true,
525
- write: false,
526
- format,
527
- minify,
528
- sourcemap: sourcemap ? "inline" : false,
529
- target,
530
- external,
531
- plugins: [plugin],
532
- jsx: "automatic"
533
- });
534
- const code = result.outputFiles?.[0]?.text ?? "";
535
- const warnings = result.warnings.map((w) => convertEsbuildMessage(w));
536
- return {
537
- success: true,
538
- code,
539
- warnings,
540
- includedFiles: Array.from(includedFiles)
541
- };
542
- } catch (err) {
543
- if (isEsbuildBuildFailure(err)) {
544
- const errors = err.errors.map((e) => convertEsbuildMessage(e));
545
- const warnings = err.warnings.map((w) => convertEsbuildMessage(w));
546
- return {
547
- success: false,
548
- errors,
549
- warnings
550
- };
551
- }
552
- const message = err instanceof Error ? err.message : String(err);
553
- return {
554
- success: false,
555
- errors: [{ text: message }],
556
- warnings: []
557
- };
558
- }
559
503
  }
560
504
  }
561
505
  function createEsbuildWasmNodeBundler(options) {
@@ -2291,6 +2235,64 @@ async function createSandboxImpl(fs, options, context) {
2291
2235
  if (!fs.exists(TSCONFIG_PATH)) {
2292
2236
  fs.writeFile(TSCONFIG_PATH, JSON.stringify(DEFAULT_TSCONFIG, null, 2));
2293
2237
  }
2238
+ if (sharedModuleRegistry && typesResolver) {
2239
+ const sharedModuleIds = sharedModuleRegistry.list();
2240
+ const typesFetches = sharedModuleIds.map(async (moduleId) => {
2241
+ try {
2242
+ const typeFiles = await typesResolver.resolveTypes(moduleId);
2243
+ return { moduleId, typeFiles, error: null };
2244
+ } catch (err) {
2245
+ console.warn(`[sandlot] Failed to fetch types for shared module "${moduleId}":`, err);
2246
+ return { moduleId, typeFiles: {}, error: err };
2247
+ }
2248
+ });
2249
+ const results = await Promise.all(typesFetches);
2250
+ for (const { moduleId, typeFiles } of results) {
2251
+ if (Object.keys(typeFiles).length === 0)
2252
+ continue;
2253
+ let packageName = moduleId;
2254
+ let subpath;
2255
+ if (moduleId.startsWith("@")) {
2256
+ const parts = moduleId.split("/");
2257
+ if (parts.length >= 2) {
2258
+ packageName = `${parts[0]}/${parts[1]}`;
2259
+ subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
2260
+ }
2261
+ } else {
2262
+ const slashIndex = moduleId.indexOf("/");
2263
+ if (slashIndex !== -1) {
2264
+ packageName = moduleId.slice(0, slashIndex);
2265
+ subpath = moduleId.slice(slashIndex + 1);
2266
+ }
2267
+ }
2268
+ const packageDir = `/node_modules/${packageName}`;
2269
+ let typesEntry = null;
2270
+ let fallbackEntry = null;
2271
+ for (const [filePath, content] of Object.entries(typeFiles)) {
2272
+ const fullPath = filePath.startsWith("/") ? filePath : `${packageDir}/${filePath}`;
2273
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
2274
+ ensureDir(fs, dir);
2275
+ fs.writeFile(fullPath, content);
2276
+ const relativePath = fullPath.replace(`${packageDir}/`, "");
2277
+ if (relativePath === "index.d.ts") {
2278
+ typesEntry = "index.d.ts";
2279
+ } else if (!fallbackEntry && relativePath.endsWith(".d.ts") && !relativePath.includes("/")) {
2280
+ fallbackEntry = relativePath;
2281
+ }
2282
+ }
2283
+ const finalTypesEntry = typesEntry ?? fallbackEntry ?? "index.d.ts";
2284
+ const pkgJsonPath = `${packageDir}/package.json`;
2285
+ if (!fs.exists(pkgJsonPath)) {
2286
+ const pkgJson = {
2287
+ name: packageName,
2288
+ version: "shared",
2289
+ types: finalTypesEntry,
2290
+ main: finalTypesEntry.replace(/\.d\.ts$/, ".js")
2291
+ };
2292
+ fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
2293
+ }
2294
+ }
2295
+ }
2294
2296
  async function install(packageSpec) {
2295
2297
  const { name, version } = parsePackageSpec(packageSpec);
2296
2298
  let resolvedVersion = version ?? "latest";
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-bundler.d.ts","sourceRoot":"","sources":["../../src/node/wasm-bundler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAoClB,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,GAAE,6BAAkC;IAOvD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IAoB1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA0G5D;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,sBAAsB,CAExB"}
1
+ {"version":3,"file":"wasm-bundler.d.ts","sourceRoot":"","sources":["../../src/node/wasm-bundler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAgCtE,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,GAAE,6BAAkC;IAOvD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IAoB1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAY5D;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,sBAAsB,CAExB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandlot",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "TypeScript sandbox with esbuild bundling and type checking for browser and server",
5
5
  "author": "blindmansion",
6
6
  "repository": {
@@ -6,18 +6,8 @@
6
6
  */
7
7
 
8
8
  import type * as EsbuildTypes from "esbuild-wasm";
9
- import type {
10
- IBundler,
11
- BundleOptions,
12
- BundleResult,
13
- BundleWarning,
14
- BundleError,
15
- } from "../types";
16
- import {
17
- createVfsPlugin,
18
- isEsbuildBuildFailure,
19
- convertEsbuildMessage,
20
- } from "../core/bundler-utils";
9
+ import type { IBundler, BundleOptions, BundleResult } from "../types";
10
+ import { executeBundleWithEsbuild } from "../core/bundler-utils";
21
11
 
22
12
  /**
23
13
  * esbuild-wasm version - should match what's in package.json
@@ -211,101 +201,11 @@ export class EsbuildWasmBundler implements IBundler {
211
201
  async bundle(options: BundleOptions): Promise<BundleResult> {
212
202
  await this.initialize();
213
203
 
214
- const esbuild = this.getEsbuild();
215
-
216
- const {
217
- fs,
218
- entryPoint,
219
- installedPackages = {},
220
- sharedModules = [],
221
- sharedModuleRegistry,
222
- external = [],
223
- format = "esm",
224
- minify = false,
225
- sourcemap = false,
226
- target = ["es2020"],
227
- } = options;
228
-
229
- // Normalize entry point to absolute path
230
- const normalizedEntry = entryPoint.startsWith("/")
231
- ? entryPoint
232
- : `/${entryPoint}`;
233
-
234
- // Verify entry point exists
235
- if (!fs.exists(normalizedEntry)) {
236
- return {
237
- success: false,
238
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
239
- warnings: [],
240
- };
241
- }
242
-
243
- // Track files included in the bundle
244
- const includedFiles = new Set<string>();
245
-
246
- // Create the VFS plugin
247
- const plugin = createVfsPlugin({
248
- fs,
249
- entryPoint: normalizedEntry,
250
- installedPackages,
251
- sharedModules: new Set(sharedModules),
252
- sharedModuleRegistry: sharedModuleRegistry ?? null,
204
+ return executeBundleWithEsbuild({
205
+ esbuild: this.getEsbuild(),
206
+ bundleOptions: options,
253
207
  cdnBaseUrl: this.options.cdnBaseUrl!,
254
- includedFiles,
208
+ bundleCdnImports: false, // Browser can fetch CDN imports at runtime
255
209
  });
256
-
257
- try {
258
- // Run esbuild
259
- const result = await esbuild.build({
260
- entryPoints: [normalizedEntry],
261
- bundle: true,
262
- write: false,
263
- format,
264
- minify,
265
- sourcemap: sourcemap ? "inline" : false,
266
- target,
267
- external,
268
- // Cast to esbuild's Plugin type since our minimal interface is compatible
269
- plugins: [plugin as EsbuildTypes.Plugin],
270
- jsx: "automatic",
271
- });
272
-
273
- const code = result.outputFiles?.[0]?.text ?? "";
274
-
275
- // Convert esbuild warnings to our format
276
- const warnings: BundleWarning[] = result.warnings.map((w) =>
277
- convertEsbuildMessage(w)
278
- );
279
-
280
- return {
281
- success: true,
282
- code,
283
- warnings,
284
- includedFiles: Array.from(includedFiles),
285
- };
286
- } catch (err) {
287
- // esbuild throws BuildFailure with .errors array
288
- if (isEsbuildBuildFailure(err)) {
289
- const errors: BundleError[] = err.errors.map((e) =>
290
- convertEsbuildMessage(e)
291
- );
292
- const warnings: BundleWarning[] = err.warnings.map((w) =>
293
- convertEsbuildMessage(w)
294
- );
295
- return {
296
- success: false,
297
- errors,
298
- warnings,
299
- };
300
- }
301
-
302
- // Unknown error - wrap it
303
- const message = err instanceof Error ? err.message : String(err);
304
- return {
305
- success: false,
306
- errors: [{ text: message }],
307
- warnings: [],
308
- };
309
- }
310
210
  }
311
211
  }
@@ -7,6 +7,8 @@
7
7
 
8
8
  import type {
9
9
  ISharedModuleRegistry,
10
+ BundleOptions,
11
+ BundleResult,
10
12
  BundleWarning,
11
13
  BundleError,
12
14
  BundleLocation,
@@ -628,3 +630,149 @@ export function generateSharedModuleCode(
628
630
 
629
631
  return code;
630
632
  }
633
+
634
+ // =============================================================================
635
+ // Shared Bundle Execution
636
+ // =============================================================================
637
+
638
+ /**
639
+ * Minimal esbuild interface needed for bundling.
640
+ * Compatible with both esbuild and esbuild-wasm.
641
+ *
642
+ * Uses a loose `Record<string, unknown>` for build options to avoid
643
+ * type conflicts between the various esbuild module signatures.
644
+ */
645
+ export interface EsbuildInstance {
646
+ build(options: Record<string, unknown>): Promise<{
647
+ outputFiles?: Array<{ text: string }>;
648
+ warnings: EsbuildMessage[];
649
+ }>;
650
+ }
651
+
652
+ /**
653
+ * Options for the shared bundle execution helper.
654
+ */
655
+ export interface ExecuteBundleOptions {
656
+ /** The esbuild instance to use */
657
+ esbuild: EsbuildInstance;
658
+ /** Bundle options from the IBundler interface */
659
+ bundleOptions: BundleOptions;
660
+ /** Base URL for CDN imports */
661
+ cdnBaseUrl: string;
662
+ /**
663
+ * Whether to bundle CDN imports inline.
664
+ * - Browser: false (external) - browser can fetch at runtime
665
+ * - Node/Bun: true (bundle) - esbuild fetches during build
666
+ */
667
+ bundleCdnImports: boolean;
668
+ }
669
+
670
+ /**
671
+ * Execute a bundle using esbuild with the VFS plugin.
672
+ *
673
+ * This is the shared implementation used by both browser and node WASM bundlers.
674
+ * It handles entry point normalization, VFS plugin creation, and error handling.
675
+ *
676
+ * @param options - Bundle execution options
677
+ * @returns Bundle result with code or errors
678
+ */
679
+ export async function executeBundleWithEsbuild(
680
+ options: ExecuteBundleOptions
681
+ ): Promise<BundleResult> {
682
+ const { esbuild, bundleOptions, cdnBaseUrl, bundleCdnImports } = options;
683
+
684
+ const {
685
+ fs,
686
+ entryPoint,
687
+ installedPackages = {},
688
+ sharedModules = [],
689
+ sharedModuleRegistry,
690
+ external = [],
691
+ format = "esm",
692
+ minify = false,
693
+ sourcemap = false,
694
+ target = ["es2020"],
695
+ } = bundleOptions;
696
+
697
+ // Normalize entry point to absolute path
698
+ const normalizedEntry = entryPoint.startsWith("/")
699
+ ? entryPoint
700
+ : `/${entryPoint}`;
701
+
702
+ // Verify entry point exists
703
+ if (!fs.exists(normalizedEntry)) {
704
+ return {
705
+ success: false,
706
+ errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
707
+ warnings: [],
708
+ };
709
+ }
710
+
711
+ // Track files included in the bundle
712
+ const includedFiles = new Set<string>();
713
+
714
+ // Create the VFS plugin
715
+ const plugin = createVfsPlugin({
716
+ fs,
717
+ entryPoint: normalizedEntry,
718
+ installedPackages,
719
+ sharedModules: new Set(sharedModules),
720
+ sharedModuleRegistry: sharedModuleRegistry ?? null,
721
+ cdnBaseUrl,
722
+ includedFiles,
723
+ bundleCdnImports,
724
+ });
725
+
726
+ try {
727
+ // Run esbuild
728
+ const result = await esbuild.build({
729
+ entryPoints: [normalizedEntry],
730
+ bundle: true,
731
+ write: false,
732
+ format,
733
+ minify,
734
+ sourcemap: sourcemap ? "inline" : false,
735
+ target,
736
+ external,
737
+ plugins: [plugin],
738
+ jsx: "automatic",
739
+ });
740
+
741
+ const code = result.outputFiles?.[0]?.text ?? "";
742
+
743
+ // Convert esbuild warnings to our format
744
+ const warnings: BundleWarning[] = result.warnings.map((w) =>
745
+ convertEsbuildMessage(w)
746
+ );
747
+
748
+ return {
749
+ success: true,
750
+ code,
751
+ warnings,
752
+ includedFiles: Array.from(includedFiles),
753
+ };
754
+ } catch (err) {
755
+ // esbuild throws BuildFailure with .errors array
756
+ if (isEsbuildBuildFailure(err)) {
757
+ const errors: BundleError[] = err.errors.map((e) =>
758
+ convertEsbuildMessage(e)
759
+ );
760
+ const warnings: BundleWarning[] = err.warnings.map((w) =>
761
+ convertEsbuildMessage(w)
762
+ );
763
+ return {
764
+ success: false,
765
+ errors,
766
+ warnings,
767
+ };
768
+ }
769
+
770
+ // Unknown error - wrap it
771
+ const message = err instanceof Error ? err.message : String(err);
772
+ return {
773
+ success: false,
774
+ errors: [{ text: message }],
775
+ warnings: [],
776
+ };
777
+ }
778
+ }
@@ -262,6 +262,88 @@ export async function createSandboxImpl(
262
262
  fs.writeFile(TSCONFIG_PATH, JSON.stringify(DEFAULT_TSCONFIG, null, 2));
263
263
  }
264
264
 
265
+ // ---------------------------------------------------------------------------
266
+ // Install types for shared modules (if both registry and resolver exist)
267
+ // ---------------------------------------------------------------------------
268
+
269
+ if (sharedModuleRegistry && typesResolver) {
270
+ const sharedModuleIds = sharedModuleRegistry.list();
271
+
272
+ // Resolve types for all shared modules in parallel
273
+ const typesFetches = sharedModuleIds.map(async (moduleId) => {
274
+ try {
275
+ const typeFiles = await typesResolver.resolveTypes(moduleId);
276
+ return { moduleId, typeFiles, error: null };
277
+ } catch (err) {
278
+ // Log but don't fail - types are nice to have but not required
279
+ console.warn(`[sandlot] Failed to fetch types for shared module "${moduleId}":`, err);
280
+ return { moduleId, typeFiles: {}, error: err };
281
+ }
282
+ });
283
+
284
+ const results = await Promise.all(typesFetches);
285
+
286
+ // Write type files to the filesystem
287
+ for (const { moduleId, typeFiles } of results) {
288
+ if (Object.keys(typeFiles).length === 0) continue;
289
+
290
+ // Determine the package name (strip subpath for @types resolution)
291
+ // e.g., "react-dom/client" -> "react-dom"
292
+ let packageName = moduleId;
293
+ let subpath: string | undefined;
294
+
295
+ if (moduleId.startsWith("@")) {
296
+ const parts = moduleId.split("/");
297
+ if (parts.length >= 2) {
298
+ packageName = `${parts[0]}/${parts[1]}`;
299
+ subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
300
+ }
301
+ } else {
302
+ const slashIndex = moduleId.indexOf("/");
303
+ if (slashIndex !== -1) {
304
+ packageName = moduleId.slice(0, slashIndex);
305
+ subpath = moduleId.slice(slashIndex + 1);
306
+ }
307
+ }
308
+
309
+ const packageDir = `/node_modules/${packageName}`;
310
+ let typesEntry: string | null = null;
311
+ let fallbackEntry: string | null = null;
312
+
313
+ for (const [filePath, content] of Object.entries(typeFiles)) {
314
+ const fullPath = filePath.startsWith("/")
315
+ ? filePath
316
+ : `${packageDir}/${filePath}`;
317
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
318
+ ensureDir(fs, dir);
319
+ fs.writeFile(fullPath, content);
320
+
321
+ // Track types entry: prefer index.d.ts, fallback to first top-level .d.ts
322
+ const relativePath = fullPath.replace(`${packageDir}/`, "");
323
+ if (relativePath === "index.d.ts") {
324
+ typesEntry = "index.d.ts";
325
+ } else if (!fallbackEntry && relativePath.endsWith(".d.ts") && !relativePath.includes("/")) {
326
+ fallbackEntry = relativePath;
327
+ }
328
+ }
329
+
330
+ // Use index.d.ts if found, otherwise use fallback
331
+ const finalTypesEntry = typesEntry ?? fallbackEntry ?? "index.d.ts";
332
+
333
+ // Create package.json if it doesn't exist yet
334
+ const pkgJsonPath = `${packageDir}/package.json`;
335
+ if (!fs.exists(pkgJsonPath)) {
336
+ const pkgJson = {
337
+ name: packageName,
338
+ version: "shared",
339
+ types: finalTypesEntry,
340
+ main: finalTypesEntry.replace(/\.d\.ts$/, ".js"),
341
+ };
342
+ fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
343
+ }
344
+ }
345
+ }
346
+
265
347
  // ---------------------------------------------------------------------------
266
348
  // Core Methods
267
349
  // ---------------------------------------------------------------------------
@@ -6,18 +6,8 @@
6
6
  */
7
7
 
8
8
  import type * as EsbuildTypes from "esbuild";
9
- import type {
10
- IBundler,
11
- BundleOptions,
12
- BundleResult,
13
- BundleWarning,
14
- BundleError,
15
- } from "../types";
16
- import {
17
- createVfsPlugin,
18
- isEsbuildBuildFailure,
19
- convertEsbuildMessage,
20
- } from "../core/bundler-utils";
9
+ import type { IBundler, BundleOptions, BundleResult } from "../types";
10
+ import { executeBundleWithEsbuild } from "../core/bundler-utils";
21
11
 
22
12
  export interface EsbuildNativeBundlerOptions {
23
13
  /**
@@ -90,108 +80,14 @@ export class EsbuildNativeBundler implements IBundler {
90
80
  async bundle(options: BundleOptions): Promise<BundleResult> {
91
81
  await this.initialize();
92
82
 
93
- const esbuild = this.getEsbuild();
94
-
95
- const {
96
- fs,
97
- entryPoint,
98
- installedPackages = {},
99
- sharedModules = [],
100
- sharedModuleRegistry,
101
- external = [],
102
- format = "esm",
103
- minify = false,
104
- sourcemap = false,
105
- target = ["es2020"],
106
- } = options;
107
-
108
- // Normalize entry point to absolute path
109
- const normalizedEntry = entryPoint.startsWith("/")
110
- ? entryPoint
111
- : `/${entryPoint}`;
112
-
113
- // Verify entry point exists
114
- if (!fs.exists(normalizedEntry)) {
115
- return {
116
- success: false,
117
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
118
- warnings: [],
119
- };
120
- }
121
-
122
- // Track files included in the bundle
123
- const includedFiles = new Set<string>();
124
-
125
- // Create the VFS plugin
126
- // Note: bundleCdnImports is true for Node/Bun because they cannot
83
+ // bundleCdnImports is true for Node/Bun because they cannot
127
84
  // resolve HTTP imports at runtime - native esbuild will fetch and bundle them
128
- const plugin = createVfsPlugin({
129
- fs,
130
- entryPoint: normalizedEntry,
131
- installedPackages,
132
- sharedModules: new Set(sharedModules),
133
- sharedModuleRegistry: sharedModuleRegistry ?? null,
85
+ return executeBundleWithEsbuild({
86
+ esbuild: this.getEsbuild(),
87
+ bundleOptions: options,
134
88
  cdnBaseUrl: this.options.cdnBaseUrl!,
135
- includedFiles,
136
89
  bundleCdnImports: true,
137
90
  });
138
-
139
- try {
140
- // Run esbuild
141
- // Note: We do NOT mark http/https as external here because Node/Bun
142
- // cannot resolve HTTP imports at runtime. Instead, bundleCdnImports: true
143
- // tells the VFS plugin to let native esbuild fetch and bundle CDN imports.
144
- const result = await esbuild.build({
145
- entryPoints: [normalizedEntry],
146
- bundle: true,
147
- write: false,
148
- format,
149
- minify,
150
- sourcemap: sourcemap ? "inline" : false,
151
- target,
152
- external,
153
- // Cast to esbuild's Plugin type since our minimal interface is compatible
154
- plugins: [plugin as EsbuildTypes.Plugin],
155
- jsx: "automatic",
156
- });
157
-
158
- const code = result.outputFiles?.[0]?.text ?? "";
159
-
160
- // Convert esbuild warnings to our format
161
- const warnings: BundleWarning[] = result.warnings.map((w) =>
162
- convertEsbuildMessage(w)
163
- );
164
-
165
- return {
166
- success: true,
167
- code,
168
- warnings,
169
- includedFiles: Array.from(includedFiles),
170
- };
171
- } catch (err) {
172
- // esbuild throws BuildFailure with .errors array
173
- if (isEsbuildBuildFailure(err)) {
174
- const errors: BundleError[] = err.errors.map((e) =>
175
- convertEsbuildMessage(e)
176
- );
177
- const warnings: BundleWarning[] = err.warnings.map((w) =>
178
- convertEsbuildMessage(w)
179
- );
180
- return {
181
- success: false,
182
- errors,
183
- warnings,
184
- };
185
- }
186
-
187
- // Unknown error - wrap it
188
- const message = err instanceof Error ? err.message : String(err);
189
- return {
190
- success: false,
191
- errors: [{ text: message }],
192
- warnings: [],
193
- };
194
- }
195
91
  }
196
92
  }
197
93
 
@@ -12,18 +12,8 @@
12
12
  */
13
13
 
14
14
  import type * as EsbuildTypes from "esbuild-wasm";
15
- import type {
16
- IBundler,
17
- BundleOptions,
18
- BundleResult,
19
- BundleWarning,
20
- BundleError,
21
- } from "../types";
22
- import {
23
- createVfsPlugin,
24
- isEsbuildBuildFailure,
25
- convertEsbuildMessage,
26
- } from "../core/bundler-utils";
15
+ import type { IBundler, BundleOptions, BundleResult } from "../types";
16
+ import { executeBundleWithEsbuild } from "../core/bundler-utils";
27
17
 
28
18
  // =============================================================================
29
19
  // Global Singleton for esbuild-wasm initialization
@@ -181,108 +171,14 @@ export class EsbuildWasmNodeBundler implements IBundler {
181
171
  async bundle(options: BundleOptions): Promise<BundleResult> {
182
172
  await this.initialize();
183
173
 
184
- const esbuild = this.getEsbuild();
185
-
186
- const {
187
- fs,
188
- entryPoint,
189
- installedPackages = {},
190
- sharedModules = [],
191
- sharedModuleRegistry,
192
- external = [],
193
- format = "esm",
194
- minify = false,
195
- sourcemap = false,
196
- target = ["es2020"],
197
- } = options;
198
-
199
- // Normalize entry point to absolute path
200
- const normalizedEntry = entryPoint.startsWith("/")
201
- ? entryPoint
202
- : `/${entryPoint}`;
203
-
204
- // Verify entry point exists
205
- if (!fs.exists(normalizedEntry)) {
206
- return {
207
- success: false,
208
- errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
209
- warnings: [],
210
- };
211
- }
212
-
213
- // Track files included in the bundle
214
- const includedFiles = new Set<string>();
215
-
216
- // Create the VFS plugin
217
- // Note: bundleCdnImports is true for Node/Bun because they cannot
174
+ // bundleCdnImports is true for Node/Bun because they cannot
218
175
  // resolve HTTP imports at runtime - esbuild will fetch and bundle them
219
- const plugin = createVfsPlugin({
220
- fs,
221
- entryPoint: normalizedEntry,
222
- installedPackages,
223
- sharedModules: new Set(sharedModules),
224
- sharedModuleRegistry: sharedModuleRegistry ?? null,
176
+ return executeBundleWithEsbuild({
177
+ esbuild: this.getEsbuild(),
178
+ bundleOptions: options,
225
179
  cdnBaseUrl: this.options.cdnBaseUrl!,
226
- includedFiles,
227
180
  bundleCdnImports: true,
228
181
  });
229
-
230
- try {
231
- // Run esbuild
232
- // Note: We do NOT mark http/https as external here because Node/Bun
233
- // cannot resolve HTTP imports at runtime. Instead, bundleCdnImports: true
234
- // tells the VFS plugin to let esbuild fetch and bundle CDN imports.
235
- const result = await esbuild.build({
236
- entryPoints: [normalizedEntry],
237
- bundle: true,
238
- write: false,
239
- format,
240
- minify,
241
- sourcemap: sourcemap ? "inline" : false,
242
- target,
243
- external,
244
- // Cast to esbuild's Plugin type since our minimal interface is compatible
245
- plugins: [plugin as EsbuildTypes.Plugin],
246
- jsx: "automatic",
247
- });
248
-
249
- const code = result.outputFiles?.[0]?.text ?? "";
250
-
251
- // Convert esbuild warnings to our format
252
- const warnings: BundleWarning[] = result.warnings.map((w) =>
253
- convertEsbuildMessage(w)
254
- );
255
-
256
- return {
257
- success: true,
258
- code,
259
- warnings,
260
- includedFiles: Array.from(includedFiles),
261
- };
262
- } catch (err) {
263
- // esbuild throws BuildFailure with .errors array
264
- if (isEsbuildBuildFailure(err)) {
265
- const errors: BundleError[] = err.errors.map((e) =>
266
- convertEsbuildMessage(e)
267
- );
268
- const warnings: BundleWarning[] = err.warnings.map((w) =>
269
- convertEsbuildMessage(w)
270
- );
271
- return {
272
- success: false,
273
- errors,
274
- warnings,
275
- };
276
- }
277
-
278
- // Unknown error - wrap it
279
- const message = err instanceof Error ? err.message : String(err);
280
- return {
281
- success: false,
282
- errors: [{ text: message }],
283
- warnings: [],
284
- };
285
- }
286
182
  }
287
183
  }
288
184