Tooling

Linting

The compiler checks your code and the lint plugin only shows its errors in your editor.

mion has no linter of its own. The compiler that builds your code also checks it. The lint plugin in @mionjs/devtools only shows the compiler's errors in your editor, through OXlint (its main target) or ESLint v9 flat config.

Compiler Checks

This is not a linter added on top of your build. The checks a linter would usually run are part of the compiler, so your build fails on them too.

import {registerPureFn} from '@mionjs/run-types/runtime';

const rate = 2;
// A pure function may only use its own arguments.
export const double = registerPureFn((price: number) => price * rate);

Your editor shows:

src/pure.ts:5:65: error mion(runtime-error): [purefn-reads-outer-variable] `rate` is captured from outer scope inside a pure-fn factory; pure functions can't reach outside their own body.
src/validators.ts:4:50: error mion(runtime-error): [validate-symbol-root] Type `Symbol` can never be validated: the generated function will always fail.
src/validators.ts:8:10: error mion(error): [marker-in-generic-function] Marker call is inside a generic function: the type argument is unresolved, so no id can be computed at build time.

Your build prints the same three errors and stops. Look up any name in All Diagnostics.

Lint Rules

The plugin has four rules, one per level. A rule does not decide what is checked. It only shows every error of its level.

RuleDefaultWhat it shows
mion/errorerrorEvery Error: no code was made, so the build stops. Also a tsconfig that does not load.
mion/runtime-errorerrorEvery RuntimeError: the code throws when called, or no longer checks what you asked for, so the build stops. The dev server keeps running.
mion/warningwarnEvery Warning, and every error you lowered, marked (downgraded).
mion/infooffEvery Info message: documented behaviour or advice.
Every app gets the same checks. You can turn a whole level off in your editor, but the build still stops on errors.

Turning Off One Error

The lint config cannot turn off one check. This fails to load:

.oxlintrc.json
{
  "rules": {"mion/validate-symbol-root": "off"}
}
x Rule 'validate-symbol-root' not found in plugin 'mion'

A lint disable comment only hides the error in your editor. The build still stops on validate-symbol-root:

// eslint-disable-next-line mion/runtime-error
export const isSymbol = createValidateFn<symbol>();

Use a comment the compiler reads instead. Your editor and your build both follow it:

// @mion-expect-error validate-symbol-root
export const isSymbol = createValidateFn<symbol>();

To lower one everywhere, list its name in downgradeErrors in your tsconfig. The linter reads it too, but not the build plugin's downgradeErrors option. An Error, like marker-in-generic-function, can never be turned off: fix the code. Error Levels shows every option.

Lint Plugin Setup

Extend the recommended config from @mionjs/devtools to turn on the four rules at their defaults.

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "extends": ["./node_modules/@mionjs/devtools/oxlint-recommended.json"]
}

Showing Info Messages

Turn on mion/info to see Info messages in your editor. The levels key in your tsconfig shows them in the build.

{
  "rules": {"mion/info": "warn"}
}

Choosing the TypeScript Config

The plugin reads your full project tsconfig.json, so the linter checks types the way your build does: lib, target, strict, module resolution, custom conditions and path mappings all apply. By default it finds the config the way tsc does, searching upward from the directory the linter runs in. Point it at a different config with a setting:

{
  "settings": {"mion": {"tsconfig": "tsconfig.lint.json"}}
}
A broken config is a config-tsconfig-not-loaded error. Fix it and the next lint pass picks it up.

Checking from the CLI

The CLI reports the same errors, for CI jobs or scripts that do not run a node linter:

mion compile --no-emit
mion enrich src/.mion/enriched/friendly/src/models/user.ts --no-emit --json
mion enrich --no-emit --json

compile --no-emit reports the build's marker and JSON round trip problems and writes nothing. enrich --no-emit on one file reports its tag, content and drift problems, and exits 1 on errors. Both hide Info messages unless your tsconfig sets levels: 'all'; enrich --json keeps them. With a directory, or no path, enrich --no-emit checks the whole mirror directory for drift.

compile groups its messages by name. Add --log-style lines for one line per message, the file(line,col): error name form CI tools read.

Copyright © 2026