diff --git a/Cargo.lock b/Cargo.lock index dcdb7af2f..451c0fb5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -834,14 +834,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", - "windows-targets 0.48.0", + "wasm-bindgen", + "windows-targets 0.52.6", ] [[package]] @@ -2614,6 +2616,7 @@ dependencies = [ "base64 0.21.2", "bitflags 2.4.2", "cached", + "chrono", "clap", "colored", "config", @@ -7182,6 +7185,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7194,6 +7213,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7206,6 +7231,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7218,6 +7249,18 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7230,6 +7273,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7242,6 +7291,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -7254,6 +7309,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7266,6 +7327,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winit" version = "0.28.7" diff --git a/crates/binding/src/lib.rs b/crates/binding/src/lib.rs index 9ebd024fc..4601f0f18 100644 --- a/crates/binding/src/lib.rs +++ b/crates/binding/src/lib.rs @@ -217,7 +217,7 @@ pub fn build(env: Env, build_params: BuildParams) -> napi::Result { } let d = DevServer::new(root.clone(), Arc::new(compiler)); deferred.resolve(move |env| env.get_undefined()); - d.serve(move |_params| {}).await; + d.serve().await; Ok(()) }, move |&mut _, _res| Ok(()), diff --git a/crates/binding/src/tsfn.rs b/crates/binding/src/tsfn.rs index 214057d9f..4fd5754e2 100644 --- a/crates/binding/src/tsfn.rs +++ b/crates/binding/src/tsfn.rs @@ -15,10 +15,47 @@ pub struct JsHooks { ts_type = "(filePath: string) => Promise<{ content: string, type: 'css'|'js' } | void> | void;" )] pub load: Option, - #[napi(ts_type = "(data: {isFirstCompile: boolean; time: number; stats: { - startTime: number; - endTime: number; - }}) =>void ;")] + #[napi(ts_type = r#"(data: { + isFirstCompile: boolean; + time: number; + stats: { + hash: number; + builtAt: number; + rootPath: string; + outputPath: string; + assets: { type: string; name: string; path: string; size: number }[]; + chunkModules: { + type: string; + id: string; + chunks: string[]; + size: number; + }[]; + modules: Record< + string, + { id: string; dependents: string[]; dependencies: string[] } + >; + chunks: { + type: string; + id: string; + files: string[]; + entry: boolean; + modules: { type: string; id: string; size: number; chunks: string[] }[]; + siblings: string[]; + origin: { + module: string; + moduleIdentifier: string; + moduleName: string; + loc: string; + request: string; + }[]; + }[]; + entrypoints: Record; + rscClientComponents: { path; string; moduleId: string }[]; + rscCSSModules: { path; string; moduleId: string; modules: boolean }[]; + startTime: number; + endTime: number; + }; + }) => void"#)] pub generate_end: Option, #[napi(ts_type = "(path: string, content: Buffer) => Promise;")] pub _on_generate_file: Option, @@ -67,27 +104,9 @@ impl TsFnHooks { |ctx: threadsafe_function::ThreadSafeCallContext< ReadMessage, >| { - let mut obj = ctx.env.create_object()?; - let mut stats = ctx.env.create_object()?; - stats.set_named_property( - "startTime", - ctx.env - .create_int64(ctx.value.message.stats.start_time as i64)?, - )?; - stats.set_named_property( - "endTime", - ctx.env - .create_int64(ctx.value.message.stats.end_time as i64)?, - )?; - obj.set_named_property( - "isFirstCompile", - ctx.value.message.is_first_compile, - )?; - obj.set_named_property( - "time", - ctx.env.create_int64(ctx.value.message.time as i64), - )?; - obj.set_named_property("stats", stats)?; + let obj = ctx + .env + .to_js_value(&serde_json::to_value(ctx.value.message)?)?; let result = ctx.callback.unwrap().call(None, &[obj])?; await_promise_with_void(ctx.env, result, ctx.value.tx).unwrap(); Ok(()) diff --git a/crates/mako/Cargo.toml b/crates/mako/Cargo.toml index 4ee267541..a1aa94eb8 100644 --- a/crates/mako/Cargo.toml +++ b/crates/mako/Cargo.toml @@ -73,6 +73,7 @@ swc_node_comments = "0.19.1" anyhow = "1.0.71" base64 = "0.21.2" +chrono = "0.4.38" clap = { version = "4.3.11", features = ["derive"] } colored = "2" config = "0.13.3" diff --git a/crates/mako/src/compiler.rs b/crates/mako/src/compiler.rs index e6e4b8a7d..2047878a7 100644 --- a/crates/mako/src/compiler.rs +++ b/crates/mako/src/compiler.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::fs; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; -use std::time::{Instant, UNIX_EPOCH}; +use std::time::Instant; use anyhow::{anyhow, Error, Result}; use colored::Colorize; @@ -17,7 +17,7 @@ use crate::config::{Config, OutputMode}; use crate::generate::chunk_graph::ChunkGraph; use crate::generate::optimize_chunk::OptimizeChunksInfo; use crate::module_graph::ModuleGraph; -use crate::plugin::{Plugin, PluginDriver, PluginGenerateEndParams, PluginGenerateStats}; +use crate::plugin::{Plugin, PluginDriver, PluginGenerateEndParams}; use crate::plugins; use crate::resolve::{get_resolvers, Resolvers}; use crate::stats::StatsInfo; @@ -339,7 +339,7 @@ impl Compiler { } let t_compiler = Instant::now(); - let start_time = std::time::SystemTime::now(); + let start_time = chrono::Local::now().timestamp_millis(); let building_with_message = format!( "Building with {} for {}...", "mako".to_string().cyan(), @@ -378,6 +378,9 @@ impl Compiler { .plugin_driver .after_build(&self.context, self)?; } + + self.context.plugin_driver.before_generate(&self.context)?; + let result = { crate::mako_profile_scope!("Generate Stage"); // need to put all rayon parallel iterators run in the existed scope, or else rayon @@ -385,33 +388,32 @@ impl Compiler { thread_pool::scope(|_| self.generate()) }; let t_compiler_duration = t_compiler.elapsed(); - if result.is_ok() { - println!( - "{}", - format!( - "✓ Built in {}", - format!("{}ms", t_compiler_duration.as_millis()).bold() - ) - .green() - ); - if !self.context.args.watch { - println!("{}", "Complete!".bold()); + match result { + Ok(mut stats) => { + stats.start_time = start_time; + stats.end_time = chrono::Local::now().timestamp_millis(); + println!( + "{}", + format!( + "✓ Built in {}", + format!("{}ms", t_compiler_duration.as_millis()).bold() + ) + .green() + ); + if !self.context.args.watch { + println!("{}", "Complete!".bold()); + } + let params = PluginGenerateEndParams { + is_first_compile: true, + time: t_compiler.elapsed().as_millis() as i64, + stats, + }; + self.context + .plugin_driver + .generate_end(¶ms, &self.context)?; + Ok(()) } - let end_time = std::time::SystemTime::now(); - let params = PluginGenerateEndParams { - is_first_compile: true, - time: t_compiler.elapsed().as_millis() as u64, - stats: PluginGenerateStats { - start_time: start_time.duration_since(UNIX_EPOCH)?.as_millis() as u64, - end_time: end_time.duration_since(UNIX_EPOCH)?.as_millis() as u64, - }, - }; - self.context - .plugin_driver - .generate_end(¶ms, &self.context)?; - Ok(()) - } else { - result + Err(e) => Err(e), } } diff --git a/crates/mako/src/dev/mod.rs b/crates/mako/src/dev/mod.rs index 141a97c59..eb132bf65 100644 --- a/crates/mako/src/dev/mod.rs +++ b/crates/mako/src/dev/mod.rs @@ -4,7 +4,7 @@ mod watch; use std::net::{SocketAddr, TcpListener}; use std::path::PathBuf; use std::sync::{mpsc, Arc}; -use std::time::{Duration, Instant, UNIX_EPOCH}; +use std::time::{Duration, Instant}; use anyhow::{self, Result}; use colored::Colorize; @@ -20,7 +20,7 @@ use tungstenite::Message; use {hyper, hyper_staticfile, hyper_tungstenite, open}; use crate::compiler::{Compiler, Context}; -use crate::plugin::{PluginGenerateEndParams, PluginGenerateStats}; +use crate::plugin::PluginGenerateEndParams; use crate::utils::{process_req_url, tokio_runtime}; pub struct DevServer { @@ -33,10 +33,7 @@ impl DevServer { Self { root, compiler } } - pub async fn serve( - &self, - callback: impl Fn(OnDevCompleteParams) + Send + Sync + Clone + 'static, - ) { + pub async fn serve(&self) { let (txws, _) = broadcast::channel::(256); // watch @@ -46,11 +43,11 @@ impl DevServer { if self.compiler.context.config.dev_server.is_some() { std::thread::spawn(move || { - if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch, callback) { + if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch) { eprintln!("Error watching files: {:?}", e); } }); - } else if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch, callback) { + } else if let Err(e) = Self::watch_for_changes(root, compiler, txws_watch) { eprintln!("Error watching files: {:?}", e); } @@ -266,7 +263,6 @@ impl DevServer { root: PathBuf, compiler: Arc, txws: broadcast::Sender, - callback: impl Fn(OnDevCompleteParams) + Clone, ) -> Result<()> { let (tx, rx) = mpsc::channel(); // let mut watcher = RecommendedWatcher::new(tx, notify::Config::default())?; @@ -287,15 +283,9 @@ impl DevServer { if !paths.is_empty() { let compiler = compiler.clone(); let txws = txws.clone(); - let callback = callback.clone(); - if let Err(e) = Self::rebuild( - paths, - compiler, - txws, - &mut snapshot_hash, - &mut hmr_hash, - callback, - ) { + if let Err(e) = + Self::rebuild(paths, compiler, txws, &mut snapshot_hash, &mut hmr_hash) + { eprintln!("Error rebuilding: {:?}", e); } } @@ -310,7 +300,6 @@ impl DevServer { txws: broadcast::Sender, last_snapshot_hash: &mut Box, hmr_hash: &mut Box, - callback: impl Fn(OnDevCompleteParams), ) -> Result<()> { debug!("watch paths detected: {:?}", paths); debug!("checking update status..."); @@ -344,7 +333,7 @@ impl DevServer { } let t_compiler = Instant::now(); - let start_time = std::time::SystemTime::now(); + let start_time = chrono::Local::now().timestamp_millis(); let next_hash = compiler.generate_hot_update_chunks(res, **last_snapshot_hash, **hmr_hash); debug!( "hot update chunks generated, next_full_hash: {:?}", @@ -379,10 +368,16 @@ impl DevServer { compiler.context.stats_info.clear_assets(); - if let Err(e) = compiler.emit_dev_chunks(next_hmr_hash, current_hmr_hash) { - debug!(" > build failed: {:?}", e); - return Err(e); - } + let mut stats = compiler + .emit_dev_chunks(next_hmr_hash, current_hmr_hash) + .map_err(|e| { + debug!(" > build failed: {:?}", e); + e + })?; + + stats.start_time = start_time; + stats.end_time = chrono::Local::now().timestamp_millis(); + debug!("full rebuild...done"); if !has_missing_deps { println!( @@ -390,29 +385,16 @@ impl DevServer { format!("{}ms", t_compiler.elapsed().as_millis()).bold() ); - let end_time = std::time::SystemTime::now(); let params = PluginGenerateEndParams { is_first_compile: false, - time: t_compiler.elapsed().as_millis() as u64, - stats: PluginGenerateStats { - start_time: start_time.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64, - end_time: end_time.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64, - }, + time: t_compiler.elapsed().as_millis() as i64, + stats, }; compiler .context .plugin_driver .generate_end(¶ms, &compiler.context) .unwrap(); - // TODO: remove this? - callback(OnDevCompleteParams { - is_first_compile: false, - time: t_compiler.elapsed().as_millis() as u64, - stats: Stats { - start_time: start_time.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64, - end_time: end_time.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64, - }, - }); } let receiver_count = txws.receiver_count(); @@ -426,17 +408,6 @@ impl DevServer { } } -pub struct OnDevCompleteParams { - pub is_first_compile: bool, - pub time: u64, - pub stats: Stats, -} - -pub struct Stats { - pub start_time: u64, - pub end_time: u64, -} - #[derive(Clone, Debug)] struct WsMessage { hash: u64, diff --git a/crates/mako/src/generate/mod.rs b/crates/mako/src/generate/mod.rs index 7230ba084..c934ecae5 100644 --- a/crates/mako/src/generate/mod.rs +++ b/crates/mako/src/generate/mod.rs @@ -30,7 +30,7 @@ use crate::dev::update::UpdateResult; use crate::generate::generate_chunks::{ChunkFile, ChunkFileType}; use crate::module::{Dependency, ModuleId}; use crate::plugins::bundless_compiler::BundlessCompiler; -use crate::stats::{create_stats_info, print_stats, write_stats}; +use crate::stats::{write_stats, StatsJsonMap}; use crate::utils::base64_encode; use crate::visitors::async_module::mark_async; @@ -49,16 +49,16 @@ struct ChunksUrlMap { } impl Compiler { - fn generate_bundless(&self) -> Result<()> { + fn generate_bundless(&self) -> Result { let bundless_compiler = BundlessCompiler::new(self.context.clone()); bundless_compiler.generate()?; - let stats = create_stats_info(0, self); + let stats = self.create_stats_info(); self.context .plugin_driver .build_success(&stats, &self.context)?; - Ok(()) + Ok(stats) } fn mark_async(&self) -> HashMap> { @@ -73,9 +73,7 @@ impl Compiler { mark_async(&module_ids, &self.context) } - pub fn generate(&self) -> Result<()> { - self.context.plugin_driver.before_generate(&self.context)?; - + pub fn generate(&self) -> Result { debug!("generate"); let t_generate = Instant::now(); @@ -117,7 +115,8 @@ impl Compiler { let t_tree_shaking = t_tree_shaking.elapsed(); if self.context.config.output.mode == OutputMode::Bundless { - return self.generate_bundless(); + let stats = self.generate_bundless()?; + return Ok(stats); } let t_group_chunks = Instant::now(); @@ -181,10 +180,10 @@ impl Compiler { } // generate stats - let stats = create_stats_info(0, self); + let stats = self.create_stats_info(); if self.context.config.stats.is_some() { - write_stats(&stats, &self.context.config.output.path); + write_stats(&self.context.config.output.path, &stats); } // build_success hook @@ -194,7 +193,7 @@ impl Compiler { // print stats if !self.context.args.watch { - print_stats(self); + self.print_stats(); } if self.context.config.analyze.is_some() { @@ -215,7 +214,7 @@ impl Compiler { t_ast_to_code_and_write.as_millis() ); - Ok(()) + Ok(stats) } fn write_chunk_files(&self, full_hash: u64) -> Result<(Duration, Duration)> { @@ -268,11 +267,14 @@ impl Compiler { emit_chunk_file(&self.context, chunk_file); } - pub fn emit_dev_chunks(&self, current_hmr_hash: u64, last_hmr_hash: u64) -> Result<()> { + pub fn emit_dev_chunks( + &self, + current_hmr_hash: u64, + last_hmr_hash: u64, + ) -> Result { crate::mako_profile_function!("emit_dev_chunks"); debug!("generate(hmr-fullbuild)"); - let t_generate = Instant::now(); if self @@ -347,12 +349,13 @@ impl Compiler { } let t_write_assets = t_write_assets.elapsed(); + let stats = self.create_stats_info(); + // TODO: do not write to fs, using jsapi hooks to pass stats // why generate stats? // ref: https://github.com/umijs/mako/issues/1107 if self.context.config.stats.is_some() { - let stats = create_stats_info(0, self); - write_stats(&stats, &self.context.config.output.path); + write_stats(&self.context.config.output.path, &stats); } let t_generate = t_generate.elapsed(); @@ -368,7 +371,7 @@ impl Compiler { ); debug!(" - write assets: {}ms", t_write_assets.as_millis()); - Ok(()) + Ok(stats) } // TODO: integrate into generate fn @@ -542,12 +545,13 @@ fn write_dev_chunk_file(context: &Arc, chunk: &ChunkFile) -> Result<()> // why add chunk info in dev mode? // ref: https://github.com/umijs/mako/issues/1094 let size = code.len() as u64; + let dist_name = chunk.disk_name(); context.stats_info.add_assets( size, chunk.file_name.clone(), chunk.chunk_id.clone(), - PathBuf::from(chunk.disk_name()), - chunk.disk_name(), + dist_name.clone(), + dist_name, ); context.write_static_content(chunk.disk_name(), code, chunk.raw_hash)?; @@ -561,7 +565,9 @@ fn write_dev_chunk_file(context: &Arc, chunk: &ChunkFile) -> Result<()> fn emit_chunk_file(context: &Arc, chunk_file: &ChunkFile) { crate::mako_profile_function!(&chunk_file.file_name); - let to: PathBuf = context.config.output.path.join(chunk_file.disk_name()); + let dist_name = chunk_file.disk_name(); + + let to: PathBuf = context.config.output.path.join(dist_name.as_str()); let stats_info = &context.stats_info; match context.config.devtool { @@ -575,7 +581,7 @@ fn emit_chunk_file(context: &Arc, chunk_file: &ChunkFile) { size, chunk_file.source_map_name(), chunk_file.chunk_id.clone(), - to.clone(), + to.to_string_lossy().to_string(), chunk_file.source_map_disk_name(), ); fs::write( @@ -610,8 +616,8 @@ fn emit_chunk_file(context: &Arc, chunk_file: &ChunkFile) { size, chunk_file.file_name.clone(), chunk_file.chunk_id.clone(), - to.clone(), - chunk_file.disk_name(), + to.to_string_lossy().to_string(), + dist_name.clone(), ); fs::write(to, &code).unwrap(); } @@ -634,8 +640,8 @@ fn emit_chunk_file(context: &Arc, chunk_file: &ChunkFile) { size, chunk_file.file_name.clone(), chunk_file.chunk_id.clone(), - to.clone(), - chunk_file.disk_name(), + to.to_string_lossy().to_string(), + dist_name.clone(), ); fs::write(to, code).unwrap(); } @@ -644,8 +650,8 @@ fn emit_chunk_file(context: &Arc, chunk_file: &ChunkFile) { chunk_file.content.len() as u64, chunk_file.file_name.clone(), chunk_file.chunk_id.clone(), - to.clone(), - chunk_file.disk_name(), + to.to_string_lossy().to_string(), + dist_name, ); fs::write(to, &chunk_file.content).unwrap(); diff --git a/crates/mako/src/lib.rs b/crates/mako/src/lib.rs index b1d36032f..51d52b062 100644 --- a/crates/mako/src/lib.rs +++ b/crates/mako/src/lib.rs @@ -16,7 +16,7 @@ mod module_graph; pub mod plugin; mod plugins; mod resolve; -mod stats; +pub mod stats; pub mod utils; mod visitors; diff --git a/crates/mako/src/main.rs b/crates/mako/src/main.rs index db1e0bcb2..45691164d 100644 --- a/crates/mako/src/main.rs +++ b/crates/mako/src/main.rs @@ -96,7 +96,7 @@ async fn run() -> Result<()> { if cli.watch { let d = dev::DevServer::new(root.clone(), compiler); // TODO: when in Dev Mode, Dev Server should start asap, and provider a loading while in first compiling - d.serve(move |_params| {}).await; + d.serve().await; } } Ok(()) diff --git a/crates/mako/src/plugin.rs b/crates/mako/src/plugin.rs index 29816fa40..047a57e1d 100644 --- a/crates/mako/src/plugin.rs +++ b/crates/mako/src/plugin.rs @@ -3,6 +3,7 @@ use std::path::Path; use std::sync::Arc; use anyhow::Result; +use serde::Serialize; use swc_core::common::errors::Handler; use swc_core::common::Mark; use swc_core::ecma::ast::Module; @@ -33,11 +34,11 @@ pub struct PluginTransformJsParam<'a> { pub unresolved_mark: Mark, } -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct PluginGenerateEndParams { pub is_first_compile: bool, - pub time: u64, - pub stats: PluginGenerateStats, + pub time: i64, + pub stats: StatsJsonMap, } #[derive(Clone)] @@ -277,11 +278,11 @@ impl PluginDriver { pub fn generate_end( &self, - param: &PluginGenerateEndParams, + params: &PluginGenerateEndParams, context: &Arc, ) -> Result<()> { for plugin in &self.plugins { - plugin.generate_end(param, context)?; + plugin.generate_end(params, context)?; } Ok(()) } diff --git a/crates/mako/src/stats.rs b/crates/mako/src/stats.rs index 8998270fe..96054b576 100644 --- a/crates/mako/src/stats.rs +++ b/crates/mako/src/stats.rs @@ -1,9 +1,8 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::sync::{Arc, Mutex}; -use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::Result; use colored::*; @@ -16,6 +15,299 @@ use crate::compiler::{Compiler, Context}; use crate::features::rsc::{RscClientInfo, RscCssModules}; use crate::generate::chunk::ChunkType; +impl Compiler { + pub fn create_stats_info(&self) -> StatsJsonMap { + let mut stats_map = StatsJsonMap::new(); + let context = self.context.clone(); + + // 获取 hash + let hash = self.full_hash(); + // 获取 root_path + let root_path = context.root.to_string_lossy().to_string(); + // 获取 output_path + let output_path = context.config.output.path.to_string_lossy().to_string(); + + stats_map.built_at = chrono::Local::now().timestamp_millis(); + stats_map.hash = hash; + stats_map.root_path = root_path; + stats_map.output_path = output_path; + + let stats_info = &context.stats_info; + + // 把 context 中的静态资源信息加入到 stats_info 中 + self.context + .assets_info + .lock() + .unwrap() + .iter() + .for_each(|asset| { + let size = file_size(asset.0).unwrap(); + stats_info.add_assets( + size, + asset.1.clone(), + "".to_string(), + self.context + .config + .output + .path + .join(asset.1.clone()) + .to_string_lossy() + .to_string(), + asset.1.clone(), + ); + }); + + // 获取 assets + stats_map.assets = stats_info + .get_assets() + .iter() + .map(|asset| StatsJsonAssetsItem { + assets_type: StatsJsonType::Asset(asset.assets_type.clone()), + size: asset.size, + name: asset.hashname.clone(), + path: asset.path.clone(), + }) + .collect(); + + let chunk_graph = self.context.chunk_graph.read().unwrap(); + let module_graph = self.context.module_graph.read().unwrap(); + let chunks = chunk_graph.get_chunks(); + + // 在 chunks 中获取 modules + let mut chunk_modules: Vec = Vec::new(); + + // 获取 chunks + stats_map.chunks = chunks + .iter() + .map(|chunk| { + let modules = chunk.get_modules(); + let entry = matches!(chunk.chunk_type, ChunkType::Entry(_, _, _)); + let id = chunk.id.id.clone(); + let chunk_modules: Vec = modules + .iter() + .filter(|module| { + // ?modules 是虚拟模块,暂不记录 + // TODO: 支持虚拟模块属性,同时增加 content 以用于 size 计算等用途 + !module.id.contains("?modules") + }) + .map(|module| { + let id = module.id.clone(); + // 去拿 module 的文件 size 时,有可能 module 不存在,size 则设为 0 + // 场景: xlsx 中引入了 fs 模块 + let size = match file_size(&id) { + Ok(size) => size, + Err(..) => 0, + }; + let module = StatsJsonChunkModuleItem { + module_type: StatsJsonType::Module("module".to_string()), + size, + id, + // TODO: 现在是从每个 chunk 中找到包含的 module, 所以 chunk_id 是单个, 但是一个 module 有可能存在于多个 chunk 中 + chunks: vec![chunk.id.id.clone()], + }; + chunk_modules.push(module.clone()); + module + }) + .collect(); + let files: Vec = stats_info + .get_assets() + .iter() + .filter(|asset| asset.chunk_id == id) + .map(|asset| asset.hashname.clone()) + .collect(); + let siblings = chunk_graph + .sync_dependencies_chunk(&chunk.id) + .iter() + .map(|id| id.id.clone()) + .collect::>(); + let origin_chunk_modules = match chunk.chunk_type { + // sync chunk is the common dependency of async chunk + // so the origin chunk module within its dependent async chunk rather than itself + ChunkType::Sync => chunk_graph + .dependents_chunk(&chunk.id) + .iter() + .filter_map(|chunk_id| chunk_graph.chunk(chunk_id).unwrap().modules.last()) + .collect::>(), + _ => vec![chunk.modules.last().unwrap()], + }; + let mut origins_set = IndexMap::new(); + for origin_chunk_module in origin_chunk_modules { + let origin_deps = module_graph.get_dependents(origin_chunk_module); + + for (id, dep) in origin_deps { + let unique_key = format!("{}:{}", id.id, dep.source); + + if !origins_set.contains_key(&unique_key) { + origins_set.insert( + unique_key, + StatsJsonChunkOriginItem { + module: id.id.clone(), + module_identifier: id.id.clone(), + module_name: module_graph + .get_module(id) + .and_then(|module| { + module.info.as_ref().map(|info| { + info.file.path.to_string_lossy().to_string() + }) + }) + .unwrap_or("".to_string()), + // -> "lo-hi" + loc: dep + .span + .map(|span| { + format!("{}-{}", span.lo.to_u32(), span.hi.to_u32()) + }) + .unwrap_or("".to_string()), + request: dep.source.clone(), + }, + ); + } + } + } + let origins = origins_set.into_values().collect::>(); + + StatsJsonChunkItem { + chunk_type: StatsJsonType::Chunk("chunk".to_string()), + id, + files, + entry, + modules: chunk_modules, + siblings, + origins, + } + }) + .collect(); + stats_map.entrypoints = chunks + .iter() + .filter_map(|chunk| match &chunk.chunk_type { + ChunkType::Entry(_, name, _) => { + let mut chunks = chunk_graph + .entry_dependencies_chunk(&chunk.id) + .into_iter() + .map(|id| id.id) + .collect::>(); + + chunks.push(chunk.id.id.clone()); + + Some(( + name.clone(), + StatsJsonEntryItem { + name: name.clone(), + chunks, + }, + )) + } + _ => None, + }) + .collect::>(); + stats_map.chunk_modules = chunk_modules; + + stats_map.modules = stats_info.get_modules(); + stats_map.rsc_client_components = stats_info.get_rsc_client_components(); + stats_map.rsc_css_modules = stats_info.get_rsc_css_modules(); + + stats_map + } + + pub fn print_stats(&self) { + let mut assets = self.context.stats_info.get_assets(); + // 按照产物名称排序 + assets.sort(); + // 产物路径需要按照 output.path 来 + let abs_path = &self.context.root; + let output_path = &self.context.config.output.path; + let dist_path = diff_paths(output_path, abs_path).unwrap_or_else(|| output_path.clone()); + let mut path_str = dist_path.to_str().unwrap().to_string(); + if !path_str.ends_with('/') { + path_str.push('/'); + } + let dist = path_str.truecolor(128, 128, 128); + + // 最长的文件名字, size长度, map_size长度, 后续保持输出整齐 + let mut max_length_name = String::new(); + let mut max_size = 0; + let mut max_map_size = 0; + // 记录 name size map_size 的数组 + let mut assets_vec: Vec<(String, u64, u64)> = vec![]; + + // 生成 (name, size, map_size) 的 vec + for asset in assets { + let name = asset.hashname.clone(); + let size_length = human_readable_size(asset.size).chars().count(); + // 记录较长的名字 + if name.chars().count() > max_length_name.chars().count() { + max_length_name = name.clone(); + } + + // 如果是 .map 文件判断是否是上一个的文件的 sourceMap + // 前面排序过了, sourceMap 一定 js/css 在后面 + if name.ends_with(".map") { + let len = assets_vec.len(); + if let Some(last) = assets_vec.get_mut(len - 1) { + if name == format!("{}.map", last.0) { + // 记录较长的 map_size + if size_length > max_map_size { + max_map_size = size_length; + } + *last = (last.0.clone(), last.1, asset.size); + continue; + } + } + } + // 记录较长的 size + if size_length > max_size { + max_size = size_length; + } + assets_vec.push((asset.hashname.clone(), asset.size, 0)); + } + + // Sort the output stats by their size in desc order + assets_vec.sort_by_key(|(_, size, _)| std::cmp::Reverse(*size)); + // 输出 stats + let mut s = String::new(); + for asset in assets_vec { + let file_name = format!("{}{}", dist, asset.0); + let length = format!("{}{}", dist, max_length_name).chars().count() + 2; + let file_name_str: String = pad_string(&file_name, length, false); + let color_file_name_str = match file_name { + s if s.ends_with(".js") => file_name_str.cyan(), + s if s.ends_with(".css") => file_name_str.magenta(), + _ => file_name_str.green(), + }; + // 没有 map 的输出 + if asset.2 == 0 { + let size = human_readable_size(asset.1); + s.push_str( + format!( + "{} {}\n", + color_file_name_str, + pad_string(&size, max_size, true), + ) + .as_str(), + ); + } else { + // 有 map 的输出, | map: map_size + let size = human_readable_size(asset.1); + let map_size = human_readable_size(asset.2); + s.push_str( + format!( + "{} {} {} {}\n", + color_file_name_str, + pad_string(&size, max_size, true) + .truecolor(128, 128, 128) + .bold(), + "│ map:".truecolor(128, 128, 128), + pad_string(&map_size, max_map_size, true).truecolor(128, 128, 128) + ) + .as_str(), + ); + } + } + + println!("{}", s.trim_end_matches('\n')); + } +} + #[derive(Debug, PartialEq, Eq, Clone)] // name 记录实际 filename , 用在 stats.json 中, hashname 用在产物描述和 manifest 中 pub struct AssetsInfo { @@ -24,7 +316,7 @@ pub struct AssetsInfo { pub name: String, pub hashname: String, pub chunk_id: String, - pub path: PathBuf, + pub path: String, } impl Ord for AssetsInfo { @@ -69,7 +361,7 @@ impl StatsInfo { size: u64, name: String, chunk_id: String, - path: PathBuf, + path: String, hashname: String, ) { let mut assets = self.assets.lock().unwrap(); @@ -157,13 +449,13 @@ pub enum StatsJsonType { Chunk(String), } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, Clone)] pub struct StatsJsonAssetsItem { #[serde(flatten)] pub assets_type: StatsJsonType, pub size: u64, pub name: String, - pub path: PathBuf, + pub path: String, } #[derive(Serialize, Clone, Debug)] @@ -181,7 +473,7 @@ pub struct StatsJsonChunkModuleItem { pub chunks: Vec, } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct StatsJsonChunkOriginItem { pub module: String, @@ -190,7 +482,7 @@ pub struct StatsJsonChunkOriginItem { pub loc: String, pub request: String, } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, Clone)] pub struct StatsJsonChunkItem { #[serde(flatten)] pub chunk_type: StatsJsonType, @@ -201,19 +493,18 @@ pub struct StatsJsonChunkItem { pub siblings: Vec, pub origins: Vec, } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, Clone)] pub struct StatsJsonEntryItem { pub name: String, pub chunks: Vec, } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct StatsJsonMap { hash: u64, - time: u128, - built_at: u128, - root_path: PathBuf, - output_path: PathBuf, + built_at: i64, + root_path: String, + output_path: String, assets: Vec, chunk_modules: Vec, modules: HashMap, @@ -222,16 +513,17 @@ pub struct StatsJsonMap { rsc_client_components: Vec, #[serde(rename = "rscCSSModules")] rsc_css_modules: Vec, + pub start_time: i64, + pub end_time: i64, } impl StatsJsonMap { fn new() -> Self { Self { hash: 0, - time: 0, built_at: 0, - root_path: PathBuf::new(), - output_path: PathBuf::new(), + root_path: String::new(), + output_path: String::new(), assets: vec![], modules: HashMap::new(), chunk_modules: vec![], @@ -239,204 +531,13 @@ impl StatsJsonMap { entrypoints: HashMap::new(), rsc_client_components: vec![], rsc_css_modules: vec![], + start_time: 0, + end_time: 0, } } } -pub fn create_stats_info(compile_time: u128, compiler: &Compiler) -> StatsJsonMap { - let mut stats_map = StatsJsonMap::new(); - let context = compiler.context.clone(); - // 获取当前时间 - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis(); - // 获取 hash - let hash = compiler.full_hash(); - // 获取 root_path - let root_path = context.root.clone(); - // 获取 output_path - let output_path = context.config.output.path.clone(); - - stats_map.built_at = now; - stats_map.time = compile_time; - stats_map.hash = hash; - stats_map.root_path = root_path; - stats_map.output_path = output_path; - - let stats_info = &context.stats_info; - - // 把 context 中的静态资源信息加入到 stats_info 中 - compiler - .context - .assets_info - .lock() - .unwrap() - .iter() - .for_each(|asset| { - let size = file_size(asset.0).unwrap(); - stats_info.add_assets( - size, - asset.1.clone(), - "".to_string(), - compiler.context.config.output.path.join(asset.1.clone()), - asset.1.clone(), - ); - }); - - // 获取 assets - stats_map.assets = stats_info - .get_assets() - .iter() - .map(|asset| StatsJsonAssetsItem { - assets_type: StatsJsonType::Asset(asset.assets_type.clone()), - size: asset.size, - name: asset.hashname.clone(), - path: asset.path.clone(), - }) - .collect(); - - let chunk_graph = compiler.context.chunk_graph.read().unwrap(); - let module_graph = compiler.context.module_graph.read().unwrap(); - let chunks = chunk_graph.get_chunks(); - - // 在 chunks 中获取 modules - let mut chunk_modules: Vec = Vec::new(); - - // 获取 chunks - stats_map.chunks = chunks - .iter() - .map(|chunk| { - let modules = chunk.get_modules(); - let entry = matches!(chunk.chunk_type, ChunkType::Entry(_, _, _)); - let id = chunk.id.id.clone(); - let chunk_modules: Vec = modules - .iter() - .filter(|module| { - // ?modules 是虚拟模块,暂不记录 - // TODO: 支持虚拟模块属性,同时增加 content 以用于 size 计算等用途 - !module.id.contains("?modules") - }) - .map(|module| { - let id = module.id.clone(); - // 去拿 module 的文件 size 时,有可能 module 不存在,size 则设为 0 - // 场景: xlsx 中引入了 fs 模块 - let size = match file_size(&id) { - Ok(size) => size, - Err(..) => 0, - }; - let module = StatsJsonChunkModuleItem { - module_type: StatsJsonType::Module("module".to_string()), - size, - id, - // TODO: 现在是从每个 chunk 中找到包含的 module, 所以 chunk_id 是单个, 但是一个 module 有可能存在于多个 chunk 中 - chunks: vec![chunk.id.id.clone()], - }; - chunk_modules.push(module.clone()); - module - }) - .collect(); - let files: Vec = stats_info - .get_assets() - .iter() - .filter(|asset| asset.chunk_id == id) - .map(|asset| asset.hashname.clone()) - .collect(); - let siblings = chunk_graph - .sync_dependencies_chunk(&chunk.id) - .iter() - .map(|id| id.id.clone()) - .collect::>(); - let origin_chunk_modules = match chunk.chunk_type { - // sync chunk is the common dependency of async chunk - // so the origin chunk module within its dependent async chunk rather than itself - ChunkType::Sync => chunk_graph - .dependents_chunk(&chunk.id) - .iter() - .filter_map(|chunk_id| chunk_graph.chunk(chunk_id).unwrap().modules.last()) - .collect::>(), - _ => vec![chunk.modules.last().unwrap()], - }; - let mut origins_set = IndexMap::new(); - for origin_chunk_module in origin_chunk_modules { - let origin_deps = module_graph.get_dependents(origin_chunk_module); - - for (id, dep) in origin_deps { - let unique_key = format!("{}:{}", id.id, dep.source); - - if !origins_set.contains_key(&unique_key) { - origins_set.insert( - unique_key, - StatsJsonChunkOriginItem { - module: id.id.clone(), - module_identifier: id.id.clone(), - module_name: module_graph - .get_module(id) - .and_then(|module| { - module.info.as_ref().map(|info| { - info.file.path.to_string_lossy().to_string() - }) - }) - .unwrap_or("".to_string()), - // -> "lo-hi" - loc: dep - .span - .map(|span| { - format!("{}-{}", span.lo.to_u32(), span.hi.to_u32()) - }) - .unwrap_or("".to_string()), - request: dep.source.clone(), - }, - ); - } - } - } - let origins = origins_set.into_values().collect::>(); - - StatsJsonChunkItem { - chunk_type: StatsJsonType::Chunk("chunk".to_string()), - id, - files, - entry, - modules: chunk_modules, - siblings, - origins, - } - }) - .collect(); - stats_map.entrypoints = chunks - .iter() - .filter_map(|chunk| match &chunk.chunk_type { - ChunkType::Entry(_, name, _) => { - let mut chunks = chunk_graph - .entry_dependencies_chunk(&chunk.id) - .into_iter() - .map(|id| id.id) - .collect::>(); - - chunks.push(chunk.id.id.clone()); - - Some(( - name.clone(), - StatsJsonEntryItem { - name: name.clone(), - chunks, - }, - )) - } - _ => None, - }) - .collect::>(); - stats_map.chunk_modules = chunk_modules; - - stats_map.modules = stats_info.get_modules(); - stats_map.rsc_client_components = stats_info.get_rsc_client_components(); - stats_map.rsc_css_modules = stats_info.get_rsc_css_modules(); - - stats_map -} - -pub fn write_stats(stats: &StatsJsonMap, path: &Path) { +pub fn write_stats(path: &Path, stats: &StatsJsonMap) { let path = path.join("stats.json"); let stats_json = serde_json::to_string_pretty(stats).unwrap(); fs::write(path, stats_json).unwrap(); @@ -470,105 +571,6 @@ fn pad_string(text: &str, max_length: usize, front: bool) -> String { padded_text } } - -pub fn print_stats(compiler: &Compiler) { - let mut assets = compiler.context.stats_info.get_assets(); - // 按照产物名称排序 - assets.sort(); - // 产物路径需要按照 output.path 来 - let abs_path = &compiler.context.root; - let output_path = &compiler.context.config.output.path; - let dist_path = diff_paths(output_path, abs_path).unwrap_or_else(|| output_path.clone()); - let mut path_str = dist_path.to_str().unwrap().to_string(); - if !path_str.ends_with('/') { - path_str.push('/'); - } - let dist = path_str.truecolor(128, 128, 128); - - // 最长的文件名字, size长度, map_size长度, 后续保持输出整齐 - let mut max_length_name = String::new(); - let mut max_size = 0; - let mut max_map_size = 0; - // 记录 name size map_size 的数组 - let mut assets_vec: Vec<(String, u64, u64)> = vec![]; - - // 生成 (name, size, map_size) 的 vec - for asset in assets { - let name = asset.hashname.clone(); - let size_length = human_readable_size(asset.size).chars().count(); - // 记录较长的名字 - if name.chars().count() > max_length_name.chars().count() { - max_length_name = name.clone(); - } - - // 如果是 .map 文件判断是否是上一个的文件的 sourceMap - // 前面排序过了, sourceMap 一定 js/css 在后面 - if name.ends_with(".map") { - let len = assets_vec.len(); - if let Some(last) = assets_vec.get_mut(len - 1) { - if name == format!("{}.map", last.0) { - // 记录较长的 map_size - if size_length > max_map_size { - max_map_size = size_length; - } - *last = (last.0.clone(), last.1, asset.size); - continue; - } - } - } - // 记录较长的 size - if size_length > max_size { - max_size = size_length; - } - assets_vec.push((asset.hashname.clone(), asset.size, 0)); - } - - // Sort the output stats by their size in desc order - assets_vec.sort_by_key(|(_, size, _)| std::cmp::Reverse(*size)); - // 输出 stats - let mut s = String::new(); - for asset in assets_vec { - let file_name = format!("{}{}", dist, asset.0); - let length = format!("{}{}", dist, max_length_name).chars().count() + 2; - let file_name_str: String = pad_string(&file_name, length, false); - let color_file_name_str = match file_name { - s if s.ends_with(".js") => file_name_str.cyan(), - s if s.ends_with(".css") => file_name_str.magenta(), - _ => file_name_str.green(), - }; - // 没有 map 的输出 - if asset.2 == 0 { - let size = human_readable_size(asset.1); - s.push_str( - format!( - "{} {}\n", - color_file_name_str, - pad_string(&size, max_size, true), - ) - .as_str(), - ); - } else { - // 有 map 的输出, | map: map_size - let size = human_readable_size(asset.1); - let map_size = human_readable_size(asset.2); - s.push_str( - format!( - "{} {} {} {}\n", - color_file_name_str, - pad_string(&size, max_size, true) - .truecolor(128, 128, 128) - .bold(), - "│ map:".truecolor(128, 128, 128), - pad_string(&map_size, max_map_size, true).truecolor(128, 128, 128) - ) - .as_str(), - ); - } - } - - println!("{}", s.trim_end_matches('\n')); -} - fn file_size(path: &str) -> Result { let metadata = fs::metadata(path)?; Ok(metadata.len()) diff --git a/crates/mako/src/utils/profile_gui.rs b/crates/mako/src/utils/profile_gui.rs index 59c7bb9e1..aa2d1dc9a 100644 --- a/crates/mako/src/utils/profile_gui.rs +++ b/crates/mako/src/utils/profile_gui.rs @@ -31,7 +31,7 @@ impl eframe::App for ProfileApp { tokio_runtime::spawn(async move { let root = for_spawn.context.root.clone(); let d = crate::dev::DevServer::new(root, for_spawn); - d.serve(move |_params| {}).await; + d.serve().await; }); } self.inited = true; diff --git a/docs/config.md b/docs/config.md index 464b25441..738737378 100644 --- a/docs/config.md +++ b/docs/config.md @@ -492,6 +492,7 @@ Specify the plugins to use. stats: { startTime: number; endTime: number; + ... }; }) => void; load?: (filePath: string) => Promise<{ content: string, type: 'css'|'js'|'jsx'|'ts'|'tsx' }>; @@ -547,7 +548,7 @@ __mako_public_path__ = '/foo/'; ### px2rem -- Type: `false | { root?: number, propBlackList?: string[], propWhiteList?: string[], selectorBlackList?: string[], +- Type: `false | { root?: number, propBlackList?: string[], propWhiteList?: string[], selectorBlackList?: string[], selectorWhiteList?: string[], selectorDoubleList?: string[], minPixelValue?: number, mediaQuery?: boolean }` - Default: `false` diff --git a/docs/config.zh-CN.md b/docs/config.zh-CN.md index a39667e8b..c70b087fc 100644 --- a/docs/config.zh-CN.md +++ b/docs/config.zh-CN.md @@ -493,6 +493,7 @@ e.g. stats: { startTime: number; endTime: number; + ... }; }) => void; load?: (filePath: string) => Promise<{ content: string, type: 'css'|'js'|'jsx'|'ts'|'tsx' }>; diff --git a/e2e/fixtures/hooks/plugins.config.js b/e2e/fixtures/hooks/plugins.config.js index 662a7819b..5ab683ee4 100644 --- a/e2e/fixtures/hooks/plugins.config.js +++ b/e2e/fixtures/hooks/plugins.config.js @@ -7,9 +7,10 @@ module.exports = [{ await delay(1000); console.log('>> build start after delay 1s'); }, - async generateEnd() { + async generateEnd(params) { console.log('>> generate end'); await delay(1000); + console.dir(params.stats,{ depth: 10 }); console.log('>> generate end after delay 1s'); }, async load(path) { diff --git a/packages/mako/binding.d.ts b/packages/mako/binding.d.ts index 32bf34fb2..8ebc1ee81 100644 --- a/packages/mako/binding.d.ts +++ b/packages/mako/binding.d.ts @@ -3,6 +3,10 @@ /* auto-generated by NAPI-RS */ +export interface TransformOutput { + code: string; + map?: string; +} export interface JsHooks { name?: string; load?: ( @@ -12,6 +16,39 @@ export interface JsHooks { isFirstCompile: boolean; time: number; stats: { + hash: number; + builtAt: number; + rootPath: string; + outputPath: string; + assets: { type: string; name: string; path: string; size: number }[]; + chunkModules: { + type: string; + id: string; + chunks: string[]; + size: number; + }[]; + modules: Record< + string, + { id: string; dependents: string[]; dependencies: string[] } + >; + chunks: { + type: string; + id: string; + files: string[]; + entry: boolean; + modules: { type: string; id: string; size: number; chunks: string[] }[]; + siblings: string[]; + origin: { + module: string; + moduleIdentifier: string; + moduleName: string; + loc: string; + request: string; + }[]; + }[]; + entrypoints: Record; + rscClientComponents: { path; string; moduleId: string }[]; + rscCSSModules: { path; string; moduleId: string; modules: boolean }[]; startTime: number; endTime: number; };