Real-world use case
This issue came out of a real GitLab project. Full context and the exact reproduction discussion here: gitlab-org/.../contributors-gitlab-com#556.
Short version: the project follows the Rails/Vite convention of putting Vue components under app/javascript/…. Since eslint-plugin-unicorn v65 the filename-case rule started validating directory segments too, so the lowercase app folder began failing PascalCase enforcement. The project shipped checkDirectories: false as a workaround (MR !2348), but that also disables directory casing checks everywhere else — the maintainer flagged this as unideal and asked whether a directory-only skip is possible. Investigation confirmed the current API can't express it, hence this issue.
Problem
The ignore option in unicorn/filename-case is a full early-return: if any path segment matches an ignore regex, both the directory-name check and the filename check are skipped for that file.
This makes it impossible to express the following (fairly common) use case: "validate PascalCase on all .vue files under app/javascript/, but don't complain that the app directory itself is lowercase."
The app/ folder name is a Rails / Vite convention and can't be renamed. Since v65, filename-case also validates directory names, so the only way to silence the false positive today is checkDirectories: false — which disables directory casing enforcement everywhere, not just for the app segment.
Reproduction
eslint.config.js:
{
files: ['app/javascript/**/*.vue'],
rules: {
'unicorn/filename-case': ['error', {
cases: { pascalCase: true },
checkDirectories: true,
ignore: [/^app$/],
}],
},
}
Create app/javascript/pages/badly-named.vue. Expected: rule flags the kebab-case filename. Actual: rule passes silently, because pathSegments.some(segment => ignore.some(re => re.test(segment))) matches on the app segment and returns before the filename is validated.
Relevant source (rules/filename-case.js):
if (pathSegments.some(segment => ignore.some(regexp => regexp.test(segment)))) {
return; // skips directory AND filename checks
}
if (isCheckDirectories) { /* ... */ }
Proposal
Introduce a way to exempt directory segments from casing enforcement without also skipping the filename check on files inside them. Two possible shapes:
Option A — new ignoreDirectories option.
'unicorn/filename-case': ['error', {
checkDirectories: true,
ignoreDirectories: [/^app$/], // only skips the directory segment check
ignore: [/* still a full-rule bypass, unchanged */],
}]
Backwards compatible; ignore semantics preserved.
Option B — change ignore to skip only the directory check when checkDirectories: true.
Cleaner API but a breaking change. Users relying on ignore as a whole-file bypass (per #1406's original intent) would need to migrate.
Option A seems safer.
Related
Happy to send a PR if the maintainers are open to Option A.
Real-world use case
This issue came out of a real GitLab project. Full context and the exact reproduction discussion here: gitlab-org/.../contributors-gitlab-com#556.
Short version: the project follows the Rails/Vite convention of putting Vue components under
app/javascript/…. Sinceeslint-plugin-unicornv65 thefilename-caserule started validating directory segments too, so the lowercaseappfolder began failing PascalCase enforcement. The project shippedcheckDirectories: falseas a workaround (MR !2348), but that also disables directory casing checks everywhere else — the maintainer flagged this as unideal and asked whether a directory-only skip is possible. Investigation confirmed the current API can't express it, hence this issue.Problem
The
ignoreoption inunicorn/filename-caseis a full early-return: if any path segment matches anignoreregex, both the directory-name check and the filename check are skipped for that file.This makes it impossible to express the following (fairly common) use case: "validate PascalCase on all
.vuefiles underapp/javascript/, but don't complain that theappdirectory itself is lowercase."The
app/folder name is a Rails / Vite convention and can't be renamed. Since v65,filename-casealso validates directory names, so the only way to silence the false positive today ischeckDirectories: false— which disables directory casing enforcement everywhere, not just for theappsegment.Reproduction
eslint.config.js:Create
app/javascript/pages/badly-named.vue. Expected: rule flags the kebab-case filename. Actual: rule passes silently, becausepathSegments.some(segment => ignore.some(re => re.test(segment)))matches on theappsegment and returns before the filename is validated.Relevant source (
rules/filename-case.js):Proposal
Introduce a way to exempt directory segments from casing enforcement without also skipping the filename check on files inside them. Two possible shapes:
Option A — new
ignoreDirectoriesoption.Backwards compatible;
ignoresemantics preserved.Option B — change
ignoreto skip only the directory check whencheckDirectories: true.Cleaner API but a breaking change. Users relying on
ignoreas a whole-file bypass (per #1406's original intent) would need to migrate.Option A seems safer.
Related
ignorefeature: intent was whole-file exemption based on path, so today's behavior is consistent with that intent.filename-case#686 — introducedcheckDirectories.filename-casefalse positive for files inside__mocks__directory #2642 — related but different: asked for a blanket "off" inside__mocks__; this issue asks for a directory-only skip that still validates filenames.Happy to send a PR if the maintainers are open to Option A.