fix(tsc-wrapped): use tsickle's new source map composition feature (#14150)

Tsickle transforms typescript code, which can change the location of code.
This can cause issues such as runtime stack traces reporting that errors
were raised on the incorrect line in the orginal source.  This change replaces
the DecoratorDownlevelCompilerHost and the TsickleCompilerHost with tsickle's
TsickleCompilerHost, which automatically composes tsickle's source maps with
typescript's source maps, so that line numbers are correctly reported at
runtime.

PR Close #14150
This commit is contained in:
Lucas Sloan
2017-01-27 10:36:46 -08:00
committed by Miško Hevery
parent 2e1413016e
commit 5bccff0d7a
8 changed files with 1223 additions and 127 deletions

View File

@ -50,64 +50,6 @@ export abstract class DelegatingHost implements ts.CompilerHost {
directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName);
}
export class DecoratorDownlevelCompilerHost extends DelegatingHost {
private ANNOTATION_SUPPORT = `
interface DecoratorInvocation {
type: Function;
args?: any[];
}
`;
/** Error messages produced by tsickle, if any. */
public diagnostics: ts.Diagnostic[] = [];
constructor(delegate: ts.CompilerHost, private program: ts.Program) { super(delegate); }
getSourceFile =
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => {
const originalContent = this.delegate.readFile(fileName);
let newContent = originalContent;
if (!/\.d\.ts$/.test(fileName)) {
try {
const converted = tsickle.convertDecorators(
this.program.getTypeChecker(), this.program.getSourceFile(fileName));
if (converted.diagnostics) {
this.diagnostics.push(...converted.diagnostics);
}
newContent = converted.output + this.ANNOTATION_SUPPORT;
} catch (e) {
console.error('Cannot convertDecorators on file', fileName);
throw e;
}
}
return ts.createSourceFile(fileName, newContent, languageVersion, true);
}
}
export class TsickleCompilerHost extends DelegatingHost {
/** Error messages produced by tsickle, if any. */
public diagnostics: ts.Diagnostic[] = [];
constructor(
delegate: ts.CompilerHost, private oldProgram: ts.Program, private options: NgOptions) {
super(delegate);
}
getSourceFile =
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => {
let sourceFile = this.oldProgram.getSourceFile(fileName);
let isDefinitions = /\.d\.ts$/.test(fileName);
// Don't tsickle-process any d.ts that isn't a compilation target;
// this means we don't process e.g. lib.d.ts.
if (isDefinitions) return sourceFile;
const es2015Target = this.options.target == ts.ScriptTarget.ES2015; // This covers ES6 too
let {output, externs, diagnostics} = tsickle.annotate(
this.oldProgram, sourceFile, {untyped: true, convertIndexImportShorthand: es2015Target},
this.delegate, this.options);
this.diagnostics = diagnostics;
return ts.createSourceFile(fileName, output, languageVersion, true);
}
}
const IGNORED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$/;
export class MetadataWriterHost extends DelegatingHost {

View File

@ -8,12 +8,13 @@
import * as fs from 'fs';
import * as path from 'path';
import * as tsickle from 'tsickle';
import * as ts from 'typescript';
import {check, tsc} from './tsc';
import NgOptions from './options';
import {MetadataWriterHost, DecoratorDownlevelCompilerHost, TsickleCompilerHost} from './compiler_host';
import {MetadataWriterHost} from './compiler_host';
import {CliOptions} from './cli_options';
import {VinylFile, isVinylFile} from './vinyl_file';
@ -77,22 +78,40 @@ export function main(
let preprocessHost = host;
let programForJsEmit = programWithCodegen;
const tsickleCompilerHostOptions: tsickle.Options = {
googmodule: false,
untyped: true,
convertIndexImportShorthand:
ngOptions.target === ts.ScriptTarget.ES2015, // This covers ES6 too
};
const tsickleHost: tsickle.TsickleHost = {
shouldSkipTsickleProcessing: (fileName) => false,
pathToModuleName: (context, importPath) => '',
shouldIgnoreWarningsForPath: (filePath) => false,
fileNameToModuleId: (fileName) => fileName,
};
const tsickleCompilerHost = new tsickle.TsickleCompilerHost(
preprocessHost, ngOptions, tsickleCompilerHostOptions, tsickleHost);
if (ngOptions.annotationsAs !== 'decorators') {
if (diagnostics) console.time('NG downlevel');
const downlevelHost = new DecoratorDownlevelCompilerHost(preprocessHost, programForJsEmit);
tsickleCompilerHost.reconfigureForRun(programForJsEmit, tsickle.Pass.DECORATOR_DOWNLEVEL);
// A program can be re-used only once; save the programWithCodegen to be reused by
// metadataWriter
programForJsEmit = createProgram(downlevelHost);
check(downlevelHost.diagnostics);
preprocessHost = downlevelHost;
programForJsEmit = createProgram(tsickleCompilerHost);
check(tsickleCompilerHost.diagnostics);
preprocessHost = tsickleCompilerHost;
if (diagnostics) console.timeEnd('NG downlevel');
}
if (ngOptions.annotateForClosureCompiler) {
if (diagnostics) console.time('NG JSDoc');
const tsickleHost = new TsickleCompilerHost(preprocessHost, programForJsEmit, ngOptions);
programForJsEmit = createProgram(tsickleHost);
check(tsickleHost.diagnostics);
tsickleCompilerHost.reconfigureForRun(programForJsEmit, tsickle.Pass.CLOSURIZE);
programForJsEmit = createProgram(tsickleCompilerHost);
check(tsickleCompilerHost.diagnostics);
if (diagnostics) console.timeEnd('NG JSDoc');
}