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:

committed by
Chuck Jazdzewski

parent
c0e05e6f03
commit
492153a986
@ -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);
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user