From de21f1e166cdb0e2eb5b7aae051c2f63122d6acf Mon Sep 17 00:00:00 2001 From: Bartek Wrona Date: Mon, 9 Mar 2026 19:41:08 +0000 Subject: [PATCH] Add bundler integration tests verifying EXPORT_ES6 output has no require() Add two tests that verify EXPORT_ES6 output is valid ESM and works with bundlers: - test_webpack_esm_output_clean: Compiles with EXPORT_ES6 and default environment (web+node), then builds with webpack. On main, webpack hard-fails because it cannot resolve 'node:module' (used by emscripten's createRequire polyfill). This breaks any webpack/Next.js/Nuxt project. - test_vite_esm_output_clean: Compiles with EXPORT_ES6 and default environment, then builds with vite. On main, vite externalizes 'node:module' for browser compatibility, emitting a warning. The resulting bundle contains code referencing unavailable node modules. These tests are expected to fail on main and pass after eliminating require() from EXPORT_ES6 output. --- test/test_other.py | 37 +++++++++++++++++++++++++++++++++++++ test/vite/vite.config.js | 12 ++++++++++++ 2 files changed, 49 insertions(+) diff --git a/test/test_other.py b/test/test_other.py index 7ba92eb07b506..31865c25a07e4 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -15028,6 +15028,43 @@ def test_rollup(self): shutil.copy('hello.wasm', 'dist/') self.assertContained('hello, world!', self.run_js('dist/bundle.mjs')) + @crossplatform + @requires_dev_dependency('webpack') + def test_webpack_esm_output_clean(self): + """Verify webpack can build EXPORT_ES6 output without errors. + + When emscripten generates require() in EXPORT_ES6 output (via + createRequire from 'node:module'), webpack fails with: + UnhandledSchemeError: Reading from "node:module" is not handled by plugins + This breaks any webpack/Next.js/Nuxt project using emscripten's ESM output. + """ + copytree(test_file('webpack_es6'), '.') + # Compile with default environment (web+node). On main, this generates + # 'import { createRequire } from "node:module"' and require() calls for + # node support, which webpack cannot resolve for web targets. + self.run_process([EMCC, test_file('hello_world.c'), '-sEXPORT_ES6', '-sEXIT_RUNTIME', + '-sMODULARIZE', '-o', 'src/hello.mjs']) + self.run_process(shared.get_npm_cmd('webpack') + ['--mode=development', '--no-devtool']) + + @crossplatform + @requires_dev_dependency('vite') + def test_vite_esm_output_clean(self): + """Verify vite bundles EXPORT_ES6 output without require() or externalizing. + + When emscripten generates require() in EXPORT_ES6 output, vite externalizes + the node modules for browser compatibility, emitting a warning. The resulting + bundle contains code that references modules unavailable in browsers. + """ + copytree(test_file('vite'), '.') + # Compile with default environment (web+node). On main, this generates + # require() calls for node support which vite externalizes but leaves + # as require() in the bundle output. + self.run_process([EMCC, test_file('hello_world.c'), '-sEXPORT_ES6', '-sEXIT_RUNTIME', + '-sMODULARIZE', '-o', 'hello.mjs']) + # vite.config.js turns externalization warnings into errors, so vite + # will fail with non-zero exit code if require() appears in ESM output. + self.run_process(shared.get_npm_cmd('vite') + ['build']) + def test_rlimit(self): self.do_other_test('test_rlimit.c', cflags=['-O1']) diff --git a/test/vite/vite.config.js b/test/vite/vite.config.js index 019c96058a246..ac7e5529a23fc 100644 --- a/test/vite/vite.config.js +++ b/test/vite/vite.config.js @@ -1,3 +1,15 @@ export default { base: './', + build: { + rollupOptions: { + onwarn(warning, defaultHandler) { + // Treat externalized-for-browser-compatibility warnings as errors. + // This catches require() in ESM output that vite silently externalizes. + if (warning.message && warning.message.includes('externalized for browser compatibility')) { + throw new Error(warning.message); + } + defaultHandler(warning); + }, + }, + }, }