eslint-plugin-legion
v0.5.0
Published
LEGION-STANDARDS as a lint plugin. Runs under oxlint (jsPlugins) and ESLint v9+. Zero comments, no React.FC, no enum, no ternaries, no function keyword, guarded context hooks, hydration-safe state, server-first pages, scoped lint bypasses.
Maintainers
Readme
eslint-plugin-legion
LEGION-STANDARDS as a
lint plugin. One package, two runtimes: it is an ESLint v9+ plugin, and
oxlint's jsPlugins runs ESLint plugins as-is, so the same rules with the
same diagnostics run under either. CI on this repo proves the two agree on
every fixture.
What it enforces: components that render and never think (no state,
effects, fetching, handler bodies or projections in a component), zero
comments, no React.FC, no enum, no ternaries, no function keyword,
guarded context hooks, hydration-safe useState, server-first
page/layout files, no literal conditions in JSX, no manual memoization
when the React Compiler is on, no service-role client in browser code, a 180-line
component cap, any only on declared boundary paths, and lint bypasses
that are scoped, named, explained, and impossible for locked rules. See
legion-rules for the rule
table and the tolerance levels.
oxlint
yarn add -D eslint-plugin-legion oxlint// .oxlintrc.json
{
"jsPlugins": ["eslint-plugin-legion"],
"rules": {
"legion/no-narrative-comments": "error",
"legion/no-enum": "error",
},
}To get a full preset instead of listing rules, generate the config from
legion-rules:
// scripts/sync-oxlintrc.ts
import { writeFileSync } from 'node:fs';
import { strict } from 'legion-rules';
writeFileSync('.oxlintrc.json', JSON.stringify(strict.oxlint, null, 2));ESLint
yarn add -D eslint-plugin-legion eslint @typescript-eslint/parser// eslint.config.mjs
import legion from 'eslint-plugin-legion';
import * as tsParser from '@typescript-eslint/parser';
export default [
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsParser,
parserOptions: { ecmaFeatures: { jsx: true } },
},
},
...legion.configs.strict,
];legion.configs.recommended is the softer preset. For your own levels:
import { legionEslintConfig } from 'eslint-plugin-legion';
export default [
...legionEslintConfig({ rules: { 'no-enum': 3 }, noTernary: 2 }),
];Pass { typescriptEslint: tsPlugin } as the second argument to also wire
@typescript-eslint/no-explicit-any with the boundary overrides.
The bypass
A rule at level 2 can be bypassed on one line, and only like this:
// oxlint-disable-next-line legion/no-enum -- generated by the API clientFile-wide disables, unnamed disables, disables without a reason, and any
disable of a rule at level 3 fail lint through legion/scoped-disables. The
one shape no lint rule can see, a bare /* eslint-disable */ at the top of a
file, is caught by
legion-audit directives; put
it in front of your linter in the lint script.
Requirements
Node 22 or newer. Peer: oxlint >= 1.53 (JS plugins) or eslint >= 9, both
optional. No runtime dependencies beyond legion-rules.
MIT, Copyright (c) 2026 Veracium LLC.
