npm.io
0.14.1 • Published 1 week agoCLI

neostandard

Licence
MIT
Version
0.14.1
Deps
8
Size
120 kB
Vulns
0
Weekly
0
neostandard

npm version npm downloads neostandard javascript style

A spiritual successor to the standard javascript style guide

Initial development sponsored by:

platformatic

ESLint 10 + JSX: This release supports ESLint 10 (while keeping ESLint 9 support). JSX/TSX files are still parsed and the JSX style rules still apply, but the React-specific logic rules (from eslint-plugin-react) are temporarily removed because eslint-plugin-react is not yet compatible with ESLint 10 (jsx-eslint/eslint-plugin-react#3977). They return once a v10-compatible React plugin is adopted. Tracking: #350.

ESM, Node.js and TypeScript versions: neostandard is now an ESM package requiring Node.js ^22.13.0 || >=24. CommonJS configs keep working unchanged — require('neostandard') returns the neostandard function with plugins and resolveIgnoresFromGitignore attached, same as before (they are also proper ESM named exports, the preferred form). For the ts option, the supported TypeScript range for the installed typescript package tops out at 6.0 (typescript-eslint's peer is <6.1.0, and TypeScript 7 ships no compiler API for tooling until 7.1) — TypeScript 7 users should install typescript@npm:@typescript/typescript6 for linting and alias TS7 separately for tsc, per Microsoft's side-by-side guidance.

Table of Contents

Quick Start

Migrate from standard
  1. npm install -D neostandard eslint
  2. (Validate that it runs cleanly by running npx neostandard --help, see #267)
  3. npx neostandard --migrate > eslint.config.js (uses our config helper)
  4. Replace standard with eslint in all places where you run standard, eg. "scripts" and .github/workflows/ (neostandard CLI tracked in #2)
  5. (Add ESLint editor integration, eg. VS Code ESLint extension)
  6. Cleanup:
    • npm uninstall standard
    • Remove unused "standard" top level key from your package.json
    • Deactivate standard specific integrations if you no longer use them (eg. vscode-standard))
Add to new project
  1. npm install -D neostandard eslint

  2. Add an eslint.config.js:

    Using config helper:

    npx neostandard --esm > eslint.config.js

    Or to get CommonJS:

    npx neostandard > eslint.config.js

    Or manually create the file as ESM:

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      // options
    })

    Or as CommonJS:

    module.exports = require('neostandard')({
      // options
    })
  3. Run neostandard by running ESLint, eg. using npx eslint, npx eslint --fix or similar

Configuration options

All examples below use ESM (ECMAScript Modules) syntax. If you're using CommonJS (CJS), replace the import/export statements with the following:

// Replace
import { neostandard } from 'neostandard'
export default neostandard({ /* options */ })

// With
const neostandard = require('neostandard')
module.exports = neostandard({ /* options */ })

Here's a basic example of how to configure neostandard:

import { neostandard } from 'neostandard'

export default neostandard({
  ts: true,  // an option
  // Add other options here
})

The options below allow you to customize neostandard for your project. Use them to add global variables, ignore files, enable TypeScript support, and more.

  • env - string[] - adds additional globals by importing them from the globals npm module

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      env: ['browser', 'mocha'],  // Add browser and mocha global variables
    })
  • files - string[] - additional file patterns to match. Uses the same shape as ESLint files

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      files: ['src/**/*.js', 'tests/**/*.js'],  // Also lint these patterns (neostandard's built-in JS/TS patterns still apply)
    })
  • filesTs - string[] - additional file patterns for the TypeScript configs to match. Uses the same shape as ESLint files

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      ts: true,   // Enable TypeScript support
      filesTs: ['src/**/*.ts', 'tests/**/*.ts'],  // Also lint these as TypeScript (neostandard's built-in TS patterns still apply)
    })
  • globals - string[] | object - an array of names of globals or an object of the same shape as ESLint languageOptions.globals

    Using an array:

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      globals: ['
    

    Using an object:

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      globals: {
        $: 'readonly',  // $ is a read-only global
        jQuery: 'writable',  // jQuery can be modified
        localStorage: 'off',  // Disable the localStorage global
      },
    })
  • ignores - string[] - glob patterns that prevent neostandard's own config layers from applying to matching files. These are not global ignores: other configs in your flat config can still lint those files. This is a change from v0.13.0; see ESLint's ignore documentation.

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      ignores: ['dist/**/*', 'tests/**'],  // Skip these files in neostandard's config layers
    })

    To ignore files globally across all configs (the v0.13.0 behavior), use ESLint's globalIgnores() instead:

    import { globalIgnores } from 'eslint/config'
    import { neostandard, resolveIgnoresFromGitignore } from 'neostandard'
    
    export default [
      globalIgnores(resolveIgnoresFromGitignore()),
      ...neostandard(),
    ]
  • noJsx - boolean - if set, skips JSX parsing and the JSX style rules. (Note: the React-specific logic rules are currently not included regardless — pending ESLint 10 support; see the note at the top and #350)

  • noStyle - boolean - if set, no style rules will be added. Especially useful when combined with Prettier, dprint or similar

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      noStyle: true,  // Disable style-related rules (useful with Prettier or dprint)
    })
  • semi - boolean - if set, enforce rather than forbid semicolons (same as semistandard did)

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      semi: true,  // Enforce semicolons (like semistandard)
    })
  • ts - boolean - if set, TypeScript syntax will be supported and *.ts (including *.d.ts) will be checked. To add additional file patterns to the TypeScript checks, use filesTs

    import { neostandard } from 'neostandard'
    
    export default neostandard({
      ts: true,  // Enable TypeScript support and lint .ts files
    })

Extending

The neostandard() function returns an ESLint config array which is intended to be exported directly or, if you want to modify or extend the config, can be combined with other configs using ESLint's defineConfig helper (from eslint/config):

import { defineConfig } from 'eslint/config'
import { neostandard } from 'neostandard'
import jsdoc from 'eslint-plugin-jsdoc'

export default defineConfig([
  ...neostandard(),
  jsdoc.configs['flat/recommended-typescript-flavor'],
])

Do note that neostandard() is intended to be a complete linting config in itself, only extend it if you have needs that goes beyond what neostandard provides, and open an issue if you believe neostandard itself should be extended or changed in that direction.

It's recommended to stay compatible with the plain config when extending and only make your config stricter, not relax any of the rules, as your project would then still pass when using just the plain neostandard-config, which helps people know what baseline to expect from your project.

Scope your rule-tweak layers. neostandard defines its plugins only for the JS/TS files it owns, so a config object that adjusts eg. @stylistic/* rules without a files scope applies to every lintable file — and as soon as any other language is linted (package.json via eslint-plugin-package-json, @eslint/json, @eslint/markdown, …) that file sees a rule for a plugin that isn't defined there, which is a fatal could not find plugin config error. Scope such layers with the exported globs:

import { defineConfig } from 'eslint/config'
import { globs, neostandard } from 'neostandard'

export default defineConfig([
  ...neostandard(),
  {
    files: [...globs.all], // the same files neostandard itself touches
    rules: {
      '@stylistic/comma-dangle': ['warn', { arrays: 'always-multiline' }],
    },
  },
])
Linting other languages (Markdown, JSON, …)

neostandard scopes all of its rules to the JavaScript and TypeScript files it owns (.js, .cjs, .mjs, .jsx, .ts, .tsx, plus whatever you add through the files / filesTs options). It will not apply its rules to other languages, so it composes directly with the official ESLint language plugins:

import { defineConfig } from 'eslint/config'
import { neostandard } from 'neostandard'
import markdown from '@eslint/markdown'
import json from '@eslint/json'

export default defineConfig([
  ...neostandard(),
  ...markdown.configs.recommended,
  { ...json.configs.recommended, files: ['**/*.json'], language: 'json/json' },
])

Fenced code blocks inside Markdown (the virtual *.md/*.js files produced by @eslint/markdown's processor) are intentionally not linted by neostandard for now — see #296. To lint them yourself, add your own config block scoped to **/*.md/**.

If you need to apply neostandard to a non-default set of files (or scope it to a subdirectory), wrap it with ESLint's defineConfig and its extends — the parent files/ignores propagate into every layer:

import { defineConfig } from 'eslint/config'
import { neostandard } from 'neostandard'

export default defineConfig([
  { files: ['packages/app/**/*.js'], extends: [neostandard()] },
])
Adding back import checking

As of neostandard v0.13.0, eslint-plugin-import-x has been removed to reduce dependency weight and installation complexity. For most projects, TypeScript's compiler (tsc) provides superior import/export checking with full project context.

If you still need ESLint-based import checking, you can add it back manually:

import { defineConfig } from 'eslint/config'
import { neostandard } from 'neostandard'
import importX from 'eslint-plugin-import-x'

export default defineConfig([
  ...neostandard(),
  {
    plugins: {
      'import-x': importX
    },
    rules: {
      'import-x/export': 'error',
      'import-x/first': 'error',
      'import-x/no-absolute-path': ['error', { esmodule: true, commonjs: true, amd: false }],
      'import-x/no-duplicates': 'error',
      'import-x/no-named-default': 'error',
      'import-x/no-webpack-loader-syntax': 'error',
    }
  }
])

For TypeScript projects, you may also want to add the TypeScript resolver:

import { defineConfig } from 'eslint/config'
import { neostandard } from 'neostandard'
import importX from 'eslint-plugin-import-x'
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'

export default defineConfig([
  ...neostandard(),
  {
    plugins: {
      'import-x': importX
    },
    settings: {
      'import-x/resolver-next': [
        createTypeScriptImportResolver({
          project: './tsconfig.json'
        })
      ]
    },
    rules: {
      'import-x/export': 'error',
      'import-x/first': 'error',
      'import-x/no-absolute-path': ['error', { esmodule: true, commonjs: true, amd: false }],
      'import-x/no-duplicates': 'error',
      'import-x/no-named-default': 'error',
      'import-x/no-webpack-loader-syntax': 'error',
    }
  }
])

Recommended alternative: Use TypeScript's compiler for import checking instead:

tsc --noEmit

This provides more comprehensive checking including type imports, module resolution, and cross-file validation.

Additional exports

resolveIgnoresFromGitignore()

Finds a .gitignore file that resides in the same directory as the ESLint config file and returns matching glob patterns. Passing them to neostandard({ ignores }) skips those files in neostandard's layers only. To skip them for every config, pass the patterns to ESLint's globalIgnores() (the v0.13.0 behavior).

Global ESM example:

import { globalIgnores } from 'eslint/config'
import { neostandard, resolveIgnoresFromGitignore } from 'neostandard'

export default [
  globalIgnores(resolveIgnoresFromGitignore()),
  ...neostandard(),
]

To scope the ignores to neostandard only, pass them as an option instead:

import { neostandard, resolveIgnoresFromGitignore } from 'neostandard'

export default neostandard({
  ignores: resolveIgnoresFromGitignore(),
})

CommonJS (global ignores):

const { globalIgnores } = require('eslint/config')
const neostandard = require('neostandard')

module.exports = [
  globalIgnores(neostandard.resolveIgnoresFromGitignore()),
  ...neostandard(),
]
Exported plugins

neostandard exports all the ESLint plugins that it uses. This to ensure that users who need to reference the plugin themselves will use the exact same instance of the plugin, which is a necessity when a plugin prefix is defined in multiple places.

List of exported plugins

(The react plugin export is temporarily removed along with the React logic rules — see the note at the top.)

Usage of exported plugin

If one eg. wants to add the eslint-plugin-n recommended config, then one can do:

import { defineConfig } from 'eslint/config'
import { neostandard, plugins } from 'neostandard'

export default defineConfig([
  ...neostandard(),
  plugins.n.configs['flat/recommended'],
])

Missing for 1.0.0 release

  • Investigate a dedicated neostandard runner: #33 / #2

Full list in 1.0.0 milestone

Differences to standard / eslint-config-standard 17.x

Relaxed rules

Config helper

You can use the provided CLI tool to generate a config for you:

neostandard --semi --ts > eslint.config.js

To see all available flags, run:

neostandard --help
Config migration

The CLI tool can also migrate an existing "standard" configuration from package.json:

neostandard --migrate > eslint.config.js

Migrations can also be extended, so to eg. migrate a semistandard setup, do:

neostandard --semi --migrate > eslint.config.js

Readme badges

Yes! If you use neostandard in your project, you can include one of these badges in your readme to let people know that your code is using the neostandard style.

neostandard javascript style

[![neostandard javascript style](https://img.shields.io/badge/neo-standard-7fffff?style=flat&labelColor=ff80ff)](https://github.com/neostandard/neostandard)

neostandard javascript style

[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-7fffff?style=flat&labelColor=ff80ff)](https://github.com/neostandard/neostandard)

neostandard javascript style

[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)

Mission statement

Prior to the 1.0.0 release we are still rapidly evolving with fixes and improvements to reach rule parity with standard, hence more breaking changes will be experienced until then, as well as evolution of this statement

neostandard intends to set an expectable baseline for project linting that's descriptive of best practices rather than prescriptive of any opinionated approach.

Rule guidelines
  1. neostandard rules describes current best practices in the community and help align developers, contributors and maintainers along those
  2. neostandard rules are not a tool to promote changed practices within the community by prescribing new such practices
  3. neostandard rule changes and additions should be aligned with projects prior to being released, by eg. sending PR:s to them to align them ahead of time. When new best practices are incompatible with current best practices, rules should first be relaxed to allow for both approaches, then be made stricter when the community has moved to the new approach
  4. neostandard rule changes and additions should improve the description of project best practices, not prescribe new practices
  5. neostandard should, when faced with no clear best practice, avoid adding such a rule as it risks becoming prescriptive rather than descriptive. If leaving out such a rule would make neostandard an incomplete baseline config, and the community is split between a few clear alternatives (such as semi), then making it configurable can enable it to still be added, but that should only be done in exceptional cases

Governance

neostandard is a community project with open governance.

See GOVERNANCE.md for specifics.

Used by

A subset of some of the projects that rely on neostandard:

, 'jQuery'], // Treat $ and jQuery as global variables })

Using an object:

__CODE_BLOCK_10__
  • __INLINE_CODE_48__ - __INLINE_CODE_49__ - glob patterns that prevent neostandard's own config layers from applying to matching files. These are not global ignores: other configs in your flat config can still lint those files. This is a change from v0.13.0; see ESLint's ignore documentation.

    __CODE_BLOCK_11__

    To ignore files globally across all configs (the v0.13.0 behavior), use ESLint's __INLINE_CODE_50__ instead:

    __CODE_BLOCK_12__
  • __INLINE_CODE_51__ - __INLINE_CODE_52__ - if set, skips JSX parsing and the JSX style rules. (Note: the React-specific logic rules are currently not included regardless — pending ESLint 10 support; see the note at the top and #350)

  • __INLINE_CODE_53__ - __INLINE_CODE_54__ - if set, no style rules will be added. Especially useful when combined with Prettier, dprint or similar

    __CODE_BLOCK_13__
  • __INLINE_CODE_55__ - __INLINE_CODE_56__ - if set, enforce rather than forbid semicolons (same as __INLINE_CODE_57__ did)

    __CODE_BLOCK_14__
  • __INLINE_CODE_58__ - __INLINE_CODE_59__ - if set, TypeScript syntax will be supported and __INLINE_CODE_60__ (including __INLINE_CODE_61__) will be checked. To add additional file patterns to the TypeScript checks, use __INLINE_CODE_62__

    __CODE_BLOCK_15__
  • Extending

    The __INLINE_CODE_63__ function returns an ESLint config array which is intended to be exported directly or, if you want to modify or extend the config, can be combined with other configs using ESLint's __INLINE_CODE_64__ helper (from __INLINE_CODE_65__):

    __CODE_BLOCK_16__

    Do note that __INLINE_CODE_66__ is intended to be a complete linting config in itself, only extend it if you have needs that goes beyond what __INLINE_CODE_67__ provides, and open an issue if you believe __INLINE_CODE_68__ itself should be extended or changed in that direction.

    It's recommended to stay compatible with the plain config when extending and only make your config stricter, not relax any of the rules, as your project would then still pass when using just the plain __INLINE_CODE_69__-config, which helps people know what baseline to expect from your project.

    Scope your rule-tweak layers. neostandard defines its plugins only for the JS/TS files it owns, so a config object that adjusts eg. __INLINE_CODE_70__ rules without a __INLINE_CODE_71__ scope applies to every lintable file — and as soon as any other language is linted (__INLINE_CODE_72__ via __INLINE_CODE_73__, __INLINE_CODE_74__, __INLINE_CODE_75__, …) that file sees a rule for a plugin that isn't defined there, which is a fatal __INLINE_CODE_76__ config error. Scope such layers with the exported __INLINE_CODE_77__:

    __CODE_BLOCK_17__
    Linting other languages (Markdown, JSON, …)

    __INLINE_CODE_78__ scopes all of its rules to the JavaScript and TypeScript files it owns (__INLINE_CODE_79__, __INLINE_CODE_80__, __INLINE_CODE_81__, __INLINE_CODE_82__, __INLINE_CODE_83__, __INLINE_CODE_84__, plus whatever you add through the __INLINE_CODE_85__ / __INLINE_CODE_86__ options). It will not apply its rules to other languages, so it composes directly with the official ESLint language plugins:

    __CODE_BLOCK_18__

    Fenced code blocks inside Markdown (the virtual __INLINE_CODE_87__ files produced by __INLINE_CODE_88__'s processor) are intentionally not linted by __INLINE_CODE_89__ for now — see #296. To lint them yourself, add your own config block scoped to __INLINE_CODE_90__.

    If you need to apply __INLINE_CODE_91__ to a non-default set of files (or scope it to a subdirectory), wrap it with ESLint's __INLINE_CODE_92__ and its __INLINE_CODE_93__ — the parent __INLINE_CODE_94__/__INLINE_CODE_95__ propagate into every layer:

    __CODE_BLOCK_19__
    Adding back import checking

    As of neostandard v0.13.0, __INLINE_CODE_96__ has been removed to reduce dependency weight and installation complexity. For most projects, TypeScript's compiler (__INLINE_CODE_97__) provides superior import/export checking with full project context.

    If you still need ESLint-based import checking, you can add it back manually:

    __CODE_BLOCK_20__

    For TypeScript projects, you may also want to add the TypeScript resolver:

    __CODE_BLOCK_21__

    Recommended alternative: Use TypeScript's compiler for import checking instead:

    __CODE_BLOCK_22__

    This provides more comprehensive checking including type imports, module resolution, and cross-file validation.

    Additional exports

    resolveIgnoresFromGitignore()

    Finds a __INLINE_CODE_98__ file that resides in the same directory as the ESLint config file and returns matching glob patterns. Passing them to __INLINE_CODE_99__ skips those files in neostandard's layers only. To skip them for every config, pass the patterns to ESLint's __INLINE_CODE_100__ (the v0.13.0 behavior).

    Global ESM example:

    __CODE_BLOCK_23__

    To scope the ignores to neostandard only, pass them as an option instead:

    __CODE_BLOCK_24__

    CommonJS (global ignores):

    __CODE_BLOCK_25__
    Exported plugins

    __INLINE_CODE_101__ exports all the ESLint plugins that it uses. This to ensure that users who need to reference the plugin themselves will use the exact same instance of the plugin, which is a necessity when a plugin prefix is defined in multiple places.

    List of exported plugins

    (The __INLINE_CODE_110__ plugin export is temporarily removed along with the React logic rules — see the note at the top.)

    Usage of exported plugin

    If one eg. wants to add the __INLINE_CODE_111__ recommended config, then one can do:

    __CODE_BLOCK_26__

    Missing for 1.0.0 release

    • Investigate a dedicated __INLINE_CODE_112__ runner: #33 / #2

    Full list in 1.0.0 milestone

    Differences to standard / eslint-config-standard 17.x

    Relaxed rules
    • __INLINE_CODE_125__ – changed – set to ignore dangling commas in arrays, objects, imports, exports and is it set to __INLINE_CODE_126__ rather than __INLINE_CODE_127__
    • __INLINE_CODE_128__ – changed – sets __INLINE_CODE_129__ to __INLINE_CODE_130__, useful for aligning comments across multiple line
    • __INLINE_CODE_131__ – deactivated – clashes with the __INLINE_CODE_132__ check in TypeScript
    • __INLINE_CODE_133__ – changed – changed to __INLINE_CODE_134__ instead of __INLINE_CODE_135__ as they are not urgent to fix

    Config helper

    You can use the provided CLI tool to generate a config for you:

    __CODE_BLOCK_27__

    To see all available flags, run:

    __CODE_BLOCK_28__
    Config migration

    The CLI tool can also migrate an existing __INLINE_CODE_136__ configuration from __INLINE_CODE_137__:

    __CODE_BLOCK_29__

    Migrations can also be extended, so to eg. migrate a __INLINE_CODE_138__ setup, do:

    __CODE_BLOCK_30__

    Readme badges

    Yes! If you use __INLINE_CODE_139__ in your project, you can include one of these badges in your readme to let people know that your code is using the neostandard style.

    neostandard javascript style

    __CODE_BLOCK_31__

    neostandard javascript style

    __CODE_BLOCK_32__

    neostandard javascript style

    __CODE_BLOCK_33__

    Mission statement

    Prior to the __INLINE_CODE_140__ release we are still rapidly evolving with fixes and improvements to reach rule parity with __INLINE_CODE_141__, hence more breaking changes will be experienced until then, as well as evolution of this statement

    __INLINE_CODE_142__ intends to set an expectable baseline for project linting that's descriptive of best practices rather than prescriptive of any opinionated approach.

    Rule guidelines
    1. __INLINE_CODE_143__ rules describes current best practices in the community and help align developers, contributors and maintainers along those
    2. __INLINE_CODE_144__ rules are not a tool to promote changed practices within the community by prescribing new such practices
    3. __INLINE_CODE_145__ rule changes and additions should be aligned with projects prior to being released, by eg. sending PR:s to them to align them ahead of time. When new best practices are incompatible with current best practices, rules should first be relaxed to allow for both approaches, then be made stricter when the community has moved to the new approach
    4. __INLINE_CODE_146__ rule changes and additions should improve the description of project best practices, not prescribe new practices
    5. __INLINE_CODE_147__ should, when faced with no clear best practice, avoid adding such a rule as it risks becoming prescriptive rather than descriptive. If leaving out such a rule would make __INLINE_CODE_148__ an incomplete baseline config, and the community is split between a few clear alternatives (such as __INLINE_CODE_149__), then making it configurable can enable it to still be added, but that should only be done in exceptional cases

    Governance

    __INLINE_CODE_150__ is a community project with open governance.

    See GOVERNANCE.md for specifics.

    Used by

    A subset of some of the projects that rely on __INLINE_CODE_151__: