Skip to content

Commit da1e039

Browse files
Keen Yee Liaukyliau
authored andcommitted
fix: attempt to resolve tsdk using fs path
This commit fixes an inconsistency in resolving `typescript.tsdk` in the Angular extension compared to native TypeScript extension. In Angular, `require.resolve` is used to resolve `typescript.tsdk`, but this does not work in environment that does not support node module resolution (where `node_modules` directory does not exist). The documentation for `typescript.tsdk` explicitly mentions the support of providing the value as a fs path: ``` Specifies the folder path to the tsserver and lib*.d.ts files under a TypeScript install to use for IntelliSense, for example: ./node_modules/typescript/lib. ```
1 parent 40a9084 commit da1e039

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

server/src/tests/version_provider_spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {isAbsolute, resolve} from 'path';
10+
911
import {resolveNgLangSvc, resolveTsServer, Version} from '../version_provider';
1012

1113
describe('Node Module Resolver', () => {
@@ -17,6 +19,14 @@ describe('Node Module Resolver', () => {
1719
expect(result.resolvedPath).toMatch(/typescript\/lib\/tsserverlibrary.js$/);
1820
});
1921

22+
it('should resolve tsserver from typescript.tsdk provided as fs path', () => {
23+
// Resolve relative to cwd.
24+
const absPath = resolve('node_modules/typescript/lib');
25+
expect(isAbsolute(absPath)).toBeTrue();
26+
const result = resolveTsServer([absPath]);
27+
expect(result.resolvedPath.endsWith('typescript/lib/tsserverlibrary.js')).toBeTrue();
28+
});
29+
2030
it('should be able to resolve Angular language service', () => {
2131
const result = resolveNgLangSvc(probeLocations, false /* ivy */);
2232
expect(result).toBeDefined();

server/src/version_provider.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
*/
88

99
import * as fs from 'fs';
10+
import * as path from 'path';
1011

1112
const MIN_TS_VERSION = '4.1';
1213
const MIN_NG_VERSION = '11.2';
1314
export const NGLANGSVC = '@angular/language-service';
15+
const TSSERVERLIB = 'typescript/lib/tsserverlibrary';
1416

1517
/**
1618
* Represents a valid node module that has been successfully resolved.
@@ -75,8 +77,41 @@ function resolveWithMinVersion(
7577
* @param probeLocations
7678
*/
7779
export function resolveTsServer(probeLocations: string[]): NodeModule {
78-
const tsserver = 'typescript/lib/tsserverlibrary';
79-
return resolveWithMinVersion(tsserver, MIN_TS_VERSION, probeLocations, 'typescript');
80+
if (probeLocations.length > 0) {
81+
// The first probe location is `typescript.tsdk` if it is specified.
82+
const resolvedFromTsdk = resolveTsServerFromTsdk(probeLocations[0]);
83+
if (resolvedFromTsdk !== undefined) {
84+
return resolvedFromTsdk;
85+
}
86+
}
87+
return resolveWithMinVersion(TSSERVERLIB, MIN_TS_VERSION, probeLocations, 'typescript');
88+
}
89+
90+
function resolveTsServerFromTsdk(tsdk: string): NodeModule|undefined {
91+
// `tsdk` is the folder path to the tsserver and lib*.d.ts files under a
92+
// TypeScript install, for example
93+
// - /google/src/head/depot/google3/third_party/javascript/node_modules/typescript/stable/lib
94+
if (!path.isAbsolute(tsdk)) {
95+
return undefined;
96+
}
97+
const tsserverlib = path.join(tsdk, 'tsserverlibrary.js');
98+
if (!fs.existsSync(tsserverlib)) {
99+
return undefined;
100+
}
101+
const packageJson = path.resolve(tsserverlib, '../../package.json');
102+
if (!fs.existsSync(packageJson)) {
103+
return undefined;
104+
}
105+
try {
106+
const json = JSON.parse(fs.readFileSync(packageJson, 'utf8'));
107+
return {
108+
name: TSSERVERLIB,
109+
resolvedPath: tsserverlib,
110+
version: new Version(json.version),
111+
};
112+
} catch {
113+
return undefined;
114+
}
80115
}
81116

82117
/**

0 commit comments

Comments
 (0)