【问题标题】:Creating a SourceFile from an array of Statement-nodes instead of a string从语句节点数组而不是字符串创建 SourceFile
【发布时间】:2020-04-15 01:43:18
【问题描述】:

从纯字符串创建SoureFile 对象很容易:

ts.createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind)

但是,我没有看到从Statement-nodes 数组(由各种工厂函数创建)创建一个的方法。

我试图提出这样的解决方案:

const source = ts.createSourceFile(fileName, '', languageVersion);
source.statements = myNodeArray;

但这(也许不足为奇)不起作用。 我也尝试过(ab)像这样使用转换器 API:

function createSourcefile(filename: string, ast: ts.Node[], languageVersion: ts.ScriptTarget): ts.SourceFile {
    const dummy = ts.createSourceFile(filename, 'dummy', languageVersion); // need at least 1 node

    return ts.transform(
        dummy,
        [ transformContext => sourceFile => ts.visitEachChild(sourceFile, node => ast, transformContext) ]
    ).transformed[0];
}

但这似乎也不起作用。

使用这两种方法,我在emit 过程中收到以下错误:

Error: start < 0
  at createTextSpan (node_modules\typescript\lib\typescript.js:10263:19)
  at Object.createTextSpanFromBounds (node_modules\typescript\lib\typescript.js:10272:16)
  at getErrorSpanForNode (node_modules\typescript\lib\typescript.js:13544:19)
  at createDiagnosticForNodeInSourceFile (node_modules\typescript\lib\typescript.js:13449:20)
  at Object.createDiagnosticForNode (node_modules\typescript\lib\typescript.js:13440:16)
  at lookupOrIssueError (node_modules\typescript\lib\typescript.js:34976:22)
  at addDuplicateDeclarationError (node_modules\typescript\lib\typescript.js:35177:23)
  at \node_modules\typescript\lib\typescript.js:35173:17
  at Object.forEach (node_modules\typescript\lib\typescript.js:317:30)
  at addDuplicateDeclarationErrorsForSymbols (node_modules\typescript\lib\typescript.js:35171:16)
  at mergeSymbol (node_modules\typescript\lib\typescript.js:35158:21)
  at \node_modules\typescript\lib\typescript.js:35200:47
  at Map.forEach (<anonymous>)
  at mergeSymbolTable (node_modules\typescript\lib\typescript.js:35198:20)
  at initializeTypeChecker (node_modules\typescript\lib\typescript.js:66463:21)
  at Object.createTypeChecker (node_modules\typescript\lib\typescript.js:34935:9)
  at getDiagnosticsProducingTypeChecker (node_modules\typescript\lib\typescript.js:98560:93)
  at emitWorker (node_modules\typescript\lib\typescript.js:98588:32)
  at \node_modules\typescript\lib\typescript.js:98569:66
  at runWithCancellationToken (node_modules\typescript\lib\typescript.js:98665:24)
  at Object.emit (node_modules\typescript\lib\typescript.js:98569:20)
  *snip*

有没有办法让它工作?

我想我可以理论上使用打印机将 AST 转换为字符串,但这显然是一种巨大的浪费。


我使用“虚拟编译器主机”和 David Sherret 的范围剥离建议创建了一个 gist 的独立示例。

奇怪的是,我发现并非所有节点类型都会发生此错误。在我的(有限)测试中,我只在 AST 包含 ImportDeclaration 节点时遇到它。

【问题讨论】:

    标签: typescript typescript-compiler-api


    【解决方案1】:

    感谢您提供可重现的示例!我已经完全改变了我的答案,所以请查看我过去答案的历史记录。

    我对此进行了调查,发生的情况如下:

    1. 类型检查器会检查文件。
    2. 它遇到诊断"Cannot find module 'bar'."(注意:这是在修复ts.createImportDeclaration 以提供字符串文字而不是模块说明符的标识符之后)
    3. 它会为此诊断创建一个范围,并且createTextSpan 函数会抛出,因为合成节点的位置小于零。这与我之前的stripRanges 建议无关,因为ts.createImportDeclaration 创建了一个具有负位置的节点。

    因此,在进行此调查后,我被提醒,类型检查器通常会假设节点将引用源文件文本中的位置。这是因为编译的转换阶段发生在类型检查之后。

    如果您想对创建的源文件进行类型检查,我认为您需要将其打印为字符串并重新解析以获取包含具有正确位置的后代节点的新源文件,然后使用它。

    如果您不关心类型检查,那么不幸的是,目前我认为没有办法在不使用当前 API 进行类型检查的情况下转换代码。虽然在发射时可能会将某些内容破解到自定义转换器中......也许有一个空文件,然后在自定义转换期间添加语句。

    【讨论】:

    • 这感觉比我的转换器方法要少得多“hacky”(结果证明它根本不起作用),但我仍然遇到同样的错误。难道是打印机不支持任何形式的“合成”源文件?
    • 这很不幸。也许尝试剥离树中所有节点的范围?我在我的答案中添加了一些代码来做到这一点。如果这不起作用,您能否在具有一些复制步骤的问题中添加一个示例?之后我会进一步研究。
    • 不幸的是,这也不起作用。我在我的问题中添加了一个独立的示例,非常感谢您愿意提供帮助!
    猜你喜欢
    • 2014-01-04
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2012-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多