Restructure updating the configured project from disk and actual project update
Also reload the projects when extra extension in the host change
This commit is contained in:
parent
21ad26b6ff
commit
ae33ae894d
|
@ -770,7 +770,7 @@ namespace ts.projectSystem {
|
||||||
|
|
||||||
// remove the tsconfig file
|
// remove the tsconfig file
|
||||||
host.reloadFS(filesWithoutConfig);
|
host.reloadFS(filesWithoutConfig);
|
||||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Deleted);
|
||||||
|
|
||||||
checkNumberOfInferredProjects(projectService, 2);
|
checkNumberOfInferredProjects(projectService, 2);
|
||||||
checkNumberOfConfiguredProjects(projectService, 0);
|
checkNumberOfConfiguredProjects(projectService, 0);
|
||||||
|
@ -1837,16 +1837,18 @@ namespace ts.projectSystem {
|
||||||
|
|
||||||
// HTML file will not be included in any projects yet
|
// HTML file will not be included in any projects yet
|
||||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, config.path]);
|
const configuredProj = projectService.configuredProjects[0];
|
||||||
|
checkProjectActualFiles(configuredProj, [file1.path, config.path]);
|
||||||
|
|
||||||
// Specify .html extension as mixed content
|
// Specify .html extension as mixed content
|
||||||
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
|
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
|
||||||
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
|
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
|
||||||
session.executeCommand(configureHostRequest).response;
|
session.executeCommand(configureHostRequest).response;
|
||||||
|
|
||||||
// HTML file still not included in the project as it is closed
|
// The configured project should now be updated to include html file
|
||||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, config.path]);
|
assert.strictEqual(projectService.configuredProjects[0], configuredProj, "Same configured project should be updated");
|
||||||
|
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||||
|
|
||||||
// Open HTML file
|
// Open HTML file
|
||||||
projectService.applyChangesInOpenFiles(
|
projectService.applyChangesInOpenFiles(
|
||||||
|
@ -2859,7 +2861,7 @@ namespace ts.projectSystem {
|
||||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||||
moduleFile.path = moduleFileNewPath;
|
moduleFile.path = moduleFileNewPath;
|
||||||
host.reloadFS([moduleFile, file1]);
|
host.reloadFS([moduleFile, file1]);
|
||||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Deleted);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||||
host.runQueuedTimeoutCallbacks();
|
host.runQueuedTimeoutCallbacks();
|
||||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||||
|
@ -2911,7 +2913,8 @@ namespace ts.projectSystem {
|
||||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||||
moduleFile.path = moduleFileNewPath;
|
moduleFile.path = moduleFileNewPath;
|
||||||
host.reloadFS([moduleFile, file1, configFile]);
|
host.reloadFS([moduleFile, file1, configFile]);
|
||||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Deleted);
|
||||||
|
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Created);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||||
host.runQueuedTimeoutCallbacks();
|
host.runQueuedTimeoutCallbacks();
|
||||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||||
|
@ -2919,7 +2922,8 @@ namespace ts.projectSystem {
|
||||||
|
|
||||||
moduleFile.path = moduleFileOldPath;
|
moduleFile.path = moduleFileOldPath;
|
||||||
host.reloadFS([moduleFile, file1, configFile]);
|
host.reloadFS([moduleFile, file1, configFile]);
|
||||||
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Changed);
|
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Deleted);
|
||||||
|
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Created);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||||
host.runQueuedTimeoutCallbacks();
|
host.runQueuedTimeoutCallbacks();
|
||||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||||
|
|
|
@ -538,21 +538,21 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSourceFileChanged(fileName: NormalizedPath) {
|
private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) {
|
||||||
const info = this.getScriptInfoForNormalizedPath(fileName);
|
const info = this.getScriptInfoForNormalizedPath(fileName);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
this.logger.info(`Error: got watch notification for unknown file: ${fileName}`);
|
this.logger.info(`Error: got watch notification for unknown file: ${fileName}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.host.fileExists(fileName)) {
|
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||||
// File was deleted
|
// File was deleted
|
||||||
this.handleDeletedFile(info);
|
this.handleDeletedFile(info);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (info && (!info.isScriptOpen())) {
|
if (!info.isScriptOpen()) {
|
||||||
if (info.containingProjects.length === 0) {
|
if (info.containingProjects.length === 0) {
|
||||||
// Orphan script info, remove it as we can always reload it on next open
|
// Orphan script info, remove it as we can always reload it on next open file request
|
||||||
info.stopWatcher();
|
info.stopWatcher();
|
||||||
this.filenameToScriptInfo.delete(info.path);
|
this.filenameToScriptInfo.delete(info.path);
|
||||||
}
|
}
|
||||||
|
@ -605,7 +605,7 @@ namespace ts.server {
|
||||||
this.logger.info(`Type root file ${fileName} changed`);
|
this.logger.info(`Type root file ${fileName} changed`);
|
||||||
this.throttledOperations.schedule(project.getConfigFilePath() + " * type root", /*delay*/ 250, () => {
|
this.throttledOperations.schedule(project.getConfigFilePath() + " * type root", /*delay*/ 250, () => {
|
||||||
project.updateTypes();
|
project.updateTypes();
|
||||||
this.updateConfiguredProject(project); // TODO: Figure out why this is needed (should be redundant?)
|
this.reloadConfiguredProject(project); // TODO: Figure out why this is needed (should be redundant?)
|
||||||
this.refreshInferredProjects();
|
this.refreshInferredProjects();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -644,7 +644,7 @@ namespace ts.server {
|
||||||
// just update the current project.
|
// just update the current project.
|
||||||
|
|
||||||
this.logger.info("Updating configured project");
|
this.logger.info("Updating configured project");
|
||||||
this.updateConfiguredProject(project);
|
this.updateConfiguredProject(project, projectOptions, configFileErrors);
|
||||||
|
|
||||||
// Call refreshInferredProjects to clean up inferred projects we may have
|
// Call refreshInferredProjects to clean up inferred projects we may have
|
||||||
// created for the new files
|
// created for the new files
|
||||||
|
@ -652,11 +652,19 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConfigChangedForConfiguredProject(project: ConfiguredProject) {
|
private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) {
|
||||||
const configFileName = project.getConfigFilePath();
|
const configFileName = project.getConfigFilePath();
|
||||||
this.logger.info(`Config file changed: ${configFileName}`);
|
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||||
const configFileErrors = this.updateConfiguredProject(project);
|
this.logger.info(`Config file deleted: ${configFileName}`);
|
||||||
this.reportConfigFileDiagnostics(configFileName, configFileErrors, /*triggerFile*/ configFileName);
|
// TODO: (sheetalkamat) Get the list of open files from this project
|
||||||
|
// and update the projects
|
||||||
|
this.removeProject(project);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.logger.info(`Config file changed: ${configFileName}`);
|
||||||
|
this.reloadConfiguredProject(project);
|
||||||
|
this.reportConfigFileDiagnostics(configFileName, project.getGlobalProjectErrors(), /*triggerFile*/ configFileName);
|
||||||
|
}
|
||||||
this.refreshInferredProjects();
|
this.refreshInferredProjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,6 +678,8 @@ namespace ts.server {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: (sheetalkamat) Technically we shouldnt have to do this:
|
||||||
|
// We can report these errors in reload projects or after reload project?
|
||||||
const { configFileErrors } = this.convertConfigFileContentToProjectOptions(fileName);
|
const { configFileErrors } = this.convertConfigFileContentToProjectOptions(fileName);
|
||||||
this.reportConfigFileDiagnostics(fileName, configFileErrors, fileName);
|
this.reportConfigFileDiagnostics(fileName, configFileErrors, fileName);
|
||||||
|
|
||||||
|
@ -883,7 +893,7 @@ namespace ts.server {
|
||||||
this.openConfigFile(configFileName, fileName);
|
this.openConfigFile(configFileName, fileName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.updateConfiguredProject(project);
|
this.reloadConfiguredProject(project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1133,7 +1143,7 @@ namespace ts.server {
|
||||||
|
|
||||||
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
|
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
|
||||||
|
|
||||||
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
|
project.watchConfigFile((project, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind));
|
||||||
if (!sizeLimitExceeded) {
|
if (!sizeLimitExceeded) {
|
||||||
this.watchConfigDirectoryForProject(project, projectOptions);
|
this.watchConfigDirectoryForProject(project, projectOptions);
|
||||||
}
|
}
|
||||||
|
@ -1161,6 +1171,9 @@ namespace ts.server {
|
||||||
project.addRoot(info);
|
project.addRoot(info);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// TODO: (sheetalkamat) because the files are not added as a root, we wont have these available in
|
||||||
|
// missing files unless someone recreates the project or it was also refrenced in existing sourcefile
|
||||||
|
// Also these errors wouldnt show correct errors
|
||||||
(configFileErrors || (configFileErrors = [])).push(createFileNotFoundDiagnostic(rootFilename));
|
(configFileErrors || (configFileErrors = [])).push(createFileNotFoundDiagnostic(rootFilename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1183,12 +1196,11 @@ namespace ts.server {
|
||||||
const newRootScriptInfos: ScriptInfo[] = [];
|
const newRootScriptInfos: ScriptInfo[] = [];
|
||||||
const newRootScriptInfoMap: NormalizedPathMap<ScriptInfo> = createNormalizedPathMap<ScriptInfo>();
|
const newRootScriptInfoMap: NormalizedPathMap<ScriptInfo> = createNormalizedPathMap<ScriptInfo>();
|
||||||
|
|
||||||
let projectErrors: Diagnostic[];
|
|
||||||
let rootFilesChanged = false;
|
let rootFilesChanged = false;
|
||||||
for (const f of newUncheckedFiles) {
|
for (const f of newUncheckedFiles) {
|
||||||
const newRootFile = propertyReader.getFileName(f);
|
const newRootFile = propertyReader.getFileName(f);
|
||||||
if (!this.host.fileExists(newRootFile)) {
|
if (!this.host.fileExists(newRootFile)) {
|
||||||
(projectErrors || (projectErrors = [])).push(createFileNotFoundDiagnostic(newRootFile));
|
(configFileErrors || (configFileErrors = [])).push(createFileNotFoundDiagnostic(newRootFile));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const normalizedPath = toNormalizedPath(newRootFile);
|
const normalizedPath = toNormalizedPath(newRootFile);
|
||||||
|
@ -1247,17 +1259,20 @@ namespace ts.server {
|
||||||
if (compileOnSave !== undefined) {
|
if (compileOnSave !== undefined) {
|
||||||
project.compileOnSaveEnabled = compileOnSave;
|
project.compileOnSaveEnabled = compileOnSave;
|
||||||
}
|
}
|
||||||
project.setProjectErrors(concatenate(configFileErrors, projectErrors));
|
project.setProjectErrors(configFileErrors);
|
||||||
|
|
||||||
project.updateGraph();
|
project.updateGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateConfiguredProject(project: ConfiguredProject) {
|
/**
|
||||||
if (!this.host.fileExists(project.getConfigFilePath())) {
|
* Read the config file of the project again and update the project
|
||||||
this.logger.info("Config file deleted");
|
* @param project
|
||||||
this.removeProject(project);
|
*/
|
||||||
return;
|
private reloadConfiguredProject(project: ConfiguredProject) {
|
||||||
}
|
// At this point, there is no reason to not have configFile in the host
|
||||||
|
|
||||||
|
// TODO: (sheetalkamat) configErrors should always be in project and there shouldnt be
|
||||||
|
// any need to return these errors
|
||||||
|
|
||||||
// note: the returned "success" is true does not mean the "configFileErrors" is empty.
|
// note: the returned "success" is true does not mean the "configFileErrors" is empty.
|
||||||
// because we might have tolerated the errors and kept going. So always return the configFileErrors
|
// because we might have tolerated the errors and kept going. So always return the configFileErrors
|
||||||
|
@ -1266,9 +1281,16 @@ namespace ts.server {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// reset project settings to default
|
// reset project settings to default
|
||||||
this.updateNonInferredProject(project, [], fileNamePropertyReader, {}, {}, /*compileOnSave*/ false, configFileErrors);
|
this.updateNonInferredProject(project, [], fileNamePropertyReader, {}, {}, /*compileOnSave*/ false, configFileErrors);
|
||||||
return configFileErrors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.updateConfiguredProject(project, projectOptions, configFileErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the configured project with updated config file contents
|
||||||
|
* @param project
|
||||||
|
*/
|
||||||
|
private updateConfiguredProject(project: ConfiguredProject, projectOptions: ProjectOptions, configFileErrors: Diagnostic[]) {
|
||||||
if (this.exceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) {
|
if (this.exceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) {
|
||||||
project.setCompilerOptions(projectOptions.compilerOptions);
|
project.setCompilerOptions(projectOptions.compilerOptions);
|
||||||
if (!project.languageServiceEnabled) {
|
if (!project.languageServiceEnabled) {
|
||||||
|
@ -1277,13 +1299,13 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
project.disableLanguageService();
|
project.disableLanguageService();
|
||||||
project.stopWatchingDirectory();
|
project.stopWatchingDirectory();
|
||||||
|
project.setProjectErrors(configFileErrors);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
project.enableLanguageService();
|
project.enableLanguageService();
|
||||||
this.watchConfigDirectoryForProject(project, projectOptions);
|
this.watchConfigDirectoryForProject(project, projectOptions);
|
||||||
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors);
|
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors);
|
||||||
}
|
}
|
||||||
return configFileErrors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
|
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
|
||||||
|
@ -1324,7 +1346,7 @@ namespace ts.server {
|
||||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||||
if (!info.hasMixedContent) {
|
if (!info.hasMixedContent) {
|
||||||
const { fileName } = info;
|
const { fileName } = info;
|
||||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
info.setWatcher(this.host.watchFile(fileName, (_fileName, eventKind) => this.onSourceFileChanged(fileName, eventKind)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1390,8 +1412,9 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
if (args.extraFileExtensions) {
|
if (args.extraFileExtensions) {
|
||||||
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
|
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
|
||||||
// TODO: (sheetalkamat) We need to update the projects because of we might interprete more/less files
|
// We need to update the projects because of we might interprete more/less files
|
||||||
// depending on whether extra files extenstions are either added or removed
|
// depending on whether extra files extenstions are either added or removed
|
||||||
|
this.reloadProjects();
|
||||||
this.logger.info("Host file extension mappings updated");
|
this.logger.info("Host file extension mappings updated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1408,6 +1431,8 @@ namespace ts.server {
|
||||||
this.logger.info("reload projects.");
|
this.logger.info("reload projects.");
|
||||||
// try to reload config file for all open files
|
// try to reload config file for all open files
|
||||||
for (const info of this.openFiles) {
|
for (const info of this.openFiles) {
|
||||||
|
// TODO: (sheetalkamat) batch these = multiple open files from the same project will
|
||||||
|
// end up updating project that many times
|
||||||
this.openOrUpdateConfiguredProjectForFile(info.fileName);
|
this.openOrUpdateConfiguredProjectForFile(info.fileName);
|
||||||
}
|
}
|
||||||
this.refreshInferredProjects();
|
this.refreshInferredProjects();
|
||||||
|
|
|
@ -1072,8 +1072,8 @@ namespace ts.server {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
watchConfigFile(callback: (project: ConfiguredProject) => void) {
|
watchConfigFile(callback: (project: ConfiguredProject, eventKind: FileWatcherEventKind) => void) {
|
||||||
this.projectFileWatcher = this.projectService.host.watchFile(this.getConfigFilePath(), _ => callback(this));
|
this.projectFileWatcher = this.projectService.host.watchFile(this.getConfigFilePath(), (_fileName, eventKind) => callback(this, eventKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void) {
|
watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void) {
|
||||||
|
|
Loading…
Reference in a new issue