move global cache location to node typings installer

This commit is contained in:
Vladimir Matveev 2016-08-15 11:48:28 -07:00
parent f603f024ba
commit 672813afb6
6 changed files with 120 additions and 64 deletions

View file

@ -29,15 +29,12 @@ namespace ts {
abstract class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
protected projectService: server.ProjectService;
constructor(private readonly host: server.ServerHost) {
super();
constructor(readonly cachePath: string, readonly installTypingHost: server.ServerHost) {
super(cachePath, <Path>"");
this.init();
}
abstract cachePath: string;
safeFileList = <Path>"";
packageNameToTypingLocation: Map<string> = createMap<string>();
postInstallActions: (( map: (t: string[]) => string[]) => void)[] = [];
runPostInstallActions(map: (t: string[]) => string[]) {
@ -52,7 +49,7 @@ namespace ts {
}
getInstallTypingHost() {
return this.host;
return this.installTypingHost;
}
installPackage(packageName: string) {
@ -74,7 +71,7 @@ namespace ts {
}
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
const request = server.createInstallTypingsRequest(project, typingOptions, this.safeFileList, this.packageNameToTypingLocation, this.cachePath);
const request = server.createInstallTypingsRequest(project, typingOptions, this.cachePath);
this.install(request);
}
}
@ -1519,9 +1516,8 @@ namespace ts {
const host = createServerHost([file1, tsconfig, packageJson]);
class TypingInstaller extends TestTypingsInstaller {
cachePath = "/a/data/";
constructor(host: server.ServerHost) {
super(host);
super("/a/data/", host);
}
};
const installer = new TypingInstaller(host);

View file

@ -18,10 +18,6 @@ namespace ts.server {
fork(modulePath: string): NodeChildProcess;
} = require("child_process");
const os: {
homedir(): string
} = require("os");
interface ReadLineOptions {
input: NodeJS.ReadableStream;
output?: NodeJS.WritableStream;
@ -167,24 +163,8 @@ namespace ts.server {
class NodeTypingsInstaller implements ITypingsInstaller {
private installer: NodeChildProcess;
private projectService: ProjectService;
private cachePath: string;
constructor(private readonly logger: server.Logger) {
let basePath: string;
switch (process.platform) {
case "win32":
basePath = process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir();
break;
case "linux":
basePath = os.homedir();
break;
case "darwin":
basePath = combinePaths(os.homedir(), "Library/Application Support/")
break;
}
if (basePath) {
this.cachePath = combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
}
}
attach(projectService: ProjectService) {
@ -198,13 +178,7 @@ namespace ts.server {
}
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void {
const request = createInstallTypingsRequest(
project,
typingOptions,
/*safeListPath*/ <Path>(combinePaths(process.cwd(), "typingSafeList.json")), // TODO: fixme
/*packageNameToTypingLocation*/ createMap<string>(), // TODO: fixme
this.cachePath
);
const request = createInstallTypingsRequest(project, typingOptions);
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
}

View file

@ -7,11 +7,9 @@ declare namespace ts.server {
readonly projectName: string;
readonly fileNames: string[];
readonly projectRootPath: ts.Path;
readonly safeListPath: ts.Path;
readonly packageNameToTypingLocation: ts.Map<string>;
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly cachePath: string;
readonly cachePath?: string;
}
export interface CompressedData {

View file

@ -2,13 +2,39 @@
/// <reference types="node" />
namespace ts.server.typingsInstaller {
const os: {
homedir(): string
} = require("os");
function getGlobalCacheLocation() {
let basePath: string;
switch (process.platform) {
case "win32":
basePath = process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir();
break;
case "linux":
basePath = os.homedir();
break;
case "darwin":
basePath = combinePaths(os.homedir(), "Library/Application Support/")
break;
}
Debug.assert(basePath !== undefined);
return combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
}
export class NodeTypingsInstaller extends TypingsInstaller {
private execSync: { (command: string, options: { stdio: "ignore" }): any };
private exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
readonly installTypingHost: InstallTypingHost = sys;
constructor() {
super();
this.execSync = require("child_process").execSync;
this.exec = require("child_process").exec;
super(getGlobalCacheLocation(), toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)));
const { exec, execSync } = require("child_process");
this.execSync = execSync;
this.exec = exec;
}
init() {
@ -38,10 +64,6 @@ namespace ts.server.typingsInstaller {
}
}
protected getInstallTypingHost() {
return sys;
}
protected sendResponse(response: InstallTypingsResponse) {
process.send(response);
}

View file

@ -9,16 +9,38 @@ namespace ts.server.typingsInstaller {
path: "typings"
}, /*replacer*/undefined, /*space*/4);
interface TsdConfig {
installed: MapLike<any>;
}
function tsdTypingToFileName(cachePath: string, tsdTypingFile: string) {
return combinePaths(cachePath, `typings/${tsdTypingFile}`);
}
function getPackageName(tsdTypingFile: string) {
const idx = tsdTypingFile.indexOf("/");
return idx > 0 ? tsdTypingFile.substr(0, idx) : undefined;
}
export abstract class TypingsInstaller {
private isTsdInstalled: boolean;
private missingTypings: Map<string> = createMap<string>();
private packageNameToTypingLocation: Map<string> = createMap<string>();
private missingTypingsSet: Map<true> = createMap<true>();
private knownCachesSet: Map<true> = createMap<true>();
abstract readonly installTypingHost: InstallTypingHost;
constructor(readonly globalCachePath: string, readonly safeListPath: Path) {
}
init() {
this.isTsdInstalled = this.isPackageInstalled("tsd");
if (!this.isTsdInstalled) {
this.isTsdInstalled = this.installPackage("tsd");
}
this.processCacheLocation(this.globalCachePath);
}
install(req: InstallTypingsRequest) {
@ -26,43 +48,91 @@ namespace ts.server.typingsInstaller {
return;
}
// load existing typing information from the cache
if (req.cachePath) {
this.processCacheLocation(req.cachePath);
}
const discoverTypingsResult = JsTyping.discoverTypings(
this.getInstallTypingHost(),
this.installTypingHost,
req.fileNames,
req.projectRootPath,
req.safeListPath,
req.packageNameToTypingLocation,
this.safeListPath,
this.packageNameToTypingLocation,
req.typingOptions,
req.compilerOptions);
// respond with whatever cached typings we have now
this.sendResponse(this.createResponse(req, discoverTypingsResult.cachedTypingPaths));
// start watching files
this.watchFiles(discoverTypingsResult.filesToWatch);
// install typings and
this.installTypings(req, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
}
private processCacheLocation(cacheLocation: string) {
if (this.knownCachesSet[cacheLocation]) {
return;
}
const tsdJson = combinePaths(cacheLocation, "tsd.json");
if (this.installTypingHost.fileExists(tsdJson)) {
const tsdConfig = <TsdConfig>JSON.parse(this.installTypingHost.readFile(tsdJson));
if (tsdConfig.installed) {
for (const key in tsdConfig.installed) {
// key is <package name>/<typing file>
const packageName = getPackageName(key);
if (!packageName) {
continue;
}
const typingFile = tsdTypingToFileName(cacheLocation, key);
const existingTypingFile = this.packageNameToTypingLocation[packageName];
if (existingTypingFile === typingFile) {
continue;
}
if (existingTypingFile) {
// TODO: log warning
}
this.packageNameToTypingLocation[packageName] = typingFile;
}
}
}
this.knownCachesSet[cacheLocation] = true;
}
private installTypings(req: InstallTypingsRequest, currentlyCachedTypings: string[], typingsToInstall: string[]) {
typingsToInstall = filter(typingsToInstall, x => !hasProperty(this.missingTypings, x));
typingsToInstall = filter(typingsToInstall, x => !this.missingTypingsSet[x]);
if (typingsToInstall.length === 0) {
return;
}
// TODO: install typings and send response when they are ready
const host = this.getInstallTypingHost();
const tsdPath = combinePaths(req.cachePath, "tsd.json");
if (!host.fileExists(tsdPath)) {
this.ensureDirectoryExists(req.cachePath, host);
host.writeFile(tsdPath, DefaultTsdSettings);
if (!this.installTypingHost.fileExists(tsdPath)) {
this.ensureDirectoryExists(req.cachePath, this.installTypingHost);
this.installTypingHost.writeFile(tsdPath, DefaultTsdSettings);
}
this.runTsd(req.cachePath, typingsToInstall, installedTypings => {
// TODO: record new missing package names
// TODO: watch project directory
const typingDirectory = combinePaths(req.cachePath, "typings");
installedTypings = installedTypings.map(x => combinePaths(typingDirectory, x));
this.sendResponse(this.createResponse(req, currentlyCachedTypings.concat(installedTypings)));
const installedPackages: Map<true> = createMap<true>();
const installedTypingFiles: string[] = [];
for (const t of installedTypings) {
const packageName = getPackageName(t);
if (!packageName) {
continue;
}
installedPackages[packageName] = true;
installedTypingFiles.push(tsdTypingToFileName(req.cachePath, t));
}
for (const toInstall of typingsToInstall) {
if (!installedPackages[toInstall]) {
this.missingTypingsSet[toInstall] = true;
}
}
this.sendResponse(this.createResponse(req, currentlyCachedTypings.concat(installedTypingFiles)));
});
}
@ -91,7 +161,6 @@ namespace ts.server.typingsInstaller {
protected abstract isPackageInstalled(packageName: string): boolean;
protected abstract installPackage(packageName: string): boolean;
protected abstract getInstallTypingHost(): InstallTypingHost;
protected abstract sendResponse(response: InstallTypingsResponse): void;
protected abstract runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void;
}

View file

@ -1,4 +1,3 @@
/// <reference path="..\services\services.ts" />
/// <reference path="types.d.ts" />
namespace ts.server {
@ -30,15 +29,13 @@ namespace ts.server {
export type Types = Err | Info | Perf;
}
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, safeListPath: Path, packageNameToTypingLocation: Map<string>, cachePath: string): InstallTypingsRequest {
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): InstallTypingsRequest {
return {
projectName: project.getProjectName(),
fileNames: project.getFileNames(),
compilerOptions: project.getCompilerOptions(),
typingOptions,
projectRootPath: <Path>(project.projectKind === ProjectKind.Inferred ? "" : getDirectoryPath(project.getProjectName())), // TODO: fixme
safeListPath,
packageNameToTypingLocation,
cachePath
};
}