From c0dc6bd77fb4559405cd05b79a96cfd8a773933e Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Mon, 18 Nov 2024 10:56:15 +0800 Subject: [PATCH 1/8] wip: new plugin: remove-side-effect --- .vscode/settings.json | 4 ++ Cargo.lock | 15 ++++++ Cargo.toml | 3 +- packages/remove-side-effect/.cargo/config | 5 ++ packages/remove-side-effect/Cargo.toml | 26 ++++++++++ packages/remove-side-effect/package.json | 14 ++++++ packages/remove-side-effect/src/lib.rs | 47 +++++++++++++++++++ packages/remove-side-effect/tests/fixtrue.rs | 45 ++++++++++++++++++ .../tests/fixture/base/input.js | 9 ++++ .../tests/fixture/base/output.js | 6 +++ 10 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 packages/remove-side-effect/.cargo/config create mode 100644 packages/remove-side-effect/Cargo.toml create mode 100644 packages/remove-side-effect/package.json create mode 100644 packages/remove-side-effect/src/lib.rs create mode 100644 packages/remove-side-effect/tests/fixtrue.rs create mode 100644 packages/remove-side-effect/tests/fixture/base/input.js create mode 100644 packages/remove-side-effect/tests/fixture/base/output.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ffb2410 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + // cause we use a old version of rust, so we need to add this option to make rust-analyzer work + "rust-analyzer.cargo.extraArgs": ["-Z=unstable-options"] +} diff --git a/Cargo.lock b/Cargo.lock index eec2c05..cb474d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1360,6 +1360,7 @@ dependencies = [ "swc_atoms", "swc_common", "swc_ecma_ast", + "swc_ecma_codegen", "swc_ecma_parser", "swc_ecma_transforms_base", "swc_ecma_transforms_testing", @@ -1663,6 +1664,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "swc_plugin_remove_side_effect" +version = "0.0.1" +dependencies = [ + "easy-error", + "fxhash", + "serde", + "serde_json", + "swc_common", + "swc_core", + "testing", + "tracing", +] + [[package]] name = "swc_trace_macro" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 0a19700..0fcaa17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ "packages/remove-export", "packages/keep-platform", "packages/keep-export", - "packages/node-transform" + "packages/node-transform", + "packages/remove-side-effect" ] [profile.release] diff --git a/packages/remove-side-effect/.cargo/config b/packages/remove-side-effect/.cargo/config new file mode 100644 index 0000000..eaa0fee --- /dev/null +++ b/packages/remove-side-effect/.cargo/config @@ -0,0 +1,5 @@ +# These command aliases are not final, may change +[alias] +# Alias to build actual plugin binary for the specified target. +build-wasi = "build --target wasm32-wasi" +build-wasm32 = "build --target wasm32-unknown-unknown" diff --git a/packages/remove-side-effect/Cargo.toml b/packages/remove-side-effect/Cargo.toml new file mode 100644 index 0000000..00b5f9f --- /dev/null +++ b/packages/remove-side-effect/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "swc_plugin_remove_side_effect" +version = "0.0.1" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +serde = "1" +fxhash= "0.2.1" +easy-error = "1.0.0" +tracing = { version="0.1.34", features = ["release_max_level_info"] } +swc_core = {version = "0.79.56", features = [ + "ecma_plugin_transform", + "ecma_utils", + "ecma_visit", + "ecma_ast", + "common", + "ecma_codegen", +]} +swc_common = { version = "0.31.18", features = ["concurrent"] } +serde_json = {version = "1", features = ["unbounded_depth"]} + +[dev-dependencies] +testing = "0.33.21" diff --git a/packages/remove-side-effect/package.json b/packages/remove-side-effect/package.json new file mode 100644 index 0000000..c8e92ba --- /dev/null +++ b/packages/remove-side-effect/package.json @@ -0,0 +1,14 @@ +{ + "name": "remove-side-effect", + "version": "0.1.0", + "description": "", + "author": "", + "license": "ISC", + "keywords": ["swc-plugin"], + "main": "target/wasm32-wasi/release/remove_side_effect.wasm", + "scripts": { + "prepublishOnly": "cargo build-wasi --release" + }, + "files": [], + "preferUnplugged": true +} diff --git a/packages/remove-side-effect/src/lib.rs b/packages/remove-side-effect/src/lib.rs new file mode 100644 index 0000000..cce585f --- /dev/null +++ b/packages/remove-side-effect/src/lib.rs @@ -0,0 +1,47 @@ +use swc_core::ecma::{ + ast::*, + visit::{as_folder, FoldWith, VisitMut, VisitMutWith}, +}; +use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata}; + +pub struct TransformVisitor; + +impl VisitMut for TransformVisitor { + fn visit_mut_expr(&mut self, expr: &mut Expr) { + // 首先递归访问子节点 + expr.visit_mut_children_with(self); + + // 检查是否是函数调用 + if let Expr::Call(call_expr) = expr { + // 检查被调用的是否是 useEffect + if let Callee::Expr(callee) = &call_expr.callee { + if let Expr::Ident(ident) = &**callee { + if ident.sym.to_string() == "useEffect" { + // 将 useEffect 调用替换为空语句 + *expr = Expr::Lit(Lit::Null(Null { + span: call_expr.span, + })); + } + } + } + } + } + + fn visit_mut_stmt(&mut self, stmt: &mut Stmt) { + stmt.visit_mut_children_with(self); + + // 如果语句只包含一个被我们替换为 null 的表达式,则移除整个语句 + if let Stmt::Expr(expr_stmt) = stmt { + if let Expr::Lit(Lit::Null(_)) = &*expr_stmt.expr { + *stmt = Stmt::Empty(EmptyStmt { + span: expr_stmt.span, + }); + } + } + } +} + +#[plugin_transform] +pub fn process_transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program { + program.fold_with(&mut as_folder(TransformVisitor)) +} diff --git a/packages/remove-side-effect/tests/fixtrue.rs b/packages/remove-side-effect/tests/fixtrue.rs new file mode 100644 index 0000000..ec54555 --- /dev/null +++ b/packages/remove-side-effect/tests/fixtrue.rs @@ -0,0 +1,45 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use swc_core::common::SourceMap; +use swc_core::ecma::codegen::text_writer::JsWriter; +use swc_core::ecma::codegen::Emitter; +use swc_core::ecma::transforms::testing::{test_fixture, FixtureTestConfig}; +use swc_core::ecma::visit::as_folder; +use swc_plugin_remove_side_effect::TransformVisitor; +use testing::fixture; + +#[fixture("tests/fixture/**/input.js")] +fn fixture(input: PathBuf) { + let parent = input.parent().unwrap(); + let output = parent.join("output.js"); + + println!("Processing file: {:?}", input); + + test_fixture( + Default::default(), + &|t| { + let program = t.try_get_program().expect("failed to get program"); + + let srcmap = Arc::new(SourceMap::default()); + let mut buf = vec![]; + let writer = JsWriter::new(srcmap.clone(), "\n", &mut buf, None); + let mut emitter = Emitter { + cfg: swc_core::ecma::codegen::Config::default(), + cm: srcmap, + comments: None, + wr: writer, + }; + + emitter.emit_program(&program).unwrap(); + println!("Transform result:\n{}", String::from_utf8_lossy(&buf)); + + as_folder(TransformVisitor) + }, + &input, + &output, + FixtureTestConfig { + ..Default::default() + }, + ); +} diff --git a/packages/remove-side-effect/tests/fixture/base/input.js b/packages/remove-side-effect/tests/fixture/base/input.js new file mode 100644 index 0000000..9110559 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/base/input.js @@ -0,0 +1,9 @@ +const Component = () => { + useEffect(() => { + console.log("Hello"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/base/output.js b/packages/remove-side-effect/tests/fixture/base/output.js new file mode 100644 index 0000000..63fbf48 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/base/output.js @@ -0,0 +1,6 @@ +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file From 924016bbb9007197ae75b818d458f872ed0a4bec Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Mon, 18 Nov 2024 16:01:08 +0800 Subject: [PATCH 2/8] feat: new plugin: remove-side-effect --- packages/remove-side-effect/.cargo/config | 3 +- packages/remove-side-effect/Cargo.toml | 1 + packages/remove-side-effect/package.json | 23 ++- packages/remove-side-effect/src/lib.rs | 155 +++++++++++++++--- packages/remove-side-effect/tests/fixtrue.rs | 45 ++--- .../tests/fixture/base/input.js | 2 + .../tests/fixture/base/output.js | 2 + .../fixture/default-import-react/input.js | 11 ++ .../fixture/default-import-react/output.js | 8 + .../tests/fixture/mutli-import/input.js | 11 ++ .../tests/fixture/mutli-import/output.js | 8 + .../fixture/namespace-import-react/input.js | 11 ++ .../fixture/namespace-import-react/output.js | 8 + .../tests/fixture/nest/input.js | 20 +++ .../tests/fixture/nest/output.js | 17 ++ 15 files changed, 252 insertions(+), 73 deletions(-) create mode 100644 packages/remove-side-effect/tests/fixture/default-import-react/input.js create mode 100644 packages/remove-side-effect/tests/fixture/default-import-react/output.js create mode 100644 packages/remove-side-effect/tests/fixture/mutli-import/input.js create mode 100644 packages/remove-side-effect/tests/fixture/mutli-import/output.js create mode 100644 packages/remove-side-effect/tests/fixture/namespace-import-react/input.js create mode 100644 packages/remove-side-effect/tests/fixture/namespace-import-react/output.js create mode 100644 packages/remove-side-effect/tests/fixture/nest/input.js create mode 100644 packages/remove-side-effect/tests/fixture/nest/output.js diff --git a/packages/remove-side-effect/.cargo/config b/packages/remove-side-effect/.cargo/config index eaa0fee..44dc7cd 100644 --- a/packages/remove-side-effect/.cargo/config +++ b/packages/remove-side-effect/.cargo/config @@ -1,5 +1,4 @@ # These command aliases are not final, may change [alias] # Alias to build actual plugin binary for the specified target. -build-wasi = "build --target wasm32-wasi" -build-wasm32 = "build --target wasm32-unknown-unknown" +prepublish = "build --target wasm32-wasi" diff --git a/packages/remove-side-effect/Cargo.toml b/packages/remove-side-effect/Cargo.toml index 00b5f9f..6fc7cfc 100644 --- a/packages/remove-side-effect/Cargo.toml +++ b/packages/remove-side-effect/Cargo.toml @@ -18,6 +18,7 @@ swc_core = {version = "0.79.56", features = [ "ecma_ast", "common", "ecma_codegen", + "ecma_parser", ]} swc_common = { version = "0.31.18", features = ["concurrent"] } serde_json = {version = "1", features = ["unbounded_depth"]} diff --git a/packages/remove-side-effect/package.json b/packages/remove-side-effect/package.json index c8e92ba..8d31bc6 100644 --- a/packages/remove-side-effect/package.json +++ b/packages/remove-side-effect/package.json @@ -1,14 +1,13 @@ { - "name": "remove-side-effect", - "version": "0.1.0", - "description": "", - "author": "", - "license": "ISC", - "keywords": ["swc-plugin"], - "main": "target/wasm32-wasi/release/remove_side_effect.wasm", - "scripts": { - "prepublishOnly": "cargo build-wasi --release" - }, - "files": [], - "preferUnplugged": true + "name": "@ice/swc-plugin-remove-side-effect", + "version": "0.0.1", + "license": "MIT", + "keywords": ["swc-plugin"], + "main": "swc_plugin_remove_side_effect.wasm", + "scripts": { + "prepublishOnly": "cargo prepublish --release && cp ../../target/wasm32-wasi/release/swc_plugin_remove_side_effect.wasm ." + }, + "publishConfig": { + "access": "public" + } } diff --git a/packages/remove-side-effect/src/lib.rs b/packages/remove-side-effect/src/lib.rs index cce585f..07808d6 100644 --- a/packages/remove-side-effect/src/lib.rs +++ b/packages/remove-side-effect/src/lib.rs @@ -4,44 +4,149 @@ use swc_core::ecma::{ }; use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata}; -pub struct TransformVisitor; +#[derive(Default)] +pub struct TransformVisitor { + react_imports: Vec, + has_effect_import: bool, + scope_stack: Vec>, +} -impl VisitMut for TransformVisitor { - fn visit_mut_expr(&mut self, expr: &mut Expr) { - // 首先递归访问子节点 - expr.visit_mut_children_with(self); - - // 检查是否是函数调用 - if let Expr::Call(call_expr) = expr { - // 检查被调用的是否是 useEffect - if let Callee::Expr(callee) = &call_expr.callee { - if let Expr::Ident(ident) = &**callee { - if ident.sym.to_string() == "useEffect" { - // 将 useEffect 调用替换为空语句 - *expr = Expr::Lit(Lit::Null(Null { - span: call_expr.span, - })); +impl TransformVisitor { + pub fn new() -> Self { + Self { + react_imports: Vec::new(), + has_effect_import: false, + scope_stack: vec![Vec::new()], + } + } + + fn enter_scope(&mut self) { + self.scope_stack.push(Vec::new()); + } + + fn exit_scope(&mut self) { + self.scope_stack.pop(); + } + + fn add_to_current_scope(&mut self, name: String) { + if let Some(scope) = self.scope_stack.last_mut() { + scope.push(name); + } + } + + fn is_local_variable(&self, name: &str) -> bool { + for scope in self.scope_stack.iter().rev() { + if scope.contains(&name.to_string()) { + return true; + } + } + false + } + + fn is_react_effect(&self, expr: &Expr) -> bool { + match expr { + Expr::Ident(ident) => { + let name = ident.sym.to_string(); + if name == "useEffect" { + if self.is_local_variable(&name) { + return false; } + return self.has_effect_import; } } + Expr::Member(member) => { + if let Expr::Ident(obj) = &*member.obj { + if let Some(prop) = &member.prop.as_ident() { + return self.react_imports.contains(&obj.sym.to_string()) + && prop.sym.to_string() == "useEffect"; + } + } + } + _ => {} + } + false + } +} + +impl VisitMut for TransformVisitor { + // when into any function, find same effect var exist or not. + fn visit_mut_function(&mut self, func: &mut Function) { + self.enter_scope(); + for param in &func.params { + if let Pat::Ident(ident) = ¶m.pat { + self.add_to_current_scope(ident.id.sym.to_string()); + } } + func.visit_mut_children_with(self); + self.exit_scope(); } - fn visit_mut_stmt(&mut self, stmt: &mut Stmt) { - stmt.visit_mut_children_with(self); + fn visit_mut_arrow_expr(&mut self, arrow: &mut ArrowExpr) { + self.enter_scope(); + for param in &arrow.params { + if let Pat::Ident(ident) = param { + self.add_to_current_scope(ident.sym.to_string()); + } + } + arrow.visit_mut_children_with(self); + self.exit_scope(); + } - // 如果语句只包含一个被我们替换为 null 的表达式,则移除整个语句 - if let Stmt::Expr(expr_stmt) = stmt { - if let Expr::Lit(Lit::Null(_)) = &*expr_stmt.expr { - *stmt = Stmt::Empty(EmptyStmt { - span: expr_stmt.span, - }); + fn visit_mut_var_decl(&mut self, var_decl: &mut VarDecl) { + for decl in &var_decl.decls { + if let Pat::Ident(ident) = &decl.name { + self.add_to_current_scope(ident.id.sym.to_string()); } } + var_decl.visit_mut_children_with(self); + } + + fn visit_mut_module(&mut self, module: &mut Module) { + for item in &module.body { + if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = item { + if import.src.value.to_string() == "react" { + for spec in &import.specifiers { + match spec { + ImportSpecifier::Named(named) => { + if named.local.sym.to_string() == "useEffect" { + self.has_effect_import = true; + } + } + ImportSpecifier::Default(default_import) => { + self.react_imports + .push(default_import.local.sym.to_string()); + } + ImportSpecifier::Namespace(namespace) => { + self.react_imports.push(namespace.local.sym.to_string()); + } + } + } + } + } + } + + module.visit_mut_children_with(self); + } + + fn visit_mut_stmts(&mut self, stmts: &mut Vec) { + for stmt in stmts.iter_mut() { + stmt.visit_mut_children_with(self); + } + + stmts.retain(|stmt| { + if let Stmt::Expr(expr_stmt) = stmt { + if let Expr::Call(call_expr) = &*expr_stmt.expr { + if let Callee::Expr(callee) = &call_expr.callee { + return !self.is_react_effect(callee); + } + } + } + true + }); } } #[plugin_transform] pub fn process_transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program { - program.fold_with(&mut as_folder(TransformVisitor)) + program.fold_with(&mut as_folder(TransformVisitor::new())) } diff --git a/packages/remove-side-effect/tests/fixtrue.rs b/packages/remove-side-effect/tests/fixtrue.rs index ec54555..afcd6fb 100644 --- a/packages/remove-side-effect/tests/fixtrue.rs +++ b/packages/remove-side-effect/tests/fixtrue.rs @@ -1,45 +1,22 @@ use std::path::PathBuf; -use std::sync::Arc; - -use swc_core::common::SourceMap; -use swc_core::ecma::codegen::text_writer::JsWriter; -use swc_core::ecma::codegen::Emitter; -use swc_core::ecma::transforms::testing::{test_fixture, FixtureTestConfig}; +use swc_core::ecma::parser::{EsConfig, Syntax}; +use swc_core::ecma::transforms::testing::test_fixture; use swc_core::ecma::visit::as_folder; use swc_plugin_remove_side_effect::TransformVisitor; use testing::fixture; #[fixture("tests/fixture/**/input.js")] -fn fixture(input: PathBuf) { - let parent = input.parent().unwrap(); - let output = parent.join("output.js"); - - println!("Processing file: {:?}", input); - +fn fixture_test(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); test_fixture( - Default::default(), - &|t| { - let program = t.try_get_program().expect("failed to get program"); - - let srcmap = Arc::new(SourceMap::default()); - let mut buf = vec![]; - let writer = JsWriter::new(srcmap.clone(), "\n", &mut buf, None); - let mut emitter = Emitter { - cfg: swc_core::ecma::codegen::Config::default(), - cm: srcmap, - comments: None, - wr: writer, - }; - - emitter.emit_program(&program).unwrap(); - println!("Transform result:\n{}", String::from_utf8_lossy(&buf)); - - as_folder(TransformVisitor) - }, + Syntax::Es(EsConfig { + jsx: true, + decorators: true, + ..Default::default() + }), + &|_| as_folder(TransformVisitor::new()), &input, &output, - FixtureTestConfig { - ..Default::default() - }, + Default::default(), ); } diff --git a/packages/remove-side-effect/tests/fixture/base/input.js b/packages/remove-side-effect/tests/fixture/base/input.js index 9110559..65f46dc 100644 --- a/packages/remove-side-effect/tests/fixture/base/input.js +++ b/packages/remove-side-effect/tests/fixture/base/input.js @@ -1,3 +1,5 @@ +import { useEffect } from "react"; + const Component = () => { useEffect(() => { console.log("Hello"); diff --git a/packages/remove-side-effect/tests/fixture/base/output.js b/packages/remove-side-effect/tests/fixture/base/output.js index 63fbf48..168298b 100644 --- a/packages/remove-side-effect/tests/fixture/base/output.js +++ b/packages/remove-side-effect/tests/fixture/base/output.js @@ -1,3 +1,5 @@ +import { useEffect } from "react"; + const Component = () => { return
Hello
diff --git a/packages/remove-side-effect/tests/fixture/default-import-react/input.js b/packages/remove-side-effect/tests/fixture/default-import-react/input.js new file mode 100644 index 0000000..fbd66a7 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/default-import-react/input.js @@ -0,0 +1,11 @@ +import ReactA, { useState } from 'react' + +const Component = () => { + ReactA.useEffect(() => { + console.log("Hello"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/default-import-react/output.js b/packages/remove-side-effect/tests/fixture/default-import-react/output.js new file mode 100644 index 0000000..dc8c529 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/default-import-react/output.js @@ -0,0 +1,8 @@ +import ReactA, { useState } from 'react' + +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/mutli-import/input.js b/packages/remove-side-effect/tests/fixture/mutli-import/input.js new file mode 100644 index 0000000..f6ca81e --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/mutli-import/input.js @@ -0,0 +1,11 @@ +import { useEffect, useCallback } from "react"; + +const Component = () => { + useEffect(() => { + console.log("Hello"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/mutli-import/output.js b/packages/remove-side-effect/tests/fixture/mutli-import/output.js new file mode 100644 index 0000000..211bc72 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/mutli-import/output.js @@ -0,0 +1,8 @@ +import { useEffect, useCallback } from "react"; + +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/namespace-import-react/input.js b/packages/remove-side-effect/tests/fixture/namespace-import-react/input.js new file mode 100644 index 0000000..b0fad30 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/namespace-import-react/input.js @@ -0,0 +1,11 @@ +import * as ReactA from 'react' + +const Component = () => { + ReactA.useEffect(() => { + console.log("Hello"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/namespace-import-react/output.js b/packages/remove-side-effect/tests/fixture/namespace-import-react/output.js new file mode 100644 index 0000000..7ffb942 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/namespace-import-react/output.js @@ -0,0 +1,8 @@ +import * as ReactA from 'react' + +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/nest/input.js b/packages/remove-side-effect/tests/fixture/nest/input.js new file mode 100644 index 0000000..4e91e9e --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/nest/input.js @@ -0,0 +1,20 @@ +import { useEffect } from "react"; + +const Component = () => { + useEffect(() => { + console.log("Hello"); + }, []); + + const B = () => { + const useEffect = () => { + console.log('another UseEffect'); + }; + useEffect(); + } + + B(); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/nest/output.js b/packages/remove-side-effect/tests/fixture/nest/output.js new file mode 100644 index 0000000..74097e1 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/nest/output.js @@ -0,0 +1,17 @@ +import { useEffect } from "react"; + +const Component = () => { + + const B = () => { + const useEffect = () => { + console.log('another UseEffect'); + }; + useEffect(); + } + + B(); + + return
Hello
+} + +export default Component; \ No newline at end of file From e220a7e2c464640b70806926e7111458eb6b6d1e Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Mon, 18 Nov 2024 16:34:15 +0800 Subject: [PATCH 3/8] feat(remove-side-effect): add useLayoutEffect & block stmt --- packages/remove-side-effect/src/lib.rs | 58 ++++++++++++++++--- .../tests/fixture/base/input.js | 6 +- .../tests/fixture/base/output.js | 2 +- .../tests/fixture/nest/input.js | 13 +++++ .../tests/fixture/nest/output.js | 10 ++++ 5 files changed, 78 insertions(+), 11 deletions(-) diff --git a/packages/remove-side-effect/src/lib.rs b/packages/remove-side-effect/src/lib.rs index 07808d6..698d1d5 100644 --- a/packages/remove-side-effect/src/lib.rs +++ b/packages/remove-side-effect/src/lib.rs @@ -7,7 +7,7 @@ use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata #[derive(Default)] pub struct TransformVisitor { react_imports: Vec, - has_effect_import: bool, + imported_hooks: Vec, scope_stack: Vec>, } @@ -15,11 +15,19 @@ impl TransformVisitor { pub fn new() -> Self { Self { react_imports: Vec::new(), - has_effect_import: false, + imported_hooks: Vec::new(), scope_stack: vec![Vec::new()], } } + fn target_hooks() -> Vec<&'static str> { + vec!["useEffect", "useLayoutEffect"] + } + + fn is_target_hook(name: &str) -> bool { + Self::target_hooks().contains(&name) + } + fn enter_scope(&mut self) { self.scope_stack.push(Vec::new()); } @@ -43,22 +51,22 @@ impl TransformVisitor { false } - fn is_react_effect(&self, expr: &Expr) -> bool { + fn is_react_hook(&self, expr: &Expr) -> bool { match expr { Expr::Ident(ident) => { let name = ident.sym.to_string(); - if name == "useEffect" { + if Self::is_target_hook(&name) { if self.is_local_variable(&name) { return false; } - return self.has_effect_import; + return self.imported_hooks.contains(&name); } } Expr::Member(member) => { if let Expr::Ident(obj) = &*member.obj { if let Some(prop) = &member.prop.as_ident() { return self.react_imports.contains(&obj.sym.to_string()) - && prop.sym.to_string() == "useEffect"; + && Self::is_target_hook(&prop.sym.to_string()); } } } @@ -108,8 +116,9 @@ impl VisitMut for TransformVisitor { for spec in &import.specifiers { match spec { ImportSpecifier::Named(named) => { - if named.local.sym.to_string() == "useEffect" { - self.has_effect_import = true; + let name = named.local.sym.to_string(); + if Self::is_target_hook(&name) { + self.imported_hooks.push(name); } } ImportSpecifier::Default(default_import) => { @@ -137,13 +146,44 @@ impl VisitMut for TransformVisitor { if let Stmt::Expr(expr_stmt) = stmt { if let Expr::Call(call_expr) = &*expr_stmt.expr { if let Callee::Expr(callee) = &call_expr.callee { - return !self.is_react_effect(callee); + return !self.is_react_hook(callee); } } } true }); } + + fn visit_mut_block_stmt(&mut self, block: &mut BlockStmt) { + self.enter_scope(); + block.visit_mut_children_with(self); + self.exit_scope(); + } + + // try-catch + fn visit_mut_try_stmt(&mut self, try_stmt: &mut TryStmt) { + self.enter_scope(); + try_stmt.block.visit_mut_children_with(self); + self.exit_scope(); + + // catch + if let Some(catch) = &mut try_stmt.handler { + self.enter_scope(); + // 添加 catch 参数到作用域 + if let Some(Pat::Ident(ident)) = &catch.param { + self.add_to_current_scope(ident.sym.to_string()); + } + catch.body.visit_mut_children_with(self); + self.exit_scope(); + } + + // finally + if let Some(finally) = &mut try_stmt.finalizer { + self.enter_scope(); + finally.visit_mut_children_with(self); + self.exit_scope(); + } + } } #[plugin_transform] diff --git a/packages/remove-side-effect/tests/fixture/base/input.js b/packages/remove-side-effect/tests/fixture/base/input.js index 65f46dc..8f52fe6 100644 --- a/packages/remove-side-effect/tests/fixture/base/input.js +++ b/packages/remove-side-effect/tests/fixture/base/input.js @@ -1,10 +1,14 @@ -import { useEffect } from "react"; +import { useEffect, useLayoutEffect } from "react"; const Component = () => { useEffect(() => { console.log("Hello"); }, []); + useLayoutEffect(() => { + console.log("Hello Layout"); + }, []); + return
Hello
} diff --git a/packages/remove-side-effect/tests/fixture/base/output.js b/packages/remove-side-effect/tests/fixture/base/output.js index 168298b..2b70c32 100644 --- a/packages/remove-side-effect/tests/fixture/base/output.js +++ b/packages/remove-side-effect/tests/fixture/base/output.js @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useLayoutEffect } from "react"; const Component = () => { diff --git a/packages/remove-side-effect/tests/fixture/nest/input.js b/packages/remove-side-effect/tests/fixture/nest/input.js index 4e91e9e..4e9f171 100644 --- a/packages/remove-side-effect/tests/fixture/nest/input.js +++ b/packages/remove-side-effect/tests/fixture/nest/input.js @@ -5,6 +5,19 @@ const Component = () => { console.log("Hello"); }, []); + { + const useEffect = () => {} + useEffect() + } + + try { + const useEffect = () => {} + useEffect() + } catch (e) { + // React useEffect + useEffect(() => {}) + } + const B = () => { const useEffect = () => { console.log('another UseEffect'); diff --git a/packages/remove-side-effect/tests/fixture/nest/output.js b/packages/remove-side-effect/tests/fixture/nest/output.js index 74097e1..8ffe5ca 100644 --- a/packages/remove-side-effect/tests/fixture/nest/output.js +++ b/packages/remove-side-effect/tests/fixture/nest/output.js @@ -2,6 +2,16 @@ import { useEffect } from "react"; const Component = () => { + { + const useEffect = () => {} + useEffect() + } + + try { + const useEffect = () => {} + useEffect() + } catch (e) {} + const B = () => { const useEffect = () => { console.log('another UseEffect'); From 8abfbe46b2c9b33d4884a5e72f3ca915234db244 Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Mon, 18 Nov 2024 17:46:03 +0800 Subject: [PATCH 4/8] feat(remove-side-effect): add some cases --- packages/remove-side-effect/src/lib.rs | 120 +++++++++++++----- .../tests/fixture/base/input.js | 14 ++ .../tests/fixture/base/output.js | 7 + .../tests/fixture/hooks/input.js | 23 ++++ .../tests/fixture/hooks/output.js | 16 +++ .../tests/fixture/mix-import-react/input.js | 20 +++ .../tests/fixture/mix-import-react/output.js | 9 ++ .../tests/fixture/nest/input.js | 3 + .../tests/fixture/redeclar/input.js | 20 +++ .../tests/fixture/redeclar/output.js | 17 +++ .../fixture/rename-import-react/input.js | 21 +++ .../fixture/rename-import-react/output.js | 8 ++ 12 files changed, 247 insertions(+), 31 deletions(-) create mode 100644 packages/remove-side-effect/tests/fixture/hooks/input.js create mode 100644 packages/remove-side-effect/tests/fixture/hooks/output.js create mode 100644 packages/remove-side-effect/tests/fixture/mix-import-react/input.js create mode 100644 packages/remove-side-effect/tests/fixture/mix-import-react/output.js create mode 100644 packages/remove-side-effect/tests/fixture/redeclar/input.js create mode 100644 packages/remove-side-effect/tests/fixture/redeclar/output.js create mode 100644 packages/remove-side-effect/tests/fixture/rename-import-react/input.js create mode 100644 packages/remove-side-effect/tests/fixture/rename-import-react/output.js diff --git a/packages/remove-side-effect/src/lib.rs b/packages/remove-side-effect/src/lib.rs index 698d1d5..05caca7 100644 --- a/packages/remove-side-effect/src/lib.rs +++ b/packages/remove-side-effect/src/lib.rs @@ -6,8 +6,11 @@ use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata #[derive(Default)] pub struct TransformVisitor { + // Track React imports (e.g., import React from 'react') react_imports: Vec, + // Track imported hooks (e.g., import { useEffect } from 'react') imported_hooks: Vec, + // Stack of scopes for tracking variable declarations scope_stack: Vec>, } @@ -77,26 +80,96 @@ impl TransformVisitor { } impl VisitMut for TransformVisitor { - // when into any function, find same effect var exist or not. + fn visit_mut_stmts(&mut self, stmts: &mut Vec) { + self.enter_scope(); + + // First, process all variable declarations to build the scope + for stmt in stmts.iter_mut() { + if let Stmt::Decl(Decl::Var(var_decl)) = stmt { + for decl in &var_decl.decls { + if let Pat::Ident(ident) = &decl.name { + self.add_to_current_scope(ident.id.sym.to_string()); + } + } + } + } + + // Process statements and remove React hooks + stmts.retain(|stmt| { + if let Stmt::Expr(expr_stmt) = stmt { + if let Expr::Call(call_expr) = &*expr_stmt.expr { + if let Callee::Expr(callee) = &call_expr.callee { + if let Expr::Ident(ident) = &**callee { + let name = ident.sym.to_string(); + // If it's a local variable, keep the call + if self.is_local_variable(&name) { + return true; + } + // If it's an imported hook, remove the call + if self.imported_hooks.contains(&name) { + return false; + } + } + return !self.is_react_hook(callee); + } + } + } + true + }); + + // Process child nodes + for stmt in stmts.iter_mut() { + stmt.visit_mut_children_with(self); + } + + self.exit_scope(); + } + + fn visit_mut_block_stmt(&mut self, block: &mut BlockStmt) { + self.enter_scope(); + // Process all statements in the block + self.visit_mut_stmts(&mut block.stmts); + self.exit_scope(); + } + fn visit_mut_function(&mut self, func: &mut Function) { self.enter_scope(); + + // Add function parameters to scope for param in &func.params { if let Pat::Ident(ident) = ¶m.pat { self.add_to_current_scope(ident.id.sym.to_string()); } } - func.visit_mut_children_with(self); + + // Process function body + if let Some(body) = &mut func.body { + self.visit_mut_stmts(&mut body.stmts); + } + self.exit_scope(); } fn visit_mut_arrow_expr(&mut self, arrow: &mut ArrowExpr) { self.enter_scope(); + + // Add arrow function parameters to scope for param in &arrow.params { if let Pat::Ident(ident) = param { self.add_to_current_scope(ident.sym.to_string()); } } - arrow.visit_mut_children_with(self); + + // Process arrow function body + match &mut *arrow.body { + BlockStmtOrExpr::BlockStmt(block) => { + self.visit_mut_block_stmt(block); + } + BlockStmtOrExpr::Expr(expr) => { + expr.visit_mut_with(self); + } + } + self.exit_scope(); } @@ -116,9 +189,18 @@ impl VisitMut for TransformVisitor { for spec in &import.specifiers { match spec { ImportSpecifier::Named(named) => { - let name = named.local.sym.to_string(); - if Self::is_target_hook(&name) { - self.imported_hooks.push(name); + let original_name = match &named.imported { + Some(imported) => match imported { + // eg: import { useEffect as myEffect } from "react"; + ModuleExportName::Ident(ident) => ident.sym.to_string(), + // eg: import { 'use-effect' as myEffect } from 'react'; + ModuleExportName::Str(str) => str.value.to_string(), + }, + None => named.local.sym.to_string(), + }; + + if Self::is_target_hook(&original_name) { + self.imported_hooks.push(named.local.sym.to_string()); } } ImportSpecifier::Default(default_import) => { @@ -137,30 +219,6 @@ impl VisitMut for TransformVisitor { module.visit_mut_children_with(self); } - fn visit_mut_stmts(&mut self, stmts: &mut Vec) { - for stmt in stmts.iter_mut() { - stmt.visit_mut_children_with(self); - } - - stmts.retain(|stmt| { - if let Stmt::Expr(expr_stmt) = stmt { - if let Expr::Call(call_expr) = &*expr_stmt.expr { - if let Callee::Expr(callee) = &call_expr.callee { - return !self.is_react_hook(callee); - } - } - } - true - }); - } - - fn visit_mut_block_stmt(&mut self, block: &mut BlockStmt) { - self.enter_scope(); - block.visit_mut_children_with(self); - self.exit_scope(); - } - - // try-catch fn visit_mut_try_stmt(&mut self, try_stmt: &mut TryStmt) { self.enter_scope(); try_stmt.block.visit_mut_children_with(self); @@ -169,7 +227,7 @@ impl VisitMut for TransformVisitor { // catch if let Some(catch) = &mut try_stmt.handler { self.enter_scope(); - // 添加 catch 参数到作用域 + // add catch params to context if let Some(Pat::Ident(ident)) = &catch.param { self.add_to_current_scope(ident.sym.to_string()); } diff --git a/packages/remove-side-effect/tests/fixture/base/input.js b/packages/remove-side-effect/tests/fixture/base/input.js index 8f52fe6..e508e22 100644 --- a/packages/remove-side-effect/tests/fixture/base/input.js +++ b/packages/remove-side-effect/tests/fixture/base/input.js @@ -12,4 +12,18 @@ const Component = () => { return
Hello
} +function Component2() { + useEffect(() => { + console.log("Hello"); + }, []); + + useLayoutEffect(() => { + console.log("Hello Layout"); + }, []); + + return
Hello
+} + +export { Component2 }; + export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/base/output.js b/packages/remove-side-effect/tests/fixture/base/output.js index 2b70c32..b6d7af2 100644 --- a/packages/remove-side-effect/tests/fixture/base/output.js +++ b/packages/remove-side-effect/tests/fixture/base/output.js @@ -5,4 +5,11 @@ const Component = () => { return
Hello
} +function Component2() { + + return
Hello
+} + +export { Component2 }; + export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/hooks/input.js b/packages/remove-side-effect/tests/fixture/hooks/input.js new file mode 100644 index 0000000..dc97cc7 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/hooks/input.js @@ -0,0 +1,23 @@ +import { useEffect, useLayoutEffect } from "react"; + +function useCustomHook() { + useEffect(() => { + console.log("Custom Hook Effect"); + }, []); + + useLayoutEffect(() => { + console.log("Hello Layout"); + }, []); + + { + const useEffect = () => {} + useEffect() + } +} + +const Component = () => { + useCustomHook(); + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/hooks/output.js b/packages/remove-side-effect/tests/fixture/hooks/output.js new file mode 100644 index 0000000..5285dfc --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/hooks/output.js @@ -0,0 +1,16 @@ +import { useEffect, useLayoutEffect } from "react"; + +function useCustomHook() { + + { + const useEffect = () => {} + useEffect() + } +} + +const Component = () => { + useCustomHook(); + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/mix-import-react/input.js b/packages/remove-side-effect/tests/fixture/mix-import-react/input.js new file mode 100644 index 0000000..2f3286b --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/mix-import-react/input.js @@ -0,0 +1,20 @@ +import React, { useLayoutEffect } from 'react' +import { useEffect } from 'react' + +const Component = () => { + React.useEffect(() => { + console.log("React.useEffect"); + }, []); + + useEffect(() => { + console.log("useEffect"); + }, []); + + useLayoutEffect(() => { + console.log("useLayoutEffect"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/mix-import-react/output.js b/packages/remove-side-effect/tests/fixture/mix-import-react/output.js new file mode 100644 index 0000000..8157df4 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/mix-import-react/output.js @@ -0,0 +1,9 @@ +import React, { useLayoutEffect } from 'react' +import { useEffect } from 'react' + +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/nest/input.js b/packages/remove-side-effect/tests/fixture/nest/input.js index 4e9f171..218c56d 100644 --- a/packages/remove-side-effect/tests/fixture/nest/input.js +++ b/packages/remove-side-effect/tests/fixture/nest/input.js @@ -3,6 +3,9 @@ import { useEffect } from "react"; const Component = () => { useEffect(() => { console.log("Hello"); + + const useEffect = () => {} + useEffect() }, []); { diff --git a/packages/remove-side-effect/tests/fixture/redeclar/input.js b/packages/remove-side-effect/tests/fixture/redeclar/input.js new file mode 100644 index 0000000..a43bfca --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/redeclar/input.js @@ -0,0 +1,20 @@ +import { useEffect, useLayoutEffect } from "react"; + +const Component = () => { + // can't find useEffect, because the Temporal Dead Zone, TDZ, but we should not to remove it + // because it's a local variable, not form react + useEffect(() => { + console.log("Hello"); + }, []); + + useLayoutEffect(() => { + console.log("Hello Layout"); + }, []); + + const useEffect = () => {} + useEffect() + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/redeclar/output.js b/packages/remove-side-effect/tests/fixture/redeclar/output.js new file mode 100644 index 0000000..c48e400 --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/redeclar/output.js @@ -0,0 +1,17 @@ +import { useEffect, useLayoutEffect } from "react"; + +const Component = () => { + // can't find useEffect, because the Temporal Dead Zone, TDZ, but we should not to remove it + // because it's a local variable, not form react + useEffect(() => { + console.log("Hello"); + }, []); + + + const useEffect = () => {} + useEffect() + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/rename-import-react/input.js b/packages/remove-side-effect/tests/fixture/rename-import-react/input.js new file mode 100644 index 0000000..a69d7bc --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/rename-import-react/input.js @@ -0,0 +1,21 @@ +import { useEffect as myEffect, useLayoutEffect as myLayout } from "react"; +import { 'useEffect' as myEffect2 } from 'react'; + + +const Component = () => { + myEffect2(() => { + console.log("Hello"); + }, []); + + myEffect(() => { + console.log("Hello"); + }, []); + + myLayout(() => { + console.log("Layout"); + }, []); + + return
Hello
+} + +export default Component; \ No newline at end of file diff --git a/packages/remove-side-effect/tests/fixture/rename-import-react/output.js b/packages/remove-side-effect/tests/fixture/rename-import-react/output.js new file mode 100644 index 0000000..8e1816a --- /dev/null +++ b/packages/remove-side-effect/tests/fixture/rename-import-react/output.js @@ -0,0 +1,8 @@ +import { useEffect as myEffect, useLayoutEffect as myLayout } from "react"; +import { 'useEffect' as myEffect2 } from 'react'; +const Component = () => { + + return
Hello
+} + +export default Component; \ No newline at end of file From 982816042ff525443e81ee467ab3dee2b4319af7 Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Mon, 18 Nov 2024 18:01:03 +0800 Subject: [PATCH 5/8] feat(remove-side-effect): rename is_react_hook --- packages/remove-side-effect/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/remove-side-effect/src/lib.rs b/packages/remove-side-effect/src/lib.rs index 05caca7..702f04d 100644 --- a/packages/remove-side-effect/src/lib.rs +++ b/packages/remove-side-effect/src/lib.rs @@ -54,7 +54,7 @@ impl TransformVisitor { false } - fn is_react_hook(&self, expr: &Expr) -> bool { + fn is_removable_effect(&self, expr: &Expr) -> bool { match expr { Expr::Ident(ident) => { let name = ident.sym.to_string(); @@ -110,7 +110,7 @@ impl VisitMut for TransformVisitor { return false; } } - return !self.is_react_hook(callee); + return !self.is_removable_effect(callee); } } } From 02537de8bb3b2d59ddc49cd0d1b4ae83151fdbc5 Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Fri, 22 Nov 2024 18:02:05 +0800 Subject: [PATCH 6/8] feat(remove-side-effect): update swc_core --- Cargo.lock | 1 - packages/remove-side-effect/Cargo.toml | 15 +++++++-------- packages/remove-side-effect/tests/fixtrue.rs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e9a613..1bde680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1589,7 +1589,6 @@ dependencies = [ name = "swc_plugin_remove_side_effect" version = "0.0.1" dependencies = [ - "easy-error", "fxhash", "serde", "serde_json", diff --git a/packages/remove-side-effect/Cargo.toml b/packages/remove-side-effect/Cargo.toml index 6fc7cfc..e47b7ab 100644 --- a/packages/remove-side-effect/Cargo.toml +++ b/packages/remove-side-effect/Cargo.toml @@ -7,11 +7,10 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -serde = "1" -fxhash= "0.2.1" -easy-error = "1.0.0" -tracing = { version="0.1.34", features = ["release_max_level_info"] } -swc_core = {version = "0.79.56", features = [ +serde = { workspace = true } +fxhash= { workspace = true } +tracing = { workspace = true, features = ["release_max_level_info"] } +swc_core = { workspace = true, features = [ "ecma_plugin_transform", "ecma_utils", "ecma_visit", @@ -20,8 +19,8 @@ swc_core = {version = "0.79.56", features = [ "ecma_codegen", "ecma_parser", ]} -swc_common = { version = "0.31.18", features = ["concurrent"] } -serde_json = {version = "1", features = ["unbounded_depth"]} +swc_common = { workspace = true, features = ["concurrent"] } +serde_json = { workspace = true, features = ["unbounded_depth"]} [dev-dependencies] -testing = "0.33.21" +testing = { workspace = true } diff --git a/packages/remove-side-effect/tests/fixtrue.rs b/packages/remove-side-effect/tests/fixtrue.rs index afcd6fb..327f605 100644 --- a/packages/remove-side-effect/tests/fixtrue.rs +++ b/packages/remove-side-effect/tests/fixtrue.rs @@ -1,5 +1,5 @@ use std::path::PathBuf; -use swc_core::ecma::parser::{EsConfig, Syntax}; +use swc_core::ecma::parser::{Syntax, EsSyntax}; use swc_core::ecma::transforms::testing::test_fixture; use swc_core::ecma::visit::as_folder; use swc_plugin_remove_side_effect::TransformVisitor; @@ -9,7 +9,7 @@ use testing::fixture; fn fixture_test(input: PathBuf) { let output = input.parent().unwrap().join("output.js"); test_fixture( - Syntax::Es(EsConfig { + Syntax::Es(EsSyntax { jsx: true, decorators: true, ..Default::default() From 22de9475159ae13a84309549d99d77d64237b537 Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Fri, 22 Nov 2024 18:03:59 +0800 Subject: [PATCH 7/8] feat(remove-side-effect): remove useless vscode config file --- .vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ffb2410..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - // cause we use a old version of rust, so we need to add this option to make rust-analyzer work - "rust-analyzer.cargo.extraArgs": ["-Z=unstable-options"] -} From f91afc41e5aa46190fd1eaa2356471b96c672032 Mon Sep 17 00:00:00 2001 From: XXXMrG Date: Tue, 18 Feb 2025 14:52:49 +0800 Subject: [PATCH 8/8] fix: workspace config --- Cargo.toml | 1 + packages/remove-side-effect/.cargo/{config => config.toml} | 0 2 files changed, 1 insertion(+) rename packages/remove-side-effect/.cargo/{config => config.toml} (100%) diff --git a/Cargo.toml b/Cargo.toml index ee964dd..cddb329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "packages/remove-export", "packages/keep-platform", diff --git a/packages/remove-side-effect/.cargo/config b/packages/remove-side-effect/.cargo/config.toml similarity index 100% rename from packages/remove-side-effect/.cargo/config rename to packages/remove-side-effect/.cargo/config.toml