fix(compiler): make sourcemaps work in AOT mode

Inlcuded fixes:
- include preamble in generated source map
- always add a mapping for line/col 0 so that the
  generated sourcemap is not sparse
- use a uniue sourceUrl for inline templates even
  in the AOT case
This commit is contained in:
Tobias Bosch
2017-03-15 15:50:30 -07:00
committed by Chuck Jazdzewski
parent c0e05e6f03
commit 492153a986
24 changed files with 299 additions and 123 deletions

View File

@ -18,7 +18,9 @@ export const CATCH_ERROR_VAR = o.variable('error');
export const CATCH_STACK_VAR = o.variable('stack');
export abstract class OutputEmitter {
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
abstract emitStatements(
srcFilePath: string, genFilePath: string, stmts: o.Statement[], exportedVars: string[],
preamble?: string): string;
}
class _EmittedLine {
@ -89,13 +91,24 @@ export class EmitterVisitorContext {
.join('\n');
}
toSourceMapGenerator(file: string|null = null, startsAtLine: number = 0): SourceMapGenerator {
const map = new SourceMapGenerator(file);
toSourceMapGenerator(sourceFilePath: string, genFilePath: string, startsAtLine: number = 0):
SourceMapGenerator {
const map = new SourceMapGenerator(genFilePath);
let firstOffsetMapped = false;
const mapFirstOffsetIfNeeded = () => {
if (!firstOffsetMapped) {
map.addSource(sourceFilePath).addMapping(0, sourceFilePath, 0, 0);
firstOffsetMapped = true;
}
};
for (let i = 0; i < startsAtLine; i++) {
map.addLine();
mapFirstOffsetIfNeeded();
}
this.sourceLines.forEach(line => {
this.sourceLines.forEach((line, lineIdx) => {
map.addLine();
const spans = line.srcSpans;
@ -107,13 +120,17 @@ export class EmitterVisitorContext {
col0 += parts[spanIdx].length;
spanIdx++;
}
if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
firstOffsetMapped = true;
} else {
mapFirstOffsetIfNeeded();
}
while (spanIdx < spans.length) {
const span = spans[spanIdx];
const source = span.start.file;
const sourceLine = span.start.line;
const sourceCol = span.start.col;
map.addSource(source.url, source.content)
.addMapping(col0, source.url, sourceLine, sourceCol);

View File

@ -18,29 +18,29 @@ import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
emitStatements(
srcFilePath: string, genFilePath: string, stmts: o.Statement[], exportedVars: string[],
preamble: string = ''): string {
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
const preambleLines = preamble ? preamble.split('\n') : [];
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
preambleLines.push(
`var ${prefix} = req` +
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
});
srcParts.push(ctx.toSource());
const prefixLines = converter.importsWithPrefixes.size;
const sm = ctx.toSourceMapGenerator(genFilePath, prefixLines).toJsComment();
const sm =
ctx.toSourceMapGenerator(srcFilePath, genFilePath, preambleLines.length).toJsComment();
const lines = [...preambleLines, ctx.toSource(), sm];
if (sm) {
srcParts.push(sm);
// always add a newline at the end, as some tools have bugs without it.
lines.push('');
}
// always add a newline at the end, as some tools have bugs without it.
srcParts.push('');
return srcParts.join('\n');
return lines.join('\n');
}
}

View File

@ -30,7 +30,7 @@ function evalExpression(
// We don't want to hard code this fact, so we auto detect it via an empty function first.
const emptyFn = new Function(...fnArgNames.concat('return null;')).toString();
const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, sourceUrl, headerLines).toJsComment()}`;
}
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
}

View File

@ -44,40 +44,38 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
emitStatements(
srcFilePath: string, genFilePath: string, stmts: o.Statement[], exportedVars: string[],
preamble: string = ''): string {
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
const preambleLines = preamble ? preamble.split('\n') : [];
converter.reexports.forEach((reexports, exportedFilePath) => {
const reexportsCode =
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
srcParts.push(
preambleLines.push(
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
});
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
preambleLines.push(
`imp` +
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
});
srcParts.push(ctx.toSource());
const prefixLines = converter.reexports.size + converter.importsWithPrefixes.size;
const sm = ctx.toSourceMapGenerator(genFilePath, prefixLines).toJsComment();
const sm =
ctx.toSourceMapGenerator(srcFilePath, genFilePath, preambleLines.length).toJsComment();
const lines = [...preambleLines, ctx.toSource(), sm];
if (sm) {
srcParts.push(sm);
// always add a newline at the end, as some tools have bugs without it.
lines.push('');
}
// always add a newline at the end, as some tools have bugs without it.
srcParts.push('');
return srcParts.join('\n');
return lines.join('\n');
}
}