eslint-plugin-module-restrictions
v0.5.0
Published
An ESLint plugin that restricts module imports based on file naming patterns. This tool helps enforce architectural rules and maintain consistent code structure in your projects. **With zero configuration needed**!
Maintainers
Readme
ESLint Plugin Module Restrictions
An ESLint plugin that restricts module imports based on file naming patterns. This tool helps enforce architectural rules and maintain consistent code structure in your projects. With zero configuration needed!
✨ Features
- File naming pattern-based import restrictions: Control where files with specific patterns can be imported from
- Flexible rule configuration: Support for various restriction rules (same directory, sub-module prefix, same file prefix, custom)
- ESLint integration: Seamlessly integrates with existing ESLint workflows
- TypeScript support: Full TypeScript support with type definitions
📦 Installation
npm install --save-dev eslint-plugin-module-restrictions🚀 Quick Start
Basic Configuration
// .eslintrc.js
module.exports = {
plugins: ["module-restrictions"],
extends: ["plugin:module-restrictions/recommended"],
};Custom Rule Configuration
// .eslintrc.js
module.exports = {
plugins: ["module-restrictions"],
rules: {
"module-restrictions/restrict-imports": [
"error",
{
restrictions: [
{
pattern: "**/Layer-Level-1/**/*",
rule: "custom",
message:
"Layer-Level-1 files can only be imported from Layer-Level-2",
allowedImporters: ["**/Layer-Level-2/**/*"],
},
],
},
],
},
};📋 Supported Rules
private-module
Private modules can only be imported by files with same parent name.
Example:
// File: Modal.private.Header.tsx
// File: Modal.p.Header.tsx -> 'p' is shorthand alias for 'private'
// ✅ Allowed
// From: src/components/Modal/Modal.tsx
import { ModalHeader } from "./Modal.private.Header";
// ✅ Allowed
// From: src/components/Modal/Modal.tsx
import { ModalHeader } from "./Modal.p.Header";
// ❌ Not allowed
// From: src/components/Box/Box.tsx
import { ModalHeader } from "./Modal.private.Header";
// ❌ Not allowed
// From: src/components/Modal/ModalContent.tsx
import { ModalHeader } from "./Modal.private.Header";shared-module
Allows imports only from files that start with the same prefix as the importing file.
Example:
// File: Box.shared.Icon.tsx
// File: Box.s.Icon.tsx -> 's' is shorthand alias for 'shared'
// ✅ Allowed
// From: src/components/Box/Box.tsx
import { BoxIcon } from "./Box.shared.Icon";
// ✅ Allowed
// From: src/components/Box/Box.tsx
import { BoxIcon } from "./Box.s.Icon";
// ✅ Allowed
// From: src/components/Box/BoxHeader.tsx
import { BoxIcon } from "./Box.shared.Icon";
// ❌ Not allowed
// From: src/components/Button/Button.tsx
import { BoxIcon } from "./Box.shared.Icon";internal-directory
Restricts imports from files in underscore-prefixed directories (_*) to only allow imports from the same level directory or within the underscore directory itself.
Example:
// File structure:
// src/
// ├── _shared/
// │ ├── api-client.ts
// │ ├── constants.ts
// │ └── types.ts
// ├── components/
// │ ├── Button/
// │ │ ├── Button.tsx
// │ │ └── _internal/
// │ │ ├── button-styles.ts
// │ │ └── button-utils.ts
// │ └── Modal/
// │ ├── Modal.tsx
// │ └── _internal/
// │ └── modal-hooks.ts
// ├── pages/
// │ ├── Home/
// │ │ └── HomePage.tsx
// │ └── Profile/
// │ └── ProfilePage.tsx
// └── hooks/
// └── useAuth.ts
// ✅ Allowed - same level directory
// From: src/pages/Home/HomePage.tsx
import { API_BASE_URL } from "../../_shared/constants";
// ✅ Allowed - same level directory
// From: src/components/Button/Button.tsx
import { API_BASE_URL } from "../../_shared/constants";
// ✅ Allowed - within underscore directory
// From: src/_shared/api-client.ts
import { API_BASE_URL } from "./constants";
import { ApiResponse } from "./types";
// ✅ Allowed - within underscore directory
// From: src/components/Button/_internal/button-utils.ts
import { buttonStyles } from "./button-styles";
// ❌ Not allowed - different parent directory
// From: src/components/Modal/Modal.tsx
import { buttonUtils } from "../Button/_internal/button-utils";
// ❌ Not allowed - accessing from parent directories above
// From: src/App.tsx
import { buttonStyles } from "../../components/Button/_internal/button-styles";no-deep-import
When an index file exists, modules within a directory can only be accessed through its index file. This promotes better encapsulation and cleaner import statements.
Example:
// File structure:
// src/
// ├── components/
// │ ├── index.ts // ✅ Exists - exports all components
// │ ├── Button.ts
// │ ├── Input.ts
// │ └── Modal.ts
// ├── utils/
// │ ├── index.ts // ✅ Exists - exports all utilities
// │ ├── formatter.ts
// │ └── validator.ts
// └── pages/
// ├── Home.ts
// └── Profile.ts
// ✅ Allowed - importing through index file
// From: src/pages/Home.ts
import { Button, Input, Modal } from "../components";
import { formatter, validator } from "../utils";
// ❌ Not allowed - direct deep import when index exists
// From: src/pages/Home.ts
import { Button } from "../components/Button";
import { formatter } from "../utils/formatter";
// ✅ Allowed - direct import when no index file exists
// From: src/App.tsx
import { Home } from "./pages/Home.ts"; // No index.ts in pages/avoid-circular-dependency
Prevents circular dependencies by restricting index file imports within the same module. This helps maintain clean architecture and prevents runtime issues.
Example:
// File structure:
// src/
// ├── features/
// │ ├── index.ts // Exports all modules in features
// │ ├── user/
// │ │ ├── index.ts // Exports user components
// │ │ ├── UserProfile.ts
// │ │ ├── UserSettings.ts
// │ │ └── UserService.ts
// │ └── shared/
// │ ├── index.ts // Exports shared utilities
// │ ├── constants.ts
// │ └── helpers.ts
// ✅ Allowed - importing from different modules
// From: src/features/user/UserProfile.ts
import { constants } from "../shared";
// ✅ Allowed - importing specific files within same module
// From: src/features/user/UserProfile.ts
import { UserService } from "./UserService";
// ❌ Not allowed - importing through index in same directory
// From: src/features/user/UserProfile.ts
import { UserService } from "./index"; // Circular dependency risk
// ❌ Not allowed - importing through index in common parent directory
// From: src/features/user/UserProfile.ts
import { helperA } from "../index"; // Circular dependency riskcustom
Allows custom logic for import restrictions.
Example:
{
pattern: "*.api.ts",
rule: "custom",
validator: (importPath, filePath) => {
// Custom validation logic
return importPath.includes('/api/');
},
message: "API files can only import from API directories"
}🛠️ Development
Prerequisites
- Node.js >= 16
- npm or yarn
Setup
# Clone the repository
git clone https://github.com/your-username/eslint-plugin-module-restrictions.git
cd eslint-plugin-module-restrictions
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm testAvailable Scripts
npm run build- Build the TypeScript codenpm run test- Run tests with Vitestnpm run typecheck- Type check without emitting filesnpm run clean- Clean build artifacts
🤝 Contributing
We welcome bug reports, feature requests, and pull requests!
How to Contribute
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes and add tests
- Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow the existing code style and patterns
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
📞 Support
- Feature Requests & Bug Reports: GitHub Issues
🙏 Acknowledgments
Thanks to all contributors who have helped make this project better!
Made with ❤️ by the ESLint Plugin Module Restrictions team
