Skip to content

Commit ca90fb0

Browse files
authored
Fix exclude-file-types for known files (#465)
* Fix exclude-file-types for known files * docs: added changelog entry * chore: regenerate known files from Linguist
1 parent e2a9b44 commit ca90fb0

8 files changed

Lines changed: 65 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4141
- KnownFiles now take priority over extension matching in the finder, so `tsconfig.json` resolves to JSONC (not JSON)
4242
- Extension exclusion cache no longer prevents known files from being found
4343
- Linguist known files that conflict with dedicated validators are automatically excluded (e.g. `.editorconfig` stays with EditorConfig, not INI)
44+
- `--exclude-file-types` now excludes files by resolved file type, including extensionless known files like `.gitconfig` and `justfile`
4445

4546

4647
### Changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ Exclude file types in the search path. JSON and JSONC are treated as a family
471471
validator --exclude-file-types=json /path/to/search
472472
```
473473

474-
Note: `--exclude-file-types` filters by file extension. Extensionless known files (like `.gitconfig` or `.babelrc`) are not affected by this flag. Use `--type-map` or `.cfv.toml` for fine-grained control.
474+
Note: `--exclude-file-types` filters by resolved file type. Extensionless known files (like `.gitconfig` or `.babelrc`) are excluded when they resolve to an excluded type.
475475

476476
![Exclude File Types Run](./img/exclude_file_types.gif)
477477

cmd/validator/testdata/stress_bugs.txtar

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,13 @@ stdout '✓'
168168
# ============================================================
169169
# BUG VECTOR 10: --exclude-file-types with extensionless known files
170170
#
171-
# Excluding "ini" should skip .ini files. But .gitconfig has no
172-
# extension — the exclude check uses extensionLowerCase which is
173-
# empty string for extensionless files. Does "" match "ini"? No.
174-
# So .gitconfig should NOT be excluded by --exclude-file-types=ini.
171+
# Excluding "ini" should skip both .ini files and known files that
172+
# resolve to the INI type, such as .gitconfig.
175173
# ============================================================
176174

177-
# .gitconfig has no extension — not excluded by extension-based filter
175+
# .gitconfig resolves to INI and is excluded
178176
exec validator --exclude-file-types=ini ext_exclude
179-
stdout '✓.*gitconfig'
177+
! stdout 'gitconfig'
180178

181179
# .ini file IS excluded
182180
exec validator --exclude-file-types=ini ext_exclude

cmd/validator/testdata/stress_known_files.txtar

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ exec validator --exclude-file-types=jsonc project
154154
! stdout 'tsconfig'
155155
stdout '✓.*pom.xml'
156156

157-
# Exclude ini — .gitconfig has no extension so it's not excluded by extension.
157+
# Exclude ini — .gitconfig resolves to INI, so it is excluded.
158158
exec validator --exclude-file-types=ini dotfiles
159-
stdout 'gitconfig'
159+
! stdout 'gitconfig'
160160

161161
# ============================================================
162162
# PART 6: --file-types with known files

index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ Exclude file types in the search path. JSON and JSONC are treated as a family
473473
validator --exclude-file-types=json /path/to/search
474474
```
475475

476-
Note: `--exclude-file-types` filters by file extension. Extensionless known files (like `.gitconfig` or `.babelrc`) are not affected by this flag. Use `--type-map` or `.cfv.toml` for fine-grained control.
476+
Note: `--exclude-file-types` filters by resolved file type. Extensionless known files (like `.gitconfig` or `.babelrc`) are excluded when they resolve to an excluded type.
477477

478478
![Exclude File Types Run](./img/exclude_file_types.gif)
479479

pkg/filetype/known_files_gen.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/finder/finder_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,38 @@ func Test_fsFinderExcludeFileTypes(t *testing.T) {
5050
require.Len(t, files, 1)
5151
}
5252

53+
func Test_fsFinderExcludeFileTypesKnownFiles(t *testing.T) {
54+
dir := t.TempDir()
55+
testhelper.WriteFile(t, dir, ".gitconfig", testhelper.ValidContent["ini"])
56+
testhelper.WriteFile(t, dir, "config.ini", testhelper.ValidContent["ini"])
57+
testhelper.WriteFile(t, dir, "good.json", testhelper.ValidContent["json"])
58+
59+
fsFinder := FileSystemFinderInit(
60+
WithPathRoots(dir),
61+
WithExcludeFileTypes([]string{"ini"}),
62+
)
63+
files, err := fsFinder.Find()
64+
require.NoError(t, err)
65+
require.Len(t, files, 1)
66+
require.Equal(t, "good.json", files[0].Name)
67+
}
68+
69+
func Test_fsFinderExcludeFileTypesKnownFilesByExtensionAlias(t *testing.T) {
70+
dir := t.TempDir()
71+
testhelper.WriteFile(t, dir, "justfile", "test:\n\techo test\n")
72+
testhelper.WriteFile(t, dir, "task.just", "test:\n\techo test\n")
73+
testhelper.WriteFile(t, dir, "good.json", testhelper.ValidContent["json"])
74+
75+
fsFinder := FileSystemFinderInit(
76+
WithPathRoots(dir),
77+
WithExcludeFileTypes([]string{"just"}),
78+
)
79+
files, err := fsFinder.Find()
80+
require.NoError(t, err)
81+
require.Len(t, files, 1)
82+
require.Equal(t, "good.json", files[0].Name)
83+
}
84+
5385
func Test_fsFinderWithDepth(t *testing.T) {
5486
// root/good.json, root/sub/good.yaml
5587
dir := testhelper.CreateFixtureDir(t, "json")

pkg/finder/fsfinder.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,18 +290,14 @@ func (fsf FileSystemFinder) handleFile(path string, dirEntry fs.DirEntry, seenMa
290290
walkFileExtension := strings.TrimPrefix(filepath.Ext(path), ".")
291291
extensionLowerCase := strings.ToLower(walkFileExtension)
292292

293-
if _, isExcluded := fsf.ExcludeFileTypes[extensionLowerCase]; isExcluded {
294-
return nil
295-
}
296-
297293
// Check type overrides first (user-specified mappings take priority)
298294
for _, override := range fsf.TypeOverrides {
299295
matched, err := doublestar.PathMatch(override.Pattern, path)
300296
if err != nil {
301297
return err
302298
}
303299
if matched {
304-
return fsf.addFile(path, dirEntry, override.FileType, seenMap, matchingFiles)
300+
return fsf.addFileIfNotExcluded(path, dirEntry, override.FileType, seenMap, matchingFiles)
305301
}
306302
}
307303

@@ -310,12 +306,12 @@ func (fsf FileSystemFinder) handleFile(path string, dirEntry fs.DirEntry, seenMa
310306
// files like tsconfig.json resolve to jsonc (not json).
311307
for _, fileType := range fsf.FileTypes {
312308
if _, isKnownFile := fileType.KnownFiles[walkFileName]; isKnownFile {
313-
return fsf.addFile(path, dirEntry, fileType, seenMap, matchingFiles)
309+
return fsf.addFileIfNotExcluded(path, dirEntry, fileType, seenMap, matchingFiles)
314310
}
315311
}
316312
for _, fileType := range fsf.FileTypes {
317313
if _, hasExtension := fileType.Extensions[extensionLowerCase]; hasExtension {
318-
return fsf.addFile(path, dirEntry, fileType, seenMap, matchingFiles)
314+
return fsf.addFileIfNotExcluded(path, dirEntry, fileType, seenMap, matchingFiles)
319315
}
320316
}
321317

@@ -329,6 +325,25 @@ func (fsf FileSystemFinder) handleFile(path string, dirEntry fs.DirEntry, seenMa
329325
return nil
330326
}
331327

328+
func (fsf FileSystemFinder) addFileIfNotExcluded(path string, dirEntry fs.DirEntry, fileType filetype.FileType, seenMap map[string]struct{}, matchingFiles *[]FileMetadata) error {
329+
if fsf.isFileTypeExcluded(fileType) {
330+
return nil
331+
}
332+
return fsf.addFile(path, dirEntry, fileType, seenMap, matchingFiles)
333+
}
334+
335+
func (fsf FileSystemFinder) isFileTypeExcluded(fileType filetype.FileType) bool {
336+
if _, isExcluded := fsf.ExcludeFileTypes[fileType.Name]; isExcluded {
337+
return true
338+
}
339+
for extension := range fileType.Extensions {
340+
if _, isExcluded := fsf.ExcludeFileTypes[extension]; isExcluded {
341+
return true
342+
}
343+
}
344+
return false
345+
}
346+
332347
func (FileSystemFinder) addFile(path string, dirEntry fs.DirEntry, fileType filetype.FileType, seenMap map[string]struct{}, matchingFiles *[]FileMetadata) error {
333348
absPath, err := filepath.Abs(path)
334349
if err != nil {

0 commit comments

Comments
 (0)