mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 03:20:56 -06:00
Remove project status, watches etc when project is no longer part of build order
This commit is contained in:
parent
4efcfb7120
commit
2db8a13d81
@ -564,8 +564,51 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getBuildOrder(state: SolutionBuilderState) {
|
||||
return state.buildOrder ||
|
||||
(state.buildOrder = createBuildOrder(state, state.rootNames.map(f => resolveProjectName(state, f))));
|
||||
return state.buildOrder || createStateBuildOrder(state);
|
||||
}
|
||||
|
||||
function createStateBuildOrder(state: SolutionBuilderState) {
|
||||
const buildOrder = createBuildOrder(state, state.rootNames.map(f => resolveProjectName(state, f)));
|
||||
if (arrayIsEqualTo(state.buildOrder, buildOrder)) return state.buildOrder!;
|
||||
|
||||
// Clear all to ResolvedConfigFilePaths cache to start fresh
|
||||
state.resolvedConfigFilePaths.clear();
|
||||
const currentProjects = arrayToSet(
|
||||
buildOrder,
|
||||
resolved => toResolvedConfigFilePath(state, resolved)
|
||||
) as ConfigFileMap<true>;
|
||||
|
||||
const noopOnDelete = { onDeleteValue: noop };
|
||||
// Config file cache
|
||||
mutateMapSkippingNewValues(state.configFileCache, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.projectStatus, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.buildInfoChecked, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.builderPrograms, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.diagnostics, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.projectPendingBuild, currentProjects, noopOnDelete);
|
||||
mutateMapSkippingNewValues(state.projectErrorsReported, currentProjects, noopOnDelete);
|
||||
|
||||
// Remove watches for the program no longer in the solution
|
||||
if (state.watch) {
|
||||
mutateMapSkippingNewValues(
|
||||
state.allWatchedConfigFiles,
|
||||
currentProjects,
|
||||
{ onDeleteValue: closeFileWatcher }
|
||||
);
|
||||
|
||||
mutateMapSkippingNewValues(
|
||||
state.allWatchedWildcardDirectories,
|
||||
currentProjects,
|
||||
{ onDeleteValue: existingMap => existingMap.forEach(closeFileWatcherOf) }
|
||||
);
|
||||
|
||||
mutateMapSkippingNewValues(
|
||||
state.allWatchedInputFiles,
|
||||
currentProjects,
|
||||
{ onDeleteValue: existingMap => existingMap.forEach(closeFileWatcher) }
|
||||
);
|
||||
}
|
||||
return state.buildOrder = buildOrder;
|
||||
}
|
||||
|
||||
function getBuildOrderFor(state: SolutionBuilderState, project: string | undefined, onlyReferences: boolean | undefined) {
|
||||
|
||||
@ -4466,8 +4466,7 @@ namespace ts {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
export interface MutateMapOptions<T, U> {
|
||||
createNewValue(key: string, valueInNewMap: U): T;
|
||||
export interface MutateMapSkippingNewValuesOptions<T, U> {
|
||||
onDeleteValue(existingValue: T, key: string): void;
|
||||
|
||||
/**
|
||||
@ -4482,8 +4481,12 @@ namespace ts {
|
||||
/**
|
||||
* Mutates the map with newMap such that keys in map will be same as newMap.
|
||||
*/
|
||||
export function mutateMap<T, U>(map: Map<T>, newMap: ReadonlyMap<U>, options: MutateMapOptions<T, U>) {
|
||||
const { createNewValue, onDeleteValue, onExistingValue } = options;
|
||||
export function mutateMapSkippingNewValues<T, U>(
|
||||
map: Map<T>,
|
||||
newMap: ReadonlyMap<U>,
|
||||
options: MutateMapSkippingNewValuesOptions<T, U>
|
||||
) {
|
||||
const { onDeleteValue, onExistingValue } = options;
|
||||
// Needs update
|
||||
map.forEach((existingValue, key) => {
|
||||
const valueInNewMap = newMap.get(key);
|
||||
@ -4497,7 +4500,20 @@ namespace ts {
|
||||
onExistingValue(existingValue, valueInNewMap, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface MutateMapOptions<T, U> extends MutateMapSkippingNewValuesOptions<T, U> {
|
||||
createNewValue(key: string, valueInNewMap: U): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutates the map with newMap such that keys in map will be same as newMap.
|
||||
*/
|
||||
export function mutateMap<T, U>(map: Map<T>, newMap: ReadonlyMap<U>, options: MutateMapOptions<T, U>) {
|
||||
// Needs update
|
||||
mutateMapSkippingNewValues(map, newMap, options);
|
||||
|
||||
const { createNewValue } = options;
|
||||
// Add new values that are not already present
|
||||
newMap.forEach((valueInNewMap, key) => {
|
||||
if (!map.has(key)) {
|
||||
|
||||
@ -405,7 +405,7 @@ interface Array<T> {}`
|
||||
return s;
|
||||
}
|
||||
|
||||
private now() {
|
||||
now() {
|
||||
this.time += timeIncrements;
|
||||
return new Date(this.time);
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@
|
||||
"unittests/tsbuild/resolveJsonModule.ts",
|
||||
"unittests/tsbuild/sample.ts",
|
||||
"unittests/tsbuild/transitiveReferences.ts",
|
||||
"unittests/tsbuild/watchEnvironment.ts",
|
||||
"unittests/tsbuild/watchMode.ts",
|
||||
"unittests/tscWatch/consoleClearing.ts",
|
||||
"unittests/tscWatch/emit.ts",
|
||||
|
||||
111
src/testRunner/unittests/tsbuild/watchEnvironment.ts
Normal file
111
src/testRunner/unittests/tsbuild/watchEnvironment.ts
Normal file
@ -0,0 +1,111 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("unittests:: tsbuild:: watchEnvironment:: tsbuild:: watchMode:: with different watch environments", () => {
|
||||
it("watchFile on same file multiple times because file is part of multiple projects", () => {
|
||||
const project = `${TestFSWithWatch.tsbuildProjectsLocation}/myproject`;
|
||||
let maxPkgs = 4;
|
||||
const configPath = `${project}/tsconfig.json`;
|
||||
const typing: File = {
|
||||
path: `${project}/typings/xterm.d.ts`,
|
||||
content: "export const typing = 10;"
|
||||
};
|
||||
|
||||
const allPkgFiles = pkgs(pkgFiles);
|
||||
const system = createWatchedSystem([libFile, typing, ...flatArray(allPkgFiles)], { currentDirectory: project });
|
||||
writePkgReferences();
|
||||
const host = createSolutionBuilderWithWatchHost(system);
|
||||
const solutionBuilder = createSolutionBuilderWithWatch(host, ["tsconfig.json"], { watch: true, verbose: true });
|
||||
solutionBuilder.build();
|
||||
checkOutputErrorsInitial(system, emptyArray, /*disableConsoleClears*/ undefined, [
|
||||
`Projects in this build: \r\n${
|
||||
concatenate(
|
||||
pkgs(index => ` * pkg${index}/tsconfig.json`),
|
||||
[" * tsconfig.json"]
|
||||
).join("\r\n")}\n\n`,
|
||||
...flatArray(pkgs(index => [
|
||||
`Project 'pkg${index}/tsconfig.json' is out of date because output file 'pkg${index}/index.js' does not exist\n\n`,
|
||||
`Building project '${project}/pkg${index}/tsconfig.json'...\n\n`
|
||||
]))
|
||||
]);
|
||||
|
||||
const watchFilesDetailed = arrayToMap(flatArray(allPkgFiles), f => f.path, () => 1);
|
||||
watchFilesDetailed.set(configPath, 1);
|
||||
watchFilesDetailed.set(typing.path, maxPkgs);
|
||||
checkWatchedFilesDetailed(system, watchFilesDetailed);
|
||||
system.writeFile(typing.path, `${typing.content}export const typing1 = 10;`);
|
||||
verifyInvoke();
|
||||
|
||||
// Make change
|
||||
maxPkgs--;
|
||||
writePkgReferences();
|
||||
system.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(system, emptyArray);
|
||||
const lastFiles = last(allPkgFiles);
|
||||
lastFiles.forEach(f => watchFilesDetailed.delete(f.path));
|
||||
watchFilesDetailed.set(typing.path, maxPkgs);
|
||||
checkWatchedFilesDetailed(system, watchFilesDetailed);
|
||||
system.writeFile(typing.path, typing.content);
|
||||
verifyInvoke();
|
||||
|
||||
// Make change to remove all the watches
|
||||
maxPkgs = 0;
|
||||
writePkgReferences();
|
||||
system.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(system, [
|
||||
`tsconfig.json(1,10): error TS18002: The 'files' list in config file '${configPath}' is empty.\n`
|
||||
]);
|
||||
checkWatchedFilesDetailed(system, [configPath], 1);
|
||||
|
||||
system.writeFile(typing.path, `${typing.content}export const typing1 = 10;`);
|
||||
system.checkTimeoutQueueLength(0);
|
||||
|
||||
function flatArray<T>(arr: T[][]): readonly T[] {
|
||||
return flatMap(arr, identity);
|
||||
}
|
||||
function pkgs<T>(cb: (index: number) => T): T[] {
|
||||
const result: T[] = [];
|
||||
for (let index = 0; index < maxPkgs; index++) {
|
||||
result.push(cb(index));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function createPkgReference(index: number) {
|
||||
return { path: `./pkg${index}` };
|
||||
}
|
||||
function pkgFiles(index: number): File[] {
|
||||
return [
|
||||
{
|
||||
path: `${project}/pkg${index}/index.ts`,
|
||||
content: `export const pkg${index} = ${index};`
|
||||
},
|
||||
{
|
||||
path: `${project}/pkg${index}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
complerOptions: { composite: true },
|
||||
include: [
|
||||
"**/*.ts",
|
||||
"../typings/xterm.d.ts"
|
||||
]
|
||||
})
|
||||
}
|
||||
];
|
||||
}
|
||||
function writePkgReferences() {
|
||||
system.writeFile(configPath, JSON.stringify({
|
||||
files: [],
|
||||
include: [],
|
||||
references: pkgs(createPkgReference)
|
||||
}));
|
||||
}
|
||||
function verifyInvoke() {
|
||||
pkgs(() => system.checkTimeoutQueueLengthAndRun(1));
|
||||
checkOutputErrorsIncremental(system, emptyArray, /*disableConsoleClears*/ undefined, /*logsBeforeWatchDiagnostics*/ undefined, [
|
||||
...flatArray(pkgs(index => [
|
||||
`Project 'pkg${index}/tsconfig.json' is out of date because oldest output 'pkg${index}/index.js' is older than newest input 'typings/xterm.d.ts'\n\n`,
|
||||
`Building project '${project}/pkg${index}/tsconfig.json'...\n\n`,
|
||||
`Updating unchanged output timestamps of project '${project}/pkg${index}/tsconfig.json'...\n\n`
|
||||
]))
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -16,11 +16,19 @@ namespace ts.tscWatch {
|
||||
return host;
|
||||
}
|
||||
|
||||
|
||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderHost(system);
|
||||
host.now = system.now.bind(system);
|
||||
return ts.createSolutionBuilder(host, rootNames, defaultOptions || {});
|
||||
}
|
||||
|
||||
export function createSolutionBuilderWithWatchHost(system: WatchedSystem) {
|
||||
const host = ts.createSolutionBuilderWithWatchHost(system);
|
||||
host.now = system.now.bind(system);
|
||||
return host;
|
||||
}
|
||||
|
||||
function createSolutionBuilderWithWatch(system: TsBuildWatchSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderWithWatchHost(system);
|
||||
const solutionBuilder = ts.createSolutionBuilderWithWatch(host, rootNames, defaultOptions || { watch: true });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user