mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 10:41:56 -05:00
[WIP] file watching
This commit is contained in:
@@ -27,7 +27,7 @@ namespace ts {
|
||||
content: libFileContent
|
||||
};
|
||||
|
||||
abstract class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
|
||||
class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
|
||||
protected projectService: server.ProjectService;
|
||||
constructor(readonly cachePath: string, readonly installTypingHost: server.ServerHost) {
|
||||
super(cachePath, <Path>"");
|
||||
@@ -44,6 +44,10 @@ namespace ts {
|
||||
this.postInstallActions = [];
|
||||
}
|
||||
|
||||
onProjectClosed(p: server.Project) {
|
||||
|
||||
}
|
||||
|
||||
attach(projectService: server.ProjectService) {
|
||||
this.projectService = projectService;
|
||||
}
|
||||
@@ -66,7 +70,7 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
sendResponse(response: server.InstallTypingsResponse) {
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings) {
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
}
|
||||
|
||||
@@ -1515,12 +1519,7 @@ namespace ts {
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, tsconfig, packageJson]);
|
||||
class TypingInstaller extends TestTypingsInstaller {
|
||||
constructor(host: server.ServerHost) {
|
||||
super("/a/data/", host);
|
||||
}
|
||||
};
|
||||
const installer = new TypingInstaller(host);
|
||||
const installer = new TestTypingsInstaller("/a/data/", host);
|
||||
const projectService = new server.ProjectService(host, nullLogger, nullCancellationToken, /*useSingleInferredProject*/ true, installer);
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
|
||||
@@ -206,14 +206,21 @@ namespace ts.server {
|
||||
this.ensureInferredProjectsUpToDate();
|
||||
}
|
||||
|
||||
updateTypingsForProject(response: InstallTypingsResponse): void {
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
|
||||
const project = this.findProject(response.projectName);
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings);
|
||||
project.setTypings(response.typings);
|
||||
project.updateGraph();
|
||||
switch (response.kind) {
|
||||
case "set":
|
||||
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings);
|
||||
project.setTypings(response.typings);
|
||||
project.updateGraph();
|
||||
break;
|
||||
case "invalidate":
|
||||
this.typingsCache.invalidateCachedTypingsForProject(project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setCompilerOptionsForInferredProjects(compilerOptions: CompilerOptions): void {
|
||||
|
||||
@@ -177,6 +177,10 @@ namespace ts.server {
|
||||
this.installer.on("message", m => this.handleMessage(m));
|
||||
}
|
||||
|
||||
onProjectClosed(p: Project): void {
|
||||
this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" });
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void {
|
||||
const request = createInstallTypingsRequest(project, typingOptions);
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
@@ -185,7 +189,7 @@ namespace ts.server {
|
||||
this.installer.send(request);
|
||||
}
|
||||
|
||||
private handleMessage(response: InstallTypingsResponse) {
|
||||
private handleMessage(response: SetTypings | InvalidateCachedTypings) {
|
||||
if (this.logger.hasLevel(LogLevel.verbose)) {
|
||||
this.logger.info(`Received response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
|
||||
39
src/server/types.d.ts
vendored
39
src/server/types.d.ts
vendored
@@ -3,15 +3,6 @@
|
||||
/// <reference path="../services/jsTyping.ts"/>
|
||||
|
||||
declare namespace ts.server {
|
||||
export interface InstallTypingsRequest {
|
||||
readonly projectName: string;
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: ts.Path;
|
||||
readonly typingOptions: ts.TypingOptions;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly cachePath?: string;
|
||||
}
|
||||
|
||||
export interface CompressedData {
|
||||
length: number;
|
||||
compressionKind: string;
|
||||
@@ -27,15 +18,43 @@ declare namespace ts.server {
|
||||
gc?(): void;
|
||||
}
|
||||
|
||||
export interface InstallTypingsResponse {
|
||||
export interface TypingInstallerRequest {
|
||||
readonly projectName: string;
|
||||
readonly kind: "discover" | "closeProject";
|
||||
}
|
||||
|
||||
export interface DiscoverTypings extends TypingInstallerRequest {
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: ts.Path;
|
||||
readonly typingOptions: ts.TypingOptions;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly cachePath?: string;
|
||||
readonly kind: "discover";
|
||||
}
|
||||
|
||||
export interface CloseProject extends TypingInstallerRequest {
|
||||
readonly kind: "closeProject";
|
||||
}
|
||||
|
||||
export interface TypingInstallerResponse {
|
||||
readonly projectName: string;
|
||||
readonly kind: "set" | "invalidate";
|
||||
}
|
||||
|
||||
export interface SetTypings extends TypingInstallerResponse {
|
||||
readonly typingOptions: ts.TypingOptions;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typings: string[];
|
||||
readonly kind: "set";
|
||||
}
|
||||
|
||||
export interface InvalidateCachedTypings extends TypingInstallerResponse {
|
||||
readonly kind: "invalidate";
|
||||
}
|
||||
|
||||
export interface InstallTypingHost extends JsTyping.TypingResolutionHost {
|
||||
writeFile(path: string, content: string): void;
|
||||
createDirectory(path: string): void;
|
||||
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@ namespace ts.server {
|
||||
export interface ITypingsInstaller {
|
||||
enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions): void;
|
||||
attach(projectService: ProjectService): void;
|
||||
onProjectClosed(p: Project): void;
|
||||
}
|
||||
|
||||
export const nullTypingsInstaller: ITypingsInstaller = {
|
||||
enqueueInstallTypingsRequest: () => {},
|
||||
attach: (projectService: ProjectService) => {}
|
||||
attach: (projectService: ProjectService) => {},
|
||||
onProjectClosed: (p: Project) => {}
|
||||
};
|
||||
|
||||
class TypingsCacheEntry {
|
||||
@@ -95,6 +97,14 @@ namespace ts.server {
|
||||
return entry ? entry.typings : emptyArray;
|
||||
}
|
||||
|
||||
invalidateCachedTypingsForProject(project: Project) {
|
||||
const typingOptions = getTypingOptionsForProjects(project);
|
||||
if (!typingOptions.enableAutoDiscovery) {
|
||||
return;
|
||||
}
|
||||
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
|
||||
}
|
||||
|
||||
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) {
|
||||
this.perProjectCache[projectName] = {
|
||||
compilerOptions,
|
||||
@@ -103,8 +113,9 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
deleteTypingsForProject(project: Project) {
|
||||
onProjectClosed(project: Project) {
|
||||
delete this.perProjectCache[project.getProjectName()];
|
||||
this.installer.onProjectClosed(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,8 +71,14 @@ namespace ts.server.typingsInstaller {
|
||||
this.log.writeLine(`Error when getting npm bin path: ${e}. Set bin path to ""`);
|
||||
}
|
||||
}
|
||||
process.on("message", (req: InstallTypingsRequest) => {
|
||||
this.install(req);
|
||||
process.on("message", (req: DiscoverTypings | CloseProject) => {
|
||||
switch (req.kind) {
|
||||
case "discover":
|
||||
this.install(req);
|
||||
break;
|
||||
case "closeProject":
|
||||
this.closeProject(req);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,7 +102,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
protected sendResponse(response: InstallTypingsResponse) {
|
||||
protected sendResponse(response: SetTypings | InvalidateCachedTypings) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace ts.server.typingsInstaller {
|
||||
private missingTypingsSet: Map<true> = createMap<true>();
|
||||
private knownCachesSet: Map<true> = createMap<true>();
|
||||
|
||||
private projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
|
||||
|
||||
abstract readonly installTypingHost: InstallTypingHost;
|
||||
|
||||
constructor(readonly globalCachePath: string, readonly safeListPath: Path, protected readonly log = nullLog) {
|
||||
@@ -66,7 +68,36 @@ namespace ts.server.typingsInstaller {
|
||||
this.processCacheLocation(this.globalCachePath);
|
||||
}
|
||||
|
||||
install(req: InstallTypingsRequest) {
|
||||
closeProject(req: CloseProject) {
|
||||
this.closeWatchers(req.projectName);
|
||||
}
|
||||
|
||||
private closeWatchers(projectName: string): boolean {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
|
||||
}
|
||||
const watchers = this.projectWatchers[projectName];
|
||||
if (!watchers) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
for (const w of watchers) {
|
||||
w.close();
|
||||
}
|
||||
|
||||
delete this.projectWatchers[projectName]
|
||||
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
install(req: DiscoverTypings) {
|
||||
if (!this.isTsdInstalled) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`tsd is not installed, ignoring request...`);
|
||||
@@ -100,10 +131,10 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
// respond with whatever cached typings we have now
|
||||
this.sendResponse(this.createResponse(req, discoverTypingsResult.cachedTypingPaths));
|
||||
this.sendResponse(this.createSetTypings(req, discoverTypingsResult.cachedTypingPaths));
|
||||
|
||||
// start watching files
|
||||
this.watchFiles(discoverTypingsResult.filesToWatch);
|
||||
this.watchFiles(req.projectRootPath, discoverTypingsResult.filesToWatch);
|
||||
|
||||
// install typings
|
||||
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
|
||||
@@ -158,7 +189,7 @@ namespace ts.server.typingsInstaller {
|
||||
this.knownCachesSet[cacheLocation] = true;
|
||||
}
|
||||
|
||||
private installTypings(req: InstallTypingsRequest, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
|
||||
private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`);
|
||||
}
|
||||
@@ -210,7 +241,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
this.sendResponse(this.createResponse(req, currentlyCachedTypings.concat(installedTypingFiles)));
|
||||
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -224,22 +255,38 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
private watchFiles(files: string[]) {
|
||||
// TODO: start watching files
|
||||
private watchFiles(projectRootPath: string, files: string[]) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
const watchers: FileWatcher[] = [];
|
||||
for (const file of files) {
|
||||
const w = this.installTypingHost.watchFile(file, f => {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`FS notification for '${f}', sending 'clean' message for project '${projectRootPath}'`);
|
||||
}
|
||||
if (!this.closeWatchers(projectRootPath)) {
|
||||
return;
|
||||
}
|
||||
this.sendResponse(<InvalidateCachedTypings>{ projectName: projectRootPath, kind: "invalidate" })
|
||||
});
|
||||
watchers.push(w);
|
||||
}
|
||||
}
|
||||
|
||||
private createResponse(request: InstallTypingsRequest, typings: string[]) {
|
||||
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {
|
||||
return {
|
||||
projectName: request.projectName,
|
||||
typingOptions: request.typingOptions,
|
||||
compilerOptions: request.compilerOptions,
|
||||
typings
|
||||
typings,
|
||||
kind: "set"
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract isPackageInstalled(packageName: string): boolean;
|
||||
protected abstract installPackage(packageName: string): boolean;
|
||||
protected abstract sendResponse(response: InstallTypingsResponse): void;
|
||||
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void;
|
||||
protected abstract runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void;
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,15 @@ namespace ts.server {
|
||||
export type Types = Err | Info | Perf;
|
||||
}
|
||||
|
||||
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): InstallTypingsRequest {
|
||||
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): DiscoverTypings {
|
||||
return {
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(),
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typingOptions,
|
||||
projectRootPath: <Path>(project.projectKind === ProjectKind.Inferred ? "" : getDirectoryPath(project.getProjectName())), // TODO: fixme
|
||||
cachePath
|
||||
cachePath,
|
||||
kind: "discover"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user