@boring-stack-pkg/eslint-plugin-module-boundaries
v0.1.2
Published
ESLint plugin that enforces one semantic top-level concern per TypeScript module.
Downloads
1,331
Maintainers
Readme
eslint-plugin-module-boundaries

Production-oriented ESLint rules for deterministic TypeScript module cohesion.
The main rule, module-boundaries/single-semantic-module, enforces one semantic top-level concern per file. It analyzes TypeScript and TSX AST nodes, not filenames, so a mixed module fails regardless of whether it is named types.ts, utils.ts, anything.ts, or something else.
Why
AI-generated codebases often accumulate unrelated declarations in whichever file is already open: types beside constants, schemas beside functions, hooks beside components. This plugin makes that architectural drift fail during linting.
Install
npm install --save-dev @boring-stack-pkg/eslint-plugin-module-boundaries @typescript-eslint/parserFlat config
import tsParser from "@typescript-eslint/parser";
import moduleBoundaries from "@boring-stack-pkg/eslint-plugin-module-boundaries";
export default [
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: { jsx: true },
},
},
plugins: {
"module-boundaries": moduleBoundaries,
},
rules: {
"module-boundaries/single-semantic-module": "error",
},
},
];You can also use the built-in flat config:
import moduleBoundaries from "@boring-stack-pkg/eslint-plugin-module-boundaries";
export default [moduleBoundaries.configs.recommended];Rule behavior
The rule walks top-level module declarations and classifies each declaration into one semantic category. Imports, re-exports, export lists without declarations, type-only imports, type-only exports, comments, empty statements, and nested declarations are ignored.
Default categories:
| Category | Examples |
| ----------------- | -------------------------------------------------------------------------------- |
| type | interface, type, ambient declarations, TypeScript namespaces |
| constant | top-level runtime values, literals, objects, arrays, templates, computed values |
| function | function declarations, function expressions, arrow functions |
| class | class declarations and class expressions |
| react-component | PascalCase functions or variables returning JSX, React.FC, FunctionComponent |
| hook | functions/variables matching ^use[A-Z0-9].* |
| schema | zod, yup, and valibot schema builder expressions |
| enum | TypeScript enum declarations by default |
If more than one category is detected, the rule reports:
Mixed semantic categories detected in module:
- type
- constant
A module must contain only one semantic concern.
Move declarations into separate files/modules.Configuration
{
rules: {
"module-boundaries/single-semantic-module": [
"error",
{
allow: [
["type", "schema"],
["react-component", "hook"]
],
enumCategory: "enum",
debug: false,
ignoreAmbientDeclarations: false,
schemaLibraries: ["zod", "yup", "valibot"],
reactComponentDetection: {
enabled: true
},
hookDetection: {
enabled: true,
namePattern: "^use[A-Z0-9].*"
}
}
]
}
}allow
Allows specific combinations of semantic categories. For example, ["type", "schema"] permits modules containing both types and schemas, while all other mixed combinations still fail.
enumCategory
Controls enum classification:
"enum": enums are their own runtime category."type": enums are allowed in type-only modules.
debug
Includes declaration names and classification reasons in the lint error.
ignoreAmbientDeclarations
When true, ambient declarations such as declare global are ignored instead of classified as type.
Examples
Valid:
export interface User {}
export type UserId = string;export const USER_ROLE_ADMIN = "admin";
export const USER_ROLE_USER = "user";export function createUser() {}
export function deleteUser() {}Invalid:
export interface User {}
export const DEFAULT_USER = {};export const USER_LIMIT = 5;
export function validateUser() {}export class UserService {}
export function createUser() {}Development
npm install
npm test
npm run typecheck
npm run build