Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions packages/rstack/src/fmt/discoverPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down Expand Up @@ -209,7 +209,7 @@ const createTraversalOptions = (
gitIgnore: GitIgnoreMatcher,
ignoredDirNames: ReadonlySet<string>,
isIncluded?: (filePath: string) => boolean,
isDirectoryIgnored?: (directoryPath: string) => boolean,
isIgnored?: (filePath: string, isDirectory: boolean) => boolean,
) => {
return {
followSymlinks: false,
Expand All @@ -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)
);
},
Expand Down Expand Up @@ -352,7 +356,7 @@ const discoverFmtPaths = async ({
cwd,
patterns: inputPatterns,
withNodeModules = false,
isDirectoryIgnored,
isIgnored,
}: DiscoverFmtPathsOptions): Promise<string[]> => {
const patterns = inputPatterns?.length ? inputPatterns : ['.'];
const resolveRelativePath = createRelativePathResolver(cwd);
Expand Down Expand Up @@ -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 [];
}

Expand All @@ -406,7 +410,7 @@ const discoverFmtPaths = async ({
return (
await readdir(
rootPath,
createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isDirectoryIgnored),
createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isIgnored),
)
).files;
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/rstack/src/fmt/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const discoverFmtFiles = async ({
cwd,
patterns,
withNodeModules,
isDirectoryIgnored: (directoryPath) => isIgnored(directoryPath, true),
isIgnored,
});
if (candidates.length === 0) {
return [];
Expand Down
29 changes: 20 additions & 9 deletions packages/rstack/tests/fmt/discoverPaths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
});
});

Expand Down