# Source: https://swc.rs/docs/plugin/ecmascript/cheatsheet

# Plugin cheatsheet
💡 This page describes the known hard points for implementing plugins for ecmascript.
You may find documentation at [https://rustdoc.swc.rs/swc ]useful, especially if you are dealing with a visitor or `Id `issue.
## Understanding types []
### `JsWord `[]
`String `allocates, and ‘text’-s of source code has a special trait.
Those are lots of duplicates. Obviously, if your variable is named `foo `, you need to use `foo `multiple times.
So SWC interns the string to reduce the number of allocations.
`JsWord `is a string type that is interned.
You can create a `JsWord `from `&str `, or from a `String `.
Use `.into() `to convert to `JsWord `.
### `Ident `, `Id `, `Mark `, `SyntaxContext `[]
SWC uses a special system for managing variables.
See [the rustdoc for `Ident `]for details.
## Common issues []
### Getting AST representation of input []
[SWC Playground ]supports getting AST from the input code.
### Variable management of SWC []
### Error reporting []
See [rustdoc for `swc_common::errors::Handler `].
### Comparing `JsWord `with `&str `[]
If you don’t know what `JsWord `is, see [the rustdoc for swc_atoms ].
You can create `&str `by doing `&val `where `val `is a variable of type `JsWord `.
### Matching `Box<T> `[]
You will need to use `match `to match on various nodes, including `Box<T> `.
For performance reason, all expressions are stored in a boxed form. ( `Box<Expr> `)
SWC stores callee of call expressions as a `Callee `enum, and it has `Box<Expr> `.
```use swc_core :: ast ::* ; use swc_core :: visit :: { VisitMut , VisitMutWith }; struct MatchExample ; impl VisitMut for MatchExample { fn visit_mut_callee ( &mut self , callee : &mut Callee ) { callee . visit_mut_children_with ( self ); if let Callee :: Expr (expr) = callee { // expr is `Box<Expr>` if let Expr :: Ident (i) = &mut**expr { i . sym = "foo" . into (); } } } } ```
### Changing AST type []
If you want to change `ExportDefaultDecl `to `ExportDefaultExpr `, you should do it from `visit_mut_module_decl `.
### Inserting new nodes []
If you want to inject a new `Stmt `, you need to store the value in the struct, and inject it from `visit_mut_stmts `or `visit_mut_module_items `.
See [a destructuring core transform ].
```struct MyPlugin { stmts : Vec < Stmt >, } ```
## Tips []
### Decorators and TypeScript types []
Those are handled before your plugin is called. So you can’t access them from the Wasm plugin.
This design decision is made to make Wasm plugins easier to write and the Wasm binary smaller.
- Tracking issue: [https://github.com/swc-project/swc/issues/9132 ]

### Comments while testing []
You can make your pass generic over `C: Comments `. `test_fixture `provides `&mut Tester `, which has `comments `field.
- [Example pass ]
- [Testing for the test ]

### Apply `resolver `while testing []
SWC applies plugin after applying [`resolver `], so it’s better to test your transform with it.
As written in the rustdoc for the `resolver `, you have to use correct `SyntaxContext `if you need to reference global variable (e.g. `__dirname `, `require `) or top-level bindings written by the user.
```fn tr () -> impl Pass { ( resolver ( Mark :: new (), Mark :: new (), false ), // Most of transform does not care about globals so it does not need `SyntaxContext` your_transform () ) } test! ( Syntax :: default (), | _ | tr (), basic, // input "(function a ([a]) { a });" , // output "(function a([_a]) { _a; });" ); ```
### Make your handlers stateless []
Let’s say we are going to handle all array expressions in a function expression.
You can add a flag to the visitor to check if we are in a function expression.
You will be tempted to do
```struct Transform { in_fn_expr : bool } impl VisitMut for Transform { noop_visit_mut_type! (); fn visit_mut_fn_expr ( &mut self , n : &mut FnExpr ) { self . in_fn_expr = true ; n . visit_mut_children_with ( self ); self . in_fn_expr = false ; } fn visit_mut_array_lit ( &mut self , n : &mut ArrayLit ) { if self . in_fn_expr { // Do something } } } ```
but this cannot handle
```const foo = function () { const arr = [ 1 , 2 , 3 ]; const bar = function () {}; const arr2 = [ 2 , 4 , 6 ]; } ```
After visiting `bar `, `in_fn_expr `is `false `.
You have to do
```struct Transform { in_fn_expr : bool } impl VisitMut for Transform { noop_visit_mut_type! (); fn visit_mut_fn_expr ( &mut self , n : &mut FnExpr ) { let old_in_fn_expr = self . in_fn_expr; self . in_fn_expr = true ; n . visit_mut_children_with ( self ); self . in_fn_expr = old_in_fn_expr; } fn visit_mut_array_lit ( &mut self , n : &mut ArrayLit ) { if self . in_fn_expr { // Do something } } } ```
instead.
### Test with `@swc/jest `[]
You can test your transform with `@swc/jest `by adding your plugin to your `jest.config.js `.
jest.config.js ```module . exports = { rootDir: __dirname, moduleNameMapper: { "css-variable$" : "../../dist" , }, transform: { "^.+ \\ .(t|j)sx?$" : [ "@swc/jest" , { jsc: { experimental: { plugins: [ [ require. resolve ( "../../swc/target/wasm32-wasi/release/swc_plugin_css_variable.wasm" ), { basePath: __dirname, displayName: true , }, ], ], }, }, }, ], }, }; ```
See [https://github.com/jantimon/css-variable/blob/main/test/swc/jest.config.js ]
### `Path `is one of unix, while FileName can be one of host OS []
This is because linux version of `Path `code is used while compiling to wasm.
So you may need to replace `\\ `with `/ `in your plugin.
As `/ `is a valid path separator in windows, it’s valid thing to do.
### Comments []
A comment belongs to a span of the node. (lo for leading comments, hi for trailing comments)
If you want to add a leading comment to a node, you can do `PluginCommentsProxy.add_leading(span.lo, comment); `.
See [PluginCommentsProxy ].
## Ownership model (of rust) []
> This section is not about `swc `itself. But this is described at here because it’s the cause of almost all trickyness of APIs.

In rust, only one variable can *own *a data, and there’s at most one mutable reference to it.
Also, you need to *own *the value or have a mutable reference to it if you want to modify the data.
But there’s at most one owner/mutable reference, so it means if you have a mutable reference to a value, other code cannot modify the value.
Every update operation should performed by the code which *owns *the value or has a mutable reference to it.
So, some of babel APIs like `node.delete `is super tricky to implement.
As your code has ownership or mutable refernce to *some *part of AST, SWC cannot modify the AST.
## Tricky operations []
### Deleting node []
Let’s say, we want to drop the variable named `bar `in the code below.
```var foo = 1 ; var bar = 1 ; ```
There are two ways to do this.
#### Mark & Delete []
The first way is to mark it as *invalid *and delete it later.
This is typically more convenient.
```use swc_core :: ast ::* ; use swc_core :: visit :: { VisitMut , VisitMutWith }; impl VisitMut for Remover { fn visit_mut_var_declarator ( &mut self , v : &mut VarDeclarator ) { // This is not required in this example, but you typically need this. v . visit_mut_children_with ( self ); // v.name is `Pat`. // See https://rustdoc.swc.rs/swc_ecma_ast/enum.Pat.html match v . name { // If we want to delete the node, we should return false. // // Note the `&*` before i.sym. // The type of symbol is `JsWord`, which is an interned string. Pat :: Ident (i) => { if &* i . sym == "bar" { // Take::take() is a helper function, which stores invalid value in the node. // For Pat, it's `Pat::Invalid`. v . name . take (); } } _ => { // Noop if we don't want to delete the node. } } } fn visit_mut_var_declarators ( &mut self , vars : &mut Vec < VarDeclarator >) { vars . visit_mut_children_with ( self ); vars . retain ( | node | { // We want to remove the node, so we should return false. if node . name . is_invalid () { return false } // Return true if we want to keep the node. true }); } fn visit_mut_stmt ( &mut self , s : &mut Stmt ) { s . visit_mut_children_with ( self ); match s { Stmt :: Decl ( Decl :: Var (var)) => { if var . decls . is_empty () { // Variable declaration without declarator is invalid. // // After this, `s` becomes `Stmt::Empty`. s . take (); } } _ => {} } } fn visit_mut_stmts ( &mut self , stmts : &mut Vec < Stmt >) { stmts . visit_mut_children_with ( self ); // We remove `Stmt::Empty` from the statement list. // This is optional, but it's required if you don't want extra `;` in output. stmts . retain ( | s | { // We use `matches` macro as this match is trivial. ! matches! (s, Stmt :: Empty ( .. )) }); } fn visit_mut_module_items ( &mut self , stmts : &mut Vec < ModuleItem >) { stmts . visit_mut_children_with ( self ); // This is also required, because top-level statements are stored in `Vec<ModuleItem>`. stmts . retain ( | s | { // We use `matches` macro as this match is trivial. ! matches! (s, ModuleItem :: Stmt ( Stmt :: Empty ( .. ))) }); } } ```
#### Delete from the parent handler []
Another way to delete the node is deleting it from the parent handler.
This can be useful if you want to delete the node only if the parent node is specific type.
e.g. You don’t want to touch the variables in for loops while deleting free variable statements.
```use swc_core :: ast ::* ; use swc_core :: visit :: { VisitMut , VsiitMutWith }; struct Remover ; impl VisitMut for Remover { fn visit_mut_stmt ( &mut self , s : &mut Stmt ) { // This is not required in this example, but just to show that you typically need this. s . visit_mut_children_with ( self ); match s { Stmt :: Decl ( Decl :: Var (var)) => { if var . decls . len () == 1 { match var . decls[ 0 ] . name { Pat :: Ident (i) => { if &* i . sym == "bar" { s . take (); } } } } } _ => {} } } fn visit_mut_stmts ( &mut self , stmts : &mut Vec < Stmt >) { stmts . visit_mut_children_with ( self ); // We do same thing here. stmts . retain ( | s | { ! matches! (s, Stmt :: Empty ( .. )) }); } fn visit_mut_module_items ( &mut self , stmts : &mut Vec < ModuleItem >) { stmts . visit_mut_children_with ( self ); // We do same thing here. stmts . retain ( | s | { ! matches! (s, ModuleItem :: Stmt ( Stmt :: Empty ( .. ))) }); } } ```
### Referencing parent node from handler of child node []
This includes usage of `paths `and `scope `.
### Caching some information about an AST node []
You have two way to use informantion from a parent node.
For first, you can precompute information from the parent node handler.
Alternatively, you can clone the parent node and use it in the child node handler.
## Alternatives for babel APIs []
### `generateUidIdentifier `[]
This returns a unique identifier with a monotonically increasing integer suffix. `swc `does not provide API to do this, because there’s a very easy way to do this.
You can store an integer field in transformer type and use it while calling `quote_ident! `or `private_ident! `.
```struct Example { // You don't need to share counter. cnt : usize } impl Example { /// For properties, it's okay to use `quote_ident`. pub fn next_property_id ( &mut self ) -> Ident { self . cnt += 1 ; quote_ident! ( format! ( "$_css_{}" , self . cnt)) } /// If you want to create a safe variable, you should use `private_ident` pub fn next_variable_id ( &mut self ) -> Ident { self . cnt += 1 ; private_ident! ( format! ( "$_css_{}" , self . cnt)) } } ```
### `path.find `[]
Upward traversal is not supported by `swc `.
It’s because upward traversal requires storing information about parent at children nodes, which requires using types like `Arc `or `Mutex `in rust.
Instead of traversing upward, you should make it top-down.
For example, if you want to infer name of a jsx component from variable assignments or assignments, you can store `name `of component while visiting `VarDecl `and/or `AssignExpr `and use it from the component handler.
### `state.file.get `/ `state.file.set `[]
You can simply store the value in the transform struct as an instance of transform struct only process one file.