diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1b27eb2a9a..8aa0c43cc28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -190,7 +190,6 @@ jobs: - name: Check Dependency Version if: steps.changes.outputs.src == 'true' run: pnpm run check-dependency-version - lint-website: name: Lint and format website @@ -316,7 +315,8 @@ jobs: echo 'debug = false' >> Cargo.toml - name: Run test - run: cargo test --workspace -- --nocapture + # reason for excluding https://github.com/napi-rs/napi-rs/issues/2200 + run: cargo test --workspace --exclude rspack_binding_options --exclude rspack_node -- --nocapture run_benchmark: name: Run benchmark diff --git a/Cargo.lock b/Cargo.lock index 9d437c049ef..fa493e59bd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -1417,7 +1417,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", ] [[package]] @@ -6110,9 +6110,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -6131,9 +6131,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", diff --git a/crates/rspack_collections/src/ukey.rs b/crates/rspack_collections/src/ukey.rs index 0f1b8650890..1aa1ef7bef2 100644 --- a/crates/rspack_collections/src/ukey.rs +++ b/crates/rspack_collections/src/ukey.rs @@ -226,7 +226,7 @@ where ::ItemUkey: Eq + Hash + Debug, { pub fn add(&mut self, item: Item) -> &mut Item { - debug_assert!(self.inner.get(&item.ukey()).is_none()); + debug_assert!(!self.inner.contains_key(&item.ukey())); let ukey = item.ukey(); self.inner.entry(ukey).or_insert(item) } diff --git a/crates/rspack_core/src/build_chunk_graph/code_splitter.rs b/crates/rspack_core/src/build_chunk_graph/code_splitter.rs index b5003995d93..84014cf44ae 100644 --- a/crates/rspack_core/src/build_chunk_graph/code_splitter.rs +++ b/crates/rspack_core/src/build_chunk_graph/code_splitter.rs @@ -1014,10 +1014,10 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on .chunk_group_by_ukey .expect_get_mut(&cgi.chunk_group); - if chunk_group + #[allow(clippy::map_entry)] + if !chunk_group .module_pre_order_indices - .get(&item.module) - .is_none() + .contains_key(&item.module) { chunk_group .module_pre_order_indices @@ -1058,10 +1058,10 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on .chunk_group_by_ukey .expect_get_mut(&cgi.chunk_group); - if chunk_group + #[allow(clippy::map_entry)] + if !chunk_group .module_post_order_indices - .get(&item.module) - .is_none() + .contains_key(&item.module) { chunk_group .module_post_order_indices diff --git a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs index c0df7d34fcd..447197d42f3 100644 --- a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs +++ b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs @@ -76,10 +76,9 @@ impl ChunkGraph { old_module_id: &ModuleIdentifier, new_module_id: &ModuleIdentifier, ) { - if self + if !self .chunk_graph_module_by_module_identifier - .get(new_module_id) - .is_none() + .contains_key(new_module_id) { let new_chunk_graph_module = ChunkGraphModule::new(); self diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index af4aaf5f5af..1a816db12ea 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -602,9 +602,9 @@ impl Compilation { /// Get sorted errors based on the factors as follows in order: /// - module identifier /// - error offset - /// Rspack assumes for each offset, there is only one error. - /// However, when it comes to the case that there are multiple errors with the same offset, - /// the order of these errors will not be guaranteed. + /// Rspack assumes for each offset, there is only one error. + /// However, when it comes to the case that there are multiple errors with the same offset, + /// the order of these errors will not be guaranteed. pub fn get_errors_sorted(&self) -> impl Iterator { let get_offset = |d: &dyn rspack_error::miette::Diagnostic| { d.labels() @@ -630,9 +630,9 @@ impl Compilation { /// Get sorted warnings based on the factors as follows in order: /// - module identifier /// - error offset - /// Rspack assumes for each offset, there is only one error. - /// However, when it comes to the case that there are multiple errors with the same offset, - /// the order of these errors will not be guaranteed. + /// Rspack assumes for each offset, there is only one error. + /// However, when it comes to the case that there are multiple errors with the same offset, + /// the order of these errors will not be guaranteed. pub fn get_warnings_sorted(&self) -> impl Iterator { let get_offset = |d: &dyn rspack_error::miette::Diagnostic| { d.labels() diff --git a/crates/rspack_core/src/compiler/module_executor/mod.rs b/crates/rspack_core/src/compiler/module_executor/mod.rs index e0f75689581..012bea692b4 100644 --- a/crates/rspack_core/src/compiler/module_executor/mod.rs +++ b/crates/rspack_core/src/compiler/module_executor/mod.rs @@ -62,11 +62,7 @@ impl ModuleExecutor { make_artifact.diagnostics = Default::default(); make_artifact.has_module_graph_change = false; - make_artifact = if let Ok(artifact) = update_module_graph(compilation, make_artifact, params) { - artifact - } else { - MakeArtifact::default() - }; + make_artifact = update_module_graph(compilation, make_artifact, params).unwrap_or_default(); let mut ctx = MakeTaskContext::new(compilation, make_artifact); let (event_sender, event_receiver) = unbounded_channel(); diff --git a/crates/rspack_core/src/exports_info.rs b/crates/rspack_core/src/exports_info.rs index 68bef1161b2..754a006db9d 100644 --- a/crates/rspack_core/src/exports_info.rs +++ b/crates/rspack_core/src/exports_info.rs @@ -79,9 +79,7 @@ impl ExportsInfoId { } pub fn is_export_provided(&self, names: &[Atom], mg: &ModuleGraph) -> Option { - let Some(name) = names.first() else { - return None; - }; + let name = names.first()?; let info = self.get_read_only_export_info(name, mg); if let Some(exports_info) = info.exports_info && names.len() > 1 @@ -256,9 +254,7 @@ impl ExportsInfoId { if names.len() == 1 { return Some(export_info); } - let Some(exports_info) = export_info.exports_info else { - return None; - }; + let exports_info = export_info.exports_info?; exports_info.get_read_only_export_info_recursive(&names[1..], mg) } @@ -456,10 +452,7 @@ impl ExportsInfoId { return Some(UsedName::Vec(names)); } let export_info = self.get_read_only_export_info(&names[0], mg); - let x = export_info.get_used_name(Some(&names[0]), runtime); - let Some(x) = x else { - return None; - }; + let x = export_info.get_used_name(Some(&names[0]), runtime)?; let names_len = names.len(); let mut arr = if x == names[0] && names.len() == 1 { names.clone() @@ -473,9 +466,7 @@ impl ExportsInfoId { && export_info.get_used(runtime) == UsageState::OnlyPropertiesUsed { let nested = exports_info.get_used_name(mg, runtime, UsedName::Vec(names[1..].to_vec())); - let Some(nested) = nested else { - return None; - }; + let nested = nested?; arr.extend(match nested { UsedName::Str(name) => vec![name], UsedName::Vec(names) => names, diff --git a/crates/rspack_core/src/lib.rs b/crates/rspack_core/src/lib.rs index 3206ff1c58c..ddb6dfce3d0 100644 --- a/crates/rspack_core/src/lib.rs +++ b/crates/rspack_core/src/lib.rs @@ -5,7 +5,6 @@ #![feature(anonymous_lifetime_in_impl_trait)] #![feature(hash_raw_entry)] #![feature(option_get_or_insert_default)] -#![feature(slice_group_by)] use std::{fmt, sync::Arc}; mod dependencies_block; diff --git a/crates/rspack_core/src/module_factory.rs b/crates/rspack_core/src/module_factory.rs index e3198d094c4..347f043e225 100644 --- a/crates/rspack_core/src/module_factory.rs +++ b/crates/rspack_core/src/module_factory.rs @@ -39,7 +39,7 @@ impl ModuleFactoryCreateData { } pub fn add_file_dependencies(&mut self, files: impl IntoIterator) { - self.file_dependencies.extend(files.into_iter()); + self.file_dependencies.extend(files); } pub fn add_context_dependency(&mut self, context: PathBuf) { @@ -47,7 +47,7 @@ impl ModuleFactoryCreateData { } pub fn add_context_dependencies(&mut self, contexts: impl IntoIterator) { - self.context_dependencies.extend(contexts.into_iter()); + self.context_dependencies.extend(contexts); } pub fn add_missing_dependency(&mut self, missing: PathBuf) { diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index e91577afd2b..c020add6430 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -272,9 +272,7 @@ impl<'a> ModuleGraph<'a> { connection_id: &ConnectionId, force: bool, ) -> Option { - let Some(connection) = self.connection_by_connection_id(connection_id) else { - return None; - }; + let connection = self.connection_by_connection_id(connection_id)?; let module_identifier = *connection.module_identifier(); let original_module_identifier = connection.original_module_identifier; let dependency_id = connection.dependency_id; diff --git a/crates/rspack_core/src/update_hash.rs b/crates/rspack_core/src/update_hash.rs index a50de02a22b..7b7f47bc70d 100644 --- a/crates/rspack_core/src/update_hash.rs +++ b/crates/rspack_core/src/update_hash.rs @@ -2,6 +2,7 @@ use std::hash::Hasher; use crate::{Compilation, RuntimeSpec}; +#[allow(dead_code)] pub struct UpdateHashContext<'a> { pub compilation: &'a Compilation, pub runtime: Option<&'a RuntimeSpec>, diff --git a/crates/rspack_fs_node/Cargo.toml b/crates/rspack_fs_node/Cargo.toml index 444e044a157..f959b39fb4a 100644 --- a/crates/rspack_fs_node/Cargo.toml +++ b/crates/rspack_fs_node/Cargo.toml @@ -21,3 +21,6 @@ rspack_napi = { path = "../rspack_napi" } [build-dependencies] napi-build = { workspace = true } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(node)'] } diff --git a/crates/rspack_loader_runner/src/loader.rs b/crates/rspack_loader_runner/src/loader.rs index 86ff6b176c8..c602a2ea549 100644 --- a/crates/rspack_loader_runner/src/loader.rs +++ b/crates/rspack_loader_runner/src/loader.rs @@ -233,6 +233,7 @@ pub(crate) mod test { use super::{Loader, LoaderItem}; + #[allow(dead_code)] pub(crate) struct Custom; #[async_trait::async_trait] @@ -243,6 +244,7 @@ pub(crate) mod test { } } + #[allow(dead_code)] pub(crate) struct Custom2; #[async_trait::async_trait] impl Loader<()> for Custom2 {} @@ -252,6 +254,7 @@ pub(crate) mod test { } } + #[allow(dead_code)] pub(crate) struct Builtin; #[async_trait::async_trait] impl Loader<()> for Builtin {} diff --git a/crates/rspack_loader_swc/src/compiler.rs b/crates/rspack_loader_swc/src/compiler.rs index 2486b413d65..cb2a42bf960 100644 --- a/crates/rspack_loader_swc/src/compiler.rs +++ b/crates/rspack_loader_swc/src/compiler.rs @@ -525,13 +525,7 @@ impl SwcCompiler { Ok(r) => r, Err(_err) => { // Load original source map if possible - match read_file_sourcemap(data_url) { - Ok(v) => v, - Err(_) => { - // tracing::error!("failed to read input source map: {:?}", err); - None - } - } + read_file_sourcemap(data_url).unwrap_or(None) } } }; diff --git a/crates/rspack_macros_test/tests/runtime_module.rs b/crates/rspack_macros_test/tests/runtime_module.rs index f657040edb2..7188b83b328 100644 --- a/crates/rspack_macros_test/tests/runtime_module.rs +++ b/crates/rspack_macros_test/tests/runtime_module.rs @@ -5,6 +5,7 @@ use rspack_core::{rspack_sources::Source, Compilation}; use rspack_error::Result; use rspack_macros::impl_runtime_module; +#[allow(dead_code)] #[test] fn with_generic() { #[impl_runtime_module] diff --git a/crates/rspack_plugin_javascript/src/ast/parse.rs b/crates/rspack_plugin_javascript/src/ast/parse.rs index 5eb7ba3f734..ca517d00d35 100644 --- a/crates/rspack_plugin_javascript/src/ast/parse.rs +++ b/crates/rspack_plugin_javascript/src/ast/parse.rs @@ -25,11 +25,11 @@ fn module_type_to_is_module(value: &ModuleType) -> IsModule { /// Why this helper function design like this? /// 1. `swc_ecma_parser` could return ast with some errors which are recoverable -/// or warning (though swc defined them as errors), but the parser at here should -/// be non-error-tolerant. +/// or warning (though swc defined them as errors), but the parser at here should +/// be non-error-tolerant. /// /// 2. We can't convert to [rspack_error::Error] at this point, because there is -/// no `path` and `source` +/// no `path` and `source` pub fn parse_js( fm: Arc, target: EsVersion, diff --git a/crates/rspack_plugin_javascript/src/dependency/commonjs/common_js_export_require_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/commonjs/common_js_export_require_dependency.rs index a0bf1fb1519..e9b85cde295 100644 --- a/crates/rspack_plugin_javascript/src/dependency/commonjs/common_js_export_require_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/commonjs/common_js_export_require_dependency.rs @@ -189,9 +189,7 @@ impl Dependency for CommonJsExportRequireDependency { let Some(name) = self.names.first() else { unreachable!(); }; - let Some(from) = mg.connection_by_dependency(&self.id) else { - return None; - }; + let from = mg.connection_by_dependency(&self.id)?; Some(ExportsSpec { exports: ExportsOfExportsSpec::Array(vec![ExportNameOrSpec::ExportSpec(ExportSpec { name: name.to_owned(), @@ -208,9 +206,7 @@ impl Dependency for CommonJsExportRequireDependency { ..Default::default() }) } else if self.names.is_empty() { - let Some(from) = mg.connection_by_dependency(&self.id) else { - return None; - }; + let from = mg.connection_by_dependency(&self.id)?; if let Some(reexport_info) = self.get_star_reexports(mg, None, from.module_identifier()) { Some(ExportsSpec { exports: ExportsOfExportsSpec::Array( diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs index 9e89aa91b6c..63e5600e90f 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs @@ -921,9 +921,7 @@ impl HarmonyExportImportedSpecifierDependency { &potential_conflicts.names[potential_conflicts.names_slice ..potential_conflicts.dependency_indices[potential_conflicts.dependency_index]], ); - let Some(imported_module) = module_graph.get_module_by_dependency_id(&self.id) else { - return None; - }; + let imported_module = module_graph.get_module_by_dependency_id(&self.id)?; let exports_info = module_graph.get_exports_info(&imported_module.identifier()); let mut conflicts: IndexMap<&str, Vec<&Atom>, BuildHasherDefault> = IndexMap::default(); diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs index 80701b5dcbb..5550495a4ad 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs @@ -207,10 +207,7 @@ pub fn harmony_import_dependency_get_linking_error( additional_msg: String, should_error: bool, ) -> Option { - let Some(imported_module) = module_graph.get_module_by_dependency_id(module_dependency.id()) - else { - return None; - }; + let imported_module = module_graph.get_module_by_dependency_id(module_dependency.id())?; if !imported_module.get_diagnostics().is_empty() { return None; } diff --git a/crates/rspack_plugin_javascript/src/lib.rs b/crates/rspack_plugin_javascript/src/lib.rs index 8284889d696..f131cd226a5 100644 --- a/crates/rspack_plugin_javascript/src/lib.rs +++ b/crates/rspack_plugin_javascript/src/lib.rs @@ -2,6 +2,7 @@ #![feature(if_let_guard)] #![feature(let_chains)] #![feature(box_patterns)] +#![feature(trait_upcasting)] #![recursion_limit = "256"] pub mod ast; diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs index e29a1d2687b..416fa399ef5 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_exports_parse_plugin.rs @@ -32,7 +32,7 @@ fn get_member_expression_info( None => is_module_exports_member_expr_start(expr), }; expr.as_member().and_then(|expr: &MemberExpr| { - let Some(members) = parser + let members = parser .get_member_expression_info(expr, AllowedMemberTypes::Expression) .and_then(|info| match info { MemberExpressionInfo::Call(_) => None, @@ -44,10 +44,7 @@ fn get_member_expression_info( .skip(if is_module_exports_start { 1 } else { 0 }) .map(|n| n.to_owned()) .collect::>() - }) - else { - return None; - }; + })?; match expr.obj { box Expr::Call(_) => Some(members), box Expr::Ident(_) => Some(members), @@ -378,11 +375,7 @@ impl JavascriptParserPlugin for CommonJsExportsParserPlugin { let handle_remaining = |parser: &mut JavascriptParser, base: ExportsBase| { let is_module_exports_start = matches!(base, ExportsBase::ModuleExports); - let Some(remaining) = - get_member_expression_info(parser, left_expr, Some(is_module_exports_start)) - else { - return None; - }; + let remaining = get_member_expression_info(parser, left_expr, Some(is_module_exports_start))?; if (remaining.is_empty() || remaining.first().is_some_and(|i| i != "__esModule")) && parser.is_require_call_expr(&assign_expr.right) diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index 832c9229250..268f8a60c64 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -88,9 +88,7 @@ impl CommonJsImportsParserPlugin { return None; } - let Some((members, first_arg, loc)) = extract_require_call_info(parser, mem_expr) else { - return None; - }; + let (members, first_arg, loc) = extract_require_call_info(parser, mem_expr)?; let param = parser.evaluate_expression(&first_arg.expr); param.is_string().then(|| { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs index 65bf805c26b..a8930b25b81 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs @@ -117,12 +117,8 @@ impl JavascriptParserPlugin for CompatibilityPlugin { } fn pre_statement(&self, parser: &mut JavascriptParser, stmt: Statement) -> Option { - let Some(fn_decl) = stmt.as_function_decl() else { - return None; - }; - let Some(ident) = fn_decl.ident() else { - return None; - }; + let fn_decl = stmt.as_function_decl()?; + let ident = fn_decl.ident()?; let name = ident.sym.as_str(); if name != RuntimeGlobals::REQUIRE.name() { None diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/const/if_stmt.rs b/crates/rspack_plugin_javascript/src/parser_plugin/const/if_stmt.rs index 0e06bf089bf..da36a88741e 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/const/if_stmt.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/const/if_stmt.rs @@ -132,9 +132,7 @@ fn get_hoisted_declarations<'a>( pub fn statement_if(scanner: &mut JavascriptParser, stmt: &IfStmt) -> Option { let param = scanner.evaluate_expression(&stmt.test); - let Some(boolean) = param.as_bool() else { - return None; - }; + let boolean = param.as_bool()?; if !param.could_have_side_effects() { scanner .presentational_dependencies diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/const/logic_expr.rs b/crates/rspack_plugin_javascript/src/parser_plugin/const/logic_expr.rs index 8820f5e6bab..accdee19247 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/const/logic_expr.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/const/logic_expr.rs @@ -16,9 +16,7 @@ pub fn expression_logic_operator(scanner: &mut JavascriptParser, expr: &BinExpr) if expr.op == BinaryOp::LogicalAnd || expr.op == BinaryOp::LogicalOr { let param = scanner.evaluate_expression(&expr.left); let boolean = param.as_bool(); - let Some(boolean) = boolean else { - return None; - }; + let boolean = boolean?; let keep_right = if boolean { expr.op == BinaryOp::LogicalAnd } else { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/parser.rs b/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/parser.rs index 56107a852e5..eccbbd87ea5 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/parser.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/parser.rs @@ -367,7 +367,7 @@ impl JavascriptParserPlugin for DefineParserPlugin { && let Some(on_evaluate_typeof) = &record.on_evaluate_typeof { return on_evaluate_typeof(record, parser, expr.span.real_lo(), expr.span.hi.0); - } else if self.walk_data.object_define_record.get(for_name).is_some() { + } else if self.walk_data.object_define_record.contains_key(for_name) { return Some(evaluate_to_string( "object".to_string(), expr.span.real_lo(), @@ -406,7 +406,7 @@ impl JavascriptParserPlugin for DefineParserPlugin { && let Some(on_typeof) = &record.on_typeof { return on_typeof(record, parser, expr.span.real_lo(), expr.span.real_hi()); - } else if self.walk_data.object_define_record.get(for_name).is_some() { + } else if self.walk_data.object_define_record.contains_key(for_name) { debug_assert!(!parser.in_short_hand); parser.presentational_dependencies.push(Box::new(dep( parser, diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs index 8fc5e4fa8ce..c35f5c5c0df 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/exports_info_api_plugin.rs @@ -17,7 +17,7 @@ impl JavascriptParserPlugin for ExportsInfoApiPlugin { member_expr: &swc_core::ecma::ast::MemberExpr, _name: &str, ) -> Option { - let Some((members, root)) = parser + let (members, root) = parser .get_member_expression_info(member_expr, AllowedMemberTypes::Expression) .and_then(|info| match info { MemberExpressionInfo::Call(_) => None, @@ -28,10 +28,8 @@ impl JavascriptParserPlugin for ExportsInfoApiPlugin { None } } - }) - else { - return None; - }; + })?; + let len = members.len(); if len >= 1 && root == WEBPACK_EXPORTS_INFO && parser.is_unresolved_ident(WEBPACK_EXPORTS_INFO) { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs index 0901eafc924..edb0162a5ed 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs @@ -26,9 +26,7 @@ impl JavascriptParserPlugin for ImportParserPlugin { let Callee::Import(import_call) = &node.callee else { unreachable!() }; - let Some(dyn_imported) = node.args.first() else { - return None; - }; + let dyn_imported = node.args.first()?; if dyn_imported.spread.is_some() { return None; } diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs index 4e886fd8313..f64d2dc2ed2 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs @@ -17,7 +17,7 @@ type KeepRight = bool; pub trait JavascriptParserPlugin { /// Return: /// - `Some(true)` signifies the termination of the current - /// statement's visit during the pre-walk phase. + /// statement's visit during the pre-walk phase. /// - Other return values imply that the walk operation ought to continue fn pre_statement(&self, _parser: &mut JavascriptParser, _stmt: Statement) -> Option { None diff --git a/crates/rspack_plugin_javascript/src/plugin/mod.rs b/crates/rspack_plugin_javascript/src/plugin/mod.rs index 9e3fbb74eb4..812e2a76e25 100644 --- a/crates/rspack_plugin_javascript/src/plugin/mod.rs +++ b/crates/rspack_plugin_javascript/src/plugin/mod.rs @@ -683,7 +683,7 @@ impl JsPlugin { continue; }; - if renamed_inline_modules.get(m_identifier).is_some() { + if renamed_inline_modules.contains_key(m_identifier) { if let Some(source) = renamed_inline_modules.get(m_identifier) { rendered_module = source.clone(); }; diff --git a/crates/rspack_plugin_javascript/src/utils/eval/eval_new_expr.rs b/crates/rspack_plugin_javascript/src/utils/eval/eval_new_expr.rs index 2efd7e6acba..3b38cc0d3fb 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/eval_new_expr.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/eval_new_expr.rs @@ -10,9 +10,7 @@ pub fn eval_new_expression( scanner: &mut JavascriptParser, expr: &NewExpr, ) -> Option { - let Some(ident) = expr.callee.as_ident() else { - return None; - }; + let ident = expr.callee.as_ident()?; if ident.sym.as_str() != "RegExp" { // FIXME: call hooks return None; @@ -35,9 +33,7 @@ pub fn eval_new_expression( } let evaluated_reg_exp = scanner.evaluate_expression(&arg1.expr); - let Some(reg_exp) = evaluated_reg_exp.as_string() else { - return None; - }; + let reg_exp = evaluated_reg_exp.as_string()?; let flags = if let Some(arg2) = args.get(1) { if arg2.spread.is_some() { diff --git a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs index ac9bc43e102..5f5c15072af 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs @@ -113,9 +113,7 @@ pub fn eval_unary_expression( UnaryOp::TypeOf => eval_typeof(scanner, expr), UnaryOp::Bang => { let arg = scanner.evaluate_expression(&expr.arg); - let Some(boolean) = arg.as_bool() else { - return None; - }; + let boolean = arg.as_bool()?; let mut eval = BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span_hi().0); eval.set_bool(!boolean); eval.set_side_effects(arg.could_have_side_effects()); @@ -123,9 +121,7 @@ pub fn eval_unary_expression( } UnaryOp::Tilde => { let arg = scanner.evaluate_expression(&expr.arg); - let Some(number) = arg.as_int() else { - return None; - }; + let number = arg.as_int()?; let mut eval = BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span_hi().0); eval.set_number(!number as f64); eval.set_side_effects(arg.could_have_side_effects()); @@ -133,9 +129,7 @@ pub fn eval_unary_expression( } UnaryOp::Minus | UnaryOp::Plus => { let arg = scanner.evaluate_expression(&expr.arg); - let Some(number) = arg.as_number() else { - return None; - }; + let number = arg.as_number()?; let res = match &expr.op { UnaryOp::Minus => -number, UnaryOp::Plus => number, diff --git a/crates/rspack_plugin_javascript/src/utils/object_properties.rs b/crates/rspack_plugin_javascript/src/utils/object_properties.rs index 4c02a9a79f3..6640730c457 100644 --- a/crates/rspack_plugin_javascript/src/utils/object_properties.rs +++ b/crates/rspack_plugin_javascript/src/utils/object_properties.rs @@ -20,9 +20,7 @@ pub fn get_value_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Option<& } pub fn get_literal_str_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Option<&'a Str> { - let Some(lit) = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit()) else { - return None; - }; + let lit = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit())?; match lit { Lit::Str(str) => Some(str), _ => None, @@ -30,9 +28,7 @@ pub fn get_literal_str_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Op } pub fn get_bool_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Option<&'a Bool> { - let Some(lit) = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit()) else { - return None; - }; + let lit = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit())?; match lit { Lit::Bool(bool) => Some(bool), _ => None, @@ -40,9 +36,7 @@ pub fn get_bool_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Option<&' } pub fn get_regex_by_obj_prop<'a>(obj: &'a ObjectLit, field: &'a str) -> Option<&'a Regex> { - let Some(lit) = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit()) else { - return None; - }; + let lit = get_value_by_obj_prop(obj, field).and_then(|e| e.as_lit())?; match lit { Lit::Regex(regexp) => Some(regexp), _ => None, diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs index fa6fcc4506a..8404ecc3384 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs @@ -427,16 +427,12 @@ impl<'parser> JavascriptParser<'parser> { } pub fn get_mut_variable_info(&mut self, name: &str) -> Option<&mut VariableInfo> { - let Some(id) = self.definitions_db.get(self.definitions, name) else { - return None; - }; + let id = self.definitions_db.get(self.definitions, name)?; Some(self.definitions_db.expect_get_mut_variable(id)) } pub fn get_variable_info(&mut self, name: &str) -> Option<&VariableInfo> { - let Some(id) = self.definitions_db.get(self.definitions, name) else { - return None; - }; + let id = self.definitions_db.get(self.definitions, name)?; Some(self.definitions_db.expect_get_variable(id)) } @@ -573,16 +569,12 @@ impl<'parser> JavascriptParser<'parser> { if !allowed_types.contains(AllowedMemberTypes::CallExpression) { return None; } - let Some(root_name) = expr.callee.get_root_name() else { - return None; - }; - let Some(FreeInfo { + let root_name = expr.callee.get_root_name()?; + let FreeInfo { name: resolved_root, info: root_info, - }) = self.get_free_info_from_variable(&root_name) - else { - return None; - }; + } = self.get_free_info_from_variable(&root_name)?; + let callee_name = object_and_members_to_name(resolved_root, &members); members.reverse(); members_optionals.reverse(); @@ -602,16 +594,13 @@ impl<'parser> JavascriptParser<'parser> { if !allowed_types.contains(AllowedMemberTypes::Expression) { return None; } - let Some(root_name) = object.get_root_name() else { - return None; - }; - let Some(FreeInfo { + let root_name = object.get_root_name()?; + + let FreeInfo { name: resolved_root, info: root_info, - }) = self.get_free_info_from_variable(&root_name) - else { - return None; - }; + } = self.get_free_info_from_variable(&root_name)?; + let name = object_and_members_to_name(resolved_root, &members); members.reverse(); members_optionals.reverse(); diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs index 774b2cfb992..7182e0ba398 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs @@ -35,6 +35,7 @@ pub fn collect_destructuring_assignment_properties( } } +#[allow(dead_code)] pub(crate) mod expr_like { use std::any::Any; @@ -255,9 +256,7 @@ pub fn extract_require_call_info( }; // call require() with no param - let Some(first_arg) = args.first() else { - return None; - }; + let first_arg = args.first()?; let loc = DependencyLocation::new(expr.span().real_lo(), expr.span().real_hi(), None); diff --git a/crates/rspack_plugin_library/src/assign_library_plugin.rs b/crates/rspack_plugin_library/src/assign_library_plugin.rs index 4dc4ffbc9c9..a99bbf17ecc 100644 --- a/crates/rspack_plugin_library/src/assign_library_plugin.rs +++ b/crates/rspack_plugin_library/src/assign_library_plugin.rs @@ -498,7 +498,7 @@ fn access_with_init(accessor: &[String], existing_length: usize, init_last: bool props_so_far.push(accessor[i].clone()); current = format!( "({current}{} = {base}{} || {{}})", - property_access(&vec![&accessor[i]], 0), + property_access(vec![&accessor[i]], 0), property_access(&props_so_far, 0) ); i += 1; diff --git a/crates/rspack_plugin_library/src/module_library_plugin.rs b/crates/rspack_plugin_library/src/module_library_plugin.rs index 08e1eeaedb2..2a987c34010 100644 --- a/crates/rspack_plugin_library/src/module_library_plugin.rs +++ b/crates/rspack_plugin_library/src/module_library_plugin.rs @@ -85,7 +85,7 @@ fn render_startup( let var_name = format!("__webpack_exports__{}", to_identifier(info_name)); source.add(RawSource::from(format!( "var {var_name} = __webpack_exports__{};\n", - property_access(&vec![used_name], 0) + property_access(vec![used_name], 0) ))); exports.push(format!("{var_name} as {}", info_name)); } diff --git a/crates/rspack_plugin_library/src/system_library_plugin.rs b/crates/rspack_plugin_library/src/system_library_plugin.rs index e8e308a4a55..fd6adb10f16 100644 --- a/crates/rspack_plugin_library/src/system_library_plugin.rs +++ b/crates/rspack_plugin_library/src/system_library_plugin.rs @@ -23,6 +23,7 @@ struct SystemLibraryPluginParsed<'a> { } #[derive(Debug, Default)] +#[allow(dead_code)] struct SystemLibraryJavascriptModulesPluginPlugin; #[plugin] diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index 3aeaa5702f0..5556c73b24f 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -117,9 +117,7 @@ fn get_required_version_from_description_file( data: serde_json::Value, package_name: &str, ) -> Option { - let Some(data) = data.as_object() else { - return None; - }; + let data = data.as_object()?; let get_version_from_dependencies = |dependencies: &str| { data .get(dependencies) diff --git a/crates/rspack_plugin_wasm/src/dependency/wasm_import_dependency.rs b/crates/rspack_plugin_wasm/src/dependency/wasm_import_dependency.rs index cd2a6613d00..0778e2b4901 100644 --- a/crates/rspack_plugin_wasm/src/dependency/wasm_import_dependency.rs +++ b/crates/rspack_plugin_wasm/src/dependency/wasm_import_dependency.rs @@ -6,6 +6,7 @@ use swc_core::ecma::atoms::Atom; use crate::WasmNode; +#[allow(dead_code)] #[derive(Debug, Clone)] pub struct WasmImportDependency { id: DependencyId, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8a00cf72b62..a9f7244565d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,4 +2,4 @@ profile = "default" # Use nightly for better access to the latest Rust features. # This date is aligned to stable release dates. -channel = "nightly-2023-12-28" # v1.75.0 +channel = "nightly-2024-06-07" # v1.80.0 diff --git a/website/docs/en/contribute/development/profiling.md b/website/docs/en/contribute/development/profiling.md index 6884473da3b..79ee9cdc764 100644 --- a/website/docs/en/contribute/development/profiling.md +++ b/website/docs/en/contribute/development/profiling.md @@ -67,6 +67,14 @@ npm install -g speedscope speedscope CPU.20230522.154658.14577.0.001.cpuprofile ``` +### Rsdoctor Timeline + +If we want to analyze the time cost of loaders and plugins or the compilation behavior of loaders, we can use Rsdoctor to view: + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-loader-timeline.png) + +Refer to [Rsdoctor Compilation Analysis](/guide/optimization/profile#use-rsdoctor) + ## Mac Xcode Instruments Xcode instruments can be used to produce a CPU profile if you are on a Mac. diff --git a/website/docs/en/guide/optimization/analysis.mdx b/website/docs/en/guide/optimization/analysis.mdx index c069a4f01e2..a826e215ea2 100644 --- a/website/docs/en/guide/optimization/analysis.mdx +++ b/website/docs/en/guide/optimization/analysis.mdx @@ -1,13 +1,46 @@ # Bundle analysis +## webpack-bundle-analyzer + Rspack's Command Line Interface (CLI) supports bundle analysis out-of-box via the `--analyze` option. It uses [webpack-bundle-analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer) behind the scenes. ```sh $ rspack build --analyze ``` +## bundle-stats & statoscope + You can also generate a `stats.json` file for further analysis with other bundle analysis tools like [bundle-stats](https://github.com/relative-ci/bundle-stats) or [statoscope](https://statoscope.tech/): ```sh $ rspack build --json stats.json ``` + +## Rsdoctor’s Bundle Analysis + +Rsdoctor provides the `Bundle Size` module, which is mainly used to analyze the information of the outputs of Rspack, including the size of resources, duplicate packages, and module reference relationships: + +- **Bundle Overview**: Displays the total number and size of artifacts, as well as the number and size of each file type. It also shows the duplicate packages and their reference chains. +- **Bundle Analysis Module**: Analyzes the size and code information of the build artifacts' resources (**Assets**) and the included **Modules**. In this module, you can view the **actual code size of modules after packaging** in the Assets, as well as the original code or **packaged code segments** and **module reference relationships**. + + + +Click on the **"Bundle Size"** option in the navigation bar to view the Bundle analysis report. You can see more details from this page: [Bundle Size](https://rsdoctor.dev/zh/guide/usage/bundle-size) + +### Reduce duplicate dependencies + +Bundle size optimization is an important part in production build because it directly affects the user experience of online users. In this document, we will introduce some common bundle size optimization methods in Rspack. + +It is common for web projects to bundle multiple versions of third-party dependencies. Duplicate dependencies can lead to increased bundle size and slower build speed. + +- Detect duplicate dependencies + +You can use [Rsdoctor](https://rsdoctor.dev) to detect whether there are duplicate dependencies in the project. Rsdoctor will analyze during the build process, find any duplicate bundled dependencies and display them visually: + +![](https://assets.rspack.dev/others/assets/rsdoctor/overall-alerts.jpg) + +For more details, see [Rsdoctor - Duplicate Dependency Problem](https://rsdoctor.dev/blog/topic/duplicate-pkg-problem). diff --git a/website/docs/en/guide/optimization/profile.mdx b/website/docs/en/guide/optimization/profile.mdx index 3e7b831053c..fdd66004dbc 100644 --- a/website/docs/en/guide/optimization/profile.mdx +++ b/website/docs/en/guide/optimization/profile.mdx @@ -2,6 +2,8 @@ import { ApiMeta } from '@components/ApiMeta.tsx'; # Build performance profile +## Rspack Profile + The Rspack CLI supports the use of the `RSPACK_PROFILE` environment variable for build performance profile. @@ -15,3 +17,36 @@ This command will generate a `.rspack-profile-${timestamp}-${pid}` folder in the - `trace.json`: The time spent on each phase of the Rust side is recorded at a granular level using [tracing](https://github.com/tokio-rs/tracing) and can be viewed using [ui.perfetto.dev](https://ui.perfetto.dev/) - `jscpuprofile.json`: The time spent at each stage on the JavaScript side is recorded at a granular level using [Node.js inspector](https://nodejs.org/dist/latest-v18.x/docs/api/inspector.html) and can be viewed using [speedscope.app](https://www.speedscope.app/) - `logging.json`: Includes some logging information that keeps a coarse-grained record of how long each phase of the build took + +## Rsdoctor's Compilation Analysis + +Rsdoctor is a build analyser that can visually display the compilation time of each loaders and plugins. + +#### Loader Timeline + +If you need to analyze the time cost of loaders and plugins, or the compilation behavior of loaders, you can use Rsdoctor to view: + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-loader-timeline.png) + +You can view the execution time of each loader and the files that were compiled, as well as the time taken for each file, in the timeline. + +Refer to [Loader Timeline](https://rsdoctor.dev/guide/usage/loaders-timeline). + +#### Loader Details + +If you want to view the compilation process of loaders, you can use the [Loader Details](https://rsdoctor.dev/guide/usage/loaders-analysis): + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-analyze-code.png) + +On this report page, you can see the code changes made by the loaders for each file before and after compilation. + +Refer to [Loader Details](https://rsdoctor.dev/guide/usage/loaders-analysis). + +#### Enable Compilation Analysis + +To enable `RsdoctorRspackPlugin`, you can refer to the documentation: [Use Rsdoctor](/guide/optimization/use-rsdoctor). + +- The `RsdoctorRspackPlugin` requires the `features.loader` and `features.plugins` parameters to be set to `true`. By default, `features.loader` and `features.plugins` are enabled. Refer to the parameter documentation [Rsdoctor options](https://rsdoctor.dev/config/options/options#features). +- Usage documentation: + - For Loader Analysis, refer to [Loader Timeline](https://rsdoctor.dev/guide/usage/loaders-timeline) and [Loader Details](https://rsdoctor.dev/guide/usage/loaders-analysis). + - For Plugins Analysis, refer to [Plugin Analysis](https://rsdoctor.dev/guide/usage/plugins-analysis). diff --git a/website/docs/en/guide/optimization/use-rsdoctor.mdx b/website/docs/en/guide/optimization/use-rsdoctor.mdx new file mode 100644 index 00000000000..0a215364b5f --- /dev/null +++ b/website/docs/en/guide/optimization/use-rsdoctor.mdx @@ -0,0 +1,89 @@ +# Use Rsdoctor + +[Rsdoctor](https://rsdoctor.dev/) is a build analyzer that can visually display the build process, such as compilation time, code changes before and after compilation, module reference relationships, duplicate modules, etc. + +If you need to debug the build outputs or build process, you can use Rsdoctor for troubleshooting. + +## 💡 What is Rsdoctor? + +- Rsdoctor is a one-stop tool for diagnosing and analyzing the build process and build artifacts. +- Rsdoctor is a tool that supports **Rspack** or **webpack** build analysis. +- Rsdoctor is an analysis tool that can display the time-consuming and behavioral details of the compilation. +- Rsdoctor is a tool that can analyze the time-consuming and compilation process of the rspack builtin:swc-loader. + +## 🔥 Features + +- **Compilation Visualization**: Rsdoctor visualizes the compilation behavior and time consumption, making it easy to view build issues. + +| loader timeline | loader codes | +| ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | + +- **Multiple Analysis Capabilities**: Rsdoctor supports build artifact, build-time analysis, and anti-degradation capabilities: + + - Build artifact support for resource lists and module dependencies, etc. + - Build-time analysis supports Loader, Plugin, and Resolver building process analysis, including: **Rspack's builtin:swc-loader**. + - Build rules support duplicate package detection and ES Version Check, etc. + +- **Support Custom Rules**: In addition to built-in build scan rules, Rsdoctor also supports users adding custom component scan rules based on the build data of Rsdoctor. + +### More about Features + +You can see more details of features from this wiki: [Rsdoctor Features](https://rsdoctor.dev/guide/start/features). + +### Bundle Analysis + +You can use Rsdoctor for **[Bundle Analysis](/guide/optimization/analysis#rsdoctors-bundle-analysis)** in Rspack. + +### Compilation Analysis + +Rsdoctor is a build analyzer that can visually display the compilation time of each loader and plugin. See [Compilation Analysis](/guide/optimization/profile#rsdoctors-compilation-analysis) for more details. + +## Quick Start + +In an Rspack-based project, you can enable Rsdoctor as follows: + +1. Install the Rsdoctor plugin: + +import { PackageManagerTabs } from '@theme'; + + + +2. Register the `RsdoctorRspackPlugin` plugin: + +:::danger + +- Please do not use Rsdoctor in production version. + +::: + +Initialize the RsdoctorRspackPlugin plugin in the [plugins](/config/plugins.html#plugins) section of `rspack.config.js`, as shown below: + +```ts title="rspack.config.js" +const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin'); + +module.exports = { + // ... + plugins: [ + process.env.RSDOCTOR && + new RsdoctorRspackPlugin({ + // plugin options + }), + ].filter(Boolean), +}; +``` + +3. Add `RSDOCTOR=true` env variable before the CLI command: + +```bash +# dev +RSDOCTOR=true rspack serve + +# build +RSDOCTOR=true rspack build +``` + +After running the above commands, when the build is completed, it will open the build analysis page. For complete features, please refer to [Rsdoctor document](https://rsdoctor.dev/). + +4. **[Optional]** Configure Options + +You can refer to this document [**options**](https://rsdoctor.dev/config/options/options) for parameter configuration of the RsdoctorRspackPlugin. diff --git a/website/docs/en/guide/tech/_meta.json b/website/docs/en/guide/tech/_meta.json index 2b827743e75..e37f08560b3 100644 --- a/website/docs/en/guide/tech/_meta.json +++ b/website/docs/en/guide/tech/_meta.json @@ -1,6 +1,7 @@ [ "typescript", "css", + "html", "json", "react", "preact", diff --git a/website/docs/en/guide/tech/html.mdx b/website/docs/en/guide/tech/html.mdx new file mode 100644 index 00000000000..ff46c1ef0e3 --- /dev/null +++ b/website/docs/en/guide/tech/html.mdx @@ -0,0 +1,43 @@ +# HTML + +Rspack supports generating HTML files using the following plugins, and automatically injecting the generated CSS and JavaScript files into the HTML. +This is particularly useful for Rspack bundles that contain filename hashes, as the hashes can change with each Rspack build. + +## HtmlWebpackPlugin + +Rspack fully supports [HtmlWebpackPlugin](https://github.com/jantimon/html-webpack-plugin). + +```js title="rspack.config.js" +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path'); + +module.exports = { + entry: 'index.js', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'index_bundle.js', + }, + plugins: [new HtmlWebpackPlugin()], +}; +``` + +For all configuration options, see the [plugin documentation](https://github.com/jantimon/html-webpack-plugin#options). + +## Built-in HtmlRspackPlugin + +[HtmlRspackPlugin](/plugins/rspack/html-rspack-plugin) is a high-performance HTML plugin implemented in Rust, offering significantly better build performance than the `HtmlWebpackPlugin`, especially when building a large number of HTML files. + +```js title="rspack.config.js" +const rspack = require('@rspack/core'); + +module.exports = { + entry: 'index.js', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'index_bundle.js', + }, + plugins: [new rspack.HtmlRspackPlugin()], +}; +``` + +For all configuration options, see the [plugin documentation](/plugins/rspack/html-rspack-plugin). diff --git a/website/docs/zh/contribute/development/profiling.md b/website/docs/zh/contribute/development/profiling.md index f04a05a95a1..9cd22be8f62 100644 --- a/website/docs/zh/contribute/development/profiling.md +++ b/website/docs/zh/contribute/development/profiling.md @@ -69,6 +69,14 @@ npm install -g speedscope speedscope CPU.20230522.154658.14577.0.001.cpuprofile ``` +### Rsdoctor Timeline + +如果我们想要分析 Loader 和 Plugin 耗时或者 Loader 的编译行为,可以利用 Rsdoctor 来查看: + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-loader-timeline.png) + +参考 [Rsdoctor Compilation Analysis](/guide/optimization/profile#rsdoctor-%E7%9A%84%E7%BC%96%E8%AF%91%E5%88%86%E6%9E%90) + ## Mac Xcode Instruments 如果您使用的是 Mac,则 Xcode Instruments 工具可用于生成 CPU profile 文件。 diff --git a/website/docs/zh/guide/optimization/analysis.mdx b/website/docs/zh/guide/optimization/analysis.mdx index d7a1294e1c9..e86b28e8717 100644 --- a/website/docs/zh/guide/optimization/analysis.mdx +++ b/website/docs/zh/guide/optimization/analysis.mdx @@ -1,13 +1,44 @@ # 包分析 +## webpack-bundle-analyzer + Rspack CLI 支持使用 `--analyze` 选项进行包体积分析,我们在内部使用 [webpack-bundle-analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer) 实现该功能。 ```sh $ rspack build --analyze ``` +## bundle-stats & statoscope + 你也可以生成 stats.json 文件,用其他包解析工具进一步分析,如 [bundle-stats](https://github.com/relative-ci/bundle-stats) 或 [statoscope](https://statoscope.tech/)。 ```sh $ rspack build --json stats.json ``` + +## Rsdoctor 的产物分析 + +Rsdoctor 提供了 `Bundle Size` 模块,该模块主要用于分析 Rspack 构建产物的信息,包括当前编译产物的资源大小、重复包、模块引用关系等: + +- **产物概览**:展示产物总数、各类型文件数目、大小以及占比,以及重复包和重复包的引用链; +- **产物分析(`Bundle Analysis`)模块**:分析构建产物资源(**Assets**)以及所包含的 **Modules** 的大小和代码信息。在该模块中,可以查看 Assets 中 **Module 打包后的实际代码大小**,以及模块的原始代码或**打包后的代码段**以及**模块引用关系**。 + + + +点击**导航栏 「Bundle Size」-> 「Bundle Size」选项**,即可查看 Bundle 分析报告。详细文档可查看[Bundle Size](https://rsdoctor.dev/zh/guide/usage/bundle-size) + +### 重复包治理 + +在生产构建中,优化包大小是一个重要环节,因为它直接影响到在线用户的使用体验。 + +- 检查重复包: + +你可以使用 Rsdoctor 来检测项目中是否存在重复的第三方依赖包。Rsdoctor 在构建过程中进行分析,找到重复依赖包并以可视化方式显示出来: + +![](https://assets.rspack.dev/others/assets/rsdoctor/overall-alerts.jpg) + +更多的细节, 可以查看 [Rsdoctor - 重复包治理](https://rsdoctor.dev/zh/blog/topic/duplicate-pkg-problem). diff --git a/website/docs/zh/guide/optimization/profile.mdx b/website/docs/zh/guide/optimization/profile.mdx index b2a24e1de61..138af643052 100644 --- a/website/docs/zh/guide/optimization/profile.mdx +++ b/website/docs/zh/guide/optimization/profile.mdx @@ -2,6 +2,8 @@ import { ApiMeta, Stability } from '@components/ApiMeta.tsx'; # 性能分析 +## Rspack Profile + Rspack CLI 支持使用 `RSPACK_PROFILE` 环境变量来进行构建性能分析。 @@ -15,3 +17,36 @@ $ RSPACK_PROFILE=ALL rspack build - `trace.json`:使用 [tracing](https://github.com/tokio-rs/tracing) 细粒度地记录了 Rust 侧各个阶段的耗时,可以使用 [ui.perfetto.dev](https://ui.perfetto.dev/) 进行查看 - `jscpuprofile.json`:使用 [Node.js inspector](https://nodejs.org/dist/latest-v18.x/docs/api/inspector.html) 细粒度地记录了 JavaScript 侧的各个阶段的耗时,可以使用 [speedscope.app](https://www.speedscope.app/) 进行查看 - `logging.json`:包含一些日志信息,粗粒度地记录了构建的各个阶段耗时 + +## Rsdoctor 的编译分析 + +你可以通过使用 Rsdoctor 来查看 Loader 和 Plugin 的编译耗时及编译过程。 + +#### Loader Timeline + +如果想要分析 Loader 和 Plugin 耗时或者 Loader 的编译行为,可以利用 Rsdoctor 来查看: + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-loader-timeline.png) + +您可以在时间轴中查看每个 Loader 的执行时间以及编译的文件,以及这个 Loader 编译文件所花费的时间。 + +参考 [Loader Timeline](https://rsdoctor.dev/guide/usage/loaders-timeline). + +#### Loader Details + +如果你想查看 Loader 对代码编译过程,那么可以利用 [Loader Details](https://rsdoctor.dev/guide/usage/loaders-analysis) 功能: + +![image](https://assets.rspack.dev/others/assets/rsdoctor/rsdoctor-analyze-code.png) + +在这个报告页面中,你可以查看到 Loader 对每个文件的编译前后的代码的变化。 + +参考 [Loader Details](https://rsdoctor.dev/guide/usage/loaders-analysis). + +#### 开启编译耗时分析 + +关于如何启用 `RsdoctorRspackPlugin`,可以参考 [使用 Rsdoctor](/guide/optimization/use-rsdoctor)。 + +- `RsdoctorRspackPlugin` 的 `features.loader` 和 `features.plugins` 参数需要为 `true`,`features.loader` 和 `features.plugins` 是默认开启的。查看参数文档[Rsdoctor options](https://rsdoctor.dev/zh/config/options/options#features)。 +- 使用文档: + - Loader Analysis 使用文档可查看 [Loader Timeline](https://rsdoctor.dev/zh/guide/usage/loaders-timeline) 和 [Loader Details](https://rsdoctor.dev/zh/guide/usage/loaders-analysis)。 + - Plugins Analysis 使用文档可查看 [Plugin Analysis](https://rsdoctor.dev/zh/guide/usage/plugins-analysis)。 diff --git a/website/docs/zh/guide/optimization/use-rsdoctor.mdx b/website/docs/zh/guide/optimization/use-rsdoctor.mdx new file mode 100644 index 00000000000..be36929479e --- /dev/null +++ b/website/docs/zh/guide/optimization/use-rsdoctor.mdx @@ -0,0 +1,88 @@ +# 使用 Rsdoctor + +[Rsdoctor](https://rsdoctor.dev/) 是一个针对 Rspack 的构建分析器,可以直观地展示构建过程,例如编译时间、编译前后的代码变化、模块引用关系、重复模块等。如果您需要排查构建产物或构建时编译问题,可以使用 Rsdoctor。 + +## 💡 什么是 Rsdoctor? + +- Rsdoctor 是一个面向构建过程与构建产物提供诊断和分析的一站式工具。 +- Rsdoctor 是一个支持 **Rspack** 或 **webpack** 构建分析工具。 +- Rsdoctor 是一个可以展示编译耗时及编译行为细节的分析工具。 +- Rsdoctor 是一个可以支持 **Rspack builtin:swc-loader** 构建耗时及构建行为分析的工具 + +## 🔥 特性 + +- **编译可视化**:Rsdoctor 将编译行为及耗时进行可视化展示,方便开发者查看构建问题。 + +| loader timeline | loader codes | +| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | + +- **多种分析能力**:支持构建产物、构建时分析能力: + - 构建产物支持资源列表及模块依赖等。 + - 构建时分析支持 Loader、Plugin、Resolver 构建过程分析。 + - 支持 Rspack 的 builtin:swc-loader 分析。 + - 构建规则支持重复包检测及 ES Version Check 检查等。 +- **支持自定义规则**:除了内置构建扫描规则外,还支持用户根据 Rsdoctor 的构建数据添加自定义构建扫描规则。 +- **框架无关**:支持所有基于 **Rspack** 或 **webpack** 构建的项目。 + +### 特性详情 + +更详细的特性使用介绍你可以查看这篇文档 [Rsdoctor Features](https://rsdoctor.dev/zh/guide/start/features)。 + +### 产物分析 + +你可以利用 Rsdoctor **[分析 Rspack 的构建产物](/guide/optimization/analysis#rsdoctor-%E7%9A%84%E4%BA%A7%E7%89%A9%E5%88%86%E6%9E%90)**. + +### 编译分析 + +Rsdoctor 是一个构建分析工具,可以直观地展示每个 loader 和 plugin 的编译时间。详细信息请参阅[编译分析](/guide/optimization/profile#rsdoctor-%E7%9A%84%E7%BC%96%E8%AF%91%E5%88%86%E6%9E%90)。 + +## 快速开始 + +在使用 Rspack 的项目中,你可以使用以下方式开启 Rsdoctor: + +1. 安装 `RsdoctorRspackPlugin` 插件: + +import { PackageManagerTabs } from '@theme'; + + + +2. 注册 `RsdoctorRspackPlugin` 插件: + +:::danger + +- 请不要在线上版本构建时使用 Rsdoctor。 + +::: + +在 `rspack.config.js` 的 [plugins](/config/plugins.html#plugins) 中初始化 RsdoctorRspackPlugin 插件,参考: + +```ts title="rspack.config.js" +const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin'); + +module.exports = { + // ... + plugins: [ + // 仅在 RSDOCTOR 为 true 时注册插件,因为插件会增加构建耗时 + process.env.RSDOCTOR && + new RsdoctorRspackPlugin({ + // 插件选项 + }), + ].filter(Boolean), +}; +``` + +3. 在构建命令之前添加 `RSDOCTOR=true` 变量: + +```bash +# dev +RSDOCTOR=true rspack serve + +# build +RSDOCTOR=true rspack build +``` + +运行上述命令后,会在构建完成后打开构建分析页面。完整功能请参考 [Rsdoctor 文档](https://rsdoctor.dev/)。 + +4. **[可选]** 配置 Options + +你可以参照这篇文档 [**options**](https://rsdoctor.dev/config/options/options) 对 RsdoctorRspackPlugin 进行参数配置。 diff --git a/website/docs/zh/guide/tech/_meta.json b/website/docs/zh/guide/tech/_meta.json index 2b827743e75..e37f08560b3 100644 --- a/website/docs/zh/guide/tech/_meta.json +++ b/website/docs/zh/guide/tech/_meta.json @@ -1,6 +1,7 @@ [ "typescript", "css", + "html", "json", "react", "preact", diff --git a/website/docs/zh/guide/tech/html.mdx b/website/docs/zh/guide/tech/html.mdx new file mode 100644 index 00000000000..2be81f4a130 --- /dev/null +++ b/website/docs/zh/guide/tech/html.mdx @@ -0,0 +1,42 @@ +# HTML + +Rspack 支持通过以下插件来生成 HTML 文件,并自动将生成的 CSS 和 JavaScript 文件注入到 HTML 中。这对于文件名包含哈希值的输出文件尤为有用,因为 Rspack 每次构建时哈希值都可能会变化。 + +## HtmlWebpackPlugin + +Rspack 完全支持 [HtmlWebpackPlugin](https://github.com/jantimon/html-webpack-plugin)。 + +```js title="rspack.config.js" +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path'); + +module.exports = { + entry: 'index.js', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'index_bundle.js', + }, + plugins: [new HtmlWebpackPlugin()], +}; +``` + +有关所有配置选项,请参阅[插件文档](https://github.com/jantimon/html-webpack-plugin#options)。 + +## 内置 HtmlRspackPlugin + +[HtmlRspackPlugin](/plugins/rspack/html-rspack-plugin) 是以 Rust 实现的高性能 HTML 插件,它的构建性能显著优于 `HtmlWebpackPlugin` 插件,尤其是在构建大量 HTML 文件的场景下。 + +```js title="rspack.config.js" +const rspack = require('@rspack/core'); + +module.exports = { + entry: 'index.js', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'index_bundle.js', + }, + plugins: [new rspack.HtmlRspackPlugin()], +}; +``` + +有关所有配置选项,请参阅[插件文档](/plugins/rspack/html-rspack-plugin)。 diff --git a/website/project-words.txt b/website/project-words.txt index 4f2dfe3e692..579327efe76 100644 --- a/website/project-words.txt +++ b/website/project-words.txt @@ -118,6 +118,8 @@ Rollup rome rsbuild Rsdoctor +rsdoctor +rsdoctors rsfamily Rslib rspack