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:
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);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
(function () {
|
||||
var module = { exports: {} };
|
||||
var exports = module.exports;
|
||||
exports.value = "ok-cjs";
|
||||
window.__cjs_classic_value = module.exports.value;
|
||||
})();
|
||||
@@ -0,0 +1,7 @@
|
||||
// No `with_module(true)` is set on the asset — the CLI must detect this file
|
||||
// as ESM from its source (top-level `export`) and emit `<script type="module">`
|
||||
// automatically. The `import.meta.url` reference would be a syntax error if the
|
||||
// script tag were a classic <script>.
|
||||
export const value = "ok-auto";
|
||||
window.__esm_auto_value = value;
|
||||
window.__esm_auto_meta = typeof import.meta.url;
|
||||
@@ -0,0 +1 @@
|
||||
export const importedValue = "ok-esm-import";
|
||||
@@ -0,0 +1,4 @@
|
||||
import { importedValue } from "./esm_import_dep.js";
|
||||
|
||||
window.__esm_import_value = importedValue;
|
||||
export const entryValue = importedValue;
|
||||
@@ -0,0 +1,6 @@
|
||||
// No top-level import/export — auto-detection alone would classify this as a
|
||||
// classic script. `with_module(true)` on the asset overrides that and forces
|
||||
// the CLI to emit `<script type="module">` and run esbuild with the ESM
|
||||
// pipeline. The playwright spec asserts both that the global is set and that
|
||||
// the script tag actually has `type="module"`.
|
||||
window.__esm_module_value = "ok-module";
|
||||
@@ -0,0 +1,3 @@
|
||||
(function () {
|
||||
window.__iife_classic_value = "ok-classic";
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
(function () {
|
||||
var value = "ok-minify";
|
||||
window.__iife_minify_value = value;
|
||||
})();
|
||||
@@ -0,0 +1 @@
|
||||
window.__mjs_auto_value = typeof import.meta.url === "string" ? "ok-mjs" : "bad-mjs";
|
||||
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* sweetalert2 v11.22.2
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Sweetalert2=t()}(this,function(){"use strict";function e(e,t,n){if("function"==typeof e?e===t:e.has(t))return arguments.length<3?t:n;throw new TypeError("Private element is not present on this object")}function t(t,n){return t.get(e(t,n))}function n(e,t,n){(function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")})(e,t),t.set(e,n)}const o={},i=e=>new Promise(t=>{if(!e)return t();const n=window.scrollX,i=window.scrollY;o.restoreFocusTimeout=setTimeout(()=>{o.previousActiveElement instanceof HTMLElement?(o.previousActiveElement.focus(),o.previousActiveElement=null):document.body&&document.body.focus(),t()},100),window.scrollTo(n,i)}),s="swal2-",r=["container","shown","height-auto","iosfix","popup","modal","no-backdrop","no-transition","toast","toast-shown","show","hide","close","title","html-container","actions","confirm","deny","cancel","footer","icon","icon-content","image","input","file","range","select","radio","checkbox","label","textarea","inputerror","input-label","validation-message","progress-steps","active-progress-step","progress-step","progress-step-line","loader","loading","styled","top","top-start","top-end","top-left","top-right","center","center-start","center-end","center-left","center-right","bottom","bottom-start","bottom-end","bottom-left","bottom-right","grow-row","grow-column","grow-fullscreen","rtl","timer-progress-bar","timer-progress-bar-container","scrollbar-measure","icon-success","icon-warning","icon-info","icon-question","icon-error","draggable","dragging"].reduce((e,t)=>(e[t]=s+t,e),{}),a=["success","warning","info","question","error"].reduce((e,t)=>(e[t]=s+t,e),{}),l="SweetAlert2:",c=e=>e.charAt(0).toUpperCase()+e.slice(1),u=e=>{console.warn(`${l} ${"object"==typeof e?e.join(" "):e}`)},d=e=>{console.error(`${l} ${e}`)},p=[],m=(e,t=null)=>{var n;n=`"${e}" is deprecated and will be removed in the next major release.${t?` Use "${t}" instead.`:""}`,p.includes(n)||(p.push(n),u(n))},h=e=>"function"==typeof e?e():e,g=e=>e&&"function"==typeof e.toPromise,f=e=>g(e)?e.toPromise():Promise.resolve(e),b=e=>e&&Promise.resolve(e)===e,y=()=>document.body.querySelector(`.${r.container}`),v=e=>{const t=y();return t?t.querySelector(e):null},w=e=>v(`.${e}`),C=()=>w(r.popup),A=()=>w(r.icon),E=()=>w(r.title),k=()=>w(r["html-container"]),$=()=>w(r.image),B=()=>w(r["progress-steps"]),L=()=>w(r["validation-message"]),P=()=>v(`.${r.actions} .${r.confirm}`),x=()=>v(`.${r.actions} .${r.cancel}`),T=()=>v(`.${r.actions} .${r.deny}`),S=()=>v(`.${r.loader}`),O=()=>w(r.actions),M=()=>w(r.footer),j=()=>w(r["timer-progress-bar"]),H=()=>w(r.close),I=()=>{const e=C();if(!e)return[];const t=e.querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])'),n=Array.from(t).sort((e,t)=>{const n=parseInt(e.getAttribute("tabindex")||"0"),o=parseInt(t.getAttribute("tabindex")||"0");return n>o?1:n<o?-1:0}),o=e.querySelectorAll('\n a[href],\n area[href],\n input:not([disabled]),\n select:not([disabled]),\n textarea:not([disabled]),\n button:not([disabled]),\n iframe,\n object,\n embed,\n [tabindex="0"],\n [contenteditable],\n audio[controls],\n video[controls],\n summary\n'),i=Array.from(o).filter(e=>"-1"!==e.getAttribute("tabindex"));return[...new Set(n.concat(i))].filter(e=>ee(e))},D=()=>N(document.body,r.shown)&&!N(document.body,r["toast-shown"])&&!N(document.body,r["no-backdrop"]),V=()=>{const e=C();return!!e&&N(e,r.toast)},q=(e,t)=>{if(e.textContent="",t){const n=(new DOMParser).parseFromString(t,"text/html"),o=n.querySelector("head");o&&Array.from(o.childNodes).forEach(t=>{e.appendChild(t)});const i=n.querySelector("body");i&&Array.from(i.childNodes).forEach(t=>{t instanceof HTMLVideoElement||t instanceof HTMLAudioElement?e.appendChild(t.cloneNode(!0)):e.appendChild(t)})}},N=(e,t)=>{if(!t)return!1;const n=t.split(/\s+/);for(let t=0;t<n.length;t++)if(!e.classList.contains(n[t]))return!1;return!0},_=(e,t,n)=>{if(((e,t)=>{Array.from(e.classList).forEach(n=>{Object.values(r).includes(n)||Object.values(a).includes(n)||Object.values(t.showClass||{}).includes(n)||e.classList.remove(n)})})(e,t),!t.customClass)return;const o=t.customClass[n];o&&("string"==typeof o||o.forEach?z(e,o):u(`Invalid type of customClass.${n}! Expected string or iterable object, got "${typeof o}"`))},F=(e,t)=>{if(!t)return null;switch(t){case"select":case"textarea":case"file":return e.querySelector(`.${r.popup} > .${r[t]}`);case"checkbox":return e.querySelector(`.${r.popup} > .${r.checkbox} input`);case"radio":return e.querySelector(`.${r.popup} > .${r.radio} input:checked`)||e.querySelector(`.${r.popup} > .${r.radio} input:first-child`);case"range":return e.querySelector(`.${r.popup} > .${r.range} input`);default:return e.querySelector(`.${r.popup} > .${r.input}`)}},R=e=>{if(e.focus(),"filLine truncated
|
||||
"undefined"!=typeof document&&function(e,t){var n=e.createElement("style");if(e.getElementsByTagName("head")[0].appendChild(n),n.styleSheet)n.styleSheet.disabled||(n.styleSheet.cssText=t);else try{n.innerHTML=t}catch(e){n.innerText=t}}(document,":root{--swal2-outline: 0 0 0 3px rgba(100, 150, 200, 0.5);--swal2-container-padding: 0.625em;--swal2-backdrop: rgba(0, 0, 0, 0.4);--swal2-backdrop-transition: background-color 0.1s;--swal2-width: 32em;--swal2-padding: 0 0 1.25em;--swal2-border: none;--swal2-border-radius: 0.3125rem;--swal2-background: white;--swal2-color: #545454;--swal2-show-animation: swal2-show 0.3s;--swal2-hide-animation: swal2-hide 0.15s forwards;--swal2-icon-zoom: 1;--swal2-icon-animations: true;--swal2-title-padding: 0.8em 1em 0;--swal2-html-container-padding: 1em 1.6em 0.3em;--swal2-input-border: 1px solid #d9d9d9;--swal2-input-border-radius: 0.1875em;--swal2-input-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-background: transparent;--swal2-input-transition: border-color 0.2s, box-shadow 0.2s;--swal2-input-hover-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-focus-border: 1px solid #b4dbed;--swal2-input-focus-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px $swal2-outline-color;--swal2-progress-step-background: #add8e6;--swal2-validation-message-background: #f0f0f0;--swal2-validation-message-color: #666;--swal2-footer-border-color: #eee;--swal2-footer-background: transparent;--swal2-footer-color: inherit;--swal2-close-button-position: initial;--swal2-close-button-inset: auto;--swal2-close-button-font-size: 2.5em;--swal2-close-button-color: #ccc;--swal2-close-button-transition: color 0.2s, box-shadow 0.2s;--swal2-close-button-outline: initial;--swal2-close-button-box-shadow: inset 0 0 0 3px transparent;--swal2-close-button-focus-box-shadow: inset var(--swal2-outline);--swal2-close-button-hover-transform: none;--swal2-actions-justify-content: center;--swal2-actions-width: auto;--swal2-actions-margin: 1.25em auto 0;--swal2-actions-padding: 0;--swal2-actions-border-radius: 0;--swal2-actions-background: transparent;--swal2-action-button-transition: background-color 0.2s, box-shadow 0.2s;--swal2-action-button-hover: black 10%;--swal2-action-button-active: black 10%;--swal2-confirm-button-box-shadow: none;--swal2-confirm-button-border-radius: 0.25em;--swal2-confirm-button-background-color: #7066e0;--swal2-confirm-button-color: #fff;--swal2-deny-button-box-shadow: none;--swal2-deny-button-border-radius: 0.25em;--swal2-deny-button-background-color: #dc3741;--swal2-deny-button-color: #fff;--swal2-cancel-button-box-shadow: none;--swal2-cancel-button-border-radius: 0.25em;--swal2-cancel-button-background-color: #6e7881;--swal2-cancel-button-color: #fff;--swal2-toast-show-animation: swal2-toast-show 0.5s;--swal2-toast-hide-animation: swal2-toast-hide 0.1s forwards;--swal2-toast-border: none;--swal2-toast-box-shadow: 0 0 1px hsl(0deg 0% 0% / 0.075), 0 1px 2px hsl(0deg 0% 0% / 0.075), 1px 2px 4px hsl(0deg 0% 0% / 0.075), 1px 3px 8px hsl(0deg 0% 0% / 0.075), 2px 4px 16px hsl(0deg 0% 0% / 0.075)}[data-swal2-theme=dark]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white)}@media(prefers-color-scheme: dark){[data-swal2-theme=auto]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white)}}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown){overflow:hidden}body.swal2-height-auto{height:auto !important}body.swal2-no-backdrop .swal2-container{background-color:rgba(0,0,0,0) !important;pointer-events:none}body.swal2-no-backdrop .swal2-container .swal2-popup{pointer-events:all}body.swal2-no-backdrop .swal2-container .swal2-modal{box-shadow:0 0 10px var(--swal2-backdrop)}body.swal2-toast-shown .swal2-container{box-sizing:border-box;width:360px;max-width:100%;background-color:rgba(0,0,0,0);pointer-events:none}body.swal2-toast-shown .swal2-container.swal2-top{inset:0 auto auto 50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-top-end,body.swal2-toast-shown .swal2-container.swal2-top-right{inset:0 0 auto auto}body.swal2-toast-showLine truncated
|
||||
@@ -0,0 +1,14 @@
|
||||
(function (root, factory) {
|
||||
if (typeof exports === "object" && typeof module === "object") {
|
||||
root.__umd_minify_value = (module.exports = factory()).value;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
define(function () {
|
||||
root.__umd_minify_value = factory().value;
|
||||
return factory();
|
||||
});
|
||||
} else {
|
||||
root.__umd_minify_value = factory().value;
|
||||
}
|
||||
})(this, function () {
|
||||
return { value: "ok-umd" };
|
||||
});
|
||||
@@ -17,6 +17,65 @@ static _ASSET: Asset = asset!(
|
||||
AssetOptions::builder().with_hash_suffix(false)
|
||||
);
|
||||
|
||||
// Regression coverage for https://github.com/DioxusLabs/dioxus/issues/5512:
|
||||
// `with_minify(false)` must copy the file byte-for-byte (no IIFE rewrap, no
|
||||
// ESM transform), and `with_minify(true)` must keep the script as a classic
|
||||
// script (not ESM) so the `<script>` tag without `type="module"` still runs.
|
||||
#[used]
|
||||
static _IIFE_CLASSIC_JS: Asset = asset!(
|
||||
"/assets/iife_classic.js",
|
||||
AssetOptions::js().with_minify(false).with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _IIFE_MINIFY_JS: Asset = asset!(
|
||||
"/assets/iife_minify.js",
|
||||
AssetOptions::js().with_minify(true).with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _UMD_MINIFY_JS: Asset = asset!(
|
||||
"/assets/umd_minify.js",
|
||||
AssetOptions::js().with_minify(true).with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _CJS_CLASSIC_JS: Asset = asset!(
|
||||
"/assets/cjs_classic.cjs",
|
||||
AssetOptions::js().with_minify(true).with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _SWEETALERT2_JS: Asset = asset!(
|
||||
"/assets/sweetalert2.all.min.js",
|
||||
AssetOptions::js().with_minify(false).with_static_head(true)
|
||||
);
|
||||
|
||||
// Coverage for `with_module(true)`: the script tag must be emitted as
|
||||
// `<script type="module">` and minification must preserve module syntax so
|
||||
// the browser can parse the file as ESM.
|
||||
#[used]
|
||||
static _ESM_MODULE_JS: Asset = asset!(
|
||||
"/assets/esm_module.js",
|
||||
AssetOptions::js().with_module(true).with_static_head(true)
|
||||
);
|
||||
|
||||
// Coverage for module auto-detection: this asset has no `with_module(true)`
|
||||
// override, but its source contains a top-level `export`, so the CLI's
|
||||
// has_module_syntax scan must classify it as ESM and emit the script tag with
|
||||
// `type="module"`. Confirms detection works without explicit user opt-in.
|
||||
#[used]
|
||||
static _ESM_AUTO_JS: Asset = asset!(
|
||||
"/assets/esm_auto.js",
|
||||
AssetOptions::js().with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _ESM_IMPORT_ENTRY_JS: Asset = asset!(
|
||||
"/assets/esm_import_entry.js",
|
||||
AssetOptions::js().with_static_head(true)
|
||||
);
|
||||
#[used]
|
||||
static _MJS_AUTO_JS: Asset = asset!(
|
||||
"/assets/mjs_auto.mjs",
|
||||
AssetOptions::js().with_static_head(true)
|
||||
);
|
||||
|
||||
pub fn main() {
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user