diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d65119..e9b833b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - Write the virtual HTML/style/script loader modules at the package's resolved on-disk root instead of a path relative to the webpack compiler context, so the bare `@studiometa/playground/-loader.js` imports resolve. These specifiers go through the package's `"./*": "./*"` exports entry, and since enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399) a matched exports target that is missing on disk is a hard error instead of falling back to legacy resolution — so consumer builds on webpack 5.109+ failed with `Package path ./html-loader.js is exported from package ... but no valid target file was found` ([#77](https://github.com/studiometa/playground/pull/77), [d0c924a](https://github.com/studiometa/playground/commit/d0c924a)) +- Load `esbuild-wasm` from esm.sh at runtime (mirroring `modern-monaco`) instead of bundling it through webpack. The bare-specifier `new URL('esbuild-wasm/esbuild.wasm', import.meta.url)` is no longer resolved to a real asset URL by webpack 5.109+, so the `wasmURL` became `.../[object Object]` (404), esbuild failed to initialize with a cryptic `g[e] is not a function`, and the preview never ran the compiled script. This also removes the ~11 MB wasm and the esbuild chunk from consumer bundles ([#78](https://github.com/studiometa/playground/pull/78), [89b55ae](https://github.com/studiometa/playground/commit/89b55ae)) ## v0.3.13 - 2026.08.06 diff --git a/packages/playground/scripts/shared.js b/packages/playground/scripts/shared.js index 12c1a75..aa3299c 100644 --- a/packages/playground/scripts/shared.js +++ b/packages/playground/scripts/shared.js @@ -17,6 +17,13 @@ function getOptions() { const modernMonacoPkg = JSON.parse(readFileSync(modernMonacoPkgPath, 'utf-8')); const modernMonacoVersion = modernMonacoPkg.version; + // Read the installed esbuild-wasm version to inline it at build time. + // The front-end loads esbuild-wasm from esm.sh at runtime (like modern-monaco). + const esbuildWasmEntry = new URL(import.meta.resolve('esbuild-wasm')); + const esbuildWasmPkgPath = resolve(esbuildWasmEntry.pathname, '../../package.json'); + const esbuildWasmPkg = JSON.parse(readFileSync(esbuildWasmPkgPath, 'utf-8')); + const esbuildWasmVersion = esbuildWasmPkg.version; + return { entryPoints: glob.globSync( ['src/**/*.ts', 'src/**/*.twig', 'src/**/*.json', '!src/**/*.test.ts'], @@ -31,6 +38,7 @@ function getOptions() { sourcemap: true, define: { __MODERN_MONACO_VERSION__: JSON.stringify(modernMonacoVersion), + __ESBUILD_WASM_VERSION__: JSON.stringify(esbuildWasmVersion), }, loader: { '.twig': 'copy', diff --git a/packages/playground/src/front/js/components/Iframe.ts b/packages/playground/src/front/js/components/Iframe.ts index dbe7579..2293983 100644 --- a/packages/playground/src/front/js/components/Iframe.ts +++ b/packages/playground/src/front/js/components/Iframe.ts @@ -13,6 +13,13 @@ import { createPatchedUrl } from '../utils/patch-iframe-url.js'; type esbuildType = typeof import('esbuild-wasm'); +/** + * Installed esbuild-wasm version, inlined at build time by esbuild. + * Used to load the dependency from esm.sh at runtime (like modern-monaco), + * instead of bundling it through webpack. + */ +declare const __ESBUILD_WASM_VERSION__: string; + export interface IframeProps extends BaseProps { $refs: { iframe: HTMLIFrameElement; @@ -139,9 +146,17 @@ ${html} Iframe.esbuildPromise = Promise.withResolvers(); - const esbuild = await import('esbuild-wasm'); + // Load esbuild-wasm from esm.sh at runtime rather than bundling it through + // webpack. `new URL('esbuild-wasm/esbuild.wasm', import.meta.url)` (a bare + // specifier) is not resolved to a real asset URL by webpack 5.109+, so the + // wasmURL became `.../[object Object]` and esbuild failed to initialize. This + // mirrors how modern-monaco is loaded, and keeps the ~11 MB wasm out of the + // consumer bundle. esm.sh serves the CJS default export, so unwrap `.default`. + const esbuildUrl = `https://esm.sh/esbuild-wasm@${__ESBUILD_WASM_VERSION__}`; + const mod = await import(/* webpackIgnore: true */ esbuildUrl); + const esbuild: esbuildType = mod.default ?? mod; await esbuild.initialize({ - wasmURL: new URL('esbuild-wasm/esbuild.wasm', import.meta.url), + wasmURL: `https://esm.sh/esbuild-wasm@${__ESBUILD_WASM_VERSION__}/esbuild.wasm`, }); Iframe.esbuild = esbuild;