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

@moso/eslint-config

v3.0.3

Published

@moso's sharable ESLint config

Readme

@moso/eslint-config

npm

Flat ESLint config for JavaScript, TypeScript, Vue, React, and more.

Features

  • ESLint Flat config with reasonable but opinionated defaults
  • Stylistic with single quotes, semi enabled, sorted imports, and dangling commas
  • Aimed to be used without Prettier
  • Designed to work with JSX, TypeScript, Vue, React, Astro, Nuxt, and Next.js out of the box
  • Applies Functional, Perfectionist, and e18e by default
  • Lints for JSDoc, JSON, RegEx, TOML, and YAML
  • Ships its own small @moso-ruleset with security and hygiene rules
  • Every config can be enabled/disabled
  • Respects .gitignore by default
  • Requires Node.js >= v22.23.x/v24.18.x, ESLint v10+

[!NOTE] Note regarding TypeScript v7 See the FAQ

Configs

This section contains a list of the plugins used in the named configs that ships with this shareable config.

- ✅ Enabled by default
- ☑️ Enabled by auto-detect
- 🌟 Enabled unless lessOpinionated: true
- 🟨 Can be enabled manually
- 🎨 Stylistic rules enabled
- 💭 Type-aware rules available by setting the path to your tsconfig.json
- ℹ️ Accessibility rules available with a11y: true

eslint-plugin-astro, astro-eslint-parser, eslint-plugin-jsx-a11y

@e18e/eslint-plugin

eslint-plugin-eslint-comments

eslint-plugin-functional

eslint-plugin-import-lite

@eslint/js, eslint-plugin-de-morgan, eslint-plugin-unused-imports

eslint-plugin-jsdoc

eslint-plugin-jsonc

@stylistic/eslint-plugin, eslint-plugin-jsx-a11y

@next/eslint-plugin-next

eslint-plugin-n

eslint-plugin-perfectionist

eslint-plugin-promise

@eslint-react, @eslint-react/dom, @eslint-react/hooks-extra, @eslint-react/naming-convention, @eslint-react/web-api, eslint-plugin-react-hooks, eslint-plugin-react-you-might-not-need-an-effect, eslint-plugin-jsx-a11y

eslint-plugin-regexp

@stylistic/eslint-plugin

@vitest/eslint-plugin, eslint-plugin-no-only-tests

eslint-plugin-toml

@typescript-eslint, eslint-plugin-erasable-syntax-only

eslint-plugin-unicorn

eslint-plugin-vue, eslint-plugin-vuejs-accessibility

eslint-plugin-yml

Custom rules

This config ships its own small plugin, registered as @moso. Each rule has full documentation next to its implementation.

| Rule | Description | Default | Fixable | | --- | --- | --- | --- | | avoid-barrel-files | Disallow authoring barrel files | ✅ | | | no-bidi | Detect and stop trojan source attacks | ✅ | 🔧 | | no-force-cast-via-top-type | Disallow T as any as Q casts | ✅ | | | no-import-duplicates | Fix duplicate specifiers within an import | ✅ | 🔧 | | no-import-from-dist | Prevent importing from dist folders | ✅ | | | no-import-node-modules-by-path | Prevent importing node_modules by path | ✅ | | | no-invisible-characters | Disallow invisible characters | ✅ | 🔧 | | no-redundant-variable | Disallow variables that are immediately returned | | 🔧 | | no-string-interpolation | Disallow multiline expressions in template literals | | | | no-top-level-await | Prevent top-level await | ✅ | | | no-unneeded-array-flat-map | Disallow flatMap with identity callbacks | | 🔧 | | prefer-early-return | Prefer guard clauses over wrapped function bodies | | 🔧 | | prefer-fetch | Enforce fetch over legacy HTTP clients | | | | prefer-reduce-over-chaining | Prefer one .reduce() pass over .map().filter() chains | ✅ | |

Usage

Install

[!NOTE] I like to use Bun because it's hella fast. Thus all the install instructions are with Bun. If you use something else, check the syntax with your favorite package manager.

bun add -d eslint @moso/eslint-config

Create eslint.config.js in the root of your project:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso();

If you still use some configs from the legacy eslintrc format, you can use the @eslint/eslintrc package to convert them to the flat config.

// eslint.config.js
import moso from '@moso/eslint-config';
import { FlatCompat } from '@eslint/eslintrc';

const compat = new FlatCompat()

export default moso(
    {
        ignores: [],
    },

    // Legacy config
    ...compat.config({
        extends: [
            'eslint:recommended',
            // Other extends...
        ],
    })

    // Other flat configs...
);

[!NOTE] .eslintignore no longer works in Flat config, see customization for more details.

Add script for package.json

For example:

{
    "scripts": {
        "lint": "eslint",
        "lint:fix": "eslint --fix"
    }
}

VS Code support (auto fix)

Install VS Code ESLint extension

Add the following settings to your .vscode/settings.json:

{
  // Disable the default formatter, use eslint instead
  "prettier.enable": false,
  "editor.formatOnSave": false,

  // Auto fix
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "never"
  },

    // You can silent specific rules in you IDE, but still auto fix them
  "eslint.rules.customizations": [
    { "rule": "@stylistic/*", "severity": "off", "fixable": true },
    { "rule": "*-indent", "severity": "off", "fixable": true },
    { "rule": "*-spacing", "severity": "off", "fixable": true },
    { "rule": "*-spaces", "severity": "off", "fixable": true },
    { "rule": "*-order", "severity": "off", "fixable": true },
    { "rule": "*-dangle", "severity": "off", "fixable": true },
    { "rule": "*-newline", "severity": "off", "fixable": true },
    { "rule": "*quotes", "severity": "off", "fixable": true },
    { "rule": "*semi", "severity": "off", "fixable": true }
  ],

  // Enable eslint for all supported languages
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
    "json",
    "jsonc",
    "yaml"
  ]
}

Customization

Since v1.0.0, this config has been migrated to ESLint Flat config. It provides much better organization and composition. And speed!

Normally you only need to import the moso preset:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso();

Configuration Options

Configure integrations by passing options to the main function:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    // Stylistic rules (enabled by default)
    stylistic: false, // disable entirely
    stylistic: {
        indent: 4, // 2, or 'tab'
        jsx: true, // or false to disable JSX support
        quotes: 'single', // or 'double'
        semi: true, // or false
    },

    // Auto-detected, or enable explicitly
    astro: true,
    react: true,
    typescript: true,
    vue: true,

    // JSDoc support
    jsdoc: true, // enable explicitly with reasonable defaults

    // File format support
    jsonc: false,
    yaml: false,

    // Replaces .eslintignore (no longer supported)
    ignores: [
        '**/dist',
        '**/node_modules',
    ],
});

Overriding Rules

Pass additional flat config objects as arguments. Rules are scoped to specific file types:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso(
    {
        // First argument: moso config options
        vue: true,
        typescript: true,
    },
    {
        // Additional arguments: standard ESLint flat configs
        files: ['**/*.vue'],
        rules: {
            'vue/multi-word-component-names': ['error', { ignores: [] }],
        },
    },
    {
        // You can pass multiple config objects
        files: ['**/*.ts'],
        rules: {
            '@typescript-eslint/no-explicit-any': 'warn',
        },
    },
);

Advanced Composition

The config returns a FlatConfigComposer object from eslint-flat-config-utils, enabling chainable methods:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso()
    .prepend({
        // Configs before the main config
    })
    .override('moso/javascript/rules', {
        rules: {
            'no-var': 'off',
        },
    })
    .removeRules(
        'no-console',
        'no-debugger',
    );

You can import and compose individual configs directly. Only use this if you need granular control:

import {
    combine,
    comments,
    ignores,
    imports,
    javascript,
    jsdoc,
    jsonc,
    node,
    sortPackageJson,
    sortTsconfig,
    stylistic,
    typescript,
    unicorn,
    vue,
    yaml,
} from '@moso/eslint-config';

export default combine(
    comments(),
    ignores(),
    imports(),
    javascript(/* options */),
    jsdoc(),
    jsonc(),
    node(),
    stylistic(/* options */),
    typescript(/* options */),
    unicorn(),
    vue(/* options */),
    yaml(),
);

Optional configs

JSDoc

Disabled by default, will have to be enabled manually.

There are many ways JSDoc support can be configured. However, the most simple way is just enabling it which will use very reasonable defaults:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    jsdoc: true,
});

This will enable the preset recommended, which is recommended starting rules for enforcing proper tag values, common tags exists, and tags are formatted and styled consistently.

Individual rules can be tweaked with the overrides property:

{
    jsdoc: {
        overrides: {
            'jsdoc/require-description': 'off',
        },
    },
}

Frameworks

Framework support is auto-detected based on installed packages, but can be enabled explicitly. Dependencies are not bundled and will be prompted for installation.

[!NOTE] Note: Framework support is just as opinionated as the rest of the config. If your favorite framework is missing, then you can always extend the config yourself.

Astro

Auto-detected if you have Astro installed. Enable explicitly:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    astro: true,
});

Install dependencies when prompted, or manually:

bun add --dev eslint-plugin-astro astro-eslint-parser

[!NOTE] Since Astro can be plugged with other frameworks as well, you'll be prompted to install the required packages for linting those as well. Example: If you have installed @astrojs/react, you'll be prompted to install the required packages to lint React.

React

Auto-detected if you have React, Next.js, Nextra, Remix, Gatsby, or the @astrojs/react Astro framework integration installed. Enable explicitly:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    react: true,
});

Install dependencies when prompted, or manually:

bun add --dev @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh eslint-plugin-react-you-might-not-need-an-effect

Vue

Auto-detected if you have Vue, Nuxt, VitePress, or the @astrojs/vue Astro framework intefration installed. Enable explicitly:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    vue: true,
});

Install dependencies when prompted, or manually (the Vue parser and SFC processors are already bundled as dependencies):

bun add --dev eslint-plugin-vue

[!CAUTION] Since Vue 2 has reached EOL, this config does not support Vue 2. If you need to support for Vue 2, you'll need to disable the imported configs from Vue 3, and replace them with the Vue 2 ones. You can see an inspiring example on eslint-plugin-vue. I recommend upgrading to Vue 3 if possible.

Typed Linting

You can optionally enable typed linting. These are also known as "rules that require types", or simply "type-aware rules". This enables for much deeper insight into your code.

You enable them by passing the path of your tsconfig.json to the projectRoot option.

[!WARNING] Enabling these rules will come with a slight performance cost, explained here.

To make things even more complicated, if you have enabled typed linting but disabled @stylistic, it will also disable the type-aware rules considered stylistic.

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso(
    {
        projectRoot: import.meta.dirname,

        // If you wish to override any of these rules, use `overridesTypeAware`:
        typescript: {
            overridesTypeAware: {
                '@typescript-eslint/no-deprecated': 'off',
            },
        },
    },
);

[!NOTE] TypeScript will still be auto-detected if you have the typescript-package installed, and any non-type-aware @typescript-eslint-rules will still be applied. Typed Linting is considered as an extra option.

Disable type-aware

You can disable the type-aware layer entirely, or exclude certain globs or specific files from it:

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    projectRoot: import.meta.dirname,

    typescript: {
        // Kill switch: keep regular TypeScript rules, drop the type-aware layer
        disableTypeAwareRules: true,

        // ...or keep it, but exclude globs or specific files from it
        ignoresTypeAware: [
            '**/*.{js,jsx}',
            '**/components/User.tsx',
        ],
    },
});

Editor specific non-fixes

Some rules are deemed as 'non-fixable' when inside your editor with ESLint integration:

Before v1.0.0, they used to be hard disabled. But with a helper, they are now just marked as 'non-fixable'. They are re-applied when you're linting through a terminal, or by using Lint Staged.

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    isInEditor: false,
});

Lint Staged

Linting and auto-fixing before every commit is easy, you just add the following to your package.json:

{
    "simple-git-hooks": {
        "pre-commit": "bunx lint-staged"
    },
    "nano-staged": {
        "*": "eslint --fix"
    }
}

and then

bun add --dev nano-staged simple-git-hooks

# to activate the hooks
bunx simple-git-hooks

FAQ

ESLint v9, older Node.js versions?

ESLint v9 will reach EOL on August 6th, 2026. The world of development moves fast, and as with Vue v2.x, I don't intend to support versions that has reached EOL unless absolutely necessary. The same goes with older Node.js versions.

Even though ESLint v10 supports Node.js v20.19.0 (as of current status), this version has also reached EOL. The rest of their requirements mention v22.13.0 and >= 24. However, the minimum requirements of some of the configs that this sharable config apply rules from have a minimum requirement of v22.22.3, this project will stay within this range.

TypeScript 7?

TypeScript 7.0 does not ship a compiler API (a new one supposedly arrives in 7.1), so typescript-eslint - and therefore all TypeScript linting in this config - cannot run against it. TypeScript's supported setup is running TypeScript 6 side-by-side through the @typescript/typescript6 compatibility package, aliased so tooling that imports typescript keeps working:

bun add --dev typescript@npm:@typescript/typescript6

Optionally keep TypeScript 7's own tsc around as well:

{
    "devDependencies": {
        "@typescript/native": "npm:typescript@^7.0.2",
        "typescript": "npm:@typescript/typescript6@^6.0.2"
    }
}

With the alias in place, this config works unchanged. Without it, the config detects TypeScript >7 and fails fast: an explicit typescript: true throws with these instructions, while auto-detection prints a warning and degrades to JavaScript-only linting instead of crashing inside the parser.

Keep in mind, this is temporary until an API is provided by TypeScript for v7.

I want it less opinionated

No problem. I've extracted the things that I've deemed very opinionated in each integration, and made a setting that helps you disable all of it in one go.

// eslint.config.js
import moso from '@moso/eslint-config';

export default moso({
    lessOpinionated: true,
});

[!NOTE] The above will also disable functional and perfectionist completely. If you want to keep these enabled, you'll have to re-enable them explicitly, as demonstrated below

{
    // Disable opinionated rules
    lessOpinionated: true,

    // Re-enable functional - a string sets the enforcement level directly
    functional: 'lite', // 'lite' | 'recommended' | 'strict'

    // ...or as an object:
    functional: {
        functionalEnforcement: 'recommended',
    },

    // Re-enable `perfectionist`
    perfectionist: true,
}

Prettier? dprint?

You can still use these to format files that aren't linted with this config, however, I strongly recommend you only format your code with ESLint, as Prettier and other AST-reading-then-reprint projects tend to ignore stuff like the original line breaks and might also cause inconsistent diffs when committing code.

How to format CSS?

You will need to install and configure stylelint yourself, unfortunately.

I am actively considering adding linting support for TailwindCSS, however.

I prefer this or that rule

No worries, you can always override the rules locally in your project to fit your needs. If that doesn't cut it, you're welcome to fork this project and maintain your own config.

Inspiration

This ESLint config is heavily inspired by (and uses some of the same logic of):

- @antfu/eslint-config - @rebeccastevens/eslint-config - @eslint-sukka/eslint-config

Anthony Fu's config inspired me to take the journey, but this project has since evolved into a personal project. Especially Rebecca's config and strict(er) approach to coding has pushed me towards a stricter coding environment, with Functional and Azat's Perfectionist and De Morgan enabled.

A personal thank-you to Anthony, Rebecca, Sukka, and Azat, and everyone else who contributed to their projects.

Some rules are the same, however, there are some differences:

  • My own opinionated rules added
  • Stylistic, Perfectionist, and Functional enabled per default
  • More freedom to disable every individual config, or only disable the opinionated parts
  • Deprecated Vue 2 support
  • Simplification in some areas
  • Better type-aware checks
  • Plugin memoization
  • Optional a11y support for JSX, React and Vue
  • No dangerous plugin renaming (except for node)

License

BSD 3-Clause License