diff --git a/packages/rstack/src/fmt/discoverPaths.ts b/packages/rstack/src/fmt/discoverPaths.ts index d236bc6b..7ed432ae 100644 --- a/packages/rstack/src/fmt/discoverPaths.ts +++ b/packages/rstack/src/fmt/discoverPaths.ts @@ -18,8 +18,8 @@ interface DiscoverFmtPathsOptions { patterns?: string[]; /** Whether files inside node_modules may be discovered. */ withNodeModules?: boolean; - /** Returns whether a scanned directory can be pruned before traversal. */ - isDirectoryIgnored?: (directoryPath: string) => boolean; + /** Returns whether a scanned path can be excluded during traversal. */ + isIgnored?: (filePath: string, isDirectory: boolean) => boolean; } const isErrnoException = (error: unknown): error is NodeJS.ErrnoException => @@ -209,7 +209,7 @@ const createTraversalOptions = ( gitIgnore: GitIgnoreMatcher, ignoredDirNames: ReadonlySet, isIncluded?: (filePath: string) => boolean, - isDirectoryIgnored?: (directoryPath: string) => boolean, + isIgnored?: (filePath: string, isDirectory: boolean) => boolean, ) => { return { followSymlinks: false, @@ -221,12 +221,16 @@ const createTraversalOptions = ( } if (dirent.isDirectory()) { - return gitIgnore.isIgnored(targetPath, true) || isDirectoryIgnored?.(targetPath) === true; + return gitIgnore.isIgnored(targetPath, true) || isIgnored?.(targetPath, true) === true; + } + + if (isIncluded !== undefined && !isIncluded(targetPath)) { + return true; } return ( + isIgnored?.(targetPath, false) === true || isBinaryPath(targetPath) || - (isIncluded !== undefined && !isIncluded(targetPath)) || gitIgnore.isIgnored(targetPath, false) ); }, @@ -352,7 +356,7 @@ const discoverFmtPaths = async ({ cwd, patterns: inputPatterns, withNodeModules = false, - isDirectoryIgnored, + isIgnored, }: DiscoverFmtPathsOptions): Promise => { const patterns = inputPatterns?.length ? inputPatterns : ['.']; const resolveRelativePath = createRelativePathResolver(cwd); @@ -385,7 +389,7 @@ const discoverFmtPaths = async ({ } await gitIgnore.loadThrough(rootPath); - if (gitIgnore.isIgnored(rootPath, true) || isDirectoryIgnored?.(rootPath) === true) { + if (gitIgnore.isIgnored(rootPath, true) || isIgnored?.(rootPath, true) === true) { return []; } @@ -406,7 +410,7 @@ const discoverFmtPaths = async ({ return ( await readdir( rootPath, - createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isDirectoryIgnored), + createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isIgnored), ) ).files; }), diff --git a/packages/rstack/src/fmt/discovery.ts b/packages/rstack/src/fmt/discovery.ts index bacd7553..d4e012e8 100644 --- a/packages/rstack/src/fmt/discovery.ts +++ b/packages/rstack/src/fmt/discovery.ts @@ -24,7 +24,7 @@ const discoverFmtFiles = async ({ cwd, patterns, withNodeModules, - isDirectoryIgnored: (directoryPath) => isIgnored(directoryPath, true), + isIgnored, }); if (candidates.length === 0) { return []; diff --git a/packages/rstack/tests/fmt/discoverPaths.test.ts b/packages/rstack/tests/fmt/discoverPaths.test.ts index 02bfbd2d..5ad4aa0b 100644 --- a/packages/rstack/tests/fmt/discoverPaths.test.ts +++ b/packages/rstack/tests/fmt/discoverPaths.test.ts @@ -139,28 +139,39 @@ test('lets explicit files bypass gitignore', async () => { }); }); -test('prunes directories with an external ignore matcher', async () => { +test('applies an external ignore matcher during traversal', async () => { await withTempProject(async (rootPath) => { writeProjectFile(rootPath, 'generated/nested/output.ts'); + const ignoredFilePath = writeProjectFile(rootPath, 'src/ignored.ts'); writeProjectFile(rootPath, 'src/index.ts'); - const checkedDirectories: string[] = []; + const checkedPaths: { path: string; isDirectory: boolean }[] = []; const generatedPath = path.join(rootPath, 'generated'); - const isDirectoryIgnored = (directoryPath: string): boolean => { - checkedDirectories.push(path.relative(rootPath, directoryPath)); - return directoryPath === generatedPath; + const isIgnored = (filePath: string, isDirectory: boolean): boolean => { + checkedPaths.push({ + path: path.relative(rootPath, filePath), + isDirectory, + }); + return isDirectory ? filePath === generatedPath : filePath === ignoredFilePath; }; - const files = await discoverFmtPaths({ cwd: rootPath, isDirectoryIgnored }); + const files = await discoverFmtPaths({ cwd: rootPath, isIgnored }); const ignoredRoot = await discoverFmtPaths({ cwd: rootPath, patterns: ['generated'], - isDirectoryIgnored, + isIgnored, }); expect(relativePaths(rootPath, files)).toEqual([path.join('src', 'index.ts')]); expect(ignoredRoot).toEqual([]); - expect(checkedDirectories).toContain('generated'); - expect(checkedDirectories).not.toContain(path.join('generated', 'nested')); + expect(checkedPaths).toContainEqual({ path: 'generated', isDirectory: true }); + expect(checkedPaths).toContainEqual({ + path: path.join('src', 'ignored.ts'), + isDirectory: false, + }); + expect(checkedPaths).not.toContainEqual({ + path: path.join('generated', 'nested'), + isDirectory: true, + }); }); });