Skip to content

Commit

Permalink
fix: retain typecheck files after project reload
Browse files Browse the repository at this point in the history
It turns out typecheck files are not retained even after the fix in
angular/angular#40162.

For a complete explanation of the situation, see prior issue:
#1030

This is because even after adding the typecheck files to the list of
files to retain, tsserver will still remove them from a project if they
do not exist on disk:
https://github.com/microsoft/TypeScript/blob/3c32f6e154ead6749b76ec9c19cbfdd2acad97d6/src/server/editorServices.ts#L2188-L2193

In order to force tsserver to retain the files, we need to fake their
existence. This is done in the `ServerHost`.

Admittedly, this is not a great fix because it relies on the knowledge
of the typecheck file suffix, which is an implementation detail in the
compiler.

The alternative is to explore the concept of "dynamic files", which are
files that exist only in memory and not on disk. This would require
changes to the Ivy compiler and `@angular/language-service`.
Ideally, the compiler should ask `@angular/language-service` how to name the
typecheck files, so that the naming is dictated by LS rather than
the compiler.

fix #1090
  • Loading branch information
Keen Yee Liau committed Mar 1, 2021
1 parent dd91729 commit bc9d9fc
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 3 deletions.
29 changes: 28 additions & 1 deletion integration/lsp/ivy_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import * as fs from 'fs';
import {promisify} from 'util';
import {MessageConnection} from 'vscode-jsonrpc';
import * as lsp from 'vscode-languageserver-protocol';
import {URI} from 'vscode-uri';
Expand All @@ -15,7 +16,9 @@ import {ProjectLanguageService, ProjectLanguageServiceParams, SuggestStrictMode,
import {NgccProgress, NgccProgressToken, NgccProgressType} from '../../common/progress';
import {GetComponentsWithTemplateFile, GetTcbRequest} from '../../common/requests';

import {APP_COMPONENT, createConnection, createTracer, FOO_COMPONENT, FOO_TEMPLATE, initializeServer, openTextDocument, TSCONFIG} from './test_utils';
import {APP_COMPONENT, createConnection, createTracer, FOO_COMPONENT, FOO_TEMPLATE, initializeServer, openTextDocument, PROJECT_PATH, TSCONFIG} from './test_utils';

const setTimeoutP = promisify(setTimeout);

describe('Angular Ivy language server', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; /* 10 seconds */
Expand Down Expand Up @@ -125,6 +128,30 @@ describe('Angular Ivy language server', () => {
});
});

describe('project reload', () => {
const dummy = `${PROJECT_PATH}/node_modules/__foo__`;

afterEach(() => {
fs.unlinkSync(dummy);
});

it('should retain typecheck files', async () => {
openTextDocument(client, APP_COMPONENT);
const languageServiceEnabled = await waitForNgcc(client);
expect(languageServiceEnabled).toBeTrue();
// Create a file in node_modules, this will trigger a project reload via
// the directory watcher
fs.writeFileSync(dummy, '');
// Project reload happens after 250ms delay
// https://github.com/microsoft/TypeScript/blob/3c32f6e154ead6749b76ec9c19cbfdd2acad97d6/src/server/editorServices.ts#L957
await setTimeoutP(500);
// The following operation would result in compiler crash if typecheck
// files are not retained after project reload
const diagnostics = await getDiagnosticsForFile(client, APP_COMPONENT);
expect(diagnostics.length).toBe(0);
});
});

describe('renaming', () => {
describe('from template files', () => {
beforeEach(async () => {
Expand Down
8 changes: 6 additions & 2 deletions integration/lsp/test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as lsp from 'vscode-languageserver-protocol';

const SERVER_PATH = resolve(__dirname, '../../../dist/npm/server/index.js');
const PACKAGE_ROOT = resolve(__dirname, '../../..');
const PROJECT_PATH = `${PACKAGE_ROOT}/integration/project`;
export const PROJECT_PATH = `${PACKAGE_ROOT}/integration/project`;
export const APP_COMPONENT = `${PROJECT_PATH}/app/app.component.ts`;
export const FOO_TEMPLATE = `${PROJECT_PATH}/app/foo.component.html`;
export const FOO_COMPONENT = `${PROJECT_PATH}/app/foo.component.ts`;
Expand All @@ -40,7 +40,11 @@ export function createConnection(serverOptions: ServerOptions): MessageConnectio
// uncomment to debug server process
// execArgv: ['--inspect-brk=9330']
});

server.on('close', (code: number) => {
if (code !== null && code !== 0) {
throw new Error(`Server exited with code: ${code}`);
}
});
const connection = createMessageConnection(
new IPCMessageReader(server),
new IPCMessageWriter(server),
Expand Down
3 changes: 3 additions & 0 deletions server/src/server_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export class ServerHost implements ts.server.ServerHost {
}

fileExists(path: string): boolean {
if (path.endsWith('.ngtypecheck.ts')) {
return true;
}
return ts.sys.fileExists(path);
}

Expand Down

0 comments on commit bc9d9fc

Please sign in to comment.