unique-filenames-fnc
v1.0.0
Published
Assert globally unique filenames across your project. Configurable per folder, extension and exclusion rules.
Maintainers
Readme
unique-filenames-fnc
A File Naming Convention (FNC) enforcement tool that asserts globally unique filenames across your project. Duplicate filenames — even in different folders — are caught before they cause confusion in stack traces, IDE search, and auto-imports. Configurable per folder with glob and gitignore-style pattern support. Exits with code 1 when duplicates are found, or run in --warn mode for gradual adoption on existing projects.
Why?
As projects scale, naming files by role alone — Modal.js, utils.js, api.js — leads to collisions. Multiple files sharing the same name across different folders create subtle but persistent maintenance problems:
- Stack traces reference
Modal.js:42with no indication of which one - IDE file search returns multiple matches — wrong file gets opened
- Refactor rename operates on one file, silently leaving duplicates untouched
- Import paths become the only disambiguation — easy to import from the wrong location
A globally unique File Naming Convention eliminates this class of problem entirely. It also naturally improves naming quality — Modal.js, ConfirmModal.js and MediaModal.js are not just unique, they are genuinely more descriptive than three files all called Modal.js.
Install
npm install --save-dev unique-filenames-fncUsage
npx unique-filenames-fncAdd to your scripts and run as part of CI:
{
"scripts": {
"unique-filenames-fnc": "unique-filenames-fnc"
}
}Configuration
Config is read from .unique-filenames-fnc.json in the project root, or from "unique-filenames-fnc" key in package.json. Supports a single config object or an array of objects — each is an independent scan with its own rules.
All params accept a single string or an array of strings.
Example 1 — scan entire project from root
The simplest setup. Omitting folders scans from the project root. Use exclude to skip generated folders and barrel files.
.unique-filenames-fnc.json:
{
"extensions": [".js", ".ts", ".html", ".scss"],
"exclude": ["node_modules", "dist", "out", ".git", "**/index.js", "**/index.ts", "**/*.d.ts"]
}Example 2 — separate rules per folder
Different folders often have different conventions. A scripts/ folder only needs .js checked. An electron/ folder only has .ts. Splitting into multiple configs gives you precision.
.unique-filenames-fnc.json:
[
{
"folders": "src",
"extensions": [".js", ".html", ".scss"],
"exclude": ["node_modules", "dist", "**/index.js", "**/*.d.ts"]
},
{
"folders": "electron",
"extensions": ".ts",
"exclude": ["node_modules", "**/index.ts", "**/*.d.ts"]
},
{
"folders": "scripts",
"extensions": ".js",
"exclude": "**/index.js"
}
]Example 3 — monorepo with package globs
In a monorepo you want to check each package independently — duplicates within a package are fine, duplicates across packages are not.
.unique-filenames-fnc.json:
[
{
"folders": ["packages/ui/**", "packages/core/**"],
"extensions": [".ts", ".tsx"],
"exclude": ["node_modules", "**/index.ts", "**/*.d.ts", "**/*.test.ts"]
},
{
"folders": "apps/**",
"extensions": [".ts", ".tsx"],
"exclude": ["node_modules", ".next", "dist", "**/*.d.ts"]
}
]Config reference
| Option | Type | Default | Description |
|---|---|---|---|
| folders | string \| string[] | (root) | Micromatch glob(s) for folders to scan. Omit to scan from project root. |
| extensions | string \| string[] | [".js", ".ts"] | File extensions to check. |
| exclude | string \| string[] | [] | Gitignore-style patterns — matched against full relative path. |
Exclude pattern semantics
Patterns follow the gitignore spec, implemented via the ignore package — the same library used by ESLint.
| Pattern | Meaning |
|---|---|
| node_modules | No slash — matches any file or folder with this name at any depth |
| /dist | Leading slash — anchored to project root only, not nested src/dist |
| dist/ | Trailing slash — matches directories only, not a file named dist |
| *.d.ts | Wildcard — any .d.ts file at the current directory level |
| **/*.d.ts | Double star — any .d.ts file at any depth |
| **/index.js | Any index.js file at any depth |
| src/generated/** | Everything inside src/generated/ |
CI integration
GitHub Actions
name: CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Assert unique filenames
run: npm run unique-filenames-fncBitbucket Pipelines
pipelines:
default:
- step:
name: Assert unique filenames
image: node:20
caches:
- node
script:
- npm ci
- npm run unique-filenames-fncCLI options
--config <path> Path to config file (default: .unique-filenames-fnc.json or package.json)
--warn, -w Report duplicates as warnings — exits with code 0 instead of 1
--help, -h Show help messageWarn mode
By default unique-filenames-fnc exits with code 1 when duplicates are found, failing the pipeline step. Use --warn to report duplicates without failing — useful when adopting the tool on an existing project that already has duplicates, or when you want visibility without blocking deploys.
unique-filenames-fnc --warnIn warn mode duplicates are printed with ⚠ instead of ✗ and a summary line is added:
⚠ [src] — 1 duplicate(s):
Button.js
src/lib/Button/Button.js
src/components/Toolbar/Button.js
⚠ 1 duplicate filename(s) found — running in warn mode, not failing.GitHub Actions — warn mode
- name: Check unique filenames (warn only)
run: npx unique-filenames-fnc --warnOr configure a dedicated script:
{
"scripts": {
"unique-filenames-fnc:warn": "unique-filenames-fnc --warn"
}
}Bitbucket Pipelines — warn mode
- step:
name: Check unique filenames (warn only)
image: node:20
caches:
- node
script:
- npm ci
- npx unique-filenames-fnc --warnExample output
✗ [src] — 2 duplicate(s):
Button.js
src/lib/Button/Button.js
src/components/Toolbar/Button.js
utils.js
src/lib/utils.js
src/pages/Settings/utils.jsLicense
MIT
