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'
]
👀