feat(ivy): add source mappings to compiled Angular templates (#28055)
During analysis, the `ComponentDecoratorHandler` passes the component template to the `parseTemplate()` function. Previously, there was little or no information about the original source file, where the template is found, passed when calling this function. Now, we correctly compute the URL of the source of the template, both for external `templateUrl` and in-line `template` cases. Further in the in-line template case we compute the character range of the template in its containing source file; *but only in the case that the template is a simple string literal*. If the template is actually a dynamic value like an interpolated string or a function call, then we do not try to add the originating source file information. The translator that converts Ivy AST nodes to TypeScript now adds these template specific source mappings, which account for the file where the template was found, to the templates to support stepping through the template creation and update code when debugging an Angular application. Note that some versions of TypeScript have a bug which means they cannot support external template source-maps. We check for this via the `canSourceMapExternalTemplates()` helper function and avoid trying to add template mappings to external templates if not supported. PR Close #28055
This commit is contained in:

committed by
Misko Hevery

parent
cffd86260a
commit
08de52b9f0
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @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 * as ts from 'typescript';
|
||||
|
||||
let _tsSourceMapBug29300Fixed: boolean|undefined;
|
||||
|
||||
/**
|
||||
* Test the current version of TypeScript to see if it has fixed the external SourceMap
|
||||
* file bug: https://github.com/Microsoft/TypeScript/issues/29300.
|
||||
*
|
||||
* The bug is fixed in TS 3.3+ but this check avoid us having to rely upon the version number,
|
||||
* and allows us to gracefully fail if the TS version still has the bug.
|
||||
*
|
||||
* We check for the bug by compiling a very small program `a;` and transforming it to `b;`,
|
||||
* where we map the new `b` identifier to an external source file, which has different lines to
|
||||
* the original source file. If the bug is fixed then the output SourceMap should contain
|
||||
* mappings that correspond ot the correct line/col pairs for this transformed node.
|
||||
*
|
||||
* @returns true if the bug is fixed.
|
||||
*/
|
||||
export function tsSourceMapBug29300Fixed() {
|
||||
if (_tsSourceMapBug29300Fixed === undefined) {
|
||||
let writtenFiles: {[filename: string]: string} = {};
|
||||
const sourceFile =
|
||||
ts.createSourceFile('test.ts', 'a;', ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS);
|
||||
const host = {
|
||||
getSourceFile(): ts.SourceFile | undefined{return sourceFile;},
|
||||
fileExists(): boolean{return true;},
|
||||
readFile(): string | undefined{return '';},
|
||||
writeFile(fileName: string, data: string) { writtenFiles[fileName] = data; },
|
||||
getDefaultLibFileName(): string{return '';},
|
||||
getCurrentDirectory(): string{return '';},
|
||||
getDirectories(): string[]{return [];},
|
||||
getCanonicalFileName(): string{return '';},
|
||||
useCaseSensitiveFileNames(): boolean{return true;},
|
||||
getNewLine(): string{return '\n';},
|
||||
};
|
||||
|
||||
const transform = (context: ts.TransformationContext) => {
|
||||
return (node: ts.SourceFile) => ts.visitNode(node, visitor);
|
||||
function visitor(node: ts.Node): ts.Node {
|
||||
if (ts.isIdentifier(node) && node.text === 'a') {
|
||||
const newNode = ts.createIdentifier('b');
|
||||
ts.setSourceMapRange(newNode, {
|
||||
pos: 16,
|
||||
end: 16,
|
||||
source: ts.createSourceMapSource('test.html', 'abc\ndef\nghi\njkl\nmno\npqr')
|
||||
});
|
||||
return newNode;
|
||||
}
|
||||
return ts.visitEachChild(node, visitor, context);
|
||||
}
|
||||
};
|
||||
|
||||
const program = ts.createProgram(['test.ts'], {sourceMap: true}, host);
|
||||
program.emit(sourceFile, undefined, undefined, undefined, {after: [transform]});
|
||||
// The first two mappings in the source map should look like:
|
||||
// [0,1,4,0] col 0 => source file 1, row 4, column 0)
|
||||
// [1,0,0,0] col 1 => source file 1, row 4, column 0)
|
||||
_tsSourceMapBug29300Fixed = /ACIA,CAAA/.test(writtenFiles['test.js.map']);
|
||||
}
|
||||
return _tsSourceMapBug29300Fixed;
|
||||
}
|
Reference in New Issue
Block a user