refactor(compiler-cli): remove TS bug workaround (#42000)

The TS bug at https://github.com/Microsoft/TypeScript/issues/29300 was
fixed in TS 3.3, so the workaround is no longer required.

PR Close #42000
This commit is contained in:
Pete Bacon Darwin 2021-05-08 09:06:16 +01:00 committed by Alex Rickabaugh
parent 500db42a2e
commit 0cb68632c6
3 changed files with 80 additions and 184 deletions

View File

@ -23,7 +23,6 @@ import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObj
import {ComponentScopeReader, LocalModuleScopeRegistry, TypeCheckScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api';
import {tsSourceMapBug29300Fixed} from '../../util/src/ts_source_map_bug_29300';
import {SubsetOfKeys} from '../../util/src/typescript';
import {ResourceLoader} from './api';
@ -1231,7 +1230,7 @@ export class ComponentDecoratorHandler implements
templateUrl,
templateUrlExpression: templateUrlExpr,
resolvedTemplateUrl: resourceUrl,
potentialSourceMapUrl: sourceMapUrl(resourceUrl),
potentialSourceMapUrl: resourceUrl,
};
} catch (e) {
throw this.makeResourceNotFoundError(
@ -1342,17 +1341,6 @@ function getTemplateRange(templateExpr: ts.Expression) {
};
}
function sourceMapUrl(resourceUrl: string): string {
if (!tsSourceMapBug29300Fixed()) {
// By removing the template URL we are telling the translator not to try to
// map the external source file to the generated code, since the version
// of TS that is running does not support it.
return '';
} else {
return resourceUrl;
}
}
/** Determines if the result of an evaluation is a string array. */
function isStringArray(resolvedValue: ResolvedValue): resolvedValue is string[] {
return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string');

View File

@ -1,90 +0,0 @@
/**
* @license
* Copyright Google LLC 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;
}

View File

@ -11,7 +11,6 @@ import {inspect} from 'util';
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
import {loadStandardTestFiles} from '../../src/ngtsc/testing';
import {tsSourceMapBug29300Fixed} from '../../src/ngtsc/util/src/ts_source_map_bug_29300';
import {NgtscTestEnvironment} from './env';
import {getMappedSegments, SegmentMapping} from './sourcemap_utils';
@ -603,91 +602,90 @@ runInEachFileSystem((os) => {
});
});
if (tsSourceMapBug29300Fixed()) {
describe('External templates (where TS supports source-mapping)', () => {
it('should create external template source-mapping', () => {
const mappings =
compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>', './dir/test.html');
describe('External templates (where TS supports source-mapping)', () => {
it('should create external template source-mapping', () => {
const mappings =
compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>', './dir/test.html');
// Creation mode
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(0, "div")',
source: '<div>',
sourceUrl: '../dir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(1, "this is a test")',
source: 'this is a test',
sourceUrl: '../dir/test.html'
});
expectMapping(
mappings,
{generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(2, "div")',
source: '<div>',
sourceUrl: '../dir/test.html'
});
expectMapping(
mappings,
{generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../dir/test.html'});
expectMapping(
mappings,
{generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
// Update mode
expectMapping(mappings, {
generated: 'i0.ɵɵtextInterpolate(1 + 2)',
source: '{{ 1 + 2 }}',
sourceUrl: '../dir/test.html'
});
// Creation mode
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(0, "div")',
source: '<div>',
sourceUrl: '../dir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(1, "this is a test")',
source: 'this is a test',
sourceUrl: '../dir/test.html'
});
expectMapping(
mappings,
{generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(2, "div")',
source: '<div>',
sourceUrl: '../dir/test.html'
});
expectMapping(
mappings,
{generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../dir/test.html'});
expectMapping(
mappings,
{generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
it('should create correct mappings when templateUrl is in a different rootDir', () => {
const mappings = compileAndMap(
'<div>this is a test</div><div>{{ 1 + 2 }}</div>', 'extraRootDir/test.html');
// Creation mode
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(0, "div")',
source: '<div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(1, "this is a test")',
source: 'this is a test',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementEnd()',
source: '</div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(2, "div")',
source: '<div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(3)',
source: '{{ 1 + 2 }}',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementEnd()',
source: '</div>',
sourceUrl: '../extraRootDir/test.html'
});
// Update mode
expectMapping(mappings, {
generated: 'i0.ɵɵtextInterpolate(1 + 2)',
source: '{{ 1 + 2 }}',
sourceUrl: '../extraRootDir/test.html'
});
// Update mode
expectMapping(mappings, {
generated: 'i0.ɵɵtextInterpolate(1 + 2)',
source: '{{ 1 + 2 }}',
sourceUrl: '../dir/test.html'
});
});
}
it('should create correct mappings when templateUrl is in a different rootDir', () => {
const mappings = compileAndMap(
'<div>this is a test</div><div>{{ 1 + 2 }}</div>', 'extraRootDir/test.html');
// Creation mode
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(0, "div")',
source: '<div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(1, "this is a test")',
source: 'this is a test',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementEnd()',
source: '</div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementStart(2, "div")',
source: '<div>',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵtext(3)',
source: '{{ 1 + 2 }}',
sourceUrl: '../extraRootDir/test.html'
});
expectMapping(mappings, {
generated: 'i0.ɵɵelementEnd()',
source: '</div>',
sourceUrl: '../extraRootDir/test.html'
});
// Update mode
expectMapping(mappings, {
generated: 'i0.ɵɵtextInterpolate(1 + 2)',
source: '{{ 1 + 2 }}',
sourceUrl: '../extraRootDir/test.html'
});
});
});
function compileAndMap(template: string, templateUrl: string|null = null) {
const templateConfig = templateUrl ? `templateUrl: '${templateUrl}'` :