npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@useinsider/eslint-config

v1.14.0

Published

A collection of ESLint configurations provided by @useinsider, offering support for various environments including Vanilla JS, TypeScript, and framework-specific setups such as Vue 3.

Readme

@useinsider/eslint-config

Shared ESLint 9 flat-config for Insider projects. useInsider() takes a single scope-keyed config object and returns a defineConfig([...]) array — one entry per file scope you describe. There is no opaque preset list, no freeform config escape hatch, and no expectation that you spread the result into your own array.

The default export is the resolved Promise returned by useInsider(...); ESLint 9 awaits it automatically. Do not await it. Do not spread it. Do not wrap it 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

Prerequisites

  • Node.js 20+
  • ESLint 9+
  • 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 ESLint plugin pinned to the versions this package was built against, and writes a starter eslint.config.mjs at the project root.

To migrate an existing v1 eslint.config.{js,mjs,cjs} or a legacy .eslintrc.{js,cjs}:

npx @useinsider/eslint-config migrate [path]

The migrate flow auto-detects the source format. For flat configs it parses the v1 file, prints a summary plus any dropped custom rules, asks for confirmation, then rewrites the file in place. For legacy .eslintrc files it writes a fresh eslint.config.mjs next to the original (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.

Example output for a Node + TypeScript project:

import { useInsider } from '@useinsider/eslint-config';

export default useInsider({
    typescriptNode: [
        {
            files: ['**/*.{ts,tsx}'],
            tsconfigPaths: ['tsconfig.json'],
        },
    ],
});

What the CLI installs for that answer set (versions reflect the publish-time pins):

  • @useinsider/eslint-config
  • eslint
  • globals
  • typescript
  • typescript-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

Then create eslint.config.mjs and call useInsider(...) with the scopes your project needs. Each scope holds an array of { files, ... } entries; each entry becomes its own flat-config block.

Node + TypeScript

import { useInsider } from '@useinsider/eslint-config';

export default 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 default 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 default 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 default useInsider({
    typescriptNode: [
        {
            files: ['**/*.{ts,tsx}'],
            tsconfigPaths: ['tsconfig.json'],
        },
    ],
    strict: ['src/**/*.ts'],
    spellChecker: {
        ignoredPaths: ['vendor/**', 'dist/**'],
        customWordListFile: './cspell.custom.json',
    },
    silenceRules: ['no-console'],
});
  • strict: true applies strict rules to every file.
  • strict: string[] applies strict rules only to the matching paths.
  • spellChecker.customWordListFile re-points @cspell/spellchecker to a project-local word list.
  • spellChecker.ignoredPaths turns the spell-checker off for the matching globs (the off block is emitted after the custom-list block, so ignore wins on overlap).
  • silenceRules rewrites 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's ignores key, 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's Linter.Globals. Use this to declare project-specific globals like APP_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 default useInsider({
    ignores: ['dist', 'coverage'],
    javascriptDom: [
        {
            files: ['src/**/*.js'],
            ignores: ['src/vendor/**'],
            globals: {
                APP_URL: 'readonly',
                axios: 'readonly',
                atatus: 'readonly',
            },
        },
    ],
});

Block-emission order — scope blocks first (in the order their keys were inserted into the config object), then jquery blocks, then any strict blocks, then spellChecker blocks. Later blocks win on overlap, matching ESLint's flat-config precedence model.

useInsider returns Promise<Linter.Config[]>. Export it directly — ESLint 9 resolves the Promise at load time. useInsider's return type carries through, so no JSDoc annotation is needed; the UseInsiderResult type alias is exported for TypeScript callers that wrap useInsider in a helper of their own.

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 default useInsider({ /* ... */ });

Migrating from v1.x

v2 is a hard break. The summary:

  • preset: string[] → top-level scope properties. The order in the v1 preset array 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'] + configvue3Typescript entry with a dedicated globalComponents field. No more hand-rolled vue/no-undef-components overrides.

    - 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-level strict: 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 strict preset was always global when listed. In v2, strict: true keeps that global behavior; strict: string[] scopes the strict rule set to the matching globs only. If you have paths-targeted strict checks, set strict: [...] instead of strict: true.

  • Shape change: useInsider(...) is now exported directly as the default export, without await, without spread, and without an array wrap. ESLint 9 resolves the Promise on load. The old pattern export default [...await useInsider({...})] still parses, but the preferred shape is export default useInsider({...}). There is no supported override path — describe every glob you want linted inside the call, including jQuery files via the dedicated jquery scope.

  • Removed: the preset array, the config: Linter.Config escape hatch, and the config preset itself. Configuration-file linting is now an ordinary typescriptNode (or javascriptNode) entry pointed at the right tsconfig.

  • Kept: silenceRules accepts the same string[] shape both as a top-level option and via the named silenceRules() export. silenceDependencyWarning() works unchanged. Side-effect lines such as silenceDependencyWarning(true) continue to sit above the export default useInsider({...}) call.

First lint after install

The first time you run lint after installing v2, 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 default 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.