mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 01:04:49 -05:00
Refactor to simplify project references tsc-watch and tsserver tests (#42926)
This commit is contained in:
@@ -147,6 +147,7 @@
|
||||
"unittests/tscWatch/forceConsistentCasingInFileNames.ts",
|
||||
"unittests/tscWatch/incremental.ts",
|
||||
"unittests/tscWatch/programUpdates.ts",
|
||||
"unittests/tscWatch/projectsWithReferences.ts",
|
||||
"unittests/tscWatch/resolutionCache.ts",
|
||||
"unittests/tscWatch/sourceOfProjectReferenceRedirect.ts",
|
||||
"unittests/tscWatch/watchApi.ts",
|
||||
@@ -192,6 +193,7 @@
|
||||
"unittests/tsserver/projectReferences.ts",
|
||||
"unittests/tsserver/projectReferencesSourcemap.ts",
|
||||
"unittests/tsserver/projects.ts",
|
||||
"unittests/tsserver/projectsWithReferences.ts",
|
||||
"unittests/tsserver/refactors.ts",
|
||||
"unittests/tsserver/reload.ts",
|
||||
"unittests/tsserver/reloadProjects.ts",
|
||||
|
||||
@@ -10,18 +10,6 @@ namespace ts.tscWatch {
|
||||
);
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: readonly string[], defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderHost(system);
|
||||
return ts.createSolutionBuilder(host, rootNames, defaultOptions || {});
|
||||
}
|
||||
|
||||
export function ensureErrorFreeBuild(host: WatchedSystem, rootNames: readonly string[]) {
|
||||
// ts build should succeed
|
||||
const solutionBuilder = createSolutionBuilder(host, rootNames, {});
|
||||
solutionBuilder.build();
|
||||
assert.equal(host.getOutput().length, 0, JSON.stringify(host.getOutput(), /*replacer*/ undefined, " "));
|
||||
}
|
||||
|
||||
type OutputFileStamp = [string, Date | undefined, boolean];
|
||||
function transformOutputToOutputFileStamp(f: string, host: TsBuildWatchSystem): OutputFileStamp {
|
||||
return [f, host.getModifiedTime(f), host.writtenFiles.has(host.toFullPath(f))] as OutputFileStamp;
|
||||
@@ -39,10 +27,6 @@ namespace ts.tscWatch {
|
||||
type ReadonlyFile = Readonly<File>;
|
||||
/** [tsconfig, index] | [tsconfig, index, anotherModule, someDecl] */
|
||||
type SubProjectFiles = [ReadonlyFile, ReadonlyFile] | [ReadonlyFile, ReadonlyFile, ReadonlyFile, ReadonlyFile];
|
||||
function getProjectPath(project: string) {
|
||||
return `${projectsLocation}/${project}`;
|
||||
}
|
||||
|
||||
function projectPath(subProject: SubProject) {
|
||||
return getFilePathInProject(project, subProject);
|
||||
}
|
||||
@@ -51,10 +35,6 @@ namespace ts.tscWatch {
|
||||
return `${projectPath(subProject)}/${baseFileName.toLowerCase()}`;
|
||||
}
|
||||
|
||||
function projectFileName(subProject: SubProject, baseFileName: string) {
|
||||
return `${projectPath(subProject)}/${baseFileName}`;
|
||||
}
|
||||
|
||||
function projectFile(subProject: SubProject, baseFileName: string): File {
|
||||
return getFileFromProject(project, `${subProject}/${baseFileName}`);
|
||||
}
|
||||
@@ -551,580 +531,6 @@ let x: string = 10;`),
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsc-watch and tsserver works with project references", () => {
|
||||
describe("invoking when references are already built", () => {
|
||||
function verifyWatchesOfProject(host: TsBuildWatchSystem, expectedWatchedFiles: readonly string[], expectedWatchedDirectoriesRecursive: readonly string[], expectedWatchedDirectories?: readonly string[]) {
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles, 1);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories || emptyArray, 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectoriesRecursive, 1, /*recursive*/ true);
|
||||
}
|
||||
|
||||
function createSolutionOfProject(allFiles: readonly File[],
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => readonly OutputFileStamp[]) {
|
||||
// Build the composite project
|
||||
const host = createTsBuildWatchSystem(allFiles, { currentDirectory });
|
||||
const solutionBuilder = createSolutionBuilder(host, [solutionBuilderconfig], {});
|
||||
solutionBuilder.build();
|
||||
const outputFileStamps = getOutputFileStamps(host);
|
||||
for (const stamp of outputFileStamps) {
|
||||
assert.isDefined(stamp[1], `${stamp[0]} expected to be present`);
|
||||
}
|
||||
return { host, solutionBuilder };
|
||||
}
|
||||
|
||||
function createSolutionAndWatchModeOfProject(
|
||||
allFiles: readonly File[],
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
watchConfig: string,
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => readonly OutputFileStamp[]) {
|
||||
// Build the composite project
|
||||
const { host, solutionBuilder } = createSolutionOfProject(allFiles, currentDirectory, solutionBuilderconfig, getOutputFileStamps);
|
||||
|
||||
// Build in watch mode
|
||||
const watch = createWatchOfConfigFile(watchConfig, host);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
return { host, solutionBuilder, watch };
|
||||
}
|
||||
|
||||
function createSolutionAndServiceOfProject(allFiles: readonly File[],
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
openFileName: string,
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => readonly OutputFileStamp[]) {
|
||||
// Build the composite project
|
||||
const { host, solutionBuilder } = createSolutionOfProject(allFiles, currentDirectory, solutionBuilderconfig, getOutputFileStamps);
|
||||
|
||||
// service
|
||||
const service = projectSystem.createProjectService(host);
|
||||
service.openClientFile(openFileName);
|
||||
|
||||
return { host, solutionBuilder, service };
|
||||
}
|
||||
|
||||
function checkProjectActualFiles(service: projectSystem.TestProjectService, configFile: string, expectedFiles: readonly string[]) {
|
||||
projectSystem.checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
projectSystem.checkProjectActualFiles(service.configuredProjects.get(configFile.toLowerCase())!, expectedFiles);
|
||||
}
|
||||
|
||||
function verifyDependencies(watch: Watch, filePath: string, expected: readonly string[]) {
|
||||
checkArray(`${filePath} dependencies`, watch.getCurrentProgram().getAllDependencies(watch.getCurrentProgram().getSourceFile(filePath)!), expected);
|
||||
}
|
||||
|
||||
describe("on sample project", () => {
|
||||
const coreIndexDts = projectFileName(SubProject.core, "index.d.ts");
|
||||
const coreAnotherModuleDts = projectFileName(SubProject.core, "anotherModule.d.ts");
|
||||
const logicIndexDts = projectFileName(SubProject.logic, "index.d.ts");
|
||||
const expectedWatchedDirectoriesRecursive = projectSystem.getTypeRootsFromLocation(projectPath(SubProject.tests));
|
||||
const expectedProjectFiles = () => [libFile, ...tests, ...logic.slice(1), ...core.slice(1, core.length - 1)].map(f => f.path);
|
||||
const expectedProgramFiles = () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, logicIndexDts];
|
||||
|
||||
function createSolutionAndWatchMode() {
|
||||
return createSolutionAndWatchModeOfProject(allFiles, projectsLocation, `${project}/${SubProject.tests}`, tests[0].path, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function createSolutionAndService() {
|
||||
return createSolutionAndServiceOfProject(allFiles, projectsLocation, `${project}/${SubProject.tests}`, tests[1].path, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function verifyWatches(host: TsBuildWatchSystem, withTsserver?: boolean) {
|
||||
verifyWatchesOfProject(
|
||||
host,
|
||||
withTsserver ?
|
||||
[...core.slice(0, core.length - 1), ...logic, tests[0], libFile].map(f => f.path.toLowerCase()) :
|
||||
[core[0], logic[0], ...tests, libFile].map(f => f.path).concat([coreIndexDts, coreAnotherModuleDts, logicIndexDts].map(f => f.toLowerCase())),
|
||||
expectedWatchedDirectoriesRecursive
|
||||
);
|
||||
}
|
||||
|
||||
function verifyScenario(
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
||||
expectedProgramFilesAfterEdit: () => readonly string[],
|
||||
expectedProjectFilesAfterEdit: () => readonly string[]
|
||||
) {
|
||||
it("with tsc-watch", () => {
|
||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
checkProgramActualFiles(watch.getCurrentProgram().getProgram(), expectedProgramFilesAfterEdit());
|
||||
|
||||
});
|
||||
|
||||
it("with tsserver", () => {
|
||||
const { host, solutionBuilder, service } = createSolutionAndService();
|
||||
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(service, tests[0].path, expectedProjectFilesAfterEdit());
|
||||
});
|
||||
}
|
||||
|
||||
describe("verifies dependencies and watches", () => {
|
||||
it("with tsc-watch", () => {
|
||||
const { host, watch } = createSolutionAndWatchMode();
|
||||
verifyWatches(host);
|
||||
verifyDependencies(watch, coreIndexDts, [coreIndexDts]);
|
||||
verifyDependencies(watch, coreAnotherModuleDts, [coreAnotherModuleDts]);
|
||||
verifyDependencies(watch, logicIndexDts, [logicIndexDts, coreAnotherModuleDts]);
|
||||
verifyDependencies(watch, tests[1].path, expectedProgramFiles().filter(f => f !== libFile.path));
|
||||
});
|
||||
|
||||
it("with tsserver", () => {
|
||||
const { host } = createSolutionAndService();
|
||||
verifyWatches(host, /*withTsserver*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("local edit in ts file, result in watch compilation because logic.d.ts is written", () => {
|
||||
verifyScenario((host, solutionBuilder) => {
|
||||
host.writeFile(logic[1].path, `${logic[1].content}
|
||||
function foo() {
|
||||
}`);
|
||||
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath);
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
|
||||
// not ideal, but currently because of d.ts but no new file is written
|
||||
// There will be timeout queued even though file contents are same
|
||||
}, expectedProgramFiles, expectedProjectFiles);
|
||||
});
|
||||
|
||||
describe("non local edit in ts file, rebuilds in watch compilation", () => {
|
||||
verifyScenario((host, solutionBuilder) => {
|
||||
host.writeFile(logic[1].path, `${logic[1].content}
|
||||
export function gfoo() {
|
||||
}`);
|
||||
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath);
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
}, expectedProgramFiles, expectedProjectFiles);
|
||||
});
|
||||
|
||||
describe("change in project reference config file builds correctly", () => {
|
||||
verifyScenario((host, solutionBuilder) => {
|
||||
host.writeFile(logic[0].path, JSON.stringify({
|
||||
compilerOptions: { composite: true, declaration: true, declarationDir: "decls" },
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath, ConfigFileProgramReloadLevel.Full);
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
}, () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")], expectedProjectFiles);
|
||||
});
|
||||
});
|
||||
|
||||
describe("on transitive references", () => {
|
||||
const project = "transitiveReferences";
|
||||
const aTsFile = getFileFromProject(project, "a.ts");
|
||||
const bTsFile = getFileFromProject(project, "b.ts");
|
||||
const cTsFile = getFileFromProject(project, "c.ts");
|
||||
const aTsconfigFile = getFileFromProject(project, "tsconfig.a.json");
|
||||
const bTsconfigFile = getFileFromProject(project, "tsconfig.b.json");
|
||||
const cTsconfigFile = getFileFromProject(project, "tsconfig.c.json");
|
||||
const refs = getFileFromProject(project, "refs/a.d.ts");
|
||||
|
||||
function getRootFile(multiFolder: boolean, fileFromDisk: File, multiFolderPath: string): File {
|
||||
return multiFolder ? {
|
||||
path: getFilePathInProject(project, multiFolderPath),
|
||||
content: fileFromDisk.content
|
||||
// Replace the relative imports
|
||||
.replace("./", "../")
|
||||
} : fileFromDisk;
|
||||
}
|
||||
|
||||
function dtsFile(extensionLessFile: string) {
|
||||
return getFilePathInProject(project, `${extensionLessFile}.d.ts`);
|
||||
}
|
||||
|
||||
function jsFile(extensionLessFile: string) {
|
||||
return getFilePathInProject(project, `${extensionLessFile}.js`);
|
||||
}
|
||||
|
||||
function verifyWatchState(
|
||||
host: TsBuildWatchSystem,
|
||||
watch: Watch,
|
||||
expectedProgramFiles: readonly string[],
|
||||
expectedWatchedFiles: readonly string[],
|
||||
expectedWatchedDirectoriesRecursive: readonly string[],
|
||||
dependencies: readonly [string, readonly string[]][],
|
||||
expectedWatchedDirectories?: readonly string[]) {
|
||||
checkProgramActualFiles(watch.getCurrentProgram().getProgram(), expectedProgramFiles);
|
||||
verifyWatchesOfProject(host, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
|
||||
for (const [file, deps] of dependencies) {
|
||||
verifyDependencies(watch, file, deps);
|
||||
}
|
||||
}
|
||||
|
||||
function getTsConfigFile(multiFolder: boolean, fileFromDisk: File, folder: string): File {
|
||||
if (!multiFolder) return fileFromDisk;
|
||||
|
||||
return {
|
||||
path: getFilePathInProject(project, `${folder}/tsconfig.json`),
|
||||
content: fileFromDisk.content
|
||||
// Replace files array
|
||||
.replace(`${folder}.ts`, "index.ts")
|
||||
// Replace path mappings
|
||||
.replace("./*", "../*")
|
||||
.replace("./refs", "../refs")
|
||||
// Replace references
|
||||
.replace("tsconfig.a.json", "../a")
|
||||
.replace("tsconfig.b.json", "../b")
|
||||
};
|
||||
}
|
||||
|
||||
// function writeFile(file: File) {
|
||||
// Harness.IO.writeFile(file.path.replace(projectsLocation, "c:/temp"), file.content);
|
||||
// }
|
||||
|
||||
function verifyTransitiveReferences(multiFolder: boolean) {
|
||||
const aTs = getRootFile(multiFolder, aTsFile, "a/index.ts");
|
||||
const bTs = getRootFile(multiFolder, bTsFile, "b/index.ts");
|
||||
const cTs = getRootFile(multiFolder, cTsFile, "c/index.ts");
|
||||
|
||||
const configToBuild = multiFolder ? "c/tsconfig.json" : "tsconfig.c.json";
|
||||
const aTsconfig = getTsConfigFile(multiFolder, aTsconfigFile, "a");
|
||||
const bTsconfig = getTsConfigFile(multiFolder, bTsconfigFile, "b");
|
||||
const cTsconfig = getTsConfigFile(multiFolder, cTsconfigFile, "c");
|
||||
|
||||
// if (multiFolder) {
|
||||
// writeFile(aTs);
|
||||
// writeFile(bTs);
|
||||
// writeFile(cTs);
|
||||
// writeFile(aTsconfig);
|
||||
// writeFile(bTsconfig);
|
||||
// writeFile(cTsconfig);
|
||||
// }
|
||||
|
||||
const allFiles = [libFile, aTs, bTs, cTs, aTsconfig, bTsconfig, cTsconfig, refs];
|
||||
const aDts = dtsFile(multiFolder ? "a/index" : "a"), bDts = dtsFile(multiFolder ? "b/index" : "b");
|
||||
const expectedFiles = [jsFile(multiFolder ? "a/index" : "a"), aDts, jsFile(multiFolder ? "b/index" : "b"), bDts, jsFile(multiFolder ? "c/index" : "c")];
|
||||
const expectedProgramFiles = [cTs.path, libFile.path, aDts, refs.path, bDts];
|
||||
const expectedProjectFiles = [cTs.path, libFile.path, aTs.path, refs.path, bTs.path];
|
||||
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
|
||||
const expectedProjectWatchedFiles = expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
|
||||
const expectedWatchedDirectories = multiFolder ? [
|
||||
getProjectPath(project).toLowerCase() // watches for directories created for resolution of b
|
||||
] : emptyArray;
|
||||
const nrefsPath = multiFolder ? ["../nrefs/*"] : ["./nrefs/*"];
|
||||
const expectedWatchedDirectoriesRecursive = [
|
||||
...(multiFolder ? [
|
||||
getFilePathInProject(project, "a"), // Failed to package json
|
||||
getFilePathInProject(project, "b"), // Failed to package json
|
||||
] : []),
|
||||
getFilePathInProject(project, "refs"), // Failed lookup since refs/a.ts does not exist
|
||||
...projectSystem.getTypeRootsFromLocation(multiFolder ? getFilePathInProject(project, "c") : getProjectPath(project))
|
||||
].map(s => s.toLowerCase());
|
||||
|
||||
const defaultDependencies: readonly [string, readonly string[]][] = [
|
||||
[aDts, [aDts]],
|
||||
[bDts, [bDts, aDts]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bDts, aDts]]
|
||||
];
|
||||
|
||||
function createSolutionAndWatchMode() {
|
||||
return createSolutionAndWatchModeOfProject(allFiles, getProjectPath(project), configToBuild, configToBuild, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function createSolutionAndService() {
|
||||
return createSolutionAndServiceOfProject(allFiles, getProjectPath(project), configToBuild, cTs.path, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function getOutputFileStamps(host: TsBuildWatchSystem) {
|
||||
return expectedFiles.map(file => transformOutputToOutputFileStamp(file, host));
|
||||
}
|
||||
|
||||
function verifyProgram(host: TsBuildWatchSystem, watch: Watch) {
|
||||
verifyWatchState(host, watch, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, defaultDependencies, expectedWatchedDirectories);
|
||||
}
|
||||
|
||||
function verifyProject(host: TsBuildWatchSystem, service: projectSystem.TestProjectService, orphanInfos?: readonly string[]) {
|
||||
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos });
|
||||
}
|
||||
|
||||
interface VerifyServerState {
|
||||
host: TsBuildWatchSystem;
|
||||
service: projectSystem.TestProjectService;
|
||||
expectedProjectFiles: readonly string[];
|
||||
expectedProjectWatchedFiles: readonly string[];
|
||||
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||
orphanInfos?: readonly string[];
|
||||
}
|
||||
function verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos }: VerifyServerState) {
|
||||
checkProjectActualFiles(service, cTsconfig.path, expectedProjectFiles.concat(cTsconfig.path));
|
||||
const watchedFiles = expectedProjectWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
|
||||
const actualOrphan = arrayFrom(mapDefinedIterator(
|
||||
service.filenameToScriptInfo.values(),
|
||||
v => v.containingProjects.length === 0 ? v.fileName : undefined
|
||||
));
|
||||
assert.equal(actualOrphan.length, orphanInfos ? orphanInfos.length : 0, `Orphans found: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
|
||||
if (orphanInfos && orphanInfos.length) {
|
||||
for (const orphan of orphanInfos) {
|
||||
const info = service.getScriptInfoForPath(orphan as Path);
|
||||
assert.isDefined(info, `${orphan} expected to be present. Actual: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
|
||||
assert.equal(info!.containingProjects.length, 0);
|
||||
watchedFiles.push(orphan);
|
||||
}
|
||||
}
|
||||
verifyWatchesOfProject(host, watchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
|
||||
}
|
||||
|
||||
interface VerifyScenario {
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void;
|
||||
schedulesFailedWatchUpdate?: boolean;
|
||||
expectedEditErrors: readonly string[];
|
||||
expectedProgramFiles: readonly string[];
|
||||
expectedProjectFiles: readonly string[];
|
||||
expectedWatchedFiles: readonly string[];
|
||||
expectedProjectWatchedFiles: readonly string[];
|
||||
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||
dependencies: readonly [string, readonly string[]][];
|
||||
revert?: (host: TsBuildWatchSystem) => void;
|
||||
orphanInfosAfterEdit?: readonly string[];
|
||||
orphanInfosAfterRevert?: readonly string[];
|
||||
}
|
||||
function verifyScenario({ edit, schedulesFailedWatchUpdate, expectedEditErrors, expectedProgramFiles, expectedProjectFiles, expectedWatchedFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, dependencies, revert, orphanInfosAfterEdit, orphanInfosAfterRevert }: VerifyScenario) {
|
||||
it("with tsc-watch", () => {
|
||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(schedulesFailedWatchUpdate ? 2 : 1);
|
||||
checkOutputErrorsIncremental(host, expectedEditErrors);
|
||||
verifyWatchState(host, watch, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, dependencies, expectedWatchedDirectories);
|
||||
|
||||
if (revert) {
|
||||
revert(host);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(schedulesFailedWatchUpdate ? 2 : 1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyProgram(host, watch);
|
||||
}
|
||||
});
|
||||
|
||||
if (!multiFolder) return; // With side by side file open is in inferred project without any settings
|
||||
|
||||
it("with tsserver", () => {
|
||||
const { host, solutionBuilder, service } = createSolutionAndService();
|
||||
|
||||
edit(host, solutionBuilder);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(schedulesFailedWatchUpdate ? 3 : 2);
|
||||
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos: orphanInfosAfterEdit });
|
||||
|
||||
if (revert) {
|
||||
revert(host);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(schedulesFailedWatchUpdate ? 3 : 2);
|
||||
verifyProject(host, service, orphanInfosAfterRevert);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe("verifies dependencies and watches", () => {
|
||||
// Initial build
|
||||
it("with tsc-watch", () => {
|
||||
const { host, watch } = createSolutionAndWatchMode();
|
||||
verifyProgram(host, watch);
|
||||
});
|
||||
if (!multiFolder) return;
|
||||
it("with tsserver", () => {
|
||||
const { host, service } = createSolutionAndService();
|
||||
verifyProject(host, service);
|
||||
});
|
||||
});
|
||||
|
||||
describe("non local edit updates the program and watch correctly", () => {
|
||||
verifyScenario({
|
||||
edit: (host, solutionBuilder) => {
|
||||
// edit
|
||||
host.writeFile(bTs.path, `${bTs.content}\nexport function gfoo() {\n}`);
|
||||
solutionBuilder.invalidateProject((bTsconfig.path.toLowerCase() as ResolvedConfigFilePath));
|
||||
solutionBuilder.buildNextInvalidatedProject();
|
||||
},
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles,
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles,
|
||||
expectedProjectWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive,
|
||||
dependencies: defaultDependencies
|
||||
});
|
||||
});
|
||||
|
||||
describe("edit on config file", () => {
|
||||
const nrefReplacer = (f: string) => f.replace("refs", "nrefs");
|
||||
const nrefs: File = {
|
||||
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
||||
content: refs.content
|
||||
};
|
||||
verifyScenario({
|
||||
edit: host => {
|
||||
const cTsConfigJson = JSON.parse(cTsconfig.content);
|
||||
host.ensureFileOrFolder(nrefs);
|
||||
cTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||
host.writeFile(cTsconfig.path, JSON.stringify(cTsConfigJson));
|
||||
},
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles: expectedProgramFiles.map(nrefReplacer),
|
||||
expectedProjectFiles: expectedProjectFiles.map(nrefReplacer),
|
||||
expectedWatchedFiles: expectedWatchedFiles.map(nrefReplacer),
|
||||
expectedProjectWatchedFiles: expectedProjectWatchedFiles.map(nrefReplacer),
|
||||
expectedWatchedDirectoriesRecursive: expectedWatchedDirectoriesRecursive.map(nrefReplacer),
|
||||
dependencies: [
|
||||
[aDts, [aDts]],
|
||||
[bDts, [bDts, aDts]],
|
||||
[nrefs.path, [nrefs.path]],
|
||||
[cTs.path, [cTs.path, nrefs.path, bDts, aDts]]
|
||||
],
|
||||
// revert the update
|
||||
revert: host => host.writeFile(cTsconfig.path, cTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
orphanInfosAfterEdit: [refs.path.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
|
||||
});
|
||||
});
|
||||
|
||||
describe("edit in referenced config file", () => {
|
||||
const nrefs: File = {
|
||||
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
||||
content: "export declare class A {}"
|
||||
};
|
||||
const expectedProgramFiles = [cTs.path, bDts, nrefs.path, refs.path, libFile.path];
|
||||
const expectedProjectFiles = [cTs.path, bTs.path, nrefs.path, refs.path, libFile.path];
|
||||
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||
verifyScenario({
|
||||
edit: host => {
|
||||
const bTsConfigJson = JSON.parse(bTsconfig.content);
|
||||
host.ensureFileOrFolder(nrefs);
|
||||
bTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
|
||||
},
|
||||
expectedEditErrors: emptyArray,
|
||||
expectedProgramFiles,
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles: expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
||||
expectedProjectWatchedFiles: expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
||||
expectedWatchedDirectoriesRecursive: (multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
|
||||
dependencies: [
|
||||
[nrefs.path, [nrefs.path]],
|
||||
[bDts, [bDts, nrefs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bDts, nrefs.path]],
|
||||
],
|
||||
// revert the update
|
||||
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
orphanInfosAfterEdit: [aTs.path.toLowerCase()],
|
||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleting referenced config file", () => {
|
||||
const expectedProgramFiles = [cTs.path, bTs.path, refs.path, libFile.path];
|
||||
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase());
|
||||
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||
// Resolutions should change now
|
||||
// Should map to b.ts instead with options from our own config
|
||||
verifyScenario({
|
||||
edit: host => host.deleteFile(bTsconfig.path),
|
||||
schedulesFailedWatchUpdate: multiFolder,
|
||||
expectedEditErrors: [
|
||||
`${multiFolder ? "c/tsconfig.json" : "tsconfig.c.json"}(9,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "b" : "tsconfig.b.json"}' not found.\n`
|
||||
],
|
||||
expectedProgramFiles,
|
||||
expectedProjectFiles: expectedProgramFiles,
|
||||
expectedWatchedFiles,
|
||||
expectedProjectWatchedFiles: expectedWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive: multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
|
||||
dependencies: [
|
||||
[bTs.path, [bTs.path, refs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bTs.path]],
|
||||
],
|
||||
// revert the update
|
||||
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
|
||||
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||
orphanInfosAfterEdit: [aTs.path.toLowerCase(), aTsconfig.path.toLowerCase()],
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleting transitively referenced config file", () => {
|
||||
verifyScenario({
|
||||
edit: host => host.deleteFile(aTsconfig.path),
|
||||
schedulesFailedWatchUpdate: multiFolder,
|
||||
expectedEditErrors: [
|
||||
`${multiFolder ? "b/tsconfig.json" : "tsconfig.b.json"}(10,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "a" : "tsconfig.a.json"}' not found.\n`
|
||||
],
|
||||
expectedProgramFiles: expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
|
||||
expectedProjectFiles,
|
||||
expectedWatchedFiles: expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
|
||||
expectedProjectWatchedFiles,
|
||||
expectedWatchedDirectoriesRecursive,
|
||||
dependencies: [
|
||||
[aTs.path, [aTs.path]],
|
||||
[bDts, [bDts, aTs.path]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTs.path, [cTs.path, refs.path, bDts, aTs.path]],
|
||||
],
|
||||
// revert the update
|
||||
revert: host => host.writeFile(aTsconfig.path, aTsconfig.content),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("when config files are side by side", () => {
|
||||
verifyTransitiveReferences(/*multiFolder*/ false);
|
||||
|
||||
it("when referenced project uses different module resolution", () => {
|
||||
const bTs: File = {
|
||||
path: bTsFile.path,
|
||||
content: `import {A} from "a";export const b = new A();`
|
||||
};
|
||||
const bTsconfig: File = {
|
||||
path: bTsconfigFile.path,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, moduleResolution: "classic" },
|
||||
files: ["b.ts"],
|
||||
references: [{ path: "tsconfig.a.json" }]
|
||||
})
|
||||
};
|
||||
const allFiles = [libFile, aTsFile, bTs, cTsFile, aTsconfigFile, bTsconfig, cTsconfigFile, refs];
|
||||
const aDts = dtsFile("a"), bDts = dtsFile("b");
|
||||
const expectedFiles = [jsFile("a"), aDts, jsFile("b"), bDts, jsFile("c")];
|
||||
const expectedProgramFiles = [cTsFile.path, libFile.path, aDts, refs.path, bDts];
|
||||
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfigFile.path, bTsconfigFile.path, aTsconfigFile.path).map(s => s.toLowerCase());
|
||||
const expectedWatchedDirectoriesRecursive = [
|
||||
getFilePathInProject(project, "refs"), // Failed lookup since refs/a.ts does not exist
|
||||
...projectSystem.getTypeRootsFromLocation(getProjectPath(project))
|
||||
].map(s => s.toLowerCase());
|
||||
|
||||
const defaultDependencies: readonly [string, readonly string[]][] = [
|
||||
[aDts, [aDts]],
|
||||
[bDts, [bDts, aDts]],
|
||||
[refs.path, [refs.path]],
|
||||
[cTsFile.path, [cTsFile.path, refs.path, bDts, aDts]]
|
||||
];
|
||||
function getOutputFileStamps(host: TsBuildWatchSystem) {
|
||||
return expectedFiles.map(file => transformOutputToOutputFileStamp(file, host));
|
||||
}
|
||||
const { host, watch } = createSolutionAndWatchModeOfProject(allFiles, getProjectPath(project), "tsconfig.c.json", "tsconfig.c.json", getOutputFileStamps);
|
||||
verifyWatchState(host, watch, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, defaultDependencies);
|
||||
});
|
||||
});
|
||||
describe("when config files are in side by side folders", () => {
|
||||
verifyTransitiveReferences(/*multiFolder*/ true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
scenario,
|
||||
subScenario: "incremental updates in verbose mode",
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace ts {
|
||||
baselineSourceMap?: boolean;
|
||||
baselineReadFileCalls?: boolean;
|
||||
baselinePrograms?: boolean;
|
||||
baselineDependencies?: boolean;
|
||||
}
|
||||
|
||||
export type CommandLineProgram = [Program, EmitAndSemanticDiagnosticsBuilderProgram?];
|
||||
@@ -73,7 +74,7 @@ namespace ts {
|
||||
const {
|
||||
scenario, subScenario, buildKind,
|
||||
commandLineArgs, modifyFs,
|
||||
baselineSourceMap, baselineReadFileCalls, baselinePrograms
|
||||
baselineSourceMap, baselineReadFileCalls, baselinePrograms, baselineDependencies
|
||||
} = input;
|
||||
if (modifyFs) modifyFs(inputFs);
|
||||
inputFs.makeReadonly();
|
||||
@@ -110,7 +111,7 @@ namespace ts {
|
||||
sys.write(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}\n`);
|
||||
if (baselinePrograms) {
|
||||
const baseline: string[] = [];
|
||||
tscWatch.baselinePrograms(baseline, getPrograms);
|
||||
tscWatch.baselinePrograms(baseline, getPrograms, baselineDependencies);
|
||||
sys.write(baseline.join("\n"));
|
||||
}
|
||||
if (baselineReadFileCalls) {
|
||||
|
||||
@@ -299,6 +299,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
export interface TscWatchCheckOptions {
|
||||
baselineSourceMap?: boolean;
|
||||
baselineDependencies?: boolean;
|
||||
}
|
||||
export interface TscWatchCompileBase extends TscWatchCheckOptions {
|
||||
scenario: string;
|
||||
@@ -323,7 +324,7 @@ namespace ts.tscWatch {
|
||||
const {
|
||||
scenario, subScenario,
|
||||
commandLineArgs, changes,
|
||||
baselineSourceMap
|
||||
baselineSourceMap, baselineDependencies
|
||||
} = input;
|
||||
|
||||
if (!isWatch(commandLineArgs)) sys.exit = exitCode => sys.exitCode = exitCode;
|
||||
@@ -342,6 +343,7 @@ namespace ts.tscWatch {
|
||||
oldSnap,
|
||||
getPrograms,
|
||||
baselineSourceMap,
|
||||
baselineDependencies,
|
||||
changes,
|
||||
watchOrSolution
|
||||
});
|
||||
@@ -381,7 +383,7 @@ namespace ts.tscWatch {
|
||||
export function runWatchBaseline({
|
||||
scenario, subScenario, commandLineArgs,
|
||||
getPrograms, sys, baseline, oldSnap,
|
||||
baselineSourceMap,
|
||||
baselineSourceMap, baselineDependencies,
|
||||
changes, watchOrSolution
|
||||
}: RunWatchBaseline) {
|
||||
baseline.push(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}`);
|
||||
@@ -390,7 +392,8 @@ namespace ts.tscWatch {
|
||||
getPrograms,
|
||||
sys,
|
||||
oldSnap,
|
||||
baselineSourceMap
|
||||
baselineSourceMap,
|
||||
baselineDependencies,
|
||||
});
|
||||
|
||||
for (const { caption, change, timeouts } of changes) {
|
||||
@@ -401,7 +404,8 @@ namespace ts.tscWatch {
|
||||
getPrograms,
|
||||
sys,
|
||||
oldSnap,
|
||||
baselineSourceMap
|
||||
baselineSourceMap,
|
||||
baselineDependencies,
|
||||
});
|
||||
}
|
||||
Harness.Baseline.runBaseline(`${isBuild(commandLineArgs) ?
|
||||
@@ -420,10 +424,10 @@ namespace ts.tscWatch {
|
||||
export interface WatchBaseline extends Baseline, TscWatchCheckOptions {
|
||||
getPrograms: () => readonly CommandLineProgram[];
|
||||
}
|
||||
export function watchBaseline({ baseline, getPrograms, sys, oldSnap, baselineSourceMap }: WatchBaseline) {
|
||||
export function watchBaseline({ baseline, getPrograms, sys, oldSnap, baselineSourceMap, baselineDependencies }: WatchBaseline) {
|
||||
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
|
||||
sys.serializeOutput(baseline);
|
||||
const programs = baselinePrograms(baseline, getPrograms);
|
||||
const programs = baselinePrograms(baseline, getPrograms, baselineDependencies);
|
||||
sys.serializeWatches(baseline);
|
||||
baseline.push(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}`, "");
|
||||
sys.diff(baseline, oldSnap);
|
||||
@@ -434,15 +438,15 @@ namespace ts.tscWatch {
|
||||
return programs;
|
||||
}
|
||||
|
||||
export function baselinePrograms(baseline: string[], getPrograms: () => readonly CommandLineProgram[]) {
|
||||
export function baselinePrograms(baseline: string[], getPrograms: () => readonly CommandLineProgram[], baselineDependencies: boolean | undefined) {
|
||||
const programs = getPrograms();
|
||||
for (const program of programs) {
|
||||
baselineProgram(baseline, program);
|
||||
baselineProgram(baseline, program, baselineDependencies);
|
||||
}
|
||||
return programs;
|
||||
}
|
||||
|
||||
function baselineProgram(baseline: string[], [program, builderProgram]: CommandLineProgram) {
|
||||
function baselineProgram(baseline: string[], [program, builderProgram]: CommandLineProgram, baselineDependencies: boolean | undefined) {
|
||||
const options = program.getCompilerOptions();
|
||||
baseline.push(`Program root files: ${JSON.stringify(program.getRootFileNames())}`);
|
||||
baseline.push(`Program options: ${JSON.stringify(options)}`);
|
||||
@@ -466,6 +470,15 @@ namespace ts.tscWatch {
|
||||
baseline.push("No cached semantic diagnostics in the builder::");
|
||||
}
|
||||
baseline.push("");
|
||||
if (!baselineDependencies) return;
|
||||
baseline.push("Dependencies for::");
|
||||
for (const file of builderProgram.getSourceFiles()) {
|
||||
baseline.push(`${file.fileName}:`);
|
||||
for (const depenedency of builderProgram.getAllDependencies(file)) {
|
||||
baseline.push(` ${depenedency}`);
|
||||
}
|
||||
}
|
||||
baseline.push("");
|
||||
}
|
||||
|
||||
export interface VerifyTscWatch extends TscWatchCompile {
|
||||
@@ -492,4 +505,31 @@ namespace ts.tscWatch {
|
||||
const content = Debug.checkDefined(sys.readFile(file));
|
||||
sys.writeFile(file, content.replace(searchValue, replaceValue));
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: readonly string[], defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderHost(system);
|
||||
return ts.createSolutionBuilder(host, rootNames, defaultOptions || {});
|
||||
}
|
||||
|
||||
export function ensureErrorFreeBuild(host: WatchedSystem, rootNames: readonly string[]) {
|
||||
// ts build should succeed
|
||||
const solutionBuilder = createSolutionBuilder(host, rootNames, {});
|
||||
solutionBuilder.build();
|
||||
assert.equal(host.getOutput().length, 0, JSON.stringify(host.getOutput(), /*replacer*/ undefined, " "));
|
||||
}
|
||||
|
||||
export function createSystemWithSolutionBuild(solutionRoots: readonly string[], files: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
|
||||
const sys = createWatchedSystem(files, params);
|
||||
const originalReadFile = sys.readFile;
|
||||
const originalWrite = sys.write;
|
||||
const originalWriteFile = sys.writeFile;
|
||||
const solutionBuilder = createSolutionBuilder(TestFSWithWatch.changeToHostTrackingWrittenFiles(
|
||||
fakes.patchHostForBuildInfoReadWrite(sys)
|
||||
), solutionRoots, {});
|
||||
solutionBuilder.build();
|
||||
sys.readFile = originalReadFile;
|
||||
sys.write = originalWrite;
|
||||
sys.writeFile = originalWriteFile;
|
||||
return sys;
|
||||
}
|
||||
}
|
||||
|
||||
299
src/testRunner/unittests/tscWatch/projectsWithReferences.ts
Normal file
299
src/testRunner/unittests/tscWatch/projectsWithReferences.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("unittests:: tsc-watch:: projects with references: invoking when references are already built", () => {
|
||||
verifyTscWatch({
|
||||
scenario: "projectsWithReferences",
|
||||
subScenario: "on sample project",
|
||||
sys: () => createSystemWithSolutionBuild(
|
||||
["tests"],
|
||||
[
|
||||
libFile,
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/tsconfig.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/index.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/anotherModule.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/some_decl.d.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/tsconfig.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/index.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "tests/tsconfig.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("sample1", "tests/index.ts"),
|
||||
],
|
||||
{ currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/sample1` }
|
||||
),
|
||||
commandLineArgs: ["-w", "-p", "tests"],
|
||||
changes: [
|
||||
{
|
||||
caption: "local edit in logic ts, and build logic",
|
||||
change: sys => {
|
||||
sys.appendFile(TestFSWithWatch.getTsBuildProjectFilePath("sample1", "logic/index.ts"), `function foo() { }`);
|
||||
const solutionBuilder = createSolutionBuilder(sys, ["logic"]);
|
||||
solutionBuilder.build();
|
||||
},
|
||||
// not ideal, but currently because of d.ts but no new file is written
|
||||
// There will be timeout queued even though file contents are same
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "non local edit in logic ts, and build logic",
|
||||
change: sys => {
|
||||
sys.appendFile(TestFSWithWatch.getTsBuildProjectFilePath("sample1", "logic/index.ts"), `export function gfoo() { }`);
|
||||
const solutionBuilder = createSolutionBuilder(sys, ["logic"]);
|
||||
solutionBuilder.build();
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "change in project reference config file builds correctly",
|
||||
change: sys => {
|
||||
sys.writeFile(TestFSWithWatch.getTsBuildProjectFilePath("sample1", "logic/tsconfig.json"), JSON.stringify({
|
||||
compilerOptions: { composite: true, declaration: true, declarationDir: "decls" },
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
const solutionBuilder = createSolutionBuilder(sys, ["logic"]);
|
||||
solutionBuilder.build();
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
],
|
||||
baselineDependencies: true
|
||||
});
|
||||
|
||||
function changeCompilerOpitonsPaths(sys: WatchedSystem, config: string, newPaths: object) {
|
||||
const configJson = JSON.parse(sys.readFile(config)!);
|
||||
configJson.compilerOptions.paths = newPaths;
|
||||
sys.writeFile(config, JSON.stringify(configJson));
|
||||
}
|
||||
|
||||
verifyTscWatch({
|
||||
scenario: "projectsWithReferences",
|
||||
subScenario: "on transitive references",
|
||||
sys: () => createSystemWithSolutionBuild(
|
||||
["tsconfig.c.json"],
|
||||
[
|
||||
libFile,
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.a.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.b.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.c.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "a.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "b.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "c.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "refs/a.d.ts"),
|
||||
],
|
||||
{ currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/transitiveReferences` }
|
||||
),
|
||||
commandLineArgs: ["-w", "-p", "tsconfig.c.json"],
|
||||
changes: [
|
||||
{
|
||||
caption: "non local edit b ts, and build b",
|
||||
change: sys => {
|
||||
sys.appendFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b.ts"), `export function gfoo() { }`);
|
||||
const solutionBuilder = createSolutionBuilder(sys, ["tsconfig.b.json"]);
|
||||
solutionBuilder.build();
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "edit on config file",
|
||||
change: sys => {
|
||||
sys.ensureFileOrFolder({
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "nrefs/a.d.ts"),
|
||||
content: sys.readFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "refs/a.d.ts"))!
|
||||
});
|
||||
changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.c.json"), { "@ref/*": ["./nrefs/*"] });
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert config file edit",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.c.json"), { "@ref/*": ["./refs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "edit in referenced config file",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.b.json"), { "@ref/*": ["./nrefs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert referenced config file edit",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.b.json"), { "@ref/*": ["./refs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "deleting referenced config file",
|
||||
change: sys => sys.deleteFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.b.json")),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert deleting referenced config file",
|
||||
change: sys => sys.ensureFileOrFolder(TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.b.json")),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "deleting transitively referenced config file",
|
||||
change: sys => sys.deleteFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.a.json")),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert deleting transitively referenced config file",
|
||||
change: sys => sys.ensureFileOrFolder(TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.a.json")),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
],
|
||||
baselineDependencies: true,
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
scenario: "projectsWithReferences",
|
||||
subScenario: "when referenced project uses different module resolution",
|
||||
sys: () => createSystemWithSolutionBuild(
|
||||
["tsconfig.c.json"],
|
||||
[
|
||||
libFile,
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.a.json"),
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "tsconfig.b.json"),
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, moduleResolution: "classic" },
|
||||
files: ["b.ts"],
|
||||
references: [{ path: "tsconfig.a.json" }]
|
||||
})
|
||||
},
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "tsconfig.c.json"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "a.ts"),
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b.ts"),
|
||||
content: `import {A} from "a";export const b = new A();`
|
||||
},
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "c.ts"),
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "refs/a.d.ts"),
|
||||
],
|
||||
{ currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/transitiveReferences` }
|
||||
),
|
||||
commandLineArgs: ["-w", "-p", "tsconfig.c.json"],
|
||||
changes: emptyArray,
|
||||
baselineDependencies: true,
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
scenario: "projectsWithReferences",
|
||||
subScenario: "on transitive references in different folders",
|
||||
sys: () => createSystemWithSolutionBuild(
|
||||
["c"],
|
||||
[
|
||||
libFile,
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "a/tsconfig.json"),
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true },
|
||||
files: ["index.ts"]
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/tsconfig.json"),
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, baseUrl: "./", paths: { "@ref/*": ["../*"] } },
|
||||
files: ["index.ts"],
|
||||
references: [{ path: `../a` }]
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "c/tsconfig.json"),
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { baseUrl: "./", paths: { "@ref/*": ["../refs/*"] } },
|
||||
files: ["index.ts"],
|
||||
references: [{ path: `../b` }]
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "a/index.ts"),
|
||||
content: `export class A {}`,
|
||||
},
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/index.ts"),
|
||||
content: `import {A} from '@ref/a';
|
||||
export const b = new A();`,
|
||||
},
|
||||
{
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "c/index.ts"),
|
||||
content: `import {b} from '../b';
|
||||
import {X} from "@ref/a";
|
||||
b;
|
||||
X;`,
|
||||
},
|
||||
TestFSWithWatch.getTsBuildProjectFile("transitiveReferences", "refs/a.d.ts"),
|
||||
],
|
||||
{ currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/transitiveReferences` }
|
||||
),
|
||||
commandLineArgs: ["-w", "-p", "c"],
|
||||
changes: [
|
||||
{
|
||||
caption: "non local edit b ts, and build b",
|
||||
change: sys => {
|
||||
sys.appendFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/index.ts"), `export function gfoo() { }`);
|
||||
const solutionBuilder = createSolutionBuilder(sys, ["b"]);
|
||||
solutionBuilder.build();
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "edit on config file",
|
||||
change: sys => {
|
||||
sys.ensureFileOrFolder({
|
||||
path: TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "nrefs/a.d.ts"),
|
||||
content: sys.readFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "refs/a.d.ts"))!
|
||||
});
|
||||
changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "c/tsconfig.json"), { "@ref/*": ["../nrefs/*"] });
|
||||
},
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert config file edit",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "c/tsconfig.json"), { "@ref/*": ["../refs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "edit in referenced config file",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/tsconfig.json"), { "@ref/*": ["../nrefs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "Revert referenced config file edit",
|
||||
change: sys => changeCompilerOpitonsPaths(sys, TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/tsconfig.json"), { "@ref/*": ["../refs/*"] }),
|
||||
timeouts: checkSingleTimeoutQueueLengthAndRun
|
||||
},
|
||||
{
|
||||
caption: "deleting referenced config file",
|
||||
change: sys => sys.deleteFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/tsconfig.json")),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2)
|
||||
},
|
||||
{
|
||||
caption: "Revert deleting referenced config file",
|
||||
change: sys => sys.writeFile(
|
||||
TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "b/tsconfig.json"),
|
||||
JSON.stringify({
|
||||
compilerOptions: { composite: true, baseUrl: "./", paths: { "@ref/*": ["../*"] } },
|
||||
files: ["index.ts"],
|
||||
references: [{ path: `../a` }]
|
||||
})
|
||||
),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2)
|
||||
},
|
||||
{
|
||||
caption: "deleting transitively referenced config file",
|
||||
change: sys => sys.deleteFile(TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "a/tsconfig.json")),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2)
|
||||
},
|
||||
{
|
||||
caption: "Revert deleting transitively referenced config file",
|
||||
change: sys => sys.writeFile(
|
||||
TestFSWithWatch.getTsBuildProjectFilePath("transitiveReferences", "a/tsconfig.json"),
|
||||
JSON.stringify({
|
||||
compilerOptions: { composite: true },
|
||||
files: ["index.ts"]
|
||||
}),
|
||||
),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2)
|
||||
},
|
||||
],
|
||||
baselineDependencies: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -440,6 +440,13 @@ namespace ts.projectSystem {
|
||||
return iterResult.value;
|
||||
}
|
||||
|
||||
export function checkOrphanScriptInfos(service: server.ProjectService, expectedFiles: readonly string[]) {
|
||||
checkArray("Orphan ScriptInfos:", arrayFrom(mapDefinedIterator(
|
||||
service.filenameToScriptInfo.values(),
|
||||
v => v.containingProjects.length === 0 ? v.fileName : undefined
|
||||
)), expectedFiles);
|
||||
}
|
||||
|
||||
export function checkProjectActualFiles(project: server.Project, expectedFiles: readonly string[]) {
|
||||
checkArray(`${server.ProjectKind[project.projectKind]} project: ${project.getProjectName()}:: actual files`, project.getFileNames(), expectedFiles);
|
||||
}
|
||||
|
||||
287
src/testRunner/unittests/tsserver/projectsWithReferences.ts
Normal file
287
src/testRunner/unittests/tsserver/projectsWithReferences.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: projects with references: invoking when references are already built", () => {
|
||||
it("on sample project", () => {
|
||||
const coreConfig = TestFSWithWatch.getTsBuildProjectFile("sample1", "core/tsconfig.json");
|
||||
const coreIndex = TestFSWithWatch.getTsBuildProjectFile("sample1", "core/index.ts");
|
||||
const coreAnotherModule = TestFSWithWatch.getTsBuildProjectFile("sample1", "core/anotherModule.ts");
|
||||
const coreSomeDecl = TestFSWithWatch.getTsBuildProjectFile("sample1", "core/some_decl.d.ts");
|
||||
const logicConfig = TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/tsconfig.json");
|
||||
const logicIndex = TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/index.ts");
|
||||
const testsConfig = TestFSWithWatch.getTsBuildProjectFile("sample1", "tests/tsconfig.json");
|
||||
const testsIndex = TestFSWithWatch.getTsBuildProjectFile("sample1", "tests/index.ts");
|
||||
const host = createServerHost([libFile, coreConfig, coreIndex, coreAnotherModule, coreSomeDecl, logicConfig, logicIndex, testsConfig, testsIndex]);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(testsIndex.path);
|
||||
|
||||
checkWatchedFilesDetailed(host, [coreConfig, coreIndex, coreAnotherModule, logicConfig, logicIndex, testsConfig, libFile].map(f => f.path.toLowerCase()), 1);
|
||||
checkWatchedDirectoriesDetailed(host, emptyArray, 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, getTypeRootsFromLocation(TestFSWithWatch.getTsBuildProjectFilePath("sample1", "tests")), 1, /*recursive*/ true);
|
||||
|
||||
// local edit in ts file
|
||||
host.appendFile(logicIndex.path, `function foo() {}`);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(testsConfig.path)!, [libFile.path, coreIndex.path, coreAnotherModule.path, logicIndex.path, testsIndex.path, testsConfig.path]);
|
||||
|
||||
// non local edit in ts file
|
||||
host.appendFile(logicIndex.path, `export function gfoo() {}`);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(testsConfig.path)!, [libFile.path, coreIndex.path, coreAnotherModule.path, logicIndex.path, testsIndex.path, testsConfig.path]);
|
||||
|
||||
// change in project reference config file
|
||||
host.writeFile(logicConfig.path, JSON.stringify({
|
||||
compilerOptions: { composite: true, declaration: true, declarationDir: "decls" },
|
||||
references: [{ path: "../core" }]
|
||||
}));
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(testsConfig.path)!, [libFile.path, coreIndex.path, coreAnotherModule.path, logicIndex.path, testsIndex.path, testsConfig.path]);
|
||||
});
|
||||
|
||||
describe("on transitive references in different folders", () => {
|
||||
function createService() {
|
||||
const aConfig: File = {
|
||||
path: `${tscWatch.projectRoot}/a/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true },
|
||||
files: ["index.ts"]
|
||||
}),
|
||||
};
|
||||
const bConfig: File = {
|
||||
path: `${tscWatch.projectRoot}/b/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { composite: true, baseUrl: "./", paths: { "@ref/*": ["../*"] } },
|
||||
files: ["index.ts"],
|
||||
references: [{ path: `../a` }]
|
||||
}),
|
||||
};
|
||||
const cConfig: File = {
|
||||
path: `${tscWatch.projectRoot}/c/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { baseUrl: "./", paths: { "@ref/*": ["../refs/*"] } },
|
||||
files: ["index.ts"],
|
||||
references: [{ path: `../b` }]
|
||||
}),
|
||||
};
|
||||
const aTs: File = {
|
||||
path: `${tscWatch.projectRoot}/a/index.ts`,
|
||||
content: `export class A {}`,
|
||||
};
|
||||
const bTs: File = {
|
||||
path: `${tscWatch.projectRoot}/b/index.ts`,
|
||||
content: `import {A} from '@ref/a';
|
||||
export const b = new A();`,
|
||||
};
|
||||
const cTs: File = {
|
||||
path: `${tscWatch.projectRoot}/c/index.ts`,
|
||||
content: `import {b} from '../b';
|
||||
import {X} from "@ref/a";
|
||||
b;
|
||||
X;`,
|
||||
};
|
||||
const refsTs: File = {
|
||||
path: `${tscWatch.projectRoot}/refs/a.d.ts`,
|
||||
content: `export class X {}
|
||||
export class A {}`
|
||||
};
|
||||
const host = createServerHost([libFile, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs]);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(cTs.path);
|
||||
return { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs };
|
||||
}
|
||||
|
||||
it("non local edit", () => {
|
||||
const { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs } = createService();
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
checkOrphanScriptInfos(service, emptyArray);
|
||||
|
||||
// non local edit
|
||||
host.appendFile(bTs.path, `export function gFoo() { }`);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
checkOrphanScriptInfos(service, emptyArray);
|
||||
});
|
||||
|
||||
it("edit on config file", () => {
|
||||
const { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs } = createService();
|
||||
const nRefsTs: File = {
|
||||
path: `${tscWatch.projectRoot}/nrefs/a.d.ts`,
|
||||
content: refsTs.content
|
||||
};
|
||||
const cTsConfigJson = JSON.parse(cConfig.content);
|
||||
host.ensureFileOrFolder(nRefsTs);
|
||||
cTsConfigJson.compilerOptions.paths = { "@ref/*": ["../nrefs/*"] };
|
||||
host.writeFile(cConfig.path, JSON.stringify(cTsConfigJson));
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, nRefsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path, nRefsTs.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/nrefs`, // Failed lookup since nrefs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
// Script infos arent deleted till next file open
|
||||
checkOrphanScriptInfos(service, [refsTs.path]);
|
||||
|
||||
// revert the edit on config file
|
||||
host.writeFile(cConfig.path, cConfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path, nRefsTs.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
// Script infos arent deleted till next file open
|
||||
checkOrphanScriptInfos(service, [nRefsTs.path]);
|
||||
});
|
||||
|
||||
it("edit in referenced config file", () => {
|
||||
const { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs } = createService();
|
||||
const nRefsTs: File = {
|
||||
path: `${tscWatch.projectRoot}/nrefs/a.d.ts`,
|
||||
content: refsTs.content
|
||||
};
|
||||
const bTsConfigJson = JSON.parse(bConfig.content);
|
||||
host.ensureFileOrFolder(nRefsTs);
|
||||
bTsConfigJson.compilerOptions.paths = { "@ref/*": ["../nrefs/*"] };
|
||||
host.writeFile(bConfig.path, JSON.stringify(bTsConfigJson));
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, refsTs.path, nRefsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path, nRefsTs.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
`${tscWatch.projectRoot}/nrefs`, // Failed lookup since nrefs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
// Script infos arent deleted till next file open
|
||||
checkOrphanScriptInfos(service, [aTs.path]);
|
||||
|
||||
// revert the edit on config file
|
||||
host.writeFile(bConfig.path, bConfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path, nRefsTs.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
// Script infos arent deleted till next file open
|
||||
checkOrphanScriptInfos(service, [nRefsTs.path]);
|
||||
});
|
||||
|
||||
it("deleting referenced config file", () => {
|
||||
const { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs } = createService();
|
||||
host.deleteFile(bConfig.path);
|
||||
host.checkTimeoutQueueLengthAndRun(3); // Schedules failed lookup invalidation
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
// Script infos arent deleted till next file open
|
||||
checkOrphanScriptInfos(service, [aTs.path, aConfig.path]);
|
||||
|
||||
// revert
|
||||
host.writeFile(bConfig.path, bConfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(3); // Schedules failed lookup invalidation
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
checkOrphanScriptInfos(service, emptyArray);
|
||||
});
|
||||
|
||||
it("deleting transitively referenced config file", () => {
|
||||
const { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs } = createService();
|
||||
host.deleteFile(aConfig.path);
|
||||
host.checkTimeoutQueueLengthAndRun(3); // Schedules failed lookup invalidation
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
checkOrphanScriptInfos(service, emptyArray);
|
||||
|
||||
// revert
|
||||
host.writeFile(aConfig.path, aConfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(3); // Schedules failed lookup invalidation
|
||||
checkProjectActualFiles(service.configuredProjects.get(cConfig.path)!, [libFile.path, cTs.path, cConfig.path, bTs.path, aTs.path, refsTs.path]);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, aTs.path, bTs.path, refsTs.path, aConfig.path, bConfig.path, cConfig.path], 1);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
tscWatch.projectRoot // watches for directories created for resolution of b
|
||||
], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [
|
||||
`${tscWatch.projectRoot}/a`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/b`, // Failed to package json
|
||||
`${tscWatch.projectRoot}/refs`, // Failed lookup since refs/a.ts does not exist
|
||||
...getTypeRootsFromLocation(`${tscWatch.projectRoot}/c`)
|
||||
], 1, /*recursive*/ true);
|
||||
checkOrphanScriptInfos(service, emptyArray);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user