Skip to content

noctcore-architecture/filename-matches-export

A file’s basename must match its primary export (a default export, or the sole named export).

Recommended preset: error · Autofix: no · Suggestions: yes · Type information: not needed

When a module has one clear public export, the filename should announce it. A helpers.ts that exports a single formatDate hides its contents from anyone scanning the file tree, and a rename of one without the other leaves the two permanently out of sync. Keeping them aligned makes the tree self-describing.

The rule first resolves the file’s primary export:

  1. If the file has a default export with a name (export default function Foo, export default class Foo, export default Foo), that name is the primary export. An anonymous default (export default () => …) has no identifier to compare, so the file is skipped.
  2. Otherwise, if the file has exactly one local named export, that is the primary export.
  3. Otherwise (no primary, or several named exports) the file is skipped.

The basename and the identifier are compared case- and separator-insensitively, so naming conventions never collide:

Incorrect · src/utils/helpers.ts
// genuine mismatch
export const formatDate = () => {};

Renaming the file is one fix, renaming the export the other:

Correct · src/utils/format-date.ts
export const formatDate = () => {};
Correct · src/utils/helpers.ts
export const helpers = () => {};

The comparison ignores case and separators:

Correct · src/components/TaskCard.tsx
export default function TaskCard() {}
Correct · src/hooks/use-thing.ts
// kebab-case file, camelCase export
export const useThing = () => {};

A mismatch offers a suggestion to rename the export to the filename (the code-side resolution — the other is renaming the file). It is never an autofix: renaming a public identifier is a decision a human should confirm. When the basename is not a valid identifier (e.g. 2fa.ts), the mismatch is reported without a suggestion.

  • index files and files matching an ignore glob.
  • A file with no primary export: an anonymous default (export default () => 1), several named exports and no default, or no exports at all.
  • A pure re-export (export { Foo } from './Foo'), which has no local identity.
  • A file whose default export matches, even when it also has other named exports.
Option Type Default Meaning
ignore string[] [] Globs (supporting **, *, ?) of files to skip.
'noctcore-architecture/filename-matches-export': ['error', {
ignore: ['**/*.stories.tsx', '**/route.ts'],
}]

If your files routinely export several unrelated symbols, or you use fixed conventional filenames (route.ts, handler.ts) that will never match their export, add them to ignore or leave the rule off.