fix(ivy): speed up ngtsc if project has no templates to check (#31922)
If a project being built with ngtsc has no templates to check, then ngtsc previously generated an empty typecheck file. This seems to trigger some pathological behavior in TS where the entire user program is re-checked, which is extremely expensive. This likely has to do with the fact that the empty file is not considered an ES module, meaning the module structure of the program has changed. This commit causes an export to be produced in the typecheck file regardless of its other contents, which guarantees that it will be an ES module. The pathological behavior is avoided and template type-checking is fast once again. PR Close #31922
This commit is contained in:
parent
ecffbda664
commit
82b97280f3
|
@ -59,6 +59,11 @@ export class TypeCheckFile extends Environment {
|
||||||
source += printer.printNode(ts.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
|
source += printer.printNode(ts.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the template type-checking file is an ES module. Otherwise, it's interpreted as some
|
||||||
|
// kind of global namespace in TS, which forces a full re-typecheck of the user's program that
|
||||||
|
// is somehow more expensive than the initial parse.
|
||||||
|
source += '\nexport const IS_A_MODULE = true;\n';
|
||||||
|
|
||||||
return ts.createSourceFile(
|
return ts.createSourceFile(
|
||||||
this.fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
this.fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,14 @@ import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflectio
|
||||||
import {getDeclaration, makeProgram} from '../../testing';
|
import {getDeclaration, makeProgram} from '../../testing';
|
||||||
import {getRootDirs} from '../../util/src/typescript';
|
import {getRootDirs} from '../../util/src/typescript';
|
||||||
import {TypeCheckContext} from '../src/context';
|
import {TypeCheckContext} from '../src/context';
|
||||||
|
import {TypeCheckFile} from '../src/type_check_file';
|
||||||
import {ALL_ENABLED_CONFIG} from './test_utils';
|
import {ALL_ENABLED_CONFIG} from './test_utils';
|
||||||
|
|
||||||
runInEachFileSystem(() => {
|
runInEachFileSystem(() => {
|
||||||
describe('ngtsc typechecking', () => {
|
describe('ngtsc typechecking', () => {
|
||||||
let _: typeof absoluteFrom;
|
let _: typeof absoluteFrom;
|
||||||
let LIB_D_TS: TestFile;
|
let LIB_D_TS: TestFile;
|
||||||
|
let TYPE_CHECK_TS: TestFile;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
_ = absoluteFrom;
|
_ = absoluteFrom;
|
||||||
|
@ -29,12 +31,25 @@ runInEachFileSystem(() => {
|
||||||
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
|
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
|
||||||
type NonNullable<T> = T extends null | undefined ? never : T;`
|
type NonNullable<T> = T extends null | undefined ? never : T;`
|
||||||
};
|
};
|
||||||
|
TYPE_CHECK_TS = {
|
||||||
|
name: _('/_typecheck_.ts'),
|
||||||
|
contents: `
|
||||||
|
export const IS_A_MODULE = true;
|
||||||
|
`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not produce an empty SourceFile when there is nothing to typecheck', () => {
|
||||||
|
const file =
|
||||||
|
new TypeCheckFile(_('/_typecheck_.ts'), ALL_ENABLED_CONFIG, new ReferenceEmitter([]));
|
||||||
|
const sf = file.render();
|
||||||
|
expect(sf.statements.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ctors', () => {
|
describe('ctors', () => {
|
||||||
it('compiles a basic type constructor', () => {
|
it('compiles a basic type constructor', () => {
|
||||||
const files: TestFile[] = [
|
const files: TestFile[] = [
|
||||||
LIB_D_TS, {
|
LIB_D_TS, TYPE_CHECK_TS, {
|
||||||
name: _('/main.ts'),
|
name: _('/main.ts'),
|
||||||
contents: `
|
contents: `
|
||||||
class TestClass<T extends string> {
|
class TestClass<T extends string> {
|
||||||
|
@ -72,7 +87,7 @@ TestClass.ngTypeCtor({value: 'test'});
|
||||||
|
|
||||||
it('should not consider query fields', () => {
|
it('should not consider query fields', () => {
|
||||||
const files: TestFile[] = [
|
const files: TestFile[] = [
|
||||||
LIB_D_TS, {
|
LIB_D_TS, TYPE_CHECK_TS, {
|
||||||
name: _('/main.ts'),
|
name: _('/main.ts'),
|
||||||
contents: `class TestClass { value: any; }`,
|
contents: `class TestClass { value: any; }`,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue