bellona
v0.5.0
Published
Opt-in Oxlint JS plugins for JS/TS, React, Base UI, Zod, TanStack Query, TanStack Router, Elysia, Effect, and Tailwind.
Maintainers
Readme
bellona
Opt-in Oxlint JS plugins for TypeScript, React, Base UI, Zod, TanStack Query, TanStack Router, Elysia, Effect, and Tailwind.
Load only the stacks you use. Turn rules on by id. Nothing is enabled by default.
Oxlint JS plugins are still alpha (outside Oxlint semver). Pin
oxlintto^1.78next to bellona.
Features
- ⚡ Built for Oxlint JS plugins — fast lint, same config you already use
- 🔌 One subpath per stack — install one package, load
bellona/reactorbellona/effectonly when you need it - 🎛️ Fully opt-in — no recommended preset; you pick each
bl-<plugin>/<rule>id - 🤖 Agent-ready reports — each diagnostic has Problem, Why, Fix, and Avoid
- 🛡️ TypeScript evidence rules (widening,
unknown, unsafe dictionaries, mocks, assertions) - ⚛️ React file and JSX rules, plus Base UI
nativeButton/renderchecks - 🧭 Zod, TanStack Query, TanStack Router, Elysia, Effect v4, and Tailwind class-name rules
Plugins
| Package subpath | When to load |
| ------------------------- | ----------------------------------- |
| bellona/js | TypeScript / JavaScript evidence |
| bellona/react | React components, hooks, JSX |
| bellona/base-ui | Base UI nativeButton and render |
| bellona/zod | Zod 4 schemas |
| bellona/tanstack-query | TanStack Query current v5 APIs |
| bellona/tanstack-router | TanStack Router / Start |
| bellona/elysia | Elysia HTTP apps |
| bellona/effect | Effect v4 (effect@rc) |
| bellona/tailwind | Tailwind class strings |
Rule ids are bl-<plugin>/<rule>, for example bl-js/no-useless-reexport and bl-effect/prefer-fn.
Install
bun add -D bellona oxlintnpm, pnpm, and yarn work too. Peer: oxlint ^1.78.0. Node: ^20.19.0 || >=22.12.0.
Upgrading from 0.4? Version 0.5.0 removes and renames some rule ids. Read the upgrade notes in CHANGELOG.md before you update your Oxlint config.
Quick start
// oxlint.config.ts
import { defineConfig } from 'oxlint';
export default defineConfig({
jsPlugins: [
'bellona/js',
'bellona/react',
'bellona/base-ui',
'bellona/zod',
'bellona/tanstack-router',
'bellona/elysia',
'bellona/effect',
'bellona/tailwind',
],
rules: {
'bl-js/no-inline-import-type': 'error',
'bl-react/no-react-namespace': 'error',
'bl-base-ui/require-native-button-with-render': 'error',
'bl-zod/schema-naming': 'error',
'bl-tanstack-router/no-dynamic-to': 'error',
'bl-tanstack-router/require-hook-from': 'error',
'bl-elysia/no-context-param': 'error',
'bl-effect/prefer-fn': 'error',
'bl-tailwind/no-classname-constants': 'error',
},
});Omit a subpath you do not use. Loading a plugin does not turn its rules on — you still set each id in rules.
Many domain rules skip test and spec files. Most of them also skip files that never import that stack. Pass allow as a list of path substrings to skip extra files.
Rules
bellona/js
bl-js/no-chained-type-assertionsbl-js/no-generic-module-namesbl-js/no-inline-import-typebl-js/no-known-value-wideningbl-js/no-module-mockingbl-js/no-object-keys-assertionbl-js/no-object-parametersbl-js/no-runtime-typeof(allowInTypeGuards, defaultfalse)bl-js/no-shape-in-symbol-names(term,caseSensitive; matching is case-insensitive by default)bl-js/no-unknown-parameters(allow, default["cause"])bl-js/no-unknown-returnsbl-js/no-unknown-type-aliasesbl-js/no-unsafe-dictionary-typebl-js/no-untyped-jsonbl-js/no-useless-reexport(allow,allowRenames; renames in mixed files are allowed by default)bl-js/no-widen-then-assertbl-js/require-file-layoutbl-js/require-own-key-lookupbl-js/require-safety-comment-for-type-assertion(marker, defaultSAFETY)
rules: {
'bl-js/no-chained-type-assertions': 'error',
'bl-js/no-runtime-typeof': ['error', { allowInTypeGuards: false }],
'bl-js/no-unknown-parameters': ['error', { allow: ['cause'] }],
'bl-js/no-shape-in-symbol-names': ['error', { term: 'shape', caseSensitive: false }],
'bl-js/require-safety-comment-for-type-assertion': ['error', { marker: 'SAFETY' }],
}bellona/react
Most of these apply to .tsx / .jsx. Test and spec files are skipped.
bl-react/component-file-name-matchbl-react/component-props-typebl-react/hook-file-name-matchbl-react/no-forward-refbl-react/no-impl-component-suffixbl-react/no-jsx-iife-in-componentsbl-react/no-jsx-local-constants-in-componentsbl-react/no-jsx-module-constantsbl-react/no-jsx-variable-reassignment-in-componentsbl-react/no-multi-component-filesbl-react/no-multi-hook-filesbl-react/no-native-html(tags, optionalreplacementsmap of{ component, from })bl-react/no-react-namespacebl-react/no-render-helper-functions-in-componentsbl-react/prefer-context-as-providerbl-react/require-bare-hook-call
bellona/base-ui
bl-base-ui/no-component-as-renderbl-base-ui/require-native-button-with-render(components,nonNativeButtonComponents,buttonHosts,nonButtonHosts,requireExplicitWhenUnknown)
components and nonNativeButtonComponents replace the default part lists. buttonHosts and nonButtonHosts add names to the built-in render host lists. The rule matches JSX names (Dialog.Trigger or DialogTrigger) and does not require an @base-ui/react import.
bellona/zod
Test and spec files are skipped.
bl-zod/modern-format-validatorsbl-zod/no-deprecated-v4-apisbl-zod/schema-naming
bellona/tanstack-query
Nine opt-in rules target React Query and Query Core 5.103.2. Tests are checked too.
Update older v5 versions before enabling rules that require query and infiniteQuery.
bl-tanstack-query/exhaustive-depsbl-tanstack-query/no-deprecated-client-methodsbl-tanstack-query/no-deprecated-importsbl-tanstack-query/no-deprecated-query-contextbl-tanstack-query/no-deprecated-resultsbl-tanstack-query/no-removed-optionsbl-tanstack-query/no-rest-destructuringbl-tanstack-query/no-unstable-depsbl-tanstack-query/stable-query-client
See the Query rule guide for setup, replacements, source links, options, and detection limits.
bellona/tanstack-router
Test and spec files are skipped.
bl-tanstack-router/create-route-property-orderbl-tanstack-router/no-control-flow-outside-edgebl-tanstack-router/no-deprecated-apisbl-tanstack-router/no-dynamic-tobl-tanstack-router/no-get-route-apibl-tanstack-router/no-hrefbl-tanstack-router/no-imperative-location-navigationbl-tanstack-router/no-loader-data-in-not-foundbl-tanstack-router/no-not-found-in-componentbl-tanstack-router/no-not-found-routebl-tanstack-router/no-relative-to-without-frombl-tanstack-router/no-search-in-loaderbl-tanstack-router/no-type-assertionbl-tanstack-router/no-whole-search-loader-depsbl-tanstack-router/require-hook-frombl-tanstack-router/require-inline-route-optionsbl-tanstack-router/require-throw-not-foundbl-tanstack-router/require-throw-redirect
bellona/elysia
Test and spec files are skipped. Files that do not import elysia are skipped except bl-elysia/no-elysia-factory-function (path-gated). bl-elysia/prefer-eden-treaty-in-tests runs only on test files.
bl-elysia/hook-after-routesbl-elysia/no-context-parambl-elysia/no-cookie-undefined-checkbl-elysia/no-decorate-singletonsbl-elysia/no-elysia-factory-functionbl-elysia/no-functional-plugin-callbackbl-elysia/no-set-redirectbl-elysia/one-route-method-per-file(routes/leaf files)bl-elysia/prefer-eden-treaty-in-testsbl-elysia/prefer-resolve-for-auth(/plugins/paths)bl-elysia/prefer-status-helperbl-elysia/prefer-throw-statusbl-elysia/require-error-body-literalbl-elysia/require-plugin-name(also skips/main.ts,/server.ts,/index.ts,/app.ts)bl-elysia/require-response-schema(requireAllRoutes, defaulttrue)bl-elysia/require-route-export-name(routes/leaf files)bl-elysia/require-route-schema(methods, defaultpost/put/patch)bl-elysia/routes-index-mount-only(routes/indexfiles)bl-elysia/status-code-in-response
bellona/effect
Files that do not import effect, effect/*, or @effect/* are skipped. Some style rules also skip test files (marked below).
bl-effect/effect-functions-in-servicesbl-effect/max-service-methodsbl-effect/max-services(at most one service per file, including test files)bl-effect/no-date-now(test files skipped)bl-effect/no-duplicate-layer-constructionbl-effect/no-eager-acquirebl-effect/no-effect-run-in-testsbl-effect/no-floating-effectbl-effect/no-fork-detachbl-effect/no-forwarding-servicebl-effect/no-inferred-service-contractbl-effect/no-interpolated-log-messagebl-effect/no-it-scopedbl-effect/no-log-and-rethrowbl-effect/no-module-level-mutable-statebl-effect/no-new-error-in-effectbl-effect/no-per-call-cache-constructionbl-effect/no-return-effect-in-genbl-effect/no-run-promise-in-modules(entry, default/main.ts/server.ts/index.ts/app.ts/runtime.ts; test files skipped)bl-effect/no-service-make-factorybl-effect/no-silent-catch-causebl-effect/no-status-in-tagged-errorbl-effect/no-throw-in-genbl-effect/no-try-catch-in-genbl-effect/no-v3-apisbl-effect/no-v3-importsbl-effect/no-v3-service-tagsbl-effect/no-yield-ref-handlebl-effect/prefer-clock-sleep(test files skipped)bl-effect/prefer-decode-unknownbl-effect/prefer-fnbl-effect/prefer-fn-untraced-in-callbacksbl-effect/prefer-predicate(test files skipped)bl-effect/prefer-schema-tagged-error(test files skipped)bl-effect/prefer-service-of(test files skipped)bl-effect/prefer-try-promise(test files skipped)bl-effect/prefer-vitest(test files only)bl-effect/require-bounded-concurrencybl-effect/require-bounded-retrybl-effect/require-defect-causebl-effect/require-fn-namebl-effect/require-fn-owner-prefixbl-effect/require-fn-return-annotationbl-effect/require-gen-self-optionsbl-effect/require-ignore-logbl-effect/require-promise-abort-signalbl-effect/require-redacted-secret-configbl-effect/require-return-yield-on-failbl-effect/require-service-filename(requires.service.ts, including test files)bl-effect/require-service-id-path(test files skipped)bl-effect/require-service-static-layer(test files skipped)bl-effect/require-timeout-on-external-iobl-effect/schema-no-legacy-filter(filter/filterEffect/rename/encodedSchema/typeSchema)bl-effect/schema-union-array
bellona/tailwind
No import gate. Test and spec files are skipped. Prefer tv / createTV from tailwind-variants, or a reusable component.
bl-tailwind/no-classname-constants(minUtilities, default2;allowedCallees, defaulttv/createTV)bl-tailwind/no-dynamic-class-constructionbl-tailwind/no-v3-arbitrary-var
Agent skill
Coding agents can install the bellona skill from skills.sh:
npx skills add DobroslavRadosavljevic/bellona --skill bellonaUse --skill bellona so only this skill is installed.
Rule speed bench
Time every Bellona rule on a mixed synthetic corpus (JS, React, Effect, Elysia, TanStack Router, Tailwind, Zod, Base UI):
bun run bench
bun run bench -- --plugin js --rule no-useless-reexport
bun run bench -- --scale 16 --repeat 3 --jsonnet is rule time minus a baseline that loads all plugins with rules off.
