@stricture/eslint-plugin API
The @stricture/eslint-plugin package provides ESLint integration for Stricture. It’s a thin wrapper around @stricture/core that extracts import information from ESLint’s AST and reports violations.
Overview
Package: @stricture/eslint-plugin
Purpose: ESLint integration for real-time architecture enforcement in your editor and CI/CD
Key Features:
- Real-time feedback in your editor
- Integrates with existing ESLint setup
- Supports both flat config and legacy config
- Checks static imports, dynamic imports, require(), and re-exports
- TypeScript path alias support
Installation
npm install -D @stricture/eslint-plugin# orpnpm add -D @stricture/eslint-plugin# oryarn add -D @stricture/eslint-pluginNote:
@stricture/coreis automatically installed as a dependency and does not need to be added explicitly.
Rules
enforce-boundaries
The main (and only) rule in the plugin. Enforces architectural boundaries defined in .stricture/config.json.
Rule Name: @stricture/enforce-boundaries
Type: problem (catches code errors)
Recommended: Yes
Fixable: No (architectural violations require manual fixes)
Configuration
ESLint Flat Config (ESLint 9+)
import stricture from '@stricture/eslint-plugin'
export default [ { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': 'error' } }]Legacy Config (ESLint 8 and below)
module.exports = { plugins: ['@stricture'], rules: { '@stricture/enforce-boundaries': 'error' }}Rule Options
The enforce-boundaries rule accepts an options object:
interface RuleOptions { configPath?: string // Path to .stricture/config.json (default: '.stricture/config.json') baseUrl?: string // Base URL for path resolution (default: project root) checkDynamicImports?: boolean // Check dynamic imports (default: true) reportUnusedRules?: boolean // Warn about unused rules (default: false)}Option: configPath
Specify a custom path to your Stricture configuration file.
export default [ { rules: { '@stricture/enforce-boundaries': [ 'error', { configPath: '.stricture/custom-config.json' } ] } }]Default: .stricture/config.json
Option: baseUrl
Override the base directory for path resolution.
export default [ { rules: { '@stricture/enforce-boundaries': [ 'error', { baseUrl: './src' } ] } }]Default: Project root (where ESLint is run from)
Option: checkDynamicImports
Enable or disable checking of dynamic import() statements.
export default [ { rules: { '@stricture/enforce-boundaries': [ 'error', { checkDynamicImports: false // Don't check import('...') } ] } }]Default: true
Why disable?: Dynamic imports are often used for code splitting and may have different architectural constraints.
Option: reportUnusedRules
Warn about rules that never match any imports (experimental).
export default [ { rules: { '@stricture/enforce-boundaries': [ 'error', { reportUnusedRules: true } ] } }]Default: false
Checked Import Types
The plugin checks all import forms:
Static Imports
// ✅ Checkedimport { User } from '../domain/user'import * as utils from '@/utils'Dynamic Imports
// ✅ Checked (if checkDynamicImports: true)const module = await import('../domain/user')CommonJS Require
// ✅ Checkedconst { User } = require('../domain/user')Re-exports
// ✅ Checkedexport { User } from '../domain/user'export * from '../domain/user'TypeScript Path Alias Support
The plugin automatically resolves TypeScript path aliases from tsconfig.json:
tsconfig.json:
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@domain/*": ["src/core/domain/*"], "@adapters/*": ["src/adapters/*"] } }}Code:
// These imports are resolved correctlyimport { User } from '@domain/user' // → src/core/domain/user.tsimport { CLI } from '@adapters/driving/cli' // → src/adapters/driving/cli.tsThe plugin uses the tsconfig.json in your project root by default. You can specify a different location via the baseUrl option.
Error Messages
The plugin provides detailed, actionable error messages:
Example 1: Domain Importing Adapter
import { MemoryRepository } from '../../adapters/driven/memory-repository' // ❌Error:
src/core/domain/user.ts 1:1 error Domain layer must be pure - no external dependencies or infrastructure imports.
Why: Domain is the core of your application and must remain independent.
Allowed: Other domain entities onlyForbidden: Ports, Application, Adapters, External dependencies
Fix: Remove this import or move the logic to the application layer.
@stricture/enforce-boundariesExample 2: Application Importing Concrete Adapter
import { MemoryRepository } from '../../adapters/driven/memory-repository' // ❌Error:
src/core/application/create-user.ts 1:1 error Application layer should depend on port interfaces, not concrete adapter implementations.
Why: This maintains dependency inversion and allows you to swap implementations.
Fix: Import from ports instead: import { UserRepository } from '../ports/user-repository'
Then use dependency injection to receive the concrete implementation.
@stricture/enforce-boundariesExample 3: No Rule Defined (Deny-by-Default)
import { User } from '../domain/user' // ❌ No rule allows thisError:
src/utils/helper.ts 1:1 error No architectural rule defined for this import.
From: unknown boundary (src/utils/helper.ts)To: domain (src/domain/user.ts)
Stricture uses deny-by-default policy for safety. Add an explicit rule to allow this import:
{ "rules": [{ "id": "allow-utils-to-domain", "from": { "pattern": "src/utils/**" }, "to": { "tag": "domain" }, "allowed": true }]}
@stricture/enforce-boundariesSeverity Levels
Control how violations are reported:
Error (Recommended)
Fail builds and block commits:
rules: { '@stricture/enforce-boundaries': 'error'}Warn
Show warnings but don’t fail builds:
rules: { '@stricture/enforce-boundaries': 'warn'}Use case: During migration to gradually enforce boundaries.
Off
Disable the rule:
rules: { '@stricture/enforce-boundaries': 'off'}Per-Rule Severity
You can also control severity per rule in .stricture/config.json:
{ "rules": [ { "id": "domain-isolation", "severity": "error", "from": { "tag": "domain" }, "to": { "tag": "*" }, "allowed": false }, { "id": "experimental-feature", "severity": "warn", "from": { "tag": "adapters" }, "to": { "tag": "experimental" }, "allowed": false }, { "id": "deprecated-rule", "severity": "off", "from": { "tag": "old" }, "to": { "tag": "new" }, "allowed": false } ]}Integration Examples
With TypeScript
import eslint from '@eslint/js'import tseslint from '@typescript-eslint/eslint-plugin'import tsparser from '@typescript-eslint/parser'import stricture from '@stricture/eslint-plugin'
export default [ eslint.configs.recommended, { files: ['**/*.ts', '**/*.tsx'], languageOptions: { parser: tsparser, parserOptions: { project: './tsconfig.json' } }, plugins: { '@typescript-eslint': tseslint, '@stricture': stricture }, rules: { ...tseslint.configs.recommended.rules, '@stricture/enforce-boundaries': 'error' } }]With React
import reactPlugin from 'eslint-plugin-react'import stricture from '@stricture/eslint-plugin'
export default [ { files: ['**/*.jsx', '**/*.tsx'], plugins: { react: reactPlugin, '@stricture': stricture }, rules: { ...reactPlugin.configs.recommended.rules, '@stricture/enforce-boundaries': 'error' } }]With Vue
import vuePlugin from 'eslint-plugin-vue'import stricture from '@stricture/eslint-plugin'
export default [ ...vuePlugin.configs['flat/recommended'], { plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': 'error' } }]Monorepo Setup
For monorepos, you can have different configs per package:
import stricture from '@stricture/eslint-plugin'
export default [ { plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': [ 'error', { configPath: '.stricture/api-config.json' } ] } }]import stricture from '@stricture/eslint-plugin'
export default [ { plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': [ 'error', { configPath: '.stricture/web-config.json' } ] } }]Ignoring Files
Use ESLint’s standard ignore patterns:
Flat Config
export default [ { ignores: [ 'dist/**', 'build/**', 'node_modules/**', '**/*.generated.ts', 'scripts/**' ] }, { plugins: { '@stricture': stricture }, rules: { '@stricture/enforce-boundaries': 'error' } }]Legacy Config
module.exports = { plugins: ['@stricture'], rules: { '@stricture/enforce-boundaries': 'error' }, ignorePatterns: [ 'dist/**', 'build/**', 'node_modules/**', '**/*.generated.ts', 'scripts/**' ]}You can also use .eslintignore:
dist/build/node_modules/**/*.generated.tsscripts/CI/CD Integration
GitHub Actions
name: Lint
on: [push, pull_request]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 20 - run: npm install - run: npm run lint # Runs ESLint with Stricture rulesGitLab CI
lint: image: node:20 script: - npm install - npm run lintPre-commit Hook
{ "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.{ts,tsx,js,jsx}": ["eslint --fix"] }}Performance
The plugin is designed for performance:
- Configuration cached:
.stricture/config.jsonloaded once per ESLint run - No file I/O during validation: All validation happens in memory
- Fast pattern matching: Uses optimized glob matching from micromatch
- Minimal AST traversal: Only checks import-related nodes
Typical performance: < 1ms per file on modern hardware.
Troubleshooting
Config Not Loading
Problem: ESLint reports “Failed to load .stricture/config.json”
Solutions:
- Ensure
.stricture/config.jsonexists in your project root - Check JSON syntax (no trailing commas, valid JSON)
- Run
npx stricture validateto check config - Specify custom path:
configPath: './path/to/config.json'
Path Aliases Not Resolving
Problem: Imports using @/ or other aliases are not resolved correctly
Solutions:
- Ensure
tsconfig.jsonexists withpathsconfiguration - Check
baseUrlin tsconfig matches your setup - Verify ESLint is running from the correct directory
- Use
baseUrloption to override if needed
No Violations Reported
Problem: You have violations but ESLint doesn’t report them
Solutions:
- Check the rule is enabled:
'@stricture/enforce-boundaries': 'error' - Verify
.stricture/config.jsonhas rules defined - Check file patterns in boundaries match your files
- Run
npx stricture checkto test outside ESLint - Ensure files aren’t in
ignorePatterns
Too Many Violations
Problem: Thousands of violations after enabling Stricture
Solutions:
- Start with
'warn'instead of'error' - Use
severity: 'off'for rules you’ll fix later - Focus on one layer at a time
- Use
ignorePatternsto exclude legacy code temporarily
See Also
- @stricture/core API - Core validation engine
- @stricture/cli API - Command-line interface
- Configuration Guide
- ESLint Integration Guide