Skip to content

Commit 24cc284

Browse files
committed
The assert that cached value of config file existance is always correct, might not be true if file watcher is not invoked before creating configured project
Fixes #29191
1 parent 799656a commit 24cc284

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

src/server/editorServices.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,8 @@ namespace ts.server {
12771277
private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) {
12781278
const configFileExistenceInfo = this.getConfigFileExistenceInfo(project);
12791279
if (configFileExistenceInfo) {
1280-
Debug.assert(configFileExistenceInfo.exists);
1280+
// The existance might not be set if the file watcher is not invoked by the time config project is created by external project
1281+
configFileExistenceInfo.exists = true;
12811282
// close existing watcher
12821283
if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) {
12831284
const configFileName = project.getConfigFilePath();

src/testRunner/unittests/tsserver/externalProjects.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,5 +760,63 @@ namespace ts.projectSystem {
760760
assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
761761
checkProjectActualFiles(project2, [config.path, f1.path]);
762762
});
763+
764+
it("handles creation of external project with jsconfig before jsconfig creation watcher is invoked", () => {
765+
const projectLocation = `/user/username/projects/WebApplication36/WebApplication36`;
766+
const projectFileName = `${projectLocation}/WebApplication36.csproj`;
767+
const tsconfig: File = {
768+
path: `${projectLocation}/tsconfig.json`,
769+
content: "{}"
770+
};
771+
const files = [libFile, tsconfig];
772+
const host = createServerHost(files);
773+
const service = createProjectService(host);
774+
775+
// Create external project
776+
service.openExternalProjects([{
777+
projectFileName,
778+
rootFiles: [{ fileName: tsconfig.path }],
779+
options: { allowJs: false }
780+
}]);
781+
checkNumberOfProjects(service, { configuredProjects: 1 });
782+
const configProject = service.configuredProjects.get(tsconfig.path.toLowerCase())!;
783+
checkProjectActualFiles(configProject, [tsconfig.path]);
784+
785+
// write js file, open external project and open it for edit
786+
const jsFilePath = `${projectLocation}/javascript.js`;
787+
host.writeFile(jsFilePath, "");
788+
service.openExternalProjects([{
789+
projectFileName,
790+
rootFiles: [{ fileName: tsconfig.path }, { fileName: jsFilePath }],
791+
options: { allowJs: false }
792+
}]);
793+
service.applyChangesInOpenFiles([
794+
{ fileName: jsFilePath, scriptKind: ScriptKind.JS, content: "" }
795+
], /*changedFiles*/ undefined, /*closedFiles*/ undefined);
796+
checkNumberOfProjects(service, { configuredProjects: 1, inferredProjects: 1 });
797+
checkProjectActualFiles(configProject, [tsconfig.path]);
798+
const inferredProject = service.inferredProjects[0];
799+
checkProjectActualFiles(inferredProject, [libFile.path, jsFilePath]);
800+
801+
// write jsconfig file
802+
const jsConfig: File = {
803+
path: `${projectLocation}/jsconfig.json`,
804+
content: "{}"
805+
};
806+
// Dont invoke file creation watchers as the repro suggests
807+
host.ensureFileOrFolder(jsConfig, /*ignoreWatchInvokedWithTriggerAsFileCreate*/ true);
808+
809+
// Open external project
810+
service.openExternalProjects([{
811+
projectFileName,
812+
rootFiles: [{ fileName: jsConfig.path }, { fileName: tsconfig.path }, { fileName: jsFilePath }],
813+
options: { allowJs: false }
814+
}]);
815+
checkNumberOfProjects(service, { configuredProjects: 2, inferredProjects: 1 });
816+
checkProjectActualFiles(configProject, [tsconfig.path]);
817+
assert.isTrue(inferredProject.isOrphan());
818+
const jsConfigProject = service.configuredProjects.get(jsConfig.path.toLowerCase())!;
819+
checkProjectActualFiles(jsConfigProject, [jsConfig.path, jsFilePath, libFile.path]);
820+
});
763821
});
764822
}

src/testRunner/unittests/tsserver/helpers.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ namespace ts.projectSystem {
5757

5858
export const nullLogger: server.Logger = {
5959
close: noop,
60-
hasLevel: () => false,
61-
loggingEnabled: () => false,
60+
hasLevel: returnFalse,
61+
loggingEnabled: returnFalse,
6262
perftrc: noop,
6363
info: noop,
6464
msg: noop,
@@ -80,6 +80,21 @@ namespace ts.projectSystem {
8080
return { logger, hasErrorMsg: () => hasErrorMsg };
8181
}
8282

83+
export function createLoggerWritingToConsole(): server.Logger {
84+
const { close, startGroup, endGroup, getLogFileName } = nullLogger;
85+
return {
86+
close,
87+
hasLevel: returnTrue,
88+
loggingEnabled: returnTrue,
89+
perftrc: s => console.log(s),
90+
info: s => console.log(s),
91+
msg: (s, type) => console.log(`${type}:: ${s}`),
92+
startGroup,
93+
endGroup,
94+
getLogFileName
95+
};
96+
}
97+
8398
export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller {
8499
protected projectService!: server.ProjectService;
85100
constructor(

0 commit comments

Comments
 (0)