Skip to content
This repository was archived by the owner on Oct 9, 2024. It is now read-only.

Commit 5fb4b7c

Browse files
committed
Complete function detection logic
1 parent ee93eea commit 5fb4b7c

File tree

16 files changed

+419
-150
lines changed

16 files changed

+419
-150
lines changed

.vscode/settings.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
{
22
"typescript.tsdk": "node_modules/typescript/lib",
3-
"rust-analyzer.linkedProjects": ["./Cargo.toml"]
3+
"rust-analyzer.linkedProjects": ["./Cargo.toml"],
4+
"rust-analyzer.check.command": "clippy",
5+
"[rust]": {
6+
"editor.defaultFormatter": "rust-lang.rust-analyzer",
7+
"editor.formatOnSave": true,
8+
}
49
}

packages/swc-plugin/transform/src/lib.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use std::{cell::RefCell, ptr::null, rc::Rc};
1+
use std::{cell::RefCell, rc::Rc};
22

33
use serde::Deserialize;
4-
use swc_common::{chain, comments::Comments, FileName};
5-
use swc_ecma_visit::{Fold, VisitMut};
4+
use swc_common::{comments::Comments, FileName};
5+
use swc_ecma_visit::Fold;
66

77
pub use crate::utils::{component_finder, State};
88

@@ -28,15 +28,12 @@ fn default_skip_components() -> Vec<String> {
2828
vec![]
2929
}
3030

31-
pub fn react_unforget<C>(_file_name: FileName, config: Config, _comments: C) -> impl Fold + VisitMut
31+
pub fn react_unforget<C>(_file_name: FileName, config: Config, _comments: C) -> impl Fold
3232
where
3333
C: Comments,
3434
{
3535
let state: Rc<RefCell<State>> = Default::default();
3636
let config = Rc::new(config);
3737

38-
println!("Calling component_finder");
39-
let finder = crate::utils::component_finder(config, state);
40-
41-
return finder
38+
crate::utils::component_finder(config, state)
4239
}
Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,58 @@
1-
pub struct Component {}
1+
#[allow(clippy::all)]
2+
use std::borrow::Borrow;
3+
use std::cell::RefCell;
4+
use std::collections::HashMap;
5+
use std::rc::Rc;
6+
7+
use swc_ecma_ast::{BlockStmt, BlockStmtOrExpr, Stmt};
8+
9+
use crate::models::component_segment::ComponentSegment;
10+
use crate::utils::babel_shims::GenericFn;
11+
12+
#[derive(Debug)]
13+
pub struct Component {
14+
pub name: String,
15+
pub is_hook: bool,
16+
pub function: Rc<RefCell<GenericFn>>,
17+
segments: HashMap<Stmt, ComponentSegment>,
18+
}
19+
20+
impl Component {
21+
pub fn new(name: String, is_hook: bool, function: Rc<RefCell<GenericFn>>) -> Self {
22+
function.borrow_mut().ensure_body_block_stmt();
23+
println!("Creating component: {}", name);
24+
25+
Self {
26+
name,
27+
is_hook,
28+
function,
29+
segments: Default::default(),
30+
}
31+
}
32+
33+
pub fn add_segment(&mut self, stmt: Stmt, segment: ComponentSegment) {
34+
self.segments.insert(stmt, segment);
35+
}
36+
37+
pub fn get_segments(&self) -> &HashMap<Stmt, ComponentSegment> {
38+
self.segments.borrow()
39+
}
40+
41+
pub fn get_name(&self) -> &str {
42+
&self.name
43+
}
44+
45+
pub fn prepare(&self) {}
46+
47+
fn get_body_block_stmt(&self) -> BlockStmt {
48+
let body = self.function.as_ref().borrow().get_body();
49+
50+
match body {
51+
Some(body) => match body {
52+
BlockStmtOrExpr::BlockStmt(block_stmt) => block_stmt,
53+
BlockStmtOrExpr::Expr(_) => panic!("Expected block statement"),
54+
},
55+
None => panic!("Function has no body"),
56+
}
57+
}
58+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[derive(Debug)]
2+
pub struct ComponentSegment {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod component;
2+
pub mod component_segment;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pub mod generic;
1+
pub mod generic;
2+
pub mod returns_like_component;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use swc_ecma_ast::{ArrowExpr, Expr, FnDecl, Function, Lit, ReturnStmt};
2+
use swc_ecma_utils::ExprExt;
3+
use swc_ecma_visit::{noop_visit_type, Visit};
4+
5+
pub struct ReturnTypeVisitorState {
6+
return_type_mathces_component: bool,
7+
return_count: u32,
8+
}
9+
10+
impl Default for ReturnTypeVisitorState {
11+
fn default() -> Self {
12+
Self {
13+
return_type_mathces_component: true,
14+
return_count: 0,
15+
}
16+
}
17+
}
18+
19+
impl ReturnTypeVisitorState {
20+
pub fn does_return_type_match_component(&self) -> bool {
21+
self.return_type_mathces_component && self.return_count > 0
22+
}
23+
}
24+
25+
#[derive(Default)]
26+
pub struct VisitReturnStatements {
27+
pub state: ReturnTypeVisitorState,
28+
}
29+
30+
impl Visit for VisitReturnStatements {
31+
noop_visit_type!();
32+
33+
// We don't want to visit nested functions
34+
fn visit_fn_decl(&mut self, _: &FnDecl) {}
35+
fn visit_function(&mut self, _: &Function) {}
36+
fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
37+
38+
fn visit_return_stmt(&mut self, n: &ReturnStmt) {
39+
self.state.return_count += 1;
40+
41+
if let Some(arg) = &n.arg {
42+
if !is_component_return_type(arg.as_expr()) {
43+
self.state.return_type_mathces_component = false;
44+
}
45+
}
46+
}
47+
}
48+
49+
/**
50+
* Check if the given expression qualifies as a component return type.
51+
*/
52+
pub fn is_component_return_type(node: &Expr) -> bool {
53+
if let Expr::JSXElement(_) = node {
54+
return true;
55+
}
56+
57+
if let Expr::JSXFragment(_) = node {
58+
return true;
59+
}
60+
61+
if let Expr::Lit(literal) = node {
62+
return matches!(literal, Lit::Null(_));
63+
}
64+
65+
false
66+
}
Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,70 @@
1-
use swc_ecma_ast::{ArrowExpr, Function};
1+
use std::ops::Deref;
22

3-
pub enum Fn {
4-
Function(Function),
5-
ArrowFunction(ArrowExpr),
3+
use swc_common::DUMMY_SP;
4+
use swc_ecma_ast::{ArrowExpr, BlockStmt, BlockStmtOrExpr, Function};
5+
use swc_ecma_utils::ExprFactory;
6+
use swc_ecma_visit::VisitWith;
7+
8+
use super::ast_tools::returns_like_component::{is_component_return_type, VisitReturnStatements};
9+
10+
#[derive(Debug, Clone)]
11+
pub enum GenericFn {
12+
Function(Box<Function>),
13+
ArrowFunction(Box<ArrowExpr>),
14+
}
15+
16+
impl GenericFn {
17+
pub fn get_body(&self) -> Option<BlockStmtOrExpr> {
18+
match self {
19+
GenericFn::Function(f) => {
20+
let body = &f.body;
21+
body.as_ref()
22+
.map(|body| BlockStmtOrExpr::BlockStmt(body.clone()))
23+
}
24+
GenericFn::ArrowFunction(f) => Some(f.body.deref().clone()),
25+
}
26+
}
27+
28+
pub fn update_body(&mut self, body: BlockStmt) {
29+
match self {
30+
GenericFn::Function(f) => {
31+
f.body = Some(body);
32+
}
33+
GenericFn::ArrowFunction(f) => {
34+
f.body = Box::new(body.into());
35+
}
36+
}
37+
}
38+
39+
pub fn ensure_body_block_stmt(&mut self) {
40+
if let GenericFn::ArrowFunction(arrow_function) = self {
41+
let body = arrow_function.body.deref();
42+
if let BlockStmtOrExpr::Expr(expr) = body {
43+
let return_arg = expr.clone().into_return_stmt();
44+
let block_stmt = BlockStmt {
45+
span: DUMMY_SP,
46+
stmts: vec![return_arg.into()],
47+
};
48+
49+
self.update_body(block_stmt);
50+
}
51+
}
52+
}
53+
54+
pub fn return_type_matches_component(&self) -> bool {
55+
let fn_body = self.get_body();
56+
57+
if let Some(fn_body) = fn_body {
58+
match fn_body {
59+
BlockStmtOrExpr::BlockStmt(ref block_stmt) => {
60+
let mut visitor: VisitReturnStatements = Default::default();
61+
block_stmt.visit_children_with(&mut visitor);
62+
visitor.state.does_return_type_match_component()
63+
}
64+
BlockStmtOrExpr::Expr(ref expr) => is_component_return_type(expr.deref()),
65+
}
66+
} else {
67+
false
68+
}
69+
}
670
}

0 commit comments

Comments
 (0)