feat: introduce source maps for templates (#15011)

The main use case for the generated source maps is to give
errors a meaningful context in terms of the original source
that the user wrote.

Related changes that are included in this commit:

* renamed virtual folders used for jit:
  * ng://<module type>/module.ngfactory.js
  * ng://<module type>/<comp type>.ngfactory.js
  * ng://<module type>/<comp type>.html (for inline templates)
* error logging:
  * all errors that happen in templates are logged
    from the place of the nearest element.
  * instead of logging error messages and stacks separately,
    we log the actual error. This is needed so that browsers apply
    source maps to the stack correctly.
  * error type and error is logged as one log entry.

Note that long-stack-trace zone has a bug that 
disables source maps for stack traces,
see https://github.com/angular/zone.js/issues/661.

BREAKING CHANGE:

- DebugNode.source no more returns the source location of a node.  

Closes 14013
This commit is contained in:
Tobias Bosch
2017-03-14 09:16:15 -07:00
committed by Chuck Jazdzewski
parent 1c1085b140
commit cdc882bd36
48 changed files with 1196 additions and 515 deletions

View File

@ -6,12 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotCompiler, AotCompilerHost, AotCompilerOptions, createAotCompiler} from '@angular/compiler';
import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
import {RenderComponentType, ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
import {async} from '@angular/core/testing';
import {async, fakeAsync, tick} from '@angular/core/testing';
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockMetadataBundlerHost, settings} from './test_util';
import {extractSourceMap, originalPositionFor} from '../output/source_map_util';
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, MockMetadataBundlerHost, settings} from './test_util';
const DTS = /\.d\.ts$/;
@ -39,6 +42,9 @@ describe('compiler (unbundled Angular)', () => {
angularFiles = emittingHost.written;
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
describe('Quickstart', () => {
let host: MockCompilerHost;
let aotHost: MockAotCompilerHost;
@ -48,9 +54,6 @@ describe('compiler (unbundled Angular)', () => {
aotHost = new MockAotCompilerHost(host);
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
it('should compile',
async(() => compile(host, aotHost, expectNoDiagnostics).then(generatedFiles => {
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
@ -67,6 +70,155 @@ describe('compiler (unbundled Angular)', () => {
.toBeDefined();
})));
});
describe('aot source mapping', () => {
const componentPath = '/app/app.component.ts';
let rootDir: MockDirectory;
let appDir: MockDirectory;
beforeEach(() => {
appDir = {
'app.module.ts': `
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
`
};
rootDir = {'app': appDir};
});
function compileApp(): GeneratedFile {
const host = new MockCompilerHost(['/app/app.module.ts'], rootDir, angularFiles);
const aotHost = new MockAotCompilerHost(host);
let result: GeneratedFile[];
let error: Error;
compile(host, aotHost, expectNoDiagnostics, expectNoDiagnostics)
.then((files) => result = files, (err) => error = err);
tick();
if (error) {
throw error;
}
return result.find(genFile => genFile.srcFileUrl === componentPath);
;
}
function findLineAndColumn(file: string, token: string): {line: number, column: number} {
const index = file.indexOf(token);
if (index === -1) {
return {line: null, column: null};
}
const linesUntilToken = file.slice(0, index).split('\n');
const line = linesUntilToken.length;
const column = linesUntilToken[linesUntilToken.length - 1].length;
return {line, column};
}
function createComponentSource(componentDecorator: string) {
return `
import { NgModule, Component } from '@angular/core';
@Component({
${componentDecorator}
})
export class AppComponent {
someMethod() {}
}
`;
}
describe('inline templates', () => {
const templateUrl = componentPath;
function templateDecorator(template: string) { return `template: \`${template}\`,`; }
declareTests({templateUrl, templateDecorator});
});
describe('external templates', () => {
const templateUrl = '/app/app.component.html';
function templateDecorator(template: string) {
appDir['app.component.html'] = template;
return `templateUrl: 'app.component.html',`;
}
declareTests({templateUrl, templateDecorator});
});
function declareTests(
{templateUrl, templateDecorator}:
{templateUrl: string, templateDecorator: (template: string) => string}) {
it('should use the right source url in html parse errors', fakeAsync(() => {
appDir['app.component.ts'] =
createComponentSource(templateDecorator('<div>\n </error>'));
expect(() => compileApp())
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${templateUrl}@1:2`));
}));
it('should use the right source url in template parse errors', fakeAsync(() => {
appDir['app.component.ts'] = createComponentSource(
templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
expect(() => compileApp())
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${templateUrl}@1:7`));
}));
it('should create a sourceMap for the template', fakeAsync(() => {
const template = 'Hello World!';
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
const genFile = compileApp();
const sourceMap = extractSourceMap(genFile.source);
expect(sourceMap.file).toEqual(genFile.genFileUrl);
// the generated file contains the host view and the component view.
// we are only interested in the component view.
const sourceIndex = sourceMap.sources.indexOf(templateUrl);
expect(sourceMap.sourcesContent[sourceIndex]).toEqual(template);
}));
it('should map elements correctly to the source', fakeAsync(() => {
const template = '<div>\n <span></span></div>';
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
const genFile = compileApp();
const sourceMap = extractSourceMap(genFile.source);
expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `'span'`)))
.toEqual({line: 2, column: 3, source: templateUrl});
}));
it('should map bindings correctly to the source', fakeAsync(() => {
const template = `<div>\n <span [title]="someMethod()"></span></div>`;
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
const genFile = compileApp();
const sourceMap = extractSourceMap(genFile.source);
expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
.toEqual({line: 2, column: 9, source: templateUrl});
}));
it('should map events correctly to the source', fakeAsync(() => {
const template = `<div>\n <span (click)="someMethod()"></span></div>`;
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
const genFile = compileApp();
const sourceMap = extractSourceMap(genFile.source);
expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
.toEqual({line: 2, column: 9, source: templateUrl});
}));
}
});
});
describe('compiler (bundled Angular)', () => {

View File

@ -28,6 +28,7 @@ export function main() {
it('should throw if no template was specified',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(() => normalizer.normalizeTemplate({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
})).toThrowError('No template specified for component SomeComp');
@ -35,6 +36,7 @@ export function main() {
it('should throw if template is not a string',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(() => normalizer.normalizeTemplate({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
template: <any>{}
@ -43,6 +45,7 @@ export function main() {
it('should throw if templateUrl is not a string',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(() => normalizer.normalizeTemplate({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
templateUrl: <any>{}
@ -54,6 +57,7 @@ export function main() {
it('should store the template',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeTemplateSync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -64,11 +68,13 @@ export function main() {
});
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/a.js');
expect(template.isInline).toBe(true);
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeTemplateSync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -83,6 +89,7 @@ export function main() {
it('should resolve styles in the template against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeTemplateSync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -97,6 +104,7 @@ export function main() {
it('should use ViewEncapsulation.Emulated by default',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeTemplateSync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -114,6 +122,7 @@ export function main() {
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
config.defaultEncapsulation = ViewEncapsulation.None;
const template = normalizer.normalizeTemplateSync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -136,6 +145,7 @@ export function main() {
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
normalizer
.normalizeTemplateAsync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -147,6 +157,7 @@ export function main() {
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
expect(template.isInline).toBe(false);
async.done();
});
resourceLoader.flush();
@ -160,6 +171,7 @@ export function main() {
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
normalizer
.normalizeTemplateAsync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -184,6 +196,7 @@ export function main() {
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
normalizer
.normalizeTemplateAsync({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -271,6 +284,7 @@ export function main() {
resourceLoader: MockResourceLoader) => {
resourceLoader.expect('package:some/module/cmp.html', 'a');
const prenormMeta = {
ngModuleType: null as any,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
templateUrl: 'cmp.html',
@ -297,6 +311,7 @@ export function main() {
const viewEncapsulation = ViewEncapsulation.Native;
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: viewEncapsulation,
@ -311,6 +326,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -325,6 +341,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -339,6 +356,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -354,6 +372,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -368,6 +387,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -382,6 +402,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -396,6 +417,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -410,6 +432,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -424,6 +447,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -438,6 +462,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -453,6 +478,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -467,6 +493,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -482,6 +509,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_HTTP_MODULE_URL,
encapsulation: null,
@ -497,6 +525,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: ViewEncapsulation.Emulated,
@ -511,6 +540,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,
@ -526,6 +556,7 @@ export function main() {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizer.normalizeLoadedTemplate(
{
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
encapsulation: null,

View File

@ -9,10 +9,7 @@
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler';
import {EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitter';
import {SourceMap} from '@angular/compiler/src/output/source_map';
const SourceMapConsumer = require('source-map').SourceMapConsumer;
const b64 = require('base64-js');
import {extractSourceMap, originalPositionFor} from './source_map_util';
export function main() {
describe('AbstractEmitter', () => {
@ -47,12 +44,10 @@ export function main() {
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
const sm = ctx.toSourceMapGenerator(null, 10).toJSON();
const smc = new SourceMapConsumer(sm);
expect(smc.originalPositionFor({line: 11, column: 0})).toEqual({
expect(originalPositionFor(sm, {line: 11, column: 0})).toEqual({
line: 1,
column: 0,
source: 'a.js',
name: null,
});
});
@ -109,9 +104,8 @@ function expectMap(
ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string = null,
srcLine: number = null, srcCol: number = null) {
const sm = ctx.toSourceMapGenerator().toJSON();
const smc = new SourceMapConsumer(sm);
const genPosition = {line: genLine + 1, column: genCol};
const origPosition = smc.originalPositionFor(genPosition);
const origPosition = originalPositionFor(sm, genPosition);
expect(origPosition.source).toEqual(source);
expect(origPosition.line).toEqual(srcLine === null ? null : srcLine + 1);
expect(origPosition.column).toEqual(srcCol);
@ -134,15 +128,3 @@ function createSourceSpan(file: ParseSourceFile, idx: number) {
const sourceSpan = new ParseSourceSpan(start, end);
return {sourceSpan};
}
export function extractSourceMap(source: string): SourceMap {
let idx = source.lastIndexOf('\n//#');
if (idx == -1) return null;
const smComment = source.slice(idx).trim();
const smB64 = smComment.split('sourceMappingURL=data:application/json;base64,')[1];
return smB64 ? JSON.parse(decodeB64String(smB64)) : null;
}
function decodeB64String(s: string): string {
return b64.toByteArray(s).reduce((s: string, c: number) => s + String.fromCharCode(c), '');
}

View File

@ -33,7 +33,10 @@ export function main() {
});
}
export function stripSourceMap(source: string): string {
export function stripSourceMapAndNewLine(source: string): string {
if (source.endsWith('\n')) {
source = source.substring(0, source.length - 1);
}
const smi = source.lastIndexOf('\n//#');
if (smi == -1) return source;
return source.slice(0, smi);

View File

@ -14,9 +14,7 @@ import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {SourceMap} from '@angular/compiler/src/output/source_map';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {extractSourceMap} from './abstract_emitter_node_only_spec';
const SourceMapConsumer = require('source-map').SourceMapConsumer;
import {extractSourceMap, originalPositionFor} from './source_map_util';
const someModuleUrl = 'somePackage/somePath';
@ -54,12 +52,11 @@ export function main() {
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const smc = new SourceMapConsumer(sm);
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(smc.originalPositionFor({line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js', name: null});
expect(originalPositionFor(sm, {line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js'});
});
});
});

View File

@ -12,7 +12,7 @@ import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {stripSourceMap} from './abstract_emitter_spec';
import {stripSourceMapAndNewLine} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath';
@ -50,7 +50,7 @@ export function main() {
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
const source = emitter.emitStatements(someModuleUrl, [stmt], exportedVars || []);
return stripSourceMap(source);
return stripSourceMapAndNewLine(source);
}
it('should declare variables', () => {

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SourceMap} from '@angular/compiler/src/output/source_map';
const b64 = require('base64-js');
const SourceMapConsumer = require('source-map').SourceMapConsumer;
export interface SourceLocation {
line: number;
column: number;
source: string;
}
export function originalPositionFor(
sourceMap: SourceMap, genPosition: {line: number, column: number}): SourceLocation {
const smc = new SourceMapConsumer(sourceMap);
// Note: We don't return the original object as it also contains a `name` property
// which is always null and we don't want to include that in our assertions...
const {line, column, source} = smc.originalPositionFor(genPosition);
return {line, column, source};
}
export function extractSourceMap(source: string): SourceMap {
let idx = source.lastIndexOf('\n//#');
if (idx == -1) return null;
const smComment = source.slice(idx).trim();
const smB64 = smComment.split('sourceMappingURL=data:application/json;base64,')[1];
return smB64 ? JSON.parse(decodeB64String(smB64)) : null;
}
function decodeB64String(s: string): string {
return b64.toByteArray(s).reduce((s: string, c: number) => s + String.fromCharCode(c), '');
}

View File

@ -14,9 +14,7 @@ import {SourceMap} from '@angular/compiler/src/output/source_map';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {extractSourceMap} from './abstract_emitter_node_only_spec';
const SourceMapConsumer = require('source-map').SourceMapConsumer;
import {extractSourceMap, originalPositionFor} from './source_map_util';
const someModuleUrl = 'somePackage/somePath';
@ -59,12 +57,11 @@ export function main() {
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const smc = new SourceMapConsumer(sm);
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(smc.originalPositionFor({line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js', name: null});
expect(originalPositionFor(sm, {line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js'});
});
});
});

View File

@ -12,7 +12,7 @@ import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {stripSourceMap} from './abstract_emitter_spec';
import {stripSourceMapAndNewLine} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath';
@ -52,7 +52,7 @@ export function main() {
function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
return stripSourceMap(source);
return stripSourceMapAndNewLine(source);
}
it('should declare variables', () => {