diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs index f409f0b64f2..63342c3a9f1 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/api_plugin.rs @@ -22,6 +22,7 @@ const WEBPACK_NONCE: &str = "__webpack_nonce__"; const WEBPACK_CHUNK_NAME: &str = "__webpack_chunkname__"; const WEBPACK_RUNTIME_ID: &str = "__webpack_runtime_id__"; const WEBPACK_REQUIRE: &str = RuntimeGlobals::REQUIRE.name(); +const WEBPACK_GET_SCRIPT_FILENAME: &str = "__webpack_get_script_filename__"; const RSPACK_VERSION: &str = "__rspack_version__"; const RSPACK_UNIQUE_ID: &str = "__rspack_unique_id__"; @@ -56,6 +57,7 @@ fn get_typeof_evaluate_of_api(sym: &str) -> Option<&str> { WEBPACK_NONCE => Some("string"), WEBPACK_CHUNK_NAME => Some("string"), WEBPACK_RUNTIME_ID => None, + WEBPACK_GET_SCRIPT_FILENAME => Some("function"), RSPACK_VERSION => Some("string"), RSPACK_UNIQUE_ID => Some("string"), _ => None, @@ -239,6 +241,17 @@ impl JavascriptParserPlugin for APIPlugin { ))); Some(true) } + WEBPACK_GET_SCRIPT_FILENAME => { + parser + .presentational_dependencies + .push(Box::new(ConstDependency::new( + ident.span.real_lo(), + ident.span.real_hi(), + RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME.name().into(), + Some(RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME), + ))); + Some(true) + } // rspack specific RSPACK_VERSION => { parser diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs index 193dc7d9a50..0eb4f356af6 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs @@ -49,13 +49,16 @@ impl JavascriptParserPlugin for ImportMetaPlugin { fn evaluate_identifier( &self, - _parser: &mut JavascriptParser, + parser: &mut JavascriptParser, ident: &str, start: u32, end: u32, ) -> Option { if ident == expr_name::IMPORT_META_WEBPACK { Some(eval::evaluate_to_number(5_f64, start, end)) + } else if ident == expr_name::IMPORT_META_URL { + let url = Url::from_file_path(&parser.resource_data.resource).expect("should be a path"); + Some(eval::evaluate_to_string(url.to_string(), start, end)) } else { None } 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 1b4e0747df2..e07d974f592 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/initialize_evaluating.rs @@ -6,6 +6,8 @@ use crate::utils::eval::BasicEvaluatedExpression; const SLICE_METHOD_NAME: &str = "slice"; const REPLACE_METHOD_NAME: &str = "replace"; const CONCAT_METHOD_NAME: &str = "concat"; +const INDEXOF_METHOD_NAME: &str = "indexOf"; +// TODO: substr, substring pub struct InitializeEvaluating; @@ -17,7 +19,36 @@ impl JavascriptParserPlugin for InitializeEvaluating { expr: &swc_core::ecma::ast::CallExpr, param: &BasicEvaluatedExpression, ) -> Option { - if property == SLICE_METHOD_NAME + if property == INDEXOF_METHOD_NAME && param.is_string() { + let arg1 = (!expr.args.is_empty()).then_some(true).and_then(|_| { + if expr.args[0].spread.is_some() { + return None; + } + let arg = parser.evaluate_expression(&expr.args[0].expr); + arg.is_string().then_some(arg) + }); + + let arg2 = (expr.args.len() >= 2).then_some(true).and_then(|_| { + if expr.args[1].spread.is_some() { + return None; + } + let arg = parser.evaluate_expression(&expr.args[1].expr); + arg.is_number().then_some(arg) + }); + + if let Some(result) = arg1.map(|arg1| { + mock_javascript_indexof( + param.string().as_str(), + arg1.string().as_str(), + arg2.map(|a| a.number()), + ) + }) { + let mut res = BasicEvaluatedExpression::with_range(expr.span.real_lo(), expr.span.hi().0); + res.set_number(result as f64); + res.set_side_effects(param.could_have_side_effects()); + return Some(res); + } + } else if property == SLICE_METHOD_NAME && param.is_string() && expr.args.len() == 1 && expr.args[0].spread.is_none() @@ -213,3 +244,57 @@ fn test_mock_javascript_slice() { assert_eq!(mock_javascript_slice("123", -2.2), "23".to_string()); assert_eq!(mock_javascript_slice("123", -3.), "123".to_string()); } + +fn mock_javascript_indexof(str: &str, sub: &str, start: Option) -> i32 { + let mut start_pos = start.unwrap_or_default().trunc() as i32; + if start_pos < 0 { + start_pos = 0_i32; + } + if start_pos >= str.len() as i32 { + return -1_i32; + } + + if let Some(pos) = str[(start_pos as usize)..].find(sub) { + (pos as i32) + start_pos + } else { + -1_i32 + } +} + +#[test] +fn test_mock_javascript_indexof() { + assert_eq!(mock_javascript_indexof("abcdefg", "cde", None), 2_i32); + assert_eq!(mock_javascript_indexof("abcdefg", "ccc", None), -1_i32); + assert_eq!(mock_javascript_indexof("abcdefg", "abcdefg", None), 0_i32); + + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(1_f64)), + 2_i32 + ); + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(1.1_f64)), + 2_i32 + ); + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(3_f64)), + -1_i32 + ); + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(3.3_f64)), + -1_i32 + ); + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(2.9_f64)), + 2_i32 + ); + + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(7_f64)), + -1_i32 + ); + + assert_eq!( + mock_javascript_indexof("abcdefg", "cde", Some(-1_f64)), + 2_i32 + ); +} diff --git a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs index fd509c7adeb..ac9bc43e102 100644 --- a/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs +++ b/crates/rspack_plugin_javascript/src/utils/eval/eval_unary_expr.rs @@ -121,6 +121,31 @@ pub fn eval_unary_expression( eval.set_side_effects(arg.could_have_side_effects()); Some(eval) } + UnaryOp::Tilde => { + let arg = scanner.evaluate_expression(&expr.arg); + let Some(number) = arg.as_int() else { + return None; + }; + let mut eval = BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span_hi().0); + eval.set_number(!number as f64); + eval.set_side_effects(arg.could_have_side_effects()); + Some(eval) + } + UnaryOp::Minus | UnaryOp::Plus => { + let arg = scanner.evaluate_expression(&expr.arg); + let Some(number) = arg.as_number() else { + return None; + }; + let res = match &expr.op { + UnaryOp::Minus => -number, + UnaryOp::Plus => number, + _ => unreachable!(), + }; + let mut eval = BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span_hi().0); + eval.set_number(res); + eval.set_side_effects(arg.could_have_side_effects()); + Some(eval) + } _ => None, } } diff --git a/tests/webpack-test/cases/esm/import-meta/index.js b/tests/webpack-test/cases/esm/import-meta/index.js index a05abe98ac8..950f1556dc6 100644 --- a/tests/webpack-test/cases/esm/import-meta/index.js +++ b/tests/webpack-test/cases/esm/import-meta/index.js @@ -30,7 +30,7 @@ it("should return correct import.meta.url", () => { expect(import.meta.url).toBe(url); expect(import.meta["url"]).toBe(url); expect("my" + import.meta.url).toBe("my" + url); - // if (import.meta.url.indexOf("index.js") === -1) require("fail"); + if (import.meta.url.indexOf("index.js") === -1) require("fail"); }); it("should return correct import.meta.webpack", () => { diff --git a/tests/webpack-test/configCases/filename-template/script-src-filename/test.filter.js b/tests/webpack-test/configCases/filename-template/script-src-filename/test.filter.js deleted file mode 100644 index 3be456dcd23..00000000000 --- a/tests/webpack-test/configCases/filename-template/script-src-filename/test.filter.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => {return false} \ No newline at end of file