Skip to content

Commit

Permalink
Merge pull request #4 from bendtherules/return-multiple-nodes
Browse files Browse the repository at this point in the history
 Add section "return multiple nodes"
  • Loading branch information
Madou authored Jan 3, 2020
2 parents 0233b49 + d11d0d4 commit da6b0bd
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 2 deletions.
6 changes: 6 additions & 0 deletions example-transformers/return-multiple-node/source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let a = 1;
{
const a = 'abcd';
}
a = 2;
console.log(a);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let a = 1;
{
const a = 'abcd';
}
a = 2;
a = 2;
console.log(a);
console.log(a);
20 changes: 20 additions & 0 deletions example-transformers/return-multiple-node/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as ts from 'typescript';

const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
return sourceFile => {
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
// If it is a expression statement,
if (ts.isExpressionStatement(node)) {
// Return it twice.
// Effectively duplicating the statement
return [node, node];
}

return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sourceFile, visitor);
};
};

export default transformer;
8 changes: 8 additions & 0 deletions example-transformers/return-multiple-node/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "transformed",
"plugins": [{ "transform": "./transformer.ts", "type": "raw" }]
},
"files": ["source.ts"]
}
53 changes: 51 additions & 2 deletions translations/en/transformer-handbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -1086,11 +1086,60 @@ if (ts.isFunctionDeclaration(node)) {
#### Replacing a node with multiple nodes

> **TODO** - Is this possible?
Interestingly, a visitor function can also return a array of nodes instead of just one node.
That means, even though it gets one node as input, it can return multiple nodes which replaces that input node.

```ts
export type Visitor = (node: Node) => VisitResult<Node>;
export type VisitResult<T extends Node> = T | T[] | undefined;
```

Let's just replace every expression statement with two copies of the same statement (duplicating it) -

```ts
const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
return sourceFile => {
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
// If it is a expression statement,
if (ts.isExpressionStatement(node)) {
// Return it twice.
// Effectively duplicating the statement
return [node, node];
}

return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sourceFile, visitor);
};
};
```
So,

```ts
let a = 1;
a = 2;
```

becomes

```js
let a = 1;
a = 2;
a = 2;
```
> **Tip** - You can see the source for this at [/example-transformers/return-multiple-node](/example-transformers/return-multiple-node)
The declaration statement (first line) is ignored as it's not a `ExpressionStatement`.

*Note* - Make sure that what you are trying to do actually makes sense in the AST. For ex., returning two expressions instead of one is often just invalid.

Say there is a assignment expression (BinaryExpression with with EqualToken operator), `a = b = 2`. Now returning two nodes instead of `b = 2` expression is invalid (because right hand side can not be multiple nodes). So, TS will throw an error - `Debug Failure. False expression: Too many nodes written to output.`


#### Inserting a sibling node

> **TODO** - Is this possible?
This is effectively same as the [previous section](#replacing-a-node-with-multiple-nodes). Just return a array of nodes including itself and other sibling nodes.

#### Removing a node

Expand Down

0 comments on commit da6b0bd

Please sign in to comment.