From f56fff5d59129ef3a43228b5549552fdb2236f4f Mon Sep 17 00:00:00 2001 From: faga Date: Fri, 9 Feb 2024 13:15:31 +0800 Subject: [PATCH] feat: support resource query in context module (#5476) * feat: support resource query in context module * fix: update --------- Co-authored-by: LingyuCoder --- .../common_js_require_context_dependency.rs | 13 +- .../context/import_context_dependency.rs | 13 +- .../common_js_imports_parse_plugin.rs | 13 +- .../src/parser_plugin/import_parser_plugin.rs | 12 +- .../src/visitors/dependency/context_helper.rs | 80 ++++- .../src/visitors/dependency/import_scanner.rs | 331 ++++++++++++++++++ .../src/visitors/dependency/mod.rs | 2 +- .../tests/cases/context/resource-query/a.js | 3 + .../tests/cases/context/resource-query/abc.js | 3 + .../cases/context/resource-query/index.js | 48 +++ 10 files changed, 495 insertions(+), 23 deletions(-) create mode 100644 crates/rspack_plugin_javascript/src/visitors/dependency/import_scanner.rs create mode 100644 packages/rspack/tests/cases/context/resource-query/a.js create mode 100644 packages/rspack/tests/cases/context/resource-query/abc.js create mode 100644 packages/rspack/tests/cases/context/resource-query/index.js diff --git a/crates/rspack_plugin_javascript/src/dependency/context/common_js_require_context_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/context/common_js_require_context_dependency.rs index 41ae04f164e..e4c29905ef4 100644 --- a/crates/rspack_plugin_javascript/src/dependency/context/common_js_require_context_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/context/common_js_require_context_dependency.rs @@ -1,4 +1,4 @@ -use rspack_core::{module_raw, AsModuleDependency, ContextDependency}; +use rspack_core::{module_raw, parse_resource, AsModuleDependency, ContextDependency}; use rspack_core::{normalize_context, DependencyCategory, DependencyId, DependencyTemplate}; use rspack_core::{ContextOptions, Dependency, TemplateReplaceSource}; use rspack_core::{DependencyType, ErrorSpan, TemplateContext}; @@ -113,13 +113,22 @@ impl DependencyTemplate for CommonJsRequireContextDependency { source.replace(self.callee_start, self.callee_end, &expr, None); let context = normalize_context(&self.options.request); + let query = parse_resource(&self.options.request).and_then(|data| data.query); if !context.is_empty() { source.insert(self.callee_end, "(", None); source.insert( self.args_end, - format!(".replace('{context}', './'))").as_str(), + format!(".replace('{context}', './')").as_str(), None, ); + if let Some(query) = query { + source.insert( + self.args_end, + format!(".replace('{query}', '')").as_str(), + None, + ); + } + source.insert(self.args_end, ")", None); } } diff --git a/crates/rspack_plugin_javascript/src/dependency/context/import_context_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/context/import_context_dependency.rs index 7d5bb16bd49..2a69c5461ce 100644 --- a/crates/rspack_plugin_javascript/src/dependency/context/import_context_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/context/import_context_dependency.rs @@ -1,4 +1,4 @@ -use rspack_core::{module_raw, AsModuleDependency, ContextDependency}; +use rspack_core::{module_raw, parse_resource, AsModuleDependency, ContextDependency}; use rspack_core::{normalize_context, DependencyCategory, DependencyId, DependencyTemplate}; use rspack_core::{ContextOptions, Dependency, TemplateReplaceSource}; use rspack_core::{DependencyType, ErrorSpan, TemplateContext}; @@ -113,13 +113,22 @@ impl DependencyTemplate for ImportContextDependency { source.replace(self.callee_start, self.callee_end, &expr, None); let context = normalize_context(&self.options.request); + let query = parse_resource(&self.options.request).and_then(|data| data.query); if !context.is_empty() { source.insert(self.callee_end, "(", None); source.insert( self.args_end, - format!(".replace('{context}', './'))").as_str(), + format!(".replace('{context}', './')").as_str(), None, ); + if let Some(query) = query { + source.insert( + self.args_end, + format!(".replace('{query}', '')").as_str(), + None, + ); + } + source.insert(self.args_end, ")", None); } } diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index 213cb4e4c2b..5358d1337fd 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -9,7 +9,9 @@ use crate::dependency::RequireHeaderDependency; use crate::dependency::{CommonJsFullRequireDependency, CommonJsRequireContextDependency}; use crate::dependency::{CommonJsRequireDependency, RequireResolveDependency}; use crate::utils::eval::{self, BasicEvaluatedExpression}; -use crate::visitors::{expr_matcher, scanner_context_module, JavascriptParser}; +use crate::visitors::{ + expr_matcher, scanner_context_module, ContextModuleScanResult, JavascriptParser, +}; use crate::visitors::{extract_require_call_info, is_require_call_start}; pub const COMMONJS_REQUIRE: &str = "require"; @@ -311,7 +313,12 @@ impl JavascriptParserPlugin for CommonJsImportsParserPlugin { && let Some(expr) = call_expr.args.first() && call_expr.args.len() == 1 && expr.spread.is_none() - && let Some((context, reg)) = scanner_context_module(expr.expr.as_ref()) + && let Some(ContextModuleScanResult { + context, + reg, + query, + fragment, + }) = scanner_context_module(expr.expr.as_ref()) { // `require.resolve` parser @@ -329,7 +336,7 @@ impl JavascriptParserPlugin for CommonJsImportsParserPlugin { include: None, exclude: None, category: DependencyCategory::CommonJS, - request: context, + request: format!("{}{}{}", context, query, fragment), namespace_object: ContextNameSpaceObject::Unset, }, Some(call_expr.span.into()), diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs index ce8524ec5e3..cf22d679c4f 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs @@ -10,7 +10,7 @@ use swc_core::ecma::atoms::Atom; use super::JavascriptParserPlugin; use crate::dependency::{ImportContextDependency, ImportDependency, ImportEagerDependency}; -use crate::visitors::{parse_order_string, scanner_context_module}; +use crate::visitors::{parse_order_string, scanner_context_module, ContextModuleScanResult}; use crate::webpack_comment::try_extract_webpack_magic_comment; pub struct ImportParserPlugin; @@ -150,7 +150,13 @@ impl JavascriptParserPlugin for ImportParserPlugin { Some(true) } _ => { - let Some((context, reg)) = scanner_context_module(dyn_imported.expr.as_ref()) else { + let Some(ContextModuleScanResult { + context, + reg, + query, + fragment, + }) = scanner_context_module(dyn_imported.expr.as_ref()) + else { return None; }; let magic_comment_options = try_extract_webpack_magic_comment( @@ -178,7 +184,7 @@ impl JavascriptParserPlugin for ImportParserPlugin { include: None, exclude: None, category: DependencyCategory::Esm, - request: context, + request: format!("{}{}{}", context, query, fragment), namespace_object: if parser.build_meta.strict_harmony_module { ContextNameSpaceObject::Strict } else { diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/context_helper.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/context_helper.rs index 23e0e09b17b..a8c036006fa 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/context_helper.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/context_helper.rs @@ -1,5 +1,6 @@ use once_cell::sync::Lazy; use regex::Regex; +use rspack_core::parse_resource; use swc_core::ecma::ast::{ BinExpr, BinaryOp, CallExpr, Callee, Expr, Lit, MemberProp, TaggedTpl, Tpl, }; @@ -9,6 +10,13 @@ enum TemplateStringKind { Cooked, } +pub struct ContextModuleScanResult { + pub context: String, + pub reg: String, + pub query: String, + pub fragment: String, +} + #[inline] fn split_context_from_prefix(prefix: String) -> (String, String) { if let Some(idx) = prefix.rfind('/') { @@ -18,7 +26,7 @@ fn split_context_from_prefix(prefix: String) -> (String, String) { } } -pub fn scanner_context_module(expr: &Expr) -> Option<(String, String)> { +pub fn scanner_context_module(expr: &Expr) -> Option { match expr { Expr::Tpl(tpl) if !tpl.exprs.is_empty() => { Some(scan_context_module_tpl(tpl, TemplateStringKind::Cooked)) @@ -40,7 +48,7 @@ fn quote_meta(str: String) -> String { } // require(`./${a}.js`) -fn scan_context_module_tpl(tpl: &Tpl, kind: TemplateStringKind) -> (String, String) { +fn scan_context_module_tpl(tpl: &Tpl, kind: TemplateStringKind) -> ContextModuleScanResult { let prefix_raw = tpl .quasis .first() @@ -73,16 +81,31 @@ fn scan_context_module_tpl(tpl: &Tpl, kind: TemplateStringKind) -> (String, Stri }) .collect::>() .join(""); + + let (postfix, query, fragment) = match parse_resource(&postfix_raw) { + Some(data) => ( + data.path.to_string_lossy().to_string(), + data.query.unwrap_or_default(), + data.fragment.unwrap_or_default(), + ), + None => (postfix_raw, String::new(), String::new()), + }; + let reg = format!( "^{prefix}.*{inner_reg}{postfix_raw}$", prefix = quote_meta(prefix), - postfix_raw = quote_meta(postfix_raw) + postfix_raw = quote_meta(postfix) ); - (context, reg) + ContextModuleScanResult { + context, + reg, + query, + fragment, + } } // require("./" + a + ".js") -fn scan_context_module_bin(bin: &BinExpr) -> Option<(String, String)> { +fn scan_context_module_bin(bin: &BinExpr) -> Option { if !is_add_op_bin_expr(bin) { return None; } @@ -102,13 +125,28 @@ fn scan_context_module_bin(bin: &BinExpr) -> Option<(String, String)> { } let (context, prefix) = split_context_from_prefix(prefix_raw); + + let (postfix, query, fragment) = match parse_resource(&postfix_raw) { + Some(data) => ( + data.path.to_string_lossy().to_string(), + data.query.unwrap_or_default(), + data.fragment.unwrap_or_default(), + ), + None => (postfix_raw, String::new(), String::new()), + }; + let reg = format!( "^{prefix}.*{postfix_raw}$", prefix = quote_meta(prefix), - postfix_raw = quote_meta(postfix_raw) + postfix_raw = quote_meta(postfix) ); - Some((context, reg)) + Some(ContextModuleScanResult { + context, + reg, + query, + fragment, + }) } fn find_expr_prefix_string(expr: &Expr) -> Option { @@ -133,7 +171,7 @@ fn is_add_op_bin_expr(bin: &BinExpr) -> bool { // require("./".concat(a, ".js")) // babel/swc will transform template literal to string concat, so we need to handle this case // see https://github.com/webpack/webpack/pull/5679 -fn scan_context_module_concat_call(expr: &CallExpr) -> Option<(String, String)> { +fn scan_context_module_concat_call(expr: &CallExpr) -> Option { if !is_concat_call(expr) { return None; } @@ -153,17 +191,30 @@ fn scan_context_module_concat_call(expr: &CallExpr) -> Option<(String, String)> } let (context, prefix) = split_context_from_prefix(prefix_raw); + let (postfix, query, fragment) = match parse_resource(&postfix_raw) { + Some(data) => ( + data.path.to_string_lossy().to_string(), + data.query.unwrap_or_default(), + data.fragment.unwrap_or_default(), + ), + None => (postfix_raw, String::new(), String::new()), + }; let reg = format!( "^{prefix}.*{postfix_raw}$", prefix = quote_meta(prefix), - postfix_raw = quote_meta(postfix_raw) + postfix_raw = quote_meta(postfix) ); - Some((context, reg)) + Some(ContextModuleScanResult { + context, + reg, + query, + fragment, + }) } // require(String.raw`./${a}.js`) -fn scan_context_module_tagged_tpl(tpl: &TaggedTpl) -> (String, String) { +fn scan_context_module_tagged_tpl(tpl: &TaggedTpl) -> ContextModuleScanResult { match tpl.tag.as_member() { Some(tag) if tag @@ -179,7 +230,12 @@ fn scan_context_module_tagged_tpl(tpl: &TaggedTpl) -> (String, String) { { scan_context_module_tpl(tpl.tpl.as_ref(), TemplateStringKind::Raw) } - _ => (String::from("."), String::new()), + _ => ContextModuleScanResult { + context: String::from("."), + reg: String::new(), + query: String::new(), + fragment: String::new(), + }, } } diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/import_scanner.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/import_scanner.rs new file mode 100644 index 00000000000..da3373aa92b --- /dev/null +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/import_scanner.rs @@ -0,0 +1,331 @@ +use std::sync::Arc; + +use rspack_core::{ + clean_regexp_in_context_module, context_reg_exp, AsyncDependenciesBlock, DependencyLocation, + DynamicImportMode, ErrorSpan, GroupOptions, JavascriptParserOptions, ModuleIdentifier, +}; +use rspack_core::{BoxDependency, BuildMeta, ChunkGroupOptions, ContextMode}; +use rspack_core::{ContextNameSpaceObject, ContextOptions, DependencyCategory, SpanExt}; +use rspack_error::miette::Diagnostic; +use rspack_regex::{regexp_as_str, RspackRegex}; +use rustc_hash::FxHashSet; +use swc_core::common::comments::Comments; +use swc_core::common::{SourceFile, Spanned}; +use swc_core::ecma::ast::{CallExpr, Callee, Expr, Lit}; +use swc_core::ecma::atoms::Atom; +use swc_core::ecma::visit::{noop_visit_type, Visit, VisitWith}; + +use super::context_helper::{scanner_context_module, ContextModuleScanResult}; +use super::{is_import_meta_context_call, parse_order_string}; +use crate::dependency::{ImportContextDependency, ImportDependency}; +use crate::dependency::{ImportEagerDependency, ImportMetaContextDependency}; +use crate::no_visit_ignored_stmt; +use crate::utils::{get_bool_by_obj_prop, get_literal_str_by_obj_prop, get_regex_by_obj_prop}; +use crate::webpack_comment::try_extract_webpack_magic_comment; + +pub struct ImportScanner<'a> { + pub source_file: Arc, + pub module_identifier: ModuleIdentifier, + pub dependencies: &'a mut Vec, + pub blocks: &'a mut Vec, + pub comments: Option<&'a dyn Comments>, + pub build_meta: &'a BuildMeta, + pub options: Option<&'a JavascriptParserOptions>, + pub warning_diagnostics: &'a mut Vec>, + pub ignored: &'a mut FxHashSet, +} + +fn create_import_meta_context_dependency(node: &CallExpr) -> Option { + assert!(node.callee.is_expr()); + let dyn_imported = node.args.first()?; + if dyn_imported.spread.is_some() { + return None; + } + let context = dyn_imported + .expr + .as_lit() + .and_then(|lit| { + if let Lit::Str(str) = lit { + return Some(str.value.to_string()); + } + None + }) + // TODO: should've used expression evaluation to handle cases like `abc${"efg"}`, etc. + .or_else(|| { + if let Some(tpl) = dyn_imported.expr.as_tpl() + && tpl.exprs.is_empty() + && tpl.quasis.len() == 1 + && let Some(el) = tpl.quasis.first() + { + return Some(el.raw.to_string()); + } + None + })?; + let reg = r"^\.\/.*$"; + let reg_str = reg.to_string(); + let context_options = if let Some(obj) = node.args.get(1).and_then(|arg| arg.expr.as_object()) { + let regexp = get_regex_by_obj_prop(obj, "regExp") + .map(|regexp| RspackRegex::try_from(regexp).expect("reg failed")) + .unwrap_or(RspackRegex::new(reg).expect("reg failed")); + // let include = get_regex_by_obj_prop(obj, "include") + // .map(|regexp| RspackRegex::try_from(regexp).expect("reg failed")); + // let exclude = get_regex_by_obj_prop(obj, "include") + // .map(|regexp| RspackRegex::try_from(regexp).expect("reg failed")); + let mode = get_literal_str_by_obj_prop(obj, "mode") + .map(|s| s.value.to_string().as_str().into()) + .unwrap_or(ContextMode::Sync); + let recursive = get_bool_by_obj_prop(obj, "recursive") + .map(|bool| bool.value) + .unwrap_or(true); + let reg_str = regexp_as_str(®exp).to_string(); + ContextOptions { + chunk_name: None, + reg_exp: clean_regexp_in_context_module(regexp), + reg_str, + include: None, + exclude: None, + recursive, + category: DependencyCategory::Esm, + request: context, + namespace_object: ContextNameSpaceObject::Unset, + mode, + } + } else { + ContextOptions { + chunk_name: None, + recursive: true, + mode: ContextMode::Sync, + include: None, + exclude: None, + reg_exp: context_reg_exp(reg, ""), + reg_str, + category: DependencyCategory::Esm, + request: context, + namespace_object: ContextNameSpaceObject::Unset, + } + }; + Some(ImportMetaContextDependency::new( + node.span.real_lo(), + node.span.real_hi(), + context_options, + Some(node.span.into()), + )) +} + +impl<'a> ImportScanner<'a> { + #[allow(clippy::too_many_arguments)] + pub fn new( + source_file: Arc, + module_identifier: ModuleIdentifier, + dependencies: &'a mut Vec, + blocks: &'a mut Vec, + comments: Option<&'a dyn Comments>, + build_meta: &'a BuildMeta, + options: Option<&'a JavascriptParserOptions>, + warning_diagnostics: &'a mut Vec>, + ignored: &'a mut FxHashSet, + ) -> Self { + Self { + source_file, + module_identifier, + dependencies, + blocks, + comments, + build_meta, + options, + warning_diagnostics, + ignored, + } + } +} + +impl Visit for ImportScanner<'_> { + noop_visit_type!(); + no_visit_ignored_stmt!(); + + fn visit_call_expr(&mut self, node: &CallExpr) { + if !node.args.is_empty() + && is_import_meta_context_call(node) + && let Some(dep) = create_import_meta_context_dependency(node) + { + self.dependencies.push(Box::new(dep)); + return; + } + let Callee::Import(import_call) = &node.callee else { + node.visit_children_with(self); + return; + }; + let Some(dyn_imported) = node.args.first() else { + node.visit_children_with(self); + return; + }; + if dyn_imported.spread.is_some() { + node.visit_children_with(self); + return; + } + + let mode = self + .options + .map(|o| o.dynamic_import_mode) + .unwrap_or_default(); + + let dynamic_import_preload = self + .options + .map(|o| o.dynamic_import_preload) + .and_then(|o| o.get_order()); + + let dynamic_import_prefetch = self + .options + .map(|o| o.dynamic_import_prefetch) + .and_then(|o| o.get_order()); + + match dyn_imported.expr.as_ref() { + Expr::Lit(Lit::Str(imported)) => { + if matches!(mode, DynamicImportMode::Eager) { + let dep = ImportEagerDependency::new( + node.span.real_lo(), + node.span.real_hi(), + imported.value.clone(), + Some(node.span.into()), + // TODO scan dynamic import referenced exports + None, + ); + self.dependencies.push(Box::new(dep)); + return; + } + let magic_comment_options = try_extract_webpack_magic_comment( + &self.source_file, + &self.comments, + node.span, + imported.span, + self.warning_diagnostics, + ); + if magic_comment_options + .get_webpack_ignore() + .unwrap_or_default() + { + return; + } + let chunk_name = magic_comment_options + .get_webpack_chunk_name() + .map(|x| x.to_owned()); + let chunk_prefetch = magic_comment_options + .get_webpack_prefetch() + .and_then(|x| parse_order_string(x.as_str())); + let chunk_preload = magic_comment_options + .get_webpack_preload() + .and_then(|x| parse_order_string(x.as_str())); + let span = ErrorSpan::from(node.span); + let dep = Box::new(ImportDependency::new( + node.span.real_lo(), + node.span.real_hi(), + imported.value.clone(), + Some(span), + // TODO scan dynamic import referenced exports + None, + )); + let mut block = AsyncDependenciesBlock::new( + self.module_identifier, + Some(DependencyLocation::new(span.start, span.end)), + ); + block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new( + chunk_name, + chunk_preload.or(dynamic_import_preload), + chunk_prefetch.or(dynamic_import_prefetch), + ))); + block.add_dependency(dep); + self.blocks.push(block); + } + Expr::Tpl(tpl) if tpl.quasis.len() == 1 => { + let magic_comment_options = try_extract_webpack_magic_comment( + &self.source_file, + &self.comments, + node.span, + tpl.span, + self.warning_diagnostics, + ); + let chunk_name = magic_comment_options + .get_webpack_chunk_name() + .map(|x| x.to_owned()); + let chunk_prefetch = magic_comment_options + .get_webpack_prefetch() + .and_then(|x| parse_order_string(x.as_str())); + let chunk_preload = magic_comment_options + .get_webpack_preload() + .and_then(|x| parse_order_string(x.as_str())); + let request = Atom::from( + tpl + .quasis + .first() + .expect("should have one quasis") + .raw + .to_string(), + ); + let span = ErrorSpan::from(node.span); + let dep = Box::new(ImportDependency::new( + node.span.real_lo(), + node.span.real_hi(), + request, + Some(span), + None, + )); + let mut block = AsyncDependenciesBlock::new( + self.module_identifier, + Some(DependencyLocation::new(span.start, span.end)), + ); + block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new( + chunk_name, + chunk_preload.or(dynamic_import_preload), + chunk_prefetch.or(dynamic_import_prefetch), + ))); + block.add_dependency(dep); + self.blocks.push(block); + } + _ => { + let Some(ContextModuleScanResult { + context, + reg, + query, + fragment, + }) = scanner_context_module(dyn_imported.expr.as_ref()) + else { + return; + }; + let magic_comment_options = try_extract_webpack_magic_comment( + &self.source_file, + &self.comments, + node.span, + dyn_imported.span(), + self.warning_diagnostics, + ); + let chunk_name = magic_comment_options + .get_webpack_chunk_name() + .map(|x| x.to_owned()); + self + .dependencies + .push(Box::new(ImportContextDependency::new( + import_call.span.real_lo(), + import_call.span.real_hi(), + node.span.real_hi(), + ContextOptions { + chunk_name, + mode: ContextMode::Lazy, + recursive: true, + reg_exp: context_reg_exp(®, ""), + reg_str: reg, + include: None, + exclude: None, + category: DependencyCategory::Esm, + request: format!("{}{}{}", context, query, fragment), + namespace_object: if self.build_meta.strict_harmony_module { + ContextNameSpaceObject::Strict + } else { + ContextNameSpaceObject::Bool(true) + }, + }, + Some(node.span.into()), + ))); + } + } + } +} diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs index 2eed99f7ac6..a78cf33f4f7 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs @@ -6,7 +6,7 @@ mod util; use std::sync::Arc; -pub use context_helper::scanner_context_module; +pub use context_helper::{scanner_context_module, ContextModuleScanResult}; use rspack_ast::javascript::Program; use rspack_core::needs_refactor::WorkerSyntaxList; use rspack_core::{ diff --git a/packages/rspack/tests/cases/context/resource-query/a.js b/packages/rspack/tests/cases/context/resource-query/a.js new file mode 100644 index 00000000000..4c580819a08 --- /dev/null +++ b/packages/rspack/tests/cases/context/resource-query/a.js @@ -0,0 +1,3 @@ +export default function a() { + return "a" + __resourceQuery; +} diff --git a/packages/rspack/tests/cases/context/resource-query/abc.js b/packages/rspack/tests/cases/context/resource-query/abc.js new file mode 100644 index 00000000000..fcb067b3c7d --- /dev/null +++ b/packages/rspack/tests/cases/context/resource-query/abc.js @@ -0,0 +1,3 @@ +export default function abc() { + return "abc" + __resourceQuery; +} diff --git a/packages/rspack/tests/cases/context/resource-query/index.js b/packages/rspack/tests/cases/context/resource-query/index.js new file mode 100644 index 00000000000..9309fbddecf --- /dev/null +++ b/packages/rspack/tests/cases/context/resource-query/index.js @@ -0,0 +1,48 @@ +it("should detect query strings in dynamic import as a static value 1 ", function () { + return Promise.all([ + import("./a").then(({ default: a }) => { + expect(a()).toBe("a"); + }), + import("./abc").then(({ default: a }) => { + expect(a()).toBe("abc"); + }), + import("./a?queryString").then(({ default: a }) => { + expect(a()).toBe("a?queryString"); + }) + ]); +}); + +it("should detect query strings in dynamic import as a static value 2", function () { + var testFileName = "a"; + + return Promise.all([ + import(`./${testFileName}`).then(({ default: a }) => { + expect(a()).toBe("a"); + }), + import(`./${testFileName}bc`).then(({ default: a }) => { + expect(a()).toBe("abc"); + }), + import(`./${testFileName}?queryString`).then(({ default: a }) => { + expect(a()).toBe("a?queryString"); + }) + ]); +}); + +it("should detect query strings in dynamic import as a static value 3", function () { + var testFileName = "a"; + + return Promise.all([ + import("./" + testFileName).then(({ default: a }) => { + expect(a()).toBe("a"); + }), + import("./" + testFileName + "").then(({ default: a }) => { + expect(a()).toBe("a"); + }), + import("./" + testFileName + "bc").then(({ default: a }) => { + expect(a()).toBe("abc"); + }), + import("./" + testFileName + "?queryString").then(({ default: a }) => { + expect(a()).toBe("a?queryString"); + }) + ]); +});