从语句节点数组而不是字符串创建SourceFile

时间:2020-04-13 22:00:28

标签: typescript typescript-compiler-api

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

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

但是,我没有一种方法可以从Statement-节点(由各种工厂函数创建)的数组中创建一个。

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

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

但是这(也许不足为奇)不起作用。 我也尝试过使用(trans)这样的转换器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节点时遇到它。

1 个答案:

答案 0 :(得分:1)

感谢可复制的示例!我已经完全更改了答案,因此请查看我过去的答案的历史记录。

我调查了一下,发生了以下情况:

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

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

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

如果您不关心类型检查,那么不幸的是,目前我不认为有一种无需使用当前API进行类型检查就可以转换代码的方法。虽然可能会在发出时将某些内容入侵到自定义转换器中……也许有一个空文件,然后在自定义转换期间添加语句。

相关问题