Skip to content

Commit ea20bcd

Browse files
committed
fix: irrelevant files are watched if they have the same prefix as app directory
For example, if app directory is `/workspace/app`, `/workspace/apps/irrelevant/file` should be ignored.
1 parent 509dd49 commit ea20bcd

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pathStartsWith from "../config/path-starts-with";
2+
3+
describe("pathStartsWith", () => {
4+
it("real world example", () => {
5+
const APP_FOLDER = "/workspace/app";
6+
expect(pathStartsWith("/workspace/app/root.tsx", APP_FOLDER)).toBe(true);
7+
8+
const IRRELEVANT_FILE = "/workspace/apps/irrelevant-project/package.json";
9+
expect(IRRELEVANT_FILE.startsWith(APP_FOLDER)).toBe(true);
10+
expect(pathStartsWith(IRRELEVANT_FILE, APP_FOLDER)).toBe(false);
11+
});
12+
13+
it("edge cases", () => {
14+
expect(pathStartsWith("./dir", "./dir")).toBe(true);
15+
expect(pathStartsWith("./dir/", "./dir")).toBe(true);
16+
expect(pathStartsWith("./dir/path", "./dir")).toBe(true);
17+
expect(pathStartsWith("./dir/path", "./dir/")).toBe(true);
18+
expect(pathStartsWith("./dir/path/", "./dir")).toBe(true);
19+
expect(pathStartsWith("./dir/path/", "./dir/")).toBe(true);
20+
21+
expect(pathStartsWith("dir", "dir")).toBe(true);
22+
expect(pathStartsWith("dir/", "dir")).toBe(true);
23+
expect(pathStartsWith("dir/path", "dir")).toBe(true);
24+
expect(pathStartsWith("dir/path", "dir/")).toBe(true);
25+
expect(pathStartsWith("dir/path/", "dir")).toBe(true);
26+
expect(pathStartsWith("dir/path/", "dir/")).toBe(true);
27+
28+
expect(pathStartsWith("/dir", "/dir")).toBe(true);
29+
expect(pathStartsWith("/dir/", "/dir")).toBe(true);
30+
expect(pathStartsWith("/dir/path", "/dir")).toBe(true);
31+
expect(pathStartsWith("/dir/path", "/dir/")).toBe(true);
32+
expect(pathStartsWith("/dir/path/", "/dir")).toBe(true);
33+
expect(pathStartsWith("/dir/path/", "/dir/")).toBe(true);
34+
});
35+
36+
it("paths are not normalized intentionally", () => {
37+
expect(pathStartsWith("./dir/path", "dir")).toBe(false);
38+
expect(pathStartsWith("/dir/a/b/c/../../..", "/dir/a/b/c")).toBe(true);
39+
});
40+
});

packages/react-router-dev/config/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
configRoutesToRouteManifest,
2323
} from "./routes";
2424
import { detectPackageManager } from "../cli/detectPackageManager";
25+
import pathStartsWith from "./path-starts-with";
2526

2627
const excludedConfigPresetKeys = ["presets"] as const satisfies ReadonlyArray<
2728
keyof ReactRouterConfig
@@ -686,7 +687,7 @@ export async function createConfigLoader({
686687
let dirname = Path.dirname(path);
687688

688689
return (
689-
!dirname.startsWith(appDirectory) &&
690+
!pathStartsWith(dirname, appDirectory) &&
690691
// Ensure we're only watching files outside of the app directory
691692
// that are at the root level, not nested in subdirectories
692693
path !== root && // Watch the root directory itself
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Returns true if `a` is a path that starts with `b` and might contains subpath.
3+
*
4+
* Note that `a` and `b` will not be normalized
5+
* so the returned boolean doesn't indicate whether `a` resolves to a path contained in `b`.
6+
*/
7+
export default function pathStartsWith(a: string, b: string) {
8+
return (
9+
a.startsWith(b) &&
10+
// they are the same string
11+
(a.length === b.length ||
12+
// or b is a directory path
13+
b.endsWith("/") ||
14+
// or a is `${b}/${subpath}`
15+
a[b.length] === "/")
16+
);
17+
}

0 commit comments

Comments
 (0)