fix(compiler): always use ng://
prefix for sourcemap urls (#15218)
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.
This commit is contained in:
parent
d2fbbb44ae
commit
994089d36b
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {CompilerConfig} from '../config';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
@ -217,7 +217,7 @@ export class AotCompiler {
|
|||||||
return new GeneratedFile(
|
return new GeneratedFile(
|
||||||
srcFileUrl, genFileUrl,
|
srcFileUrl, genFileUrl,
|
||||||
this._outputEmitter.emitStatements(
|
this._outputEmitter.emitStatements(
|
||||||
srcFileUrl, genFileUrl, statements, exportedVars, this._genFilePreamble));
|
sourceUrl(srcFileUrl), genFileUrl, statements, exportedVars, this._genFilePreamble));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,42 +758,43 @@ export function flatten<T>(list: Array<T|T[]>): T[] {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function sourceUrl(url: string) {
|
||||||
* Note: Using `location.origin` as prefix helps displaying them as a hierarchy in chrome.
|
// Note: We need 3 "/" so that ng shows up as a separate domain
|
||||||
* It also helps long-stack-trace zone when rewriting stack traces to not break
|
// in the chrome dev tools.
|
||||||
* source maps (as now all scripts have the same origin).
|
return url.replace(/(\w+:\/\/[\w:-]+)?(\/+)?/, 'ng:///');
|
||||||
*/
|
|
||||||
function ngJitFolder() {
|
|
||||||
return 'ng://';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function templateSourceUrl(
|
export function templateSourceUrl(
|
||||||
ngModuleType: CompileIdentifierMetadata, compMeta: {type: CompileIdentifierMetadata},
|
ngModuleType: CompileIdentifierMetadata, compMeta: {type: CompileIdentifierMetadata},
|
||||||
templateMeta: {isInline: boolean, templateUrl: string}) {
|
templateMeta: {isInline: boolean, templateUrl: string}) {
|
||||||
|
let url: string;
|
||||||
if (templateMeta.isInline) {
|
if (templateMeta.isInline) {
|
||||||
if (compMeta.type.reference instanceof StaticSymbol) {
|
if (compMeta.type.reference instanceof StaticSymbol) {
|
||||||
// Note: a .ts file might contain multiple components with inline templates,
|
// 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.
|
// 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 {
|
} else {
|
||||||
return `${ngJitFolder()}/${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`;
|
url = `${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`;
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
export function sharedStylesheetJitUrl(meta: CompileStylesheetMetadata, id: number) {
|
||||||
const pathParts = meta.moduleUrl.split(/\/\\/g);
|
const pathParts = meta.moduleUrl.split(/\/\\/g);
|
||||||
const baseName = pathParts[pathParts.length - 1];
|
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 {
|
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(
|
export function templateJitUrl(
|
||||||
ngModuleType: CompileIdentifierMetadata, compMeta: CompileDirectiveMetadata): string {
|
ngModuleType: CompileIdentifierMetadata, compMeta: CompileDirectiveMetadata): string {
|
||||||
return `${ngJitFolder()}/${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.ngfactory.js`;
|
return sourceUrl(`${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.ngfactory.js`);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
|
|
||||||
describe('aot source mapping', () => {
|
describe('aot source mapping', () => {
|
||||||
const componentPath = '/app/app.component.ts';
|
const componentPath = '/app/app.component.ts';
|
||||||
|
const ngComponentPath = 'ng:///app/app.component.ts'
|
||||||
|
|
||||||
let rootDir: MockDirectory;
|
let rootDir: MockDirectory;
|
||||||
let appDir: MockDirectory;
|
let appDir: MockDirectory;
|
||||||
@ -133,14 +134,15 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('inline templates', () => {
|
describe('inline templates', () => {
|
||||||
const templateUrl = `${componentPath}#AppComponent.html`;
|
const ngUrl = `${ngComponentPath}.AppComponent.html`;
|
||||||
|
|
||||||
function templateDecorator(template: string) { return `template: \`${template}\`,`; }
|
function templateDecorator(template: string) { return `template: \`${template}\`,`; }
|
||||||
|
|
||||||
declareTests({templateUrl, templateDecorator});
|
declareTests({ngUrl, templateDecorator});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('external templates', () => {
|
describe('external templates', () => {
|
||||||
|
const ngUrl = 'ng:///app/app.component.html';
|
||||||
const templateUrl = '/app/app.component.html';
|
const templateUrl = '/app/app.component.html';
|
||||||
|
|
||||||
function templateDecorator(template: string) {
|
function templateDecorator(template: string) {
|
||||||
@ -148,18 +150,17 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
return `templateUrl: 'app.component.html',`;
|
return `templateUrl: 'app.component.html',`;
|
||||||
}
|
}
|
||||||
|
|
||||||
declareTests({templateUrl, templateDecorator});
|
declareTests({ngUrl, templateDecorator});
|
||||||
});
|
});
|
||||||
|
|
||||||
function declareTests(
|
function declareTests({ngUrl, templateDecorator}:
|
||||||
{templateUrl, templateDecorator}:
|
{ngUrl: string, templateDecorator: (template: string) => string}) {
|
||||||
{templateUrl: string, templateDecorator: (template: string) => string}) {
|
|
||||||
it('should use the right source url in html parse errors', async(() => {
|
it('should use the right source url in html parse errors', async(() => {
|
||||||
appDir['app.component.ts'] =
|
appDir['app.component.ts'] =
|
||||||
createComponentSource(templateDecorator('<div>\n </error>'));
|
createComponentSource(templateDecorator('<div>\n </error>'));
|
||||||
|
|
||||||
expectPromiseToThrow(
|
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(() => {
|
it('should use the right source url in template parse errors', async(() => {
|
||||||
@ -167,7 +168,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
|
templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
|
||||||
|
|
||||||
expectPromiseToThrow(
|
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(() => {
|
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 generated file contains code that is not mapped to
|
||||||
// the template but rather to the original source file (e.g. import statements, ...)
|
// 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);
|
expect(sourceMap.sourcesContent[templateIndex]).toEqual(template);
|
||||||
|
|
||||||
// for the mapping to the original source file we don't store the source code
|
// 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.
|
// 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);
|
expect(sourceMap.sourcesContent[sourceIndex]).toBe(null);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -199,7 +200,7 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
compileApp().then((genFile) => {
|
compileApp().then((genFile) => {
|
||||||
const sourceMap = extractSourceMap(genFile.source);
|
const sourceMap = extractSourceMap(genFile.source);
|
||||||
expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `'span'`)))
|
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);
|
const sourceMap = extractSourceMap(genFile.source);
|
||||||
expect(
|
expect(
|
||||||
originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
|
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);
|
const sourceMap = extractSourceMap(genFile.source);
|
||||||
expect(
|
expect(
|
||||||
originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
|
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) => {
|
compileApp().then((genFile) => {
|
||||||
const sourceMap = extractSourceMap(genFile.source);
|
const sourceMap = extractSourceMap(genFile.source);
|
||||||
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
|
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
|
||||||
.toEqual({line: 1, column: 0, source: componentPath});
|
.toEqual({line: 1, column: 0, source: ngComponentPath});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -73,36 +73,36 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('inline templates', () => {
|
describe('inline templates', () => {
|
||||||
const templateUrl = 'ng:///DynamicTestModule/MyComp.html';
|
const ngUrl = 'ng:///DynamicTestModule/MyComp.html';
|
||||||
|
|
||||||
function templateDecorator(template: string) { return {template}; }
|
function templateDecorator(template: string) { return {template}; }
|
||||||
|
|
||||||
declareTests({templateUrl, templateDecorator});
|
declareTests({ngUrl, templateDecorator});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('external templates', () => {
|
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) {
|
function templateDecorator(template: string) {
|
||||||
resourceLoader.expect(templateUrl, template);
|
resourceLoader.expect(templateUrl, template);
|
||||||
return {templateUrl};
|
return {templateUrl};
|
||||||
}
|
}
|
||||||
|
|
||||||
declareTests({templateUrl, templateDecorator});
|
declareTests({ngUrl, templateDecorator});
|
||||||
});
|
});
|
||||||
|
|
||||||
function declareTests({templateUrl, templateDecorator}: {
|
function declareTests(
|
||||||
templateUrl: string,
|
{ngUrl, templateDecorator}:
|
||||||
templateDecorator: (template: string) => { [key: string]: any }
|
{ngUrl: string, templateDecorator: (template: string) => { [key: string]: any }}) {
|
||||||
}) {
|
|
||||||
it('should use the right source url in html parse errors', fakeAsync(() => {
|
it('should use the right source url in html parse errors', fakeAsync(() => {
|
||||||
@Component({...templateDecorator('<div>\n </error>')})
|
@Component({...templateDecorator('<div>\n </error>')})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(() => compileAndCreateComponent(MyComp))
|
expect(() => compileAndCreateComponent(MyComp))
|
||||||
.toThrowError(new RegExp(
|
.toThrowError(
|
||||||
`Template parse errors[\\s\\S]*${templateUrl.replace('$', '\\$')}@1:2`));
|
new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use the right source url in template parse errors', fakeAsync(() => {
|
it('should use the right source url in template parse errors', fakeAsync(() => {
|
||||||
@ -111,8 +111,8 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(() => compileAndCreateComponent(MyComp))
|
expect(() => compileAndCreateComponent(MyComp))
|
||||||
.toThrowError(new RegExp(
|
.toThrowError(
|
||||||
`Template parse errors[\\s\\S]*${templateUrl.replace('$', '\\$')}@1:7`));
|
new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create a sourceMap for templates', fakeAsync(() => {
|
it('should create a sourceMap for templates', fakeAsync(() => {
|
||||||
@ -126,7 +126,7 @@ export function main() {
|
|||||||
|
|
||||||
const sourceMap = getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js');
|
const sourceMap = getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js');
|
||||||
expect(sourceMap.sources).toEqual([
|
expect(sourceMap.sources).toEqual([
|
||||||
'ng:///DynamicTestModule/MyComp.ngfactory.js', templateUrl
|
'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl
|
||||||
]);
|
]);
|
||||||
expect(sourceMap.sourcesContent).toEqual([null, template]);
|
expect(sourceMap.sourcesContent).toEqual([null, template]);
|
||||||
}));
|
}));
|
||||||
@ -155,7 +155,7 @@ export function main() {
|
|||||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 4,
|
column: 4,
|
||||||
source: templateUrl,
|
source: ngUrl,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -179,13 +179,13 @@ export function main() {
|
|||||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 12,
|
column: 12,
|
||||||
source: templateUrl,
|
source: ngUrl,
|
||||||
});
|
});
|
||||||
// The error should be logged from the element
|
// The error should be logged from the element
|
||||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 4,
|
column: 4,
|
||||||
source: templateUrl,
|
source: ngUrl,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -209,13 +209,13 @@ export function main() {
|
|||||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 12,
|
column: 12,
|
||||||
source: templateUrl,
|
source: ngUrl,
|
||||||
});
|
});
|
||||||
// The error should be logged from the element
|
// The error should be logged from the element
|
||||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 4,
|
column: 4,
|
||||||
source: templateUrl,
|
source: ngUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user