TypeScript Transformerを使って任意の構文木を書き換えるシンプルなWebアプリの例
やりたいこと
実際に動作するリポジトリ
やりかた
上記の資料でも触れられているが tsconfig.json
でCustom Transformersを指定すること現状ができない。
ttypescriptを使うことで
tsconfig.json
で指定できるようになるようだが変わったことをすると後でハマったり融通が利かなくなる経験則があるので避けた。
transformers/my-transformer.ts// (base: https://qiita.com/Quramy/items/1c9c2f7da2a6548f6901)
import * as ts from 'typescript';
// Transformer for alert("...") => console.log("[alert]: ...");
export default function (ctx: ts.TransformationContext) {
function visitNode(node: ts.Node): ts.Node {
if (!isAlert(node)) {
return ts.visitEachChild(node, visitNode, ctx);
}
return createHelper(node);
}
function createHelper(node: ts.Node) {
ctx.requestEmitHelper({
name: "ts:say",
priority: 0,
scoped: false,
text: "var __alert_console__ = function(msg) { console.log('[alert]: ' + msg); };",
});
return ts.setTextRange(ts.createIdentifier("__alert_console__" ), node);
}
function isAlert(node: ts.Node): node is ts.PropertyAccessExpression {
return node.kind === ts.SyntaxKind.Identifier && node.getText() === "alert";
}
return (source: ts.SourceFile) => ts.updateSourceFileNode(source, ts.visitNodes(source.statements, visitNode));
}
以下のように tsc transformers/*.ts
で transformers/*.js
として配置されるようにあらかじめビルドされるようにしている。
package.json...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build-transformers": "tsc transformers/*.ts",
"serve": "npm run build-transformers && webpack-dev-server --watch",
"build": "npm run build-transformers && webpack"
....
webpack.config.js// 自分のTransformerをインポート
const mytransformer = require('./transformers/my-transformer').default;
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader',
// 以下を追記
options: {
getCustomTransformers: () => ({
before: [mytransformer]
}),
transpileOnly: true,
},
}
]
},
...
main.ts
で alert()
を使った何かを書く。
実際の変更のコミット
実際の動作
alert()
しているが alert
されずにコンソールに出力されていることが確認できる。
その他の参考にした資料
おまけ
以下(おそらくソースマップ)を見ると
alert()
のままになっている。変換後の
TypeScriptだとよかったのだが。これだとデバッグ時はすごく困難な気がする。