@useinsider/eslint-config
| Install | |
|---|---|
npm install @useinsider/eslint-config |
|
| Latest Version: | 4.1.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 12, 2026 |
| Links: | npm |
@useinsider/eslint-config
Shared lint configuration for Insider projects, split across two linters.
useInsider() takes a single scope-keyed config object and returns two
renderers: useInsiderESLint() for ESLint 9 flat config and useInsiderOXC()
for oxlint. Every rule oxlint implements natively is turned off on the
ESLint side and enabled on the oxlint side from the same description, so the
two configs cannot drift. There is no opaque preset list, no freeform config
escape hatch, and no expectation that you spread either result into your own
array.
Because ESLint alone would now silently skip every rule oxlint owns, both
linters must run. useInsider() refuses to start if the oxlint config is
missing.
The three-file layout
useInsider() is called from exactly one module, and each linter's config is
a thin re-export of its renderer:
// insider.lint.ts
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
// scopes go here
});
// eslint.config.ts
import { useInsiderESLint } from './insider.lint.ts';
export default useInsiderESLint();
// oxlint.config.ts
import { useInsiderOXC } from './insider.lint.ts';
export default await useInsiderOXC();
Then run both:
oxlint --disable-nested-config && eslint .
Three constraints this layout is not free to break:
- The
./insider.lint.tsimport keeps its real extension. Neither ESLint's loader nor oxlint's applies TypeScript's.js→.tsspecifier rewriting, so'./insider.lint.js'does not resolve. - The ESLint config needs a TypeScript extension too —
eslint.config.tsoreslint.config.mts. ESLint routes those through jiti, which resolves the TypeScript lint module on every supported Node version. A.js/.mjs/.cjsconfig is loaded by Node directly, so importing./insider.lint.tsfrom one works only where Node's type stripping is unflagged (Node >= 22.18) and dies withERR_UNKNOWN_FILE_EXTENSIONon Node 20. Both CLI commands therefore rename an existing JavaScript flat config and delete the original. - The oxlint config must be
oxlint.config.tsoroxlint.config.mts. oxlint silently ignoresoxlint.config.{cts,js,mjs,cjs}and falls through to the nearest ancestor config. In a package with no"type": "module", use.mtsfor both generated files to avoid Node's module-type warning. - A TypeScript project needs
allowImportingTsExtensions: truein thetsconfig.jsonthat covers the layout files, ortscrejects the import withTS5097. Both CLI flows offer to set it for you, followingextendsfirst so a shared base that already carries it is left alone. TypeScript only accepts the flag on a project withnoEmit,emitDeclarationOnlyorrewriteRelativeImportExtensions— on an emitting project it fails withTS5096— so a project that emits is never edited; the CLI explains what to turn on instead. oxlintruns with--disable-nested-config. The emitted config carries options oxlint accepts only in a root config, and a discovered nested config carrying them aborts the whole run. Disabling nested discovery also makes the config's ownignorePatternsauthoritative. In a monorepo each package runs its ownlintscript, so every config is the root of its own run.
Do not await useInsiderESLint(). Do not spread either renderer. Do not
wrap them in an array. If you find yourself wanting to append a block, that
is a sign your scope coverage is incomplete — describe the extra files inside
useInsider({...}) instead.
Table of Contents
- The three-file layout
- Prerequisites
- Quick start
- Manual setup
- API reference
- Unused disable directives
- Lint scripts
- Migrating to v4
- Migrating from v1.x
- First lint after install
- Troubleshooting
- Contributing
Prerequisites
- Node.js 24.19+
- ESLint 9+
- oxlint 1.87+
- pnpm, npm, or yarn
Quick start
npx @useinsider/eslint-config
The CLI walks an interactive prompt (project type, language, framework, test
runner, jQuery globals, tsconfig layout), installs every required linter and
plugin pinned to the versions this package was built against, writes the three
layout files at the project root, wires oxlint into the script that actually
invokes ESLint (see Lint scripts), and offers to add
allowImportingTsExtensions: true to the tsconfig that really governs those
files. In a solution-style project that is the referenced tsconfig.node.json,
not the root — and if an applicable config already sets the flag, or no tsconfig
type-checks those files, you are not asked at all. That edit is surgical —
comments, key order and indentation survive — and declining it prints the exact
snippet to paste.
To migrate an existing eslint.config.{js,mjs,cjs} — v3's scope-keyed call or
v1's useInsider({ preset, config }) — or a legacy .eslintrc.{js,cjs}:
npx @useinsider/eslint-config migrate [path]
The migrate flow auto-detects the source format, prints a summary plus any
dropped custom rules, asks for confirmation, then splits the config into the
three-file layout: the useInsider() call moves into insider.lint.* and
both linter configs become thin re-exports.
A v3 config takes the shorter route. v4 accepts the same options v3 did,
so the call is re-housed rather than re-rendered: the file is copied into
insider.lint.* byte-for-byte and only the export statement changes to
export const { useInsiderESLint, useInsiderOXC } = useInsider({ ... }). Your
comments, satisfies clauses, computed values and formatting survive. An
await on the call and an array wrapping it are dropped, since useInsider()
is synchronous in v4 and returns the two renderers. Shapes with nowhere to go
in a lint module are refused by name rather than half-migrated: more than one
useInsider() call, a default export mixing the call with hand-written
flat-config blocks, a defineConfig() wrapper, and a call reached through a
variable. An .eslintignore beside a v3 config is left alone — flat config
never read it, and the re-housed call is not rewritten to fold it in.
Legacy .eslintrc.{js,cjs} is also handled (custom rules and
non-@useinsider/eslint-config/* extends are dropped with a loud warning so
you can review what changed). Legacy .eslintrc.{json,yml,yaml} are not
supported in this release — convert them to .eslintrc.{js,cjs} first.
Migrate also rewrites your source: an // eslint-disable-next-line <rule>
naming a rule oxlint has taken over becomes // oxlint-disable-next-line <rule>. This is not cosmetic — the emitted oxlint config refuses the
eslint-disable spelling (see Unused disable
directives), so an un-migrated comment suppresses
nothing on either side. A comment listing rules from both sides is split into
one comment per linter. A -line or -next-line directive targets one exact
physical line, so both halves stay on a single line as block comments — a
newline between them would point the first half at the second instead of at
the code. Only a whole-file eslint-disable is split across two lines. The rewrite reads each
file's syntax tree and touches only real comment nodes, so a directive spelled
out inside a string or template literal is left alone.
Example insider.lint.ts for a Node + TypeScript project:
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
typescriptNode: [
{
files: ['**/*.{ts,tsx}'],
tsconfigPaths: ['tsconfig.json'],
},
],
});
What the CLI installs for that answer set (versions reflect the publish-time pins):
@useinsider/eslint-configeslintoxlintglobalstypescripttypescript-eslint
Vue, Jest, or Vitest answers add the matching plugin set (e.g. eslint-plugin-vue,
vue-eslint-parser, eslint-plugin-vue-scoped-css, eslint-plugin-jest,
or @vitest/eslint-plugin).
Manual setup
Install the package first:
pnpm add -E -D @useinsider/eslint-config oxlint
Then create the three layout files and call
useInsider(...) from insider.lint.ts with the scopes your project needs.
Each scope holds an array of { files, ... } entries; each entry becomes its
own block in both emitted configs.
Every example below shows the body of insider.lint.ts; the two linter
configs never change.
Node + TypeScript
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
typescriptNode: [
{
files: ['**/*.{ts,tsx}'],
tsconfigPaths: ['tsconfig.json'],
},
],
vitest: [
{ files: ['**/*.{test,spec}.{ts,tsx}'] },
],
});
Browser + Vue 3 + TypeScript with split tsconfigs
A project using tsconfig.app.json for source and tsconfig.test.json for
tests, with RouterLink and RouterView as globally-registered components.
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
vue3Typescript: [
{
files: ['src/**/*.{ts,tsx,vue}'],
tsconfigPaths: ['tsconfig.app.json'],
globalComponents: ['RouterLink', 'RouterView'],
},
],
vitest: [
{
files: ['**/*.{test,spec}.{ts,tsx}'],
},
],
typescriptNode: [
{
files: ['*.config.{ts,mts,cts}'],
tsconfigPaths: ['tsconfig.node.json'],
},
],
});
Multiple tsconfigPaths entries are supported per block — pass an array of
paths if a single scope needs more than one project reference.
Vue 2 + JavaScript with jQuery globals
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
vue2: [
{ files: ['src/**/*.{js,vue}'] },
],
javascriptNode: [
{ files: ['*.{js,cjs,mjs}'] },
],
jquery: [
{ files: ['src/**/*.{js,vue}'] },
],
});
jquery is a top-level scope. Pass an array of { files } entries describing
the globs that should see the jQuery globals ($, jQuery). The block is
emitted after the scope blocks and before strict/spellChecker, so the
matching language scope still applies its rule set and jQuery globals are
layered on top of it.
Strict and spell-checker knobs
strict and spellChecker are flat cross-cutting options. They sit at the
top level of the config, not inside a scope entry.
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
typescriptNode: [
{
files: ['**/*.{ts,tsx}'],
tsconfigPaths: ['tsconfig.json'],
},
],
strict: ['src/**/*.ts'],
spellChecker: {
ignoredPaths: ['vendor/**', 'dist/**'],
customWordListFile: './cspell.custom.json',
},
silenceRules: ['no-console'],
});
strict: trueapplies strict rules to every file.strict: string[]applies strict rules only to the matching paths.spellChecker.customWordListFilere-points@cspell/spellcheckerto a project-local word list.spellChecker.ignoredPathsturns the spell-checker off for the matching globs (the off block is emitted after the custom-list block, so ignore wins on overlap).silenceRulesrewrites the severity of the listed rule IDs to'warn'across every emitted block while preserving each rule's options.
API reference
useInsider(config) accepts the following top-level properties. Each scope
holds an array of entries; each entry produces its own flat-config block.
Every scope entry shape also accepts two common fields:
ignores?: string[]— applies as the block'signoreskey, scoping the rule set away from the listed globs.globals?: Record<string, GlobalConf>— merged on top of the globals the preset already provides; pass'readonly','writable', or'off'per ESLint'sLinter.Globals. Use this to declare project-specific globals likeAPP_URL,axios,atatus.
| Property | Entry shape | Notes |
|---|---|---|
ignores |
string[] (top-level) |
Emits a leading { ignores } block applied to every other block. |
javascript |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Environment-agnostic JS (workers, isomorphic). |
javascriptDom |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Browser JS. |
javascriptNode |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Node JS. |
typescript |
{ files: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
Environment-agnostic TS. |
typescriptDom |
{ files: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
Browser TS. |
typescriptNode |
{ files: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
Node TS. |
vue2 |
{ files: string[]; globalComponents?: string[]; ignores?: string[]; globals?: Linter.Globals } |
Vue 2 single-file components in JS. |
vue3 |
{ files: string[]; globalComponents?: string[]; ignores?: string[]; globals?: Linter.Globals } |
Vue 3 single-file components in JS. |
vue2Typescript |
{ files: string[]; globalComponents?: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
Vue 2 + TS. |
vue3Typescript |
{ files: string[]; globalComponents?: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
Vue 3 + TS. |
jest |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Jest test files. |
vitest |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Vitest test files. |
jsdocBasedTs |
{ files: string[]; tsconfigPaths: string[]; ignores?: string[]; globals?: Linter.Globals } |
JS files type-checked via JSDoc. |
jquery |
{ files: string[]; ignores?: string[]; globals?: Linter.Globals } |
Layers jQuery globals ($, jQuery) on top of the matching globs. Per-entry globals merge with the jQuery ones rather than replacing them. |
strict |
boolean | string[] |
Applies the strict preset globally (true) or scoped to globs. |
spellChecker |
{ ignoredPaths?: string[]; customWordListFile?: string } |
Cross-cutting cspell overrides. |
silenceRules |
string[] |
Rule IDs to demote to 'warn' across every block. |
configFiles |
false | string[] |
Emits a dedicated block for root-level configuration files (*.config.*, eslint.config.*) that disables import-x/no-extraneous-dependencies and import-x/prefer-default-export. Defaults are applied automatically — omit the key (or pass []) to use the built-in defaults only, pass a string[] to layer your own globs on top, or pass false to opt out entirely. |
A scope entry with per-entry globals — declare project-specific bindings
without spreading the useInsider return into your own array:
import { useInsider } from '@useinsider/eslint-config';
export const { useInsiderESLint, useInsiderOXC } = useInsider({
ignores: ['dist', 'coverage'],
javascriptDom: [
{
files: ['src/**/*.js'],
ignores: ['src/vendor/**'],
globals: {
APP_URL: 'readonly',
axios: 'readonly',
atatus: 'readonly',
},
},
],
});
Block-emission order — the leading ignores block, then scope blocks (in the
order their keys were inserted into the config object), then jquery blocks,
then the configFiles block, then any strict blocks, then spellChecker
blocks. Later blocks win on overlap, matching ESLint's flat-config precedence
model.
This order is why configFiles — not a scope — is the right place to relax
rules for configuration files: its block is emitted after every scope block,
so its overrides survive. A scope listing the same globs is emitted before
the language scopes and gets overridden by any of them that also match.
Removed in v2: the
configscope. It only switchedimport-x/no-extraneous-dependenciesandimport-x/prefer-default-exportoff, and because it was emitted with the scope blocks it was silently overridden for every file a language scope also matched — which, for.js/.ts, was all of them. Passingconfignow throws with a pointer toconfigFiles. Move its globs toconfigFiles: [...]; root-level*.config.*andeslint.config.*are already covered by the defaults, so most projects can simply drop the key.
useInsider is synchronous and returns { useInsiderESLint, useInsiderOXC }.
useInsiderESLint() returns Promise<Linter.Config[]> — export it without
awaiting, ESLint 9 resolves it at load time. useInsiderOXC() also returns a
Promise, and oxlint does not await a default export, so oxlint.config.ts
must use top-level await. The UseInsiderResult type alias is exported for
TypeScript callers that wrap the renderers in a helper of their own.
Two file-matching differences between the linters are handled for you, but worth knowing:
- An unanchored oxlint glob matches at any depth, so
files: ['*.mjs']would also claim.husky/install.mjs. The emitted oxlint config anchors every glob that does not already start with**, restoring ESLint's semantics. - oxlint discovers a nested
oxlint.config.*in any subdirectory and lints that directory with it, ignoring the parent config'signorePatterns. The generated lint script passes--disable-nested-config, which turns that off and makes the root config'signorePatternsauthoritative. It is also required rather than optional: the emitted config carries root-onlyoptions, and oxlint aborts with a config-parse error the moment it discovers those in a nested file — which a monorepo whose packages each ran this CLI always has. Run oxlint once per package, from that package.
Unused disable directives
Both linters report a disable comment that no longer suppresses anything, and each one judges only its own spelling:
- The emitted oxlint config sets
options.reportUnusedDisableDirectivestowarn, so a stale// oxlint-disable-*is reported. - The emitted ESLint config pins
linterOptions.reportUnusedDisableDirectivestowarn, so a stale// eslint-disable-*is reported.
It also sets options.respectEslintDisableDirectives: false, which is what
makes the pair sound. oxlint judges an eslint-disable against the rules it
ran, so with the default (true) a comment naming @cspell/spellchecker,
sonarjs/*, @stylistic/* or jsdoc/check-tag-names — every rule the split
deliberately keeps on the ESLint side — is reported as unused. Refusing the
eslint-disable spelling removes that false positive entirely, at the cost of
making the ownership split explicit in your source: a rule oxlint owns must be
suppressed with oxlint-disable, a rule ESLint owns with eslint-disable.
migrate performs that rewrite for you; an un-migrated comment is reported by
ESLint as an unused directive, so nothing fails silently.
Neither severity fails the build on its own. Escalate with
oxlint --deny-warnings and eslint . --max-warnings 0 if you want that.
silenceRules() (named export) is also available as a global registration
hook. Call it at module top level to register rule IDs to demote before
useInsider is invoked; the per-call silenceRules: [...] option layers
on top.
import { useInsider, silenceRules } from '@useinsider/eslint-config';
silenceRules(['no-magic-numbers']);
export const { useInsiderESLint, useInsiderOXC } = useInsider({ /* ... */ });
Lint scripts
Both linters have to run, and oxlint has to run first — the ESLint side turns off every rule oxlint owns, so ESLint alone silently drops those checks. The canonical script is:
{ "scripts": { "lint": "oxlint --disable-nested-config && eslint ." } }
The CLI never replaces a script it did not author. It finds the one script
that really invokes the eslint binary and prepends oxlint to it, leaving
the original command verbatim inside the new value. A composed lint that fans
out to a type-check and a stylelint pass is therefore untouched:
"lint": "run-p lint:type lint:es lint:style",
"lint:es": "npm run lint:es-ci -- --cache",
- "lint:es-ci": "NODE_OPTIONS=--max-old-space-size=6144 eslint .",
+ "lint:es-ci": "oxlint --disable-nested-config && NODE_OPTIONS=--max-old-space-size=6144 eslint .",
"lint:style": "npm run lint:style-ci -- --cache",
"lint:type": "run-p lint:type:app lint:type:test lint:type:node",
Prepending rather than appending is deliberate: a -- passthrough
(npm run lint:es-ci -- --cache) lands on the last command, so --cache
still reaches ESLint. A script whose name ends in a --fix invocation gets
oxlint --disable-nested-config --fix. Every rewrite is printed with its
previous value.
Exactly one script is rewritten. When several entry points run ESLint —
lint, lint:fix, lint:ci — prepending to all of them would run oxlint once
per entry point over the same tree, so the CLI picks the entry point lint
designates (itself, or the leaf it delegates to) and reports the rest with the
prefix to add if you want oxlint on them too.
If no script invokes eslint and a lint script already exists, nothing is
written — the conflict is reported with the command to add by hand.
Migrating to v4
v4 requires Node.js 24.19+ (engines.node was >=20 through v3). Check
your CI images and any repo pinning an older Node before upgrading: npm and
pnpm warn with EBADENGINE, and fail outright under engine-strict.
v4 splits the lint run across ESLint and oxlint. Run the CLI to do all of this automatically:
npx @useinsider/eslint-config migrate
The guided migration offer
You do not have to know that command exists. Running lint against a v3 config under v4 detects the outdated structure and, in an interactive terminal, offers to run the migration there and then:
@useinsider/eslint-config: eslint.config.js still uses the v3 single-file layout.
/repo/eslint.config.js calls `useInsider()` itself, and
insider.lint.ts and oxlint.config.ts are missing.
Starting the guided migration. It shows every change before applying it.
Skip it with INSIDER_NO_PROMPT=true.
◆ eslint.config.js still uses the v3 single-file layout. Migrate it now?
│ ● Yes / ○ No
Accepting runs the normal migrate flow — same summary, same confirmations —
and then exits non-zero asking you to re-run lint, because this process already
loaded the configuration that was just rewritten.
The offer is suppressed, and the failure message leads with the migrate
command instead, whenever prompting would be wrong: under oxlint (whose stdout
is its report), in CI, without a TTY, with INSIDER_NO_PROMPT=true, or with
INSIDER_SKIP_CONFIG_VALIDATION=true.
The directive rewrite is reported as its own step, with the file count shown
before anything is written, so you can take it as a separate commit or
decline it and run migrate again later.
What changes:
-
eslint-disableno longer suppresses an oxlint-owned rule. This is the largest behavior change in v4. The emitted oxlint config setsoptions.respectEslintDisableDirectives: false, so oxlint reads only theoxlint-disablespelling. A comment left in the v3 form suppresses nothing, and because ESLint no longer runs that rule it reports the comment as an unused directive rather than failing quietly.migraterewrites the affected comments for you (AST-based — it touches only real comment nodes).- // eslint-disable-next-line no-console -- CLI banner + // oxlint-disable-next-line no-console -- CLI banner console.info(banner);Rules the split keeps on the ESLint side —
@cspell/*,sonarjs/*,@stylistic/*,jsdoc/check-tag-names,no-inner-declarations, every type-aware@typescript-eslintrule — keep theeslint-disablespelling. See Unused disable directives for why refusing the eslint spelling is what makes stale-directive reporting sound in both directions. -
useInsider()is now synchronous and returns two renderers. A v3export default useInsider({...})no longer produces a flat-config array.- // eslint.config.mjs - import { useInsider } from '@useinsider/eslint-config'; - - export default useInsider({ typescriptNode: [{ files: ['src/**/*.ts'], tsconfigPaths: ['tsconfig.json'] }] }); + // insider.lint.ts + import { useInsider } from '@useinsider/eslint-config'; + + export const { useInsiderESLint, useInsiderOXC } = useInsider({ + typescriptNode: [{ files: ['src/**/*.ts'], tsconfigPaths: ['tsconfig.json'] }], + }); -
Two new sibling files —
eslint.config.*andoxlint.config.*, each a thin re-export. See the three-file layout. -
The lint command runs both linters:
oxlint --disable-nested-config && eslint ..--disable-nested-configis required, not stylistic. Without it oxlint discovers any nestedoxlint.config.*in the tree and lints that subtree with it, bypassing the root config'signorePatterns. Worse, the emitted config carriesoptions.*, which oxlint accepts only in a root config — a nested config carryingoptionsaborts the whole run, even when its directory is listed inignorePatterns. -
Monorepo consumers run oxlint per package. oxlint has no workspace mode, so each package owns its own three files and its own
lintscript, and each script carries--disable-nested-config. Running one oxlint pass from the repo root over sibling packages is not equivalent. -
Only
oxlint.config.tsandoxlint.config.mtsare discovered. Any other extension —.js,.mjs,.cjs,.cts— is ignored without a word, and oxlint falls through to the nearest ancestor config. The config appears live while none of its rules run.ensureOxlintConfig()fails the ESLint side when neither accepted filename sits beside the lint module, so the mistake surfaces rather than silently disarming half the rule set. -
oxlintis version-tracked likeeslintandglobals. A consumer whose installedoxlintis older than this package's pin is upgraded automatically on the next lint run, because an older oxlint silently drops whatever rules that release did not yet implement. Opt out withsilenceDependencyWarning(['oxlint']). -
tsconfig.jsongainsallowImportingTsExtensions: true.eslint.config.tsandoxlint.config.tsmust import the lint module by its real extension — nothing here applies TypeScript's.js→.tsrewriting — andtscfails withTS5097otherwise. The CLI prompts to add it and resolves the fullextendschain first, so a shared base that already sets it is left alone; declining prints the snippet to paste. A project that emits is never edited — TypeScript rejects the flag there withTS5096. -
@typescript-eslinttype-aware rules,sonarjs,@stylistic,@cspell,jsdoc/check-tag-namesandno-inner-declarationsstay with ESLint. Two of those are permanent, for different reasons:jsdoc/check-tag-namesis driven bysettings.jsdoc.tagNamePreference(the TypeScript scopes remap@desc/@callbackonto@use/@watch), and oxlint has no equivalent of flat-configsettings.no-inner-declarationsis implemented by both, but they disagree on the default for an omitted option: ESLint 9 defaultsblockScopedFunctionstoallow, oxlint todisallow. Handing oxlint the bare'error'would reject every block-scoped function declaration ESLint accepts.
-
Stale disable directives warn on both sides, and neither fails the build on its own. Escalate with
oxlint --deny-warningsandeslint . --max-warnings 0.
Migrating from v1.x
This section describes the v1 → v2 step. The scope-keyed config object it introduces is still the v4 shape; only the wiring around the call changed again, which Migrating to v4 covers. Do both steps if you are coming from v1.
-
preset: string[]→ top-level scope properties. The order in the v1presetarray no longer matters — the new shape has one property per scope and overrides are explicit.- await useInsider({ - preset: ['typescript-node'], - config: { files: ['src/**/*.ts'], languageOptions: { parserOptions: { project: ['./tsconfig.json'] } } }, - }) + useInsider({ + typescriptNode: [ + { files: ['src/**/*.ts'], tsconfigPaths: ['tsconfig.json'] }, + ], + }) -
preset: ['vue3-typescript']+config→vue3Typescriptentry with a dedicatedglobalComponentsfield. No more hand-rolledvue/no-undef-componentsoverrides.- await useInsider({ - preset: ['vue3-typescript'], - config: { - files: ['src/**/*.{ts,vue}'], - languageOptions: { parserOptions: { project: ['./tsconfig.app.json'] } }, - rules: { 'vue/no-undef-components': ['error', { ignorePatterns: ['^RouterLink$', '^RouterView$'] }] }, - }, - }) + useInsider({ + vue3Typescript: [ + { + files: ['src/**/*.{ts,vue}'], + tsconfigPaths: ['tsconfig.app.json'], + globalComponents: ['RouterLink', 'RouterView'], + }, + ], + }) -
preset: ['strict']→ top-levelstrict: true | string[].- await useInsider({ preset: ['typescript-node', 'strict'], config: { files: ['src/**/*.ts'] } }) + useInsider({ + typescriptNode: [{ files: ['src/**/*.ts'], tsconfigPaths: ['tsconfig.json'] }], + strict: true, + })Behavior change: in v1 the
strictpreset was always global when listed. In v2,strict: truekeeps that global behavior;strict: string[]scopes the strict rule set to the matching globs only. If you have paths-targeted strict checks, setstrict: [...]instead ofstrict: true. -
Shape change:
useInsider(...)stopped being an awaited, spreadable array. In v2 it becameexport default useInsider({...}); in v4 it became the renderer destructuring shown in Migrating to v4. Either way there is no supported override path — describe every glob you want linted inside the call, including jQuery files via the dedicatedjqueryscope. -
Removed: the
presetarray, theconfig: Linter.Configescape hatch, and theconfigpreset itself. Configuration-file linting is now an ordinarytypescriptNode(orjavascriptNode) entry pointed at the right tsconfig. -
Kept:
silenceRulesaccepts the samestring[]shape both as a top-level option and via the namedsilenceRules()export.silenceDependencyWarning()works unchanged. Side-effect lines such assilenceDependencyWarning(true)continue to sit above theuseInsider(...)call, now ininsider.lint.*.
First lint after install
The first time you run lint after installing, ensureDependencies may detect
that your project's eslint or globals version is older than the pin and
auto-install them, exiting with code 1. Run lint again and it will complete
normally.
Troubleshooting
Missing dependencies detected:
If you see this error, your project is missing a peer plugin this config needs. The error message lists the install command. To silence the warning permanently:
import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';
silenceDependencyWarning(['@cspell/eslint-plugin', '@stylistic/eslint-plugin']);
export const { useInsiderESLint, useInsiderOXC } = useInsider({ /* ... */ });
Passing true silences every warning; that is not recommended because the
warning is the only signal that your installed plugin versions drifted
from what the config expects.
silenceDependencyWarning(true);
Contributing
See CONTRIBUTING.md at the repo root.
Related Packages
A set of ESLint configurations supporting flat config format with Vue (and with...
Opinionated ESLint config for consistent, clean, auto-sorted code in TRES Vue3 a...
Version History
| Version | Released | License |
|---|---|---|
| 4.1.0 | MIT | |
| 4.0.0 | MIT | |
| 3.0.2 | MIT | |
| 3.0.1 | MIT | |
| 3.0.0 | MIT | |
| 2.0.0 | MIT | |
| 1.15.2 | MIT | |
| 1.15.1 | MIT | |
| 1.15.0 | MIT | |
| 1.14.0 | MIT | |
| 1.13.0 | MIT | |
| 1.12.2 | MIT | |
| 1.12.1 | MIT | |
| 1.12.0 | MIT | |
| 1.11.0 | MIT | |
| 1.10.0 | MIT | |
| 1.9.2 | MIT | |
| 1.9.1 | MIT | |
| 1.9.0 | MIT | |
| 1.8.1 | MIT | |
| 1.8.0 | MIT | |
| 1.7.0 | MIT | |
| 1.6.0 | MIT | |
| 1.5.3 | MIT | |
| 1.5.0 | MIT | |
| 1.4.1 | MIT | |
| 1.4.0 | MIT | |
| 1.3.1 | MIT | |
| 1.3.0 | MIT | |
| 1.2.2 | MIT |
Showing the latest 30 of 39. See every release on npm
Pre-releases (76)
| Version | Released | License |
|---|---|---|
| 4.1.0-beta.2 | MIT | |
| 4.1.0-beta.1 | MIT | |
| 4.0.0-beta.6 | MIT | |
| 4.0.0-beta.5 | MIT | |
| 4.0.0-beta.4 | MIT | |
| 4.0.0-beta.3 | MIT | |
| 4.0.0-beta.2 | MIT | |
| 4.0.0-beta.1 | MIT | |
| 3.0.2-beta.1 | MIT | |
| 3.0.0-beta.1 | MIT | |
| 2.0.0-beta.1 | MIT | |
| 1.15.2-beta.1 | MIT | |
| 1.15.1-beta.2 | MIT | |
| 1.15.1-beta.1 | MIT | |
| 1.14.0-beta.1 | MIT | |
| 1.13.0-beta.8 | MIT | |
| 1.13.0-beta.7 | MIT | |
| 1.13.0-beta.6 | MIT | |
| 1.13.0-beta.5 | MIT | |
| 1.13.0-beta.4 | MIT | |
| 1.13.0-beta.3 | MIT | |
| 1.13.0-beta.2 | MIT | |
| 1.13.0-beta.1 | MIT | |
| 1.12.0-beta.2 | MIT | |
| 1.12.0-beta.1 | MIT | |
| 1.10.0-beta.11 | MIT | |
| 1.10.0-beta.10 | MIT | |
| 1.10.0-beta.9 | MIT | |
| 1.10.0-beta.8 | MIT | |
| 1.10.0-beta.7 | MIT |
Showing the latest 30 of 76. See every release on npm