From 994089d36b1c0bddfa58d4aee246e6a5ed2c2e75 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 16 Mar 2017 17:33:17 -0700 Subject: [PATCH] fix(compiler): always use `ng://` prefix for sourcemap urls (#15218) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: - In G3, filePaths don’t start with a `/` and therefore became relative. - Always using the `ng://` prefix groups angular resources in the same way for AOT and JIT. --- packages/compiler/src/aot/compiler.ts | 4 +-- packages/compiler/src/compile_metadata.ts | 27 +++++++------- packages/compiler/test/aot/compiler_spec.ts | 29 +++++++-------- .../source_map_integration_node_only_spec.ts | 36 +++++++++---------- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/packages/compiler/src/aot/compiler.ts b/packages/compiler/src/aot/compiler.ts index 14bbefda8a..da745576ae 100644 --- a/packages/compiler/src/aot/compiler.ts +++ b/packages/compiler/src/aot/compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, flatten, identifierName, templateSourceUrl} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, flatten, identifierName, sourceUrl, templateSourceUrl} from '../compile_metadata'; import {CompilerConfig} from '../config'; import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers'; import {CompileMetadataResolver} from '../metadata_resolver'; @@ -217,7 +217,7 @@ export class AotCompiler { return new GeneratedFile( srcFileUrl, genFileUrl, this._outputEmitter.emitStatements( - srcFileUrl, genFileUrl, statements, exportedVars, this._genFilePreamble)); + sourceUrl(srcFileUrl), genFileUrl, statements, exportedVars, this._genFilePreamble)); } } diff --git a/packages/compiler/src/compile_metadata.ts b/packages/compiler/src/compile_metadata.ts index 5d251c69d2..19c6ec7c70 100644 --- a/packages/compiler/src/compile_metadata.ts +++ b/packages/compiler/src/compile_metadata.ts @@ -758,42 +758,43 @@ export function flatten(list: Array): T[] { }, []); } -/** - * Note: Using `location.origin` as prefix helps displaying them as a hierarchy in chrome. - * It also helps long-stack-trace zone when rewriting stack traces to not break - * source maps (as now all scripts have the same origin). - */ -function ngJitFolder() { - return 'ng://'; +export function sourceUrl(url: string) { + // Note: We need 3 "/" so that ng shows up as a separate domain + // in the chrome dev tools. + return url.replace(/(\w+:\/\/[\w:-]+)?(\/+)?/, 'ng:///'); } export function templateSourceUrl( ngModuleType: CompileIdentifierMetadata, compMeta: {type: CompileIdentifierMetadata}, templateMeta: {isInline: boolean, templateUrl: string}) { + let url: string; if (templateMeta.isInline) { if (compMeta.type.reference instanceof StaticSymbol) { // Note: a .ts file might contain multiple components with inline templates, // so we need to give them unique urls, as these will be used for sourcemaps. - return `${compMeta.type.reference.filePath}#${compMeta.type.reference.name}.html`; + url = `${compMeta.type.reference.filePath}.${compMeta.type.reference.name}.html`; } else { - return `${ngJitFolder()}/${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`; + url = `${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`; } } else { - return templateMeta.templateUrl; + url = templateMeta.templateUrl; } + // always prepend ng:// to make angular resources easy to find and not clobber + // user resources. + return sourceUrl(url); } export function sharedStylesheetJitUrl(meta: CompileStylesheetMetadata, id: number) { const pathParts = meta.moduleUrl.split(/\/\\/g); const baseName = pathParts[pathParts.length - 1]; - return `${ngJitFolder()}/css/${id}${baseName}.ngstyle.js`; + return sourceUrl(`css/${id}${baseName}.ngstyle.js`); } export function ngModuleJitUrl(moduleMeta: CompileNgModuleMetadata): string { - return `${ngJitFolder()}/${identifierName(moduleMeta.type)}/module.ngfactory.js`; + return sourceUrl(`${identifierName(moduleMeta.type)}/module.ngfactory.js`); } export function templateJitUrl( ngModuleType: CompileIdentifierMetadata, compMeta: CompileDirectiveMetadata): string { - return `${ngJitFolder()}/${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.ngfactory.js`; + return sourceUrl(`${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.ngfactory.js`); } diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index 34192e8d06..11e526e935 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -73,6 +73,7 @@ describe('compiler (unbundled Angular)', () => { describe('aot source mapping', () => { const componentPath = '/app/app.component.ts'; + const ngComponentPath = 'ng:///app/app.component.ts' let rootDir: MockDirectory; let appDir: MockDirectory; @@ -133,14 +134,15 @@ describe('compiler (unbundled Angular)', () => { } describe('inline templates', () => { - const templateUrl = `${componentPath}#AppComponent.html`; + const ngUrl = `${ngComponentPath}.AppComponent.html`; function templateDecorator(template: string) { return `template: \`${template}\`,`; } - declareTests({templateUrl, templateDecorator}); + declareTests({ngUrl, templateDecorator}); }); describe('external templates', () => { + const ngUrl = 'ng:///app/app.component.html'; const templateUrl = '/app/app.component.html'; function templateDecorator(template: string) { @@ -148,18 +150,17 @@ describe('compiler (unbundled Angular)', () => { return `templateUrl: 'app.component.html',`; } - declareTests({templateUrl, templateDecorator}); + declareTests({ngUrl, templateDecorator}); }); - function declareTests( - {templateUrl, templateDecorator}: - {templateUrl: string, templateDecorator: (template: string) => string}) { + function declareTests({ngUrl, templateDecorator}: + {ngUrl: string, templateDecorator: (template: string) => string}) { it('should use the right source url in html parse errors', async(() => { appDir['app.component.ts'] = createComponentSource(templateDecorator('
\n ')); expectPromiseToThrow( - compileApp(), new RegExp(`Template parse errors[\\s\\S]*${templateUrl}@1:2`)); + compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`)); })); it('should use the right source url in template parse errors', async(() => { @@ -167,7 +168,7 @@ describe('compiler (unbundled Angular)', () => { templateDecorator('
\n
')); expectPromiseToThrow( - compileApp(), new RegExp(`Template parse errors[\\s\\S]*${templateUrl}@1:7`)); + compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`)); })); it('should create a sourceMap for the template', async(() => { @@ -181,12 +182,12 @@ describe('compiler (unbundled Angular)', () => { // the generated file contains code that is not mapped to // the template but rather to the original source file (e.g. import statements, ...) - const templateIndex = sourceMap.sources.indexOf(templateUrl); + const templateIndex = sourceMap.sources.indexOf(ngUrl); expect(sourceMap.sourcesContent[templateIndex]).toEqual(template); // for the mapping to the original source file we don't store the source code // as we want to keep whatever TypeScript / ... produced for them. - const sourceIndex = sourceMap.sources.indexOf(componentPath); + const sourceIndex = sourceMap.sources.indexOf(ngComponentPath); expect(sourceMap.sourcesContent[sourceIndex]).toBe(null); }); })); @@ -199,7 +200,7 @@ describe('compiler (unbundled Angular)', () => { compileApp().then((genFile) => { const sourceMap = extractSourceMap(genFile.source); expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `'span'`))) - .toEqual({line: 2, column: 3, source: templateUrl}); + .toEqual({line: 2, column: 3, source: ngUrl}); }); })); @@ -212,7 +213,7 @@ describe('compiler (unbundled Angular)', () => { const sourceMap = extractSourceMap(genFile.source); expect( originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`))) - .toEqual({line: 2, column: 9, source: templateUrl}); + .toEqual({line: 2, column: 9, source: ngUrl}); }); })); @@ -225,7 +226,7 @@ describe('compiler (unbundled Angular)', () => { const sourceMap = extractSourceMap(genFile.source); expect( originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`))) - .toEqual({line: 2, column: 9, source: templateUrl}); + .toEqual({line: 2, column: 9, source: ngUrl}); }); })); @@ -235,7 +236,7 @@ describe('compiler (unbundled Angular)', () => { compileApp().then((genFile) => { const sourceMap = extractSourceMap(genFile.source); expect(originalPositionFor(sourceMap, {line: 1, column: 0})) - .toEqual({line: 1, column: 0, source: componentPath}); + .toEqual({line: 1, column: 0, source: ngComponentPath}); }); })); } diff --git a/packages/core/test/linker/source_map_integration_node_only_spec.ts b/packages/core/test/linker/source_map_integration_node_only_spec.ts index ad53d8fa10..b6f0afacfc 100644 --- a/packages/core/test/linker/source_map_integration_node_only_spec.ts +++ b/packages/core/test/linker/source_map_integration_node_only_spec.ts @@ -73,36 +73,36 @@ export function main() { } describe('inline templates', () => { - const templateUrl = 'ng:///DynamicTestModule/MyComp.html'; + const ngUrl = 'ng:///DynamicTestModule/MyComp.html'; function templateDecorator(template: string) { return {template}; } - declareTests({templateUrl, templateDecorator}); + declareTests({ngUrl, templateDecorator}); }); describe('external templates', () => { - const templateUrl = 'http://localhost:1234:some/url.html'; + const ngUrl = 'ng:///some/url.html'; + const templateUrl = 'http://localhost:1234/some/url.html'; function templateDecorator(template: string) { resourceLoader.expect(templateUrl, template); return {templateUrl}; } - declareTests({templateUrl, templateDecorator}); + declareTests({ngUrl, templateDecorator}); }); - function declareTests({templateUrl, templateDecorator}: { - templateUrl: string, - templateDecorator: (template: string) => { [key: string]: any } - }) { + function declareTests( + {ngUrl, templateDecorator}: + {ngUrl: string, templateDecorator: (template: string) => { [key: string]: any }}) { it('should use the right source url in html parse errors', fakeAsync(() => { @Component({...templateDecorator('
\n ')}) class MyComp { } expect(() => compileAndCreateComponent(MyComp)) - .toThrowError(new RegExp( - `Template parse errors[\\s\\S]*${templateUrl.replace('$', '\\$')}@1:2`)); + .toThrowError( + new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`)); })); it('should use the right source url in template parse errors', fakeAsync(() => { @@ -111,8 +111,8 @@ export function main() { } expect(() => compileAndCreateComponent(MyComp)) - .toThrowError(new RegExp( - `Template parse errors[\\s\\S]*${templateUrl.replace('$', '\\$')}@1:7`)); + .toThrowError( + new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`)); })); it('should create a sourceMap for templates', fakeAsync(() => { @@ -126,7 +126,7 @@ export function main() { const sourceMap = getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js'); expect(sourceMap.sources).toEqual([ - 'ng:///DynamicTestModule/MyComp.ngfactory.js', templateUrl + 'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl ]); expect(sourceMap.sourcesContent).toEqual([null, template]); })); @@ -155,7 +155,7 @@ export function main() { expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ line: 2, column: 4, - source: templateUrl, + source: ngUrl, }); })); @@ -179,13 +179,13 @@ export function main() { expect(getSourcePositionForStack(error.stack)).toEqual({ line: 2, column: 12, - source: templateUrl, + source: ngUrl, }); // The error should be logged from the element expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ line: 2, column: 4, - source: templateUrl, + source: ngUrl, }); })); @@ -209,13 +209,13 @@ export function main() { expect(getSourcePositionForStack(error.stack)).toEqual({ line: 2, column: 12, - source: templateUrl, + source: ngUrl, }); // The error should be logged from the element expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ line: 2, column: 4, - source: templateUrl, + source: ngUrl, }); }));