Skip to content

Commit

Permalink
fix use declarations for ts and classes, use #static instead of var()
Browse files Browse the repository at this point in the history
  • Loading branch information
Benedikt Strehle committed Oct 12, 2024
1 parent 1ec48b9 commit 88dab3c
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 33 deletions.
105 changes: 101 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use swc_core::ecma::{ast::Program, transforms::testing::test, visit::FoldWith};
use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata};
use swc_ecma_parser::{EsSyntax, Syntax};
use swc_ecma_parser::{EsSyntax, Syntax, TsSyntax};
use visitor::TransformVisitor;

pub mod visitor;
Expand Down Expand Up @@ -649,7 +649,7 @@ test!(
t45,
r#"
renderFrontend(() => {
console.log(null, undefined, globalThis, window, true, false, NaN, Infinity, -Infinity);
console.log(null, undefined, this, globalThis, window, true, false, NaN, Infinity, -Infinity);
});
"#
);
Expand Down Expand Up @@ -753,8 +753,105 @@ test!(
t50,
r#"
<div>
<div>{val(x.y)}</div>
<div>{val(x())}</div>
<div>{x.y}</div>
<div>#static{x.y}</div>
<div>{x()}</div>
<div>#static{x()}</div>
<div>{x+1}</div>
<div>#static{x+1}</div>
<div>#static {x+1}</div>
<div>#static
{x+1}
</div>
<div>#staticxy
{x+1}
</div>
<div>#static{x+1}{x+2}</div>
</div>
"#
);


test!(
Syntax::Es(EsSyntax {
jsx: true,
..Default::default()
},),
|_| TransformVisitor,
t51,
r#"
<button
onclick:frontend={() => this.handleSettings()}>
Apply settings
</button>
"#
);


test!(
Syntax::Typescript(TsSyntax {
tsx: true,
..Default::default()
},),
|_| TransformVisitor,
t52,
r#"
run(() => {
const x1 = y as Z1;
const x2 = y satisfies Z2;
const x3: Z3 = y;
const x4: {_x: Z4} = y;
interface Interface {
x: Z5;
}
enum Color {
Red = 1,
Green = 2,
Blue = 3
}
console.log(Color.Red);
class MyClass extends ParentClass {
x: T;
y = this.x
method(methodParam: Z6) {
super.method(methodParam);
console.log(this);
alert(1)
}
static staticMethod(staticMethodParam: Z7) {
alert(2)
}
set setter(setterParam: Z8) {
alert(3)
}
static set staticSetter(staticSetterParam: Z9) {
alert(4)
}
#privateMethod(privateMethodParam: Z10) {
alert(5)
}
static #privateStaticMethod(privateStaticMethodParam: Z11) {
alert(6)
}
}
class MyClass2<T> extends ParentClass2<T> {
x: T;
}
console.log(new MyClass<W>());
function f(a: A, b: B) {
console.log(this);
return a + b;
}
})
"#
);
159 changes: 134 additions & 25 deletions src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ use swc_core::{
common::{util::take::Take, SyntaxContext, DUMMY_SP},
ecma::{
ast::{
ArrowExpr, AwaitExpr, BlockStmt, BlockStmtOrExpr, CallExpr, Callee, CatchClause, Expr, ExprOrSpread, ExprStmt, FnDecl, FnExpr, Id, Ident, JSXAttr, JSXAttrName, JSXAttrValue, JSXElement, JSXElementChild, JSXElementName, JSXEmptyExpr, JSXExpr, JSXExprContainer, JSXSpreadChild, Lit, MemberProp, Null, Number, ObjectPatProp, Pat, ReturnStmt, Stmt, Str, ThisExpr, VarDecl
ArrowExpr, AwaitExpr, BindingIdent, BlockStmt, BlockStmtOrExpr, CallExpr, Callee, CatchClause, ClassDecl, ClassMethod, Expr, ExprOrSpread, ExprStmt, FnDecl, FnExpr, Id, Ident, JSXAttr, JSXAttrName, JSXAttrValue, JSXElement, JSXElementChild, JSXElementName, JSXEmptyExpr, JSXExpr, JSXExprContainer, JSXSpreadChild, JSXText, Lit, MemberProp, Null, Number, ObjectPatProp, Param, ParamOrTsParamProp, Pat, PrivateMethod, ReturnStmt, Stmt, Str, ThisExpr, TsAsExpr, TsEnumDecl, TsInterfaceDecl, TsParamPropParam, TsType, TsTypeAliasDecl, VarDecl
},
visit::{Fold, FoldWith, Visit, VisitWith},
},
};



fn collect_params(params: &Vec<Pat>) -> Vec<Id> {
fn collect_params(params: &Vec<Pat>, add_this: bool) -> Vec<Id> {
let mut result: Vec<Id> = vec![];

if add_this {
let this = (Atom::from("this"), SyntaxContext::empty());
result.push(this);
}

for param in params {
match param {
Pat::Ident(i) => {
Expand Down Expand Up @@ -98,10 +103,11 @@ impl VariableCollector {
}

impl VariableCollector {

/**
* Visit a block or function body recursively
*/
fn visit_block_recursive(&mut self, params: &Vec<Pat>, visitable: &impl VisitWith<dyn Visit>) {
fn visit_block_recursive(&mut self, params: &Vec<Pat>, visitable: &impl VisitWith<dyn Visit>, has_this: bool) {
// recursively visit the arrow function body with a new collector
let mut collector = VariableCollector::new();

Expand All @@ -113,7 +119,7 @@ impl VariableCollector {
}

// add function parameters to the new collector
for param in collect_params(params) {
for param in collect_params(params, has_this) {
if !collector.variable_declarations.contains(&param) {
collector.variable_declarations.push(param);
}
Expand Down Expand Up @@ -157,6 +163,26 @@ impl Visit for VariableCollector {
call_expr.visit_children_with(self);
}

fn visit_ts_type(&mut self, node: &TsType) {
// ignore type annotations
}

fn visit_ts_type_alias_decl(&mut self, node: &TsTypeAliasDecl) {
// ignore type alias declarations
}

fn visit_ts_interface_decl(&mut self, node: &TsInterfaceDecl) {
// ignore interface declarations
}

fn visit_ts_enum_decl(&mut self, node: &TsEnumDecl) {
// remember enum declaration name
if !self.variable_declarations.contains(&node.id.to_id()) {
self.variable_declarations.push(node.id.to_id());
}
// ignore enum declarations
}

fn visit_jsx_element_name(&mut self, _name: &JSXElementName) {
// ignore jsx name as identifier
}
Expand All @@ -168,7 +194,8 @@ impl Visit for VariableCollector {
fn visit_this_expr(&mut self, _node: &ThisExpr) {
// add 'this' to used variables
let var = (Atom::from("this"), SyntaxContext::empty());
if !self.used_variables.contains(&var) {
if !self.used_variables.contains(&var) &&
!self.variable_declarations.contains(&var) {
self.used_variables.push(var);
}
}
Expand Down Expand Up @@ -270,26 +297,77 @@ impl Visit for VariableCollector {
// Visit the function body
self.visit_block_recursive(
&fn_decl.function.params.iter().map(|p| p.pat.clone()).collect(),
&fn_decl.function.body
&fn_decl.function.body,
true
);
}

fn visit_arrow_expr(&mut self, node: &ArrowExpr) {
// Visit function body recursively
self.visit_block_recursive(&node.params, &node.body);
}

fn visit_fn_expr(&mut self, node: &FnExpr) {
// Visit the function body
self.visit_block_recursive(
&node.function.params.iter().map(|p| p.pat.clone()).collect(),
&node.function.body
&node.function.body,
true
);
}

fn visit_constructor(&mut self, node: &swc_core::ecma::ast::Constructor) {
// Visit the constructor body
self.visit_block_recursive(
&node.params.iter().map(|p| match p {
ParamOrTsParamProp::Param(p) => p.pat.clone(),
ParamOrTsParamProp::TsParamProp(p) => match &p.param {
TsParamPropParam::Ident(i) => Pat::Ident(i.clone()),
TsParamPropParam::Assign(a) => Pat::Assign(a.clone()),
}
}).collect(),
&node.body,
true
);
}

fn visit_class_method(&mut self, node: &ClassMethod) {
// Visit the class method body
self.visit_block_recursive(
&node.function.params.iter().map(|p| p.pat.clone()).collect(),
&node.function.body,
true
);
}

fn visit_private_method(&mut self, node: &PrivateMethod) {
// Visit the private method body
self.visit_block_recursive(
&node.function.params.iter().map(|p| p.pat.clone()).collect(),
&node.function.body,
true
);
}

fn visit_arrow_expr(&mut self, node: &ArrowExpr) {
// Visit function body recursively
self.visit_block_recursive(&node.params, &node.body, false);
}


fn visit_class_decl(&mut self, node: &ClassDecl) {
// store name in variable_declarations
let var = node.ident.to_id();
if !self.variable_declarations.contains(&var) {
self.variable_declarations.push(var);
}
// Visit extended class
if let Some(super_class) = &node.class.super_class {
super_class.visit_with(self);
}

// Visit the class body
self.visit_block_recursive(&vec![], &node.class.body, true);
}

fn visit_block_stmt(&mut self, block_stmt: &BlockStmt) {
// Visit the block statement recursively
self.visit_block_recursive(&vec![], block_stmt);
self.visit_block_recursive(&vec![], block_stmt, false);
}

// catch expression, pass variables to block
Expand All @@ -301,7 +379,7 @@ impl Visit for VariableCollector {
params.push(param.clone());
}

self.visit_block_recursive(&params, &node.body);
self.visit_block_recursive(&params, &node.body, false);
}

}
Expand Down Expand Up @@ -405,15 +483,7 @@ impl TransformVisitor {
}
)
),

// is val() call expr -> ignore and just return val
Expr::Call(c)
if c.callee.is_expr()
&& c.callee.as_expr().unwrap().is_ident_ref_to("val") =>
{
e
}


// convert array.map(() => {}) to _$method(array, 'map', (() => {})
// TODO
Expr::Call(c)
Expand Down Expand Up @@ -566,7 +636,7 @@ impl TransformVisitor {
let mut collector = VariableCollector::new();

// add arrow function params to collector variable_declarations
for param in collect_params(&arrow.params) {
for param in collect_params(&arrow.params, false) {
if !collector.variable_declarations.contains(&param) {
collector.variable_declarations.push(param);
}
Expand Down Expand Up @@ -872,8 +942,47 @@ impl Fold for TransformVisitor {
}

fn fold_jsx_element_childs(&mut self, node: Vec<JSXElementChild>) -> Vec<JSXElementChild> {
let mut previous_was_hash_text = false;

node.into_iter()
.map(|child| child.fold_with(self))
.map(|mut child| {

if previous_was_hash_text {
// add a space after text containing "#"
match &child {
JSXElementChild::JSXExprContainer(_) => {
previous_was_hash_text = false;
return child;
},
_ => (),
};
}

// remember if element is text containing "#"
previous_was_hash_text = match &child {
JSXElementChild::JSXText(t) => t.value.trim_end().ends_with("#static"),
_ => false,
};

// remove the hash at the end of the text
if previous_was_hash_text {
child = match child {
JSXElementChild::JSXText(t) => {
let trimmed_val = t.value.trim_end();
let val: Atom = trimmed_val[..trimmed_val.len() - 7].into();

JSXElementChild::JSXText(JSXText {
span: t.span,
value: val.clone(),
raw: val,
})
},
_ => child,
};
}

child.fold_with(self)
})
.collect()
}

Expand Down
4 changes: 2 additions & 2 deletions tests/__swc_snapshots__/src/lib.rs/t45.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
renderFrontend(()=>{
use("silent-errors", console);
console.log(null, undefined, globalThis, window, true, false, NaN, Infinity, -Infinity);
use("silent-errors", console, this);
console.log(null, undefined, this, globalThis, window, true, false, NaN, Infinity, -Infinity);
});
Loading

0 comments on commit 88dab3c

Please sign in to comment.