diff --git a/crates/rspack_plugin_javascript/src/dependency/amd/amd_define_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/amd/amd_define_dependency.rs index 6e5caf0b5b2d..a2d667cfd8e8 100644 --- a/crates/rspack_plugin_javascript/src/dependency/amd/amd_define_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/amd/amd_define_dependency.rs @@ -164,7 +164,7 @@ impl Branch { #[cacheable] #[derive(Debug, Clone)] -pub struct AmdDefineDependency { +pub struct AMDDefineDependency { id: DependencyId, range: (u32, u32), array_range: Option<(u32, u32)>, @@ -175,7 +175,7 @@ pub struct AmdDefineDependency { local_module: Option, } -impl AmdDefineDependency { +impl AMDDefineDependency { pub fn new( range: (u32, u32), array_range: Option<(u32, u32)>, @@ -201,7 +201,7 @@ impl AmdDefineDependency { } #[cacheable_dyn] -impl Dependency for AmdDefineDependency { +impl Dependency for AMDDefineDependency { fn id(&self) -> &DependencyId { &self.id } @@ -219,7 +219,7 @@ impl Dependency for AmdDefineDependency { } } -impl AmdDefineDependency { +impl AMDDefineDependency { fn local_module_var(&self) -> Option { self.local_module.as_ref().and_then(|m| { if m.is_used() { @@ -249,7 +249,7 @@ impl AmdDefineDependency { } #[cacheable_dyn] -impl DependencyTemplate for AmdDefineDependency { +impl DependencyTemplate for AMDDefineDependency { fn apply( &self, source: &mut TemplateReplaceSource, @@ -305,6 +305,6 @@ impl DependencyTemplate for AmdDefineDependency { } } -impl AsModuleDependency for AmdDefineDependency {} +impl AsModuleDependency for AMDDefineDependency {} -impl AsContextDependency for AmdDefineDependency {} +impl AsContextDependency for AMDDefineDependency {} diff --git a/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_array_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_array_dependency.rs index 495d91f40a42..fd163f9bc60d 100644 --- a/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_array_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_array_dependency.rs @@ -1,35 +1,33 @@ +use std::borrow::Cow; + use itertools::Itertools; -use rspack_cacheable::{cacheable, cacheable_dyn, with::AsPreset}; +use rspack_cacheable::{cacheable, cacheable_dyn}; use rspack_core::{ module_raw, AffectType, AsContextDependency, AsModuleDependency, Compilation, Dependency, DependencyCategory, DependencyId, DependencyTemplate, DependencyType, ModuleDependency, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; -use rspack_util::atom::Atom; -use super::{ - amd_require_item_dependency::AMDRequireItemDependency, - local_module_dependency::LocalModuleDependency, -}; +use super::amd_require_item_dependency::AMDRequireItemDependency; #[cacheable] #[derive(Debug, Clone)] -pub enum AmdDep { - String(#[cacheable(with=AsPreset)] Atom), - LocalModuleDependency(LocalModuleDependency), - AMDRequireItemDependency(AMDRequireItemDependency), +pub enum AMDRequireArrayItem { + String(String), + LocalModuleDependency { local_module_variable_name: String }, + AMDRequireItemDependency { dep_id: DependencyId }, } #[cacheable] #[derive(Debug, Clone)] -pub struct AmdRequireArrayDependency { +pub struct AMDRequireArrayDependency { id: DependencyId, - deps_array: Vec, + deps_array: Vec, range: (u32, u32), } -impl AmdRequireArrayDependency { - pub fn new(deps_array: Vec, range: (u32, u32)) -> Self { +impl AMDRequireArrayDependency { + pub fn new(deps_array: Vec, range: (u32, u32)) -> Self { Self { id: DependencyId::new(), deps_array, @@ -39,7 +37,7 @@ impl AmdRequireArrayDependency { } #[cacheable_dyn] -impl Dependency for AmdRequireArrayDependency { +impl Dependency for AMDRequireArrayDependency { fn id(&self) -> &DependencyId { &self.id } @@ -57,7 +55,7 @@ impl Dependency for AmdRequireArrayDependency { } } -impl AmdRequireArrayDependency { +impl AMDRequireArrayDependency { fn get_content(&self, code_generatable_context: &mut TemplateContext) -> String { format!( "[{}]", @@ -69,26 +67,36 @@ impl AmdRequireArrayDependency { ) } - fn content_for_dependency( - dep: &AmdDep, + fn content_for_dependency<'a>( + dep: &'a AMDRequireArrayItem, code_generatable_context: &mut TemplateContext, - ) -> String { + ) -> Cow<'a, str> { match dep { - AmdDep::String(name) => name.to_string(), - AmdDep::LocalModuleDependency(dep) => dep.get_variable_name(), - AmdDep::AMDRequireItemDependency(dep) => module_raw( - code_generatable_context.compilation, - code_generatable_context.runtime_requirements, - dep.id(), - dep.request(), - dep.weak(), - ), + AMDRequireArrayItem::String(name) => name.into(), + AMDRequireArrayItem::LocalModuleDependency { + local_module_variable_name, + } => local_module_variable_name.into(), + AMDRequireArrayItem::AMDRequireItemDependency { dep_id } => { + let mg = code_generatable_context.compilation.get_module_graph(); + let dep = mg + .dependency_by_id(dep_id) + .and_then(|dep| dep.downcast_ref::()) + .expect("should have AMDRequireItemDependency"); + module_raw( + code_generatable_context.compilation, + code_generatable_context.runtime_requirements, + dep_id, + dep.request(), + dep.weak(), + ) + .into() + } } } } #[cacheable_dyn] -impl DependencyTemplate for AmdRequireArrayDependency { +impl DependencyTemplate for AMDRequireArrayDependency { fn apply( &self, source: &mut TemplateReplaceSource, @@ -111,6 +119,6 @@ impl DependencyTemplate for AmdRequireArrayDependency { } } -impl AsModuleDependency for AmdRequireArrayDependency {} +impl AsModuleDependency for AMDRequireArrayDependency {} -impl AsContextDependency for AmdRequireArrayDependency {} +impl AsContextDependency for AMDRequireArrayDependency {} diff --git a/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_item_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_item_dependency.rs index 4db07f584d77..030ad7d535f3 100644 --- a/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_item_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/amd/amd_require_item_dependency.rs @@ -12,12 +12,12 @@ pub struct AMDRequireItemDependency { id: DependencyId, #[cacheable(with=AsPreset)] request: Atom, - range: (u32, u32), + range: Option<(u32, u32)>, optional: bool, } impl AMDRequireItemDependency { - pub fn new(request: Atom, range: (u32, u32)) -> Self { + pub fn new(request: Atom, range: Option<(u32, u32)>) -> Self { Self { id: DependencyId::new(), request, @@ -57,6 +57,9 @@ impl DependencyTemplate for AMDRequireItemDependency { source: &mut TemplateReplaceSource, code_generatable_context: &mut TemplateContext, ) { + let Some(range) = &self.range else { + return; + }; // ModuleDependencyTemplateAsRequireId let content = module_raw( code_generatable_context.compilation, @@ -65,7 +68,7 @@ impl DependencyTemplate for AMDRequireItemDependency { &self.request, self.weak(), ); - source.replace(self.range.0, self.range.1, &content, None); + source.replace(range.0, range.1, &content, None); } fn dependency_id(&self) -> Option { diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_define_dependency_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_define_dependency_parser_plugin.rs index 6dc1597c8a20..e9db2c8913fc 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_define_dependency_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_define_dependency_parser_plugin.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use rspack_core::{ - BuildMetaDefaultObject, BuildMetaExportsType, ConstDependency, RuntimeGlobals, SpanExt, + BuildMetaDefaultObject, BuildMetaExportsType, ConstDependency, Dependency, RuntimeGlobals, + SpanExt, }; use rspack_util::atom::Atom; use rustc_hash::FxHashMap; @@ -15,7 +16,8 @@ use swc_core::{ use crate::{ dependency::{ - amd_define_dependency::AmdDefineDependency, + amd_define_dependency::AMDDefineDependency, + amd_require_array_dependency::{AMDRequireArrayDependency, AMDRequireArrayItem}, amd_require_item_dependency::AMDRequireItemDependency, local_module_dependency::LocalModuleDependency, }, @@ -65,21 +67,17 @@ fn is_callable(expr: &Expr) -> bool { is_unbound_function_expression(expr) || is_bound_function_expression(expr) } -/** - * lookup - * - * define('ui/foo/bar', ['./baz', '../qux'], ...); - * - 'ui/foo/baz' - * - 'ui/qux' - */ -fn resolve_mod_name(mod_name: &Option, dep_name: &str) -> Atom { - if let Some(mod_name) = mod_name - && dep_name.starts_with('.') +/// define('ui/foo/bar', ['./baz', '../qux'], ...); +/// - 'ui/foo/baz' +/// - 'ui/qux' +fn lookup(parent: &Option, module: &str) -> Atom { + if let Some(parent) = parent + && module.starts_with('.') { - let mut path: Vec<&str> = mod_name.split('/').collect(); + let mut path: Vec<&str> = parent.split('/').collect(); path.pop(); - for seg in dep_name.split('.') { + for seg in module.split('.') { if seg == ".." { path.pop(); } else if seg != "." { @@ -89,7 +87,7 @@ fn resolve_mod_name(mod_name: &Option, dep_name: &str) -> Atom { path.join("/").into() } else { - dep_name.into() + module.into() } } @@ -140,9 +138,38 @@ impl AMDDefineDependencyParserPlugin { } } return Some(true); + } else if param.is_const_array() { + let mut deps: Vec = vec![]; + let array = param.array(); + for (i, request) in array.iter().enumerate() { + if request == "require" { + identifiers.insert(i, REQUIRE); + deps.push(AMDRequireArrayItem::String( + RuntimeGlobals::REQUIRE.name().into(), + )); + } else if request == "exports" { + identifiers.insert(i, EXPORTS); + deps.push(AMDRequireArrayItem::String(request.into())); + } else if request == "module" { + identifiers.insert(i, MODULE); + deps.push(AMDRequireArrayItem::String(request.into())); + } else if let Some(local_module) = parser.get_local_module_mut(request) { + local_module.flag_used(); + deps.push(AMDRequireArrayItem::LocalModuleDependency { + local_module_variable_name: local_module.variable_name(), + }) + } else { + let mut dep = AMDRequireItemDependency::new(request.as_str().into(), None); + dep.set_optional(parser.in_try); + deps.push(AMDRequireArrayItem::AMDRequireItemDependency { dep_id: *dep.id() }); + parser.dependencies.push(Box::new(dep)); + } + } + let range = param.range(); + let dep = AMDRequireArrayDependency::new(deps, (range.0, range.1 - 1)); + parser.presentational_dependencies.push(Box::new(dep)); + return Some(true); } - // currently, there is no ConstArray in rspack - // TODO: check if `param` is a const string array None } @@ -193,7 +220,7 @@ impl AMDDefineDependencyParserPlugin { Some(RuntimeGlobals::MODULE), )) } else if let Some(local_module) = - parser.get_local_module_mut(&resolve_mod_name(named_module, param_str)) + parser.get_local_module_mut(&lookup(named_module, param_str)) { local_module.flag_used(); let dep = Box::new(LocalModuleDependency::new( @@ -206,7 +233,7 @@ impl AMDDefineDependencyParserPlugin { } else { let mut dep = Box::new(AMDRequireItemDependency::new( Atom::new(param_str.as_str()), - range, + Some(range), )); dep.set_optional(parser.in_try); parser.dependencies.push(dep); @@ -542,7 +569,7 @@ impl AMDDefineDependencyParserPlugin { .as_ref() .map(|name| parser.add_local_module(name.as_str())); - let dep = Box::new(AmdDefineDependency::new( + let dep = Box::new(AMDDefineDependency::new( (call_expr.span.real_lo(), call_expr.span.real_hi()), array.map(|expr| span_to_range(expr.span())), func.map(|expr| span_to_range(expr.span())), @@ -578,7 +605,7 @@ impl JavascriptParserPlugin for AMDDefineDependencyParserPlugin { */ fn finish(&self, parser: &mut JavascriptParser) -> Option { for dep in parser.presentational_dependencies.iter_mut() { - if let Some(define_dep) = dep.as_any_mut().downcast_mut::() + if let Some(define_dep) = dep.as_any_mut().downcast_mut::() && let Some(local_module) = define_dep.get_local_module_mut() && parser .local_modules diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_require_dependencies_block_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_require_dependencies_block_parser_plugin.rs index fa7865e9016a..6e88334aa71a 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_require_dependencies_block_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/amd/amd_require_dependencies_block_parser_plugin.rs @@ -3,8 +3,8 @@ use std::{borrow::Cow, iter}; use either::Either; use itertools::Itertools; use rspack_core::{ - AsyncDependenciesBlock, BoxDependency, ConstDependency, DependencyRange, RuntimeGlobals, - SharedSourceMap, SpanExt, + AsyncDependenciesBlock, BoxDependency, ConstDependency, Dependency, DependencyRange, + RuntimeGlobals, SharedSourceMap, SpanExt, }; use rspack_error::miette::Severity; use rspack_util::atom::Atom; @@ -15,9 +15,11 @@ use swc_core::{ use crate::{ dependency::{ + amd_require_array_dependency::{AMDRequireArrayDependency, AMDRequireArrayItem}, amd_require_dependency::AMDRequireDependency, amd_require_item_dependency::AMDRequireItemDependency, - local_module_dependency::LocalModuleDependency, unsupported_dependency::UnsupportedDependency, + local_module_dependency::LocalModuleDependency, + unsupported_dependency::UnsupportedDependency, }, parser_plugin::require_ensure_dependencies_block_parse_plugin::GetFunctionExpression, utils::eval::BasicEvaluatedExpression, @@ -65,6 +67,32 @@ impl AMDRequireDependenciesBlockParserPlugin { } } return Some(true); + } else if param.is_const_array() { + let mut deps: Vec = vec![]; + let array = param.array(); + for request in array.iter() { + if request == "require" { + deps.push(AMDRequireArrayItem::String( + RuntimeGlobals::REQUIRE.name().into(), + )); + } else if request == "exports" || request == "module" { + deps.push(AMDRequireArrayItem::String(request.into())); + } else if let Some(local_module) = parser.get_local_module_mut(request) { + local_module.flag_used(); + deps.push(AMDRequireArrayItem::LocalModuleDependency { + local_module_variable_name: local_module.variable_name(), + }) + } else { + let mut dep = AMDRequireItemDependency::new(request.as_str().into(), None); + dep.set_optional(parser.in_try); + deps.push(AMDRequireArrayItem::AMDRequireItemDependency { dep_id: *dep.id() }); + parser.dependencies.push(Box::new(dep)); + } + } + let range = param.range(); + let dep = AMDRequireArrayDependency::new(deps, (range.0, range.1 - 1)); + parser.presentational_dependencies.push(Box::new(dep)); + return Some(true); } None } @@ -130,7 +158,7 @@ impl AMDRequireDependenciesBlockParserPlugin { } else { let mut dep = Box::new(AMDRequireItemDependency::new( Atom::new(param_str.as_str()), - range, + Some(range), )); dep.set_optional(parser.in_try); block_deps.push(dep); diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs b/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs index 3577125edc85..c39c3cc4a6e7 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs @@ -8,6 +8,7 @@ const SLICE_METHOD_NAME: &str = "slice"; const REPLACE_METHOD_NAME: &str = "replace"; const CONCAT_METHOD_NAME: &str = "concat"; const INDEXOF_METHOD_NAME: &str = "indexOf"; +const SPLIT_METHOD_NAME: &str = "split"; // TODO: substr, substring pub struct InitializeEvaluating; @@ -179,6 +180,29 @@ impl JavascriptParserPlugin for InitializeEvaluating { ); return Some(eval); } + } else if property == SPLIT_METHOD_NAME + && param.is_string() + && expr.args.len() == 1 + && expr.args[0].spread.is_none() + { + let arg = parser.evaluate_expression(&expr.args[0].expr); + let array: Vec = if arg.is_string() { + param + .string() + .split(arg.string()) + .map(|s| s.to_owned()) + .collect() + } else if arg.is_regexp() { + let raw = arg.regexp(); + let regex = eval_regexp_to_regexp(&raw.0, &raw.1); + regex.split(param.string()).map(|s| s.to_owned()).collect() + } else { + return None; + }; + let mut res = BasicEvaluatedExpression::with_range(expr.span.real_lo(), expr.span.hi().0); + res.set_array(array); + res.set_side_effects(param.could_have_side_effects()); + return Some(res); } None diff --git a/crates/rspack_plugin_javascript/src/utils/eval/mod.rs b/crates/rspack_plugin_javascript/src/utils/eval/mod.rs index e9d2880a9ca2..07feb46c0e95 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/mod.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/mod.rs @@ -70,6 +70,7 @@ pub struct BasicEvaluatedExpression { string: Option, bigint: Option, regexp: Option, + array: Option>, identifier: Option, root_info: Option, members: Option>, @@ -81,7 +82,6 @@ pub struct BasicEvaluatedExpression { prefix: Option>, postfix: Option>, wrapped_inner_expressions: Option>, - // array: Option template_string_kind: Option, options: Option>, @@ -105,6 +105,7 @@ impl BasicEvaluatedExpression { boolean: None, number: None, bigint: None, + array: None, quasis: None, parts: None, identifier: None, @@ -165,6 +166,10 @@ impl BasicEvaluatedExpression { matches!(self.ty, Ty::Array) } + pub fn is_const_array(&self) -> bool { + matches!(self.ty, Ty::ConstArray) + } + pub fn is_wrapped(&self) -> bool { matches!(self.ty, Ty::Wrapped) } @@ -398,6 +403,12 @@ impl BasicEvaluatedExpression { self.items = Some(items); } + pub fn set_array(&mut self, array: Vec) { + self.ty = Ty::ConstArray; + self.side_effects = false; + self.array = Some(array); + } + pub fn options(&self) -> &Vec { self.options.as_ref().expect("options should not empty") } @@ -574,6 +585,14 @@ impl BasicEvaluatedExpression { self.items.as_ref().expect("items must exists for array") } + pub fn array(&self) -> &Vec { + assert!(self.is_const_array()); + self + .array + .as_ref() + .expect("array must exists for const array") + } + pub fn number(&self) -> Number { assert!(self.is_number()); self.number.expect("number must exists in ty::number") diff --git a/tests/webpack-test/cases/amd/namedModulesConstArrayDep/test.filter.js b/tests/webpack-test/cases/amd/namedModulesConstArrayDep/test.filter.js deleted file mode 100644 index 12dcfeb8efcc..000000000000 --- a/tests/webpack-test/cases/amd/namedModulesConstArrayDep/test.filter.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = () => {return "compile time eval split"} - - \ No newline at end of file diff --git a/tests/webpack-test/cases/mjs/type-module/package.json b/tests/webpack-test/cases/mjs/type-module/package.json index 1c3ab4e0dc84..3dbc1ca591c0 100644 --- a/tests/webpack-test/cases/mjs/type-module/package.json +++ b/tests/webpack-test/cases/mjs/type-module/package.json @@ -1,3 +1,3 @@ { - "TODO: remember to recover `type: \"module\"`, currently test use `require` for `test.filter.js` which violates node": "" + "type": "module" } diff --git a/tests/webpack-test/cases/mjs/type-module/test.filter.js b/tests/webpack-test/cases/mjs/type-module/test.filter.js deleted file mode 100644 index 5699e8435e09..000000000000 --- a/tests/webpack-test/cases/mjs/type-module/test.filter.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = () => { - return "compile time evaluation typeof and amd"; -};