Merge pull request #26716 from Microsoft/lazyConfiguredProjectsFromExternalProject
Add option --lazyConfiguredProjectsFromExternalProject to enable lazy load of configured projects referenced by external project
This commit is contained in:
commit
199d496ef1
|
@ -218,9 +218,15 @@ namespace ts.server {
|
|||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function convertUserPreferences(preferences: protocol.UserPreferences): UserPreferences {
|
||||
const { lazyConfiguredProjectsFromExternalProject, ...userPreferences } = preferences;
|
||||
return userPreferences;
|
||||
}
|
||||
|
||||
export interface HostConfiguration {
|
||||
formatCodeOptions: FormatCodeSettings;
|
||||
preferences: UserPreferences;
|
||||
preferences: protocol.UserPreferences;
|
||||
hostInfo: string;
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
@ -802,7 +808,7 @@ namespace ts.server {
|
|||
return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions;
|
||||
}
|
||||
|
||||
getPreferences(file: NormalizedPath): UserPreferences {
|
||||
getPreferences(file: NormalizedPath): protocol.UserPreferences {
|
||||
const info = this.getScriptInfoForNormalizedPath(file);
|
||||
return info && info.getPreferences() || this.hostConfiguration.preferences;
|
||||
}
|
||||
|
@ -811,7 +817,7 @@ namespace ts.server {
|
|||
return this.hostConfiguration.formatCodeOptions;
|
||||
}
|
||||
|
||||
getHostPreferences(): UserPreferences {
|
||||
getHostPreferences(): protocol.UserPreferences {
|
||||
return this.hostConfiguration.preferences;
|
||||
}
|
||||
|
||||
|
@ -1561,6 +1567,13 @@ namespace ts.server {
|
|||
return project;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
private createLoadAndUpdateConfiguredProject(configFileName: NormalizedPath) {
|
||||
const project = this.createAndLoadConfiguredProject(configFileName);
|
||||
project.updateGraph();
|
||||
return project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the config file of the project, and update the project root file names.
|
||||
*/
|
||||
|
@ -1979,7 +1992,19 @@ namespace ts.server {
|
|||
this.logger.info("Format host information updated");
|
||||
}
|
||||
if (args.preferences) {
|
||||
const { lazyConfiguredProjectsFromExternalProject } = this.hostConfiguration.preferences;
|
||||
this.hostConfiguration.preferences = { ...this.hostConfiguration.preferences, ...args.preferences };
|
||||
if (lazyConfiguredProjectsFromExternalProject && !this.hostConfiguration.preferences.lazyConfiguredProjectsFromExternalProject) {
|
||||
// Load configured projects for external projects that are pending reload
|
||||
this.configuredProjects.forEach(project => {
|
||||
if (project.hasExternalProjectRef() &&
|
||||
project.pendingReload === ConfigFileProgramReloadLevel.Full &&
|
||||
!this.pendingProjectUpdates.has(project.getProjectName())) {
|
||||
this.loadConfiguredProject(project);
|
||||
project.updateGraph();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (args.extraFileExtensions) {
|
||||
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
|
||||
|
@ -2192,8 +2217,7 @@ namespace ts.server {
|
|||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
project = this.createAndLoadConfiguredProject(configFileName);
|
||||
project.updateGraph();
|
||||
project = this.createLoadAndUpdateConfiguredProject(configFileName);
|
||||
// Send the event only if the project got created as part of this open request and info is part of the project
|
||||
if (info.isOrphan()) {
|
||||
// Since the file isnt part of configured project, do not send config file info
|
||||
|
@ -2633,7 +2657,9 @@ namespace ts.server {
|
|||
let project = this.findConfiguredProjectByProjectName(tsconfigFile);
|
||||
if (!project) {
|
||||
// errors are stored in the project, do not need to update the graph
|
||||
project = this.createConfiguredProjectWithDelayLoad(tsconfigFile);
|
||||
project = this.getHostPreferences().lazyConfiguredProjectsFromExternalProject ?
|
||||
this.createConfiguredProjectWithDelayLoad(tsconfigFile) :
|
||||
this.createLoadAndUpdateConfiguredProject(tsconfigFile);
|
||||
}
|
||||
if (project && !contains(exisingConfigFiles, tsconfigFile)) {
|
||||
// keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project
|
||||
|
|
|
@ -1520,6 +1520,11 @@ namespace ts.server {
|
|||
) || false;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
hasExternalProjectRef() {
|
||||
return !!this.externalProjectRefCount;
|
||||
}
|
||||
|
||||
getEffectiveTypeRoots() {
|
||||
return getEffectiveTypeRoots(this.getCompilationSettings(), this.directoryStructureHost) || [];
|
||||
}
|
||||
|
|
|
@ -2823,6 +2823,7 @@ namespace ts.server.protocol {
|
|||
readonly includeCompletionsWithInsertText?: boolean;
|
||||
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
|
||||
}
|
||||
|
||||
export interface CompilerOptions {
|
||||
|
|
|
@ -234,7 +234,7 @@ namespace ts.server {
|
|||
*/
|
||||
readonly containingProjects: Project[] = [];
|
||||
private formatSettings: FormatCodeSettings | undefined;
|
||||
private preferences: UserPreferences | undefined;
|
||||
private preferences: protocol.UserPreferences | undefined;
|
||||
|
||||
/* @internal */
|
||||
fileWatcher: FileWatcher | undefined;
|
||||
|
@ -333,7 +333,7 @@ namespace ts.server {
|
|||
}
|
||||
|
||||
getFormatCodeSettings(): FormatCodeSettings | undefined { return this.formatSettings; }
|
||||
getPreferences(): UserPreferences | undefined { return this.preferences; }
|
||||
getPreferences(): protocol.UserPreferences | undefined { return this.preferences; }
|
||||
|
||||
attachToProject(project: Project): boolean {
|
||||
const isNew = !this.isAttached(project);
|
||||
|
@ -432,7 +432,7 @@ namespace ts.server {
|
|||
}
|
||||
}
|
||||
|
||||
setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences | undefined): void {
|
||||
setOptions(formatSettings: FormatCodeSettings, preferences: protocol.UserPreferences | undefined): void {
|
||||
if (formatSettings) {
|
||||
if (!this.formatSettings) {
|
||||
this.formatSettings = getDefaultFormatCodeSettings(this.host);
|
||||
|
|
|
@ -1423,7 +1423,7 @@ namespace ts.server {
|
|||
const position = this.getPosition(args, scriptInfo);
|
||||
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(file, position, {
|
||||
...this.getPreferences(file),
|
||||
...convertUserPreferences(this.getPreferences(file)),
|
||||
triggerCharacter: args.triggerCharacter,
|
||||
includeExternalModuleExports: args.includeExternalModuleExports,
|
||||
includeInsertTextCompletions: args.includeInsertTextCompletions
|
||||
|
@ -2352,7 +2352,7 @@ namespace ts.server {
|
|||
return this.projectService.getFormatCodeOptions(file);
|
||||
}
|
||||
|
||||
private getPreferences(file: NormalizedPath): UserPreferences {
|
||||
private getPreferences(file: NormalizedPath): protocol.UserPreferences {
|
||||
return this.projectService.getPreferences(file);
|
||||
}
|
||||
|
||||
|
@ -2360,7 +2360,7 @@ namespace ts.server {
|
|||
return this.projectService.getHostFormatCodeOptions();
|
||||
}
|
||||
|
||||
private getHostPreferences(): UserPreferences {
|
||||
private getHostPreferences(): protocol.UserPreferences {
|
||||
return this.projectService.getHostPreferences();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -642,37 +642,54 @@ namespace ts.projectSystem {
|
|||
checkWatchedDirectories(host, [combinePaths(getDirectoryPath(appFile.path), nodeModulesAtTypes)], /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
include: []
|
||||
})
|
||||
};
|
||||
describe("can handle tsconfig file name with difference casing", () => {
|
||||
function verifyConfigFileCasing(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
include: []
|
||||
})
|
||||
};
|
||||
|
||||
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
|
||||
const service = createProjectService(host);
|
||||
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName: "/a/b/project.csproj",
|
||||
rootFiles: toExternalFiles([f1.path, upperCaseConfigFilePath]),
|
||||
options: {}
|
||||
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
|
||||
const service = createProjectService(host);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName: "/a/b/project.csproj",
|
||||
rootFiles: toExternalFiles([f1.path, upperCaseConfigFilePath]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
}
|
||||
else {
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
|
||||
}
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
|
||||
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
|
||||
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
|
||||
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
|
||||
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
it("create configured project without file list", () => {
|
||||
|
@ -2950,45 +2967,56 @@ namespace ts.projectSystem {
|
|||
assert.equal(navbar[0].spans[0].length, f1.content.length);
|
||||
});
|
||||
|
||||
it("deleting config file opened from the external project works", () => {
|
||||
const site = {
|
||||
path: "/user/someuser/project/js/site.js",
|
||||
content: ""
|
||||
};
|
||||
const configFile = {
|
||||
path: "/user/someuser/project/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
|
||||
const host = createServerHost([libFile, site, configFile]);
|
||||
const projectService = createProjectService(host);
|
||||
describe("deleting config file opened from the external project works", () => {
|
||||
function verifyDeletingConfigFile(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const site = {
|
||||
path: "/user/someuser/project/js/site.js",
|
||||
content: ""
|
||||
};
|
||||
const configFile = {
|
||||
path: "/user/someuser/project/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
|
||||
const host = createServerHost([libFile, site, configFile]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
const externalProject: protocol.ExternalProject = {
|
||||
projectFileName,
|
||||
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
|
||||
options: { allowJs: false },
|
||||
typeAcquisition: { include: [] }
|
||||
};
|
||||
const externalProject: protocol.ExternalProject = {
|
||||
projectFileName,
|
||||
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
|
||||
options: { allowJs: false },
|
||||
typeAcquisition: { include: [] }
|
||||
};
|
||||
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
|
||||
let knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
|
||||
let knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
|
||||
|
||||
const configProject = configuredProjectAt(projectService, 0);
|
||||
checkProjectActualFiles(configProject, []); // Since no files opened from this project, its not loaded
|
||||
const configProject = configuredProjectAt(projectService, 0);
|
||||
checkProjectActualFiles(configProject, lazyConfiguredProjectsFromExternalProject ?
|
||||
emptyArray : // Since no files opened from this project, its not loaded
|
||||
[libFile.path, configFile.path]);
|
||||
|
||||
host.reloadFS([libFile, site]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
host.reloadFS([libFile, site]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
|
||||
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
|
||||
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
|
||||
|
||||
externalProject.rootFiles.length = 1;
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
externalProject.rootFiles.length = 1;
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
|
||||
}
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
it("Getting errors from closed script info does not throw exception (because of getting project from orphan script info)", () => {
|
||||
|
@ -3298,49 +3326,64 @@ namespace ts.projectSystem {
|
|||
|
||||
});
|
||||
|
||||
it("includes deferred files in the project context", () => {
|
||||
const file1 = {
|
||||
path: "/a.deferred",
|
||||
content: "const a = 1;"
|
||||
};
|
||||
// Deferred extensions should not affect JS files.
|
||||
const file2 = {
|
||||
path: "/b.js",
|
||||
content: "const b = 1;"
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
describe("includes deferred files in the project context", () => {
|
||||
function verifyDeferredContext(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const file1 = {
|
||||
path: "/a.deferred",
|
||||
content: "const a = 1;"
|
||||
};
|
||||
// Deferred extensions should not affect JS files.
|
||||
const file2 = {
|
||||
path: "/b.js",
|
||||
content: "const b = 1;"
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, file2, tsconfig]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
const host = createServerHost([file1, file2, tsconfig]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
session.executeCommandSeq<protocol.ConfigureRequest>({
|
||||
command: protocol.CommandTypes.Configure,
|
||||
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject } }
|
||||
});
|
||||
|
||||
// Configure the deferred extension.
|
||||
const extraFileExtensions = [{ extension: ".deferred", scriptKind: ScriptKind.Deferred, isMixedContent: true }];
|
||||
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
|
||||
session.executeCommand(configureHostRequest);
|
||||
// Configure the deferred extension.
|
||||
const extraFileExtensions = [{ extension: ".deferred", scriptKind: ScriptKind.Deferred, isMixedContent: true }];
|
||||
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
|
||||
session.executeCommand(configureHostRequest);
|
||||
|
||||
// Open external project
|
||||
const projectName = "/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([file1.path, file2.path, tsconfig.path]),
|
||||
options: {}
|
||||
// Open external project
|
||||
const projectName = "/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([file1.path, file2.path, tsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
// Assert
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
|
||||
const configuredProject = configuredProjectAt(projectService, 0);
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
// configured project is just created and not yet loaded
|
||||
checkProjectActualFiles(configuredProject, emptyArray);
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
|
||||
|
||||
// Allow allowNonTsExtensions will be set to true for deferred extensions.
|
||||
assert.isTrue(configuredProject.getCompilerOptions().allowNonTsExtensions);
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyDeferredContext(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyDeferredContext(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
|
||||
// Assert
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
|
||||
const configuredProject = configuredProjectAt(projectService, 0);
|
||||
// configured project is just created and not yet loaded
|
||||
checkProjectActualFiles(configuredProject, emptyArray);
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
|
||||
|
||||
// Allow allowNonTsExtensions will be set to true for deferred extensions.
|
||||
assert.isTrue(configuredProject.getCompilerOptions().allowNonTsExtensions);
|
||||
});
|
||||
|
||||
it("Orphan source files are handled correctly on watch trigger", () => {
|
||||
|
@ -3943,143 +3986,167 @@ namespace ts.projectSystem {
|
|||
});
|
||||
|
||||
describe("tsserverProjectSystem external projects", () => {
|
||||
it("correctly handling add/remove tsconfig - 1", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f1, f2]);
|
||||
const projectService = createProjectService(host);
|
||||
describe("correctly handling add/remove tsconfig - 1", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f1, f2]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
|
||||
// rename lib.ts to tsconfig.json
|
||||
host.reloadFS([f1, tsconfig]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
}
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
|
||||
// rename lib.ts to tsconfig.json
|
||||
host.reloadFS([f1, tsconfig]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
|
||||
options: {}
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
});
|
||||
|
||||
describe("correctly handling add/remove tsconfig - 2", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const cLib = {
|
||||
path: "/a/b/c/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const cTsconfig = {
|
||||
path: "/a/b/c/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const dLib = {
|
||||
path: "/a/b/d/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const dTsconfig = {
|
||||
path: "/a/b/d/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
it("correctly handling add/remove tsconfig - 2", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const cLib = {
|
||||
path: "/a/b/c/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const cTsconfig = {
|
||||
path: "/a/b/c/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const dLib = {
|
||||
path: "/a/b/d/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const dTsconfig = {
|
||||
path: "/a/b/d/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
|
||||
const projectService = createProjectService(host);
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove one config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove second config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// open two config files
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// close all projects - no projects should be opened
|
||||
projectService.closeExternalProject(projectName);
|
||||
projectService.checkNumberOfProjects({});
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove one config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove second config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// open two config files
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// close all projects - no projects should be opened
|
||||
projectService.closeExternalProject(projectName);
|
||||
projectService.checkNumberOfProjects({});
|
||||
});
|
||||
|
||||
it("correctly handles changes in lib section of config file", () => {
|
||||
|
@ -4174,6 +4241,47 @@ namespace ts.projectSystem {
|
|||
assert.isTrue(project.hasOpenRef()); // f
|
||||
assert.isFalse(project.isClosed());
|
||||
});
|
||||
|
||||
it("handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({})
|
||||
};
|
||||
const projectFileName = "/a/b/project.csproj";
|
||||
const host = createServerHost([f1, config]);
|
||||
const service = createProjectService(host);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: true } });
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project, [config.path, f1.path]);
|
||||
|
||||
service.closeExternalProject(projectFileName);
|
||||
service.checkNumberOfProjects({});
|
||||
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project2 = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project2, [config.path, f1.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem prefer typings to js", () => {
|
||||
|
|
|
@ -7797,6 +7797,7 @@ declare namespace ts.server.protocol {
|
|||
readonly includeCompletionsWithInsertText?: boolean;
|
||||
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
|
||||
}
|
||||
interface CompilerOptions {
|
||||
allowJs?: boolean;
|
||||
|
@ -7928,14 +7929,14 @@ declare namespace ts.server {
|
|||
getSnapshot(): IScriptSnapshot;
|
||||
private ensureRealPath;
|
||||
getFormatCodeSettings(): FormatCodeSettings | undefined;
|
||||
getPreferences(): UserPreferences | undefined;
|
||||
getPreferences(): protocol.UserPreferences | undefined;
|
||||
attachToProject(project: Project): boolean;
|
||||
isAttached(project: Project): boolean;
|
||||
detachFromProject(project: Project): void;
|
||||
detachAllProjects(): void;
|
||||
getDefaultProject(): Project;
|
||||
registerFileUpdate(): void;
|
||||
setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences | undefined): void;
|
||||
setOptions(formatSettings: FormatCodeSettings, preferences: protocol.UserPreferences | undefined): void;
|
||||
getLatestVersion(): string;
|
||||
saveTo(fileName: string): void;
|
||||
reloadFromFile(tempFileName?: NormalizedPath): boolean;
|
||||
|
@ -8313,7 +8314,7 @@ declare namespace ts.server {
|
|||
function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX;
|
||||
interface HostConfiguration {
|
||||
formatCodeOptions: FormatCodeSettings;
|
||||
preferences: UserPreferences;
|
||||
preferences: protocol.UserPreferences;
|
||||
hostInfo: string;
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
@ -8432,9 +8433,9 @@ declare namespace ts.server {
|
|||
*/
|
||||
private ensureProjectStructuresUptoDate;
|
||||
getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings;
|
||||
getPreferences(file: NormalizedPath): UserPreferences;
|
||||
getPreferences(file: NormalizedPath): protocol.UserPreferences;
|
||||
getHostFormatCodeOptions(): FormatCodeSettings;
|
||||
getHostPreferences(): UserPreferences;
|
||||
getHostPreferences(): protocol.UserPreferences;
|
||||
private onSourceFileChanged;
|
||||
private handleDeletedFile;
|
||||
private onConfigChangedForConfiguredProject;
|
||||
|
|
Loading…
Reference in a new issue