Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Finish the change import statement feature #19

Merged
merged 13 commits into from
Jan 18, 2024
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ lint = "clippy --workspace --all-targets -- --deny warnings"
# AKA `test-update`, handy cargo rst update without install `cargo-rst` binary
t = "test --no-fail-fast"
tu = "run -p cargo-rst -- update"
build-wasi = "build --target wasm32-wasi"
build-wasm32 = "build --target wasm32-unknown-unknown"

[target.'cfg(all())']
rustflags = [
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,5 @@ $RECYCLE.BIN/
*.node

.rspack_crates/
node_modules/
node_modules/
.idea/
libin-code marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ members = [
"crates/swc_env_replacement",
"crates/swc_keep_export",
"crates/swc_remove_export",
"crates/swc_named_import_transform",
"crates/swc_optimize_barrel",
"crates/plugin_specilize_module_name"
"crates/plugin_specilize_module_name",
"crates/swc_plugin_change_package_import",
]
resolver = "2"

Expand Down Expand Up @@ -80,6 +80,7 @@ swc_html = { version = "=0.134.29" }
swc_html_minifier = { version = "=0.131.29" }
swc_node_comments = { version = "=0.20.9" }
tikv-jemallocator = { version = "=0.5.4", features = ["disable_initial_exec_tls"] }
testing = { version = "0.35.13" }

[profile.dev]
codegen-units = 16 # debug build will cause runtime panic if codegen-unints is default
Expand Down
3 changes: 3 additions & 0 deletions crates/swc_plugin_change_package_import/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
^target/
target
22 changes: 22 additions & 0 deletions crates/swc_plugin_change_package_import/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "swc_plugin_change_package_import"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
lto = true

[dependencies]
serde = { workspace = true }
swc_core = { workspace = true, features = [
"ecma_plugin_transform",
"ecma_ast",
"common",
"ecma_utils",
] }

[dev-dependencies]
testing = "0.35.11"
14 changes: 14 additions & 0 deletions crates/swc_plugin_change_package_import/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "plugin_optimize_package_import",
"version": "0.1.0",
"description": "",
"author": "",
"license": "ISC",
"keywords": ["swc-plugin"],
"main": "target/wasm32-wasi/release/plugin_optimize_package_import.wasm",
"scripts": {
"prepublishOnly": "cargo build-wasi --release"
},
"files": [],
"preferUnplugged": true
}
207 changes: 207 additions & 0 deletions crates/swc_plugin_change_package_import/src/change_package_import.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use crate::config::{Config, ImportType};
use swc_core::{
common::{
DUMMY_SP,
util::{
take::Take
},
},
ecma::{
ast::*,
atoms::{JsWord},
visit::{VisitMut, VisitMutWith},
utils::{quote_str, swc_ecma_ast::ImportSpecifier},
},
};

pub struct ModuleImportVisitor {
// 用户配置
pub options: Vec<Config>,
// 全新生成的导入声明
new_stmts: Vec<ModuleItem>,
}

impl ModuleImportVisitor {
pub fn new(options: Vec<Config>) -> Self {
Self {
options,
new_stmts: vec![],
}
}
}

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()));
}
_ => ()
}
}
// 清空当前导入声明,否则会被作为有效的声明添加至 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)
}
_ => true
}
}).collect::<Vec<_>>();

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()),
}))
}

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);
}
}
_ => ()
}
}
}
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());
}
}

fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
stmts.visit_mut_children_with(self);

*stmts = self.new_stmts.clone();
}
}

fn is_hit_rule(cur_import: &ImportDecl, rule: &Config) -> bool {
match rule {
Config::LiteralConfig(s) => {
if cur_import.src.value == JsWord::from(s.clone()) {
return true;
}
false
}
Config::SpecificConfig(s) => {
if cur_import.src.value == JsWord::from(s.name.clone()) {
return true;
}
false
}
}
}

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()
} else {
match &named_import_spec.imported.clone().unwrap() {
ModuleExportName::Ident(ident) => {
(&ident.sym).to_string()
}
ModuleExportName::Str(str) => {
(&str.value).to_string()
}
}
}
}

fn create_default_import_decl(src: String, local: Ident) -> ModuleItem {
wrap_with_moudle_item(
ImportDecl {
src: Box::new(quote_str!(src)),
specifiers: vec![ImportSpecifier::Default(
ImportDefaultSpecifier {
span: DUMMY_SP,
local,
}
)],
span: DUMMY_SP,
type_only: false,
with: None,
}
)
}

fn create_named_import_decl(src: String, specifiers: Vec<ImportSpecifier>) -> ModuleItem {
wrap_with_moudle_item(
ImportDecl {
src: Box::new(quote_str!(src)),
specifiers,
span: DUMMY_SP,
type_only: false,
with: None,
}
)
}

fn wrap_with_moudle_item(import_decl: ImportDecl) -> ModuleItem {
ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl))
}
62 changes: 62 additions & 0 deletions crates/swc_plugin_change_package_import/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::collections::HashMap;

#[derive(Debug)]
pub enum Config {
/// 配置:
/// ```rs
/// Config::LiteralConfig(String::from("antd"))
/// ```
/// 效果:
/// ```js
/// import { Button } from "antd";
/// // --->
/// import Button from "antd/Button";
/// ```
LiteralConfig(String),
/// 配置:
/// ```rs
/// Config::SpecificConfig(
// SpecificConfigs {
// name: String::from("ice"),
// map: HashMap::from([
// (
// "a".to_string(),
// MapProperty {
// to: String::from("@ice/x/y"),
// import_type: None,
// name: None,
// }
// ),
// ]),
// }
// ),
/// ```
/// 效果:
/// ```js
/// import { a } from "ice";
/// // --->
/// import a from "@ice/x/y";
/// ```
///
/// 更多配置请参考[文档](https://alidocs.dingtalk.com/i/nodes/20eMKjyp810mMdK4Ho1LpqX7JxAZB1Gv?utm_scene=team_space)
SpecificConfig(SpecificConfigs),
}

#[derive(Debug)]
pub struct SpecificConfigs {
pub name: String,
pub map: HashMap<String, MapProperty>,
}

#[derive(Debug)]
pub struct MapProperty {
pub to: String,
pub import_type: Option<ImportType>,
pub name: Option<String>,
}

#[derive(Debug, PartialEq)]
pub enum ImportType {
Named,
Default,
}
5 changes: 5 additions & 0 deletions crates/swc_plugin_change_package_import/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod config;
mod change_package_import;

pub use config::*;
pub use change_package_import::*;
Loading