diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index fb0f17dc16..ee93c25aa5 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -73,6 +73,16 @@ export function getWordRange(wordPattern: RegExp, position: Position, document: } return undefined; } +export function loadWorkspaceTypescript(root: string, tsdk: string): typeof import('typescript/lib/tsserverlibrary') { + const tsPath = path.join(root, tsdk, 'tsserverlibrary.js'); + return require(path.toUnix(tsPath)); +} +export function loadWorkspaceTypescriptLocalized(root: string, tsdk: string, lang: string): MapLike | undefined { + const tsPath = path.join(root, tsdk, lang, 'diagnosticMessages.generated.json'); + if (fs.existsSync(tsPath)) { + return require(path.toUnix(tsPath)); + } +} export function loadVscodeTypescript(appRoot: string): typeof import('typescript/lib/tsserverlibrary') { const tsPath = path.join(appRoot, 'extensions', 'node_modules', 'typescript'); return require(path.toUnix(tsPath)); diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index c9395b8cb2..3564a5d99c 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -3,4 +3,5 @@ export interface ServerInitializationOptions { appRoot: string, language: string, tsPlugin: boolean, + tsdk: string | undefined, } diff --git a/packages/vscode-client/src/extension.ts b/packages/vscode-client/src/extension.ts index 0c203538cb..69babc94ef 100644 --- a/packages/vscode-client/src/extension.ts +++ b/packages/vscode-client/src/extension.ts @@ -67,6 +67,7 @@ function createLanguageService(context: vscode.ExtensionContext, mode: 'api' | ' appRoot: vscode.env.appRoot, language: vscode.env.language, tsPlugin: tsPlugin.isTsPluginEnabled(), + tsdk: vscode.workspace.getConfiguration('typescript').get('tsdk') ?? undefined, }; const clientOptions: lsp.LanguageClientOptions = { documentSelector: fileOnly ? diff --git a/packages/vscode-server/src/server.ts b/packages/vscode-server/src/server.ts index e46c43fc70..e86ecf7b4e 100644 --- a/packages/vscode-server/src/server.ts +++ b/packages/vscode-server/src/server.ts @@ -1,5 +1,7 @@ import { DocumentVersionRequest, + loadWorkspaceTypescript, + loadWorkspaceTypescriptLocalized, loadVscodeTypescript, loadVscodeTypescriptLocalized, SemanticTokensChangedNotification, @@ -27,10 +29,14 @@ const documents = new TextDocuments(TextDocument); let options: ServerInitializationOptions; let folders: string[] = []; +let updateTsdk: Function | undefined; connection.onInitialize(onInitialize); connection.onInitialized(onInitialized); -connection.onDidChangeConfiguration(() => updateConfigs(connection)); +connection.onDidChangeConfiguration(() => { + updateTsdk?.(); + updateConfigs(connection); +}); connection.listen(); documents.listen(connection); @@ -74,14 +80,13 @@ async function onInitialized() { let servicesManager: ServicesManager | undefined; if (options.mode === 'html') { - const noStateLs = getDocumentLanguageService({ typescript: loadVscodeTypescript(options.appRoot) }); + const noStateLs = getDocumentLanguageService({ typescript: getTs().module }); (await import('./features/htmlFeatures')).register(connection, documents, noStateLs); } else if (options.mode === 'api') { servicesManager = createServicesManager( 'api', - loadVscodeTypescript(options.appRoot), - loadVscodeTypescriptLocalized(options.appRoot, options.language), + getTs, connection, documents, folders, @@ -90,8 +95,7 @@ async function onInitialized() { else if (options.mode === 'doc') { servicesManager = createServicesManager( 'doc', - loadVscodeTypescript(options.appRoot), - loadVscodeTypescriptLocalized(options.appRoot, options.language), + getTs, connection, documents, folders, @@ -112,4 +116,29 @@ async function onInitialized() { } connection.client.register(DidChangeConfigurationNotification.type, undefined); updateConfigs(connection); + updateTsdk = async () => { + const newTsdk: string | undefined = await connection.workspace.getConfiguration('typescript.tsdk') ?? undefined; + if (newTsdk !== options.tsdk) { + options.tsdk = newTsdk; + servicesManager?.restartAll(); + } + }; +} + +function getTs() { + if (options.tsdk) { + for (const folder of folders) { + const ts = loadWorkspaceTypescript(folder, options.tsdk); + if (ts) { + return { + module: ts, + localized: loadWorkspaceTypescriptLocalized(folder, options.tsdk, options.language), + }; + } + } + } + return { + module: loadVscodeTypescript(options.appRoot), + localized: loadVscodeTypescriptLocalized(options.appRoot, options.language), + } } diff --git a/packages/vscode-server/src/servicesManager.ts b/packages/vscode-server/src/servicesManager.ts index 0fa47ce333..0b18112122 100644 --- a/packages/vscode-server/src/servicesManager.ts +++ b/packages/vscode-server/src/servicesManager.ts @@ -9,8 +9,10 @@ export type ServicesManager = ReturnType; export function createServicesManager( mode: 'api' | 'doc', - ts: typeof import('typescript/lib/tsserverlibrary'), - tsLocalized: ts.MapLike | undefined, + getTs: () => { + module: typeof import('typescript/lib/tsserverlibrary'), + localized: ts.MapLike | undefined, + }, connection: Connection, documents: TextDocuments, rootPaths: string[], @@ -19,10 +21,11 @@ export function createServicesManager( ) { let filesUpdateTrigger = false; + const originalTs = getTs().module; const tsConfigNames = ['tsconfig.json', 'jsconfig.json']; const tsConfigWatchers = new Map(); const services = new Map(); - const tsConfigSet = new Set(rootPaths.map(rootPath => ts.sys.readDirectory(rootPath, tsConfigNames, undefined, ['**/*'])).flat()); + const tsConfigSet = new Set(rootPaths.map(rootPath => originalTs.sys.readDirectory(rootPath, tsConfigNames, undefined, ['**/*'])).flat()); const tsConfigs = [...tsConfigSet].filter(tsConfig => tsConfigNames.includes(upath.basename(tsConfig))); const checkedProject = new Set(); @@ -30,7 +33,7 @@ export function createServicesManager( onTsConfigChanged(tsConfig); } for (const rootPath of rootPaths) { - ts.sys.watchDirectory!(rootPath, async fileName => { + originalTs.sys.watchDirectory!(rootPath, async fileName => { if (tsConfigNames.includes(upath.basename(fileName))) { // tsconfig.json changed onTsConfigChanged(fileName); @@ -147,6 +150,9 @@ export function createServicesManager( } } async function onTsConfigChanged(tsConfig: string) { + const _ts = getTs(); + const ts = _ts.module; + const tsLocalized = _ts.localized; for (const doc of documents.all()) { if (doc.languageId === 'vue') { connection.sendDiagnostics({ uri: doc.uri, diagnostics: [] });