Swap out TSD for Types 2.0

This commit is contained in:
Jason Ramsay 2016-08-25 16:25:34 -07:00
parent 5e37a310d7
commit 1f9f20f043
6 changed files with 52 additions and 105 deletions

View File

@ -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":

View File

@ -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;

View File

@ -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";
}

View File

@ -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<string> {
" __typingsArrayBrand": any;
}
@ -102,18 +97,17 @@ namespace ts.server {
const entry = this.perProjectCache[project.getProjectName()];
const result: TypingsArray = entry ? entry.typings : <any>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
};
}

View File

@ -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);
});

View File

@ -1,16 +1,10 @@
/// <reference path="../../compiler/core.ts" />
/// <reference path="../../services/jsTyping.ts"/>
/// <reference path="../types.d.ts"/>
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<any>;
interface NpmConfig {
devDependencies: MapLike<any>;
}
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<string> = createMap<string>();
private missingTypingsSet: Map<true> = createMap<true>();
private knownCachesSet: Map<true> = createMap<true>();
@ -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 = <TsdConfig>JSON.parse(this.installTypingHost.readFile(tsdJson));
if (this.installTypingHost.fileExists(packageJson)) {
const npmConfig = <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 <package name>/<typing file>
if (npmConfig.devDependencies) {
for (const key in npmConfig.devDependencies) {
// key is @types/<package name>
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;
}
}