import { Uri, window, workspace } from 'coc.nvim' /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import fs from 'fs' import path from 'path' import API from './api' import { TypeScriptServiceConfiguration } from './configuration' export class TypeScriptVersion { private _api: API | null | undefined constructor( public readonly path: string, private readonly _pathLabel?: string ) { this._api = null } public get tscPath(): string { return path.resolve(this.path, '../bin/tsc') } public get tsServerPath(): string { return path.resolve(this.path, 'tsserver.js') } public get pathLabel(): string { return typeof this._pathLabel === 'undefined' ? this.path : this._pathLabel } public get isValid(): boolean { return this.version != null } public get version(): API | null { if (this._api) return this._api let api = this._api = this.getTypeScriptVersion(this.tsServerPath) return api } public get versionString(): string | null { const version = this.version return version ? version.versionString : null } private getTypeScriptVersion(serverPath: string): API | undefined { if (!fs.existsSync(serverPath)) { return undefined } const p = serverPath.split(path.sep) if (p.length <= 2) { return undefined } const p2 = p.slice(0, -2) const modulePath = p2.join(path.sep) let fileName = path.join(modulePath, 'package.json') if (!fs.existsSync(fileName)) { // Special case for ts dev versions if (path.basename(modulePath) === 'built') { fileName = path.join(modulePath, '..', 'package.json') } } if (!fs.existsSync(fileName)) { return undefined } const contents = fs.readFileSync(fileName).toString() let desc: any = null try { desc = JSON.parse(contents) } catch (err) { return undefined } if (!desc || !desc.version) { return undefined } return desc.version ? API.fromVersionString(desc.version) : undefined } } const MODULE_FOLDERS = ['node_modules/typescript/lib', '.vscode/pnpify/typescript/lib', '.yarn/sdks/typescript/lib'] export class TypeScriptVersionProvider { public constructor(private configuration: TypeScriptServiceConfiguration) {} public updateConfiguration( configuration: TypeScriptServiceConfiguration ): void { this.configuration = configuration } public getDefaultVersion(): TypeScriptVersion { // tsdk from configuration let { globalTsdk } = this.configuration if (globalTsdk) return new TypeScriptVersion(globalTsdk) return this.bundledVersion } public get globalVersion(): TypeScriptVersion | undefined { let { globalTsdk } = this.configuration if (globalTsdk) return new TypeScriptVersion(workspace.expand(globalTsdk)) return undefined } public getVersionFromTscPath(tscPath: string): TypeScriptVersion | undefined { if (!tscPath || !fs.existsSync(tscPath)) return undefined let libFolder = path.resolve(tscPath, '../../lib') if (fs.existsSync(libFolder)) { let version = new TypeScriptVersion(libFolder) if (version.isValid) return version } } public getLocalVersion(): TypeScriptVersion | undefined { let folders = workspace.workspaceFolders.map(f => Uri.parse(f.uri).fsPath) for (let p of folders) { for (let folder of MODULE_FOLDERS) { let libFolder = path.join(p, folder) if (fs.existsSync(libFolder)) { let version = new TypeScriptVersion(libFolder) if (version.isValid) return version } } } return null } public get bundledVersion(): TypeScriptVersion | null { try { const file = require.resolve('typescript') const bundledVersion = new TypeScriptVersion( path.dirname(file), '') return bundledVersion } catch (e) { window.showMessage('Bundled typescript module not found', 'error') return null } } }