Skip to content

Commit cfbbe65

Browse files
committed
fix: prevent infinite rebuild loop by excluding output path
1 parent 8a425a1 commit cfbbe65

File tree

1 file changed

+51
-3
lines changed

1 file changed

+51
-3
lines changed

src/hooks/tap-after-compile-to-add-dependencies.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
11
import type * as rspack from '@rspack/core';
2-
32
import { getInfrastructureLogger } from '../infrastructure-logger';
43
import type { TsCheckerRspackPluginConfig } from '../plugin-config';
54
import type { TsCheckerRspackPluginState } from '../plugin-state';
5+
import { sep } from 'node:path';
6+
7+
const addTrailingSep = (dir: string) => (dir.endsWith(sep) ? dir : dir + sep);
8+
9+
const isStrictSubdir = (parent: string, child: string) => {
10+
const parentDir = addTrailingSep(parent);
11+
const childDir = addTrailingSep(child);
12+
return parentDir !== childDir && childDir.startsWith(parentDir);
13+
};
14+
15+
/**
16+
* Excludes the Rspack output directory from watched dependencies
17+
* when the entire project is being watched.
18+
*
19+
* If `writeToDisk` is enabled and the build output path is not ignored,
20+
* emitted files in `output.path` may leading to an infinite rebuild loop.
21+
**/
22+
function excludeOutputPath(
23+
dependencies: NonNullable<TsCheckerRspackPluginState['lastDependencies']>,
24+
compiler: rspack.Compiler
25+
) {
26+
const isContextIncluded = dependencies.dirs.includes(compiler.context);
27+
if (!isContextIncluded) {
28+
return dependencies;
29+
}
30+
31+
const outputPath =
32+
typeof compiler.options.output?.path === 'string'
33+
? compiler.options.output.path
34+
: compiler.outputPath;
35+
if (
36+
!outputPath ||
37+
// If output path is not a subdir of context, no need to exclude it
38+
!isStrictSubdir(compiler.context, outputPath) ||
39+
// If output path is explicitly included in dirs, should not exclude it
40+
dependencies.dirs.includes(outputPath)
41+
) {
42+
return dependencies;
43+
}
44+
45+
const excluded = new Set(dependencies.excluded);
46+
excluded.add(outputPath);
47+
48+
return {
49+
...dependencies,
50+
excluded: Array.from(excluded),
51+
};
52+
}
653

754
function tapAfterCompileToAddDependencies(
855
compiler: rspack.Compiler,
@@ -21,9 +68,10 @@ function tapAfterCompileToAddDependencies(
2168

2269
debug(`Got dependencies from the getDependenciesWorker.`, dependencies);
2370
if (dependencies) {
24-
state.lastDependencies = dependencies;
71+
const sanitizedDependencies = excludeOutputPath(dependencies, compiler);
72+
state.lastDependencies = sanitizedDependencies;
2573

26-
dependencies.files.forEach((file) => {
74+
sanitizedDependencies.files.forEach((file) => {
2775
compilation.fileDependencies.add(file);
2876
});
2977
}

0 commit comments

Comments
 (0)