Sort filenames into named buckets by rules. Zero dependencies, tiny footprint.
npm install classify-filename
Quick start
import { classify } from 'classify-filename'; const files = [ 'NOC_2024.pdf', 'noc_letter.pdf', 'agreement_v1.docx', 'invoice_2.pdf', 'invoice_10.pdf', 'random.txt', ]; const result = classify(files, [ { name: 'noc', match: /^noc/i }, { name: 'agreements', match: 'agreement' }, { name: 'invoices', match: /^invoice/i }, ]); // result.sections: // { // noc: ['noc_letter.pdf', 'NOC_2024.pdf'], // agreements: ['agreement_v1.docx'], // invoices: ['invoice_2.pdf', 'invoice_10.pdf'], // natural sort: 2 before 10 // uncategorized: ['random.txt'], // }
API
classify(filenames, sections, options?)
Parameters
filenames: string[]— the filenames to sort.sections: Section[]— buckets with matching rules. Order matters — earlier sections win on conflict.options: ClassifyOptions— see below.
Returns ClassifyResult — { sections: Record<string, string[]> }, plus matches when explain: true.
Matchers
A section's match can be:
- a string — substring match (case-insensitive by default)
- a RegExp —
.test()-ed against the filename; respects its own flags - a function —
(filename) => booleanfor arbitrary logic - an array of the above — OR-combined
{ name: 'legal', match: ['agreement', 'contract', /nda/i] }
Options
| Option | Default | Behavior |
|---|---|---|
caseSensitive |
false |
Applies only to string matchers. RegExp matchers respect their own flags. |
fallback |
'uncategorized' |
Bucket for unmatched files. Pass false to drop them. |
multiMatch |
false |
If true, a file lands in every matching section, not just the first. |
sort |
'asc' |
Natural-sort ascending. 'desc', a comparator, or false to disable. |
explain |
false |
When true, result includes a matches map from filename → section name. |
Priority is section order
The first section whose predicate returns true wins:
// noc_agreement.pdf lands in 'noc', not 'agreements'. classify(['noc_agreement.pdf'], [ { name: 'noc', match: 'noc' }, // checked first { name: 'agreements', match: 'agreement' }, ]);
Put narrow rules first, broad rules last.
Debugging with explain
const result = classify(files, sections, { explain: true }); console.log(result.matches); // { 'noc_agreement.pdf': 'noc', 'random.txt': 'uncategorized', ... }
Why not just .filter()?
.filter() gives you one bucket. Real file sorting needs multiple named buckets, priority rules, natural sort, and a fallback for unmatched files — that's boilerplate you'd rewrite every time. classify-filename wraps it in a single call.
Not in v1
- No glob support — use a RegExp for now.
- No file-object input (size, date) — filename-only. Coming in v1.1.
- No CLI — this is a library. A
binwrapper may follow.
See ROADMAP.md for what's planned.
License
MIT