angular/packages/language-service/ivy/test/language_service_spec.ts
Keen Yee Liau 8f1317f06f fix(language-service): [Ivy] create compiler only when program changes (#39231)
This commit fixes a bug in which a new Ivy Compiler is created every time
language service receives a new request. This is not needed if the
`ts.Program` has not changed.

A new class `CompilerFactory` is created to manage Compiler lifecycle and
keep track of template changes so that it knows when to override them.
With this change, we no longer need the method `getModifiedResourceFile()`
on the adapter. Instead, we call `overrideComponentTemplate` on the
template type checker.

This commit also changes the incremental build strategy from
`PatchedIncrementalBuildStrategy` to `TrackedIncrementalBuildStrategy`.

PR Close #39231
2020-10-14 14:10:37 -07:00

113 lines
4.1 KiB
TypeScript

/**
* @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 {LanguageService, parseNgCompilerOptions} from '../language_service';
import {setup, TEST_TEMPLATE} from './mock_host';
const {project, tsLS, service} = setup();
describe('parseNgCompilerOptions', () => {
it('should read angularCompilerOptions in tsconfig.json', () => {
const options = parseNgCompilerOptions(project);
expect(options).toEqual(jasmine.objectContaining({
enableIvy: true, // default for ivy is true
strictTemplates: true,
strictInjectionParameters: true,
}));
});
});
describe('last known program', () => {
const ngLS = new LanguageService(project, tsLS);
beforeEach(() => {
service.reset();
});
it('should be set after getSemanticDiagnostics()', () => {
const d0 = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(d0.length).toBe(0);
const p0 = getLastKnownProgram(ngLS);
const d1 = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(d1.length).toBe(0);
const p1 = getLastKnownProgram(ngLS);
expect(p1).toBe(p0); // last known program should not have changed
service.overwrite(TEST_TEMPLATE, `<test-c¦omp></test-comp>`);
const d2 = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(d2.length).toBe(0);
const p2 = getLastKnownProgram(ngLS);
expect(p2).not.toBe(p1); // last known program should have changed
});
it('should be set after getDefinitionAndBoundSpan()', () => {
const {position: pos0} = service.overwrite(TEST_TEMPLATE, `<test-c¦omp></test-comp>`);
const d0 = ngLS.getDefinitionAndBoundSpan(TEST_TEMPLATE, pos0);
expect(d0).toBeDefined();
const p0 = getLastKnownProgram(ngLS);
const d1 = ngLS.getDefinitionAndBoundSpan(TEST_TEMPLATE, pos0);
expect(d1).toBeDefined();
const p1 = getLastKnownProgram(ngLS);
expect(p1).toBe(p0); // last known program should not have changed
const {position: pos1} = service.overwrite(TEST_TEMPLATE, `{{ ti¦tle }}`);
const d2 = ngLS.getDefinitionAndBoundSpan(TEST_TEMPLATE, pos1);
expect(d2).toBeDefined();
const p2 = getLastKnownProgram(ngLS);
expect(p2).not.toBe(p1); // last known program should have changed
});
it('should be set after getQuickInfoAtPosition()', () => {
const {position: pos0} = service.overwrite(TEST_TEMPLATE, `<test-c¦omp></test-comp>`);
const q0 = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, pos0);
expect(q0).toBeDefined();
const p0 = getLastKnownProgram(ngLS);
const q1 = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, pos0);
expect(q1).toBeDefined();
const p1 = getLastKnownProgram(ngLS);
expect(p1).toBe(p0); // last known program should not have changed
const {position: pos1} = service.overwrite(TEST_TEMPLATE, `{{ ti¦tle }}`);
const q2 = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, pos1);
expect(q2).toBeDefined();
const p2 = getLastKnownProgram(ngLS);
expect(p2).not.toBe(p1); // last known program should have changed
});
it('should be set after getTypeDefinitionAtPosition()', () => {
const {position: pos0} = service.overwrite(TEST_TEMPLATE, `<test-c¦omp></test-comp>`);
const q0 = ngLS.getTypeDefinitionAtPosition(TEST_TEMPLATE, pos0);
expect(q0).toBeDefined();
const p0 = getLastKnownProgram(ngLS);
const d1 = ngLS.getTypeDefinitionAtPosition(TEST_TEMPLATE, pos0);
expect(d1).toBeDefined();
const p1 = getLastKnownProgram(ngLS);
expect(p1).toBe(p0); // last known program should not have changed
const {position: pos1} = service.overwrite(TEST_TEMPLATE, `{{ ti¦tle }}`);
const d2 = ngLS.getTypeDefinitionAtPosition(TEST_TEMPLATE, pos1);
expect(d2).toBeDefined();
const p2 = getLastKnownProgram(ngLS);
expect(p2).not.toBe(p1); // last known program should have changed
});
});
function getLastKnownProgram(ngLS: LanguageService): ts.Program {
const program = ngLS['compilerFactory']['lastKnownProgram'];
expect(program).toBeDefined();
return program!;
}