Linting
Enforce RouteCraft best practices with ESLint.
@routecraft/eslint-plugin-routecraft
An official ESLint plugin that provides rules for RouteCraft projects.
- Package:
@routecraft/eslint-plugin-routecraft - Config: ESLint Flat Config
Install
npm install -D eslint @eslint/js typescript-eslint @routecraft/eslint-plugin-routecraft
Configure (flat config)
// eslint.config.mjs
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import routecraftPlugin from '@routecraft/eslint-plugin-routecraft'
/** @type {import('eslint').Linter.Config[]} */
export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{js,mjs,cjs,ts}'],
plugins: { '@routecraft/routecraft': routecraftPlugin },
...routecraftPlugin.configs.recommended,
},
]
Rules
require-named-route
- Action: Error (default)
- Description: Every
craft().from()chain must include.id(<non-empty string>)before.from()for easier debugging, monitoring, and consistency. - Options: None
- Autofix: None (names should be descriptive, not generated)
Examples:
// ✅ Good
export default craft()
.id('user-processor')
.from(timer({ intervalMs: 5000 }))
.to(log())
// ❌ Bad (missing .id before .from)
export default craft()
.from(timer({ intervalMs: 5000 }))
.to(log())
// ❌ Bad (empty name)
export default craft()
.id('')
.from(timer({ intervalMs: 5000 }))
batch-before-from
- Action: Warn (default)
- Description:
batch()is a route-level operation and must be configured before.from(). Usingbatch()after.from()is ambiguous and won’t affect the current route. - Options: None
- Autofix: None
Examples:
// ✅ Good: batch before from
craft()
.id('bulk')
.batch({ size: 50, flushIntervalMs: 5000 })
.from(timer({ intervalMs: 1000 }))
.to(database({ operation: 'bulkInsert' }))
// ❌ Bad: batch after from (will be staged for the next route, not this one)
craft()
.id('bulk')
.from(timer({ intervalMs: 1000 }))
.batch({ size: 50 })
.to(database({ operation: 'bulkInsert' }))
mcp-server-options
- Action: Error (default)
- Description: When using
mcp()from@routecraft/aias a server in.from(), options withdescriptionmust be provided for AI/MCP discoverability. - Options: None
- Autofix: None
This rule ensures that MCP routes are properly documented for AI agent discovery. The description field is required so that AI systems can understand what each endpoint does.
Examples:
// ✅ Good: mcp() with options in .from()
import { mcp } from '@routecraft/ai'
craft()
.id('my-tool')
.from(mcp('my-tool', { description: 'Process incoming requests' }))
.to(log())
// ✅ Good: direct() for in-process .to(); mcp() with description in .from()
import { mcp } from '@routecraft/ai'
import { direct } from '@routecraft/routecraft'
craft()
.id('producer')
.from(mcp('my-tool', { description: 'Produce messages for my-tool' }))
.to(direct('my-tool'))
// ❌ Bad: mcp() without options in .from()
craft()
.id('my-tool')
.from(mcp('my-tool'))
.to(log())
Customizing Rule Severity
You can change the severity or disable rules in your ESLint config:
// eslint.config.mjs
export default [
// ... other configs
{
files: ['**/*.{js,mjs,cjs,ts}'],
plugins: { '@routecraft/routecraft': routecraftPlugin },
rules: {
// Warn instead of error
'@routecraft/routecraft/require-named-route': 'warn',
// Elevate to error
'@routecraft/routecraft/batch-before-from': 'error',
},
},
]
// Disable a rule
export default [
// ... other configs
{
files: ['**/*.{js,mjs,cjs,ts}'],
plugins: { '@routecraft/routecraft': routecraftPlugin },
rules: {
'@routecraft/routecraft/require-named-route': 'off',
'@routecraft/routecraft/batch-before-from': 'off',
},
},
]
Using Configs
The plugin provides two pre-configured rule sets:
routecraftPlugin.configs.recommended- Recommended rules for all RouteCraft projectsroutecraftPlugin.configs.all- All available rules enabled
The recommended config enables:
require-named-routeas errorbatch-before-fromas warnmcp-server-optionsas error
The all config enables all rules as errors.