From 2c1cc452e8b2a3c87c41c1629a15853a58b982be Mon Sep 17 00:00:00 2001 From: "zhanglanxiao.zlx" Date: Thu, 7 Sep 2023 14:17:13 +0800 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20hot-update=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=94=BE=E5=88=B0=20node=5Fmodules=20?= =?UTF-8?q?=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/mako/src/generate.rs | 28 +++++++++++++++---- crates/mako/src/runtime/runtime_entry.js | 7 ++--- ...ko__tree_shaking__tests__tree_shaking.snap | 4 +-- ...ee_shaking__tests__tree_shaking_class.snap | 4 +-- ...g__tests__tree_shaking_dynamic_import.snap | 4 +-- ...g__tests__tree_shaking_export_default.snap | 4 +-- ..._tests__tree_shaking_export_namespace.snap | 4 +-- ...shaking__tests__tree_shaking_exported.snap | 4 +-- ..._tree_shaking__tests__tree_shaking_fn.snap | 4 +-- ...aking__tests__tree_shaking_issues_271.snap | 4 +-- ...tree_shaking__tests__tree_shaking_jsx.snap | 4 +-- ...ing__tests__tree_shaking_named_export.snap | 4 +-- ...g__tests__tree_shaking_named_reexport.snap | 4 +-- ...shaking__tests__tree_shaking_reexport.snap | 4 +-- ...king__tests__tree_shaking_side_effect.snap | 4 +-- crates/node/index.d.ts | 6 +++- packages/bundler-okam/index.js | 1 + 17 files changed, 56 insertions(+), 38 deletions(-) diff --git a/crates/mako/src/generate.rs b/crates/mako/src/generate.rs index bd450fb74..7cac0cceb 100644 --- a/crates/mako/src/generate.rs +++ b/crates/mako/src/generate.rs @@ -309,12 +309,15 @@ impl Compiler { let (code, ..) = self.generate_hmr_chunk(chunk, &modified_ids, current_full_hash)?; // TODO the final format should be {name}.{full_hash}.hot-update.{ext} - self.write_to_dist(to_hot_update_chunk_name(chunk_name, last_full_hash), code); + self.write_to_hot_update_dir( + to_hot_update_chunk_name(chunk_name, last_full_hash), + code, + ); } } let t_generate_hmr_chunk = t_generate_hmr_chunk.elapsed(); - self.write_to_dist( + self.write_to_hot_update_dir( format!("{}.hot-update.json", last_full_hash), serde_json::to_string(&HotUpdateManifest { removed_chunks, @@ -342,15 +345,28 @@ impl Compiler { Ok(current_full_hash) } - pub fn write_to_dist, C: AsRef<[u8]>>( + // pub fn write_to_dist, C: AsRef<[u8]>>( + // &self, + // filename: P, + // content: C, + // ) { + // let to = self.context.config.output.path.join(filename); + + // std::fs::write(to, content).unwrap(); + // } + + pub fn write_to_hot_update_dir, C: AsRef<[u8]>>( &self, filename: P, content: C, ) { - let to = self.context.config.output.path.join(filename); - - std::fs::write(to, content).unwrap(); + let hmr_dir = self.context.root.join("node_modules/.mako/hot_update"); + if !hmr_dir.exists() { + fs::create_dir_all(&hmr_dir).unwrap(); + } + std::fs::write(hmr_dir.join(filename), content).unwrap(); } + // 写入产物前记录 content 大小, 并加上 hash 值 pub fn write_to_dist_with_stats(&self, file: EmitFile) { let to: PathBuf = self.context.config.output.path.join(file.hashname.clone()); diff --git a/crates/mako/src/runtime/runtime_entry.js b/crates/mako/src/runtime/runtime_entry.js index f8c63c2c6..5b445aa48 100644 --- a/crates/mako/src/runtime/runtime_entry.js +++ b/crates/mako/src/runtime/runtime_entry.js @@ -172,10 +172,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate() {}, check() { const current_hash = requireModule.currentHash(); - - return fetch( - `${requireModule.publicPath}${current_hash}.hot-update.json`, - ) + return fetch(`/hot_update/${current_hash}.hot-update.json`) .then((res) => { return res.json(); }) @@ -196,7 +193,7 @@ function createRuntime(makoModules, entryModuleId) { ].join('.'); return new Promise((done) => { - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); }), diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking.snap index e09991436..5847b3d54 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_class.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_class.snap index ca6b4c1a9..3d59a515e 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_class.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_class.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_dynamic_import.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_dynamic_import.snap index 15411d5db..bd2e3fbfb 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_dynamic_import.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_dynamic_import.snap @@ -151,7 +151,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -166,7 +166,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_default.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_default.snap index e8f7fc3c3..dc21f07c5 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_default.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_default.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_namespace.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_namespace.snap index 8509cb8cb..4cf32d22b 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_namespace.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_export_namespace.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_exported.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_exported.snap index 35db3cbc3..07de59de5 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_exported.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_exported.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_fn.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_fn.snap index 98adb77e7..9c3d48789 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_fn.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_fn.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_issues_271.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_issues_271.snap index 943ff97fa..8e46b70dd 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_issues_271.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_issues_271.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_jsx.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_jsx.snap index 71e4c8690..ad0cfced4 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_jsx.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_jsx.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_export.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_export.snap index 8fdd67a07..10fe8483c 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_export.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_export.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_reexport.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_reexport.snap index da7821897..cb843bb61 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_reexport.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_named_reexport.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_reexport.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_reexport.snap index 3e0773319..6db57a157 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_reexport.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_reexport.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_side_effect.snap b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_side_effect.snap index a1855e910..070900fd5 100644 --- a/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_side_effect.snap +++ b/crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_side_effect.snap @@ -150,7 +150,7 @@ function createRuntime(makoModules, entryModuleId) { invalidate () {}, check () { const current_hash = requireModule.currentHash(); - return fetch(`${requireModule.publicPath}${current_hash}.hot-update.json`).then((res)=>{ + return fetch(`/hot_update/${current_hash}.hot-update.json`).then((res)=>{ return res.json(); }).then((update)=>{ return Promise.all(update.c.map((chunk)=>{ @@ -165,7 +165,7 @@ function createRuntime(makoModules, entryModuleId) { ext ].join('.'); return new Promise((done)=>{ - const url = `${requireModule.publicPath}${hotChunkName}`; + const url = `hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); })); diff --git a/crates/node/index.d.ts b/crates/node/index.d.ts index ed10bc532..f189e984a 100644 --- a/crates/node/index.d.ts +++ b/crates/node/index.d.ts @@ -12,12 +12,16 @@ alias?: Record; extensions?: string[]; }; manifest?: boolean; -manifest_config?: {file_name: string; base_path: string;}; +manifest_config?: { +file_name: string; +base_path: string; +}; mode?: "development" | "production"; define?: Record; devtool?: "source-map" | "inline-source-map" | "none"; externals?: Record; copy?: string[]; +code_splitting: "bigVendors" | "depPerChunk" | "none"; providers?: Record; public_path?: string; inline_limit?: number; diff --git a/packages/bundler-okam/index.js b/packages/bundler-okam/index.js index 96fe7dcec..b1164e447 100644 --- a/packages/bundler-okam/index.js +++ b/packages/bundler-okam/index.js @@ -59,6 +59,7 @@ exports.dev = async function (opts) { // before middlewares (opts.beforeMiddlewares || []).forEach((m) => app.use(m)); // serve dist files + app.use(express.static(path.join(opts.cwd, 'node_modules/.mako'))); app.use(express.static(path.join(opts.cwd, 'dist'))); // TODO: proxy // opts.config.proxy From b79b5cafb1597940d7cc56cb42eb25cf90b2dde7 Mon Sep 17 00:00:00 2001 From: "zhanglanxiao.zlx" Date: Sun, 10 Sep 2023 22:31:53 +0800 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20hot-update=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=94=BE=E5=88=B0=20node=5Fmodules=20?= =?UTF-8?q?=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/mako/src/dev.rs | 70 ++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/crates/mako/src/dev.rs b/crates/mako/src/dev.rs index 2709de814..626a24caa 100644 --- a/crates/mako/src/dev.rs +++ b/crates/mako/src/dev.rs @@ -1,3 +1,4 @@ +use std::io::Error as IoError; use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; @@ -79,6 +80,43 @@ impl DevServer { let static_serve = hyper_staticfile::Static::new(for_fn.context.config.output.path.clone()); + let static_serve_hmr = + hyper_staticfile::Static::new(for_fn.context.root.join("node_modules/.mako")); + + let get_serve_response = |serve_result: Result< + hyper::Response, + IoError, + >| { + match serve_result { + Ok(mut res) => { + if let Some(content_type) = res.headers().get(CONTENT_TYPE).cloned() { + if let Ok(c_str) = content_type.to_str() { + if c_str.contains("javascript") || c_str.contains("text") { + res.headers_mut() + .insert( + CONTENT_TYPE, + HeaderValue::from_str(&format!( + "{c_str}; charset=utf-8" + )) + .unwrap(), + ) + .unwrap(); + } + } + } + Ok(res) + } + Err(_) => Ok::<_, hyper::Error>( + hyper::Response::builder() + .status(hyper::StatusCode::NOT_FOUND) + .body(hyper::Body::from("404 - Page not found")) + .unwrap(), + ), + } + }; + + println!("req path is /{:?}", path); + match path { "__/hmr-ws" => { if hyper_tungstenite::is_upgrade_request(&req) { @@ -102,35 +140,13 @@ impl DevServer { ) } } + _ if path.starts_with("hot_update") => { + println!("命中了"); + get_serve_response(static_serve_hmr.serve(req).await) + } _ => { // try chunk content in memory first, else use dist content - match static_serve.serve(req).await { - Ok(mut res) => { - if let Some(content_type) = res.headers().get(CONTENT_TYPE).cloned() - { - if let Ok(c_str) = content_type.to_str() { - if c_str.contains("javascript") || c_str.contains("text") { - res.headers_mut() - .insert( - CONTENT_TYPE, - HeaderValue::from_str(&format!( - "{c_str}; charset=utf-8" - )) - .unwrap(), - ) - .unwrap(); - } - } - } - Ok(res) - } - Err(_) => Ok::<_, hyper::Error>( - hyper::Response::builder() - .status(hyper::StatusCode::NOT_FOUND) - .body(hyper::Body::from("404 - Page not found")) - .unwrap(), - ), - } + get_serve_response(static_serve.serve(req).await) } } } From 9840c188d146c13d5114417bb64ca67372209a40 Mon Sep 17 00:00:00 2001 From: "zhanglanxiao.zlx" Date: Sun, 10 Sep 2023 23:19:48 +0800 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20hot-update=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=94=BE=E5=88=B0=20node=5Fmodules=20?= =?UTF-8?q?=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy.yml | 1 + crates/mako/src/build.rs | 13 +- crates/mako/src/compiler.rs | 11 +- crates/mako/src/config.rs | 21 +- crates/mako/src/dev.rs | 5 +- crates/mako/src/generate.rs | 33 +- crates/mako/src/generate_chunks.rs | 4 +- crates/mako/src/load.rs | 1 + crates/mako/src/module.rs | 14 +- crates/mako/src/module_graph.rs | 15 +- crates/mako/src/plugin.rs | 17 +- crates/mako/src/plugins/farm_tree_shake.rs | 22 + .../src/plugins/farm_tree_shake/module.rs | 328 +++++++++++ .../farm_tree_shake/remove_useless_stmts.rs | 191 +++++++ .../mako/src/plugins/farm_tree_shake/shake.rs | 294 ++++++++++ .../farm_tree_shake/statement_graph.rs | 327 +++++++++++ .../analyze_imports_and_exports.rs | 367 ++++++++++++ .../defined_idents_collector.rs | 63 ++ .../statement_graph/used_idents_collector.rs | 22 + crates/mako/src/plugins/javascript.rs | 17 +- crates/mako/src/plugins/mod.rs | 1 + crates/mako/src/plugins/runtime.rs | 168 +++++- crates/mako/src/runtime/runtime_entry.js | 6 +- .../mako__hmr__tests__generate_hmr_chunk.snap | 2 +- ...ko__tree_shaking__tests__tree_shaking.snap | 126 +++- ...ee_shaking__tests__tree_shaking_class.snap | 167 ++++-- ..._tests__tree_shaking_dynamic_import-2.snap | 8 +- ...g__tests__tree_shaking_dynamic_import.snap | 130 ++++- ...g__tests__tree_shaking_export_default.snap | 163 ++++-- ..._tests__tree_shaking_export_namespace.snap | 178 ++++-- ...shaking__tests__tree_shaking_exported.snap | 126 +++- ..._tree_shaking__tests__tree_shaking_fn.snap | 167 ++++-- ...aking__tests__tree_shaking_issues_271.snap | 167 ++++-- ...tree_shaking__tests__tree_shaking_jsx.snap | 151 +++-- ...ing__tests__tree_shaking_named_export.snap | 126 +++- ...g__tests__tree_shaking_named_reexport.snap | 130 ++++- ...shaking__tests__tree_shaking_reexport.snap | 167 ++++-- ...king__tests__tree_shaking_side_effect.snap | 167 ++++-- ...ee_shaking__tests__tree_shaking_style.snap | 541 ++++++++++++++++++ ...ing_module__tests__used_export_test_5.snap | 22 + ...ing_module__tests__used_export_test_6.snap | 20 + .../mako__update__tests__update_multi-2.snap | 2 +- .../mako__update__tests__update_multi-3.snap | 2 +- .../mako__update__tests__update_multi-4.snap | 4 +- .../mako__update__tests__update_multi-5.snap | 2 +- .../mako__update__tests__update_multi-6.snap | 4 +- crates/mako/src/test_helper.rs | 20 +- crates/mako/src/transform.rs | 56 +- crates/mako/src/transform_in_generate.rs | 21 +- crates/mako/src/transform_optimizer.rs | 52 +- crates/mako/src/tree_shaking.rs | 18 +- crates/mako/src/tree_shaking_analyze.rs | 7 +- crates/mako/src/tree_shaking_module.rs | 69 ++- crates/mako/src/update.rs | 3 +- .../test/build/tree-shaking_style/foo.tsx | 3 + .../test/build/tree-shaking_style/index.tsx | 4 + .../build/tree-shaking_style/mako.config.json | 4 + .../test/build/tree-shaking_style/style.css | 3 + examples/interop-fallback/dep.js | 7 + examples/interop-fallback/index.ts | 3 + examples/interop-fallback/mako.config.json | 4 + examples/interop-fallback/proxy.js | 7 + examples/tree-shaking/index.ts | 2 - examples/tree-shaking/mako.config.json | 7 +- examples/with-antd/mako.config.json | 5 +- examples/with-antd/package.json | 11 +- package.json | 2 +- pnpm-lock.yaml | 188 ++++-- 68 files changed, 4424 insertions(+), 585 deletions(-) create mode 100644 crates/mako/src/plugins/farm_tree_shake.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/module.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/remove_useless_stmts.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/shake.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/statement_graph.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/statement_graph/analyze_imports_and_exports.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/statement_graph/defined_idents_collector.rs create mode 100644 crates/mako/src/plugins/farm_tree_shake/statement_graph/used_idents_collector.rs create mode 100644 crates/mako/src/snapshots/mako__tree_shaking__tests__tree_shaking_style.snap create mode 100644 crates/mako/src/snapshots/mako__tree_shaking_module__tests__used_export_test_5.snap create mode 100644 crates/mako/src/snapshots/mako__tree_shaking_module__tests__used_export_test_6.snap create mode 100644 crates/mako/test/build/tree-shaking_style/foo.tsx create mode 100644 crates/mako/test/build/tree-shaking_style/index.tsx create mode 100644 crates/mako/test/build/tree-shaking_style/mako.config.json create mode 100644 crates/mako/test/build/tree-shaking_style/style.css create mode 100644 examples/interop-fallback/dep.js create mode 100644 examples/interop-fallback/index.ts create mode 100644 examples/interop-fallback/mako.config.json create mode 100644 examples/interop-fallback/proxy.js delete mode 100644 examples/tree-shaking/index.ts diff --git a/clippy.yml b/clippy.yml index 829dd1c59..3235fec82 100644 --- a/clippy.yml +++ b/clippy.yml @@ -1 +1,2 @@ msrv = "1.51" +disallowed-lints = ["or_fun_call"] diff --git a/crates/mako/src/build.rs b/crates/mako/src/build.rs index f033f1b20..f0acb7877 100644 --- a/crates/mako/src/build.rs +++ b/crates/mako/src/build.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::analyze_deps::analyze_deps; use crate::ast::{build_js_ast, generate_code_frame}; use crate::compiler::{Compiler, Context}; -use crate::config::Config; +use crate::config::{Config, Mode}; use crate::load::{ext_name, load}; use crate::module::{Dependency, Module, ModuleAst, ModuleId, ModuleInfo}; use crate::parse::parse; @@ -59,8 +59,15 @@ impl Compiler { let resolvers = Arc::new(get_resolvers(&self.context.config)); let mut queue: VecDeque = VecDeque::new(); for entry in entries { + let mut entry = entry.to_str().unwrap().to_string(); + if self.context.config.hmr + && self.context.config.mode == Mode::Development + && self.context.args.watch + { + entry = format!("{}?hmr", entry); + } queue.push_back(Task { - path: entry.to_str().unwrap().to_string(), + path: entry, parent_resource: None, is_entry: true, }); @@ -284,7 +291,7 @@ impl Compiler { let mut ignored_deps = Vec::new(); for dep in deps { - let ret = resolve(&task.path, &dep, &resolvers, &context); + let ret = resolve(&task.path, &dep, &context.resolvers, &context); match ret { Ok(resolved_resource) => { if matches!(resolved_resource, ResolverResource::Ignored) { diff --git a/crates/mako/src/compiler.rs b/crates/mako/src/compiler.rs index e833eb88c..bc48bc742 100644 --- a/crates/mako/src/compiler.rs +++ b/crates/mako/src/compiler.rs @@ -15,6 +15,7 @@ use crate::config::{Config, OutputMode}; use crate::module_graph::ModuleGraph; use crate::plugin::{Plugin, PluginDriver}; use crate::plugins; +use crate::resolve::{get_resolvers, Resolvers}; use crate::stats::StatsInfo; pub struct Context { @@ -28,6 +29,7 @@ pub struct Context { pub meta: Meta, pub plugin_driver: PluginDriver, pub stats_info: Mutex, + pub resolvers: Resolvers, } #[derive(Default)] @@ -37,6 +39,9 @@ pub struct Args { impl Default for Context { fn default() -> Self { + let config: Config = Default::default(); + let resolvers = get_resolvers(&config); + Self { config: Default::default(), args: Args { watch: false }, @@ -49,6 +54,7 @@ impl Default for Context { plugin_driver: Default::default(), // 产物信息放在上下文里是否合适 stats_info: Mutex::new(StatsInfo::new()), + resolvers, } } } @@ -154,6 +160,7 @@ impl Compiler { Arc::new(plugins::yaml::YAMLPlugin {}), Arc::new(plugins::assets::AssetsPlugin {}), Arc::new(plugins::runtime::MakoRuntime {}), + Arc::new(plugins::farm_tree_shake::FarmTreeShake {}), ]; let mut config = config; @@ -174,6 +181,7 @@ impl Compiler { plugin_driver.modify_config(&mut config).unwrap(); + let resolvers = get_resolvers(&config); Self { context: Arc::new(Context { config, @@ -186,6 +194,7 @@ impl Compiler { meta: Meta::new(), plugin_driver, stats_info: Mutex::new(StatsInfo::new()), + resolvers, }), } } @@ -533,7 +542,7 @@ mod tests { let index_js_content = file_contents.get("index.js").unwrap(); assert!( - index_js_content.contains("cssChunksIdToUrlMap[\"./a.ts\"]"), + index_js_content.contains("cssChunksIdToUrlMap[\"a.ts\"]"), "css async chunk works" ); } diff --git a/crates/mako/src/config.rs b/crates/mako/src/config.rs index 87bf90e8d..938633973 100644 --- a/crates/mako/src/config.rs +++ b/crates/mako/src/config.rs @@ -90,14 +90,23 @@ pub enum CodeSplittingStrategy { #[serde(rename = "none")] None, } +#[derive(Deserialize, Clone, Copy, Debug)] +pub enum TreeShakeStrategy { + #[serde(rename = "basic")] + Basic, + #[serde(rename = "advanced")] + Advanced, + #[serde(rename = "none")] + None, +} #[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct Config { pub entry: HashMap, pub output: OutputConfig, pub resolve: ResolveConfig, pub manifest: bool, - #[serde(rename = "manifestConfig")] pub manifest_config: ManifestConfig, pub mode: Mode, pub minify: bool, @@ -105,29 +114,24 @@ pub struct Config { pub externals: HashMap, pub providers: Providers, pub copy: Vec, - #[serde(rename = "publicPath")] pub public_path: String, - #[serde(rename = "inlineLimit")] pub inline_limit: usize, pub targets: HashMap, pub platform: Platform, - #[serde(rename = "moduleIdStrategy")] pub module_id_strategy: ModuleIdStrategy, pub define: HashMap, pub stats: bool, pub mdx: bool, // temp solution pub hmr: bool, - #[serde(rename = "hmrPort")] pub hmr_port: String, - #[serde(rename = "hmrHost")] pub hmr_host: String, - #[serde(rename = "codeSplitting")] pub code_splitting: CodeSplittingStrategy, // temp flag #[serde(rename = "extractCSS")] pub extract_css: bool, pub hash: bool, + pub tree_shake: TreeShakeStrategy, } const CONFIG_FILE: &str = "mako.config.json"; @@ -157,7 +161,8 @@ const DEFAULT_CONFIG: &str = r#" "moduleIdStrategy": "named", "codeSplitting": "none", "extractCSS": false, - "hash": false + "hash": false, + "treeShake": "advanced" } "#; diff --git a/crates/mako/src/dev.rs b/crates/mako/src/dev.rs index 626a24caa..94bf127d4 100644 --- a/crates/mako/src/dev.rs +++ b/crates/mako/src/dev.rs @@ -59,7 +59,7 @@ impl DevServer { }); while let Some(message) = ws_recv.next().await { - if let Message::Close(_) = message.unwrap() { + if let Ok(Message::Close(_)) = message { break; } } @@ -115,8 +115,6 @@ impl DevServer { } }; - println!("req path is /{:?}", path); - match path { "__/hmr-ws" => { if hyper_tungstenite::is_upgrade_request(&req) { @@ -141,7 +139,6 @@ impl DevServer { } } _ if path.starts_with("hot_update") => { - println!("命中了"); get_serve_response(static_serve_hmr.serve(req).await) } _ => { diff --git a/crates/mako/src/generate.rs b/crates/mako/src/generate.rs index 5c9f791eb..acb4e2f0a 100644 --- a/crates/mako/src/generate.rs +++ b/crates/mako/src/generate.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::fs; +use std::ops::DerefMut; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Instant; @@ -13,7 +14,7 @@ use tracing::debug; use crate::ast::{css_ast_to_code, js_ast_to_code}; use crate::compiler::{Compiler, Context}; -use crate::config::{DevtoolConfig, Mode, OutputMode}; +use crate::config::{DevtoolConfig, Mode, OutputMode, TreeShakeStrategy}; use crate::generate_chunks::OutputAst; use crate::load::file_content_hash; use crate::minify::{minify_css, minify_js}; @@ -47,13 +48,29 @@ impl Compiler { // Disable tree shaking in watch mode temporarily // ref: https://github.com/umijs/mako/issues/396 if !self.context.args.watch { - let shaking_module_ids = self.tree_shaking(); - let t_tree_shaking = t_tree_shaking.elapsed(); - println!( - "{} modules removed in {}ms.", - shaking_module_ids.len(), - t_tree_shaking.as_millis() - ); + match self.context.config.tree_shake { + TreeShakeStrategy::Basic => { + let mut module_graph = self.context.module_graph.write().unwrap(); + + self.context + .plugin_driver + .optimize_module_graph(module_graph.deref_mut())?; + let t_tree_shaking = t_tree_shaking.elapsed(); + println!("basic optimize in {}ms.", t_tree_shaking.as_millis()); + } + TreeShakeStrategy::Advanced => { + let shaking_module_ids = self.tree_shaking(); + let t_tree_shaking = t_tree_shaking.elapsed(); + println!( + "{} modules removed in {}ms.", + shaking_module_ids.len(), + t_tree_shaking.as_millis() + ); + } + TreeShakeStrategy::None => { + // do nothing + } + } } let t_tree_shaking = t_tree_shaking.elapsed(); let t_group_chunks = Instant::now(); diff --git a/crates/mako/src/generate_chunks.rs b/crates/mako/src/generate_chunks.rs index 7ac162bc4..3ddf4528d 100644 --- a/crates/mako/src/generate_chunks.rs +++ b/crates/mako/src/generate_chunks.rs @@ -349,7 +349,7 @@ fn build_ident_param(ident: Ident) -> Param { } } -fn build_fn_expr(ident: Option, params: Vec, stmts: Vec) -> FnExpr { +pub fn build_fn_expr(ident: Option, params: Vec, stmts: Vec) -> FnExpr { let func = Function { span: DUMMY_SP, params, @@ -369,7 +369,7 @@ fn build_fn_expr(ident: Option, params: Vec, stmts: Vec) -> } } -fn build_props(key_str: &str, value: Box) -> PropOrSpread { +pub fn build_props(key_str: &str, value: Box) -> PropOrSpread { PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { key: swc_ecma_ast::PropName::Str(Str { span: DUMMY_SP, diff --git a/crates/mako/src/load.rs b/crates/mako/src/load.rs index eaf6e674b..2ae583cc4 100644 --- a/crates/mako/src/load.rs +++ b/crates/mako/src/load.rs @@ -82,6 +82,7 @@ pub fn load(request: &FileRequest, is_entry: bool, context: &Arc) -> Re path: path.to_string(), is_entry, ext_name: ext_name(path).unwrap().to_string(), + request, }, context, )?; diff --git a/crates/mako/src/module.rs b/crates/mako/src/module.rs index 899a01dae..f70fa3295 100644 --- a/crates/mako/src/module.rs +++ b/crates/mako/src/module.rs @@ -58,18 +58,9 @@ pub fn generate_module_id(origin_module_id: String, context: &Arc) -> S ModuleIdStrategy::Hashed => md5_hash(&origin_module_id, 4), ModuleIdStrategy::Named => { // readable ids for debugging usage - // relative path to `&context.root` let absolute_path = PathBuf::from(origin_module_id); let relative_path = diff_paths(&absolute_path, &context.root).unwrap_or(absolute_path); - // diff_paths result always starts with ".."/"." or not - if relative_path.starts_with("..") || relative_path.starts_with(".") { - relative_path.to_string_lossy().to_string() - } else { - PathBuf::from(".") - .join(relative_path) - .to_string_lossy() - .to_string() - } + relative_path.to_string_lossy().to_string() } } } @@ -156,6 +147,7 @@ impl ModuleAst { } #[allow(dead_code)] +#[derive(PartialEq, Eq)] pub enum ModuleType { Script, Css, @@ -183,7 +175,7 @@ impl Module { id, is_entry, info, - side_effects: false, + side_effects: is_entry, } } diff --git a/crates/mako/src/module_graph.rs b/crates/mako/src/module_graph.rs index bd00b7cbf..c0d2c256a 100644 --- a/crates/mako/src/module_graph.rs +++ b/crates/mako/src/module_graph.rs @@ -195,14 +195,25 @@ impl ModuleGraph { targets } + pub fn dependence_module_ids(&self, module_id: &ModuleId) -> Vec { + let mut edges = self.get_edges(module_id, Direction::Outgoing); + let mut targets: Vec = vec![]; + while let Some((_, node_index)) = edges.next(&self.graph) { + let module = self.graph.node_weight(node_index).unwrap(); + targets.push(module.id.clone()); + } + + targets + } + pub fn get_dependency_module_by_source( &self, module_id: &ModuleId, - source: String, + source: &String, ) -> &ModuleId { let deps = self.get_dependencies(module_id); for (module_id, dep) in deps { - if source == dep.source { + if *source == dep.source { return module_id; } } diff --git a/crates/mako/src/plugin.rs b/crates/mako/src/plugin.rs index 39564d170..077e8a839 100644 --- a/crates/mako/src/plugin.rs +++ b/crates/mako/src/plugin.rs @@ -8,13 +8,15 @@ use crate::compiler::Context; use crate::config::Config; use crate::load::Content; use crate::module::{Dependency, ModuleAst}; +use crate::module_graph::ModuleGraph; use crate::stats::StatsJsonMap; #[derive(Debug)] -pub struct PluginLoadParam { +pub struct PluginLoadParam<'a> { pub path: String, pub is_entry: bool, pub ext_name: String, + pub request: &'a FileRequest, } pub struct PluginParseParam<'a> { @@ -61,6 +63,9 @@ pub trait Plugin: Any + Send + Sync { fn runtime_plugins(&self, _context: &Arc) -> Result> { Ok(Vec::new()) } + fn optimize_module_graph(&self, _module_graph: &mut ModuleGraph) -> Result<()> { + Ok(()) + } } #[derive(Default)] @@ -133,8 +138,16 @@ impl PluginDriver { pub fn runtime_plugins_code(&self, context: &Arc) -> Result { let mut plugins = Vec::new(); for plugin in &self.plugins { - plugins.append(&mut plugin.runtime_plugins(context)?); + plugins.extend(plugin.runtime_plugins(context)?); } Ok(plugins.join("\n")) } + + pub fn optimize_module_graph(&self, module_graph: &mut ModuleGraph) -> Result<()> { + for p in &self.plugins { + p.optimize_module_graph(module_graph)?; + } + + Ok(()) + } } diff --git a/crates/mako/src/plugins/farm_tree_shake.rs b/crates/mako/src/plugins/farm_tree_shake.rs new file mode 100644 index 000000000..157635541 --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake.rs @@ -0,0 +1,22 @@ +use anyhow::Result; + +use crate::module_graph::ModuleGraph; +use crate::plugin::Plugin; + +mod module; +mod remove_useless_stmts; +mod shake; +mod statement_graph; + +pub struct FarmTreeShake {} + +impl Plugin for FarmTreeShake { + fn name(&self) -> &str { + "farm/tree-shake" + } + + fn optimize_module_graph(&self, module_graph: &mut ModuleGraph) -> Result<()> { + shake::optimize_farm(module_graph)?; + Ok(()) + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/module.rs b/crates/mako/src/plugins/farm_tree_shake/module.rs new file mode 100644 index 000000000..6c279e20d --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/module.rs @@ -0,0 +1,328 @@ +use std::collections::{HashMap, HashSet}; + +use crate::module::{Module, ModuleId}; +use crate::plugins::farm_tree_shake::statement_graph::{ + ExportInfo, ExportSpecifierInfo, ImportInfo, StatementGraph, StatementId, +}; +use crate::tree_shaking_module::ModuleSystem; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum UsedIdent { + /// Local ident + SwcIdent(String), + /// Default ident + Default, + /// This ident is used and may be exported from other module + InExportAll(String), + /// All idents is used and may be exported from other module + ExportAll, +} + +impl ToString for UsedIdent { + fn to_string(&self) -> String { + match self { + UsedIdent::SwcIdent(ident) => ident.to_string(), + UsedIdent::Default => "default".to_string(), + UsedIdent::InExportAll(ident) => ident.to_string(), + UsedIdent::ExportAll => "*".to_string(), + } + } +} + +#[derive(Debug, Clone)] +pub enum UsedExports { + All, + Partial(Vec), +} + +impl UsedExports { + pub fn add_used_export(&mut self, used_export: &dyn ToString) { + match self { + UsedExports::All => { + *self = UsedExports::All; + } + UsedExports::Partial(self_used_exports) => { + self_used_exports.push(used_export.to_string()) + } + } + } + + pub fn is_empty(&self) -> bool { + match self { + UsedExports::All => false, + UsedExports::Partial(self_used_exports) => self_used_exports.is_empty(), + } + } +} + +pub struct TreeShakeModule { + pub module_id: ModuleId, + pub side_effects: bool, + pub stmt_graph: StatementGraph, + // used exports will be analyzed when tree shaking + pub used_exports: UsedExports, + pub module_system: ModuleSystem, +} + +impl TreeShakeModule { + pub fn new(module: &Module) -> Self { + let module_info = module.info.as_ref().unwrap(); + + // 1. generate statement graph + let mut module_system = ModuleSystem::CommonJS; + let stmt_graph = match &module_info.ast { + crate::module::ModuleAst::Script(module) => { + let is_esm = module + .ast + .body + .iter() + .any(|s| matches!(s, swc_ecma_ast::ModuleItem::ModuleDecl(_))); + if is_esm { + module_system = ModuleSystem::ESModule; + StatementGraph::new(&module.ast) + } else { + StatementGraph::empty() + } + } + crate::module::ModuleAst::Css(_) => { + module_system = ModuleSystem::Custom; + StatementGraph::empty() + } + crate::module::ModuleAst::None => { + module_system = ModuleSystem::Custom; + StatementGraph::empty() + } + }; + + // 2. set default used exports + let used_exports = if module.side_effects { + UsedExports::All + } else { + UsedExports::Partial(vec![]) + }; + + Self { + module_id: module.id.clone(), + stmt_graph, + used_exports, + side_effects: module.side_effects, + module_system, + } + } + + pub fn imports(&self) -> Vec { + let mut imports = vec![]; + + for stmt in self.stmt_graph.stmts() { + if let Some(import) = &stmt.import_info { + imports.push(import.clone()); + } + } + + imports + } + + pub fn exports(&self) -> Vec { + let mut exports = vec![]; + + for stmt in self.stmt_graph.stmts() { + if let Some(export) = &stmt.export_info { + exports.push(export.clone()); + } + } + + exports + } + + pub fn used_statements(&self) -> HashMap> { + // 1. get used exports + let used_exports_idents = self.used_exports_idents(); + let mut stmt_used_idents_map = HashMap::new(); + + for (used_ident, stmt_id) in used_exports_idents { + let used_idents: &mut HashSet = + stmt_used_idents_map.entry(stmt_id).or_default(); + used_idents.insert(used_ident); + } + + { + for stmt in self.stmt_graph.stmts() { + if stmt.is_self_executed { + stmt_used_idents_map.entry(stmt.id).or_default(); + + let dep_stmts = self.stmt_graph.dependencies(&stmt.id); + + for (dep_stmt, referred_idents) in dep_stmts { + let used_idents = stmt_used_idents_map.entry(dep_stmt.id).or_default(); + used_idents.extend(referred_idents.into_iter().map(UsedIdent::SwcIdent)); + } + // stmt.used_idents.iter().for_each(|used_ident| { + // // find the defined ident + // stmt_used_idents_map + // .entry(stmt.id) + // .or_insert(HashSet::new()); + + // for stmt_inner in self.stmt_graph.stmts() { + // if stmt_inner.id == stmt.id { + // continue; + // } + + // if stmt_inner + // .defined_idents_map + // .contains_key(&used_ident.to_string()) + // || stmt_inner + // .defined_idents + // .iter() + // .any(|ident| ident.to_string() == used_ident.to_string()) + // { + // let used_idents = stmt_used_idents_map + // .entry(stmt_inner.id) + // .or_insert(HashSet::new()); + // used_idents.insert(UsedIdent::SwcIdent(used_ident.clone())); + // } + // } + // }); + } + } + } + + // 2. analyze used statements starting from used exports + + // println!("before {:?}", self.module_id); + // dbg!(&used); + // + // for imp in self.imports() { + // if imp.source.contains("@swc/helpers") { + // used.entry(imp.stmt_id) + // .or_insert(HashSet::new()); + // } + // } + // + // dbg!(&used); + + self.stmt_graph + .analyze_used_statements_and_idents(stmt_used_idents_map) + } + + pub fn used_exports_idents(&self) -> Vec<(UsedIdent, StatementId)> { + match &self.used_exports { + UsedExports::All => { + // all exported identifiers are used + let mut used_idents = vec![]; + + for export_info in self.exports() { + for sp in export_info.specifiers { + match sp { + ExportSpecifierInfo::Default => { + used_idents.push((UsedIdent::Default, export_info.stmt_id)); + } + ExportSpecifierInfo::Named { local, .. } => { + used_idents.push(( + UsedIdent::SwcIdent(local.clone()), + export_info.stmt_id, + )); + } + ExportSpecifierInfo::Namespace(ns) => { + used_idents + .push((UsedIdent::SwcIdent(ns.clone()), export_info.stmt_id)); + } + ExportSpecifierInfo::All(_) => { + used_idents.push((UsedIdent::ExportAll, export_info.stmt_id)); + } + } + } + } + + used_idents + } + UsedExports::Partial(idents) => { + let mut used_idents = vec![]; + + for ident in idents { + // find the export info that contains the ident + let export_info = self.exports().into_iter().find(|export_info| { + export_info.specifiers.iter().any(|sp| match sp { + ExportSpecifierInfo::Default => ident == "default", + ExportSpecifierInfo::Named { local, exported } => { + let exported_ident = if let Some(exported) = exported { + exported + } else { + local + }; + + is_ident_equal(ident, exported_ident) + } + ExportSpecifierInfo::Namespace(ns) => is_ident_equal(ident, ns), + ExportSpecifierInfo::All(_) => { + /* Deal with All later */ + false + } + }) + }); + + if let Some(export_info) = export_info { + for sp in export_info.specifiers { + match sp { + ExportSpecifierInfo::Default => { + if ident == "default" { + used_idents.push((UsedIdent::Default, export_info.stmt_id)); + } + } + ExportSpecifierInfo::Named { local, exported } => { + if let Some(exported) = exported { + if is_ident_equal(ident, &exported) { + used_idents.push(( + UsedIdent::SwcIdent(local.clone()), + export_info.stmt_id, + )); + } + } else if is_ident_equal(ident, &local) { + used_idents.push(( + UsedIdent::SwcIdent(local.clone()), + export_info.stmt_id, + )); + } + } + ExportSpecifierInfo::Namespace(ns) => { + if is_ident_equal(ident, &ns) { + used_idents.push(( + UsedIdent::SwcIdent(ns.clone()), + export_info.stmt_id, + )); + } + } + ExportSpecifierInfo::All(_) => unreachable!(), + } + } + } else { + // if export info is not found, and there are ExportSpecifierInfo::All, then the ident may be exported by `export * from 'xxx'` + for export_info in self.exports() { + if export_info + .specifiers + .iter() + .any(|sp| matches!(sp, ExportSpecifierInfo::All(_))) + { + let stmt_id = export_info.stmt_id; + used_idents + .push((UsedIdent::InExportAll(ident.to_string()), stmt_id)); + } + } + } + } + + used_idents + } + } + } +} + +fn is_ident_equal(ident1: &str, ident2: &str) -> bool { + let split1 = ident1.split('#').collect::>(); + let split2 = ident2.split('#').collect::>(); + + if split1.len() == 2 && split2.len() == 2 { + split1[0] == split2[0] && split1[1] == split2[1] + } else { + split1[0] == split2[0] + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/remove_useless_stmts.rs b/crates/mako/src/plugins/farm_tree_shake/remove_useless_stmts.rs new file mode 100644 index 000000000..9aa926e0a --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/remove_useless_stmts.rs @@ -0,0 +1,191 @@ +use swc_ecma_ast::{ImportDecl, ImportSpecifier, Module as SwcModule, ModuleExportName}; +use swc_ecma_visit::{VisitMut, VisitMutWith, VisitWith}; + +use crate::plugins::farm_tree_shake::module::TreeShakeModule; +use crate::plugins::farm_tree_shake::statement_graph::analyze_imports_and_exports::{ + analyze_imports_and_exports, StatementInfo, +}; +use crate::plugins::farm_tree_shake::statement_graph::defined_idents_collector::DefinedIdentsCollector; +use crate::plugins::farm_tree_shake::statement_graph::{ + ExportInfo, ExportSpecifierInfo, ImportInfo, +}; + +pub fn remove_useless_stmts( + tree_shake_module: &mut TreeShakeModule, + swc_module: &mut SwcModule, +) -> (Vec, Vec) { + // analyze the statement graph start from the used statements + let mut used_stmts = tree_shake_module + .used_statements() + .into_iter() + .collect::>(); + // sort used_stmts + used_stmts.sort_by_key(|a| a.0); + + let mut used_import_infos = vec![]; + let mut used_export_from_infos = vec![]; + + // remove unused specifiers in export statement and import statement + for (stmt_id, used_defined_idents) in &used_stmts { + let module_item = &mut swc_module.body[*stmt_id]; + + let StatementInfo { + import_info, + export_info, + .. + } = analyze_imports_and_exports(stmt_id, module_item, Some(used_defined_idents.clone())); + + if let Some(import_info) = import_info { + used_import_infos.push(import_info.clone()); + + let mut remover = UselessImportStmtsRemover { import_info }; + + module_item.visit_mut_with(&mut remover); + } + + if let Some(mut export_info) = export_info { + if export_info.specifiers.is_empty() { + continue; + } + + // if this export statement is export * from 'xxx' + if export_info.source.is_some() + && matches!(export_info.specifiers[0], ExportSpecifierInfo::All(_)) + { + export_info.specifiers[0] = ExportSpecifierInfo::All(Some( + used_defined_idents.clone().into_iter().collect(), + )); + used_export_from_infos.push(export_info.clone()); + } else { + if export_info.source.is_some() { + used_export_from_infos.push(export_info.clone()); + } + + let mut remover = UselessExportStmtRemover { export_info }; + + module_item.visit_mut_with(&mut remover); + } + } + } + + let mut stmts_to_remove = vec![]; + // TODO recognize the self-executed statements and preserve all the related statements + + let used_stmts_indexes = used_stmts + .iter() + .map(|(index, _)| index) + .collect::>(); + + // remove the unused statements from the module + for (index, _) in swc_module.body.iter().enumerate() { + if !used_stmts_indexes.contains(&&index) { + stmts_to_remove.push(index); + } + } + + // remove from the end to the start + stmts_to_remove.reverse(); + + for stmt in stmts_to_remove { + swc_module.body.remove(stmt); + } + + (used_import_infos, used_export_from_infos) +} + +pub struct UselessImportStmtsRemover { + import_info: ImportInfo, +} + +impl VisitMut for UselessImportStmtsRemover { + fn visit_mut_import_decl(&mut self, import_decl: &mut ImportDecl) { + let mut specifiers_to_remove = vec![]; + + for (index, specifier) in import_decl.specifiers.iter().enumerate() { + if let ImportSpecifier::Named(named_specifier) = specifier { + if !self. + import_info.specifiers + .iter() + .any(|specifier| match specifier { + crate::plugins::farm_tree_shake::statement_graph::ImportSpecifierInfo::Named { local, .. } => named_specifier.local.to_string() == *local, + _ => false, + }) + { + specifiers_to_remove.push(index); + } + } + } + + specifiers_to_remove.reverse(); + + for index in specifiers_to_remove { + import_decl.specifiers.remove(index); + } + } +} + +pub struct UselessExportStmtRemover { + export_info: ExportInfo, +} + +impl VisitMut for UselessExportStmtRemover { + fn visit_mut_export_decl(&mut self, export_decl: &mut swc_ecma_ast::ExportDecl) { + if let swc_ecma_ast::Decl::Var(var_decl) = &mut export_decl.decl { + let mut decls_to_remove = vec![]; + + for (index, decl) in var_decl.decls.iter_mut().enumerate() { + if !self.export_info.specifiers.iter().any( + |export_specifier| match export_specifier { + ExportSpecifierInfo::Named { local, .. } => { + let mut defined_idents_collector = DefinedIdentsCollector::new(); + decl.name.visit_with(&mut defined_idents_collector); + + defined_idents_collector.defined_idents.contains(local) + } + _ => false, + }, + ) { + decls_to_remove.push(index); + } + } + + decls_to_remove.reverse(); + + for index in decls_to_remove { + var_decl.decls.remove(index); + } + } + } + + fn visit_mut_export_specifiers(&mut self, specifiers: &mut Vec) { + let mut specifiers_to_remove = vec![]; + + for (index, specifier) in specifiers.iter().enumerate() { + if !self + .export_info + .specifiers + .iter() + .any(|export_specifier| match export_specifier { + ExportSpecifierInfo::Named { local, .. } => match specifier { + swc_ecma_ast::ExportSpecifier::Named(named_specifier) => { + match &named_specifier.orig { + ModuleExportName::Ident(ident) => ident.to_string() == *local, + _ => false, + } + } + _ => false, + }, + _ => false, + }) + { + specifiers_to_remove.push(index); + } + } + + specifiers_to_remove.reverse(); + + for index in specifiers_to_remove { + specifiers.remove(index); + } + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/shake.rs b/crates/mako/src/plugins/farm_tree_shake/shake.rs new file mode 100644 index 000000000..592156c21 --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/shake.rs @@ -0,0 +1,294 @@ +use anyhow::Result; + +use crate::module::{ModuleAst, ModuleId, ModuleType, ResolveType}; +use crate::module_graph::ModuleGraph; +use crate::plugins::farm_tree_shake::module::{TreeShakeModule, UsedExports}; +use crate::plugins::farm_tree_shake::statement_graph::{ExportInfo, ImportInfo}; +use crate::plugins::farm_tree_shake::{module, remove_useless_stmts, statement_graph}; +use crate::tree_shaking_module::ModuleSystem; + +/// tree shake useless modules and code, steps: +/// 1. topo sort the module_graph, the cyclic modules will be marked as side_effects +/// 2. generate tree_shake_modules based on the topo sorted modules +/// 3. traverse the tree_shake_modules +/// 3.1 mark entry modules as side_effects +/// 3.2 if module is commonjs, mark all imported modules as [UsedExports::All] +/// 3.3 else if module is esm and the module has side effects, add imported identifiers to [UsedExports::Partial] of the imported modules +/// 3.4 else if module is esm and the module has no side effects, analyze the used statement based on the statement graph +pub fn optimize_farm(module_graph: &mut ModuleGraph) -> Result<()> { + // topo sort the module_graph, the cyclic modules will be marked as side_effects + let (topo_sorted_modules, cyclic_modules) = { module_graph.toposort() }; + + // mark cyclic modules as side_effects + for chain in cyclic_modules { + for module_id in chain { + let module = module_graph.get_module_mut(&module_id).unwrap(); + module.side_effects = true; + } + } + + // mark entry modules as side_effects + // entry side_effects marked in new stage + + let mut tree_shake_modules_ids = vec![]; + let mut tree_shake_modules_map = std::collections::HashMap::new(); + + for module_id in topo_sorted_modules { + let module = module_graph.get_module(&module_id).unwrap(); + + let module_type = module.get_module_type(); + + // skip non script modules and external modules + if module_type != ModuleType::Script || module.is_external() { + if module_type != ModuleType::Script && !module.is_external() { + // mark all non script modules' script dependencies as side_effects + for dep_id in module_graph.dependence_module_ids(&module_id) { + let dep_module = module_graph.get_module_mut(&dep_id).unwrap(); + + let dep_module_type = dep_module.get_module_type(); + + if dep_module_type != ModuleType::Script { + continue; + } + + dep_module.side_effects = true; + } + } + + continue; + }; + + let tree_shake_module = TreeShakeModule::new(module); + tree_shake_modules_ids.push(tree_shake_module.module_id.clone()); + tree_shake_modules_map.insert(tree_shake_module.module_id.clone(), tree_shake_module); + } + + let mut modules_to_remove = vec![]; + + // traverse the tree_shake_modules + for tree_shake_module_id in tree_shake_modules_ids { + let tree_shake_module = tree_shake_modules_map + .get_mut(&tree_shake_module_id) + .unwrap(); + + // if module is not esm, mark all imported modules as [UsedExports::All] + if !matches!(tree_shake_module.module_system, ModuleSystem::ESModule) { + for (dep_id, _) in module_graph.get_dependencies(&tree_shake_module_id) { + let dep_tree_shake_module = tree_shake_modules_map.get_mut(dep_id); + + if let Some(dep_tree_shake_module) = dep_tree_shake_module { + dep_tree_shake_module.used_exports = UsedExports::All; + } + } + } else { + // if module is esm and the module has side effects, add imported identifiers to [UsedExports::Partial] of the imported modules + if tree_shake_module.side_effects { + let imports = tree_shake_module.imports(); + let exports = tree_shake_module.exports(); + + for import_info in &imports { + add_used_exports_by_import_info( + &mut tree_shake_modules_map, + &*module_graph, + &tree_shake_module_id, + import_info, + ); + } + + for export_info in &exports { + add_used_exports_by_export_info( + &mut tree_shake_modules_map, + &*module_graph, + &tree_shake_module_id, + export_info, + ); + } + } else { + let tree_shake_module = tree_shake_modules_map + .get_mut(&tree_shake_module_id) + .unwrap(); + + if tree_shake_module.used_exports.is_empty() { + // if the module's used_exports is empty, means this module is not used and should be removed + modules_to_remove.push(tree_shake_module_id.clone()); + continue; + } + + let module = module_graph + .get_module_mut(&tree_shake_module.module_id) + .unwrap(); + let ast = &mut module.info.as_mut().unwrap().ast; + + if let ModuleAst::Script(swc_module) = ast { + // remove useless statements and useless imports/exports identifiers, then all preserved import info and export info will be added to the used_exports. + let (used_imports, used_exports_from) = + remove_useless_stmts::remove_useless_stmts( + tree_shake_module, + &mut swc_module.ast, + ); + + for import_info in used_imports { + add_used_exports_by_import_info( + &mut tree_shake_modules_map, + &*module_graph, + &tree_shake_module_id, + &import_info, + ); + } + + for export_info in used_exports_from { + add_used_exports_by_export_info( + &mut tree_shake_modules_map, + &*module_graph, + &tree_shake_module_id, + &export_info, + ); + } + } + } + } + + // add all dynamic imported dependencies as [UsedExports::All] + for (dep, edge) in module_graph.get_dependencies(&tree_shake_module_id) { + if matches!(edge.resolve_type, ResolveType::DynamicImport) { + let tree_shake_module = tree_shake_modules_map.get_mut(dep).unwrap(); + tree_shake_module.side_effects = true; + tree_shake_module.used_exports = UsedExports::All; + } + } + } + + // remove the unused modules + for module_id in modules_to_remove { + module_graph.remove_module(&module_id); + } + + Ok(()) +} + +// Add all imported to used_exports +fn add_used_exports_by_import_info( + tree_shake_modules_map: &mut std::collections::HashMap, + module_graph: &ModuleGraph, + tree_shake_module_id: &ModuleId, + import_info: &ImportInfo, +) { + let imported_module_id = + module_graph.get_dependency_module_by_source(tree_shake_module_id, &import_info.source); + let imported_module = module_graph.get_module(imported_module_id).unwrap(); + + let info = imported_module.info.as_ref().unwrap(); + + let is_js = matches!(info.ast, ModuleAst::Script(_)); + + if info.external.is_some() || !is_js { + return; + } + + let imported_tree_shake_module = tree_shake_modules_map + .get_mut(imported_module_id) + .unwrap_or_else(|| { + panic!("imported module not found: {:?}", imported_module_id); + }); + + if import_info.specifiers.is_empty() { + imported_tree_shake_module.used_exports = UsedExports::All; + imported_tree_shake_module.side_effects = true; + return; + } + + for sp in &import_info.specifiers { + match sp { + statement_graph::ImportSpecifierInfo::Namespace(_) => { + imported_tree_shake_module.used_exports = UsedExports::All; + } + statement_graph::ImportSpecifierInfo::Named { local, imported } => { + if let Some(ident) = imported { + if *ident == "default" { + imported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::Default); + } else { + imported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::SwcIdent(strip_context(ident))); + } + } else { + imported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::SwcIdent(strip_context(local))); + } + } + statement_graph::ImportSpecifierInfo::Default(_) => { + imported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::Default); + } + } + } +} + +/// All all exported to used_exports +fn add_used_exports_by_export_info( + tree_shake_modules_map: &mut std::collections::HashMap, + module_graph: &ModuleGraph, + tree_shake_module_id: &ModuleId, + export_info: &ExportInfo, +) { + if let Some(source) = &export_info.source { + let exported_module_id = + module_graph.get_dependency_module_by_source(tree_shake_module_id, source); + let exported_module = module_graph.get_module(exported_module_id).unwrap(); + + if exported_module.is_external() { + return; + }; + + let exported_tree_shake_module = + tree_shake_modules_map.get_mut(exported_module_id).unwrap(); + + for sp in &export_info.specifiers { + match sp { + statement_graph::ExportSpecifierInfo::Namespace(_) => { + exported_tree_shake_module.used_exports = module::UsedExports::All; + } + statement_graph::ExportSpecifierInfo::Named { local, .. } => { + if local == &"default".to_string() { + exported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::Default); + } else { + exported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::SwcIdent(strip_context(local))); + } + } + statement_graph::ExportSpecifierInfo::Default => { + exported_tree_shake_module + .used_exports + .add_used_export(&module::UsedIdent::Default); + } + statement_graph::ExportSpecifierInfo::All(used_idents) => { + if let Some(used_idents) = used_idents { + for ident in used_idents { + if ident == "*" { + exported_tree_shake_module.used_exports = module::UsedExports::All; + } else { + exported_tree_shake_module + .used_exports + .add_used_export(&strip_context(ident)); + } + } + } else { + exported_tree_shake_module.used_exports = module::UsedExports::All; + } + } + } + } + } +} + +fn strip_context(ident: &str) -> String { + let ident_split = ident.split('#').collect::>(); + ident_split[0].to_string() +} diff --git a/crates/mako/src/plugins/farm_tree_shake/statement_graph.rs b/crates/mako/src/plugins/farm_tree_shake/statement_graph.rs new file mode 100644 index 000000000..cdeb9069e --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/statement_graph.rs @@ -0,0 +1,327 @@ +use std::collections::{HashMap, HashSet, VecDeque}; + +use petgraph::stable_graph::NodeIndex; +use swc_ecma_ast::{Module as SwcModule, ModuleItem}; + +pub(crate) mod analyze_imports_and_exports; +pub(crate) mod defined_idents_collector; +pub(crate) mod used_idents_collector; + +use analyze_imports_and_exports::analyze_imports_and_exports; + +use crate::plugins::farm_tree_shake::module::UsedIdent; +use crate::plugins::farm_tree_shake::statement_graph::analyze_imports_and_exports::StatementInfo; + +pub type StatementId = usize; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImportSpecifierInfo { + Namespace(String), + Named { + local: String, + imported: Option, + }, + Default(String), +} + +#[derive(Debug, Clone)] +pub struct ImportInfo { + pub source: String, + pub specifiers: Vec, + pub stmt_id: StatementId, +} + +// collect all exports and gathering them into a simpler structure +#[derive(Debug, Clone)] +pub enum ExportSpecifierInfo { + // export * from 'foo'; + All(Option>), + // export { foo, bar, default as zoo } from 'foo'; + Named { + local: String, + exported: Option, + }, + // export default xxx; + Default, + // export * as foo from 'foo'; + Namespace(String), +} + +#[derive(Debug, Clone)] +pub struct ExportInfo { + pub source: Option, + pub specifiers: Vec, + pub stmt_id: StatementId, +} + +#[derive(Debug)] +pub struct Statement { + pub id: StatementId, + pub import_info: Option, + pub export_info: Option, + pub defined_idents: HashSet, + pub used_idents: HashSet, + /// Use String to replace Ident as key, because Ident has position info and it will make hash map not work as expected, + /// transform it to Ident.to_string() is exactly what we want + pub defined_idents_map: HashMap>, + pub is_self_executed: bool, +} + +impl Statement { + pub fn new(id: StatementId, stmt: &ModuleItem) -> Self { + let StatementInfo { + import_info, + export_info, + defined_idents, + used_idents, + defined_idents_map, + is_self_executed, + } = analyze_imports_and_exports(&id, stmt, None); + + // transform defined_idents_map from HashMap> to HashMap using ToString + let defined_idents_map = defined_idents_map + .into_iter() + .map(|(key, value)| (key, value)) + .collect(); + + Self { + id, + import_info, + export_info, + defined_idents, + used_idents, + defined_idents_map, + is_self_executed, + } + } +} + +pub struct StatementGraphEdge { + pub idents: HashSet, +} + +pub struct StatementGraph { + g: petgraph::graph::Graph, + id_index_map: HashMap, +} + +impl StatementGraph { + pub fn new(module: &SwcModule) -> Self { + let mut g = petgraph::graph::Graph::new(); + let mut id_index_map = HashMap::new(); + + for (index, stmt) in module.body.iter().enumerate() { + let node = g.add_node(Statement::new(index, stmt)); + id_index_map.insert(index, node); + } + + let mut graph = Self { g, id_index_map }; + let mut edges_to_add = Vec::new(); + + for stmt in graph.stmts() { + // find the statement that defines the ident + for def_stmt in graph.stmts() { + let mut deps_idents = HashSet::new(); + + for di in &def_stmt.defined_idents { + if stmt.used_idents.contains(di) { + deps_idents.insert(di.clone()); + } + } + + if !deps_idents.is_empty() { + edges_to_add.push((stmt.id, def_stmt.id, deps_idents)); + } + } + } + + for (from, to, idents) in edges_to_add { + graph.add_edge(from, to, idents); + } + + graph + } + + pub fn empty() -> Self { + Self { + g: petgraph::graph::Graph::new(), + id_index_map: HashMap::new(), + } + } + + pub fn add_edge(&mut self, from: StatementId, to: StatementId, idents: HashSet) { + let from_node = self.id_index_map.get(&from).unwrap(); + let to_node = self.id_index_map.get(&to).unwrap(); + + // if self.g contains edge, insert idents into edge + if let Some(edge) = self.g.find_edge(*from_node, *to_node) { + let edge = self.g.edge_weight_mut(edge).unwrap(); + + edge.idents.extend(idents); + return; + } + + self.g + .add_edge(*from_node, *to_node, StatementGraphEdge { idents }); + } + + pub fn stmt(&self, id: &StatementId) -> &Statement { + let node = self.id_index_map.get(id).unwrap(); + &self.g[*node] + } + + #[allow(dead_code)] + pub fn stmt_mut(&mut self, id: &StatementId) -> &mut Statement { + let node = self.id_index_map.get(id).unwrap(); + &mut self.g[*node] + } + + pub fn dependencies(&self, id: &StatementId) -> Vec<(&Statement, HashSet)> { + let node = self.id_index_map.get(id).unwrap(); + self.g + .neighbors(*node) + .map(|i| { + let edge = self.g.find_edge(*node, i).unwrap(); + let edge = self.g.edge_weight(edge).unwrap(); + (&self.g[i], edge.idents.clone()) + }) + .collect() + } + + pub fn stmts(&self) -> Vec<&Statement> { + self.g.node_indices().map(|i| &self.g[i]).collect() + } + + #[allow(dead_code)] + pub fn edges(&self) -> Vec<(&Statement, &Statement, &StatementGraphEdge)> { + self.g + .edge_indices() + .map(|i| { + let (from, to) = self.g.edge_endpoints(i).unwrap(); + let edge = self.g.edge_weight(i).unwrap(); + (&self.g[from], &self.g[to], edge) + }) + .collect() + } + + pub fn analyze_used_statements_and_idents( + &self, + used_exports: HashMap>, + ) -> HashMap> { + let mut used_statements: HashMap> = HashMap::new(); + + // sort used_exports by statement id + let mut used_exports: Vec<_> = used_exports.into_iter().collect(); + used_exports.sort_by(|a, b| a.0.cmp(&b.0)); + + for (stmt_id, used_export_idents) in used_exports { + let mut used_dep_idents = HashSet::new(); + let mut used_defined_idents = HashSet::new(); + let mut skip = false; + + for ident in used_export_idents { + match ident { + UsedIdent::SwcIdent(i) => { + used_defined_idents.insert(i.to_string()); + let dep_idents = self.stmt(&stmt_id).defined_idents_map.get(&i.to_string()); + + if let Some(dep_idents) = dep_idents { + used_dep_idents.extend(dep_idents.iter().map(|i| i.to_string())); + } + } + UsedIdent::Default => { + let stmt = self.stmt(&stmt_id); + used_dep_idents.extend(stmt.used_idents.iter().map(|i| i.to_string())); + } + UsedIdent::InExportAll(specifier) => { + // if used_statements already contains this statement, add specifier to it + if let Some(specifiers) = used_statements.get_mut(&stmt_id) { + specifiers.insert(specifier); + } else { + used_statements.insert(stmt_id, [specifier].into()); + } + skip = true; + } + UsedIdent::ExportAll => { + used_statements.insert(stmt_id, ["*".to_string()].into()); + skip = true; + } + } + } + + if skip { + continue; + } + + let mut stmts = VecDeque::from([(stmt_id, used_defined_idents, used_dep_idents)]); + let mut visited = HashSet::new(); + + let hash_stmt = |stmt_id: &StatementId, used_defined_idents: &HashSet| { + let mut hash = format!("{}:", stmt_id); + + for ident in used_defined_idents { + hash += ident; + } + + hash + }; + + while let Some((stmt_id, used_defined_idents, used_dep_idents)) = stmts.pop_front() { + let hash = hash_stmt(&stmt_id, &used_defined_idents); + + // if stmt_id is already in used_statements, add used_defined_idents to it + if let Some(idents) = used_statements.get_mut(&stmt_id) { + idents.extend(used_defined_idents); + } else { + used_statements.insert(stmt_id, used_defined_idents); + } + + if visited.contains(&hash) { + continue; + } + + visited.insert(hash); + + let deps = self.dependencies(&stmt_id); + + for (dep_stmt, dep_idents) in deps { + if dep_idents.iter().any(|di| used_dep_idents.contains(di)) { + let mut dep_stmt_idents = HashSet::new(); + let mut dep_used_defined_idents = HashSet::new(); + + for ident in &used_dep_idents { + if let Some(dep_idents) = + dep_stmt.defined_idents_map.get(&ident.to_string()) + { + dep_used_defined_idents.insert(ident.to_string()); + dep_stmt_idents.extend(dep_idents.clone()); + } else { + // if dep_stmt.defined_idents contains ident, push it to dep_used_defined_idents + if let Some(find_defined_ident) = dep_stmt.defined_idents.get(ident) + { + dep_used_defined_idents.insert(find_defined_ident.to_string()); + } + } + } + + // if dep_stmt is already in stmts, merge dep_stmt_idents + if let Some((_, used_dep_defined_idents, used_dep_idents)) = + stmts.iter_mut().find(|(id, _, _)| *id == dep_stmt.id) + { + used_dep_defined_idents.extend(dep_used_defined_idents); + used_dep_idents.extend(dep_stmt_idents); + } else { + stmts.push_back(( + dep_stmt.id, + dep_used_defined_idents, + dep_stmt_idents, + )); + } + } + } + } + } + + used_statements + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/statement_graph/analyze_imports_and_exports.rs b/crates/mako/src/plugins/farm_tree_shake/statement_graph/analyze_imports_and_exports.rs new file mode 100644 index 000000000..b944c802b --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/statement_graph/analyze_imports_and_exports.rs @@ -0,0 +1,367 @@ +use std::collections::{HashMap, HashSet}; + +use swc_ecma_ast::{ModuleExportName, ModuleItem}; +use swc_ecma_visit::VisitWith; + +use super::defined_idents_collector::DefinedIdentsCollector; +use super::used_idents_collector::{self, UsedIdentsCollector}; +use super::{ExportInfo, ExportSpecifierInfo, ImportInfo, ImportSpecifierInfo, StatementId}; + +pub struct StatementInfo { + pub import_info: Option, + pub export_info: Option, + pub defined_idents: HashSet, + pub used_idents: HashSet, + pub defined_idents_map: HashMap>, + pub is_self_executed: bool, +} + +pub fn analyze_imports_and_exports( + id: &StatementId, + stmt: &ModuleItem, + used_defined_idents: Option>, +) -> StatementInfo { + let mut defined_idents = HashSet::new(); + let mut used_idents = HashSet::new(); + let mut defined_idents_map = HashMap::new(); + + let mut imports = None; + let mut exports = None; + + let mut is_self_executed = false; + + let mut analyze_and_insert_used_idents = + |stmt: &dyn VisitWith, ident: Option| { + // skip if used_defined_idents is not None as it is only uses the imports and exports for now + if used_defined_idents.is_some() { + return; + } + + let mut used_idents_collector = used_idents_collector::UsedIdentsCollector::new(); + stmt.visit_with(&mut used_idents_collector); + + if let Some(ident) = ident { + defined_idents_map.insert(ident, used_idents_collector.used_idents.clone()); + } + + used_idents.extend(used_idents_collector.used_idents); + }; + + let is_ident_used = |ident: &String| { + if let Some(used_defined_idents) = &used_defined_idents { + return used_defined_idents.contains(ident); + } + + true + }; + + match stmt { + ModuleItem::ModuleDecl(module_decl) => match module_decl { + swc_ecma_ast::ModuleDecl::Import(import_decl) => { + let source = import_decl.src.value.to_string(); + let mut specifiers = vec![]; + + for specifier in &import_decl.specifiers { + match specifier { + swc_ecma_ast::ImportSpecifier::Namespace(ns) => { + if !is_ident_used(&ns.local.to_string()) { + continue; + } + + specifiers.push(ImportSpecifierInfo::Namespace(ns.local.to_string())); + defined_idents.insert(ns.local.to_string()); + } + swc_ecma_ast::ImportSpecifier::Named(named) => { + if !is_ident_used(&named.local.to_string()) { + continue; + } + + specifiers.push(ImportSpecifierInfo::Named { + local: named.local.to_string(), + imported: named.imported.as_ref().map(|i| match i { + ModuleExportName::Ident(i) => i.to_string(), + _ => panic!("non-ident imported is not supported when tree shaking"), + }), + }); + defined_idents.insert(named.local.to_string()); + } + swc_ecma_ast::ImportSpecifier::Default(default) => { + if !is_ident_used(&default.local.to_string()) { + continue; + } + + specifiers.push(ImportSpecifierInfo::Default(default.local.to_string())); + defined_idents.insert(default.local.to_string()); + } + } + } + + // mark empty specifiers as self-executed so it will be preserved + if specifiers.is_empty() { + is_self_executed = true; + } + + imports = Some(ImportInfo { + source, + specifiers, + stmt_id: *id, + }); + } + swc_ecma_ast::ModuleDecl::ExportAll(export_all) => { + exports = Some(ExportInfo { + source: Some(export_all.src.value.to_string()), + specifiers: vec![ExportSpecifierInfo::All(None)], + stmt_id: *id, + }) + } + swc_ecma_ast::ModuleDecl::ExportDecl(export_decl) => { + match &export_decl.decl { + swc_ecma_ast::Decl::Class(class_decl) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Named { local: class_decl.ident.to_string(), exported: None }], + stmt_id: *id, + }); + defined_idents.insert(class_decl.ident.to_string()); + analyze_and_insert_used_idents(&class_decl.class, Some(class_decl.ident.to_string())); + }, + swc_ecma_ast::Decl::Fn(fn_decl) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Named { local: fn_decl.ident.to_string(), exported: None }], + stmt_id: *id, + }); + defined_idents.insert(fn_decl.ident.to_string()); + analyze_and_insert_used_idents(&fn_decl.function, Some(fn_decl.ident.to_string())); + }, + swc_ecma_ast::Decl::Var(var_decl) => { + let mut specifiers = vec![]; + + for v_decl in &var_decl.decls { + + let mut defined_idents_collector = DefinedIdentsCollector::new(); + v_decl.name.visit_with(&mut defined_idents_collector); + let mut used_idents_collector = UsedIdentsCollector::new(); + + if let Some(init) = &v_decl.init { + init.visit_with(&mut used_idents_collector); + } + + let mut local_used_idents = HashSet::new(); + local_used_idents.extend(used_idents_collector.used_idents); + local_used_idents.extend(defined_idents_collector.used_idents); + used_idents.extend(local_used_idents.clone()); + + for defined_ident in defined_idents_collector.defined_idents { + if !is_ident_used(&defined_ident.to_string()) { + continue; + } + + specifiers.push(ExportSpecifierInfo::Named { local: defined_ident.to_string(), exported: None }); + defined_idents.insert(defined_ident.clone()); + defined_idents_map.insert(defined_ident.clone(), local_used_idents.clone()); + } + } + + exports = Some(ExportInfo { + source: None, + specifiers, + stmt_id: *id, + }); + }, + _ => unreachable!("export_decl.decl should not be anything other than a class, function, or variable declaration"), + } + } + swc_ecma_ast::ModuleDecl::ExportDefaultDecl(export_default_decl) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Default], + stmt_id: *id, + }); + match &export_default_decl.decl { + swc_ecma_ast::DefaultDecl::Class(class_expr) => { + if let Some(ident) = &class_expr.ident { + defined_idents.insert(ident.to_string()); + } + analyze_and_insert_used_idents(&class_expr.class, class_expr.ident.as_ref().map(|i| i.to_string())); + } + swc_ecma_ast::DefaultDecl::Fn(fn_decl) => { + if let Some(ident) = &fn_decl.ident { + defined_idents.insert(ident.to_string()); + } + analyze_and_insert_used_idents(&fn_decl.function, fn_decl.ident.as_ref().map(|i| i.to_string())); + } + _ => unreachable!( + "export_default_decl.decl should not be anything other than a class, function" + ), + } + } + swc_ecma_ast::ModuleDecl::ExportDefaultExpr(export_default_expr) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Default], + stmt_id: *id, + }); + analyze_and_insert_used_idents(&export_default_expr.expr, None); + } + swc_ecma_ast::ModuleDecl::ExportNamed(export_named) => { + let mut specifiers = vec![]; + + for specifier in &export_named.specifiers { + match specifier { + swc_ecma_ast::ExportSpecifier::Named(named) => { + let local = match &named.orig { + ModuleExportName::Ident(i) => i.clone(), + ModuleExportName::Str(_) => unimplemented!("exporting a string is not supported"), + }; + + if !is_ident_used(&local.to_string()) { + continue; + } + + if export_named.src.is_none() { + used_idents.insert(local.to_string()); + defined_idents_map.insert(local.to_string(), [local.to_string()].into()); + } + + specifiers.push(ExportSpecifierInfo::Named { + local: local.to_string(), + exported: named.exported.as_ref().map(|i| match i { + ModuleExportName::Ident(i) => i.to_string(), + _ => panic!("non-ident exported is not supported when tree shaking"), + }), + }); + } + swc_ecma_ast::ExportSpecifier::Default(_) => { + unreachable!("ExportSpecifier::Default is not valid esm syntax") + } + swc_ecma_ast::ExportSpecifier::Namespace(ns) => { + let ident = match &ns.name { + ModuleExportName::Ident(ident) => ident.to_string(), + ModuleExportName::Str(_) => unreachable!("exporting a string is not supported"), + }; + + specifiers.push(ExportSpecifierInfo::Namespace(ident)); + } + } + } + + exports = Some(ExportInfo { + source: export_named.src.as_ref().map(|s| s.value.to_string()), + specifiers, + stmt_id: *id, + }); + } + _ => {} + }, + ModuleItem::Stmt(stmt) => match stmt { + swc_ecma_ast::Stmt::Block(block) => { + is_self_executed = true; + analyze_and_insert_used_idents(block, None); + } + swc_ecma_ast::Stmt::Empty(_) => {} + swc_ecma_ast::Stmt::Debugger(_) => {} + swc_ecma_ast::Stmt::With(with) => { + is_self_executed = true; + analyze_and_insert_used_idents(with, None) + } + swc_ecma_ast::Stmt::Return(_) => { + unreachable!("return statement should not be present in a module root") + } + swc_ecma_ast::Stmt::Labeled(label) => { + is_self_executed = true; + analyze_and_insert_used_idents(label, None) + } + swc_ecma_ast::Stmt::Break(_) => { + unreachable!("break statement should not be present in a module root") + } + swc_ecma_ast::Stmt::Continue(_) => { + unreachable!("continue statement should not be present in a module root") + } + swc_ecma_ast::Stmt::If(if_stmt) => { + is_self_executed = true; + analyze_and_insert_used_idents(if_stmt, None) + } + swc_ecma_ast::Stmt::Switch(switch_stmt) => { + is_self_executed = true; + analyze_and_insert_used_idents(switch_stmt, None) + } + swc_ecma_ast::Stmt::Throw(throw) => { + is_self_executed = true; + analyze_and_insert_used_idents(throw, None) + } + swc_ecma_ast::Stmt::Try(try_stmt) => { + is_self_executed = true; + analyze_and_insert_used_idents(try_stmt, None) + } + swc_ecma_ast::Stmt::While(while_stmt) => { + is_self_executed = true; + analyze_and_insert_used_idents(while_stmt, None) + } + swc_ecma_ast::Stmt::DoWhile(do_while) => { + is_self_executed = true; + analyze_and_insert_used_idents(do_while, None) + } + swc_ecma_ast::Stmt::For(for_stmt) => { + is_self_executed = true; + analyze_and_insert_used_idents(for_stmt, None) + } + + swc_ecma_ast::Stmt::ForIn(for_in) => { + is_self_executed = true; + analyze_and_insert_used_idents(for_in, None) + } + swc_ecma_ast::Stmt::ForOf(for_of) => { + is_self_executed = true; + analyze_and_insert_used_idents(for_of, None) + } + swc_ecma_ast::Stmt::Decl(decl) => match decl { + swc_ecma_ast::Decl::Class(class_decl) => { + defined_idents.insert(class_decl.ident.to_string()); + analyze_and_insert_used_idents(&class_decl.class, Some(class_decl.ident.to_string())); + } + swc_ecma_ast::Decl::Fn(fn_decl) => { + defined_idents.insert(fn_decl.ident.to_string()); + analyze_and_insert_used_idents(&fn_decl.function, Some(fn_decl.ident.to_string())); + } + swc_ecma_ast::Decl::Var(var_decl) => { + for v_decl in &var_decl.decls { + let mut defined_idents_collector = DefinedIdentsCollector::new(); + v_decl.name.visit_with(&mut defined_idents_collector); + let mut used_idents_collector = UsedIdentsCollector::new(); + + if let Some(init) = &v_decl.init { + init.visit_with(&mut used_idents_collector); + } + + let mut local_used_idents = HashSet::new(); + local_used_idents.extend(used_idents_collector.used_idents); + local_used_idents.extend(defined_idents_collector.used_idents); + used_idents.extend(local_used_idents.clone()); + + for defined_ident in defined_idents_collector.defined_idents { + defined_idents.insert(defined_ident.clone()); + defined_idents_map.insert(defined_ident.clone(), local_used_idents.clone()); + } + } + } + _ => unreachable!( + "decl should not be anything other than a class, function, or variable declaration" + ), + }, + swc_ecma_ast::Stmt::Expr(expr) => { + is_self_executed = true; + analyze_and_insert_used_idents(expr, None) + } + }, + }; + + StatementInfo { + import_info: imports, + export_info: exports, + defined_idents, + used_idents, + defined_idents_map, + is_self_executed, + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/statement_graph/defined_idents_collector.rs b/crates/mako/src/plugins/farm_tree_shake/statement_graph/defined_idents_collector.rs new file mode 100644 index 000000000..290c26d62 --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/statement_graph/defined_idents_collector.rs @@ -0,0 +1,63 @@ +use std::collections::HashSet; + +use swc_ecma_ast::{ObjectPatProp, Pat}; +use swc_ecma_visit::{Visit, VisitWith}; + +use super::used_idents_collector::UsedIdentsCollector; + +pub struct DefinedIdentsCollector { + pub defined_idents: HashSet, + pub used_idents: HashSet, +} + +impl DefinedIdentsCollector { + pub fn new() -> Self { + Self { + defined_idents: HashSet::new(), + used_idents: HashSet::new(), + } + } +} + +impl Visit for DefinedIdentsCollector { + fn visit_pat(&mut self, pat: &Pat) { + match pat { + Pat::Ident(bi) => { + self.defined_idents.insert(bi.id.to_string()); + } + Pat::Array(array_pat) => { + for elem in array_pat.elems.iter().flatten() { + self.visit_pat(elem); + } + } + Pat::Rest(rest_pat) => { + self.visit_pat(&rest_pat.arg); + } + Pat::Object(obj_pat) => { + for prop in &obj_pat.props { + match prop { + ObjectPatProp::KeyValue(kv_prop) => { + self.visit_pat(&kv_prop.value); + } + ObjectPatProp::Assign(assign_prop) => { + self.defined_idents.insert(assign_prop.key.to_string()); + + let mut used_idents_collector = UsedIdentsCollector::new(); + assign_prop.value.visit_with(&mut used_idents_collector); + + self.used_idents.extend(used_idents_collector.used_idents); + } + ObjectPatProp::Rest(rest_prop) => { + self.visit_pat(&rest_prop.arg); + } + } + } + } + Pat::Assign(assign_pat) => { + self.visit_pat(&assign_pat.left); + } + Pat::Invalid(_) => {} + Pat::Expr(_) => {} + } + } +} diff --git a/crates/mako/src/plugins/farm_tree_shake/statement_graph/used_idents_collector.rs b/crates/mako/src/plugins/farm_tree_shake/statement_graph/used_idents_collector.rs new file mode 100644 index 000000000..2abec82b3 --- /dev/null +++ b/crates/mako/src/plugins/farm_tree_shake/statement_graph/used_idents_collector.rs @@ -0,0 +1,22 @@ +use std::collections::HashSet; + +use swc_ecma_ast::Ident; +use swc_ecma_visit::Visit; + +pub struct UsedIdentsCollector { + pub used_idents: HashSet, +} + +impl UsedIdentsCollector { + pub fn new() -> Self { + Self { + used_idents: HashSet::new(), + } + } +} + +impl Visit for UsedIdentsCollector { + fn visit_ident(&mut self, ident: &Ident) { + self.used_idents.insert(ident.to_string()); + } +} diff --git a/crates/mako/src/plugins/javascript.rs b/crates/mako/src/plugins/javascript.rs index f182546dd..15116f62b 100644 --- a/crates/mako/src/plugins/javascript.rs +++ b/crates/mako/src/plugins/javascript.rs @@ -4,7 +4,6 @@ use anyhow::Result; use crate::ast::build_js_ast; use crate::compiler::Context; -use crate::config::Mode; use crate::load::{read_content, Content}; use crate::module::ModuleAst; use crate::plugin::{Plugin, PluginLoadParam, PluginParseParam}; @@ -21,24 +20,22 @@ impl Plugin for JavaScriptPlugin { param.ext_name.as_str(), "js" | "jsx" | "ts" | "tsx" | "cjs" | "mjs" ) { - let mut content = read_content(param.path.as_str())?; - // TODO: use array entry instead - if param.is_entry - && context.config.hmr - && context.config.mode == Mode::Development - && context.args.watch - { + if param.is_entry && param.request.has_query("hmr") { let port = &context.config.hmr_port.to_string(); let host = &context.config.hmr_host.to_string(); let host = if host == "0.0.0.0" { "127.0.0.1" } else { host }; - content = format!( + let content = format!("require(\"{}\");", param.path.as_str()); + let content = format!( "{}\n{}\n", + include_str!("../runtime/runtime_hmr_entry.js"), content, - include_str!("../runtime/runtime_hmr_entry.js") ) .replace("__PORT__", port) .replace("__HOST__", host); + return Ok(Some(Content::Js(content))); } + + let content = read_content(param.path.as_str())?; return Ok(Some(Content::Js(content))); } Ok(None) diff --git a/crates/mako/src/plugins/mod.rs b/crates/mako/src/plugins/mod.rs index d2679aabe..06d35b1ec 100644 --- a/crates/mako/src/plugins/mod.rs +++ b/crates/mako/src/plugins/mod.rs @@ -1,6 +1,7 @@ pub mod assets; pub mod copy; pub mod css; +pub mod farm_tree_shake; pub mod javascript; pub mod json; pub mod less; diff --git a/crates/mako/src/plugins/runtime.rs b/crates/mako/src/plugins/runtime.rs index 9b4d255ae..e47eb54ae 100644 --- a/crates/mako/src/plugins/runtime.rs +++ b/crates/mako/src/plugins/runtime.rs @@ -1,9 +1,25 @@ use std::sync::Arc; -use anyhow::Result; +use anyhow::{anyhow, Result}; +use swc_common::DUMMY_SP as span; +use swc_ecma_ast::{ + BlockStmt, Expr, FnExpr, Function, Module, ModuleItem, ObjectLit, PropOrSpread, Stmt, + UnaryExpr, UnaryOp, +}; +use swc_ecma_utils::{quote_ident, ExprFactory, StmtOrModuleItem}; +use crate::ast::{build_js_ast, js_ast_to_code}; +use crate::build::Task; use crate::compiler::Context; +use crate::generate_chunks::build_props; +use crate::load::read_content; +use crate::module::ModuleAst::Script; +use crate::module::{Dependency, ModuleAst, ResolveType}; use crate::plugin::Plugin; +use crate::resolve::resolve; +use crate::transform::transform; +use crate::transform_dep_replacer::DependenciesToReplace; +use crate::transform_in_generate::{transform_js_generate, TransformJsParam}; pub struct MakoRuntime {} @@ -13,7 +29,10 @@ impl Plugin for MakoRuntime { } fn runtime_plugins(&self, context: &Arc) -> Result> { - let plugins = vec![self.public_path(context)]; + let plugins = vec![ + self.public_path(context), + self.helper_runtime(context).unwrap(), + ]; Ok(plugins) } } @@ -36,4 +55,149 @@ impl MakoRuntime { public_path ) } + + fn helper_runtime(&self, context: &Arc) -> Result { + let helpers = [ + "@swc/helpers/_/_interop_require_default", + "@swc/helpers/_/_interop_require_wildcard", + "@swc/helpers/_/_export_star", + ]; + + let props = helpers + .into_iter() + .map(|source| self.build_module_prop(source.to_string(), context).unwrap()) + .collect::>(); + + let obj_expr = ObjectLit { span, props }; + + let module = Module { + span, + body: vec![ModuleItem::Stmt( + UnaryExpr { + op: UnaryOp::Bang, + span, + arg: Box::new(Expr::Call( + FnExpr { + ident: None, + function: Box::new(Function { + params: vec![], + decorators: vec![], + span, + body: Some(BlockStmt { + span, + stmts: vec![quote_ident!("registerModules") + // registerModules({}) + .as_call(span, vec![obj_expr.as_arg()]) + .into_stmt()], + }), + is_generator: false, + is_async: false, + type_params: None, + return_type: None, + }), + } + .as_iife(), + )), + } + .into_stmt(), + )], + shebang: None, + }; + + let (code, _) = js_ast_to_code(&module, context, "dummy.js").unwrap(); + + Ok(code) + } + + fn build_module_prop(&self, source: String, context: &Arc) -> Result { + let virtual_js = context.root.join("__v.js"); + + let resolved = resolve( + virtual_js.to_str().unwrap(), + &Dependency { + source: source.clone(), + order: 0, + span: None, + resolve_type: ResolveType::Import, + }, + &context.resolvers, + context, + )? + .get_resolved_path(); + + let content = read_content(&resolved)?; + + let ast = build_js_ast(&resolved, &content, context)?; + let mut script = ModuleAst::Script(ast); + + transform( + &mut script, + context, + &Task { + path: resolved, + is_entry: false, + parent_resource: None, + }, + &context.resolvers, + )?; + + let module_id = source.into(); + + let mut ast = if let Script(ast) = script { + ast + } else { + unreachable!() + }; + + transform_js_generate(TransformJsParam { + is_entry: false, + is_async: false, + top_level_await: false, + dep_map: &DependenciesToReplace { + resolved: Default::default(), + missing: Default::default(), + ignored: vec![], + }, + async_deps: &Vec::::new(), + _id: &module_id, + context, + ast: &mut ast, + }); + + let stmts: Result> = ast + .ast + .body + .into_iter() + .map(|s| { + s.into_stmt() + .map_err(|e| anyhow!("{:?} not a statement!", e)) + }) + .collect(); + let stmts = stmts.unwrap(); + + let factor_decl = FnExpr { + ident: None, + function: Box::new(Function { + params: vec![ + quote_ident!("module").into(), + quote_ident!("exports").into(), + quote_ident!("require").into(), + ], + is_async: false, + span, + decorators: vec![], + return_type: None, + type_params: None, + body: Some(BlockStmt { stmts, span }), + is_generator: false, + }), + }; + + let obj_prop = build_props( + &module_id.generate(context), + Box::new(Expr::Fn(factor_decl)), + ); + + Ok(obj_prop) + } } diff --git a/crates/mako/src/runtime/runtime_entry.js b/crates/mako/src/runtime/runtime_entry.js index 5b445aa48..8b4a86c57 100644 --- a/crates/mako/src/runtime/runtime_entry.js +++ b/crates/mako/src/runtime/runtime_entry.js @@ -47,8 +47,6 @@ function createRuntime(makoModules, entryModuleId) { // module execution interceptor requireModule.requireInterceptors = []; - // __inject_runtime_code__ - // mako/runtime/hmr plugin !(function () { let currentParents = []; @@ -193,7 +191,7 @@ function createRuntime(makoModules, entryModuleId) { ].join('.'); return new Promise((done) => { - const url = `hot_update/${hotChunkName}`; + const url = `/hot_update/${hotChunkName}`; requireModule.loadScript(url, done); }); }), @@ -474,6 +472,8 @@ function createRuntime(makoModules, entryModuleId) { return requireModule._h; }; + // __inject_runtime_code__ + // __WASM_REQUIRE_SUPPORT // __REQUIRE_ASYNC_MODULE_SUPPORT // __BEFORE_ENTRY diff --git a/crates/mako/src/snapshots/mako__hmr__tests__generate_hmr_chunk.snap b/crates/mako/src/snapshots/mako__hmr__tests__generate_hmr_chunk.snap index b030c9109..a7b71bdfa 100644 --- a/crates/mako/src/snapshots/mako__hmr__tests__generate_hmr_chunk.snap +++ b/crates/mako/src/snapshots/mako__hmr__tests__generate_hmr_chunk.snap @@ -2,4 +2,4 @@ source: crates/mako/src/hmr.rs expression: js_code.trim() --- -"globalThis.makoModuleHotUpdate('./index.ts', {\n modules: {\n \"../../../../../node_modules/.pnpm/@swc+helpers@0.5.1/node_modules/@swc/helpers/esm/_interop_require_wildcard.js\": function(module, exports, require) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", {\n value: true\n });\n function _export(target, all) {\n for(var name in all)Object.defineProperty(target, name, {\n enumerable: true,\n get: all[name]\n });\n }\n _export(exports, {\n _interop_require_wildcard: function() {\n return _interop_require_wildcard;\n },\n _: function() {\n return _interop_require_wildcard;\n }\n });\n function _getRequireWildcardCache(nodeInterop) {\n if (typeof WeakMap !== \"function\") return null;\n var cacheBabelInterop = new WeakMap();\n var cacheNodeInterop = new WeakMap();\n return (_getRequireWildcardCache = function(nodeInterop) {\n return nodeInterop ? cacheNodeInterop : cacheBabelInterop;\n })(nodeInterop);\n }\n function _interop_require_wildcard(obj, nodeInterop) {\n if (!nodeInterop && obj && obj.__esModule) return obj;\n if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") return {\n default: obj\n };\n var cache = _getRequireWildcardCache(nodeInterop);\n if (cache && cache.has(obj)) return cache.get(obj);\n var newObj = {};\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n for(var key in obj){\n if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;\n if (desc && (desc.get || desc.set)) Object.defineProperty(newObj, key, desc);\n else newObj[key] = obj[key];\n }\n }\n newObj.default = obj;\n if (cache) cache.set(obj, newObj);\n return newObj;\n }\n },\n \"../../../../../node_modules/.pnpm/react-refresh@0.14.0/node_modules/react-refresh/cjs/react-refresh-runtime.development.js\": function(module, exports, require) {\n 'use strict';\n {\n (function() {\n 'use strict';\n var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');\n var REACT_MEMO_TYPE = Symbol.for('react.memo');\n var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;\n var allFamiliesByID = new Map();\n var allFamiliesByType = new PossiblyWeakMap();\n var allSignaturesByType = new PossiblyWeakMap();\n var updatedFamiliesByType = new PossiblyWeakMap();\n var pendingUpdates = [];\n var helpersByRendererID = new Map();\n var helpersByRoot = new Map();\n var mountedRoots = new Set();\n var failedRoots = new Set();\n var rootElements = typeof WeakMap === 'function' ? new WeakMap() : null;\n var isPerformingRefresh = false;\n function computeFullKey(signature) {\n if (signature.fullKey !== null) {\n return signature.fullKey;\n }\n var fullKey = signature.ownKey;\n var hooks;\n try {\n hooks = signature.getCustomHooks();\n } catch (err) {\n signature.forceReset = true;\n signature.fullKey = fullKey;\n return fullKey;\n }\n for(var i = 0; i < hooks.length; i++){\n var hook = hooks[i];\n if (typeof hook !== 'function') {\n signature.forceReset = true;\n signature.fullKey = fullKey;\n return fullKey;\n }\n var nestedHookSignature = allSignaturesByType.get(hook);\n if (nestedHookSignature === undefined) {\n continue;\n }\n var nestedHookKey = computeFullKey(nestedHookSignature);\n if (nestedHookSignature.forceReset) {\n signature.forceReset = true;\n }\n fullKey += '\\n---\\n' + nestedHookKey;\n }\n signature.fullKey = fullKey;\n return fullKey;\n }\n function haveEqualSignatures(prevType, nextType) {\n var prevSignature = allSignaturesByType.get(prevType);\n var nextSignature = allSignaturesByType.get(nextType);\n if (prevSignature === undefined && nextSignature === undefined) {\n return true;\n }\n if (prevSignature === undefined || nextSignature === undefined) {\n return false;\n }\n if (computeFullKey(prevSignature) !== computeFullKey(nextSignature)) {\n return false;\n }\n if (nextSignature.forceReset) {\n return false;\n }\n return true;\n }\n function isReactClass(type) {\n return type.prototype && type.prototype.isReactComponent;\n }\n function canPreserveStateBetween(prevType, nextType) {\n if (isReactClass(prevType) || isReactClass(nextType)) {\n return false;\n }\n if (haveEqualSignatures(prevType, nextType)) {\n return true;\n }\n return false;\n }\n function resolveFamily(type) {\n return updatedFamiliesByType.get(type);\n }\n function cloneMap(map) {\n var clone = new Map();\n map.forEach(function(value, key) {\n clone.set(key, value);\n });\n return clone;\n }\n function cloneSet(set) {\n var clone = new Set();\n set.forEach(function(value) {\n clone.add(value);\n });\n return clone;\n }\n function getProperty(object, property) {\n try {\n return object[property];\n } catch (err) {\n return undefined;\n }\n }\n function performReactRefresh() {\n if (pendingUpdates.length === 0) {\n return null;\n }\n if (isPerformingRefresh) {\n return null;\n }\n isPerformingRefresh = true;\n try {\n var staleFamilies = new Set();\n var updatedFamilies = new Set();\n var updates = pendingUpdates;\n pendingUpdates = [];\n updates.forEach(function(_ref) {\n var family = _ref[0], nextType = _ref[1];\n var prevType = family.current;\n updatedFamiliesByType.set(prevType, family);\n updatedFamiliesByType.set(nextType, family);\n family.current = nextType;\n if (canPreserveStateBetween(prevType, nextType)) {\n updatedFamilies.add(family);\n } else {\n staleFamilies.add(family);\n }\n });\n var update = {\n updatedFamilies: updatedFamilies,\n staleFamilies: staleFamilies\n };\n helpersByRendererID.forEach(function(helpers) {\n helpers.setRefreshHandler(resolveFamily);\n });\n var didError = false;\n var firstError = null;\n var failedRootsSnapshot = cloneSet(failedRoots);\n var mountedRootsSnapshot = cloneSet(mountedRoots);\n var helpersByRootSnapshot = cloneMap(helpersByRoot);\n failedRootsSnapshot.forEach(function(root) {\n var helpers = helpersByRootSnapshot.get(root);\n if (helpers === undefined) {\n throw new Error('Could not find helpers for a root. This is a bug in React Refresh.');\n }\n if (!failedRoots.has(root)) {}\n if (rootElements === null) {\n return;\n }\n if (!rootElements.has(root)) {\n return;\n }\n var element = rootElements.get(root);\n try {\n helpers.scheduleRoot(root, element);\n } catch (err) {\n if (!didError) {\n didError = true;\n firstError = err;\n }\n }\n });\n mountedRootsSnapshot.forEach(function(root) {\n var helpers = helpersByRootSnapshot.get(root);\n if (helpers === undefined) {\n throw new Error('Could not find helpers for a root. This is a bug in React Refresh.');\n }\n if (!mountedRoots.has(root)) {}\n try {\n helpers.scheduleRefresh(root, update);\n } catch (err) {\n if (!didError) {\n didError = true;\n firstError = err;\n }\n }\n });\n if (didError) {\n throw firstError;\n }\n return update;\n } finally{\n isPerformingRefresh = false;\n }\n }\n function register(type, id) {\n {\n if (type === null) {\n return;\n }\n if (typeof type !== 'function' && typeof type !== 'object') {\n return;\n }\n if (allFamiliesByType.has(type)) {\n return;\n }\n var family = allFamiliesByID.get(id);\n if (family === undefined) {\n family = {\n current: type\n };\n allFamiliesByID.set(id, family);\n } else {\n pendingUpdates.push([\n family,\n type\n ]);\n }\n allFamiliesByType.set(type, family);\n if (typeof type === 'object' && type !== null) {\n switch(getProperty(type, '$$typeof')){\n case REACT_FORWARD_REF_TYPE:\n register(type.render, id + '$render');\n break;\n case REACT_MEMO_TYPE:\n register(type.type, id + '$type');\n break;\n }\n }\n }\n }\n function setSignature(type, key) {\n var forceReset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n var getCustomHooks = arguments.length > 3 ? arguments[3] : undefined;\n {\n if (!allSignaturesByType.has(type)) {\n allSignaturesByType.set(type, {\n forceReset: forceReset,\n ownKey: key,\n fullKey: null,\n getCustomHooks: getCustomHooks || function() {\n return [];\n }\n });\n }\n if (typeof type === 'object' && type !== null) {\n switch(getProperty(type, '$$typeof')){\n case REACT_FORWARD_REF_TYPE:\n setSignature(type.render, key, forceReset, getCustomHooks);\n break;\n case REACT_MEMO_TYPE:\n setSignature(type.type, key, forceReset, getCustomHooks);\n break;\n }\n }\n }\n }\n function collectCustomHooksForSignature(type) {\n {\n var signature = allSignaturesByType.get(type);\n if (signature !== undefined) {\n computeFullKey(signature);\n }\n }\n }\n function getFamilyByID(id) {\n {\n return allFamiliesByID.get(id);\n }\n }\n function getFamilyByType(type) {\n {\n return allFamiliesByType.get(type);\n }\n }\n function findAffectedHostInstances(families) {\n {\n var affectedInstances = new Set();\n mountedRoots.forEach(function(root) {\n var helpers = helpersByRoot.get(root);\n if (helpers === undefined) {\n throw new Error('Could not find helpers for a root. This is a bug in React Refresh.');\n }\n var instancesForRoot = helpers.findHostInstancesForRefresh(root, families);\n instancesForRoot.forEach(function(inst) {\n affectedInstances.add(inst);\n });\n });\n return affectedInstances;\n }\n }\n function injectIntoGlobalHook(globalObject) {\n {\n var hook = globalObject.__REACT_DEVTOOLS_GLOBAL_HOOK__;\n if (hook === undefined) {\n var nextID = 0;\n globalObject.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook = {\n renderers: new Map(),\n supportsFiber: true,\n inject: function(injected) {\n return nextID++;\n },\n onScheduleFiberRoot: function(id, root, children) {},\n onCommitFiberRoot: function(id, root, maybePriorityLevel, didError) {},\n onCommitFiberUnmount: function() {}\n };\n }\n if (hook.isDisabled) {\n console['warn']('Something has shimmed the React DevTools global hook (__REACT_DEVTOOLS_GLOBAL_HOOK__). ' + 'Fast Refresh is not compatible with this shim and will be disabled.');\n return;\n }\n var oldInject = hook.inject;\n hook.inject = function(injected) {\n var id = oldInject.apply(this, arguments);\n if (typeof injected.scheduleRefresh === 'function' && typeof injected.setRefreshHandler === 'function') {\n helpersByRendererID.set(id, injected);\n }\n return id;\n };\n hook.renderers.forEach(function(injected, id) {\n if (typeof injected.scheduleRefresh === 'function' && typeof injected.setRefreshHandler === 'function') {\n helpersByRendererID.set(id, injected);\n }\n });\n var oldOnCommitFiberRoot = hook.onCommitFiberRoot;\n var oldOnScheduleFiberRoot = hook.onScheduleFiberRoot || function() {};\n hook.onScheduleFiberRoot = function(id, root, children) {\n if (!isPerformingRefresh) {\n failedRoots.delete(root);\n if (rootElements !== null) {\n rootElements.set(root, children);\n }\n }\n return oldOnScheduleFiberRoot.apply(this, arguments);\n };\n hook.onCommitFiberRoot = function(id, root, maybePriorityLevel, didError) {\n var helpers = helpersByRendererID.get(id);\n if (helpers !== undefined) {\n helpersByRoot.set(root, helpers);\n var current = root.current;\n var alternate = current.alternate;\n if (alternate !== null) {\n var wasMounted = alternate.memoizedState != null && alternate.memoizedState.element != null && mountedRoots.has(root);\n var isMounted = current.memoizedState != null && current.memoizedState.element != null;\n if (!wasMounted && isMounted) {\n mountedRoots.add(root);\n failedRoots.delete(root);\n } else if (wasMounted && isMounted) ;\n else if (wasMounted && !isMounted) {\n mountedRoots.delete(root);\n if (didError) {\n failedRoots.add(root);\n } else {\n helpersByRoot.delete(root);\n }\n } else if (!wasMounted && !isMounted) {\n if (didError) {\n failedRoots.add(root);\n }\n }\n } else {\n mountedRoots.add(root);\n }\n }\n return oldOnCommitFiberRoot.apply(this, arguments);\n };\n }\n }\n function hasUnrecoverableErrors() {\n return false;\n }\n function _getMountedRootCount() {\n {\n return mountedRoots.size;\n }\n }\n function createSignatureFunctionForTransform() {\n {\n var savedType;\n var hasCustomHooks;\n var didCollectHooks = false;\n return function(type, key, forceReset, getCustomHooks) {\n if (typeof key === 'string') {\n if (!savedType) {\n savedType = type;\n hasCustomHooks = typeof getCustomHooks === 'function';\n }\n if (type != null && (typeof type === 'function' || typeof type === 'object')) {\n setSignature(type, key, forceReset, getCustomHooks);\n }\n return type;\n } else {\n if (!didCollectHooks && hasCustomHooks) {\n didCollectHooks = true;\n collectCustomHooksForSignature(savedType);\n }\n }\n };\n }\n }\n function isLikelyComponentType(type) {\n {\n switch(typeof type){\n case 'function':\n {\n if (type.prototype != null) {\n if (type.prototype.isReactComponent) {\n return true;\n }\n var ownNames = Object.getOwnPropertyNames(type.prototype);\n if (ownNames.length > 1 || ownNames[0] !== 'constructor') {\n return false;\n }\n if (type.prototype.__proto__ !== Object.prototype) {\n return false;\n }\n }\n var name = type.name || type.displayName;\n return typeof name === 'string' && /^[A-Z]/.test(name);\n }\n case 'object':\n {\n if (type != null) {\n switch(getProperty(type, '$$typeof')){\n case REACT_FORWARD_REF_TYPE:\n case REACT_MEMO_TYPE:\n return true;\n default:\n return false;\n }\n }\n return false;\n }\n default:\n {\n return false;\n }\n }\n }\n }\n exports._getMountedRootCount = _getMountedRootCount;\n exports.collectCustomHooksForSignature = collectCustomHooksForSignature;\n exports.createSignatureFunctionForTransform = createSignatureFunctionForTransform;\n exports.findAffectedHostInstances = findAffectedHostInstances;\n exports.getFamilyByID = getFamilyByID;\n exports.getFamilyByType = getFamilyByType;\n exports.hasUnrecoverableErrors = hasUnrecoverableErrors;\n exports.injectIntoGlobalHook = injectIntoGlobalHook;\n exports.isLikelyComponentType = isLikelyComponentType;\n exports.performReactRefresh = performReactRefresh;\n exports.register = register;\n exports.setSignature = setSignature;\n })();\n }\n },\n \"../../../../../node_modules/.pnpm/react-refresh@0.14.0/node_modules/react-refresh/runtime.js\": function(module, exports, require) {\n 'use strict';\n {\n module.exports = require(\"../../../../../node_modules/.pnpm/react-refresh@0.14.0/node_modules/react-refresh/cjs/react-refresh-runtime.development.js\");\n }\n },\n \"./bar_1.ts\": function(module, exports, require) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", {\n value: true\n });\n require(\"./foo.ts\");\n },\n \"./foo.ts\": function(module, exports, require) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", {\n value: true\n });\n Object.defineProperty(exports, \"default\", {\n enumerable: true,\n get: function() {\n return _default;\n }\n });\n var _default = 1;\n },\n \"./bar_2.ts\": function(module, exports, require) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", {\n value: true\n });\n require(\"./foo.ts\");\n },\n \"./hoo\": function(module, exports, require) {\n \"use strict\";\n module.exports = hoo;\n },\n \"../../../../../node_modules/.pnpm/process-okam@0.11.10/node_modules/process-okam/browser.js\": function(module, exports, require) {\n \"use strict\";\n var process = module.exports = {};\n var cachedSetTimeout;\n var cachedClearTimeout;\n function defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n }\n function defaultClearTimeout() {\n throw new Error('clearTimeout has not been defined');\n }\n (function() {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n })();\n function runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n return setTimeout(fun, 0);\n }\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n return cachedSetTimeout(fun, 0);\n } catch (e) {\n try {\n return cachedSetTimeout.call(null, fun, 0);\n } catch (e) {\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n }\n function runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n return clearTimeout(marker);\n }\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n return cachedClearTimeout(marker);\n } catch (e) {\n try {\n return cachedClearTimeout.call(null, marker);\n } catch (e) {\n return cachedClearTimeout.call(this, marker);\n }\n }\n }\n var queue = [];\n var draining = false;\n var currentQueue;\n var queueIndex = -1;\n function cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n }\n function drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n var len = queue.length;\n while(len){\n currentQueue = queue;\n queue = [];\n while(++queueIndex < len){\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n }\n process.nextTick = function(fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for(var i = 1; i < arguments.length; i++){\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n };\n function Item(fun, array) {\n this.fun = fun;\n this.array = array;\n }\n Item.prototype.run = function() {\n this.fun.apply(null, this.array);\n };\n process.title = 'browser';\n process.browser = true;\n process.env = {};\n process.argv = [];\n process.version = '';\n process.versions = {};\n function noop() {}\n process.on = noop;\n process.addListener = noop;\n process.once = noop;\n process.off = noop;\n process.removeListener = noop;\n process.removeAllListeners = noop;\n process.emit = noop;\n process.prependListener = noop;\n process.prependOnceListener = noop;\n process.listeners = function(name) {\n return [];\n };\n process.binding = function(name) {\n throw new Error('process.binding is not supported');\n };\n process.cwd = function() {\n return '/';\n };\n process.chdir = function(dir) {\n throw new Error('process.chdir is not supported');\n };\n process.umask = function() {\n return 0;\n };\n },\n \"../../../../../node_modules/.pnpm/node-libs-browser-okam@2.2.4/node_modules/node-libs-browser-okam/polyfill/process.js\": function(module, exports, require) {\n \"use strict\";\n module.exports = require(\"../../../../../node_modules/.pnpm/process-okam@0.11.10/node_modules/process-okam/browser.js\");\n },\n \"../../../../../node_modules/.pnpm/react-error-overlay@6.0.0/node_modules/react-error-overlay/lib/index.js\": function(module, exports, require) {\n \"use strict\";\n !function(e, t) {\n \"object\" == typeof exports && \"object\" == typeof module ? module.exports = t() : \"function\" == typeof define && define.amd ? define([], t) : \"object\" == typeof exports ? exports.ReactErrorOverlay = t() : e.ReactErrorOverlay = t();\n }(window, function() {\n return function(e) {\n var t = {};\n function n(r) {\n if (t[r]) return t[r].exports;\n var u = t[r] = {\n i: r,\n l: !1,\n exports: {}\n };\n return e[r].call(u.exports, u, u.exports, n), u.l = !0, u.exports;\n }\n return n.m = e, n.c = t, n.d = function(e, t, r) {\n n.o(e, t) || Object.defineProperty(e, t, {\n enumerable: !0,\n get: r\n });\n }, n.r = function(e) {\n \"undefined\" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {\n value: \"Module\"\n }), Object.defineProperty(e, \"__esModule\", {\n value: !0\n });\n }, n.t = function(e, t) {\n if (1 & t && (e = n(e)), 8 & t) return e;\n if (4 & t && \"object\" == typeof e && e && e.__esModule) return e;\n var r = Object.create(null);\n if (n.r(r), Object.defineProperty(r, \"default\", {\n enumerable: !0,\n value: e\n }), 2 & t && \"string\" != typeof e) for(var u in e)n.d(r, u, (function(t) {\n return e[t];\n }).bind(null, u));\n return r;\n }, n.n = function(e) {\n var t = e && e.__esModule ? function() {\n return e.default;\n } : function() {\n return e;\n };\n return n.d(t, \"a\", t), t;\n }, n.o = function(e, t) {\n return Object.prototype.hasOwnProperty.call(e, t);\n }, n.p = \"\", n(n.s = 16);\n }([\n function(e, t, n) {\n e.exports = n(9);\n },\n function(e, t) {\n t.getArg = function(e, t, n) {\n if (t in e) return e[t];\n if (3 === arguments.length) return n;\n throw new Error('\"' + t + '\" is a required argument.');\n };\n var n = /^(?:([\\w+\\-.]+):)?\\/\\/(?:(\\w+:\\w+)@)?([\\w.]*)(?::(\\d+))?(\\S*)$/, r = /^data:.+\\,.+$/;\n function u(e) {\n var t = e.match(n);\n return t ? {\n scheme: t[1],\n auth: t[2],\n host: t[3],\n port: t[4],\n path: t[5]\n } : null;\n }\n function o(e) {\n var t = \"\";\n return e.scheme && (t += e.scheme + \":\"), t += \"//\", e.auth && (t += e.auth + \"@\"), e.host && (t += e.host), e.port && (t += \":\" + e.port), e.path && (t += e.path), t;\n }\n function i(e) {\n var n = e, r = u(e);\n if (r) {\n if (!r.path) return e;\n n = r.path;\n }\n for(var i, a = t.isAbsolute(n), l = n.split(/\\/+/), s = 0, c = l.length - 1; c >= 0; c--)\".\" === (i = l[c]) ? l.splice(c, 1) : \"..\" === i ? s++ : s > 0 && (\"\" === i ? (l.splice(c + 1, s), s = 0) : (l.splice(c, 2), s--));\n return \"\" === (n = l.join(\"/\")) && (n = a ? \"/\" : \".\"), r ? (r.path = n, o(r)) : n;\n }\n t.urlParse = u, t.urlGenerate = o, t.normalize = i, t.join = function(e, t) {\n \"\" === e && (e = \".\"), \"\" === t && (t = \".\");\n var n = u(t), a = u(e);\n if (a && (e = a.path || \"/\"), n && !n.scheme) return a && (n.scheme = a.scheme), o(n);\n if (n || t.match(r)) return t;\n if (a && !a.host && !a.path) return a.host = t, o(a);\n var l = \"/\" === t.charAt(0) ? t : i(e.replace(/\\/+$/, \"\") + \"/\" + t);\n return a ? (a.path = l, o(a)) : l;\n }, t.isAbsolute = function(e) {\n return \"/\" === e.charAt(0) || !!e.match(n);\n }, t.relative = function(e, t) {\n \"\" === e && (e = \".\"), e = e.replace(/\\/$/, \"\");\n for(var n = 0; 0 !== t.indexOf(e + \"/\");){\n var r = e.lastIndexOf(\"/\");\n if (r < 0) return t;\n if ((e = e.slice(0, r)).match(/^([^\\/]+:\\/)?\\/*$/)) return t;\n ++n;\n }\n return Array(n + 1).join(\"../\") + t.substr(e.length + 1);\n };\n var a = !(\"__proto__\" in Object.create(null));\n function l(e) {\n return e;\n }\n function s(e) {\n if (!e) return !1;\n var t = e.length;\n if (t < 9) return !1;\n if (95 !== e.charCodeAt(t - 1) || 95 !== e.charCodeAt(t - 2) || 111 !== e.charCodeAt(t - 3) || 116 !== e.charCodeAt(t - 4) || 111 !== e.charCodeAt(t - 5) || 114 !== e.charCodeAt(t - 6) || 112 !== e.charCodeAt(t - 7) || 95 !== e.charCodeAt(t - 8) || 95 !== e.charCodeAt(t - 9)) return !1;\n for(var n = t - 10; n >= 0; n--)if (36 !== e.charCodeAt(n)) return !1;\n return !0;\n }\n function c(e, t) {\n return e === t ? 0 : e > t ? 1 : -1;\n }\n t.toSetString = a ? l : function(e) {\n return s(e) ? \"$\" + e : e;\n }, t.fromSetString = a ? l : function(e) {\n return s(e) ? e.slice(1) : e;\n }, t.compareByOriginalPositions = function(e, t, n) {\n var r = e.source - t.source;\n return 0 !== r ? r : 0 != (r = e.originalLine - t.originalLine) ? r : 0 != (r = e.originalColumn - t.originalColumn) || n ? r : 0 != (r = e.generatedColumn - t.generatedColumn) ? r : 0 != (r = e.generatedLine - t.generatedLine) ? r : e.name - t.name;\n }, t.compareByGeneratedPositionsDeflated = function(e, t, n) {\n var r = e.generatedLine - t.generatedLine;\n return 0 !== r ? r : 0 != (r = e.generatedColumn - t.generatedColumn) || n ? r : 0 != (r = e.source - t.source) ? r : 0 != (r = e.originalLine - t.originalLine) ? r : 0 != (r = e.originalColumn - t.originalColumn) ? r : e.name - t.name;\n }, t.compareByGeneratedPositionsInflated = function(e, t) {\n var n = e.generatedLine - t.generatedLine;\n return 0 !== n ? n : 0 != (n = e.generatedColumn - t.generatedColumn) ? n : 0 !== (n = c(e.source, t.source)) ? n : 0 != (n = e.originalLine - t.originalLine) ? n : 0 != (n = e.originalColumn - t.originalColumn) ? n : c(e.name, t.name);\n };\n },\n function(e, t) {\n function n(e, t) {\n for(var n = 0, r = e.length - 1; r >= 0; r--){\n var u = e[r];\n \".\" === u ? e.splice(r, 1) : \"..\" === u ? (e.splice(r, 1), n++) : n && (e.splice(r, 1), n--);\n }\n if (t) for(; n--; n)e.unshift(\"..\");\n return e;\n }\n var r = /^(\\/?|)([\\s\\S]*?)((?:\\.{1,2}|[^\\/]+?|)(\\.[^.\\/]*|))(?:[\\/]*)$/, u = function(e) {\n return r.exec(e).slice(1);\n };\n function o(e, t) {\n if (e.filter) return e.filter(t);\n for(var n = [], r = 0; r < e.length; r++)t(e[r], r, e) && n.push(e[r]);\n return n;\n }\n t.resolve = function() {\n for(var e = \"\", t = !1, r = arguments.length - 1; r >= -1 && !t; r--){\n var u = r >= 0 ? arguments[r] : require(\"../../../../../node_modules/.pnpm/node-libs-browser-okam@2.2.4/node_modules/node-libs-browser-okam/polyfill/process.js\").cwd();\n if (\"string\" != typeof u) throw new TypeError(\"Arguments to path.resolve must be strings\");\n u && (e = u + \"/\" + e, t = \"/\" === u.charAt(0));\n }\n return (t ? \"/\" : \"\") + (e = n(o(e.split(\"/\"), function(e) {\n return !!e;\n }), !t).join(\"/\")) || \".\";\n }, t.normalize = function(e) {\n var r = t.isAbsolute(e), u = \"/\" === i(e, -1);\n return (e = n(o(e.split(\"/\"), function(e) {\n return !!e;\n }), !r).join(\"/\")) || r || (e = \".\"), e && u && (e += \"/\"), (r ? \"/\" : \"\") + e;\n }, t.isAbsolute = function(e) {\n return \"/\" === e.charAt(0);\n }, t.join = function() {\n var e = Array.prototype.slice.call(arguments, 0);\n return t.normalize(o(e, function(e, t) {\n if (\"string\" != typeof e) throw new TypeError(\"Arguments to path.join must be strings\");\n return e;\n }).join(\"/\"));\n }, t.relative = function(e, n) {\n function r(e) {\n for(var t = 0; t < e.length && \"\" === e[t]; t++);\n for(var n = e.length - 1; n >= 0 && \"\" === e[n]; n--);\n return t > n ? [] : e.slice(t, n - t + 1);\n }\n e = t.resolve(e).substr(1), n = t.resolve(n).substr(1);\n for(var u = r(e.split(\"/\")), o = r(n.split(\"/\")), i = Math.min(u.length, o.length), a = i, l = 0; l < i; l++)if (u[l] !== o[l]) {\n a = l;\n break;\n }\n var s = [];\n for(l = a; l < u.length; l++)s.push(\"..\");\n return (s = s.concat(o.slice(a))).join(\"/\");\n }, t.sep = \"/\", t.delimiter = \":\", t.dirname = function(e) {\n var t = u(e), n = t[0], r = t[1];\n return n || r ? (r && (r = r.substr(0, r.length - 1)), n + r) : \".\";\n }, t.basename = function(e, t) {\n var n = u(e)[2];\n return t && n.substr(-1 * t.length) === t && (n = n.substr(0, n.length - t.length)), n;\n }, t.extname = function(e) {\n return u(e)[3];\n };\n var i = \"b\" === \"ab\".substr(-1) ? function(e, t, n) {\n return e.substr(t, n);\n } : function(e, t, n) {\n return t < 0 && (t = e.length + t), e.substr(t, n);\n };\n },\n function(e, t, n) {\n t.SourceMapGenerator = n(4).SourceMapGenerator, t.SourceMapConsumer = n(12).SourceMapConsumer, t.SourceNode = n(15).SourceNode;\n },\n function(e, t, n) {\n var r = n(5), u = n(1), o = n(6).ArraySet, i = n(11).MappingList;\n function a(e) {\n e || (e = {}), this._file = u.getArg(e, \"file\", null), this._sourceRoot = u.getArg(e, \"sourceRoot\", null), this._skipValidation = u.getArg(e, \"skipValidation\", !1), this._sources = new o, this._names = new o, this._mappings = new i, this._sourcesContents = null;\n }\n a.prototype._version = 3, a.fromSourceMap = function(e) {\n var t = e.sourceRoot, n = new a({\n file: e.file,\n sourceRoot: t\n });\n return e.eachMapping(function(e) {\n var r = {\n generated: {\n line: e.generatedLine,\n column: e.generatedColumn\n }\n };\n null != e.source && (r.source = e.source, null != t && (r.source = u.relative(t, r.source)), r.original = {\n line: e.originalLine,\n column: e.originalColumn\n }, null != e.name && (r.name = e.name)), n.addMapping(r);\n }), e.sources.forEach(function(t) {\n var r = e.sourceContentFor(t);\n null != r && n.setSourceContent(t, r);\n }), n;\n }, a.prototype.addMapping = function(e) {\n var t = u.getArg(e, \"generated\"), n = u.getArg(e, \"original\", null), r = u.getArg(e, \"source\", null), o = u.getArg(e, \"name\", null);\n this._skipValidation || this._validateMapping(t, n, r, o), null != r && (r = String(r), this._sources.has(r) || this._sources.add(r)), null != o && (o = String(o), this._names.has(o) || this._names.add(o)), this._mappings.add({\n generatedLine: t.line,\n generatedColumn: t.column,\n originalLine: null != n && n.line,\n originalColumn: null != n && n.column,\n source: r,\n name: o\n });\n }, a.prototype.setSourceContent = function(e, t) {\n var n = e;\n null != this._sourceRoot && (n = u.relative(this._sourceRoot, n)), null != t ? (this._sourcesContents || (this._sourcesContents = Object.create(null)), this._sourcesContents[u.toSetString(n)] = t) : this._sourcesContents && (delete this._sourcesContents[u.toSetString(n)], 0 === Object.keys(this._sourcesContents).length && (this._sourcesContents = null));\n }, a.prototype.applySourceMap = function(e, t, n) {\n var r = t;\n if (null == t) {\n if (null == e.file) throw new Error('SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map\\'s \"file\" property. Both were omitted.');\n r = e.file;\n }\n var i = this._sourceRoot;\n null != i && (r = u.relative(i, r));\n var a = new o, l = new o;\n this._mappings.unsortedForEach(function(t) {\n if (t.source === r && null != t.originalLine) {\n var o = e.originalPositionFor({\n line: t.originalLine,\n column: t.originalColumn\n });\n null != o.source && (t.source = o.source, null != n && (t.source = u.join(n, t.source)), null != i && (t.source = u.relative(i, t.source)), t.originalLine = o.line, t.originalColumn = o.column, null != o.name && (t.name = o.name));\n }\n var s = t.source;\n null == s || a.has(s) || a.add(s);\n var c = t.name;\n null == c || l.has(c) || l.add(c);\n }, this), this._sources = a, this._names = l, e.sources.forEach(function(t) {\n var r = e.sourceContentFor(t);\n null != r && (null != n && (t = u.join(n, t)), null != i && (t = u.relative(i, t)), this.setSourceContent(t, r));\n }, this);\n }, a.prototype._validateMapping = function(e, t, n, r) {\n if ((!(e && \"line\" in e && \"column\" in e && e.line > 0 && e.column >= 0) || t || n || r) && !(e && \"line\" in e && \"column\" in e && t && \"line\" in t && \"column\" in t && e.line > 0 && e.column >= 0 && t.line > 0 && t.column >= 0 && n)) throw new Error(\"Invalid mapping: \" + JSON.stringify({\n generated: e,\n source: n,\n original: t,\n name: r\n }));\n }, a.prototype._serializeMappings = function() {\n for(var e, t, n, o, i = 0, a = 1, l = 0, s = 0, c = 0, f = 0, p = \"\", d = this._mappings.toArray(), h = 0, m = d.length; h < m; h++){\n if (e = \"\", (t = d[h]).generatedLine !== a) for(i = 0; t.generatedLine !== a;)e += \";\", a++;\n else if (h > 0) {\n if (!u.compareByGeneratedPositionsInflated(t, d[h - 1])) continue;\n e += \",\";\n }\n e += r.encode(t.generatedColumn - i), i = t.generatedColumn, null != t.source && (o = this._sources.indexOf(t.source), e += r.encode(o - f), f = o, e += r.encode(t.originalLine - 1 - s), s = t.originalLine - 1, e += r.encode(t.originalColumn - l), l = t.originalColumn, null != t.name && (n = this._names.indexOf(t.name), e += r.encode(n - c), c = n)), p += e;\n }\n return p;\n }, a.prototype._generateSourcesContent = function(e, t) {\n return e.map(function(e) {\n if (!this._sourcesContents) return null;\n null != t && (e = u.relative(t, e));\n var n = u.toSetString(e);\n return Object.prototype.hasOwnProperty.call(this._sourcesContents, n) ? this._sourcesContents[n] : null;\n }, this);\n }, a.prototype.toJSON = function() {\n var e = {\n version: this._version,\n sources: this._sources.toArray(),\n names: this._names.toArray(),\n mappings: this._serializeMappings()\n };\n return null != this._file && (e.file = this._file), null != this._sourceRoot && (e.sourceRoot = this._sourceRoot), this._sourcesContents && (e.sourcesContent = this._generateSourcesContent(e.sources, e.sourceRoot)), e;\n }, a.prototype.toString = function() {\n return JSON.stringify(this.toJSON());\n }, t.SourceMapGenerator = a;\n },\n function(e, t, n) {\n var r = n(10);\n t.encode = function(e) {\n var t, n = \"\", u = function(e) {\n return e < 0 ? 1 + (-e << 1) : 0 + (e << 1);\n }(e);\n do {\n t = 31 & u, (u >>>= 5) > 0 && (t |= 32), n += r.encode(t);\n }while (u > 0)\n return n;\n }, t.decode = function(e, t, n) {\n var u, o, i, a, l = e.length, s = 0, c = 0;\n do {\n if (t >= l) throw new Error(\"Expected more digits in base 64 VLQ value.\");\n if (-1 === (o = r.decode(e.charCodeAt(t++)))) throw new Error(\"Invalid base64 digit: \" + e.charAt(t - 1));\n u = !!(32 & o), s += (o &= 31) << c, c += 5;\n }while (u)\n n.value = (a = (i = s) >> 1, 1 == (1 & i) ? -a : a), n.rest = t;\n };\n },\n function(e, t, n) {\n var r = n(1), u = Object.prototype.hasOwnProperty;\n function o() {\n this._array = [], this._set = Object.create(null);\n }\n o.fromArray = function(e, t) {\n for(var n = new o, r = 0, u = e.length; r < u; r++)n.add(e[r], t);\n return n;\n }, o.prototype.size = function() {\n return Object.getOwnPropertyNames(this._set).length;\n }, o.prototype.add = function(e, t) {\n var n = r.toSetString(e), o = u.call(this._set, n), i = this._array.length;\n o && !t || this._array.push(e), o || (this._set[n] = i);\n }, o.prototype.has = function(e) {\n var t = r.toSetString(e);\n return u.call(this._set, t);\n }, o.prototype.indexOf = function(e) {\n var t = r.toSetString(e);\n if (u.call(this._set, t)) return this._set[t];\n throw new Error('\"' + e + '\" is not in the set.');\n }, o.prototype.at = function(e) {\n if (e >= 0 && e < this._array.length) return this._array[e];\n throw new Error(\"No element indexed by \" + e);\n }, o.prototype.toArray = function() {\n return this._array.slice();\n }, t.ArraySet = o;\n },\n function(e, t, n) {\n \"use strict\";\n function r(e) {\n return Array.isArray(e) || (e = [\n e\n ]), Promise.all(e.map(function(e) {\n return e.then(function(e) {\n return {\n isFulfilled: !0,\n isRejected: !1,\n value: e\n };\n }).catch(function(e) {\n return {\n isFulfilled: !1,\n isRejected: !0,\n reason: e\n };\n });\n }));\n }\n Object.defineProperty(t, \"__esModule\", {\n value: !0\n }), t.settle = r, t.default = r;\n },\n function(e, t) {\n e.exports = '!function(e){var t={};function n(r){if(t[r])return t[r].exports;var u=t[r]={i:r,l:!1,exports:{}};return e[r].call(u.exports,u,u.exports,n),u.l=!0,u.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\"undefined\"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\"object\"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\"default\",{enumerable:!0,value:e}),2&t&&\"string\"!=typeof e)for(var u in e)n.d(r,u,function(t){return e[t]}.bind(null,u));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\"a\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\"\",n(n.s=202)}([function(e,t,n){\"use strict\";e.exports=n(178)},function(e,t,n){var r=n(5),u=n(34).f,o=n(17),i=n(21),a=n(38),l=n(58),c=n(54);e.exports=function(e,t){var n,s,f,d,p,h=e.target,m=e.global,g=e.stat;if(n=m?r:g?r[h]||a(h,{}):(r[h]||{}).prototype)for(s in t){if(d=t[s],f=e.noTargetGet?(p=u(n,s))&&p.value:n[s],!c(m?s:h+(g?\".\":\"#\")+s,e.forced)&&void 0!==f){if(typeof d===typeof f)continue;l(d,f)}(e.sham||f&&f.sham)&&o(d,\"sham\",!0),i(n,s,d,e)}}},function(e,t,n){var r=n(10);e.exports=function(e){if(!r(e))throw TypeError(String(e)+\" is not an object\");return e}},function(e,t){e.exports=!1},function(e,t){e.exports=function(e){if(\"function\"!=typeof e)throw TypeError(String(e)+\" is not a function\");return e}},function(e,t){e.exports=\"object\"==typeof window&&window&&window.Math==Math?window:\"object\"==typeof self&&self&&self.Math==Math?self:Function(\"return this\")()},function(e,t,n){var r=n(24)(\"wks\"),u=n(30),o=n(5).Symbol,i=n(62);e.exports=function(e){return r[e]||(r[e]=i&&o[e]||(i?o:u)(\"Symbol.\"+e))}},function(e,t,n){var r=n(4);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,u){return e.call(t,n,r,u)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){var r=n(71),u=n(11),o=n(77),i=n(13).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});u(t,e)||i(t,e,{value:o.f(e)})}},function(e,t,n){var r=n(2),u=n(61),o=n(31),i=n(7),a=n(43),l=n(64),c={};(e.exports=function(e,t,n,s,f){var d,p,h,m,g,v=i(t,n,s?2:1);if(f)d=e;else{if(\"function\"!=typeof(p=a(e)))throw TypeError(\"Target is not iterable\");if(u(p)){for(h=0,m=o(e.length);m>h;h++)if((s?v(r(g=e[h])[0],g[1]):v(e[h]))===c)return c;return}d=p.call(e)}for(;!(g=d.next()).done;)if(l(d,v,g.value,s)===c)return c}).BREAK=c},function(e,t){e.exports=function(e){return\"object\"===typeof e?null!==e:\"function\"===typeof e}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){var r=n(16),u=n(55),o=n(2),i=n(28),a=Object.defineProperty;t.f=r?a:function(e,t,n){if(o(e),t=i(t,!0),o(n),u)try{return a(e,t,n)}catch(e){}if(\"get\"in n||\"set\"in n)throw TypeError(\"Accessors not supported\");return\"value\"in n&&(e[t]=n.value),e}},function(e,t,n){var r=n(71),u=n(5),o=function(e){return\"function\"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(r[e])||o(u[e]):r[e]&&r[e][t]||u[e]&&u[e][t]}},function(e,t,n){var r=n(3),u=n(47);e.exports=r?u:function(e){return Map.prototype.entries.call(e)}},function(e,t,n){e.exports=!n(12)(function(){return 7!=Object.defineProperty({},\"a\",{get:function(){return 7}}).a})},function(e,t,n){var r=n(13),u=n(23);e.exports=n(16)?function(e,t,n){return r.f(e,t,u(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var r=n(2),u=n(4),o=n(6)(\"species\");e.exports=function(e,t){var n,i=r(e).constructor;return void 0===i||void 0==(n=r(i)[o])?t:u(n)}},function(e,t,n){var r=n(3),u=n(47);e.exports=r?u:function(e){return Set.prototype.values.call(e)}},function(e,t,n){var r=n(87),u=n(37);e.exports=function(e){return r(u(e))}},function(e,t,n){var r=n(5),u=n(17),o=n(11),i=n(38),a=n(57),l=n(25),c=l.get,s=l.enforce,f=String(a).split(\"toString\");n(24)(\"inspectSource\",function(e){return a.call(e)}),(e.exports=function(e,t,n,a){var l=!!a&&!!a.unsafe,c=!!a&&!!a.enumerable,d=!!a&&!!a.noTargetGet;\"function\"==typeof n&&(\"string\"!=typeof t||o(n,\"name\")||u(n,\"name\",t),s(n).source=f.join(\"string\"==typeof t?t:\"\")),e!==r?(l?!d&&e[t]&&(c=!0):delete e[t],c?e[t]=n:u(e,t,n)):c?e[t]=n:i(t,n)})(Function.prototype,\"toString\",function(){return\"function\"==typeof this&&c(this).source||a.call(this)})},function(e,t,n){var r=n(13).f,u=n(11),o=n(6)(\"toStringTag\");e.exports=function(e,t,n){e&&!u(e=n?e:e.prototype,o)&&r(e,o,{configurable:!0,value:t})}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){var r=n(5),u=n(38),o=r[\"__core-js_shared__\"]||u(\"__core-js_shared__\",{});(e.exports=function(e,t){return o[e]||(o[e]=void 0!==t?t:{})})(\"versions\",[]).push({version:\"3.0.1\",mode:n(3)?\"pure\":\"global\",copyright:\"© 2019 Denis Pushkarev (zloirock.ru)\"})},function(e,t,n){var r,u,o,i=n(88),a=n(10),l=n(17),c=n(11),s=n(29),f=n(26),d=n(5).WeakMap;if(i){var p=new d,h=p.get,m=p.has,g=p.set;r=function(e,t){return g.call(p,e,t),t},u=function(e){return h.call(p,e)||{}},o=function(e){return m.call(p,e)}}else{var v=s(\"state\");f[v]=!0,r=function(e,t){return l(e,v,t),t},u=function(e){return c(e,v)?e[v]:{}},o=function(e){return c(e,v)}}e.exports={set:r,get:u,has:o,enforce:function(e){return o(e)?u(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!a(t)||(n=u(t)).type!==e)throw TypeError(\"Incompatible receiver, \"+e+\" required\");return n}}}},function(e,t){e.exports={}},function(e,t){e.exports={}},function(e,t,n){var r=n(10);e.exports=function(e,t){if(!r(e))return e;var n,u;if(t&&\"function\"==typeof(n=e.toString)&&!r(u=n.call(e)))return u;if(\"function\"==typeof(n=e.valueOf)&&!r(u=n.call(e)))return u;if(!t&&\"function\"==typeof(n=e.toString)&&!r(u=n.call(e)))return u;throw TypeError(\"Can\\'t convert object to primitive value\")}},function(e,t,n){var r=n(24)(\"keys\"),u=n(30);e.exports=function(e){return r[e]||(r[e]=u(e))}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return\"Symbol(\".concat(void 0===e?\"\":e,\")_\",(++n+r).toString(36))}},function(e,t,n){var r=n(40),u=Math.min;e.exports=function(e){return e>0?u(r(e),9007199254740991):0}},function(e,t,n){var r=n(2),u=n(95),o=n(41),i=n(96),a=n(56),l=n(29)(\"IE_PROTO\"),c=function(){},s=function(){var e,t=a(\"iframe\"),n=o.length;for(t.style.display=\"none\",i.appendChild(t),t.src=String(\"javascript:\"),(e=t.contentWindow.document).open(),e.write(\"