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

@ -25,7 +25,7 @@ export function main() {
ctx.print(createSourceSpan(fileA, 1), 'o1');
ctx.print(createSourceSpan(fileB, 0), 'o2');
ctx.print(createSourceSpan(fileB, 1), 'o3');
const sm = ctx.toSourceMapGenerator('o.js').toJSON();
const sm = ctx.toSourceMapGenerator('o.ts', 'o.js').toJSON();
expect(sm.sources).toEqual([fileA.url, fileB.url]);
expect(sm.sourcesContent).toEqual([fileA.content, fileB.content]);
});
@ -43,7 +43,7 @@ export function main() {
it('should be able to shift the content', () => {
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
const sm = ctx.toSourceMapGenerator(null, 10).toJSON();
const sm = ctx.toSourceMapGenerator('o.ts', 'o.js', 10).toJSON();
expect(originalPositionFor(sm, {line: 11, column: 0})).toEqual({
line: 1,
column: 0,
@ -51,13 +51,23 @@ export function main() {
});
});
it('should not map leading segment without span', () => {
it('should use the default source file for the first character', () => {
ctx.print(null, 'fileA-0');
expectMap(ctx, 0, 0, 'o.ts', 0, 0);
});
it('should use an explicit mapping for the first character', () => {
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
expectMap(ctx, 0, 0, 'a.js', 0, 0);
});
it('should map leading segment without span', () => {
ctx.print(null, '....');
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
expectMap(ctx, 0, 0);
expectMap(ctx, 0, 0, 'o.ts', 0, 0);
expectMap(ctx, 0, 4, 'a.js', 0, 0);
expect(nbSegmentsPerLine(ctx)).toEqual([1]);
expect(nbSegmentsPerLine(ctx)).toEqual([2]);
});
it('should handle indent', () => {
@ -68,7 +78,7 @@ export function main() {
ctx.decIndent();
ctx.println(createSourceSpan(fileA, 2), 'fileA-2');
expectMap(ctx, 0, 0);
expectMap(ctx, 0, 0, 'o.ts', 0, 0);
expectMap(ctx, 0, 2, 'a.js', 0, 0);
expectMap(ctx, 1, 0);
expectMap(ctx, 1, 2);
@ -76,7 +86,7 @@ export function main() {
expectMap(ctx, 2, 0);
expectMap(ctx, 2, 2, 'a.js', 0, 4);
expect(nbSegmentsPerLine(ctx)).toEqual([1, 1, 1]);
expect(nbSegmentsPerLine(ctx)).toEqual([2, 1, 1]);
});
it('should coalesce identical span', () => {
@ -103,7 +113,7 @@ export function main() {
function expectMap(
ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string = null,
srcLine: number = null, srcCol: number = null) {
const sm = ctx.toSourceMapGenerator().toJSON();
const sm = ctx.toSourceMapGenerator('o.ts', 'o.js').toJSON();
const genPosition = {line: genLine + 1, column: genCol};
const origPosition = originalPositionFor(sm, genPosition);
expect(origPosition.source).toEqual(source);
@ -113,7 +123,7 @@ function expectMap(
// returns the number of segments per line
function nbSegmentsPerLine(ctx: EmitterVisitorContext) {
const sm = ctx.toSourceMapGenerator().toJSON();
const sm = ctx.toSourceMapGenerator('o.ts', 'o.js').toJSON();
const lines = sm.mappings.split(';');
return lines.map(l => {
const m = l.match(/,/g);

View File

@ -16,7 +16,8 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler
import {extractSourceMap, originalPositionFor} from './source_map_util';
const someModuleUrl = 'somePackage/somePath';
const someGenFilePath = 'somePackage/someGenFile';
const someSourceFilePath = 'somePackage/someSourceFile';
class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
@ -38,9 +39,11 @@ export function main() {
});
function emitSourceMap(
stmt: o.Statement | o.Statement[], exportedVars: string[] = null): SourceMap {
stmt: o.Statement | o.Statement[], exportedVars: string[] = null,
preamble?: string): SourceMap {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
const source = emitter.emitStatements(
someSourceFilePath, someGenFilePath, stmts, exportedVars || [], preamble);
return extractSourceMap(source);
}
@ -51,11 +54,11 @@ export function main() {
const endLocation = new ParseLocation(source, 7, 0, 6);
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const sm = emitSourceMap(someVar.toStmt(), [], '/* MyPreamble \n */');
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(originalPositionFor(sm, {line: 1, column: 0}))
expect(sm.sources).toEqual([someSourceFilePath, 'in.js']);
expect(sm.sourcesContent).toEqual([null, ';;;var']);
expect(originalPositionFor(sm, {line: 3, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js'});
});
});

View File

@ -14,11 +14,12 @@ import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {stripSourceMapAndNewLine} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath';
const someGenFilePath = 'somePackage/someGenFile';
const someSourceFilePath = 'somePackage/someSourceFile';
const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier: CompileIdentifierMetadata = {
reference: new StaticSymbol(someModuleUrl, 'someLocalId', [])
reference: new StaticSymbol(someGenFilePath, 'someLocalId', [])
};
const externalModuleIdentifier: CompileIdentifierMetadata = {
reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', [])
@ -48,8 +49,9 @@ export function main() {
someVar = o.variable('someVar');
});
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
const source = emitter.emitStatements(someModuleUrl, [stmt], exportedVars || []);
function emitStmt(stmt: o.Statement, exportedVars: string[] = null, preamble?: string): string {
const source = emitter.emitStatements(
someSourceFilePath, someGenFilePath, [stmt], exportedVars || [], preamble);
return stripSourceMapAndNewLine(source);
}
@ -300,5 +302,11 @@ export function main() {
].join('\n'));
});
});
it('should support a preamble', () => {
expect(emitStmt(o.variable('a').toStmt(), [], '/* SomePreamble */')).toBe([
'/* SomePreamble */', 'a;'
].join('\n'));
});
});
}

View File

@ -16,7 +16,8 @@ import {ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {extractSourceMap, originalPositionFor} from './source_map_util';
const someModuleUrl = 'somePackage/somePath';
const someGenFilePath = 'somePackage/someGenFile';
const someSourceFilePath = 'somePackage/someSourceFile';
class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
@ -43,9 +44,11 @@ export function main() {
});
function emitSourceMap(
stmt: o.Statement | o.Statement[], exportedVars: string[] = null): SourceMap {
stmt: o.Statement | o.Statement[], exportedVars: string[] = null,
preamble?: string): SourceMap {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
const source = emitter.emitStatements(
someSourceFilePath, someGenFilePath, stmts, exportedVars || [], preamble);
return extractSourceMap(source);
}
@ -56,11 +59,11 @@ export function main() {
const endLocation = new ParseLocation(source, 7, 0, 6);
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const sm = emitSourceMap(someVar.toStmt(), [], '/* MyPreamble \n */');
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(originalPositionFor(sm, {line: 1, column: 0}))
expect(sm.sources).toEqual([someSourceFilePath, 'in.js']);
expect(sm.sourcesContent).toEqual([null, ';;;var']);
expect(originalPositionFor(sm, {line: 3, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js'});
});
});

View File

@ -14,11 +14,12 @@ import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {stripSourceMapAndNewLine} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath';
const someGenFilePath = 'somePackage/someGenFile';
const someSourceFilePath = 'somePackage/someSourceFile';
const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier: CompileIdentifierMetadata = {
reference: new StaticSymbol(someModuleUrl, 'someLocalId', [])
reference: new StaticSymbol(someGenFilePath, 'someLocalId', [])
};
const externalModuleIdentifier: CompileIdentifierMetadata = {
@ -49,9 +50,12 @@ export function main() {
someVar = o.variable('someVar');
});
function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string {
function emitStmt(
stmt: o.Statement | o.Statement[], exportedVars: string[] = null,
preamble?: string): string {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
const source = emitter.emitStatements(
someSourceFilePath, someGenFilePath, stmts, exportedVars || [], preamble);
return stripSourceMapAndNewLine(source);
}
@ -459,5 +463,11 @@ export function main() {
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(o.INT_TYPE))))
.toEqual('var a:{[key: string]:number} = (null as any);');
});
it('should support a preamble', () => {
expect(emitStmt(o.variable('a').toStmt(), [], '/* SomePreamble */')).toBe([
'/* SomePreamble */', 'a;'
].join('\n'));
});
});
}