gpt4 book ai didi

typescript - 如何在 typescript 中的编译器转换器上将标识符与现有符号绑定(bind)?

转载 作者:搜寻专家 更新时间:2023-10-30 21:08:18 25 4
gpt4 key购买 nike

我正在尝试使用 typescript 编译器 API 编写 typescript 编译器转换。然而,当创建新的标识符节点时,即使节点被发送到最终的 .js 文件,它们似乎缺少符号绑定(bind)信息,因此最终输出不正确。

假设我有以下程序:

A.ts

export class A {
static myMethod() {
return 'value';
}
}

索引.ts

import { A } from './A';

export function main() {
const value1 = 'replaceMe';
const value2 = A.myMethod();
const equals = value1 == value2;
}

假设我尝试用下面的转换器编译上面的程序:

function transformer(program: ts.Program): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => (file: ts.SourceFile) => transformFile(program, context, file);
}

function transformFile(program: ts.Program, context: ts.TransformationContext, file: ts.SourceFile): ts.SourceFile {
const transformedFile = ts.visitEachChild(file, child => visit(child, context), context);
console.log(ts.createPrinter().printFile(transformedFile));
return transformedFile;
}
function visit(node: ts.Node, context: ts.TransformationContext): ts.Node {
if (ts.isStringLiteral(node) && node.text == 'replaceMe') {
return ts.createCall(
ts.createPropertyAccess(
ts.createIdentifier('A'),
'myMethod'),
[],
[]);
}
return ts.visitEachChild(node, child => visit(child, context), context);
}

中间 AST 在 pretty-print 时实际上看起来是正确的:

import { A } from './A';

export function main() {
const value1 = A.myMethod();
const value2 = A.myMethod();
const equals = value1 == value2;
}

但输出的 javascript 不正确:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var A_1 = require("./A");
function main() {
var value1 = A.myMethod();
var value2 = A_1.A.myMethod();
var equals = value1 == value2;
}
exports.main = main;

我理解这可能是因为通过使用 ts.createIdentitier('A') 创建一个新标识符,这个新标识符没有绑定(bind)到与另一个 A< 相同的符号 同一文件中的标识符。

有没有办法使用公共(public)编译器 API 将新标识符绑定(bind)到现有符号?

最佳答案

Typescript 编译分阶段进行(解析、绑定(bind)、类型检查、发出,更详细一点 here )。您可以使用前一阶段的信息,但通常不能更改它。您可以在发射阶段执行的转换旨在允许您将 AST 从 Typescript 带到 Javascript,而不是重构代码。

实现目标的一种方法是创建程序,应用转换,然后使用修改后的代码创建一个新程序,尽可能多地重用原始程序(重用相同的SourceFile 没有发生变化)

function transformFile(program: ts.Program, file: ts.SourceFile): ts.SourceFile {
let empty = ()=> {};
// Dummy transformation context
let context: ts.TransformationContext = {
startLexicalEnvironment: empty,
suspendLexicalEnvironment: empty,
resumeLexicalEnvironment: empty,
endLexicalEnvironment: ()=> [],
getCompilerOptions: ()=> program.getCompilerOptions(),
hoistFunctionDeclaration: empty,
hoistVariableDeclaration: empty,
readEmitHelpers: ()=>undefined,
requestEmitHelper: empty,
enableEmitNotification: empty,
enableSubstitution: empty,
isEmitNotificationEnabled: ()=> false,
isSubstitutionEnabled: ()=> false,
onEmitNode: empty,
onSubstituteNode: (hint, node)=>node,
};
const transformedFile = ts.visitEachChild(file, child => visit(child, context), context);
return transformedFile;
}

function visit(node: ts.Node, context: ts.TransformationContext): ts.Node {
if (ts.isStringLiteral(node) && node.text == 'replaceMe') {
return ts.createCall(
ts.createPropertyAccess(
ts.createIdentifier('A'),
'myMethod'),
[],
[]);
}
return ts.visitEachChild(node, child => visit(child, context), context);
}

let host = ts.createCompilerHost({});
let program = ts.createProgram(["toTrans.ts"], {}, host)

let transformed = program.getSourceFiles()
.map(f=> ({ original: f, transformed: transformFile(program, f) }))
.reduce<{ [name: string] : {original: ts.SourceFile, transformed: ts.SourceFile }}>((r, f)=> { r[f.original.fileName] = f; return r; }, {});

let originalGetSourceFile = host.getSourceFile;
let printer = ts.createPrinter();

// Rig the host to return the new verisons of transformed files.
host.getSourceFile = function(fileName, languageVersion, onError, shouldCreateNewSourceFile){
let file = transformed[fileName];
if(file){
if(file.original != file.transformed){
// Since we need to return a SourceFile it is tempting to return the transformed source file and not parse it again
// The compiler doe not support Synthesized nodes in the AST except during emit, and it will check node positions
// (which for Synthesized are -1) and fail. So we need to reparse
return ts.createSourceFile(fileName, printer.printFile(file.transformed), languageVersion);
} else {
// For unchanged files it should be safe to reuse the source file
return file.original;
}
}
return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
}

// Recreate the program, we pass in the original to
program = ts.createProgram(["toTrans.ts"], {}, host, program);

var result = program.emit();

另一种方法是使用语言服务并通过语言服务应用这些更改,但老实说我对编译器的那部分没有经验,它似乎比这种方法更复杂。

关于typescript - 如何在 typescript 中的编译器转换器上将标识符与现有符号绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49139601/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com