Skip to content

Commit abd1fa4

Browse files
author
Michael Dougall
committed
chore: adds find parent example
1 parent b536a89 commit abd1fa4

File tree

6 files changed

+130
-16
lines changed

6 files changed

+130
-16
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function hello() {
2+
if (true) {
3+
'world';
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function hello() {
2+
if (true) {
3+
'world';
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function hello() {
2+
if (true) {
3+
'world';
4+
}
5+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as ts from 'typescript';
2+
3+
const findParent = (node: ts.Node, predicate: (node: ts.Node) => boolean) => {
4+
if (!node.parent) {
5+
return undefined;
6+
}
7+
8+
if (predicate(node.parent)) {
9+
return node.parent;
10+
}
11+
12+
return findParent(node.parent, predicate);
13+
};
14+
15+
const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
16+
return sourceFile => {
17+
const visitor = (node: ts.Node): ts.Node => {
18+
if (ts.isStringLiteral(node)) {
19+
const parent = findParent(node, ts.isFunctionDeclaration);
20+
if (parent) {
21+
console.log('string literal has a function declaration parent');
22+
}
23+
24+
return node;
25+
}
26+
27+
return ts.visitEachChild(node, visitor, context);
28+
};
29+
30+
return ts.visitNode(sourceFile, visitor);
31+
};
32+
};
33+
34+
export default transformerFactory;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "transformed",
5+
"plugins": [{ "transform": "./transformer.ts", "type": "raw" }]
6+
},
7+
"files": ["source.ts"]
8+
}

translations/en/transformer-handbook.md

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,44 @@ const visitor = (node: ts.Node): ts.Node => {
854854
855855
#### Find a specific parent
856856

857-
> **TODO** - Is this possible?
857+
While there doesn't exist an out of the box method you can basically roll your own.
858+
Given a node:
859+
860+
```ts
861+
const findParent = (node: ts.Node, predicate: (node: ts.Node) => boolean) => {
862+
if (!node.parent) {
863+
return undefined;
864+
}
865+
866+
if (predicate(node.parent)) {
867+
return node.parent;
868+
}
869+
870+
return findParent(node.parent, predicate);
871+
};
872+
873+
const visitor = (node: ts.Node): ts.Node => {
874+
if (ts.isStringLiteral(node)) {
875+
const parent = findParent(node, ts.isFunctionDeclaration);
876+
if (parent) {
877+
console.log('string literal has a function declaration parent');
878+
}
879+
return node;
880+
}
881+
};
882+
```
883+
884+
Will log to console `string literal has a function declaration parent` with the following source:
885+
886+
```ts
887+
function hello() {
888+
if (true) {
889+
'world';
890+
}
891+
}
892+
```
893+
894+
> **Tip** - You can see the source for this at [/example-transformers/find-parent](/example-transformers/find-parent)
858895
859896
#### Stopping traversal
860897

@@ -1107,21 +1144,40 @@ This also may or may not break when type checking is turned off.
11071144
> **TODO** - Is this possible in a robust way?
11081145
11091146
```ts
1110-
if (
1111-
ts.isImportDeclaration(node) &&
1112-
ts.isStringLiteral(node.moduleSpecifier) &&
1113-
ts.isNamedImports(node.importClause.namedBindings)
1114-
) {
1115-
const moduleImportName = node.moduleSpecifier.text;
1116-
const { resolvedFileName } = sourceFile.resolvedModules.get(moduleImportName);
1117-
const moduleSourceFile = program.getSourceFile(resolvedFileName);
1118-
1119-
moduleSourceFile.symbol.exports.forEach((_, key) => {
1120-
console.log(`found export ${key}`);
1121-
});
1147+
// We need to use a Program transformer to get ahold of the program object.
1148+
const transformerProgram = (program: ts.Program) => {
1149+
const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
1150+
return sourceFile => {
1151+
const visitor = (node: ts.Node): ts.Node => {
1152+
if (
1153+
ts.isImportDeclaration(node) &&
1154+
ts.isStringLiteral(node.moduleSpecifier) &&
1155+
ts.isNamedImports(node.importClause.namedBindings)
1156+
) {
1157+
// Grab the module name
1158+
const moduleImportName = node.moduleSpecifier.text;
1159+
// Grab the modules file name
1160+
const { resolvedFileName } = (sourceFile as any).resolvedModules.get(moduleImportName);
1161+
// Grab the modules source file
1162+
const moduleSourceFile = program.getSourceFile(resolvedFileName);
1163+
1164+
// Access the exports on the source file symbol
1165+
(moduleSourceFile as any).symbol.exports.forEach((_, key) => {
1166+
console.log(`found export ${key}`);
1167+
});
1168+
1169+
return node;
1170+
}
11221171

1123-
return node;
1124-
}
1172+
return ts.visitEachChild(node, visitor, context);
1173+
};
1174+
1175+
return ts.visitNode(sourceFile, visitor);
1176+
};
1177+
};
1178+
1179+
return transformerFactory;
1180+
};
11251181
```
11261182

11271183
Which will log this to the console:
@@ -1177,7 +1233,8 @@ so you'll have to cast it to `any` to gain access to it.
11771233

11781234
## Throwing a syntax error to ease the developer experience
11791235

1180-
> **TODO** - Is this possible like it is in Babel? Or we use a [language service plugin](https://github.com/Microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)?
1236+
> **TODO** - Is this possible like it is in Babel?
1237+
> Or we use a [language service plugin](https://github.com/Microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)?
11811238
11821239
## Testing
11831240

0 commit comments

Comments
 (0)