diff --git a/crates/mako/src/dev.rs b/crates/mako/src/dev.rs index 69a93ae6d..208e820be 100644 --- a/crates/mako/src/dev.rs +++ b/crates/mako/src/dev.rs @@ -7,6 +7,7 @@ use futures::{SinkExt, StreamExt}; use hyper::header::CONTENT_TYPE; use hyper::http::HeaderValue; use hyper::Server; +use regex::Regex; use tokio::sync::broadcast::{Receiver, Sender}; use tokio::task::JoinHandle; use tokio::try_join; @@ -70,7 +71,7 @@ impl DevServer { } let arc_watcher = self.watcher.clone(); let compiler = self.compiler.clone(); - let handle_request = move |req: hyper::Request| { + let handle_request = move |mut req: hyper::Request| { let for_fn = compiler.clone(); let w = arc_watcher.clone(); async move { @@ -79,6 +80,15 @@ 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/hot_update"), + ); + + // 去除 publicPath 头尾 / + let public_path = for_fn.context.config.public_path.clone(); + let public_path_without_fix = + public_path.trim_start_matches('/').trim_end_matches('/'); + match path { "__/hmr-ws" => { if hyper_tungstenite::is_upgrade_request(&req) { @@ -102,9 +112,42 @@ impl DevServer { ) } } - _ => { - // try chunk content in memory first, else use dist content - match static_serve.serve(req).await { + _ if path.starts_with(public_path_without_fix) => { + // 如果用户设置了 public_path,修改一下原始 req,手动复制 req 担心掉属性 + if !public_path.is_empty() { + let public_path_re = Regex::new(public_path_without_fix).unwrap(); + let uri_str = public_path_re + .replacen(req.uri().to_string().as_str(), 1, "") + .to_string(); + let uri_cloned = uri_str.as_str().parse::().unwrap(); + *req.uri_mut() = uri_cloned; + }; + + // clone 一份 req,用于做 hmr 的匹配 + let herders_cloned = req.headers().clone(); + let uri_cloned = req.uri().clone(); + let mut req_cloned = hyper::Request::builder() + .method(hyper::Method::GET) + .uri(uri_cloned) + .body(hyper::Body::empty()) + .unwrap(); + req_cloned.headers_mut().extend(herders_cloned); + + // 先匹配 hmr 请求静态资源请求,用复制的 req + let static_serve_hmr_result = static_serve_hmr.serve(req_cloned).await; + let serve_result = match static_serve_hmr_result { + Ok(res) => { + if res.status() == hyper::StatusCode::OK { + Ok(res) + } else { + static_serve.serve(req).await + } + } + _ => static_serve.serve(req).await, + }; + + // 后续处理 + match serve_result { Ok(mut res) => { if let Some(content_type) = res.headers().get(CONTENT_TYPE).cloned() { @@ -132,6 +175,12 @@ impl DevServer { ), } } + _ => Ok::<_, hyper::Error>( + hyper::Response::builder() + .status(hyper::StatusCode::NOT_FOUND) + .body(hyper::Body::from("404 - Page not found")) + .unwrap(), + ), } } }; diff --git a/crates/mako/src/generate.rs b/crates/mako/src/generate.rs index 87af77d2c..acb4e2f0a 100644 --- a/crates/mako/src/generate.rs +++ b/crates/mako/src/generate.rs @@ -337,13 +337,13 @@ impl Compiler { let (code, sourcemap) = self.generate_hmr_chunk(chunk, &filename, &modified_ids, current_full_hash)?; // TODO the final format should be {name}.{full_hash}.hot-update.{ext} - self.write_to_dist(&filename, code); - self.write_to_dist(format!("{}.map", &filename), sourcemap); + self.write_to_hot_update_dir(&filename, code); + self.write_to_hot_update_dir(format!("{}.map", &filename), sourcemap); } } 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, @@ -372,15 +372,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 712fc6f4c..871b8c1d4 100644 --- a/crates/mako/src/runtime/runtime_entry.js +++ b/crates/mako/src/runtime/runtime_entry.js @@ -170,7 +170,6 @@ function createRuntime(makoModules, entryModuleId) { invalidate() {}, check() { const current_hash = requireModule.currentHash(); - return fetch( `${requireModule.publicPath}${current_hash}.hot-update.json`, ) 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/examples/with-dynamic-import/mako.config.json b/examples/with-dynamic-import/mako.config.json index f87e64f01..c0d75741e 100644 --- a/examples/with-dynamic-import/mako.config.json +++ b/examples/with-dynamic-import/mako.config.json @@ -1,4 +1,4 @@ { - "public_path": "/", + "publicPath": "/", "codeSplitting": "none" } diff --git a/packages/bundler-okam/index.js b/packages/bundler-okam/index.js index 96fe7dcec..db4f61193 100644 --- a/packages/bundler-okam/index.js +++ b/packages/bundler-okam/index.js @@ -58,8 +58,15 @@ exports.dev = async function (opts) { } // before middlewares (opts.beforeMiddlewares || []).forEach((m) => app.use(m)); + + // 暂时无法读到 mako.config 里的 publicPath + const publicPath = opts.config.publicPath || '/'; // serve dist files - app.use(express.static(path.join(opts.cwd, 'dist'))); + app.use( + publicPath, + express.static(path.join(opts.cwd, 'node_modules/.mako/hot_update')), + ); + app.use(publicPath, express.static(path.join(opts.cwd, 'dist'))); // TODO: proxy // opts.config.proxy // after middlewares @@ -155,7 +162,7 @@ function getOkamConfig(opts) { }, }, mode: 'development', - public_path: runtimePublicPath ? 'runtime' : publicPath || '/', + publicPath: runtimePublicPath ? 'runtime' : publicPath || '/', targets: targets || { chrome: 80, },