From 1f9f20f04351bd048daa00f1908181d061771bdc Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Thu, 25 Aug 2016 16:25:34 -0700 Subject: [PATCH] Swap out TSD for Types 2.0 --- src/server/editorServices.ts | 2 +- src/server/project.ts | 3 + src/server/types.d.ts | 1 - src/server/typingsCache.ts | 17 ++-- .../typingsInstaller/nodeTypingsInstaller.ts | 44 +++------ .../typingsInstaller/typingsInstaller.ts | 90 +++++++------------ 6 files changed, 52 insertions(+), 105 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4add13dd183..394613bebe4 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -213,7 +213,7 @@ namespace ts.server { } switch (response.kind) { case "set": - this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings, response.files); + this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings); project.updateGraph(); break; case "invalidate": diff --git a/src/server/project.ts b/src/server/project.ts index e8293decdd3..6d48c5d87be 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -355,6 +355,9 @@ namespace ts.server { removed.push(id); } } + if (added.length > 0 || removed.length > 0) { + this.projectService.typingsCache.invalidateCachedTypingsForProject(this); + } this.lastReportedFileNames = currentFiles; this.lastReportedFileNames = currentFiles; diff --git a/src/server/types.d.ts b/src/server/types.d.ts index 3a9733cd0e2..b4694cc4017 100644 --- a/src/server/types.d.ts +++ b/src/server/types.d.ts @@ -45,7 +45,6 @@ declare namespace ts.server { readonly typingOptions: ts.TypingOptions; readonly compilerOptions: ts.CompilerOptions; readonly typings: string[]; - readonly files: string[]; readonly kind: "set"; } diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 81dd483e65c..1ef93fbbb70 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -17,12 +17,11 @@ namespace ts.server { readonly typingOptions: TypingOptions; readonly compilerOptions: CompilerOptions; readonly typings: TypingsArray; - readonly files: string[]; poisoned: boolean; } const emptyArray: any[] = []; - const jsOrDts = [".js", ".d.ts"]; + const jsOrDts = [".js", ".jsx", ".d.ts"]; function getTypingOptionsForProjects(proj: Project): TypingOptions { if (proj.projectKind === ProjectKind.Configured) { @@ -74,10 +73,6 @@ namespace ts.server { return opt1.allowJs != opt2.allowJs; } - function filesChanged(before: string[], after: string[]): boolean { - return !setIsEqualTo(before, after); - } - export interface TypingsArray extends ReadonlyArray { " __typingsArrayBrand": any; } @@ -102,18 +97,17 @@ namespace ts.server { const entry = this.perProjectCache[project.getProjectName()]; const result: TypingsArray = entry ? entry.typings : emptyArray; - if (!entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions) || filesChanged(project.getFileNames(), entry.files)) { - // something has been changed, issue a request to update typings - this.installer.enqueueInstallTypingsRequest(project, typingOptions); + if (!entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions)) { // Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options. // instead it acts as a placeholder to prevent issuing multiple requests this.perProjectCache[project.getProjectName()] = { compilerOptions: project.getCompilerOptions(), typingOptions, typings: result, - files: project.getFileNames(), poisoned: true }; + // something has been changed, issue a request to update typings + this.installer.enqueueInstallTypingsRequest(project, typingOptions); } return result; } @@ -126,12 +120,11 @@ namespace ts.server { this.installer.enqueueInstallTypingsRequest(project, typingOptions); } - updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[], files: string[]) { + updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) { this.perProjectCache[projectName] = { compilerOptions, typingOptions, typings: toTypingsArray(newTypings), - files: files, poisoned: false }; } diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index e0a361f9411..13c70da9f8d 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -46,7 +46,7 @@ namespace ts.server.typingsInstaller { private exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any }; private npmBinPath: string; - private tsdRunCount = 1; + private installRunCount = 1; readonly installTypingHost: InstallTypingHost = sys; constructor(log?: Log) { @@ -101,23 +101,6 @@ namespace ts.server.typingsInstaller { } } - protected installPackage(packageName: string) { - try { - const output = this.execSync(`npm install --silent --global ${packageName}`, { stdio: "pipe" }).toString(); - if (this.log.isEnabled()) { - this.log.writeLine(`installPackage::stdout '${output}'`); - } - return true; - } - catch (e) { - if (this.log.isEnabled()) { - this.log.writeLine(`installPackage::err::stdout '${e.stdout && e.stdout.toString()}'`); - this.log.writeLine(`installPackage::err::stderr '${e.stdout && e.stderr.toString()}'`); - } - return false; - } - } - protected sendResponse(response: SetTypings | InvalidateCachedTypings) { if (this.log.isEnabled()) { this.log.writeLine(`Sending response: ${JSON.stringify(response)}`); @@ -128,30 +111,23 @@ namespace ts.server.typingsInstaller { } } - protected runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void { - const id = this.tsdRunCount; - this.tsdRunCount++; - const tsdPath = combinePaths(this.npmBinPath, "tsd"); - const command = `${tsdPath} install ${typingsToInstall.join(" ")} -ros`; + protected runInstall(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void { + const id = this.installRunCount; + this.installRunCount++; + const command = `npm install @types/${typingsToInstall.join(" @types/")} --save-dev`; if (this.log.isEnabled()) { - this.log.writeLine(`Running tsd ${id}, command '${command}'. cache path '${cachePath}'`); + this.log.writeLine(`Running npm install @types ${id}, command '${command}'. cache path '${cachePath}'`); } this.exec(command, { cwd: cachePath }, (err, stdout, stderr) => { if (this.log.isEnabled()) { - this.log.writeLine(`TSD ${id} stdout: ${stdout}`); - this.log.writeLine(`TSD ${id} stderr: ${stderr}`); - } - const i = stdout.indexOf("running install"); - if (i < 0) { - return; + this.log.writeLine(`npm install @types ${id} stdout: ${stdout}`); + this.log.writeLine(`npm install @types ${id} stderr: ${stderr}`); } const installedTypings: string[] = []; - - const expr = /^\s*-\s*(\S+)\s*$/gm; - expr.lastIndex = i; + const expr = /^.*(node_modules)\\(@types)\\(\S+)\s*$/gm; let match: RegExpExecArray; while (match = expr.exec(stdout)) { - installedTypings.push(match[1]); + installedTypings.push(`${match[1]}/${match[2]}/${match[3]}`); } postInstallAction(installedTypings); }); diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 701763b9560..ac72782eb5d 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -1,16 +1,10 @@ +/// /// /// namespace ts.server.typingsInstaller { - const DefaultTsdSettings = JSON.stringify({ - version: "v4", - repo: "DefinitelyTyped/DefinitelyTyped", - ref: "master", - path: "typings" - }, /*replacer*/undefined, /*space*/4); - - interface TsdConfig { - installed: MapLike; + interface NpmConfig { + devDependencies: MapLike; } export interface Log { @@ -23,18 +17,17 @@ namespace ts.server.typingsInstaller { writeLine: () => {} }; - function tsdTypingToFileName(cachePath: string, tsdTypingFile: string) { - return combinePaths(cachePath, `typings/${tsdTypingFile}`); + function typingToFileName(cachePath: string, packageName: string, installTypingHost: InstallTypingHost): string { + const result = resolveModuleName(packageName, combinePaths(cachePath, "index.d.ts"), { moduleResolution: ModuleResolutionKind.NodeJs }, installTypingHost); + return result.resolvedModule ? result.resolvedModule.resolvedFileName : null; } - function getPackageName(tsdTypingFile: string) { - const idx = tsdTypingFile.indexOf("/"); - return idx > 0 ? tsdTypingFile.substr(0, idx) : undefined; + function getPackageName(typingFile: string) { + const idx = typingFile.lastIndexOf("/"); + return idx > 0 ? typingFile.substr(idx + 1) : undefined; } export abstract class TypingsInstaller { - private isTsdInstalled: boolean; - private packageNameToTypingLocation: Map = createMap(); private missingTypingsSet: Map = createMap(); private knownCachesSet: Map = createMap(); @@ -50,20 +43,6 @@ namespace ts.server.typingsInstaller { } init() { - this.isTsdInstalled = this.isPackageInstalled("tsd"); - if (this.log.isEnabled()) { - this.log.writeLine(`isTsdInstalled: ${this.isTsdInstalled}`); - } - - if (!this.isTsdInstalled) { - if (this.log.isEnabled()) { - this.log.writeLine(`tsd is not installed, installing tsd...`); - } - this.isTsdInstalled = this.installPackage("tsd"); - if (this.log.isEnabled()) { - this.log.writeLine(`isTsdInstalled: ${this.isTsdInstalled}`); - } - } this.processCacheLocation(this.globalCachePath); } @@ -94,13 +73,6 @@ namespace ts.server.typingsInstaller { } install(req: DiscoverTypings) { - if (!this.isTsdInstalled) { - if (this.log.isEnabled()) { - this.log.writeLine(`tsd is not installed, ignoring request...`); - } - return; - } - if (this.log.isEnabled()) { this.log.writeLine(`Got install request ${JSON.stringify(req)}`); } @@ -153,23 +125,26 @@ namespace ts.server.typingsInstaller { } return; } - const tsdJson = combinePaths(cacheLocation, "tsd.json"); + const packageJson = combinePaths(cacheLocation, "package.json"); if (this.log.isEnabled()) { - this.log.writeLine(`Trying to find '${tsdJson}'...`); + this.log.writeLine(`Trying to find '${packageJson}'...`); } - if (this.installTypingHost.fileExists(tsdJson)) { - const tsdConfig = JSON.parse(this.installTypingHost.readFile(tsdJson)); + if (this.installTypingHost.fileExists(packageJson)) { + const npmConfig = JSON.parse(this.installTypingHost.readFile(packageJson)); if (this.log.isEnabled()) { - this.log.writeLine(`Loaded content of '${tsdJson}': ${JSON.stringify(tsdConfig)}`); + this.log.writeLine(`Loaded content of '${npmConfig}': ${JSON.stringify(npmConfig)}`); } - if (tsdConfig.installed) { - for (const key in tsdConfig.installed) { - // key is / + if (npmConfig.devDependencies) { + for (const key in npmConfig.devDependencies) { + // key is @types/ const packageName = getPackageName(key); if (!packageName) { continue; } - const typingFile = tsdTypingToFileName(cacheLocation, key); + var typingFile = typingToFileName(cacheLocation, packageName, this.installTypingHost); + if (!typingFile) { + continue; + } const existingTypingFile = this.packageNameToTypingLocation[packageName]; if (existingTypingFile === typingFile) { continue; @@ -204,20 +179,19 @@ namespace ts.server.typingsInstaller { return; } - // TODO: install typings and send response when they are ready - const tsdPath = combinePaths(cachePath, "tsd.json"); + const npmConfigPath = combinePaths(cachePath, "package.json"); if (this.log.isEnabled()) { - this.log.writeLine(`Tsd config file: ${tsdPath}`); + this.log.writeLine(`Npm config file: ${npmConfigPath}`); } - if (!this.installTypingHost.fileExists(tsdPath)) { + if (!this.installTypingHost.fileExists(npmConfigPath)) { if (this.log.isEnabled()) { - this.log.writeLine(`Tsd config file '${tsdPath}' is missing, creating new one...`); + this.log.writeLine(`Npm config file: '${npmConfigPath}' is missing, creating new one...`); } this.ensureDirectoryExists(cachePath, this.installTypingHost); - this.installTypingHost.writeFile(tsdPath, DefaultTsdSettings); + this.installTypingHost.writeFile(npmConfigPath, "{}"); } - this.runTsd(cachePath, typingsToInstall, installedTypings => { + this.runInstall(cachePath, typingsToInstall, installedTypings => { // TODO: watch project directory if (this.log.isEnabled()) { this.log.writeLine(`Requested to install typings ${JSON.stringify(typingsToInstall)}, installed typings ${JSON.stringify(installedTypings)}`); @@ -230,7 +204,11 @@ namespace ts.server.typingsInstaller { continue; } installedPackages[packageName] = true; - installedTypingFiles.push(tsdTypingToFileName(cachePath, t)); + var typingFile = typingToFileName(cachePath, packageName, this.installTypingHost); + if (!typingFile) { + continue; + } + installedTypingFiles.push(typingFile); } if (this.log.isEnabled()) { this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`); @@ -287,14 +265,12 @@ namespace ts.server.typingsInstaller { typingOptions: request.typingOptions, compilerOptions: request.compilerOptions, typings, - files: request.fileNames, kind: "set" }; } protected abstract isPackageInstalled(packageName: string): boolean; - protected abstract installPackage(packageName: string): boolean; protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void; - protected abstract runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void; + protected abstract runInstall(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void; } } \ No newline at end of file