diff --git a/crates/rspack_plugin_javascript/src/ast/mod.rs b/crates/rspack_plugin_javascript/src/ast/mod.rs index 201c08ddfa1..1b5e6142a00 100644 --- a/crates/rspack_plugin_javascript/src/ast/mod.rs +++ b/crates/rspack_plugin_javascript/src/ast/mod.rs @@ -4,6 +4,5 @@ mod stringify; pub use minify::minify; pub use parse::parse; -pub use parse::parse_js_code; pub use stringify::print; pub use stringify::stringify; diff --git a/crates/rspack_plugin_javascript/src/ast/parse.rs b/crates/rspack_plugin_javascript/src/ast/parse.rs index 5a2dfb34eef..faefea1c5ac 100644 --- a/crates/rspack_plugin_javascript/src/ast/parse.rs +++ b/crates/rspack_plugin_javascript/src/ast/parse.rs @@ -1,4 +1,3 @@ -use std::path::Path; use std::sync::Arc; use rspack_core::{ast::javascript::Ast, ModuleType}; @@ -11,7 +10,7 @@ use swc_core::ecma::parser::{ }; use swc_node_comments::SwcComments; -use crate::utils::{ecma_parse_error_to_rspack_error, syntax_by_module_type}; +use crate::utils::ecma_parse_error_to_rspack_error; use crate::IsModule; fn module_type_to_is_module(value: &ModuleType) -> IsModule { @@ -96,26 +95,3 @@ pub fn parse( )), } } - -pub fn parse_js_code(js_code: String, module_type: &ModuleType) -> Result { - let filename = "".to_string(); - let syntax = syntax_by_module_type(Path::new(&filename), module_type, false); - let cm: Arc = Default::default(); - let fm = cm.new_source_file(FileName::Custom(filename), js_code); - - match parse_js( - fm.clone(), - ast::EsVersion::Es2022, - syntax, - module_type_to_is_module(module_type), - None, - ) { - Ok(program) => Ok(program), - Err(errs) => Err(Error::BatchErrors( - errs - .into_iter() - .map(|err| ecma_parse_error_to_rspack_error(err, &fm, module_type)) - .collect::>(), - )), - } -} diff --git a/crates/rspack_plugin_javascript/src/visitors/mod.rs b/crates/rspack_plugin_javascript/src/visitors/mod.rs index 7256ba06f0d..7e04fce62ad 100644 --- a/crates/rspack_plugin_javascript/src/visitors/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/mod.rs @@ -93,7 +93,7 @@ pub fn run_before_pass( should_transform_by_react ), Optional::new( - swc_visitor::fold_react_refresh(), + swc_visitor::fold_react_refresh(unresolved_mark), should_transform_by_react && options.builtins.react.refresh.is_some() ), either!( diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs index dbb9061ce2e..40c7608fcfd 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs @@ -1,15 +1,17 @@ use std::sync::Arc; -use once_cell::sync::Lazy; -use rspack_core::{ModuleType, ReactOptions}; +use rspack_core::ReactOptions; +use swc_core::common::DUMMY_SP; use swc_core::common::{comments::SingleThreadedComments, Mark, SourceMap}; -use swc_core::ecma::ast::{CallExpr, Callee, Expr, Ident, ModuleItem, Program, Script}; +use swc_core::ecma::ast::{ + BinExpr, BinaryOp, BlockStmt, CallExpr, Callee, Expr, ExprOrSpread, ExprStmt, FnDecl, FnExpr, + Function, Ident, MemberExpr, ModuleItem, Program, Stmt, +}; use swc_core::ecma::transforms::react::RefreshOptions; use swc_core::ecma::transforms::react::{react as swc_react, Options}; +use swc_core::ecma::utils::{member_expr, quote_ident, quote_str}; use swc_core::ecma::visit::{noop_visit_type, Fold, Visit, VisitWith}; -use crate::ast::parse_js_code; - pub fn react<'a>( top_level_mark: Mark, comments: Option<&'a SingleThreadedComments>, @@ -41,8 +43,8 @@ pub fn react<'a>( ) } -pub fn fold_react_refresh() -> impl Fold { - ReactHmrFolder {} +pub fn fold_react_refresh(unresolved_mark: Mark) -> impl Fold { + ReactHmrFolder { unresolved_mark } } #[derive(Default)] @@ -80,24 +82,146 @@ impl Visit for ReactRefreshUsageFinder { } } -// __webpack_require__.$ReactRefreshRuntime$ is injected by the react-refresh additional entry -// See https://github.com/web-infra-dev/rspack/pull/2714 why we have a promise here -static RUNTIME_CODE: &str = r#" -function $RefreshReg$(type, id) { - __webpack_modules__.$ReactRefreshRuntime$.register(type, __webpack_module__.id+ "_" + id); +// $ReactRefreshRuntime$ is injected by provide +fn create_react_refresh_runtime_stmts(unresolved_mark: Mark) -> Vec { + fn create_react_refresh_runtime_ident(unresolved_mark: Mark) -> Box { + Box::new(Expr::Ident(Ident { + span: DUMMY_SP.apply_mark(unresolved_mark), + sym: "$ReactRefreshRuntime$".into(), + optional: false, + })) + } + vec![ + FnDecl { + ident: quote_ident!("$RefreshReg$"), + declare: false, + function: Box::new(Function { + params: vec![quote_ident!("type").into(), quote_ident!("id").into()], + decorators: Vec::new(), + span: DUMMY_SP, + body: Some(BlockStmt { + span: DUMMY_SP, + stmts: vec![Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: CallExpr { + span: DUMMY_SP, + callee: Callee::Expr( + MemberExpr { + span: DUMMY_SP, + obj: create_react_refresh_runtime_ident(unresolved_mark), + prop: quote_ident!("register").into(), + } + .into(), + ), + args: vec![ + ExprOrSpread { + spread: None, + expr: quote_ident!("type").into(), + }, + ExprOrSpread { + spread: None, + expr: BinExpr { + span: DUMMY_SP, + op: BinaryOp::Add, + left: BinExpr { + span: DUMMY_SP, + op: BinaryOp::Add, + left: member_expr!(DUMMY_SP, __webpack_module__), + right: quote_str!("_").into(), + } + .into(), + right: quote_ident!("id").into(), + } + .into(), + }, + ], + type_args: None, + } + .into(), + })], + }), + is_generator: false, + is_async: false, + type_params: None, + return_type: None, + }), + } + .into(), + ExprStmt { + span: DUMMY_SP, + expr: CallExpr { + span: DUMMY_SP, + callee: Callee::Expr( + MemberExpr { + span: DUMMY_SP, + obj: CallExpr { + span: DUMMY_SP, + // See https://github.com/web-infra-dev/rspack/pull/2714 why we have a promise here + callee: member_expr!(DUMMY_SP, Promise.resolve).into(), + args: vec![], + type_args: None, + } + .into(), + prop: quote_ident!("then").into(), + } + .into(), + ), + args: vec![ExprOrSpread { + spread: None, + expr: FnExpr { + ident: None, + function: Box::new(Function { + params: Vec::new(), + decorators: Vec::new(), + span: DUMMY_SP, + body: Some(BlockStmt { + span: DUMMY_SP, + stmts: vec![Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: CallExpr { + span: DUMMY_SP, + callee: Callee::Expr( + MemberExpr { + span: DUMMY_SP, + obj: create_react_refresh_runtime_ident(unresolved_mark), + prop: quote_ident!("refresh").into(), + } + .into(), + ), + args: vec![ + ExprOrSpread { + spread: None, + expr: member_expr!(DUMMY_SP, __webpack_module__.id), + }, + ExprOrSpread { + spread: None, + expr: member_expr!(DUMMY_SP, __webpack_module__.hot), + }, + ], + type_args: None, + } + .into(), + })], + }), + is_generator: false, + is_async: false, + type_params: None, + return_type: None, + }), + } + .into(), + }], + type_args: None, + } + .into(), + } + .into(), + ] } -Promise.resolve().then(function(){ - __webpack_modules__.$ReactRefreshRuntime$.refresh(__webpack_module__.id, __webpack_module__.hot); -}) -"#; - -static RUNTIME_CODE_AST: Lazy