diff --git a/.cargo/config.toml b/.cargo/config.toml index a4f1b35089e5..73aaed3491f0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -20,5 +20,5 @@ rustflags = ["-C", "target-feature=+crt-static"] # Alias to build actual SWC plugin binary for the specified target. build-wasi = "build --target wasm32-wasi" build-wasm32 = "build --target wasm32-unknown-unknown" -build-swc-plugins = "build-wasi --release -p swc_plugin_compile_mode -p swc_plugin_define_config" -test-swc-plugins = "test -p swc_plugin_compile_mode -p swc_plugin_define_config" +build-swc-plugins = "build-wasi --release -p swc_plugin_compile_mode -p swc_plugin_define_config -p swc_plugin_compile_mode_pre_process" +test-swc-plugins = "test -p swc_plugin_compile_mode -p swc_plugin_define_config -p swc_plugin_compile_mode_pre_process" diff --git a/.husky/commit-msg b/.husky/commit-msg old mode 100644 new mode 100755 diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/.vscode/settings.json b/.vscode/settings.json index ebd73d16e806..5c4932a411f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,8 @@ "./crates/taro_init/Cargo.toml", "./crates/native_binding/Cargo.toml", "./crates/swc_plugin_compile_mode/Cargo.toml", - "./crates/swc_plugin_define_config/Cargo.toml" + "./crates/swc_plugin_define_config/Cargo.toml", + "./crates/swc_plugin_compile_mode_pre_process/Cargo.toml" ], "rust-analyzer.showUnlinkedFileNotification": false } diff --git a/Cargo.lock b/Cargo.lock index 80018fcb0743..87ef4ab62aa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1975,6 +1975,17 @@ dependencies = [ "swc_core", ] +[[package]] +name = "swc_plugin_compile_mode_pre_process" +version = "0.1.0" +dependencies = [ + "regex", + "rustc-hash", + "serde", + "serde_json", + "swc_core", +] + [[package]] name = "swc_plugin_define_config" version = "0.2.0" diff --git a/crates/native_binding/package.json b/crates/native_binding/package.json index 011168658f25..00a4036a00d2 100644 --- a/crates/native_binding/package.json +++ b/crates/native_binding/package.json @@ -1,6 +1,6 @@ { "name": "@tarojs/binding", - "version": "4.0.6", + "version": "4.0.7-alpha.2", "description": "Node binding for taro", "main": "binding.js", "typings": "binding.d.ts", diff --git a/crates/swc_plugin_compile_mode/.editorconfig b/crates/swc_plugin_compile_mode/.editorconfig deleted file mode 100644 index 7b227545b443..000000000000 --- a/crates/swc_plugin_compile_mode/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*.rs] -indent_style = space -indent_size = 4 - -[*.js] -trim_trailing_whitespace = false diff --git a/crates/swc_plugin_compile_mode/src/lib.rs b/crates/swc_plugin_compile_mode/src/lib.rs index 593e7e5fdd59..a137e09bcbf0 100644 --- a/crates/swc_plugin_compile_mode/src/lib.rs +++ b/crates/swc_plugin_compile_mode/src/lib.rs @@ -1,54 +1,60 @@ +use serde::Deserialize; +use std::collections::HashMap; use swc_core::{ - ecma::{ - ast::Program, - visit::{as_folder, FoldWith, VisitMut}, - }, - plugin::{ - plugin_transform, - proxies::TransformPluginProgramMetadata - } + ecma::{ + ast::Program, + visit::{as_folder, FoldWith, VisitMut}, + }, + plugin::{plugin_transform, proxies::TransformPluginProgramMetadata}, }; -use serde::{Deserialize}; -use std::collections::HashMap; -mod utils; -mod transform; -mod transform_harmony; #[cfg(test)] mod tests; +mod transform; +mod transform_harmony; +mod utils; struct SerdeDefault; impl SerdeDefault { - fn platform_default () -> String { - String::from("WEAPP") - } + fn platform_default() -> String { + String::from("WEAPP") + } + fn is_use_xs_default() -> bool { + true + } + fn template_tag_default() -> String { + String::from("") + } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct ComponentReplace { - pub current_init: String, - pub dependency_define: String, + pub current_init: String, + pub dependency_define: String, } #[derive(Deserialize, Debug)] pub struct PluginConfig { - pub tmpl_prefix: String, - #[serde(default = "SerdeDefault::platform_default")] - pub platform: String, - #[serde(default)] - pub is_harmony: bool, - #[serde(default)] - pub components: HashMap>, - #[serde(default)] - pub adapter: HashMap, - #[serde(default)] - pub support_events: Vec, - #[serde(default)] - pub support_components: Vec, - #[serde(default)] - pub event_adapter: HashMap, - #[serde(default)] - pub component_replace: HashMap, - + pub tmpl_prefix: String, + #[serde(default = "SerdeDefault::platform_default")] + pub platform: String, + #[serde(default)] + pub is_harmony: bool, + #[serde(default)] + pub components: HashMap>, + #[serde(default)] + pub adapter: HashMap, + #[serde(default)] + pub support_events: Vec, + #[serde(default)] + pub support_components: Vec, + #[serde(default)] + pub event_adapter: HashMap, + #[serde(default)] + pub component_replace: HashMap, + #[serde(default = "SerdeDefault::is_use_xs_default")] + pub is_use_xs: bool, + #[serde(default = "SerdeDefault::template_tag_default")] + pub template_tag: String, } /// An example plugin function with macro support. @@ -68,19 +74,15 @@ pub struct PluginConfig { /// Refer swc_plugin_macro to see how does it work internally. #[plugin_transform] pub fn process_transform(program: Program, metadata: TransformPluginProgramMetadata) -> Program { - let config = serde_json::from_str::( - &metadata - .get_transform_plugin_config() - .unwrap() - ) - .unwrap(); + let config = + serde_json::from_str::(&metadata.get_transform_plugin_config().unwrap()).unwrap(); - // 如果 config 中的 is_harmony 字段为 true 则走 harmony_transform, 否则则走 transform - let visitor: Box = if config.is_harmony { - Box::new(transform_harmony::TransformVisitor::new(config)) - } else { - Box::new(transform::TransformVisitor::new(config)) - }; + // 如果 config 中的 is_harmony 字段为 true 则走 harmony_transform, 否则则走 transform + let visitor: Box = if config.is_harmony { + Box::new(transform_harmony::TransformVisitor::new(config)) + } else { + Box::new(transform::TransformVisitor::new(config)) + }; - program.fold_with(&mut as_folder(visitor)) + program.fold_with(&mut as_folder(visitor)) } diff --git a/crates/swc_plugin_compile_mode/src/tests/attributes.rs b/crates/swc_plugin_compile_mode/src/tests/attributes.rs index ddc9de82f4f8..891279df343b 100644 --- a/crates/swc_plugin_compile_mode/src/tests/attributes.rs +++ b/crates/swc_plugin_compile_mode/src/tests/attributes.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_keep_static_attrs_only_in_templates, - r#" + get_syntax_config(), + |_| tr(), + should_keep_static_attrs_only_in_templates, + r#" function Index () { return ( @@ -17,10 +17,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_turn_dynamic_attrs, - r#" + get_syntax_config(), + |_| tr(), + should_turn_dynamic_attrs, + r#" function Index () { return ( @@ -37,10 +37,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_handle_events, - r#" + get_syntax_config(), + |_| tr(), + should_handle_events, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/children.rs b/crates/swc_plugin_compile_mode/src/tests/children.rs index dbc9cfa5e939..85207bbfa183 100644 --- a/crates/swc_plugin_compile_mode/src/tests/children.rs +++ b/crates/swc_plugin_compile_mode/src/tests/children.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_render_fn, - r#" + get_syntax_config(), + |_| tr(), + should_support_render_fn, + r#" function Index () { return ( @@ -19,10 +19,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_fragment, - r#" + get_syntax_config(), + |_| tr(), + should_support_fragment, + r#" function Index () { return ( @@ -54,10 +54,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_context_api, - r#" + get_syntax_config(), + |_| tr(), + should_support_context_api, + r#" function Index () { return ( @@ -77,10 +77,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_render_react_component, - r#" + get_syntax_config(), + |_| tr(), + should_render_react_component, + r#" function Index () { return ( @@ -94,10 +94,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_render_native_component, - r#" + get_syntax_config(), + |_| tr(), + should_render_native_component, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/condition.rs b/crates/swc_plugin_compile_mode/src/tests/condition.rs index e9001259505d..625799d8282b 100644 --- a/crates/swc_plugin_compile_mode/src/tests/condition.rs +++ b/crates/swc_plugin_compile_mode/src/tests/condition.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_and_expr, - r#" + get_syntax_config(), + |_| tr(), + should_support_and_expr, + r#" function Index () { return ( @@ -34,26 +34,42 @@ test!( } "# ); + test!( - get_syntax_config(), - |_| tr(), - should_support_conditional_expr, - r#" - function Index () { - return ( - - {condition ? {content} : hello} - {condition1 ? condition2 ? {a} : {b} : {c}} - {condition1 ? {a} : condition2 ? {b} : {c}} - {condition1 ? {a} : (condition2 ? {b} : {c})} - {condition1 ? condition2 && {a} : {b}} - {condition1 ? {a} : condition2 && {b}} - {condition1 ? "someText" : 789} - {condition1 ? : } - {condition1 ? {condition2 ? : } : } - - - ) - } - "# + get_syntax_config(), + |_| tr(), + should_support_conditional_expr, + r#" + function Index () { + return ( + + {condition ? {content} : hello} + {condition1 ? condition2 ? {a} : {b} : {c}} + {condition1 ? {a} : condition2 ? {b} : {c}} + {condition1 ? {a} : (condition2 ? {b} : {c})} + {condition1 ? condition2 && {a} : {b}} + {condition1 ? {a} : condition2 && {b}} + {condition1 ? "someText" : 789} + {condition1 ? : } + {condition1 ? {condition2 ? : } : } + + + ) + } + "# +); + +test!( + get_syntax_config(), + |_| tr(), + should_support_jsx_container_expr, + r#" + function Index () { + return ( + + {} + + ) + } + "# ); diff --git a/crates/swc_plugin_compile_mode/src/tests/entry.rs b/crates/swc_plugin_compile_mode/src/tests/entry.rs index de9f47355941..a588ccfd227a 100644 --- a/crates/swc_plugin_compile_mode/src/tests/entry.rs +++ b/crates/swc_plugin_compile_mode/src/tests/entry.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_multi_compile_mode, - r#" + get_syntax_config(), + |_| tr(), + should_support_multi_compile_mode, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/attributes.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/attributes.rs index 23952b0b8302..f6d974c59283 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/attributes.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/attributes.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_turn_dynamic_attrs, - r#" + get_syntax_config(), + |_| tr(), + should_turn_dynamic_attrs, + r#" function Index () { return ( @@ -25,10 +25,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_handle_events, - r#" + get_syntax_config(), + |_| tr(), + should_handle_events, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/children.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/children.rs index 341731a2aace..802f0e75f749 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/children.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/children.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_render_fn, - r#" + get_syntax_config(), + |_| tr(), + should_support_render_fn, + r#" function Index () { return ( @@ -23,10 +23,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_fragment, - r#" + get_syntax_config(), + |_| tr(), + should_support_fragment, + r#" function Index () { return ( @@ -58,10 +58,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_render_react_component, - r#" + get_syntax_config(), + |_| tr(), + should_render_react_component, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/condition.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/condition.rs index 97c264e49478..a06cd6e1993c 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/condition.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/condition.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_and_expr, - r#" + get_syntax_config(), + |_| tr(), + should_support_and_expr, + r#" function Index () { return ( @@ -22,10 +22,10 @@ test!( "# ); test!( - get_syntax_config(), - |_| tr(), - should_support_conditional_expr, - r#" + get_syntax_config(), + |_| tr(), + should_support_conditional_expr, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/entry.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/entry.rs index 340e6e6d59bd..ebe8fa4904d6 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/entry.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/entry.rs @@ -1,5 +1,5 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( get_syntax_config(), diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/looping.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/looping.rs index c4362f0c7741..0d3c09a441ee 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/looping.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/looping.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_loop_with_function_expr, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_function_expr, + r#" function Index () { return ( @@ -26,10 +26,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_loop_with_arrow_function_with_blockstmt, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_arrow_function_with_blockstmt, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/harmony/mod.rs b/crates/swc_plugin_compile_mode/src/tests/harmony/mod.rs index 858212721f97..f86994eb25d8 100644 --- a/crates/swc_plugin_compile_mode/src/tests/harmony/mod.rs +++ b/crates/swc_plugin_compile_mode/src/tests/harmony/mod.rs @@ -1,19 +1,16 @@ pub use super::get_syntax_config; +use crate::{transform_harmony::*, PluginConfig}; use swc_core::ecma::visit::{as_folder, Fold, VisitMut}; -use crate::{ - PluginConfig, - transform_harmony::*, -}; -mod entry; mod attributes; +mod children; mod condition; +mod entry; mod looping; -mod children; -pub fn tr () -> impl Fold + VisitMut { +pub fn tr() -> impl Fold + VisitMut { let config = serde_json::from_str::( - r#" + r#" { "is_harmony": true, "tmpl_prefix": "f0", @@ -37,7 +34,7 @@ pub fn tr () -> impl Fold + VisitMut { "onTouchCancel": "onTouch", "onLoad": "onComplete" } - }"# + }"#, ) .unwrap(); let visitor = TransformVisitor::new(config); diff --git a/crates/swc_plugin_compile_mode/src/tests/looping.rs b/crates/swc_plugin_compile_mode/src/tests/looping.rs index fbf9257ce957..1adc3acf43d5 100644 --- a/crates/swc_plugin_compile_mode/src/tests/looping.rs +++ b/crates/swc_plugin_compile_mode/src/tests/looping.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_loop_with_function_expr, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_function_expr, + r#" function Index () { return ( @@ -26,10 +26,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_loop_with_arrow_function_with_blockstmt, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_arrow_function_with_blockstmt, + r#" function Index () { return ( @@ -43,10 +43,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_loop_with_arrow_function_with_expr, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_arrow_function_with_expr, + r#" function Index () { return ( @@ -58,10 +58,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_nested_loop, - r#" + get_syntax_config(), + |_| tr(), + should_support_nested_loop, + r#" function Index () { return ( @@ -83,12 +83,11 @@ test!( "# ); - test!( - get_syntax_config(), - |_| tr(), - should_loop_with_fragment, - r#" + get_syntax_config(), + |_| tr(), + should_loop_with_fragment, + r#" function Index () { return ( @@ -104,12 +103,11 @@ test!( "# ); - test!( - get_syntax_config(), - |_| tr(), - should_loop_be_wrapped_when_its_not_the_only_child, - r#" + get_syntax_config(), + |_| tr(), + should_loop_be_wrapped_when_its_not_the_only_child, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/mod.rs b/crates/swc_plugin_compile_mode/src/tests/mod.rs index 1675d69f25b7..b78d8af3416c 100644 --- a/crates/swc_plugin_compile_mode/src/tests/mod.rs +++ b/crates/swc_plugin_compile_mode/src/tests/mod.rs @@ -1,24 +1,21 @@ +use crate::{transform::*, PluginConfig}; use swc_core::ecma::{ - parser, - visit::{as_folder, Fold}, -}; -use crate::{ - PluginConfig, - transform::*, + parser, + visit::{as_folder, Fold}, }; -mod entry; mod attributes; -mod shake; -mod condition; -mod looping; mod children; +mod condition; +mod entry; mod harmony; +mod looping; +mod shake; mod wxs; -pub fn tr () -> impl Fold { - let config = serde_json::from_str::( - r#" +pub fn tr() -> impl Fold { + let config = serde_json::from_str::( + r#" { "tmpl_prefix": "f0", "components": { @@ -87,16 +84,16 @@ pub fn tr () -> impl Fold { "xs": "wxs", "type": "weapp" } - }"# - ) - .unwrap(); - let visitor = TransformVisitor::new(config); - as_folder(visitor) + }"#, + ) + .unwrap(); + let visitor = TransformVisitor::new(config); + as_folder(visitor) } -pub fn get_syntax_config () -> parser::Syntax { - parser::Syntax::Es(parser::EsConfig { - jsx: true, - ..Default::default() - }) +pub fn get_syntax_config() -> parser::Syntax { + parser::Syntax::Es(parser::EsConfig { + jsx: true, + ..Default::default() + }) } diff --git a/crates/swc_plugin_compile_mode/src/tests/shake.rs b/crates/swc_plugin_compile_mode/src/tests/shake.rs index 7910b49b9351..237635dcfce9 100644 --- a/crates/swc_plugin_compile_mode/src/tests/shake.rs +++ b/crates/swc_plugin_compile_mode/src/tests/shake.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_static_jsx_being_shaked, - r#" + get_syntax_config(), + |_| tr(), + should_static_jsx_being_shaked, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/tests/wxs.rs b/crates/swc_plugin_compile_mode/src/tests/wxs.rs index 489fc8fb9091..fe1accf0117c 100644 --- a/crates/swc_plugin_compile_mode/src/tests/wxs.rs +++ b/crates/swc_plugin_compile_mode/src/tests/wxs.rs @@ -1,11 +1,11 @@ +use super::{get_syntax_config, tr}; use swc_core::ecma::transforms::testing::test; -use super::{tr, get_syntax_config}; test!( - get_syntax_config(), - |_| tr(), - should_support_wxs_children, - r#" + get_syntax_config(), + |_| tr(), + should_support_wxs_children, + r#" function Index () { return ( @@ -20,10 +20,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_wxs_attributes, - r#" + get_syntax_config(), + |_| tr(), + should_support_wxs_attributes, + r#" function Index () { return ( @@ -38,10 +38,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_wxs_work_in_multi_compile_mode, - r#" + get_syntax_config(), + |_| tr(), + should_wxs_work_in_multi_compile_mode, + r#" function Index () { return ( @@ -62,10 +62,10 @@ test!( ); test!( - get_syntax_config(), - |_| tr(), - should_support_wxs_events, - r#" + get_syntax_config(), + |_| tr(), + should_support_wxs_events, + r#" function Index () { return ( diff --git a/crates/swc_plugin_compile_mode/src/transform.rs b/crates/swc_plugin_compile_mode/src/transform.rs index 723996323344..4cbca1f71b64 100644 --- a/crates/swc_plugin_compile_mode/src/transform.rs +++ b/crates/swc_plugin_compile_mode/src/transform.rs @@ -1,781 +1,927 @@ - +use crate::utils::{self, constants::*}; +use crate::{utils::as_xscript_expr_string, PluginConfig}; +use std::collections::HashMap; use swc_core::{ - common::{ - iter::IdentifyLast, - util::take::Take, - DUMMY_SP as span, - Spanned, - }, - ecma::{ - self, - ast::*, - visit::{VisitMut, VisitMutWith}, - utils::{quote_ident, quote_str}, - }, - plugin::errors::HANDLER, - atoms::Atom, + atoms::Atom, + common::{iter::IdentifyLast, util::take::Take, Spanned, DUMMY_SP as span}, + ecma::{ + self, + ast::*, + utils::{quote_ident, quote_str}, + visit::{VisitMut, VisitMutWith}, + }, + plugin::errors::HANDLER, }; -use std::collections::HashMap; -use crate::{PluginConfig, utils::as_xscript_expr_string}; -use crate::utils::{self, constants::*}; struct PreVisitor; impl PreVisitor { - fn new () -> Self { - Self {} - } + fn new() -> Self { + Self {} + } } impl VisitMut for PreVisitor { - fn visit_mut_jsx_element_children(&mut self, children: &mut Vec) { - let len = children.len(); - - // 当 JSX 循环表达式存在兄弟节点,且这些兄弟节点中有动态节点(存在 JSX 表达式)时, - // 自动为该循环体外层包裹一个 。 - // 对应测试用例:should_loop_be_wrapped_when_its_not_the_only_child - if len > 1 { - let mut list: Vec = vec![]; - - // 收集 JSX 循环表达式到 list - children - .iter_mut() - .enumerate() - .for_each(|(i, child)| { - if let JSXElementChild::JSXExprContainer(JSXExprContainer { expr: JSXExpr::Expr(expr), .. }) = child { - if let Expr::Call(CallExpr { callee: Callee::Expr(callee_expr), args, .. }) = &mut **expr { - if utils::is_call_expr_of_loop(callee_expr, args) { - list.push(i); - } - } - } - }); - - // 遍历 list,为每个 child 的外层包裹 - fn wrap (list: Vec, children: &mut Vec) { - list.into_iter().for_each(|i| { - let child = &mut children[i]; - if let JSXElementChild::JSXExprContainer(JSXExprContainer { expr: JSXExpr::Expr(expr), .. }) = child { - let expr = expr.take(); - *child = JSXElementChild::JSXElement(Box::new(JSXElement { - span, - opening: JSXOpeningElement { - name: JSXElementName::Ident(quote_ident!("block")), - span, - attrs: vec![], - self_closing: false, - type_args: None - }, - children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { span, expr: JSXExpr::Expr(expr) })], - closing: Some(JSXClosingElement { span, name: JSXElementName::Ident(quote_ident!("block")) }) - })); - } - }); + fn visit_mut_jsx_element_children(&mut self, children: &mut Vec) { + let len = children.len(); + + // 当 JSX 循环表达式存在兄弟节点,且这些兄弟节点中有动态节点(存在 JSX 表达式)时, + // 自动为该循环体外层包裹一个 。 + // 对应测试用例:should_loop_be_wrapped_when_its_not_the_only_child + if len > 1 { + let mut list: Vec = vec![]; + + // 收集 JSX 循环表达式到 list + children.iter_mut().enumerate().for_each(|(i, child)| { + if let JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(expr), + .. + }) = child + { + if let Expr::Call(CallExpr { + callee: Callee::Expr(callee_expr), + args, + .. + }) = &mut **expr + { + if utils::is_call_expr_of_loop(callee_expr, args) { + list.push(i); } + } + } + }); + + // 遍历 list,为每个 child 的外层包裹 + fn wrap(list: Vec, children: &mut Vec) { + list.into_iter().for_each(|i| { + let child = &mut children[i]; + if let JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(expr), + .. + }) = child + { + let expr = expr.take(); + *child = JSXElementChild::JSXElement(Box::new(JSXElement { + span, + opening: JSXOpeningElement { + name: JSXElementName::Ident(quote_ident!("block")), + span, + attrs: vec![], + self_closing: false, + type_args: None, + }, + children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { + span, + expr: JSXExpr::Expr(expr), + })], + closing: Some(JSXClosingElement { + span, + name: JSXElementName::Ident(quote_ident!("block")), + }), + })); + } + }); + } + + if list.len() == 1 { + // 只有一个 JSX 循环表达式时,检查兄弟节点是否全是静态节点,如果不是则要包裹 + let mut pure = true; + let index = list[0]; + for i in 0..len { + if i != index && !utils::is_static_jsx_element_child(&children[i]) { + pure = false; + break; + } + } + if !pure { + wrap(list, children); + } + } else if list.len() > 1 { + // 有多个 JSX 循环表达式时一定要包裹 + wrap(list, children); + } + } - if list.len() == 1 { - // 只有一个 JSX 循环表达式时,检查兄弟节点是否全是静态节点,如果不是则要包裹 - let mut pure = true; - let index = list[0]; - for i in 0..len { - if i != index && !utils::is_static_jsx_element_child(&children[i]) { - pure = false; - break; - } - } - if !pure { - wrap(list, children); + children.visit_mut_children_with(self); + } + fn visit_mut_jsx_element_child(&mut self, child: &mut JSXElementChild) { + if let JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(expr), + .. + }) = child + { + if let Expr::Paren(ParenExpr { expr: e, .. }) = &mut **expr { + *expr = e.take(); + } + + match &mut **expr { + Expr::Bin(BinExpr { + op, left, right, .. + }) => { + // C&&A 替换为 C?A:A',原因是为了无论显示还是隐藏都保留一个元素,从而不影响兄弟节点的变量路径 + if *op == op!("&&") { + fn inject_compile_if(el: &mut Box, condition: &mut Box) -> () { + el.opening + .attrs + .push(utils::create_jsx_expr_attr(COMPILE_IF, condition.clone())); + } + fn get_element_double( + element_name: JSXElementName, + condition: &mut Box, + right: &mut Box, + ) -> Expr { + Expr::Cond(CondExpr { + span, + test: condition.take(), + cons: right.take(), + alt: Box::new(utils::create_self_closing_jsx_element_expr( + element_name, // element 替换为同类型的元素。在显示/隐藏切换时,让运行时 diff 只更新必要属性而不是整个节点刷新 + Some(vec![utils::create_jsx_bool_attr(COMPILE_IGNORE)]), + )), + }) + } + match &mut **right { + Expr::JSXElement(el) => { + let element_name = el.opening.name.clone(); + inject_compile_if(el, left); + **expr = get_element_double(element_name, left, right); + } + Expr::Paren(ParenExpr { + expr: paren_expr, .. + }) => { + if paren_expr.is_jsx_element() { + let el: &mut Box = paren_expr.as_mut_jsx_element().unwrap(); + let element_name = el.opening.name.clone(); + inject_compile_if(el, left); + **expr = get_element_double(element_name, left, paren_expr); } - } else if list.len() > 1 { - // 有多个 JSX 循环表达式时一定要包裹 - wrap(list, children); + } + Expr::Lit(_) => { + **expr = Expr::Cond(CondExpr { + span, + test: left.take(), + cons: right.take(), + alt: Box::new(Expr::Lit(Lit::Str(quote_str!(COMPILE_IGNORE)))), + }) + } + _ => { + let jsx_el_name = JSXElementName::Ident(quote_ident!("block")); + let mut block = Box::new(JSXElement { + span, + opening: JSXOpeningElement { + name: jsx_el_name.clone(), + span, + attrs: vec![], + self_closing: false, + type_args: None, + }, + children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { + span, + expr: JSXExpr::Expr(right.take()), + })], + closing: Some(JSXClosingElement { + span, + name: jsx_el_name.clone(), + }), + }); + inject_compile_if(&mut block, left); + **expr = + get_element_double(jsx_el_name, left, &mut Box::new(Expr::JSXElement(block))); + } } + } } - - children.visit_mut_children_with(self); - } - fn visit_mut_jsx_element_child (&mut self, child: &mut JSXElementChild) { - if let JSXElementChild::JSXExprContainer(JSXExprContainer { expr: JSXExpr::Expr(expr), .. }) = child { - if let Expr::Paren(ParenExpr { expr: e, .. }) = &mut **expr { - *expr = e.take(); + Expr::Cond(CondExpr { + test, cons, alt, .. + }) => { + let compile_if = utils::create_jsx_expr_attr(COMPILE_IF, test.clone()); + let compile_else = utils::create_jsx_bool_attr(COMPILE_ELSE); + let process_cond_arm = |arm: &mut Box, attr: JSXAttrOrSpread| match &mut **arm { + Expr::JSXElement(el) => { + el.opening.attrs.push(attr); } - - match &mut **expr { - Expr::Bin(BinExpr { op, left, right, ..}) => { - // C&&A 替换为 C?A:A',原因是为了无论显示还是隐藏都保留一个元素,从而不影响兄弟节点的变量路径 - if *op == op!("&&") { - fn inject_compile_if (el: &mut Box, condition: &mut Box) -> () { - el.opening.attrs.push(utils::create_jsx_expr_attr(COMPILE_IF, condition.clone())); - } - fn get_element_double (element_name: JSXElementName, condition: &mut Box, right: &mut Box) -> Expr { - Expr::Cond(CondExpr { - span, - test: condition.take(), - cons: right.take(), - alt: Box::new(utils::create_self_closing_jsx_element_expr( - element_name, // element 替换为同类型的元素。在显示/隐藏切换时,让运行时 diff 只更新必要属性而不是整个节点刷新 - Some(vec![utils::create_jsx_bool_attr(COMPILE_IGNORE)] - ))) - }) - } - match &mut **right { - Expr::JSXElement(el) => { - let element_name = el.opening.name.clone(); - inject_compile_if(el, left); - **expr = get_element_double(element_name, left, right); - }, - Expr::Paren(ParenExpr { expr: paren_expr, .. }) => { - if paren_expr.is_jsx_element() { - let el: &mut Box = paren_expr.as_mut_jsx_element().unwrap(); - let element_name = el.opening.name.clone(); - inject_compile_if(el, left); - **expr = get_element_double(element_name, left, paren_expr); - } - }, - Expr::Lit(_) => { - **expr = Expr::Cond(CondExpr { - span, - test: left.take(), - cons: right.take(), - alt: Box::new(Expr::Lit(Lit::Str(quote_str!(COMPILE_IGNORE)))) - }) - }, - _ => { - let jsx_el_name = JSXElementName::Ident(quote_ident!("block")); - let mut block = Box::new(JSXElement { - span, - opening: JSXOpeningElement { name: jsx_el_name.clone(), span, attrs: vec![], self_closing: false, type_args: None }, - children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { span, expr: JSXExpr::Expr(right.take()) })], - closing: Some(JSXClosingElement { span, name: jsx_el_name.clone() }) - }); - inject_compile_if(&mut block, left); - **expr = get_element_double(jsx_el_name, left, &mut Box::new(Expr::JSXElement(block))); - } - } - } - }, - Expr::Cond(CondExpr { test, cons, alt, ..}) => { - let compile_if = utils::create_jsx_expr_attr(COMPILE_IF, test.clone()); - let compile_else = utils::create_jsx_bool_attr(COMPILE_ELSE); - let process_cond_arm = |arm: &mut Box, attr: JSXAttrOrSpread| { - match &mut **arm { - Expr::JSXElement(el) => { - el.opening.attrs.push(attr); - }, - _ => { - let temp = arm.take(); - let jsx_el_name = JSXElementName::Ident(quote_ident!("block")); - **arm = Expr::JSXElement(Box::new(JSXElement { - span, - opening: JSXOpeningElement { name: jsx_el_name.clone(), span, attrs: vec![attr], self_closing: false, type_args: None }, - children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { span, expr: JSXExpr::Expr(temp)})], - closing: Some(JSXClosingElement { span, name: jsx_el_name }) - })) - } - } - }; - process_cond_arm(cons, compile_if); - process_cond_arm(alt, compile_else); + _ => { + let temp = arm.take(); + let jsx_el_name = JSXElementName::Ident(quote_ident!("block")); + **arm = Expr::JSXElement(Box::new(JSXElement { + span, + opening: JSXOpeningElement { + name: jsx_el_name.clone(), + span, + attrs: vec![attr], + self_closing: false, + type_args: None, }, - _ => (), + children: vec![JSXElementChild::JSXExprContainer(JSXExprContainer { + span, + expr: JSXExpr::Expr(temp), + })], + closing: Some(JSXClosingElement { + span, + name: jsx_el_name, + }), + })) } - - expr.visit_mut_children_with(self); - } else { - child.visit_mut_children_with(self); + }; + process_cond_arm(cons, compile_if); + process_cond_arm(alt, compile_else); } + _ => (), + } + + expr.visit_mut_children_with(self); + } else { + child.visit_mut_children_with(self); } + } } pub struct TransformVisitor { - pub config: PluginConfig, - pub is_compile_mode: bool, - pub node_stack: Vec, - pub templates: HashMap, - pub get_tmpl_name: Box String>, - pub xs_module_names: Vec, - pub xs_sources: Vec, + pub config: PluginConfig, + pub is_compile_mode: bool, + pub node_stack: Vec, + pub templates: HashMap, + pub get_tmpl_name: Box String>, + pub xs_module_names: Vec, + pub xs_sources: Vec, } impl TransformVisitor { - pub fn new (config: PluginConfig) -> Self { - let get_tmpl_name = Box::new(utils::named_iter( - format!("{}t", config.tmpl_prefix) - )); - Self { - config, - is_compile_mode: false, - node_stack: vec![], - templates: HashMap::new(), - get_tmpl_name, - xs_module_names: vec![], - xs_sources: vec![], - } + pub fn new(config: PluginConfig) -> Self { + let get_tmpl_name = Box::new(utils::named_iter(format!("{}t", config.tmpl_prefix))); + Self { + config, + is_compile_mode: false, + node_stack: vec![], + templates: HashMap::new(), + get_tmpl_name, + xs_module_names: vec![], + xs_sources: vec![], } - - fn build_xml_element (&mut self, el: &mut JSXElement) -> String { - let is_inner_component = utils::is_inner_component(&el, &self.config); - let opening_element = &mut el.opening; - - match &opening_element.name { - JSXElementName::Ident(ident) => { - if is_inner_component { - // 内置组件 - let mut name = utils::to_kebab_case(ident.as_ref()); - let attrs = self.build_xml_attrs(opening_element, &name); - if attrs.is_none() { return String::new() }; - let (children, ..) = self.build_xml_children(&mut el.children, None); - - if utils::is_xscript(&name) { - name = match self.config.adapter.get("xs") { - Some(xs) => { - xs.to_string() - }, - None => { - HANDLER.with(|handler| { - handler - .struct_span_err(el.span, "Taro CompileMode 语法错误") - .span_label(el.span, "当前小程序平台不支持 xs 语法") - .emit(); - panic!() - }) - } - }; - } - - format!("<{}{}>{}", name, attrs.unwrap_or_default(), children, name) - } else { - // 回退到旧的渲染模式(React 组件、原生自定义组件) - let node_path = self.get_current_node_path(); - format!(r#"