From db3225c6f5b63465de56e266e9468766b359f996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 19 Sep 2024 17:03:41 +0900 Subject: [PATCH] feat(sdk): Initialize project (#346) --- Cargo.lock | 47 +++++ Cargo.toml | 18 +- packages/swc-sdk/.npmignore | 2 + packages/swc-sdk/CHANGELOG.md | 139 +++++++++++++++ packages/swc-sdk/Cargo.toml | 25 +++ packages/swc-sdk/README.md | 163 ++++++++++++++++++ packages/swc-sdk/README.tmpl.md | 25 +++ packages/swc-sdk/package.json | 24 +++ packages/swc-sdk/src/lib.rs | 30 ++++ packages/swc-sdk/transform/Cargo.toml | 29 ++++ packages/swc-sdk/transform/src/config.rs | 42 +++++ packages/swc-sdk/transform/src/flag.rs | 158 +++++++++++++++++ .../swc-sdk/transform/src/import_analyzer.rs | 111 ++++++++++++ packages/swc-sdk/transform/src/lib.rs | 70 ++++++++ packages/swc-sdk/transform/tests/fixture.rs | 31 ++++ .../tests/fixture/flag/arg-1/input.js | 7 + .../tests/fixture/flag/arg-1/output.js | 6 + .../fixture/flag/custom-adapter/input.js | 7 + .../fixture/flag/custom-adapter/output.js | 6 + .../tests/fixture/flag/duplicate/input.js | 8 + .../tests/fixture/flag/duplicate/output.js | 6 + .../tests/fixture/flag/no-arg/input.js | 7 + .../tests/fixture/flag/no-arg/output.js | 6 + 23 files changed, 951 insertions(+), 16 deletions(-) create mode 100644 packages/swc-sdk/.npmignore create mode 100644 packages/swc-sdk/CHANGELOG.md create mode 100644 packages/swc-sdk/Cargo.toml create mode 100644 packages/swc-sdk/README.md create mode 100644 packages/swc-sdk/README.tmpl.md create mode 100644 packages/swc-sdk/package.json create mode 100644 packages/swc-sdk/src/lib.rs create mode 100644 packages/swc-sdk/transform/Cargo.toml create mode 100644 packages/swc-sdk/transform/src/config.rs create mode 100644 packages/swc-sdk/transform/src/flag.rs create mode 100644 packages/swc-sdk/transform/src/import_analyzer.rs create mode 100644 packages/swc-sdk/transform/src/lib.rs create mode 100644 packages/swc-sdk/transform/tests/fixture.rs create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/arg-1/input.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/arg-1/output.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/input.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/output.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/duplicate/input.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/duplicate/output.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/no-arg/input.js create mode 100644 packages/swc-sdk/transform/tests/fixture/flag/no-arg/output.js diff --git a/Cargo.lock b/Cargo.lock index b26a676d1..b19608286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -642,6 +642,27 @@ dependencies = [ "uuid", ] +[[package]] +name = "default-from-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c5155e51d19a68bb8bc3b1a1ec0d5aff5c24791343dc15fa051424316c267e7" +dependencies = [ + "derive-default-from-serde", + "serde", +] + +[[package]] +name = "derive-default-from-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87f19b90ebc996d8100cabf45b0b177772844c8a911533f55239a430a964f06" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "derive_builder" version = "0.20.1" @@ -3131,6 +3152,15 @@ dependencies = [ "swc_magic", ] +[[package]] +name = "swc_plugin_swc_sdk" +version = "0.1.0" +dependencies = [ + "serde_json", + "swc_core", + "swc_sdk", +] + [[package]] name = "swc_plugin_transform_imports" version = "0.19.4" @@ -3180,6 +3210,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "swc_sdk" +version = "0.1.0" +dependencies = [ + "default-from-serde", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_testing", + "swc_ecma_utils", + "swc_ecma_visit", + "testing", +] + [[package]] name = "swc_timer" version = "0.25.0" diff --git a/Cargo.toml b/Cargo.toml index a4f882e03..adcdddb38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,5 @@ [workspace] -members = [ - "xtask", - "packages/emotion", - "packages/jest", - "packages/loadable-components", - "packages/noop", - "packages/react-remove-properties", - "packages/relay", - "packages/remove-console", - "packages/styled-components", - "packages/styled-jsx", - "packages/swc-confidential", - "packages/swc-magic", - "packages/transform-imports", - "packages/prefresh", -] +members = ["xtask", "packages/*"] resolver = "2" @@ -37,6 +22,7 @@ cargo_metadata = "0.18.1" cipher = "0.4.4" clap = "4.5.4" convert_case = "0.6.0" +default-from-serde = "0.1" fxhash = "0.2.1" handlebars = "5.1.2" hex = "0.4.3" diff --git a/packages/swc-sdk/.npmignore b/packages/swc-sdk/.npmignore new file mode 100644 index 000000000..1ed674f50 --- /dev/null +++ b/packages/swc-sdk/.npmignore @@ -0,0 +1,2 @@ +transform/ +tests/ \ No newline at end of file diff --git a/packages/swc-sdk/CHANGELOG.md b/packages/swc-sdk/CHANGELOG.md new file mode 100644 index 000000000..565f3f02a --- /dev/null +++ b/packages/swc-sdk/CHANGELOG.md @@ -0,0 +1,139 @@ +# @swc/plugin-swc-sdk + +## 3.0.1 + +### Patch Changes + +- 04548e2: Update swc_core to 0.103.x + +## 3.0.0 + +### Major Changes + +- f8e5fd0: Update swc_core to 0.102.x + +## 2.0.10 + +### Patch Changes + +- 7d17e25: Update swc_core to v0.101.x + +## 2.0.9 + +### Patch Changes + +- 7391419: Update swc_core to v0.100.0 + +## 2.0.8 + +### Patch Changes + +- 9c28afb: Update swc_core to 0.99.x (@swc/core 1.7.0) + +## 2.0.7 + +### Patch Changes + +- af25741: Update swc_core to 0.96.0 + +## 2.0.6 + +### Patch Changes + +- 41a8f56: Update swc_core to v0.95.x + +## 2.0.5 + +### Patch Changes + +- fc30490: Update swc_core to v0.93.0 + +## 2.0.4 + +### Patch Changes + +- 0f38844: Publish all chanages + +## 2.0.3 + +### Patch Changes + +- 1cc9eda: Update dependencies + +## 2.0.2 + +### Patch Changes + +- 247cca6: Update rustc to 'nightly-2024-04-16' + +## 2.0.1 + +### Patch Changes + +- 876bbce: Update swc_core to 0.92.x + +## 2.0.0 + +### Major Changes + +- 8e91d39: Update swc_core to 0.91.x + +## 1.5.121 + +### Patch Changes + +- f4df366: Update swc_core + +## 1.5.120 + +### Patch Changes + +- c88b22b: Align package metadata + +## 1.5.119 + +### Patch Changes + +- a3cc4fb: Organize pacakge metadata + +## 1.5.118 + +### Patch Changes + +- e9e78ef: Update swc crates + +## 1.5.117 + +### Patch Changes + +- 6096d6d: Fix plugin version schema issue + +## 1.5.116 + +### Patch Changes + +- 37d3aaf: Depend on the swc download counter package + +## 1.5.115 + +### Patch Changes + +- 8bd92c7: swc_core 0.90.x + +## 1.5.114 + +### Patch Changes + +- 4ef0b7f: Add changelog to the readme + +## 1.5.113 + +### Patch Changes + +- 4e72680: swc_core@0.88.0 + +## 1.5.112 + +### Patch Changes + +- 16bb4d8: swc_core@0.82.x diff --git a/packages/swc-sdk/Cargo.toml b/packages/swc-sdk/Cargo.toml new file mode 100644 index 000000000..3a8dd6cf7 --- /dev/null +++ b/packages/swc-sdk/Cargo.toml @@ -0,0 +1,25 @@ +[package] + +description = "SWC Plugin for @swc/sdk" + + +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +name = "swc_plugin_swc_sdk" +publish = false +repository = { workspace = true } +rust-version = { workspace = true } +version = "0.1.0" + + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +serde_json = { workspace = true } +swc_core = { workspace = true, features = ["ecma_plugin_transform"] } + + +swc_sdk = { path = "./transform" } diff --git a/packages/swc-sdk/README.md b/packages/swc-sdk/README.md new file mode 100644 index 000000000..e8152e812 --- /dev/null +++ b/packages/swc-sdk/README.md @@ -0,0 +1,163 @@ +# @swc/plugin-swc-sdk + +## Usage + +.swcrc: + +```json +{ + "jsc": { + "experimental": { + "plugins": ["@swc/plugin-swc-sdk"] + } + } +} +``` + +### `markAsPure` + +```js +import { markAsPure } from "@swc/sdk"; + +markAsPure(() => console.log("This will be removed by the SWC minifier")); +``` + +# @swc/plugin-swc-sdk + +## 3.0.1 + +### Patch Changes + +- 04548e2: Update swc_core to 0.103.x + +## 3.0.0 + +### Major Changes + +- f8e5fd0: Update swc_core to 0.102.x + +## 2.0.10 + +### Patch Changes + +- 7d17e25: Update swc_core to v0.101.x + +## 2.0.9 + +### Patch Changes + +- 7391419: Update swc_core to v0.100.0 + +## 2.0.8 + +### Patch Changes + +- 9c28afb: Update swc_core to 0.99.x (@swc/core 1.7.0) + +## 2.0.7 + +### Patch Changes + +- af25741: Update swc_core to 0.96.0 + +## 2.0.6 + +### Patch Changes + +- 41a8f56: Update swc_core to v0.95.x + +## 2.0.5 + +### Patch Changes + +- fc30490: Update swc_core to v0.93.0 + +## 2.0.4 + +### Patch Changes + +- 0f38844: Publish all chanages + +## 2.0.3 + +### Patch Changes + +- 1cc9eda: Update dependencies + +## 2.0.2 + +### Patch Changes + +- 247cca6: Update rustc to 'nightly-2024-04-16' + +## 2.0.1 + +### Patch Changes + +- 876bbce: Update swc_core to 0.92.x + +## 2.0.0 + +### Major Changes + +- 8e91d39: Update swc_core to 0.91.x + +## 1.5.121 + +### Patch Changes + +- f4df366: Update swc_core + +## 1.5.120 + +### Patch Changes + +- c88b22b: Align package metadata + +## 1.5.119 + +### Patch Changes + +- a3cc4fb: Organize pacakge metadata + +## 1.5.118 + +### Patch Changes + +- e9e78ef: Update swc crates + +## 1.5.117 + +### Patch Changes + +- 6096d6d: Fix plugin version schema issue + +## 1.5.116 + +### Patch Changes + +- 37d3aaf: Depend on the swc download counter package + +## 1.5.115 + +### Patch Changes + +- 8bd92c7: swc_core 0.90.x + +## 1.5.114 + +### Patch Changes + +- 4ef0b7f: Add changelog to the readme + +## 1.5.113 + +### Patch Changes + +- 4e72680: swc_core@0.88.0 + +## 1.5.112 + +### Patch Changes + +- 16bb4d8: swc_core@0.82.x diff --git a/packages/swc-sdk/README.tmpl.md b/packages/swc-sdk/README.tmpl.md new file mode 100644 index 000000000..1d6738e70 --- /dev/null +++ b/packages/swc-sdk/README.tmpl.md @@ -0,0 +1,25 @@ +# @swc/plugin-swc-sdk + +## Usage + +.swcrc: + +```json +{ + "jsc": { + "experimental": { + "plugins": ["@swc/plugin-swc-sdk"] + } + } +} +``` + +### `markAsPure` + +```js +import { markAsPure } from "@swc/sdk"; + +markAsPure(() => console.log("This will be removed by the SWC minifier")); +``` + +${CHANGELOG} diff --git a/packages/swc-sdk/package.json b/packages/swc-sdk/package.json new file mode 100644 index 000000000..d35ea6cb7 --- /dev/null +++ b/packages/swc-sdk/package.json @@ -0,0 +1,24 @@ +{ + "name": "@swc/plugin-swc-sdk", + "version": "3.0.1", + "description": "SWC plugin for swc-sdk", + "main": "swc_plugin_swc_sdk.wasm", + "scripts": { + "prepack": "cargo build --release -p swc_plugin_swc_sdk --target wasm32-wasi && cp ../../target/wasm32-wasi/release/swc_plugin_swc_sdk.wasm ." + }, + "homepage": "https://swc.rs", + "repository": { + "type": "git", + "url": "+https://github.com/swc-project/plugins.git" + }, + "bugs": { + "url": "https://github.com/swc-project/plugins/issues" + }, + "author": "강동윤 ", + "keywords": [], + "license": "Apache-2.0", + "preferUnplugged": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } +} diff --git a/packages/swc-sdk/src/lib.rs b/packages/swc-sdk/src/lib.rs new file mode 100644 index 000000000..91ffe8d89 --- /dev/null +++ b/packages/swc-sdk/src/lib.rs @@ -0,0 +1,30 @@ +#![allow(clippy::not_unsafe_ptr_arg_deref)] +#![feature(box_patterns)] + +use swc_core::{ + ecma::{ast::Program, visit::VisitMutWith}, + plugin::{ + plugin_transform, + proxies::{PluginCommentsProxy, TransformPluginProgramMetadata}, + }, +}; + +#[plugin_transform] +fn swc_sdk_plugin(mut program: Program, data: TransformPluginProgramMetadata) -> Program { + let config = serde_json::from_str::( + &data + .get_transform_plugin_config() + .expect("failed to get plugin config for swc-sdk"), + ) + .expect("invalid config for swc-sdk"); + + let unresolved_mark = data.unresolved_mark; + + program.visit_mut_with(&mut swc_sdk::swc_sdk( + swc_sdk::Env { unresolved_mark }, + config, + PluginCommentsProxy, + )); + + program +} diff --git a/packages/swc-sdk/transform/Cargo.toml b/packages/swc-sdk/transform/Cargo.toml new file mode 100644 index 000000000..67932112b --- /dev/null +++ b/packages/swc-sdk/transform/Cargo.toml @@ -0,0 +1,29 @@ +[package] + +description = "AST transforms visitor for swc-sdk" + + +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +name = "swc_sdk" +repository = { workspace = true } +rust-version = { workspace = true } +version = "0.1.0" + + +[dependencies] +default-from-serde = { workspace = true } +serde = { workspace = true, features = ["derive"] } +swc_atoms = { workspace = true } +swc_common = { workspace = true } +swc_ecma_ast = { workspace = true } +swc_ecma_utils = { workspace = true } +swc_ecma_visit = { workspace = true } + +[dev-dependencies] +swc_ecma_parser = { workspace = true } +swc_ecma_transforms_base = { workspace = true } +swc_ecma_transforms_testing = { workspace = true } +testing = { workspace = true } diff --git a/packages/swc-sdk/transform/src/config.rs b/packages/swc-sdk/transform/src/config.rs new file mode 100644 index 000000000..cda3d4aad --- /dev/null +++ b/packages/swc-sdk/transform/src/config.rs @@ -0,0 +1,42 @@ +use default_from_serde::SerdeDefault; +use serde::Deserialize; +use swc_atoms::Atom; + +#[derive(Debug, Clone, Deserialize, SerdeDefault)] +pub struct Config { + #[serde(default)] + pub flag: FlagConfig, + + /// Drop imports from the following modules. + #[serde(default = "default_remove_imports_from")] + pub remove_imports_from: Vec, +} + +#[derive(Debug, Clone, Deserialize, SerdeDefault)] +pub struct FlagConfig { + /// If true, + /// + /// - the variable name must be an identifier. + #[serde(default)] + pub strict: bool, + + #[serde(default = "default_flag_import_source")] + pub import_sources: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ImportItem { + pub module: Atom, + pub name: Atom, +} + +fn default_remove_imports_from() -> Vec { + vec![Atom::new("@swc/sdk/annotations")] +} + +fn default_flag_import_source() -> Vec { + vec![ImportItem { + module: Atom::new("@swc/sdk/flag"), + name: Atom::new("flag"), + }] +} diff --git a/packages/swc-sdk/transform/src/flag.rs b/packages/swc-sdk/transform/src/flag.rs new file mode 100644 index 000000000..02309ea82 --- /dev/null +++ b/packages/swc-sdk/transform/src/flag.rs @@ -0,0 +1,158 @@ +use swc_common::{comments::Comments, errors::HANDLER, Spanned}; +use swc_ecma_ast::{ + Expr, KeyValueProp, ObjectLit, Pat, Prop, PropName, PropOrSpread, VarDeclarator, +}; +use swc_ecma_utils::ExprFactory; + +use crate::SwcSdkTransform; + +impl SwcSdkTransform +where + C: Comments, +{ + /// + /// ## Cases + /// + /// ### Empty arugments + /// + /// ```js + /// + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag(); + /// ``` + /// + /// becomes + /// + /// ```js + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag({ + /// key: "foo", + /// }); + /// ``` + /// + /// ### With arguments + /// + /// ```js + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag({ + /// decide: () => false, + /// }); + /// ``` + /// + /// becomes + /// + /// ```js + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag({ + /// key: "foo", + /// decide: () => false, + /// }); + /// ``` + /// + /// ### With custom adapter + /// + /// + /// ```js + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag(someAdapter({ + /// decide: () => false, + /// }); + /// ``` + /// + /// becomes + /// + /// ```js + /// import { flag } from "@swc/sdk/flag"; + /// + /// const foo = flag(someAdapter({ + /// key: "foo", + /// decide: () => false, + /// })); + /// ``` + pub(super) fn transform_flag(&mut self, v: &mut VarDeclarator) -> Option { + let init = v.init.as_deref_mut()?; + let call_expr = init.as_mut_call()?; + + let callee = call_expr.callee.as_mut_expr()?; + + let import_of_flag_callee = self + .imports + .is_in_import_items(callee, &self.config.flag.import_sources)?; + + let name = match &v.name { + Pat::Ident(i) => i.clone(), + _ => { + if self.config.flag.strict { + HANDLER.with(|handler| { + handler + .struct_span_err( + v.name.span(), + "The variable name for the `flag()` calls must be an identifier", + ) + .span_note(import_of_flag_callee, "flag() is imported here") + .emit(); + }); + } + return None; + } + }; + + let prop = PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident("key".into()), + value: name.sym.clone().into(), + }))); + + if call_expr.args.is_empty() { + call_expr.args.push( + ObjectLit { + props: vec![prop], + ..Default::default() + } + .as_arg(), + ); + } else if let Some(obj) = find_object(&mut call_expr.args[0].expr) { + if obj + .props + .iter() + .filter_map(|p| p.as_prop()) + .any(|p| match &**p { + Prop::KeyValue(KeyValueProp { key, .. }) => { + matches!(key, PropName::Ident(i) if i.sym == "key") + } + _ => false, + }) + { + return None; + } + + obj.props.push(prop); + } + + None + } +} + +fn find_object(arg: &mut Expr) -> Option<&mut ObjectLit> { + match arg { + Expr::Object(obj) => Some(obj), + Expr::Call(call) => { + if call.args.is_empty() { + call.args.push( + ObjectLit { + ..Default::default() + } + .as_arg(), + ); + } + + let arg = call.args.get_mut(0)?; + find_object(&mut arg.expr) + } + _ => None, + } +} diff --git a/packages/swc-sdk/transform/src/import_analyzer.rs b/packages/swc-sdk/transform/src/import_analyzer.rs new file mode 100644 index 000000000..21b3dcdba --- /dev/null +++ b/packages/swc-sdk/transform/src/import_analyzer.rs @@ -0,0 +1,111 @@ +use swc_atoms::Atom; +use swc_common::{ + collections::{AHashMap, AHashSet}, + Span, +}; +use swc_ecma_ast::*; +use swc_ecma_visit::{noop_visit_type, Visit, VisitWith}; + +use crate::config::ImportItem; + +#[derive(Debug, Default)] +pub(crate) struct ImportMap { + /// Map from module name to (module path, exported symbol, span) + imports: AHashMap, + + namespace_imports: AHashMap, + + imported_modules: AHashSet, +} + +impl ImportMap { + /// Returns true if `e` is an import of `orig_name` from `module`. + pub fn is_import(&self, e: &Expr, module: &Atom, orig_name: &Atom) -> Option { + match e { + Expr::Ident(i) => { + if let Some((i_src, i_sym, i_span)) = self.imports.get(&i.to_id()) { + if i_src == module && i_sym == orig_name { + Some(*i_span) + } else { + None + } + } else { + None + } + } + + Expr::Member(MemberExpr { + obj: box Expr::Ident(obj), + prop: MemberProp::Ident(prop), + .. + }) => { + if let Some((obj_src, obj_span)) = self.namespace_imports.get(&obj.to_id()) { + if obj_src == module && prop.sym == *orig_name { + Some(*obj_span) + } else { + None + } + } else { + None + } + } + + _ => None, + } + } + + pub fn is_in_import_items(&self, e: &Expr, import_items: &[ImportItem]) -> Option { + import_items + .iter() + .find_map(|item| self.is_import(e, &item.module, &item.name)) + } + + pub fn analyze(m: &Module) -> Self { + let mut data = ImportMap::default(); + + m.visit_with(&mut Analyzer { data: &mut data }); + + data + } +} + +struct Analyzer<'a> { + data: &'a mut ImportMap, +} + +impl Visit for Analyzer<'_> { + noop_visit_type!(); + + fn visit_import_decl(&mut self, import: &ImportDecl) { + self.data.imported_modules.insert(import.src.value.clone()); + + for s in &import.specifiers { + let (local, orig_sym) = match s { + ImportSpecifier::Named(ImportNamedSpecifier { + local, imported, .. + }) => match imported { + Some(imported) => (local.to_id(), orig_name(imported)), + _ => (local.to_id(), local.sym.clone()), + }, + ImportSpecifier::Default(s) => (s.local.to_id(), "default".into()), + ImportSpecifier::Namespace(s) => { + self.data + .namespace_imports + .insert(s.local.to_id(), (import.src.value.clone(), s.local.span)); + continue; + } + }; + + self.data + .imports + .insert(local, (import.src.value.clone(), orig_sym, import.span)); + } + } +} + +fn orig_name(n: &ModuleExportName) -> Atom { + match n { + ModuleExportName::Ident(v) => v.sym.clone(), + ModuleExportName::Str(v) => v.value.clone(), + } +} diff --git a/packages/swc-sdk/transform/src/lib.rs b/packages/swc-sdk/transform/src/lib.rs new file mode 100644 index 000000000..86fe41725 --- /dev/null +++ b/packages/swc-sdk/transform/src/lib.rs @@ -0,0 +1,70 @@ +#![feature(box_patterns)] +#![feature(never_type)] + +use swc_common::{comments::Comments, util::take::Take, Mark}; +use swc_ecma_ast::{Module, ModuleDecl, ModuleItem, VarDeclarator}; +use swc_ecma_visit::{VisitMut, VisitMutWith}; + +use crate::{config::Config, import_analyzer::ImportMap}; + +pub mod config; +mod flag; +mod import_analyzer; + +#[derive(Debug, Clone)] +pub struct Env { + pub unresolved_mark: Mark, +} + +pub fn swc_sdk(env: Env, config: Config, comments: C) -> impl VisitMut +where + C: Comments, +{ + SwcSdkTransform { + env, + config, + comments, + imports: Default::default(), + } +} + +/// Handles functions from `@swc/sdk`. +struct SwcSdkTransform +where + C: Comments, +{ + #[allow(unused)] + env: Env, + config: Config, + #[allow(unused)] + comments: C, + imports: ImportMap, +} + +impl VisitMut for SwcSdkTransform +where + C: Comments, +{ + fn visit_mut_var_declarator(&mut self, node: &mut VarDeclarator) { + self.transform_flag(node); + + node.visit_mut_children_with(self); + } + + fn visit_mut_module(&mut self, m: &mut Module) { + self.imports = ImportMap::analyze(m); + + m.visit_mut_children_with(self); + } + + fn visit_mut_module_item(&mut self, m: &mut ModuleItem) { + if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = m { + if self.config.remove_imports_from.contains(&import.src.value) { + m.take(); + return; + } + } + + m.visit_mut_children_with(self); + } +} diff --git a/packages/swc-sdk/transform/tests/fixture.rs b/packages/swc-sdk/transform/tests/fixture.rs new file mode 100644 index 000000000..d89b476b6 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture.rs @@ -0,0 +1,31 @@ +use std::path::PathBuf; + +use swc_common::{chain, Mark}; +use swc_ecma_parser::Syntax; +use swc_ecma_transforms_base::resolver; +use swc_ecma_transforms_testing::test_fixture; +use swc_ecma_visit::as_folder; + +#[testing::fixture("tests/fixture/**/input.js")] +fn pure(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); + test_fixture( + Syntax::default(), + &|tr| { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + chain!( + resolver(unresolved_mark, top_level_mark, false), + as_folder(swc_sdk::swc_sdk( + swc_sdk::Env { unresolved_mark }, + swc_sdk::config::Config::default(), + tr.comments.clone() + )) + ) + }, + &input, + &output, + Default::default(), + ); +} diff --git a/packages/swc-sdk/transform/tests/fixture/flag/arg-1/input.js b/packages/swc-sdk/transform/tests/fixture/flag/arg-1/input.js new file mode 100644 index 000000000..3f8ee128c --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/arg-1/input.js @@ -0,0 +1,7 @@ +import { flag } from "@swc/sdk/flag"; + +const foo = flag({ + decide: () => false, +}); + +console.log(foo); \ No newline at end of file diff --git a/packages/swc-sdk/transform/tests/fixture/flag/arg-1/output.js b/packages/swc-sdk/transform/tests/fixture/flag/arg-1/output.js new file mode 100644 index 000000000..c638dbc70 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/arg-1/output.js @@ -0,0 +1,6 @@ +import { flag } from "@swc/sdk/flag"; +const foo = flag({ + decide: ()=>false, + key: "foo" +}); +console.log(foo); diff --git a/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/input.js b/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/input.js new file mode 100644 index 000000000..3f8ee128c --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/input.js @@ -0,0 +1,7 @@ +import { flag } from "@swc/sdk/flag"; + +const foo = flag({ + decide: () => false, +}); + +console.log(foo); \ No newline at end of file diff --git a/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/output.js b/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/output.js new file mode 100644 index 000000000..c638dbc70 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/custom-adapter/output.js @@ -0,0 +1,6 @@ +import { flag } from "@swc/sdk/flag"; +const foo = flag({ + decide: ()=>false, + key: "foo" +}); +console.log(foo); diff --git a/packages/swc-sdk/transform/tests/fixture/flag/duplicate/input.js b/packages/swc-sdk/transform/tests/fixture/flag/duplicate/input.js new file mode 100644 index 000000000..49355de27 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/duplicate/input.js @@ -0,0 +1,8 @@ +import { flag } from "@swc/sdk/flag"; + +const foo = flag({ + decide: () => false, + key: 'custom' +}); + +console.log(foo); \ No newline at end of file diff --git a/packages/swc-sdk/transform/tests/fixture/flag/duplicate/output.js b/packages/swc-sdk/transform/tests/fixture/flag/duplicate/output.js new file mode 100644 index 000000000..588adc579 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/duplicate/output.js @@ -0,0 +1,6 @@ +import { flag } from "@swc/sdk/flag"; +const foo = flag({ + decide: ()=>false, + key: 'custom' +}); +console.log(foo); diff --git a/packages/swc-sdk/transform/tests/fixture/flag/no-arg/input.js b/packages/swc-sdk/transform/tests/fixture/flag/no-arg/input.js new file mode 100644 index 000000000..3f8ee128c --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/no-arg/input.js @@ -0,0 +1,7 @@ +import { flag } from "@swc/sdk/flag"; + +const foo = flag({ + decide: () => false, +}); + +console.log(foo); \ No newline at end of file diff --git a/packages/swc-sdk/transform/tests/fixture/flag/no-arg/output.js b/packages/swc-sdk/transform/tests/fixture/flag/no-arg/output.js new file mode 100644 index 000000000..c638dbc70 --- /dev/null +++ b/packages/swc-sdk/transform/tests/fixture/flag/no-arg/output.js @@ -0,0 +1,6 @@ +import { flag } from "@swc/sdk/flag"; +const foo = flag({ + decide: ()=>false, + key: "foo" +}); +console.log(foo);