-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Description
Following up on #46673 (comment), I started a more thorough investigation of ts.matchFiles (which is just ts.sys.readDirectory without the system host) and discovered that even its long-standing behavior prior to 4.4 is confusing and probably undesigned. Consider this file system:
projects/
├─ a/
│ ├─ subfolder/
│ │ └─ 1.ts
│ └─ 2.ts
└─ b/
└─ 3.ts
Things tend to work as expected if you do three things: (1) use relative paths, (2) read the same directory as your CWD, and (3) don’t use any includes that access a sibling folder with "../". If you start doing those things, especially in combination, the behavior is a bit unpredictable.
Let’s start with a simple example where I think things are working as intended:
> process.cwd()
'/projects/a'
> ts.sys.readDirectory(".", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*"])
[
'2.ts',
'subfolder/1.ts'
]
Note that the entries are relative to our CWD / the directory we’re reading (which one? We don’t know yet because they’re the same), but without the leading ./. This seems probably intentional because it matches the format of fs.readdir. Also, replacing the first argument with the empty string yields the same results.
Ok, let’s see what happens if we include something outside of the directory we’re reading:
> process.cwd()
'/projects/a'
> ts.sys.readDirectory(".", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*", "../b/**/*"])
[
'2.ts',
'subfolder/1.ts'
]
No change. This seems to make sense with the name readDirectory. Now let’s find out whether our results are relative to our CWD or to the directory we’re reading. We’ll cd up a level first:
> process.cwd()
'/projects'
> ts.sys.readDirectory("a", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*"])
[
'a/2.ts',
'a/subfolder/1.ts'
]
Interesting—they’re relative to our CWD, not the directory we’re reading, which is a divergence from fs.readdir. Does this mean we can now get includes outside of the directory we’re reading?
> ts.sys.readDirectory("a", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*", "../b/**/*"])
[
'a/2.ts',
'a/subfolder/1.ts',
'b/3.ts'
]
Yes it does! readDirectory can return results outside of the target directory, but not outside the CWD. This seems very odd to me. Let’s cd back into a and try absolute paths:
> process.cwd()
'/projects/a'
> ts.sys.readDirectory("/projects/a", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*"])
[
'/projects/a/2.ts',
'/projects/a/subfolder/1.ts'
]
Absolute in, absolute out. Quite possibly an accident? Now for the final test, an absolute input path with an includes outside the CWD:
> ts.sys.readDirectory("/projects/a", [".ts"], /*excludes*/ undefined, /*includes*/ ["**/*", "../b/**/*"])
[
'/projects/a/2.ts',
'/projects/a/subfolder/1.ts',
'/projects/b/3.ts'
]
👀