diff --git a/Cargo.lock b/Cargo.lock index 5d9c1b03f6c..80ab4e4f749 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3379,6 +3379,7 @@ dependencies = [ name = "rspack_plugin_javascript" version = "0.1.0" dependencies = [ + "anymap", "async-trait", "bitflags 2.5.0", "dashmap", @@ -3401,7 +3402,6 @@ dependencies = [ "rspack_regex", "rspack_util", "rustc-hash", - "serde", "serde_json", "sugar_path", "swc_core", diff --git a/Cargo.toml b/Cargo.toml index c34e74c3e25..cb764096860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ resolver = "2" # See https://doc.rust-lang.org/cargo/reference/resolver [workspace.dependencies] anyhow = { version = "1.0.81", features = ["backtrace"] } +anymap = { version = "=1.0.0-beta.2" } async-recursion = { version = "1.1.0" } async-scoped = { version = "0.9.0" } async-trait = { version = "0.1.79" } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index f81c2e2c0ec..48e1b6e50c1 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/web-infra-dev/rspack" version = "0.1.0" [dependencies] -anymap = "1.0.0-beta.2" +anymap = { workspace = true } async-recursion = { workspace = true } async-trait = { workspace = true } bitflags = { workspace = true } diff --git a/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs b/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs index 2545942cc3f..e03ce6a75f7 100644 --- a/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs +++ b/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs @@ -299,7 +299,6 @@ mod t { let module_deps_2 = ModuleDeps::from_module(&mg, &module1_id); assert_eq!(module_deps_1, module_deps_2); - dbg!(module_deps_1.clone()); let dep2 = Box::new(TestDep::new(vec!["bar"])); let dep2_id = *dep2.id(); @@ -316,6 +315,5 @@ mod t { let module_deps_3 = ModuleDeps::from_module(&mg, &module_orig_id); assert_ne!(module_deps_3, module_deps_1); - dbg!(module_deps_3); } } diff --git a/crates/rspack_loader_runner/Cargo.toml b/crates/rspack_loader_runner/Cargo.toml index 8349353eedf..81c13a6eb04 100644 --- a/crates/rspack_loader_runner/Cargo.toml +++ b/crates/rspack_loader_runner/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/web-infra-dev/rspack" version = "0.1.0" [dependencies] -anymap = "1.0.0-beta.2" +anymap = { workspace = true } async-recursion = { workspace = true } async-trait = { workspace = true } bitflags = { workspace = true } diff --git a/crates/rspack_plugin_javascript/Cargo.toml b/crates/rspack_plugin_javascript/Cargo.toml index 8e3dba38a54..cca4231ca1e 100644 --- a/crates/rspack_plugin_javascript/Cargo.toml +++ b/crates/rspack_plugin_javascript/Cargo.toml @@ -6,6 +6,7 @@ repository = "https://github.com/web-infra-dev/rspack" version = "0.1.0" [dependencies] +anymap = { workspace = true } async-trait = { workspace = true } bitflags = { workspace = true } dashmap = { workspace = true } @@ -28,7 +29,6 @@ rspack_ids = { path = "../rspack_ids/" } rspack_regex = { path = "../rspack_regex" } rspack_util = { path = "../rspack_util" } rustc-hash = { workspace = true } -serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sugar_path = { workspace = true } swc_core = { workspace = true, features = [ 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 ff1debe8655..287fe0a4890 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 @@ -66,7 +66,6 @@ pub struct HarmonyImportSideEffectDependency { pub id: DependencyId, pub span: Option, pub source_span: Option, - pub specifiers: Vec, pub dependency_type: DependencyType, pub export_all: bool, resource_identifier: String, @@ -78,7 +77,6 @@ impl HarmonyImportSideEffectDependency { source_order: i32, span: Option, source_span: Option, - specifiers: Vec, dependency_type: DependencyType, export_all: bool, ) -> Self { @@ -89,7 +87,6 @@ impl HarmonyImportSideEffectDependency { request, span, source_span, - specifiers, dependency_type, export_all, resource_identifier, diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs index 144a1a51e0d..71c06288c58 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs @@ -187,7 +187,6 @@ impl DependencyTemplate for HarmonyImportSpecifierDependency { } } else { harmony_import_dependency_apply(self, self.source_order, code_generatable_context); - // dbg!(&self.shorthand, self.asi_safe); export_from_import( code_generatable_context, true, 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 b7890899267..02395a548ba 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/compatibility_plugin.rs @@ -11,7 +11,7 @@ use crate::{ const NESTED_WEBPACK_IDENTIFIER_TAG: &str = "_identifier__nested_webpack_identifier__"; -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(Debug, Clone)] struct NestedRequireData { name: String, update: bool, @@ -19,12 +19,14 @@ struct NestedRequireData { } impl TagInfoData for NestedRequireData { - fn serialize(data: &Self) -> serde_json::Value { - serde_json::to_value(data).expect("serialize failed for `NestedRequireData`") + fn into_any(data: Self) -> Box { + Box::new(data) } - fn deserialize(value: serde_json::Value) -> Self { - serde_json::from_value(value).expect("deserialize failed for `NestedRequireData`") + fn downcast(any: Box) -> Self { + *(any as Box) + .downcast() + .expect("NestedRequireData should be downcasted from correct tag info") } } @@ -124,37 +126,28 @@ impl JavascriptParserPlugin for CompatibilityPlugin { if name != RuntimeGlobals::REQUIRE.name() { return None; } - let Some(variable_info) = parser.get_mut_variable_info(name) else { - return None; - }; + let tag_info = parser + .definitions_db + .expect_get_mut_tag_info(&parser.current_tag_info?); - // FIXME: should find the `tag_info` which tag equal `NESTED_WEBPACK_IDENTIFIER_TAG`; - let Some(tag_info) = &mut variable_info.tag_info else { - unreachable!(); - }; - if tag_info.tag != NESTED_WEBPACK_IDENTIFIER_TAG { - return None; - } - let Some(data) = tag_info.data.as_mut().map(std::mem::take) else { - unreachable!(); - }; - let mut nested_require_data = NestedRequireData::deserialize(data); + let mut nested_require_data = NestedRequireData::downcast(tag_info.data.take()?); let mut deps = Vec::with_capacity(2); + let name = nested_require_data.name.clone(); if !nested_require_data.update { deps.push(ConstDependency::new( nested_require_data.loc.start(), nested_require_data.loc.end(), - nested_require_data.name.clone().into(), + name.clone().into(), None, )); nested_require_data.update = true; } - tag_info.data = Some(NestedRequireData::serialize(&nested_require_data)); + tag_info.data = Some(NestedRequireData::into_any(nested_require_data)); deps.push(ConstDependency::new( ident.span.real_lo(), ident.span.real_hi(), - nested_require_data.name.into(), + name.into(), None, )); for dep in deps { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs b/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs index 0dc0ba1c03b..928969df716 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/drive.rs @@ -1,3 +1,4 @@ +use swc_core::atoms::Atom; use swc_core::common::Span; use swc_core::ecma::ast::{ BinExpr, CallExpr, Callee, CondExpr, ExportDecl, ExportDefaultDecl, Expr, OptChainExpr, @@ -110,18 +111,51 @@ impl JavascriptParserPlugin for JavaScriptParserPluginDrive { None } + fn member_chain( + &self, + parser: &mut JavascriptParser, + expr: &swc_core::ecma::ast::MemberExpr, + for_name: &str, + members: &[Atom], + members_optionals: &[bool], + member_ranges: &[Span], + ) -> Option { + for plugin in &self.plugins { + let res = plugin.member_chain( + parser, + expr, + for_name, + members, + members_optionals, + member_ranges, + ); + // `SyncBailHook` + if res.is_some() { + return res; + } + } + None + } + fn call_member_chain( &self, parser: &mut JavascriptParser, - root_info: &ExportedVariableInfo, expr: &CallExpr, - // TODO: members: &Vec, - // TODO: members_optionals: Vec, - // TODO: members_ranges: Vec + for_name: &str, + members: &[Atom], + members_optionals: &[bool], + member_ranges: &[Span], ) -> Option { assert!(matches!(expr.callee, Callee::Expr(_))); for plugin in &self.plugins { - let res = plugin.call_member_chain(parser, root_info, expr); + let res = plugin.call_member_chain( + parser, + expr, + for_name, + members, + members_optionals, + member_ranges, + ); // `SyncBailHook` if res.is_some() { return res; @@ -444,8 +478,8 @@ impl JavascriptParserPlugin for JavaScriptParserPluginDrive { parser: &mut JavascriptParser, statement: &swc_core::ecma::ast::ImportDecl, source: &swc_core::atoms::Atom, - export_name: Option<&str>, - identifier_name: &str, + export_name: Option<&Atom>, + identifier_name: &Atom, ) -> Option { for plugin in &self.plugins { let res = plugin.import_specifier(parser, statement, source, export_name, identifier_name); diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_export_dependency_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_export_dependency_parser_plugin.rs index ecb4052a60f..f9cc62532d8 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_export_dependency_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_export_dependency_parser_plugin.rs @@ -74,15 +74,7 @@ fn handle_esm_export_harmony_import_side_effects_dep( }); } - handle_harmony_import_side_effects_dep( - parser, - request, - span, - source_span, - specifiers, - dep_type, - export_all, - ) + handle_harmony_import_side_effects_dep(parser, request, span, source_span, dep_type, export_all) } pub struct HarmonyExportDependencyParserPlugin; diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_import_dependency_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_import_dependency_parser_plugin.rs index 3e73a321cb1..5505ee7b00f 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/harmony_import_dependency_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/harmony_import_dependency_parser_plugin.rs @@ -1,10 +1,9 @@ -use rspack_core::tree_shaking::symbol::DEFAULT_JS_WORD; -use rspack_core::{extract_member_expression_chain, ConstDependency, DependencyType, SpanExt}; +use rspack_core::{ConstDependency, DependencyType, SpanExt}; use swc_core::atoms::Atom; use swc_core::common::{Span, Spanned}; use swc_core::ecma::ast::{ - AssignExpr, AssignOp, AssignTarget, AssignTargetPat, Callee, ImportSpecifier, ModuleExportName, - OptChainExpr, + AssignExpr, AssignOp, AssignTarget, AssignTargetPat, Callee, ImportSpecifier, MemberExpr, + ModuleExportName, OptChainBase, }; use swc_core::ecma::ast::{Expr, Ident, ImportDecl}; @@ -20,7 +19,6 @@ pub(super) fn handle_harmony_import_side_effects_dep( request: Atom, span: Span, source_span: Span, - specifiers: Vec, dep_type: DependencyType, exports_all: bool, ) { @@ -29,27 +27,69 @@ pub(super) fn handle_harmony_import_side_effects_dep( parser.last_harmony_import_order, Some(span.into()), Some(source_span.into()), - specifiers, dep_type, exports_all, ); parser.dependencies.push(Box::new(dependency)); } +fn get_non_optional_part<'a>(members: &'a [Atom], members_optionals: &[bool]) -> &'a [Atom] { + let mut i = 0; + while i < members.len() && matches!(members_optionals.get(i), Some(false)) { + i += 1; + } + if i != members.len() { + &members[0..i] + } else { + members + } +} + +fn get_non_optional_member_chain_from_expr(mut expr: &Expr, mut count: i32) -> &Expr { + while count != 0 { + if let Expr::Member(member) = expr { + expr = &member.obj; + count -= 1; + } else if let Expr::OptChain(opt_chain) = expr { + expr = match &*opt_chain.base { + OptChainBase::Member(member) => &*member.obj, + OptChainBase::Call(call) if let Some(member) = call.callee.as_member() => &*member.obj, + _ => unreachable!(), + }; + count -= 1; + } else { + unreachable!() + } + } + expr +} + +fn get_non_optional_member_chain_from_member(member: &MemberExpr, mut count: i32) -> &Expr { + count -= 1; + get_non_optional_member_chain_from_expr(&member.obj, count) +} + pub struct HarmonyImportDependencyParserPlugin; const HARMONY_SPECIFIER_TAG: &str = "_identifier__harmony_specifier_tag__"; -#[derive(serde::Deserialize, serde::Serialize, Clone)] -struct MockData; +#[derive(Debug, Clone)] +struct HarmonySpecifierData { + name: Atom, + source: Atom, + ids: Vec, + source_order: i32, +} -impl TagInfoData for MockData { - fn serialize(_: &Self) -> serde_json::Value { - serde_json::Value::Null +impl TagInfoData for HarmonySpecifierData { + fn into_any(data: Self) -> Box { + Box::new(data) } - fn deserialize(_: serde_json::Value) -> Self { - MockData + fn downcast(any: Box) -> Self { + *(any as Box) + .downcast() + .expect("HarmonySpecifierData should be downcasted from correct tag info") } } @@ -95,7 +135,7 @@ impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin { ImporterReferenceInfo::new( import_decl.src.value.clone(), specifier.clone(), - Some(DEFAULT_JS_WORD.clone()), + Some("default".into()), parser.last_harmony_import_order, ), ); @@ -121,7 +161,6 @@ impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin { import_decl.src.value.clone(), import_decl.span, import_decl.src.span, - specifiers, DependencyType::EsmImport(import_decl.span.into()), false, ); @@ -146,12 +185,20 @@ impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin { &self, parser: &mut JavascriptParser, _statement: &ImportDecl, - _source: &Atom, - _export_name: Option<&str>, - identifier_name: &str, + source: &Atom, + id: Option<&Atom>, + name: &Atom, ) -> Option { - // TODO: fill data with `Some({name, source, ids, source_order, assertions })` - parser.tag_variable::(identifier_name.to_string(), HARMONY_SPECIFIER_TAG, None); + parser.tag_variable::( + name.to_string(), + HARMONY_SPECIFIER_TAG, + Some(HarmonySpecifierData { + name: name.clone(), + source: source.clone(), + ids: id.map(|id| vec![id.clone()]).unwrap_or_default(), + source_order: parser.last_harmony_import_order, + }), + ); Some(true) } @@ -159,170 +206,146 @@ impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin { &self, parser: &mut JavascriptParser, ident: &Ident, - _for_name: &str, + for_name: &str, ) -> Option { - if parser.in_short_hand { - parser - .rewrite_usage_span - .insert(ident.span, ExtraSpanInfo::ReWriteUsedByExports); - if let Some(reference) = parser.import_map.get(&ident.to_id()) { - parser - .dependencies - .push(Box::new(HarmonyImportSpecifierDependency::new( - reference.request.clone(), - reference.specifier.name(), - reference.source_order, - true, - !parser.is_asi_position(ident.span_lo()), - ident.span.real_lo(), - ident.span.real_hi(), - reference.names.clone().map(|f| vec![f]).unwrap_or_default(), - false, - false, - HarmonyImportSpecifierDependency::create_export_presence_mode( - parser.javascript_options, - ), - None, - ident.span, - ))); - } - Some(true) - } else if let Some(reference) = parser.import_map.get(&ident.to_id()) { - parser - .rewrite_usage_span - .insert(ident.span, ExtraSpanInfo::ReWriteUsedByExports); - // dbg!(!parser.is_asi_position(ident.span_lo())); - parser - .dependencies - .push(Box::new(HarmonyImportSpecifierDependency::new( - reference.request.clone(), - reference.specifier.name(), - reference.source_order, - false, - !parser.is_asi_position(ident.span_lo()), - ident.span.real_lo(), - ident.span.real_hi(), - reference.names.clone().map(|f| vec![f]).unwrap_or_default(), - parser.enter_callee && !parser.enter_new_expr, - true, // x() - HarmonyImportSpecifierDependency::create_export_presence_mode(parser.javascript_options), - parser.properties_in_destructuring.remove(&ident.sym), - ident.span, - ))); - Some(true) - } else { - None + if for_name != HARMONY_SPECIFIER_TAG { + return None; } + let tag_info = parser + .definitions_db + .expect_get_tag_info(&parser.current_tag_info?); + let settings = HarmonySpecifierData::downcast(tag_info.data.clone()?); + + parser + .rewrite_usage_span + .insert(ident.span, ExtraSpanInfo::ReWriteUsedByExports); + parser + .dependencies + .push(Box::new(HarmonyImportSpecifierDependency::new( + settings.source, + settings.name, + settings.source_order, + parser.in_short_hand, + !parser.is_asi_position(ident.span_lo()), + ident.span.real_lo(), + ident.span.real_hi(), + settings.ids, + parser.in_tagged_template_tag, + true, + HarmonyImportSpecifierDependency::create_export_presence_mode(parser.javascript_options), + parser.properties_in_destructuring.remove(&ident.sym), + ident.span, + ))); + Some(true) } fn call_member_chain( &self, parser: &mut JavascriptParser, - _root_info: &crate::visitors::ExportedVariableInfo, - expr: &swc_core::ecma::ast::CallExpr, - // TODO: members: &Vec, - // TODO: members_optionals: Vec, - // TODO: members_ranges: Vec + call_expr: &swc_core::ecma::ast::CallExpr, + for_name: &str, + members: &[Atom], + members_optionals: &[bool], + _member_ranges: &[Span], ) -> Option { - let Callee::Expr(callee) = &expr.callee else { + let Callee::Expr(callee) = &call_expr.callee else { unreachable!() }; - let expression_info = extract_member_expression_chain(&**callee); - let member_chain = expression_info.members(); - assert!( - !member_chain.is_empty(), - "enter from call expression so the len of member must be not empty" - ); - let direct_import = member_chain.len() == 1; - if let Some(reference) = parser.import_map.get(&member_chain[0]) { - let mut member_chain = member_chain.clone(); - member_chain.pop_front(); - if !member_chain.is_empty() { - let mut ids = reference.names.clone().map(|f| vec![f]).unwrap_or_default(); - // dbg!(&member_chain); - ids.extend_from_slice( - &member_chain - .into_iter() - .map(|item| item.0.clone()) - .collect::>(), - ); - parser - .rewrite_usage_span - .insert(callee.span(), ExtraSpanInfo::ReWriteUsedByExports); - parser - .dependencies - .push(Box::new(HarmonyImportSpecifierDependency::new( - reference.request.clone(), - reference.specifier.name(), - reference.source_order, - false, - !parser.is_asi_position(expr.span_lo()), - callee.span().real_lo(), - callee.span().real_hi(), - ids, - true, - direct_import, - HarmonyImportSpecifierDependency::create_export_presence_mode( - parser.javascript_options, - ), - None, - callee.span(), - ))); - parser.walk_expr_or_spread(&expr.args); - return Some(true); - } + if for_name != HARMONY_SPECIFIER_TAG { + return None; } - None + let tag_info = parser + .definitions_db + .expect_get_tag_info(&parser.current_tag_info?); + let settings = HarmonySpecifierData::downcast(tag_info.data.clone()?); + + let non_optional_members = get_non_optional_part(members, members_optionals); + let (start, end) = if members.len() > non_optional_members.len() { + let expr = get_non_optional_member_chain_from_expr( + callee, + (members.len() - non_optional_members.len()) as i32, + ); + (expr.span().real_lo(), expr.span().real_hi()) + } else { + (callee.span().real_lo(), callee.span().real_hi()) + }; + let mut ids = settings.ids; + ids.extend(non_optional_members.iter().cloned()); + let direct_import = members.is_empty(); + parser + .rewrite_usage_span + .insert(callee.span(), ExtraSpanInfo::ReWriteUsedByExports); + parser + .dependencies + .push(Box::new(HarmonyImportSpecifierDependency::new( + settings.source, + settings.name, + settings.source_order, + false, + !parser.is_asi_position(call_expr.span_lo()), + start, + end, + ids, + true, + direct_import, + HarmonyImportSpecifierDependency::create_export_presence_mode(parser.javascript_options), + None, + callee.span(), + ))); + parser.walk_expr_or_spread(&call_expr.args); + Some(true) } - fn member( + fn member_chain( &self, parser: &mut JavascriptParser, member_expr: &swc_core::ecma::ast::MemberExpr, - _for_name: &str, + for_name: &str, + members: &[Atom], + members_optionals: &[bool], + _member_ranges: &[Span], ) -> Option { - let expression_info = extract_member_expression_chain(member_expr); - let member_chain = expression_info.members(); - if member_chain.len() > 1 - && let Some(reference) = parser.import_map.get(&member_chain[0]) - { - let mut member_chain = member_chain.clone(); - member_chain.pop_front(); - if !member_chain.is_empty() { - let mut ids = reference.names.clone().map(|f| vec![f]).unwrap_or_default(); - // dbg!(&member_chain); - ids.extend_from_slice( - &member_chain - .into_iter() - .map(|item| item.0.clone()) - .collect::>(), - ); - parser - .rewrite_usage_span - .insert(member_expr.span, ExtraSpanInfo::ReWriteUsedByExports); - parser - .dependencies - .push(Box::new(HarmonyImportSpecifierDependency::new( - reference.request.clone(), - reference.specifier.name(), - reference.source_order, - false, - !parser.is_asi_position(member_expr.span_lo()), - member_expr.span.real_lo(), - member_expr.span.real_hi(), - ids, - parser.enter_callee && !parser.enter_new_expr, - !parser.enter_callee, // x.xx() - HarmonyImportSpecifierDependency::create_export_presence_mode( - parser.javascript_options, - ), - None, - member_expr.span, - ))); - return Some(true); - } + if for_name != HARMONY_SPECIFIER_TAG { + return None; } - None + let tag_info = parser + .definitions_db + .expect_get_tag_info(&parser.current_tag_info?); + let settings = HarmonySpecifierData::downcast(tag_info.data.clone()?); + + let non_optional_members = get_non_optional_part(members, members_optionals); + let (start, end) = if members.len() > non_optional_members.len() { + let expr = get_non_optional_member_chain_from_member( + member_expr, + (members.len() - non_optional_members.len()) as i32, + ); + (expr.span().real_lo(), expr.span().real_hi()) + } else { + (member_expr.span.real_lo(), member_expr.span.real_hi()) + }; + let mut ids = settings.ids; + ids.extend(non_optional_members.iter().cloned()); + parser + .rewrite_usage_span + .insert(member_expr.span, ExtraSpanInfo::ReWriteUsedByExports); + parser + .dependencies + .push(Box::new(HarmonyImportSpecifierDependency::new( + settings.source, + settings.name, + settings.source_order, + false, + !parser.is_asi_position(member_expr.span_lo()), + start, + end, + ids, + false, + false, // x.xx() + HarmonyImportSpecifierDependency::create_export_presence_mode(parser.javascript_options), + None, + member_expr.span, + ))); + Some(true) } // collect referenced properties in destructuring @@ -345,60 +368,4 @@ impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin { } None } - - fn optional_chaining( - &self, - parser: &mut JavascriptParser, - opt_chain_expr: &OptChainExpr, - ) -> Option { - let expression_info = extract_member_expression_chain(opt_chain_expr); - // dbg!(&expression_info); - let member_chain = expression_info.members(); - if member_chain.len() > 1 - && let Some(reference) = parser.import_map.get(&member_chain[0]) - { - let mut non_optional_members = expression_info.non_optional_part(); - // dbg!(&non_optional_members); - let start = opt_chain_expr.span.real_lo(); - let end = if !non_optional_members.is_empty() - && let Some(span) = expression_info - .members_spans() - .get(non_optional_members.len() - 1) - { - span.real_hi() - } else { - opt_chain_expr.span.real_hi() - }; - non_optional_members.pop_front(); - let mut ids = reference.names.clone().map(|f| vec![f]).unwrap_or_default(); - ids.extend_from_slice( - &non_optional_members - .into_iter() - .map(|item| item.0.clone()) - .collect::>(), - ); - parser - .rewrite_usage_span - .insert(opt_chain_expr.span, ExtraSpanInfo::ReWriteUsedByExports); - parser - .dependencies - .push(Box::new(HarmonyImportSpecifierDependency::new( - reference.request.clone(), - reference.specifier.name(), - reference.source_order, - false, - !parser.is_asi_position(opt_chain_expr.span_lo()), - start, - end, - ids, - parser.enter_callee && !parser.enter_new_expr, - !parser.enter_callee, // x.xx() - HarmonyImportSpecifierDependency::create_export_presence_mode(parser.javascript_options), - None, - opt_chain_expr.span, - ))); - return Some(true); - } - None - } } diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs index bd8ca3ac8c9..8da26aaf30a 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/trait.rs @@ -94,11 +94,11 @@ pub trait JavascriptParserPlugin { fn call_member_chain( &self, _parser: &mut JavascriptParser, - _root_info: &ExportedVariableInfo, _expr: &CallExpr, - // TODO: members: &Vec, - // TODO: members_optionals: Vec, - // TODO: members_ranges: Vec + _for_name: &str, + _members: &[Atom], + _members_optionals: &[bool], + _member_ranges: &[Span], ) -> Option { None } @@ -112,6 +112,18 @@ pub trait JavascriptParserPlugin { None } + fn member_chain( + &self, + _parser: &mut JavascriptParser, + _expr: &MemberExpr, + _for_name: &str, + _members: &[Atom], + _members_optionals: &[bool], + _member_ranges: &[Span], + ) -> Option { + None + } + fn unhandled_expression_member_chain( &self, _parser: &mut JavascriptParser, @@ -236,8 +248,8 @@ pub trait JavascriptParserPlugin { _parser: &mut JavascriptParser, _statement: &ImportDecl, _source: &Atom, - _export_name: Option<&str>, - _identifier_name: &str, + _export_name: Option<&Atom>, + _identifier_name: &Atom, ) -> Option { None } diff --git a/crates/rspack_plugin_javascript/src/utils/eval/eval_member_expr.rs b/crates/rspack_plugin_javascript/src/utils/eval/eval_member_expr.rs new file mode 100644 index 00000000000..a1606e7dd1a --- /dev/null +++ b/crates/rspack_plugin_javascript/src/utils/eval/eval_member_expr.rs @@ -0,0 +1,42 @@ +use rspack_core::SpanExt; +use swc_core::ecma::ast::MemberExpr; + +use super::BasicEvaluatedExpression; +use crate::parser_plugin::JavascriptParserPlugin; +use crate::visitors::{AllowedMemberTypes, JavascriptParser, MemberExpressionInfo}; + +pub fn eval_member_expression( + parser: &mut JavascriptParser, + member: &MemberExpr, +) -> Option { + let ret = if let Some(MemberExpressionInfo::Expression(info)) = + parser.get_member_expression_info(member, AllowedMemberTypes::Expression) + { + parser + .plugin_drive + .clone() + .evaluate_identifier( + parser, + &info.name, + member.span.real_lo(), + member.span.hi().0, + ) + .or_else(|| { + // TODO: fallback with `evaluateDefinedIdentifier` + let mut eval = + BasicEvaluatedExpression::with_range(member.span.real_lo(), member.span.hi().0); + eval.set_identifier( + info.name, + info.root_info, + Some(info.members), + Some(info.members_optionals), + Some(info.member_ranges), + ); + Some(eval) + }) + } else { + None + }; + parser.member_expr_in_optional_chain = false; + ret +} diff --git a/crates/rspack_plugin_javascript/src/utils/eval/mod.rs b/crates/rspack_plugin_javascript/src/utils/eval/mod.rs index 423e9ef2c15..094e9ecf52e 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/mod.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/mod.rs @@ -3,6 +3,7 @@ mod eval_binary_expr; mod eval_call_expr; mod eval_cond_expr; mod eval_lit_expr; +mod eval_member_expr; mod eval_new_expr; mod eval_source; mod eval_tpl_expr; @@ -11,12 +12,15 @@ mod eval_unary_expr; use bitflags::bitflags; use num_bigint::BigInt; use rspack_core::DependencyLocation; +use swc_core::atoms::Atom; +use swc_core::common::Span; pub use self::eval_array_expr::eval_array_expression; pub use self::eval_binary_expr::eval_binary_expression; pub use self::eval_call_expr::eval_call_expression; pub use self::eval_cond_expr::eval_cond_expression; pub use self::eval_lit_expr::{eval_lit_expr, eval_prop_name}; +pub use self::eval_member_expr::eval_member_expression; pub use self::eval_new_expr::eval_new_expression; pub use self::eval_source::eval_source; pub use self::eval_tpl_expr::{ @@ -68,6 +72,9 @@ pub struct BasicEvaluatedExpression { regexp: Option, identifier: Option, root_info: Option, + members: Option>, + members_optionals: Option>, + member_ranges: Option>, items: Option>, quasis: Option>, parts: Option>, @@ -102,6 +109,9 @@ impl BasicEvaluatedExpression { parts: None, identifier: None, root_info: None, + members: None, + members_optionals: None, + member_ranges: None, template_string_kind: None, options: None, string: None, @@ -380,10 +390,20 @@ impl BasicEvaluatedExpression { } } - pub fn set_identifier(&mut self, name: String, root_info: ExportedVariableInfo) { + pub fn set_identifier( + &mut self, + name: String, + root_info: ExportedVariableInfo, + members: Option>, + members_optionals: Option>, + member_ranges: Option>, + ) { self.ty = Ty::Identifier; self.identifier = Some(name); self.root_info = Some(root_info); + self.members = members; + self.members_optionals = members_optionals; + self.member_ranges = member_ranges; self.side_effects = true; } @@ -439,7 +459,7 @@ impl BasicEvaluatedExpression { self.string.as_ref().expect("make sure string exist") } - pub fn identifier(&self) -> &String { + pub fn identifier(&self) -> &str { assert!(self.is_identifier()); self .identifier @@ -452,6 +472,21 @@ impl BasicEvaluatedExpression { self.root_info.as_ref().expect("make sure identifier exist") } + pub fn members(&self) -> Option<&Vec> { + assert!(self.is_identifier()); + self.members.as_ref() + } + + pub fn members_optionals(&self) -> Option<&Vec> { + assert!(self.is_identifier()); + self.members_optionals.as_ref() + } + + pub fn member_ranges(&self) -> Option<&Vec> { + assert!(self.is_identifier()); + self.member_ranges.as_ref() + } + pub fn regexp(&self) -> &Regexp { self.regexp.as_ref().expect("make sure regexp exist") } @@ -531,7 +566,13 @@ pub fn evaluate_to_identifier( end: u32, ) -> BasicEvaluatedExpression { let mut eval = BasicEvaluatedExpression::with_range(start, end); - eval.set_identifier(identifier, ExportedVariableInfo::Name(root_info)); + eval.set_identifier( + identifier, + ExportedVariableInfo::Name(root_info), + None, + None, + None, + ); eval.set_side_effects(false); match truthy { Some(v) => { diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs index c02e434a0b3..30ac83b639e 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs @@ -17,7 +17,9 @@ use swc_core::ecma::atoms::Atom; pub use self::context_dependency_helper::create_context_dependency; pub use self::context_helper::{scanner_context_module, ContextModuleScanResult}; -pub use self::parser::{CallExpressionInfo, CallHooksName, ExportedVariableInfo, PathIgnoredSpans}; +pub use self::parser::{ + AllowedMemberTypes, CallExpressionInfo, CallHooksName, ExportedVariableInfo, PathIgnoredSpans, +}; pub use self::parser::{JavascriptParser, MemberExpressionInfo, TagInfoData, TopLevelScope}; pub use self::util::*; use crate::dependency::Specifier; diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/call_hooks_name.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/call_hooks_name.rs index 3f0b567c45e..9f964184eda 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/call_hooks_name.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/call_hooks_name.rs @@ -7,15 +7,15 @@ use crate::visitors::scope_info::{FreeName, VariableInfoId}; /// webpack use HookMap and filter at callHooksForName/callHooksForInfo /// we need to pass the name to hook to filter in the hook pub trait CallHooksName { - fn call_hooks_name(&self, parser: &mut JavascriptParser, hook_call: F) -> Option + fn call_hooks_name(&self, parser: &mut JavascriptParser, hook_call: F) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option; + F: Fn(&mut JavascriptParser, &str) -> Option; } impl CallHooksName for &str { - fn call_hooks_name(&self, parser: &mut JavascriptParser, hook_call: F) -> Option + fn call_hooks_name(&self, parser: &mut JavascriptParser, hook_call: F) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option, + F: Fn(&mut JavascriptParser, &str) -> Option, { if let Some(id) = parser .get_variable_info(self.as_ref()) @@ -31,31 +31,31 @@ impl CallHooksName for &str { } impl CallHooksName for String { - fn call_hooks_name<'parser, F>(&self, parser: &mut JavascriptParser, hook_call: F) -> Option + fn call_hooks_name<'parser, F, T>(&self, parser: &mut JavascriptParser, hook_call: F) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option, + F: Fn(&mut JavascriptParser, &str) -> Option, { self.as_str().call_hooks_name(parser, hook_call) } } impl CallHooksName for Atom { - fn call_hooks_name<'parser, F>(&self, parser: &mut JavascriptParser, hook_call: F) -> Option + fn call_hooks_name<'parser, F, T>(&self, parser: &mut JavascriptParser, hook_call: F) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option, + F: Fn(&mut JavascriptParser, &str) -> Option, { self.as_str().call_hooks_name(parser, hook_call) } } impl CallHooksName for ExportedVariableInfo { - fn call_hooks_name<'parser, F>( + fn call_hooks_name<'parser, F, T>( &self, parser: &mut JavascriptParser, hooks_call: F, - ) -> Option + ) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option, + F: Fn(&mut JavascriptParser, &str) -> Option, { match self { ExportedVariableInfo::Name(n) => n.call_hooks_name(parser, hooks_call), @@ -64,35 +64,39 @@ impl CallHooksName for ExportedVariableInfo { } } -fn call_hooks_info( +fn call_hooks_info( id: VariableInfoId, parser: &mut JavascriptParser, hook_call: F, -) -> Option +) -> Option where - F: Fn(&mut JavascriptParser, &str) -> Option, + F: Fn(&mut JavascriptParser, &str) -> Option, { - // avoid ownership - let mut for_name_list = Vec::with_capacity(32); let info = parser.definitions_db.expect_get_variable(&id); - let mut next_tag_info = info.tag_info.as_ref(); + let mut next_tag_info = info.tag_info; - while let Some(tag_info) = next_tag_info { - for_name_list.push(tag_info.tag.to_string()); - next_tag_info = tag_info.next.as_deref(); + while let Some(tag_info_id) = next_tag_info { + parser.current_tag_info = Some(tag_info_id); + let tag_info = parser.definitions_db.expect_get_tag_info(&tag_info_id); + let tag = tag_info.tag.to_string(); + let next = tag_info.next; + let result = hook_call(parser, &tag); + parser.current_tag_info = None; + if result.is_some() { + return result; + } + next_tag_info = next; } + let info = parser.definitions_db.expect_get_variable(&id); if let Some(FreeName::String(free_name)) = &info.free_name { - for_name_list.push(free_name.to_string()); - } - // should run `defined ? defined() : None` if `free_name` matched FreeName::Tree? - - for name in &for_name_list { - let result = hook_call(parser, name); + let result = hook_call(parser, &free_name.to_string()); if result.is_some() { return result; } } + // should run `defined ? defined() : None` if `free_name` matched FreeName::Tree? + None // maybe we can support `fallback` here } 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 3e5ce652a6e..54fe6abeaea 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs @@ -23,27 +23,29 @@ use swc_core::common::util::take::Take; use swc_core::common::{BytePos, SourceFile, Span, Spanned}; use swc_core::ecma::ast::{ ArrayPat, AssignPat, AssignTargetPat, CallExpr, Callee, MetaPropExpr, MetaPropKind, ObjectPat, - ObjectPatProp, Pat, Program, Stmt, ThisExpr, + ObjectPatProp, OptCall, OptChainBase, OptChainExpr, Pat, Program, Stmt, ThisExpr, }; use swc_core::ecma::ast::{Expr, Ident, Lit, MemberExpr, RestPat}; +use swc_core::ecma::utils::ExprFactory; use super::ExtraSpanInfo; use super::ImportMap; use crate::parser_plugin::{self, JavaScriptParserPluginDrive, JavascriptParserPlugin}; use crate::utils::eval::{self, BasicEvaluatedExpression}; use crate::visitors::scope_info::{ - FreeName, ScopeInfoDB, ScopeInfoId, TagInfo, VariableInfo, VariableInfoId, + FreeName, ScopeInfoDB, ScopeInfoId, TagInfo, TagInfoId, VariableInfo, VariableInfoId, }; -pub trait TagInfoData: Clone { - fn serialize(data: &Self) -> serde_json::Value; - fn deserialize(value: serde_json::Value) -> Self; +pub trait TagInfoData { + fn into_any(data: Self) -> Box; + fn downcast(any: Box) -> Self; } #[derive(Debug)] pub struct ExtractedMemberExpressionChainData { object: Expr, members: Vec, + members_optionals: Vec, member_ranges: Vec, } @@ -66,12 +68,18 @@ pub struct CallExpressionInfo { pub call: CallExpr, pub callee_name: String, pub root_info: ExportedVariableInfo, + pub members: Vec, + pub members_optionals: Vec, + pub member_ranges: Vec, } #[derive(Debug)] pub struct ExpressionExpressionInfo { pub name: String, pub root_info: ExportedVariableInfo, + pub members: Vec, + pub members_optionals: Vec, + pub member_ranges: Vec, } #[derive(Debug, Clone)] @@ -236,6 +244,7 @@ pub struct JavascriptParser<'parser> { pub(crate) enter_new_expr: bool, // TODO: delete `enter_callee` pub(crate) enter_callee: bool, + pub(crate) member_expr_in_optional_chain: bool, pub(crate) stmt_level: u32, pub(crate) last_stmt_is_expr_stmt: bool, // TODO: delete `properties_in_destructuring` @@ -246,6 +255,7 @@ pub struct JavascriptParser<'parser> { // TODO: Used as a way to skip AST visits in `InnerGraphPlugin`, // which does not follow ParserPlugin's AST skipping in const plugin. pub(crate) path_ignored_spans: &'parser mut PathIgnoredSpans, + pub(crate) current_tag_info: Option, // ===== scope info ======= pub(crate) in_try: bool, pub(crate) in_short_hand: bool, @@ -393,9 +403,11 @@ impl<'parser> JavascriptParser<'parser> { rewrite_usage_span, enter_new_expr: false, enter_callee: false, + member_expr_in_optional_chain: false, properties_in_destructuring: Default::default(), semicolons, statement_path: Default::default(), + current_tag_info: None, prev_statement: None, path_ignored_spans, } @@ -461,7 +473,7 @@ impl<'parser> JavascriptParser<'parser> { { return; } - let info = VariableInfo::new(definitions, None, None); + let info = VariableInfo::create(&mut self.definitions_db, definitions, None, None); self.definitions_db.set(definitions, name, info); } @@ -470,7 +482,12 @@ impl<'parser> JavascriptParser<'parser> { if name == variable { self.definitions_db.delete(id, &name); } else { - let variable = VariableInfo::new(id, Some(FreeName::String(variable)), None); + let variable = VariableInfo::create( + &mut self.definitions_db, + id, + Some(FreeName::String(variable)), + None, + ); self.definitions_db.set(id, name, variable); } } @@ -485,36 +502,45 @@ impl<'parser> JavascriptParser<'parser> { tag: &'static str, data: Option, ) { - let data = data.as_ref().map(|data| TagInfoData::serialize(data)); + let data = data.map(|data| TagInfoData::into_any(data)); let new_info = if let Some(old_info_id) = self.definitions_db.get(&self.definitions, &name) { let old_info = self.definitions_db.expect_get_variable(&old_info_id); - if let Some(old_tag_info) = &old_info.tag_info { + if let Some(old_tag_info) = old_info.tag_info { + let declared_scope = old_info.declared_scope; // FIXME: remove `.clone` let free_name = old_info.free_name.clone(); - let tag_info = Some(TagInfo { + let tag_info = Some(TagInfo::create( + &mut self.definitions_db, tag, data, - // FIXME: remove `.clone` - next: Some(Box::new(old_tag_info.clone())), - }); - VariableInfo::new(old_info.declared_scope, free_name, tag_info) + Some(old_tag_info), + )); + VariableInfo::create( + &mut self.definitions_db, + declared_scope, + free_name, + tag_info, + ) } else { + let declared_scope = old_info.declared_scope; let free_name = Some(FreeName::True); - let tag_info = Some(TagInfo { - tag, - data, - next: None, - }); - VariableInfo::new(old_info.declared_scope, free_name, tag_info) + let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None)); + VariableInfo::create( + &mut self.definitions_db, + declared_scope, + free_name, + tag_info, + ) } } else { let free_name = Some(FreeName::String(name.clone())); - let tag_info = Some(TagInfo { - tag, - data, - next: None, - }); - VariableInfo::new(self.definitions, free_name, tag_info) + let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None)); + VariableInfo::create( + &mut self.definitions_db, + self.definitions, + free_name, + tag_info, + ) }; self.definitions_db.set(self.definitions, name, new_info); } @@ -522,8 +548,9 @@ impl<'parser> JavascriptParser<'parser> { fn _get_member_expression_info( &mut self, object: Expr, - members: Vec, - _members_range: Vec, + mut members: Vec, + mut members_optionals: Vec, + mut member_ranges: Vec, allowed_types: AllowedMemberTypes, ) -> Option { match object { @@ -542,12 +569,18 @@ impl<'parser> JavascriptParser<'parser> { return None; }; let callee_name = object_and_members_to_name(resolved_root, &members); + members.reverse(); + members_optionals.reverse(); + member_ranges.reverse(); Some(MemberExpressionInfo::Call(CallExpressionInfo { call: expr, callee_name, root_info: root_info .map(|i| ExportedVariableInfo::VariableInfo(i.id())) .unwrap_or_else(|| ExportedVariableInfo::Name(root_name.to_string())), + members, + members_optionals, + member_ranges, })) } Expr::MetaProp(_) | Expr::Ident(_) | Expr::This(_) => { @@ -565,11 +598,17 @@ impl<'parser> JavascriptParser<'parser> { return None; }; let name = object_and_members_to_name(resolved_root, &members); + members.reverse(); + members_optionals.reverse(); + member_ranges.reverse(); Some(MemberExpressionInfo::Expression(ExpressionExpressionInfo { name, root_info: root_info .map(|i| ExportedVariableInfo::VariableInfo(i.id())) .unwrap_or_else(|| ExportedVariableInfo::Name(root_name.to_string())), + members, + members_optionals, + member_ranges, })) } _ => None, @@ -584,10 +623,12 @@ impl<'parser> JavascriptParser<'parser> { expr .as_member() .and_then(|member| self.get_member_expression_info(member, allowed_types)) - .or_else(|| self._get_member_expression_info(expr.clone(), vec![], vec![], allowed_types)) + .or_else(|| { + self._get_member_expression_info(expr.clone(), vec![], vec![], vec![], allowed_types) + }) } - fn get_member_expression_info( + pub fn get_member_expression_info( &mut self, expr: &MemberExpr, allowed_types: AllowedMemberTypes, @@ -595,42 +636,69 @@ impl<'parser> JavascriptParser<'parser> { let ExtractedMemberExpressionChainData { object, members, + members_optionals, + member_ranges, + } = self.extract_member_expression_chain(expr); + self._get_member_expression_info( + object, + members, + members_optionals, member_ranges, - } = Self::extract_member_expression_chain(expr); - self._get_member_expression_info(object, members, member_ranges, allowed_types) + allowed_types, + ) } - fn extract_member_expression_chain(expr: &MemberExpr) -> ExtractedMemberExpressionChainData { + fn extract_member_expression_chain( + &self, + expr: &MemberExpr, + ) -> ExtractedMemberExpressionChainData { let mut object = Expr::Member(expr.clone()); let mut members = Vec::new(); + let mut members_optionals = Vec::new(); let mut member_ranges = Vec::new(); - while let Some(expr) = object.as_mut_member() { - if let Some(computed) = expr.prop.as_computed() { - let Expr::Lit(lit) = &*computed.expr else { - break; - }; - let value = match lit { - Lit::Str(s) => s.value.clone(), - Lit::Bool(b) => if b.value { "true" } else { "false" }.into(), - Lit::Null(_) => "null".into(), - Lit::Num(n) => n.value.to_string().into(), - Lit::BigInt(i) => i.value.to_string().into(), - Lit::Regex(r) => r.exp.clone(), - Lit::JSXText(_) => unreachable!(), - }; - members.push(value); - member_ranges.push(expr.obj.span()); - } else if let Some(ident) = expr.prop.as_ident() { - members.push(ident.sym.clone()); - member_ranges.push(expr.obj.span()); - } else { - break; + let mut in_optional_chain = self.member_expr_in_optional_chain; + loop { + match &mut object { + Expr::Member(expr) => { + if let Some(computed) = expr.prop.as_computed() { + let Expr::Lit(lit) = &*computed.expr else { + break; + }; + let value = match lit { + Lit::Str(s) => s.value.clone(), + Lit::Bool(b) => if b.value { "true" } else { "false" }.into(), + Lit::Null(_) => "null".into(), + Lit::Num(n) => n.value.to_string().into(), + Lit::BigInt(i) => i.value.to_string().into(), + Lit::Regex(r) => r.exp.clone(), + Lit::JSXText(_) => unreachable!(), + }; + members.push(value); + member_ranges.push(expr.obj.span()); + } else if let Some(ident) = expr.prop.as_ident() { + members.push(ident.sym.clone()); + member_ranges.push(expr.obj.span()); + } else { + break; + } + members_optionals.push(in_optional_chain); + object = *expr.obj.take(); + } + Expr::OptChain(expr) => { + in_optional_chain = expr.optional; + if let OptChainBase::Member(member) = &mut *expr.base { + object = Expr::Member(member.take()); + } else { + break; + } + } + _ => break, } - object = *expr.obj.take(); } ExtractedMemberExpressionChainData { object, members, + members_optionals, member_ranges, } } @@ -717,6 +785,28 @@ impl<'parser> JavascriptParser<'parser> { } } + fn enter_optional_chain(&mut self, expr: &OptChainExpr, on_call: C, on_member: M) -> R + where + C: FnOnce(&mut Self, &OptCall) -> R, + M: FnOnce(&mut Self, &MemberExpr) -> R, + { + let member_expr_in_optional_chain = self.member_expr_in_optional_chain; + let ret = match &*expr.base { + OptChainBase::Call(call) => { + if call.callee.is_member() { + self.member_expr_in_optional_chain = expr.optional; + } + on_call(self, call) + } + OptChainBase::Member(member) => { + self.member_expr_in_optional_chain = expr.optional; + on_member(self, member) + } + }; + self.member_expr_in_optional_chain = member_expr_in_optional_chain; + ret + } + pub fn walk_program(&mut self, ast: &Program) { if self.plugin_drive.clone().program(self, ast).is_none() { match ast { @@ -800,58 +890,65 @@ impl<'parser> JavascriptParser<'parser> { Expr::New(new) => eval::eval_new_expression(self, new), Expr::Call(call) => eval::eval_call_expression(self, call), Expr::Paren(paren) => self.evaluating(&paren.expr), - Expr::Member(member) => { - if let Some(MemberExpressionInfo::Expression(info)) = - self.get_member_expression_info(member, AllowedMemberTypes::Expression) - { - self - .plugin_drive - .clone() - .evaluate_identifier(self, &info.name, member.span.real_lo(), member.span.hi().0) - .or_else(|| { - // TODO: fallback with `evaluateDefinedIdentifier` - let mut eval = - BasicEvaluatedExpression::with_range(member.span.real_lo(), member.span.hi().0); - eval.set_identifier(info.name, info.root_info); - Some(eval) - }) - } else { - None - } - } + Expr::OptChain(opt_chain) => self.enter_optional_chain( + opt_chain, + |parser, call| { + eval::eval_call_expression( + parser, + &CallExpr { + span: call.span, + callee: call.callee.clone().as_callee(), + args: call.args.clone(), + type_args: None, + }, + ) + }, + |parser, member| eval::eval_member_expression(parser, member), + ), + Expr::Member(member) => eval::eval_member_expression(self, member), Expr::Ident(ident) => { + let name = &ident.sym; + if name.eq("undefined") { + let mut eval = + BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.hi.0); + eval.set_undefined(); + return Some(eval); + } let drive = self.plugin_drive.clone(); - let Some(info) = self.get_variable_info(&ident.sym) else { - // use `ident.sym` as fallback for global variable(or maybe just a undefined variable) - return drive - .evaluate_identifier( - self, - ident.sym.as_str(), - ident.span.real_lo(), - ident.span.hi.0, - ) - .or_else(|| { - let mut eval = - BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.hi.0); - - if ident.sym.eq("undefined") { - eval.set_undefined(); - } else { + name + .call_hooks_name(self, |parser, name| { + drive.evaluate_identifier(parser, name, ident.span.real_lo(), ident.span.hi.0) + }) + .or_else(|| { + let info = self.get_variable_info(name); + if let Some(info) = info { + if let Some(FreeName::String(_)) = &info.free_name { + let mut eval = + BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.hi.0); eval.set_identifier( ident.sym.to_string(), - ExportedVariableInfo::Name(ident.sym.to_string()), + ExportedVariableInfo::VariableInfo(info.id()), + None, + None, + None, ); + Some(eval) + } else { + None } - + } else { + let mut eval = + BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.hi.0); + eval.set_identifier( + ident.sym.to_string(), + ExportedVariableInfo::Name(name.to_string()), + None, + None, + None, + ); Some(eval) - }); - }; - if let Some(FreeName::String(name)) = info.free_name.as_ref() { - // avoid ownership - let name = name.to_string(); - return drive.evaluate_identifier(self, &name, ident.span.real_lo(), ident.span.hi.0); - } - None + } + }) } Expr::This(this) => { let drive = self.plugin_drive.clone(); @@ -860,6 +957,9 @@ impl<'parser> JavascriptParser<'parser> { eval.set_identifier( "this".to_string(), ExportedVariableInfo::Name("this".to_string()), + None, + None, + None, ); Some(eval) }; diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs index d809ca8d69e..ab6917640a9 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs @@ -11,7 +11,7 @@ use swc_core::ecma::ast::{DoWhileStmt, ExportDecl, ExportDefaultDecl, ExportDefa use swc_core::ecma::ast::{ExprOrSpread, ExprStmt, FnDecl, MemberExpr, MemberProp, VarDeclOrExpr}; use swc_core::ecma::ast::{FnExpr, ForHead, Function, Ident, KeyValueProp}; use swc_core::ecma::ast::{ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, WithStmt}; -use swc_core::ecma::ast::{MetaPropExpr, NamedExport, NewExpr, ObjectLit, OptCall, OptChainBase}; +use swc_core::ecma::ast::{MetaPropExpr, NamedExport, NewExpr, ObjectLit, OptCall}; use swc_core::ecma::ast::{ModuleDecl, ModuleItem, ObjectPat, ObjectPatProp, Stmt, WhileStmt}; use swc_core::ecma::ast::{OptChainExpr, Pat, ThisExpr, UnaryOp}; use swc_core::ecma::ast::{Prop, PropName, PropOrSpread, RestPat, ReturnStmt, SeqExpr, TaggedTpl}; @@ -668,10 +668,11 @@ impl<'parser> JavascriptParser<'parser> { .optional_chaining(self, expr) .is_none() { - match &*expr.base { - OptChainBase::Call(call) => self.walk_opt_call(call), - OptChainBase::Member(member) => self.walk_member_expression(member), - }; + self.enter_optional_chain( + expr, + |parser, call| parser.walk_opt_call(call), + |parser, member| parser.walk_member_expression(member), + ); } } @@ -688,7 +689,22 @@ impl<'parser> JavascriptParser<'parser> { { return; } - // TODO: member_chain + if expr_info + .root_info + .call_hooks_name(self, |this, for_name| { + drive.member_chain( + this, + expr, + for_name, + &expr_info.members, + &expr_info.members_optionals, + &expr_info.member_ranges, + ) + }) + .unwrap_or_default() + { + return; + } self.walk_member_expression_with_expression_name( expr, &expr_info.name, @@ -716,6 +732,7 @@ impl<'parser> JavascriptParser<'parser> { } } } + self.member_expr_in_optional_chain = false; self.walk_expression(&expr.obj); if let MemberProp::Computed(computed) = &expr.prop { self.walk_expression(&computed.expr) @@ -757,10 +774,13 @@ impl<'parser> JavascriptParser<'parser> { } } - fn walk_opt_call(&mut self, call: &OptCall) { - // TODO: should align to walkCallExpression in webpack. - self.walk_expression(&call.callee); - self.walk_expr_or_spread(&call.args); + fn walk_opt_call(&mut self, expr: &OptCall) { + self.walk_call_expression(&CallExpr { + span: expr.span, + callee: Callee::Expr(expr.callee.clone()), + args: expr.args.clone(), + type_args: None, + }) } /// Walk IIFE function @@ -924,15 +944,31 @@ impl<'parser> JavascriptParser<'parser> { } let evaluated_callee = self.evaluate_expression(callee); if evaluated_callee.is_identifier() { + let members = evaluated_callee + .members() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(Vec::new())); + let members_optionals = evaluated_callee + .members_optionals() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(members.iter().map(|_| false).collect::>())); + let member_ranges = evaluated_callee + .member_ranges() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(Vec::new())); let drive = self.plugin_drive.clone(); - if drive - .call_member_chain( - self, - evaluated_callee.root_info(), - expr, - // evaluated_callee.get_members(), - // evaluated_callee.identifier(), - ) + if evaluated_callee + .root_info() + .call_hooks_name(self, |parser, for_name| { + drive.call_member_chain( + parser, + expr, + for_name, + &members, + &members_optionals, + &member_ranges, + ) + }) .unwrap_or_default() { /* result1 */ @@ -1360,7 +1396,6 @@ impl<'parser> JavascriptParser<'parser> { for param in &method.function.params { this.walk_pattern(¶m.pat); } - // TODO: `hooks.body_value`; if let Some(body) = &method.function.body { this.walk_block_statement(body); @@ -1370,13 +1405,16 @@ impl<'parser> JavascriptParser<'parser> { this.top_level_scope = was_top_level; } ClassMember::PrivateMethod(method) => { - this.walk_identifier(&method.key.id); + // method.key is always not computed in private method, so we don't need to walk it let was_top_level = this.top_level_scope; this.top_level_scope = TopLevelScope::False; this.in_function_scope( true, method.function.params.iter().map(|p| Cow::Borrowed(&p.pat)), |this| { + for param in &method.function.params { + this.walk_pattern(¶m.pat); + } // TODO: `hooks.body_value`; if let Some(body) = &method.function.body { this.walk_block_statement(body); diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk_block_pre.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk_block_pre.rs index 24d72f348ea..d302148f603 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk_block_pre.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk_block_pre.rs @@ -143,29 +143,33 @@ impl<'parser> JavascriptParser<'parser> { for specifier in &decl.specifiers { match specifier { ImportSpecifier::Named(named) => { - let identifier_name = named.local.sym.as_str(); - let export_name = named.imported.as_ref().map(|imported| match imported { - ModuleExportName::Ident(ident) => ident.sym.as_str(), - ModuleExportName::Str(s) => s.value.as_str(), - }); + let identifier_name = &named.local.sym; + let export_name = named + .imported + .as_ref() + .map(|imported| match imported { + ModuleExportName::Ident(ident) => &ident.sym, + ModuleExportName::Str(s) => &s.value, + }) + .unwrap_or_else(|| &named.local.sym); if drive - .import_specifier(self, decl, source, export_name, identifier_name) + .import_specifier(self, decl, source, Some(export_name), identifier_name) .unwrap_or_default() { self.define_variable(identifier_name.to_string()) } } ImportSpecifier::Default(default) => { - let identifier_name = default.local.sym.as_str(); + let identifier_name = &default.local.sym; if drive - .import_specifier(self, decl, source, Some("default"), identifier_name) + .import_specifier(self, decl, source, Some(&"default".into()), identifier_name) .unwrap_or_default() { self.define_variable(identifier_name.to_string()) } } ImportSpecifier::Namespace(namespace) => { - let identifier_name = namespace.local.sym.as_str(); + let identifier_name = &namespace.local.sym; if drive .import_specifier(self, decl, source, None, identifier_name) .unwrap_or_default() diff --git a/crates/rspack_plugin_javascript/src/visitors/scope_info.rs b/crates/rspack_plugin_javascript/src/visitors/scope_info.rs index 0095d7b7418..dc7418d01a3 100644 --- a/crates/rspack_plugin_javascript/src/visitors/scope_info.rs +++ b/crates/rspack_plugin_javascript/src/visitors/scope_info.rs @@ -22,6 +22,15 @@ impl VariableInfoId { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TagInfoId(u32); + +impl TagInfoId { + fn init() -> TagInfoId { + TagInfoId(0) + } +} + #[derive(Debug)] pub struct VariableInfoDB { count: VariableInfoId, @@ -41,14 +50,27 @@ impl VariableInfoDB { map: Default::default(), } } +} - fn insert(&mut self, mut variable_info: VariableInfo) -> VariableInfoId { - let id = self.next(); - variable_info.set_id(id); - let prev = self.map.insert(id, variable_info); - assert!(prev.is_none()); +#[derive(Debug)] +pub struct TagInfoDB { + count: TagInfoId, + map: FxHashMap, +} + +impl TagInfoDB { + fn next(&mut self) -> TagInfoId { + let id = self.count; + self.count.0 += 1; id } + + fn new() -> Self { + Self { + count: TagInfoId::init(), + map: Default::default(), + } + } } #[derive(Debug)] @@ -56,6 +78,7 @@ pub struct ScopeInfoDB { count: ScopeInfoId, map: FxHashMap, variable_info_db: VariableInfoDB, + tag_info_db: TagInfoDB, } impl Default for ScopeInfoDB { @@ -76,6 +99,7 @@ impl ScopeInfoDB { count: ScopeInfoId::init(), map: Default::default(), variable_info_db: VariableInfoDB::new(), + tag_info_db: TagInfoDB::new(), } } @@ -141,6 +165,22 @@ impl ScopeInfoDB { .unwrap_or_else(|| panic!("{id:#?} should exist")) } + pub fn expect_get_tag_info(&self, id: &TagInfoId) -> &TagInfo { + self + .tag_info_db + .map + .get(id) + .unwrap_or_else(|| panic!("{id:#?} should exist")) + } + + pub fn expect_get_mut_tag_info(&mut self, id: &TagInfoId) -> &mut TagInfo { + self + .tag_info_db + .map + .get_mut(id) + .unwrap_or_else(|| panic!("{id:#?} should exist")) + } + pub fn get>(&mut self, id: &ScopeInfoId, key: S) -> Option { let definitions = self.expect_get_scope(id); if let Some(&top_value) = definitions.map.get(key.as_ref()) { @@ -171,8 +211,7 @@ impl ScopeInfoDB { } } - pub fn set(&mut self, id: ScopeInfoId, key: String, info: VariableInfo) { - let variable_info_id = self.variable_info_db.insert(info); + pub fn set(&mut self, id: ScopeInfoId, key: String, variable_info_id: VariableInfoId) { let scope = self.expect_get_mut_scope(&id); scope.map.insert(key, variable_info_id); } @@ -189,11 +228,36 @@ impl ScopeInfoDB { } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug)] pub struct TagInfo { + id: TagInfoId, pub tag: &'static str, - pub data: Option, - pub next: Option>, + pub data: Option>, + pub next: Option, +} + +impl TagInfo { + pub fn create( + definitions_db: &mut ScopeInfoDB, + tag: &'static str, + data: Option>, + next: Option, + ) -> TagInfoId { + let id = definitions_db.tag_info_db.next(); + let tag_info = TagInfo { + id, + tag, + data, + next, + }; + let prev = definitions_db.tag_info_db.map.insert(id, tag_info); + assert!(prev.is_none()); + id + } + + pub fn id(&self) -> TagInfoId { + self.id + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -204,37 +268,39 @@ pub enum FreeName { #[derive(Debug, PartialEq, Eq)] pub struct VariableInfo { - id: Option, + id: VariableInfoId, pub declared_scope: ScopeInfoId, pub free_name: Option, - pub tag_info: Option, + pub tag_info: Option, } impl VariableInfo { const TOMBSTONE: VariableInfoId = VariableInfoId(0); const UNDEFINED: VariableInfoId = VariableInfoId(1); - pub fn new( + pub fn create( + definitions_db: &mut ScopeInfoDB, declared_scope: ScopeInfoId, free_name: Option, - tag_info: Option, - ) -> Self { - Self { - id: None, + tag_info: Option, + ) -> VariableInfoId { + let id = definitions_db.variable_info_db.next(); + let variable_info = VariableInfo { + id, declared_scope, free_name, tag_info, - } - } - - fn set_id(&mut self, id: VariableInfoId) { - self.id = Some(id); + }; + let prev = definitions_db + .variable_info_db + .map + .insert(id, variable_info); + assert!(prev.is_none()); + id } pub fn id(&self) -> VariableInfoId { - self - .id - .expect("should already store VariableInfo to VariableInfoDB") + self.id } } diff --git a/packages/rspack-test-tools/src/case/diff.ts b/packages/rspack-test-tools/src/case/diff.ts index 735457bd1b7..92dee27ed1f 100644 --- a/packages/rspack-test-tools/src/case/diff.ts +++ b/packages/rspack-test-tools/src/case/diff.ts @@ -154,7 +154,7 @@ function checkCompareResults( .map(i => i.name) ).toEqual([]); }); - it("should not have any respack-only module", () => { + it("should not have any rspack-only module", () => { expect( getResults() .filter(i => i.type === ECompareResultType.OnlySource) diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/rspack.config.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/rspack.config.js new file mode 100644 index 00000000000..ff3b5ce500f --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/rspack.config.js @@ -0,0 +1,52 @@ +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + entry: { + a: "./src/a", + b: "./src/b", + c1: "./src/c", + c2: "./src/c", + ax: "./src/ax", + bx: "./src/bx", + cx1: "./src/cx", + cx2: "./src/cx", + d1: "./src/d1", + d2: "./src/d2" + }, + target: "web", + mode: "production", + devtool: false, + output: { + filename: "[name].js", + library: { type: "commonjs-module" } + }, + optimization: { + minimize: false, + moduleIds: "named", + chunkIds: "named", + providedExports: true, + usedExports: true, + concatenateModules: false, + innerGraph: false, + splitChunks: { + cacheGroups: { + forceMerge: { + test: /shared/, + enforce: true, + name: "shared", + chunks: "all" + } + } + } + }, + module: { + rules: [ + { + test: /dep/, + sideEffects: false + } + ] + }, + experiments: { + topLevelAwait: true + } +}; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/a.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/a.js new file mode 100644 index 00000000000..91e639dac9a --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/a.js @@ -0,0 +1,6 @@ +import { val, val2b } from "./shared"; + +it("should have the correct value", () => { + expect(val).toBe(84); + expect(val2b).toBe(42); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/ax.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/ax.js new file mode 100644 index 00000000000..3738eadfb67 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/ax.js @@ -0,0 +1,6 @@ +import { val, val2b } from "./concatenated-shared"; + +it("should have the correct value", () => { + expect(val).toBe(84); + expect(val2b).toBe(42); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/b.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/b.js new file mode 100644 index 00000000000..def6b6eef2b --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/b.js @@ -0,0 +1,8 @@ +import { other, val2c, Test } from "./shared"; + +it("should have the correct value", () => { + expect(other).toBe("other"); + expect(val2c).toBe(42); + expect(Test).toBeTypeOf("function"); + expect(new Test()).toBeInstanceOf(Test); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/bx.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/bx.js new file mode 100644 index 00000000000..1d24b378858 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/bx.js @@ -0,0 +1,8 @@ +import { other, val2c, Test } from "./concatenated-shared"; + +it("should have the correct value", () => { + expect(other).toBe("other"); + expect(val2c).toBe(42); + expect(Test).toBeTypeOf("function"); + expect(new Test()).toBeInstanceOf(Test); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/c.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/c.js new file mode 100644 index 00000000000..eb652f42cb3 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/c.js @@ -0,0 +1,5 @@ +import { other } from "./shared"; + +it("should have the correct value", () => { + expect(other).toBe("other"); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/concatenated-shared.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/concatenated-shared.js new file mode 100644 index 00000000000..42feac2d697 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/concatenated-shared.js @@ -0,0 +1 @@ +export * from "./shared?1"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/cx.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/cx.js new file mode 100644 index 00000000000..55048979a17 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/cx.js @@ -0,0 +1,5 @@ +import { other } from "./concatenated-shared"; + +it("should have the correct value", () => { + expect(other).toBe("other"); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d1.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d1.js new file mode 100644 index 00000000000..1da3d9db3f8 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d1.js @@ -0,0 +1,6 @@ +import { value2, value3 } from "./shared2"; + +it("should have the correct value", () => { + expect(value2).toBe(42); + expect(value3).toBe(42); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d2.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d2.js new file mode 100644 index 00000000000..b32578cb79f --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/d2.js @@ -0,0 +1,6 @@ +import { other2, value3 } from "./shared2"; + +it("should have the correct value", () => { + expect(other2).toBe("other"); + expect(value3).toBe(42); +}); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared3.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared3.js new file mode 100644 index 00000000000..272819e9b2b --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared3.js @@ -0,0 +1,4 @@ +import { setOther2 } from "./shared2"; + +export default 42; +setOther2("wrong"); diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared4.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared4.js new file mode 100644 index 00000000000..7a4e8a723a4 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep-shared4.js @@ -0,0 +1 @@ +export default 42; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep.js new file mode 100644 index 00000000000..7a4e8a723a4 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep.js @@ -0,0 +1 @@ +export default 42; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep2.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep2.js new file mode 100644 index 00000000000..888cae37af9 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/dep2.js @@ -0,0 +1 @@ +module.exports = 42; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared.js new file mode 100644 index 00000000000..b621dff945c --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared.js @@ -0,0 +1,16 @@ +import value from "./dep"; +import value2 from "./dep2"; +import * as dep2 from "./dep2"; +import Super from "./super"; + +const derived = value; + +export const val = /*#__PURE__*/ (() => value + derived)(); + +export const val2a = value2; +export const val2b = value2; +export const val2c = value2; + +export const other = "other"; + +export class Test extends Super {} diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared2.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared2.js new file mode 100644 index 00000000000..dc98ce98132 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/shared2.js @@ -0,0 +1,12 @@ +import value from "./dep-shared3"; +import value4 from "./dep-shared4"; + +export function setOther2(value) { + other2 = value; +} + +export const value2 = value; +export const value3 = value4; +export var other2; + +if (other2 === undefined) other2 = "other"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/super.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/super.js new file mode 100644 index 00000000000..d470a77c21a --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/src/super.js @@ -0,0 +1 @@ +export default class Super {} diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/test.config.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/test.config.js new file mode 100644 index 00000000000..b8837247301 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/test.config.js @@ -0,0 +1,18 @@ +/** @type {import("../../..").TDiffCaseConfig} */ +module.exports = { + modules: true, + runtimeModules: false, + files: [ + "shared.js", + "a.js", + "b.js", + "c1.js", + "c2.js", + "ax.js", + "bx.js", + "cx1.js", + "cx2.js", + "d1.js", + "d2.js" + ] +}; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/webpack.config.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/webpack.config.js new file mode 100644 index 00000000000..6aea61dd9df --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-interop/runtime-condition-no-inner-graph/webpack.config.js @@ -0,0 +1,52 @@ +/** @type {import("webpack").Configuration} */ +module.exports = { + entry: { + a: "./src/a", + b: "./src/b", + c1: "./src/c", + c2: "./src/c", + ax: "./src/ax", + bx: "./src/bx", + cx1: "./src/cx", + cx2: "./src/cx", + d1: "./src/d1", + d2: "./src/d2" + }, + target: "web", + mode: "production", + devtool: false, + output: { + filename: "[name].js", + library: { type: "commonjs-module" } + }, + optimization: { + minimize: false, + moduleIds: "named", + chunkIds: "named", + providedExports: true, + usedExports: true, + concatenateModules: false, + innerGraph: false, + splitChunks: { + cacheGroups: { + forceMerge: { + test: /shared/, + enforce: true, + name: "shared", + chunks: "all" + } + } + } + }, + module: { + rules: [ + { + test: /dep/, + sideEffects: false + } + ] + }, + experiments: { + topLevelAwait: true + } +}; diff --git a/webpack-test/configCases/graph/issue-11856.2/test.filter.js b/webpack-test/configCases/graph/issue-11856.2/test.filter.js deleted file mode 100644 index 3be456dcd23..00000000000 --- a/webpack-test/configCases/graph/issue-11856.2/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return false} \ No newline at end of file diff --git a/webpack-test/configCases/graph/issue-11856/test.filter.js b/webpack-test/configCases/graph/issue-11856/test.filter.js deleted file mode 100644 index 3be456dcd23..00000000000 --- a/webpack-test/configCases/graph/issue-11856/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return false} \ No newline at end of file diff --git a/webpack-test/configCases/graph/issue-11863/test.filter.js b/webpack-test/configCases/graph/issue-11863/test.filter.js deleted file mode 100644 index 3be456dcd23..00000000000 --- a/webpack-test/configCases/graph/issue-11863/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return false} \ No newline at end of file