diff --git a/Cargo.lock b/Cargo.lock index 51e3189..793dad5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6061,4 +6061,4 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.48", -] +] \ No newline at end of file diff --git a/crates/swc_change_package_import/src/change_package_import.rs b/crates/swc_change_package_import/src/change_package_import.rs index ce79792..8a178c3 100644 --- a/crates/swc_change_package_import/src/change_package_import.rs +++ b/crates/swc_change_package_import/src/change_package_import.rs @@ -1,10 +1,10 @@ use swc_core::{ - common::{util::take::Take, DUMMY_SP}, + common::DUMMY_SP, ecma::{ ast::*, atoms::JsWord, utils::{quote_str, swc_ecma_ast::ImportSpecifier}, - visit::{VisitMut, VisitMutWith}, + visit::{noop_fold_type, Fold, FoldWith}, }, }; @@ -13,142 +13,127 @@ use crate::config::{Config, ImportType}; pub struct ModuleImportVisitor { // 用户配置 pub options: Vec, - // 全新生成的导入声明 - new_stmts: Vec, } impl ModuleImportVisitor { pub fn new(options: Vec) -> Self { - Self { - options, - new_stmts: vec![], - } + Self { options } } } -impl VisitMut for ModuleImportVisitor { - fn visit_mut_import_decl(&mut self, import_decl: &mut ImportDecl) { - import_decl.visit_mut_children_with(self); - - for option in &self.options { - match option { - Config::LiteralConfig(src) => { - if is_hit_rule(import_decl, option) { - for specifier in &import_decl.specifiers { - match specifier { - ImportSpecifier::Named(named_import_spec) => { - let mut import_new_src = src.clone(); - import_new_src.push_str("/"); - import_new_src.push_str(&get_import_module_name(named_import_spec)); - - self.new_stmts.push(create_default_import_decl( - import_new_src, - named_import_spec.local.clone(), - )); +impl Fold for ModuleImportVisitor { + noop_fold_type!(); + + fn fold_module_items(&mut self, items: Vec) -> Vec { + let mut new_items: Vec = vec![]; + + for item in items { + let item = item.fold_children_with(self); + let mut hit_rule = false; + if let ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) = &item { + for option in &self.options { + match option { + Config::LiteralConfig(src) => { + if is_hit_rule(import_decl, option) { + hit_rule = true; + for specifier in &import_decl.specifiers { + match specifier { + ImportSpecifier::Named(named_import_spec) => { + let mut import_new_src = src.clone(); + import_new_src.push_str("/"); + import_new_src.push_str(&get_import_module_name(named_import_spec)); + + new_items.push(create_default_import_decl( + import_new_src, + named_import_spec.local.clone(), + )); + } + _ => (), + } } - _ => (), + break; } } - // 清空当前导入声明,否则会被作为有效的声明添加至 new_stmts 中 - import_decl.take(); - break; - } - } - Config::SpecificConfig(config) => { - if is_hit_rule(import_decl, option) { - let target_fields: Vec<&String> = config.map.keys().clone().collect(); - let mut named_import_spec_copy = import_decl.clone(); - - // 获取未命中规则的导入声明 - named_import_spec_copy.specifiers = named_import_spec_copy - .specifiers - .into_iter() - .filter(|specifier| match specifier { - ImportSpecifier::Named(named_import_spec) => { - let import_object_name = get_import_module_name(named_import_spec); - !target_fields.contains(&&import_object_name) + Config::SpecificConfig(config) => { + if is_hit_rule(import_decl, option) { + hit_rule = true; + let target_fields: Vec<&String> = config.map.keys().clone().collect(); + let mut named_import_spec_copy = import_decl.clone(); + + named_import_spec_copy.specifiers = named_import_spec_copy + .specifiers + .into_iter() + .filter(|specifier| match specifier { + ImportSpecifier::Named(named_import_spec) => { + let import_object_name = get_import_module_name(named_import_spec); + !target_fields.contains(&&import_object_name) + } + _ => true, + }) + .collect::>(); + + if named_import_spec_copy.specifiers.len() != 0 { + // It no need to redirect import source, if some specifiers are not configured. + new_items.push(item.clone()); + break; } - _ => true, - }) - .collect::>(); - - if named_import_spec_copy.specifiers.len() != 0 { - self - .new_stmts - .push(ModuleItem::ModuleDecl(ModuleDecl::Import( - named_import_spec_copy, - ))); - } - - for (target, rules) in config.map.iter() { - for specifier in &import_decl.specifiers { - match specifier { - ImportSpecifier::Named(named_import_spec) => { - let import_object_name = get_import_module_name(named_import_spec); - if target == &import_object_name { - let new_import_decl: ModuleItem; - if rules.import_type.is_none() - || match rules.import_type.as_ref().unwrap() { - ImportType::Default => true, - _ => false, - } - { - // Default import mode - new_import_decl = create_default_import_decl( - rules.to.to_string(), - named_import_spec.local.clone(), - ); - } else { - // Named import mode - let mut named_import_spec_copy = named_import_spec.clone(); - - if rules.name.is_some() { - named_import_spec_copy.imported = Some(ModuleExportName::Str(Str { - span: named_import_spec.span, - value: rules.name.clone().unwrap().into(), - raw: Some(rules.name.clone().unwrap().clone().into()), - })) + for specifier in &import_decl.specifiers { + for (target, rules) in config.map.iter() { + match specifier { + ImportSpecifier::Named(named_import_spec) => { + let import_object_name = get_import_module_name(named_import_spec); + if target == &import_object_name { + let new_import_decl: ModuleItem; + if rules.import_type.is_none() + || match rules.import_type.as_ref().unwrap() { + ImportType::Default => true, + _ => false, + } + { + // Default import mode + new_import_decl = create_default_import_decl( + rules.to.to_string(), + named_import_spec.local.clone(), + ); + } else { + // Named import mode + let mut named_import_spec_copy = named_import_spec.clone(); + + if rules.name.is_some() { + named_import_spec_copy.imported = Some(ModuleExportName::Str(Str { + span: named_import_spec.span, + value: rules.name.clone().unwrap().into(), + raw: Some(rules.name.clone().unwrap().clone().into()), + })) + } + + new_import_decl = create_named_import_decl( + rules.to.to_string(), + vec![ImportSpecifier::Named(named_import_spec_copy)], + ); + } + + new_items.push(new_import_decl); } - - new_import_decl = create_named_import_decl( - rules.to.to_string(), - vec![ImportSpecifier::Named(named_import_spec_copy)], - ); } - - self.new_stmts.push(new_import_decl); + _ => (), } } - _ => (), } + break; } } - import_decl.take(); - break; } } - } - } - - if !is_empty_decl(import_decl) { - self - .new_stmts - .push(wrap_with_moudle_item(import_decl.clone())); - } - } - - fn visit_mut_module_item(&mut self, n: &mut ModuleItem) { - n.visit_mut_children_with(self); - if !n.is_module_decl() { - self.new_stmts.push(n.clone()); + if !hit_rule { + new_items.push(item); + } + } else { + new_items.push(item); + } } - } - - fn visit_mut_module_items(&mut self, stmts: &mut Vec) { - stmts.visit_mut_children_with(self); - - *stmts = self.new_stmts.clone(); + new_items } } @@ -169,10 +154,6 @@ fn is_hit_rule(cur_import: &ImportDecl, rule: &Config) -> bool { } } -fn is_empty_decl(decl: &ImportDecl) -> bool { - decl.specifiers.len() == 0 && decl.src.value == JsWord::from("".to_string()) -} - fn get_import_module_name(named_import_spec: &ImportNamedSpecifier) -> String { if named_import_spec.imported.is_none() { (&named_import_spec.local.sym).to_string() diff --git a/crates/swc_change_package_import/tests/fixture.rs b/crates/swc_change_package_import/tests/fixture.rs index 9623887..5b80eda 100644 --- a/crates/swc_change_package_import/tests/fixture.rs +++ b/crates/swc_change_package_import/tests/fixture.rs @@ -1,24 +1,17 @@ use std::collections::HashMap; use std::path::PathBuf; -use swc_core::ecma::{ - transforms::testing::{test_fixture, FixtureTestConfig}, - visit::as_folder, -}; use swc_change_package_import::{ Config, ImportType, MapProperty, ModuleImportVisitor, SpecificConfigs, }; +use swc_core::ecma::transforms::testing::{test_fixture, FixtureTestConfig}; #[testing::fixture("tests/fixture/single_literal_transform/input.js")] fn test_single_literal_transform(input: PathBuf) { let output = input.with_file_name("output.js"); test_fixture( Default::default(), - &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::LiteralConfig( - String::from("y"), - )])) - }, + &|_t| ModuleImportVisitor::new(vec![Config::LiteralConfig(String::from("y"))]), &input, &output, FixtureTestConfig { @@ -33,10 +26,10 @@ fn test_multi_literal_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![ + ModuleImportVisitor::new(vec![ Config::LiteralConfig(String::from("z")), Config::LiteralConfig(String::from("o")), - ])) + ]) }, &input, &output, @@ -53,19 +46,17 @@ fn test_single_specific_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("y"), - map: HashMap::from([( - "x".to_string(), - MapProperty { - to: String::from("m/n"), - import_type: Some(ImportType::Named), - name: Some(String::from("a")), - }, - )]), - }, - )])) + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("y"), + map: HashMap::from([( + "x".to_string(), + MapProperty { + to: String::from("m/n"), + import_type: Some(ImportType::Named), + name: Some(String::from("a")), + }, + )]), + })]) }, &input, &output, @@ -81,19 +72,17 @@ fn test_single_specific_transform_2(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("y"), - map: HashMap::from([( - "x".to_string(), - MapProperty { - to: String::from("m/n"), - import_type: Some(ImportType::Named), - name: Some(String::from("a")), - }, - )]), - }, - )])) + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("y"), + map: HashMap::from([( + "x".to_string(), + MapProperty { + to: String::from("m/n"), + import_type: Some(ImportType::Named), + name: Some(String::from("a")), + }, + )]), + })]) }, &input, &output, @@ -109,7 +98,7 @@ fn test_mix_specific_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![ + ModuleImportVisitor::new(vec![ Config::LiteralConfig(String::from("antd")), Config::SpecificConfig(SpecificConfigs { name: String::from("ice"), @@ -122,7 +111,7 @@ fn test_mix_specific_transform(input: PathBuf) { }, )]), }), - ])) + ]) }, &input, &output, @@ -132,25 +121,72 @@ fn test_mix_specific_transform(input: PathBuf) { ); } -#[testing::fixture("tests/fixture/ice_basic_transform/input.js")] -fn test_ice_basic_transform(input: PathBuf) { +#[testing::fixture("tests/fixture/multi_specific_transform/input.js")] +fn test_multi_specific_transform(input: PathBuf) { let output = input.with_file_name("output.js"); test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), + ModuleImportVisitor::new(vec![ + Config::SpecificConfig(SpecificConfigs { + name: String::from("e"), + map: HashMap::from([ + ( + "a".to_string(), + MapProperty { + to: String::from("@e/x"), + import_type: Some(ImportType::Default), + name: None, + }, + ), + ( + "b".to_string(), + MapProperty { + to: String::from("e"), + import_type: Some(ImportType::Named), + name: None, + }, + ), + ]), + }), + Config::SpecificConfig(SpecificConfigs { + name: String::from("k"), map: HashMap::from([( - "runApp".to_string(), + "j".to_string(), MapProperty { - to: String::from("@ice/runtime"), + to: String::from("@e/k"), import_type: Some(ImportType::Named), - name: None, + name: Some(String::from("jj")), }, )]), - }, - )])) + }), + ]) + }, + &input, + &output, + FixtureTestConfig { + ..Default::default() + }, + ); +} + +#[testing::fixture("tests/fixture/ice_basic_transform/input.js")] +fn test_ice_basic_transform(input: PathBuf) { + let output = input.with_file_name("output.js"); + test_fixture( + Default::default(), + &|_t| { + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([( + "runApp".to_string(), + MapProperty { + to: String::from("@ice/runtime"), + import_type: Some(ImportType::Named), + name: None, + }, + )]), + })]) }, &input, &output, @@ -166,19 +202,17 @@ fn test_ice_as_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), - map: HashMap::from([( - "runApp".to_string(), - MapProperty { - to: String::from("@ice/runtime"), - import_type: Some(ImportType::Named), - name: None, - }, - )]), - }, - )])) + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([( + "runApp".to_string(), + MapProperty { + to: String::from("@ice/runtime"), + import_type: Some(ImportType::Named), + name: None, + }, + )]), + })]) }, &input, &output, @@ -194,19 +228,17 @@ fn test_ice_alias_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), - map: HashMap::from([( - "Head".to_string(), - MapProperty { - to: String::from("react-helmet"), - import_type: Some(ImportType::Default), - name: Some("Helmet".to_string()), - }, - )]), - }, - )])) + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([( + "Head".to_string(), + MapProperty { + to: String::from("react-helmet"), + import_type: Some(ImportType::Default), + name: Some("Helmet".to_string()), + }, + )]), + })]) }, &input, &output, @@ -222,19 +254,61 @@ fn test_ice_alias_with_as_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), - map: HashMap::from([( - "Head".to_string(), + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([( + "Head".to_string(), + MapProperty { + to: String::from("react-helmet"), + import_type: Some(ImportType::Default), + name: None, + }, + )]), + })]) + }, + &input, + &output, + FixtureTestConfig { + ..Default::default() + }, + ); +} + +#[testing::fixture("tests/fixture/ice_multiple_transform/input.js")] +fn test_ice_multiple_transform(input: PathBuf) { + let output = input.with_file_name("output.js"); + test_fixture( + Default::default(), + &|_t| { + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([ + ( + "request".to_string(), MapProperty { - to: String::from("react-helmet"), + to: String::from("axios"), + import_type: Some(ImportType::Named), + name: None, + }, + ), + ( + "test".to_string(), + MapProperty { + to: String::from("axios"), + import_type: Some(ImportType::Named), + name: None, + }, + ), + ( + "store".to_string(), + MapProperty { + to: String::from("@ice/store"), import_type: Some(ImportType::Default), name: None, }, - )]), - }, - )])) + ), + ]), + })]) }, &input, &output, @@ -250,19 +324,27 @@ fn test_ice_matched_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), - map: HashMap::from([( + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([ + ( "runApp".to_string(), MapProperty { to: String::from("@ice/runtime"), import_type: Some(ImportType::Named), name: None, }, - )]), - }, - )])) + ), + ( + "defineDataLoader".to_string(), + MapProperty { + to: String::from("@ice/runtime"), + import_type: Some(ImportType::Named), + name: None, + }, + ), + ]), + })]) }, &input, &output, @@ -278,19 +360,17 @@ fn test_ice_miss_match_transform(input: PathBuf) { test_fixture( Default::default(), &|_t| { - as_folder(ModuleImportVisitor::new(vec![Config::SpecificConfig( - SpecificConfigs { - name: String::from("ice"), - map: HashMap::from([( - "runApp".to_string(), - MapProperty { - to: String::from("@ice/runtime"), - import_type: Some(ImportType::Named), - name: None, - }, - )]), - }, - )])) + ModuleImportVisitor::new(vec![Config::SpecificConfig(SpecificConfigs { + name: String::from("ice"), + map: HashMap::from([( + "runApp".to_string(), + MapProperty { + to: String::from("@ice/runtime"), + import_type: Some(ImportType::Named), + name: None, + }, + )]), + })]) }, &input, &output, diff --git a/crates/swc_change_package_import/tests/fixture/ice_matched_transform/output.js b/crates/swc_change_package_import/tests/fixture/ice_matched_transform/output.js index 842e852..9d8c759 100644 --- a/crates/swc_change_package_import/tests/fixture/ice_matched_transform/output.js +++ b/crates/swc_change_package_import/tests/fixture/ice_matched_transform/output.js @@ -1,2 +1,2 @@ -import { defineDataLoader } from "ice"; -import { runApp } from "@ice/runtime"; \ No newline at end of file +import { runApp } from "@ice/runtime"; +import { defineDataLoader } from "@ice/runtime"; \ No newline at end of file diff --git a/crates/swc_change_package_import/tests/fixture/ice_multiple_transform/output.js b/crates/swc_change_package_import/tests/fixture/ice_multiple_transform/output.js index d305c41..de0b219 100644 --- a/crates/swc_change_package_import/tests/fixture/ice_multiple_transform/output.js +++ b/crates/swc_change_package_import/tests/fixture/ice_multiple_transform/output.js @@ -1,3 +1,3 @@ import { request } from "axios"; +import store from "@ice/store"; import { test } from "axios"; -import store from "@ice/store"; \ No newline at end of file diff --git a/crates/swc_change_package_import/tests/fixture/multi_specific_transform/output.js b/crates/swc_change_package_import/tests/fixture/multi_specific_transform/output.js index 732b909..afcacdc 100644 --- a/crates/swc_change_package_import/tests/fixture/multi_specific_transform/output.js +++ b/crates/swc_change_package_import/tests/fixture/multi_specific_transform/output.js @@ -1,7 +1,4 @@ -import { c as d } from "e"; -import a from "@e/x"; -import { b } from "e"; -import { q } from "k"; // import "f"; +import { a, b, c as d } from "e"; +import { j, q } from "k"; // import "f"; // import g from "k"; - // import y from "z"; -import { jj as j } from "@e/k"; + // import y from "z"; \ No newline at end of file