diff --git a/example-transformers/rename-symbol/source.ts b/example-transformers/rename-symbol/source.ts new file mode 100644 index 0000000..59480c6 --- /dev/null +++ b/example-transformers/rename-symbol/source.ts @@ -0,0 +1,6 @@ +let foo = 1; +foo = 2; +{ + const foo = 'abcd'; +} +foo = 3; diff --git a/example-transformers/rename-symbol/transformed/source.js b/example-transformers/rename-symbol/transformed/source.js new file mode 100644 index 0000000..a3b76a7 --- /dev/null +++ b/example-transformers/rename-symbol/transformed/source.js @@ -0,0 +1,6 @@ +let bar = 1; +bar = 2; +{ + const foo = 'abcd'; +} +bar = 3; diff --git a/example-transformers/rename-symbol/transformer.ts b/example-transformers/rename-symbol/transformer.ts new file mode 100644 index 0000000..c784777 --- /dev/null +++ b/example-transformers/rename-symbol/transformer.ts @@ -0,0 +1,78 @@ +import * as ts from 'typescript'; + +// Renames all references to the first identifier with the text "foo" +const identifierText = 'foo'; +const identifierMatchIndex = 1; +const newIdentifierText = 'bar'; + +const transformerProgram = (program: ts.Program) => { + const typeChecker = program.getTypeChecker(); + + const transformerFactory: ts.TransformerFactory = context => { + return sourceFile => { + // 1. Find the symbol, if any + let identifierCurrentIndex = -1; + const findSymbolVisitor = (node: ts.Node): ts.Symbol | undefined => { + if (ts.isIdentifier(node)) { + const relatedSymbol = typeChecker.getSymbolAtLocation(node); + + console.log(relatedSymbol.escapedName); + if (relatedSymbol.escapedName === identifierText) { + identifierCurrentIndex += 1; + if (identifierCurrentIndex === identifierMatchIndex) { + console.log('found'); + return relatedSymbol; + } + } + } + + return ts.forEachChild(node, findSymbolVisitor); + }; + + // 2. Mark the identifiers, if any + const foundIdentifiers = new Array(); + const findIdentifiersVisitor = (node: ts.Node): true | undefined => { + if (ts.isIdentifier(node)) { + const relatedSymbol = typeChecker.getSymbolAtLocation(node); + + if (relatedSymbol === foundSymbol) { + foundIdentifiers.push(node); + // Stop finding + return true; + } + } + + ts.forEachChild(node, findIdentifiersVisitor); + return undefined; + }; + + // 3. Modify the identifiers + const modifyIdentifiersVisitor = (node: ts.Node): ts.Node => { + if (ts.isIdentifier(node) && foundIdentifiers.includes(node)) { + return ts.updateIdentifier(ts.createIdentifier(newIdentifierText)); + } + + return ts.visitEachChild(node, modifyIdentifiersVisitor, context); + }; + + // Execute step 1 + const foundSymbol = ts.forEachChild(sourceFile, findSymbolVisitor); + + // Execute step 2, if step 1 passed + if (foundSymbol) { + ts.forEachChild(sourceFile, findIdentifiersVisitor); + } + + // Execute step 3, if step 2 passed + if (foundIdentifiers.length > 0) { + return ts.visitNode(sourceFile, modifyIdentifiersVisitor); + } + + return sourceFile; + }; + }; + + return transformerFactory; +}; + +export default transformerProgram; diff --git a/example-transformers/rename-symbol/tsconfig.json b/example-transformers/rename-symbol/tsconfig.json new file mode 100644 index 0000000..c7f3beb --- /dev/null +++ b/example-transformers/rename-symbol/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "transformed", + "plugins": [{ "transform": "./transformer.ts" }] + }, + "files": ["source.ts"] +}