Skip to content

Commit

Permalink
implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko committed Aug 6, 2024
1 parent 638b2a0 commit 392441d
Show file tree
Hide file tree
Showing 3 changed files with 369 additions and 108 deletions.
2 changes: 1 addition & 1 deletion src/js_macro_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,6 @@ impl<'a> Fold for JsMacroFolder<'a> {
);
}

expr
expr.fold_children_with(self)
}
}
272 changes: 169 additions & 103 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![feature(is_some_and)]

use std::collections::HashSet;
use swc_core::common::{DUMMY_SP};
use swc_core::common::DUMMY_SP;

use swc_core::plugin::errors::HANDLER;
use swc_core::{
ecma::{
ast::*,
utils::{quote_ident},
utils::quote_ident,
visit::{Fold, FoldWith, VisitWith},
},
plugin::{
Expand All @@ -33,6 +34,23 @@ use builder::*;
use js_macro_folder::JsMacroFolder;
use jsx_visitor::TransJSXVisitor;

pub struct IdentReplacer {
from: Id,
to: Ident,
}
impl IdentReplacer {
// pub fn new(from: Id, to: Ident) -> {}
}
impl Fold for IdentReplacer {
fn fold_ident(&mut self, n: Ident) -> Ident {
if n.to_id() == self.from {
return self.to.clone();
}

n
}
}

#[derive(Default)]
pub struct LinguiMacroFolder {
has_lingui_macro_imports: bool,
Expand Down Expand Up @@ -110,88 +128,21 @@ impl LinguiMacroFolder {
},
};
}
}

impl<'a> Fold for LinguiMacroFolder {
fn fold_module_items(&mut self, mut n: Vec<ModuleItem>) -> Vec<ModuleItem> {
let (i18n_source, i18n_export) = self.ctx.options.runtime_modules.i18n.clone();
let (trans_source, trans_export) = self.ctx.options.runtime_modules.trans.clone();
let (use_lingui_source, use_lingui_export) = self.ctx.options.runtime_modules.use_lingui.clone();

let mut insert_index: usize = 0;
let mut index = 0;

n.retain(|m| {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(imp)) = m {
// drop macro imports
if &imp.src.value == "@lingui/macro" || &imp.src.value == "@lingui/core/macro" || &imp.src.value == "@lingui/react/macro" {
self.has_lingui_macro_imports = true;
self.ctx.register_macro_import(imp);
insert_index = index;
return false;
}
}

index += 1;
true
});

n = n.fold_children_with(self);

if self.ctx.should_add_18n_import {
n.insert(
insert_index,
create_import(i18n_source.into(), quote_ident!(i18n_export[..]), self.ctx.runtime_idents.i18n.clone()),
);
}

if self.ctx.should_add_trans_import {
n.insert(
insert_index,
create_import(trans_source.into(), quote_ident!(trans_export[..]), self.ctx.runtime_idents.trans.clone()),
);
}

if self.ctx.should_add_uselingui_import {
n.insert(
insert_index,
create_import(use_lingui_source.into(), quote_ident!(use_lingui_export[..]), self.ctx.runtime_idents.use_lingui.clone()),
);
}

n
}
fn fold_arrow_expr(&mut self, n: ArrowExpr) -> ArrowExpr {
println!("arrow expr");
n.fold_children_with(self)
}
// fn fold_fn_decl(&mut self, n: FnDecl) -> FnDecl {
// println!("fold_fn_decl");
// n.fold_children_with(self)
// }
//
fn fold_fn_expr(&mut self, n: FnExpr) -> FnExpr {
println!("fold_fn_expr");
n.fold_children_with(self)
}
fn fold_function(&mut self, n: Function) -> Function {
// If no package that we care about is imported, skip the following
// transformation logic.
if !self.has_lingui_macro_imports {
return n;
}

pub fn handle_use_lingui(&mut self, n: BlockStmt) -> BlockStmt {
let mut ctx = self.ctx.clone();

if let Some(body) = n.body {
let stmts: Vec<Stmt> = body
let mut ident_replacer: Option<IdentReplacer> = None;

let stmts: Vec<Stmt> = n
.stmts
.into_iter()
.map(|stmt| {
return match stmt {
Stmt::Decl(Decl::Var(var_decl)) => {
let decl = *var_decl;

let underscore_ident = quote_ident!("$__");
let decls: Vec<VarDeclarator> = decl.decls.into_iter().map(|declarator| {
if let Some(init) = &declarator.init {
let expr = init.as_ref();
Expand All @@ -200,10 +151,10 @@ impl<'a> Fold for LinguiMacroFolder {
if match_callee_name(call, |n| {
self.ctx.is_lingui_ident("useLingui", n)
})
.is_some()
.is_some()
{
if let Pat::Object(obj_pat) = declarator.name {
let mew_props: Vec<ObjectPatProp> =
let mut new_props: Vec<ObjectPatProp> =
obj_pat.props.into_iter().map(|prop| {
return get_local_ident_from_object_pat_prop(&prop, "t")
.and_then(|ident| {
Expand All @@ -214,22 +165,31 @@ impl<'a> Fold for LinguiMacroFolder {

let new_i18n_ident = quote_ident!(ident.span, "$__i18n");

ident_replacer = Some(IdentReplacer {
from: ident.to_id(),
to: underscore_ident.clone(),
});

self.ctx.should_add_uselingui_import = true;
ctx.runtime_idents.i18n = new_i18n_ident.clone();

return Some(ObjectPatProp::KeyValue(
KeyValuePatProp {
value: Box::new(Pat::Ident(BindingIdent {
id: new_i18n_ident,
type_ann: None,
})),
value: Box::new(Pat::Ident(new_i18n_ident.into())),
key: PropName::Ident(quote_ident!("i18n")),
},
))
})
.unwrap_or(prop);
}).collect();

new_props.push(ObjectPatProp::KeyValue(
KeyValuePatProp {
value: Box::new(Pat::Ident(underscore_ident.clone().into())),
key: PropName::Ident(quote_ident!("_")),
},
));

return VarDeclarator {
init: Some(Box::new(Expr::Call(CallExpr {
callee: Callee::Expr(Box::new(Expr::Ident(ctx.runtime_idents.use_lingui.clone()))),
Expand All @@ -242,17 +202,25 @@ impl<'a> Fold for LinguiMacroFolder {
optional: false,
type_ann: None,
span: DUMMY_SP,
props: mew_props
props: new_props

}),
}
} else {
//panic: useLingui could be used only with object desctructuring
HANDLER.with(|h| {
h.struct_span_warn(decl.span, "Unsupported Syntax")
.note(
r#"You have to destructure `t` when using the `useLingui` macro, i.e:
const { t } = useLingui()
or
const { t: _ } = useLingui()"#)
.emit()
});
}
}
}
}

return declarator;
}).collect();

Expand All @@ -268,28 +236,126 @@ impl<'a> Fold for LinguiMacroFolder {
})
.collect();

let new_func = Function {
params: n.params,
body: Some(BlockStmt {
span: body.span,
stmts,
}),
decorators: n.decorators,
span: n.span,
is_async: n.is_async,
is_generator: n.is_generator,
return_type: n.return_type,
type_params: n.type_params,
};
let mut block = BlockStmt {
span: n.span,
stmts,
};

// use lingui matched above
if ident_replacer.is_some() {
block = block
.fold_children_with(&mut JsMacroFolder::new(&mut ctx))
// replace other
.fold_children_with(&mut ident_replacer.unwrap());
}

return block.fold_children_with(self);
}
}

impl<'a> Fold for LinguiMacroFolder {
fn fold_module_items(&mut self, mut n: Vec<ModuleItem>) -> Vec<ModuleItem> {
let (i18n_source, i18n_export) = self.ctx.options.runtime_modules.i18n.clone();
let (trans_source, trans_export) = self.ctx.options.runtime_modules.trans.clone();
let (use_lingui_source, use_lingui_export) =
self.ctx.options.runtime_modules.use_lingui.clone();

let mut insert_index: usize = 0;
let mut index = 0;

n.retain(|m| {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(imp)) = m {
// drop macro imports
if &imp.src.value == "@lingui/macro"
|| &imp.src.value == "@lingui/core/macro"
|| &imp.src.value == "@lingui/react/macro"
{
self.has_lingui_macro_imports = true;
self.ctx.register_macro_import(imp);
insert_index = index;
return false;
}
}

index += 1;
true
});

n = n.fold_children_with(self);

let mut folder = JsMacroFolder::new(&mut ctx);
return new_func
.fold_children_with(&mut folder)
.fold_children_with(self);
// folder.fold_expr(n).fold_children_with(self)
if self.ctx.should_add_18n_import {
n.insert(
insert_index,
create_import(
i18n_source.into(),
quote_ident!(i18n_export[..]),
self.ctx.runtime_idents.i18n.clone(),
),
);
}

if self.ctx.should_add_trans_import {
n.insert(
insert_index,
create_import(
trans_source.into(),
quote_ident!(trans_export[..]),
self.ctx.runtime_idents.trans.clone(),
),
);
}

if self.ctx.should_add_uselingui_import {
n.insert(
insert_index,
create_import(
use_lingui_source.into(),
quote_ident!(use_lingui_export[..]),
self.ctx.runtime_idents.use_lingui.clone(),
),
);
}

n
}
fn fold_arrow_expr(&mut self, n: ArrowExpr) -> ArrowExpr {
// If no package that we care about is imported, skip the following
// transformation logic.
if !self.has_lingui_macro_imports {
return n;
}

let mut func = n;

if func.body.is_block_stmt() {
let block = func.body.block_stmt().unwrap();

func = ArrowExpr {
body: Box::new(BlockStmtOrExpr::BlockStmt(self.handle_use_lingui(block))),
..func
}
}

func.fold_children_with(self)
}

fn fold_function(&mut self, n: Function) -> Function {
// If no package that we care about is imported, skip the following
// transformation logic.
if !self.has_lingui_macro_imports {
return n;
}

let mut func = n;

if let Some(body) = func.body {
func = Function {
body: Some(self.handle_use_lingui(body)),
..func
};
}

n.fold_children_with(self)
func.fold_children_with(self)
}

fn fold_expr(&mut self, expr: Expr) -> Expr {
Expand Down
Loading

0 comments on commit 392441d

Please sign in to comment.