Skip to content

Commit

Permalink
Fix memo & import
Browse files Browse the repository at this point in the history
  • Loading branch information
mantou132 committed Nov 28, 2024
1 parent 37045db commit 8a012b0
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 115 deletions.
6 changes: 6 additions & 0 deletions crates/swc-plugin-gem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Gem plugin for SWC

- auto import
- support [memo getter](https://github.com/tc39/proposal-decorators/issues/509#issuecomment-2226967170)
- support minify style
- resolve full path (for esm)
2 changes: 1 addition & 1 deletion crates/swc-plugin-gem/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "swc-plugin-gem",
"version": "0.1.3",
"version": "0.1.4",
"description": "swc plugin for Gem",
"keywords": [
"swc-plugin",
Expand Down
1 change: 1 addition & 0 deletions crates/swc-plugin-gem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub fn process_transform(mut program: Program, data: TransformPluginProgramMetad

program.visit_mut_with(&mut (
Optional {
// 只支持原生装饰器或 `runPluginFirst`,不然被转译了,改写不了
enabled: true,
visitor: memo_transform(),
},
Expand Down
89 changes: 56 additions & 33 deletions crates/swc-plugin-gem/src/visitors/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use indexmap::{IndexMap, IndexSet};
use once_cell::sync::Lazy;
use regex::Regex;
use serde::Deserialize;
use std::{
collections::{HashMap, HashSet},
fs,
};
use std::{collections::HashMap, fs};
use swc_common::{SyntaxContext, DUMMY_SP};
use swc_core::ecma::visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_core::{
atoms::Atom,
ecma::visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
};
use swc_ecma_ast::{
Callee, Class, ClassDecl, ClassExpr, Decorator, Ident, ImportDecl, ImportNamedSpecifier,
ImportSpecifier, JSXElementName, ModuleDecl, ModuleItem, Str, TaggedTpl,
Callee, Class, ClassDecl, ClassExpr, Decorator, FnDecl, FnExpr, Id, Ident, ImportDecl,
ImportNamedSpecifier, ImportSpecifier, JSXElementName, ModuleDecl, ModuleItem, Str, TaggedTpl,
VarDeclarator,
};

static CUSTOM_ELEMENT_REGEX: Lazy<Regex> =
Expand Down Expand Up @@ -68,37 +69,34 @@ static GEM_AUTO_IMPORT_CONFIG: Lazy<AutoImportConfig> = Lazy::new(|| {
}
});

trait IdentString {
fn to_name(&self) -> String;
}

impl IdentString for Ident {
fn to_name(&self) -> String {
self.sym.as_str().into()
}
}

#[derive(Default)]
pub struct TransformVisitor {
used_members: IndexMap<String, SyntaxContext>,
imported_members: HashSet<String>,
used_members: IndexSet<Id>,
defined_members: IndexSet<Id>,
used_elements: IndexSet<String>,
}

impl TransformVisitor {
fn inset_used_member(&mut self, ident: &Ident) {
let name = ident.to_name();
// 只保存顶层使用成员,会复用 SyntaxContext
if !self.used_members.contains_key(&name) {
self.used_members.insert(name, ident.ctxt);
}
self.used_members.insert(ident.to_id());
}

fn inset_defined_member(&mut self, ident: &Ident) {
self.defined_members.insert(ident.to_id());
}

fn visit_mut_class(&mut self, node: &Box<Class>) {
if let Some(expr) = &node.super_class {
if let Some(ident) = expr.as_ident() {
self.inset_used_member(ident);
}
// support decorators transform
// class PageHomeElement extends (_GemElement = GemElement) {}
if let Some(assign) = expr.as_assign() {
if let Some(ident) = assign.right.as_ident() {
self.inset_used_member(ident);
}
}
}
}
}
Expand All @@ -107,7 +105,7 @@ impl VisitMut for TransformVisitor {
noop_visit_mut_type!();

fn visit_mut_import_specifier(&mut self, node: &mut ImportSpecifier) {
self.imported_members.insert(node.local().to_name());
self.inset_defined_member(node.local());
}

fn visit_mut_callee(&mut self, node: &mut Callee) {
Expand Down Expand Up @@ -150,41 +148,66 @@ impl VisitMut for TransformVisitor {
node.visit_mut_children_with(self);

self.visit_mut_class(&node.class);
self.inset_defined_member(&node.ident);
}

fn visit_mut_class_expr(&mut self, node: &mut ClassExpr) {
node.visit_mut_children_with(self);

self.visit_mut_class(&node.class);
if let Some(ident) = &node.ident {
self.inset_defined_member(ident);
}
}

fn visit_mut_fn_decl(&mut self, node: &mut FnDecl) {
node.visit_mut_children_with(self);

self.inset_defined_member(&node.ident);
}

fn visit_mut_fn_expr(&mut self, node: &mut FnExpr) {
node.visit_mut_children_with(self);

if let Some(ident) = &node.ident {
self.inset_defined_member(ident);
}
}

fn visit_mut_var_declarator(&mut self, node: &mut VarDeclarator) {
node.visit_mut_children_with(self);

if let Some(ident) = &node.name.as_ident() {
self.inset_defined_member(&ident);
}
}

// https://swc.rs/docs/plugin/ecmascript/cheatsheet#inserting-new-nodes
// 只处理模块
fn visit_mut_module_items(&mut self, node: &mut Vec<ModuleItem>) {
// TODO: 收集顶层声明, 防止重复声明
node.visit_mut_children_with(self);

let mut out: Vec<ImportDecl> = vec![];
let mut available_import: HashMap<String, IndexMap<String, (String, &SyntaxContext)>> =
let mut available_import: HashMap<String, IndexMap<&Atom, (&Atom, &SyntaxContext)>> =
HashMap::new();

for (used_member, ctx) in self.used_members.iter() {
if !self.imported_members.contains(used_member) {
let pkg = GEM_AUTO_IMPORT_CONFIG.member_map.get(used_member);
for id in self.used_members.iter() {
if !self.defined_members.contains(id) {
let pkg = GEM_AUTO_IMPORT_CONFIG.member_map.get(id.0.as_str());
if let Some(pkg) = pkg {
let set = available_import
.entry(pkg.into())
.or_insert(Default::default());
set.insert(used_member.into(), (used_member.into(), ctx));
set.insert(&id.0, (&id.0, &id.1));
}
}
}

for (pkg, set) in available_import {
let mut specifiers: Vec<ImportSpecifier> = vec![];
for (member, (_, ctx)) in set {
for (member, (_member_as, ctx)) in set {
specifiers.push(ImportSpecifier::Named(ImportNamedSpecifier {
local: Ident::new(member.into(), DUMMY_SP, ctx.clone()),
local: Ident::new(member.clone(), DUMMY_SP, ctx.clone()),
span: DUMMY_SP,
imported: None,
is_type_only: false,
Expand Down
140 changes: 77 additions & 63 deletions crates/swc-plugin-gem/src/visitors/memo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,11 @@ use swc_core::{
ecma::visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
};
use swc_ecma_ast::{
AssignExpr, AssignOp, AssignTarget, Callee, Class, ClassDecl, ClassExpr, ClassMember, Expr,
MemberExpr, MemberProp, MethodKind, PrivateMethod, PrivateName, PrivateProp, ReturnStmt,
SimpleAssignTarget, ThisExpr,
AssignExpr, AssignOp, AssignTarget, BlockStmt, Callee, Class, ClassDecl, ClassExpr,
ClassMember, Decorator, Expr, ExprStmt, Function, MemberExpr, MemberProp, MethodKind,
PrivateMethod, PrivateName, PrivateProp, SimpleAssignTarget, Stmt, ThisExpr,
};

#[derive(Default)]
struct TransformVisitor {
private_props: Vec<Atom>,
current_index: usize,
}

impl TransformVisitor {
fn current_method(&self) -> &Atom {
self.private_props.get(self.current_index).unwrap()
}

fn start_visit_mut_class(&mut self) {
self.private_props = Vec::new();
self.current_index = 0;
}

fn visit_mut_class(&mut self, node: &mut Box<Class>) {
for prop in self.private_props.iter() {
node.body.push(ClassMember::PrivateProp(PrivateProp {
key: PrivateName {
span: DUMMY_SP,
name: prop.as_str().into(),
},
..Default::default()
}));
}
}
}

fn is_memo_getter(node: &mut PrivateMethod) -> bool {
if node.kind != MethodKind::Getter {
return false;
Expand All @@ -55,57 +26,100 @@ fn is_memo_getter(node: &mut PrivateMethod) -> bool {
})
}

#[derive(Default)]
struct TransformVisitor {
private_props: Vec<(Atom, Vec<Decorator>, String)>,
}

impl TransformVisitor {
fn append_private_field(&mut self, node: &mut Box<Class>) {
while let Some((prop, decorators, getter_name)) = self.private_props.pop() {
node.body.push(ClassMember::PrivateMethod(PrivateMethod {
span: DUMMY_SP,
kind: MethodKind::Method,
key: PrivateName {
span: DUMMY_SP,
name: format!("_{}", getter_name).into(),
},
function: Box::new(Function {
span: DUMMY_SP,
params: vec![],
decorators,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: AssignOp::Assign,
left: AssignTarget::Simple(SimpleAssignTarget::Member(
MemberExpr {
span: DUMMY_SP,
obj: ThisExpr { span: DUMMY_SP }.into(),
prop: MemberProp::PrivateName(PrivateName {
span: DUMMY_SP,
name: prop.clone(),
}),
},
)),
right: Box::new(Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: ThisExpr { span: DUMMY_SP }.into(),
prop: MemberProp::PrivateName(PrivateName {
span: DUMMY_SP,
name: getter_name.as_str().into(),
}),
})),
})),
})],
..Default::default()
}),
..Default::default()
}),
..Default::default()
}));
node.body.push(ClassMember::PrivateProp(PrivateProp {
key: PrivateName {
span: DUMMY_SP,
name: prop.as_str().into(),
},
..Default::default()
}));
}
}
}

impl VisitMut for TransformVisitor {
noop_visit_mut_type!();

fn visit_mut_class_decl(&mut self, node: &mut ClassDecl) {
self.start_visit_mut_class();

node.visit_mut_children_with(self);

self.visit_mut_class(&mut node.class);
self.append_private_field(&mut node.class);
}

fn visit_mut_class_expr(&mut self, node: &mut ClassExpr) {
self.start_visit_mut_class();

node.visit_mut_children_with(self);

self.visit_mut_class(&mut node.class);
self.append_private_field(&mut node.class);
}

fn visit_mut_private_method(&mut self, node: &mut PrivateMethod) {
if is_memo_getter(node) {
self.current_index = self.private_props.len();
self.private_props.push(node.key.name.clone());

node.visit_mut_children_with(self);
let name = node.key.name.clone();
let getter_name = format!("_{}", name);

node.key = PrivateName {
span: DUMMY_SP,
name: format!("_{}", node.key.name).into(),
}
}
}
name: getter_name.clone().into(),
};

let mut decorators = Vec::new();
decorators.append(&mut node.function.decorators);

fn visit_mut_return_stmt(&mut self, node: &mut ReturnStmt) {
// why? 类表达式会直接走到这里来?
if self.private_props.is_empty() {
return;
self.private_props
.push((name.clone(), decorators, getter_name));
}
node.arg = Some(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: AssignOp::Assign,
left: AssignTarget::Simple(SimpleAssignTarget::Member(MemberExpr {
span: DUMMY_SP,
obj: ThisExpr { span: DUMMY_SP }.into(),
prop: MemberProp::PrivateName(PrivateName {
span: DUMMY_SP,
name: self.current_method().clone(),
}),
})),
right: node.arg.clone().unwrap_or(Expr::undefined(DUMMY_SP)),
})));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ class MyElement extends GemElement {
return html`<div style=${styleMap({})}></div>`;
}
}
class MyElement1 extends (_GemElement = GemElement) {}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ class MyElement extends GemElement {
render() {
return html`<div style=${styleMap({})}></div>`;
}
}
}
class MyElement1 extends (_GemElement = GemElement) {}
Loading

0 comments on commit 8a012b0

Please sign in to comment.