It's easy to create a SoureFile object from a plain string:
ts.createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind)
However, I don't see a way to create one from an array of Statement-nodes (created by the various factory functions).
I tried to bodge a solution like this:
const source = ts.createSourceFile(fileName, '', languageVersion);
source.statements = myNodeArray;
But this (perhaps unsurprisingly) doesn't work. I also tried (ab)using the transformer API like this:
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];
}
But this doesn't appear to work either.
With both methods I get get following error during the emit process:
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*
Is there a way to get this to work?
I guess I could in theory use a printer to convert the AST into a string, but this would obviously be a massive waste.
I've made a gist with a self-contained example using a 'virtual compiler host' and David Sherret's range stripping suggestion.
Strangely enough, I discovered that this error doesn't happen with all node types. In my (limited) testing I only encountered it when the AST contained an ImportDeclaration node.