autodetect js module type - only bundle esm (#5515)

* wip: autodetect esm

* finish fixes

* thread minify through

* add sweetalert2

* Fix default bundle types

* ts-ignore

* small nits

* small more nits

* shrink flamegraph
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2026-04-30 17:50:37 -07:00
1 parent 2f40780ce0
commit 218bb9d56d
24 files changed
+1598 -616

No files matched your search

@@ -63,4 +63,108 @@ for (let { port, name } of test_variants) {
);
});
// Regression coverage for https://github.com/DioxusLabs/dioxus/issues/5512.
// Both files are vanilla IIFE scripts injected via `with_static_head(true)`.
// The 0.7.4–0.7.6 esbuild path wrapped them in a wrapper IIFE / `--format=esm`,
// which dropped the global side-effect and produced `export` syntax under a
// classic `<script>` tag. The asserted globals only get set if the file is
// delivered as a classic script with its body intact.
test(`classic js asset with_minify(false) is copied byte-for-byte in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__iife_classic_value);
expect(value).toBe("ok-classic");
});
test(`classic js asset with_minify(true) stays a classic script in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__iife_minify_value);
expect(value).toBe("ok-minify");
const classicScript = page.locator('script[src*="iife_minify"]');
await expect(classicScript).toHaveCount(1);
await expect(classicScript).not.toHaveAttribute("type", "module");
});
test(`umd js asset is minified without being converted to a module in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__umd_minify_value);
expect(value).toBe("ok-umd");
const classicScript = page.locator('script[src*="umd_minify"]');
await expect(classicScript).toHaveCount(1);
await expect(classicScript).not.toHaveAttribute("type", "module");
});
test(`cjs js asset is treated as a classic script in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__cjs_classic_value);
expect(value).toBe("ok-cjs");
const classicScript = page.locator('script[src*="cjs_classic"]');
await expect(classicScript).toHaveCount(1);
await expect(classicScript).not.toHaveAttribute("type", "module");
});
test(`sweetalert2 UMD bundle from issue 5512 is copied and exported globally in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => ({
// @ts-ignore
hasGlobal: typeof window.Sweetalert2 === "function",
// @ts-ignore
hasFire: typeof window.Sweetalert2?.fire === "function",
}));
expect(value).toEqual({ hasGlobal: true, hasFire: true });
const classicScript = page.locator('script[src*="sweetalert2.all.min"]');
await expect(classicScript).toHaveCount(1);
await expect(classicScript).not.toHaveAttribute("type", "module");
});
// `with_module(true)` should emit `<script type="module">` and preserve
// module syntax during minification. The fixture uses `import.meta.url`,
// which only parses when the script tag has `type="module"`.
test(`js asset with_module(true) is loaded as an ES module in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__esm_module_value);
expect(value).toBe("ok-module");
const moduleScript = page.locator('script[type="module"][src*="esm_module"]');
await expect(moduleScript).toHaveCount(1);
});
// Auto-detection: the CLI's has_module_syntax scan should recognise top-level
// `export` and emit `<script type="module">` even without `with_module(true)`.
test(`js asset with top-level export is auto-detected as ES module in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__esm_auto_value);
expect(value).toBe("ok-auto");
// import.meta only parses inside a real module; if the script were emitted
// as classic the file would have errored at parse time.
// @ts-ignore
const metaType = await page.evaluate(() => window.__esm_auto_meta);
expect(metaType).toBe("string");
const moduleScript = page.locator('script[type="module"][src*="esm_auto"]');
await expect(moduleScript).toHaveCount(1);
});
test(`js asset with relative ESM import is bundled as an ES module in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__esm_import_value);
expect(value).toBe("ok-esm-import");
const moduleScript = page.locator('script[type="module"][src*="esm_import_entry"]');
await expect(moduleScript).toHaveCount(1);
});
test(`mjs extension is loaded as an ES module in ${name}`, async ({ page }) => {
await page.goto(`http://localhost:${port}`);
// @ts-ignore
const value = await page.evaluate(() => window.__mjs_auto_value);
expect(value).toBe("ok-mjs");
const moduleScript = page.locator('script[type="module"][src*="mjs_auto"]');
await expect(moduleScript).toHaveCount(1);
});
}