@bonniernews/eslint-config

中文文档
3.1.0发布于 2026/09/22MITES Module构建来源已验证近 30 天下载 2.1万

ESLint config

原始使用文档
文档目录27 节
  1. Table of contents
  2. Usage
  3. Configuring all rules
  4. JavaScript configuration
  5. TypeScript configuration
  6. React configuration
  7. Test configuration
  8. Typed react configuration
  9. Global ignores
  10. Globals
  11. Safety rules
  12. What each rule flags
  13. Where the rules apply
  14. What the rules need from your project
  15. What these rules cannot see
  16. If a rule reports something wrong
  17. Migrating from 2.X to 3.X
  18. For ESM projects using import
  19. For CommonJS projects using require
  20. Migrating from 1.X to 2.X
  21. Running eslint
  22. Usage in an existing project
  23. Usage with Prettier
  24. Enable format on save
  25. Changelog
  26. Publishing a new version
  27. License

@bonniernews/eslint-config

Test application

Basic ESLint rules used by Bonnier News. The configuration works both with CommonJS and ES6 modules, and the appropriate setup will be used by looking at the projects package.json type property.

For Node versions that support it (version 16 and above), the es2022 environment will also be activated. Otherwise es2021 will be used.

Note: As of version 3.X, this package is published as an ES Module. See the usage examples for how to use it in both ESM and CommonJS projects.

Table of contents

Usage

Install eslint and @bonniernews/eslint-config:

npm install --save-dev eslint @bonniernews/eslint-config

Configuring all rules

Configures all rules, js, ts, tsx, jsx and test rules.

ESM - for projects with "type": "module" in package.json:

// eslint.config.js
import config from "@bonniernews/eslint-config";

export default config;

CommonJS - for projects without "type": "module":

// eslint.config.js
const { default: config } = await import("@bonniernews/eslint-config");

module.exports = config;

JavaScript configuration

// eslint.config.js
import config from "@bonniernews/eslint-config/js";

export default [ config ];

TypeScript configuration

// eslint.config.js
import config from "@bonniernews/eslint-config/ts";

export default [ config ];

React configuration

// eslint.config.js
import config from "@bonniernews/eslint-config/jsx";

export default [ config ];

Test configuration

Adds useful plugins and globals for testing with mocha-cakes-2 + chai.

For JavaScript tests:

// eslint.config.js
import config from "@bonniernews/eslint-config/test-js";

export default [ config ];

For TypeScript tests:

// eslint.config.js
import config from "@bonniernews/eslint-config/test-ts";

export default [ config ];

Typed react configuration

// eslint.config.js
import config from "@bonniernews/eslint-config/tsx";

export default [ config ];

Global ignores

To activate this config (in addition to other config(s), using it alone makes no sense), add the following:

// eslint.config.js
import ignores from "@bonniernews/eslint-config/ignores";

export default [
  ...allYourGoodConfigs,
  ignores,
  // your additional config
];

Globals

Globals for browsers, etc. that may be needed.

// eslint.config.js
import globals from "@bonniernews/eslint-config/globals";

export default [
  ...allYourGoodConfigs,
  { files: [ "assets/scripts/**/*.js" ], languageOptions: { globals: globals.browser } },
];

Safety rules

A test suite that resolves its configuration to a real environment can reach that environment's data, and a reset helper pointed at it will do what it was asked to. The controls that prevent that live outside this package: the network guard is @bonniernews/stayput, the environment pin is the import-free file your test runner loads first, and the last line of defence is database roles that cannot drop anything.

This package ships the layer that notices when one of those is missing. The eight rules below come with the configs you already import, they are all warnings, and they add no dependency. Treat them as a smoke alarm rather than a lock: they read one file's syntax at a time, so they catch the ordinary mistake and not a determined workaround.

What each rule flags

bn-safety/require-test-guards — looks at the project, not at the file: does anything load the network guard, and does anything pin the environment, before your tests run? Those are the two things that stop a suite from resolving its config to a real environment and then reaching it.

// flagged      a project depending on pg whose runner loads only a setup file that sets NODE_ENV,
//              since exp-config prefers NODE_CONFIG_ENV and would ignore it
// not flagged  the same project once its setup list has "@bonniernews/stayput/register" and a file
//              containing process.env.NODE_CONFIG_ENV = "test"

bn-safety/gitignore-env — does any .gitignore between the file and the repository root exclude .env? A .env holds local credentials, and an unignored one is a single git add . away from the remote.

// flagged      a .gitignore listing only node_modules
// not flagged  .env, /.env, .env*, *.env or **/.env, in any .gitignore up to the root

bn-safety/env-pin-must-not-import — a file that pins the test environment must not import anything. Imports run before the rest of the file, so the imported module reads the environment first and can resolve its config from whatever leaked in from the shell, while the pin applies a moment too late.

// flagged
import nock from "nock";
process.env.NODE_CONFIG_ENV = "test";

// not flagged: the same two lines in two files, with the import-free pin loaded first
import { createHash } from "node:crypto";      // Node's own modules cannot read the config
beforeEach(() => (process.env.NODE_ENV = "development"));  // a test toggling a branch, not a pin

Only an assignment at the top level of the module counts as the pin.

bn-safety/no-env-pin-tampering — the switches that widen the network guard, or that let real environment variables override the test config, belong in the workflow file where a reviewer sees them and a fleet-wide grep finds them. Set from code they are invisible to both.

// flagged
process.env.STAYPUT_ALLOW = "db.prod.example.com";
process.env.ALLOW_TEST_ENV_OVERRIDE = "1";
stayput.enable({ allow: [ "db.prod.example.com" ] });

// not flagged
process.env.ALLOW_TEST_ENV_OVERRIDE = "";       // the pin file turning it off
process.env.NODE_CONFIG_ENV = "test";           // the pin doing its job

bn-safety/no-remote-db-target — tests may only reach this machine. Flags a database, cache or broker host spelled out in a test file, and both ways of turning off TLS certificate verification, which is the usual step when a test is being pointed at real infrastructure.

// flagged
"postgres://orders-db.prod.example.com:5432/orders"
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
{ rejectUnauthorized: false }

// not flagged
"postgres://localhost:5432/orders_test"          // this machine
"postgres://postgres:5432/app_test"              // a compose or CI service
`postgres://${config.dbHost}/orders`             // value not knowable at lint time
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "1";  // the safe direction

bn-safety/no-credentials-in-source — credentials belong in config or a secret manager. Written into a source file they are in the repository's history from then on, whatever happens to the file afterwards.

// flagged
"postgres://svc_orders:[email protected]:5432/orders"
`mongodb+srv://${user}:${password}@cluster0.mongodb.net/app`
a PEM "BEGIN ... PRIVATE KEY" header
{ password: "a-real-looking-password" }              // also as a class field

// not flagged
"postgres://orders:orders@localhost:5432/orders_test"   // local credentials
"postgres://username:[email protected]/orders"    // documentation placeholders
{ secret: "The shared secret is set per environment" }  // prose, not a secret
{ password: "test" }                                    // too short to be real
{ accessToken: "fake-access-token" }                    // a placeholder word marks a fixture

A fixture that is flagged is one that looks like a real credential. To keep it, make the fixture visible in the value: put a placeholder word such as fake, dummy, test, example or replaced in it as its own segment (fake-access-token, REPLACED_BY_ENV, test_key), or keep it shorter than 12 characters. Real secrets have no words in them, so this loses nothing.

bn-safety/no-widened-nock — in a suite that mocks http with nock, nock is what keeps requests on this machine. Calling enableNetConnect without a real allow list hands that back.

// flagged
nock.enableNetConnect();       // nothing is restricted
nock.enableNetConnect("");     // matches every host
nock.enableNetConnect(/.*/);   // same, written as a pattern

// not flagged
nock.enableNetConnect(/(localhost|127\.0\.0\.1):\d+/);

bn-safety/no-dotenv-override — dotenv's override option replaces variables that are already set, which includes the pin your test runner just applied. Without it dotenv leaves existing variables alone and the pin survives.

// flagged
dotenv.config({ override: true });
require("dotenv").config({ override: 1 });   // truthy counts

// not flagged
dotenv.config();
dotenv.config({ override: false });
telemetry.config({ override: true });        // not dotenv

Where the rules apply

The rules that read test files apply to files under test/, tests/, spec/ and __tests__/, and to files named *.test.js or *.spec.js, in JavaScript and TypeScript alike.

require-test-guards and gitignore-env look at the project instead. They stay silent unless package.json depends on a database, cache or message broker client, and they report once per project rather than once per test file. A library has nothing to guard, so it is not asked to install a guard.

require-test-guards is not tied to mocha. It works out which runner a project uses from its dependencies and its npm test script, then reads that runner's setup:

RunnerWhere it looks
mocha.mocharc.*, the mocha key in package.json
jestjest.config.*, the jest key in package.json
vitestvitest.config.*, vite.config.*
avaava.config.*, the ava key in package.json
tap.taprc*, the tap key in package.json
node --testthe npm test script, which is the only place its preloads can live

The npm test script is read whatever the runner, so a guard preloaded with --import or NODE_OPTIONS there counts, and so does NODE_CONFIG_ENV=test written in front of the runner. Relative paths in a runner's setup list are followed, so a guard imported by your own setup file counts too.

A runner that is not in that table means silence, not a warning. If your repo uses something else the rule says nothing rather than guessing, and adding a runner is a few lines in safety-plugin.js. That list will never be complete, which is why silence is the right failure.

What does not depend on the table at all is stayput's own assertActive(). One test asserting the guard is loaded works in any runner and any config format, and it fails the suite instead of warning about it — enforcement rather than steering. If you want certainty, that test is the lever; these rules are what notice the repos that never added it.

What the rules need from your project

All of it is in the node-starterapp template, and merging that template is the easiest way to satisfy these rules:

  • @bonniernews/stayput/register in your runner's setup list — the require list in .mocharc.json, setupFiles in a jest or vitest config, --import on a node --test command — or imported by a file in that list.
  • An import-free file, also loaded from that setup list, containing process.env.NODE_CONFIG_ENV = "test";. It has to be that variable: exp-config prefers it over NODE_ENV, so pinning only NODE_ENV still loads production configuration when NODE_CONFIG_ENV leaks in from a shell.
  • .env in .gitignore (.env, .env*, *.env and **/.env all count), and no production hosts or credentials in that file.
  • Test databases that run on this machine. Publish container ports to 127.0.0.1 rather than reaching a container by its own address.

What these rules cannot see

Worth knowing, so nobody mistakes a green lint run for a guarantee:

  • They can be turned off. Any repo can set them to "off" in its own eslint.config.js. These rules steer, they do not enforce. The controls that enforce are stayput at connect time and database roles at the server.
  • A host that comes from configuration is invisible, which is the case that matters most. A literal postgres://orders-db.prod.example.com/orders in a test is reported; `postgres://${config.dbHost}/orders` cannot be judged here and is deliberately left alone. Only stayput sees the host that is actually dialled.
  • Only the plain shapes of a variable assignment are recognised. process.env.X = …, process.env["X"] = … and globalThis.process.env.X = … are; const { env } = process and Object.assign(process.env, …) are not.
  • eslint --cache hides the project checks. A cached file is never handed to a rule, so removing the guard from a runner config goes unreported until a test file changes. Run lint in CI without --cache if you rely on require-test-guards.
  • A guard loaded outside the project's own files looks missing. require-test-guards reads the runner config, the files it loads and the npm test script. A guard injected through NODE_OPTIONS in a workflow file, or a runner config that builds its setup list in code, is not visible from there, so the rule can report a project that is in fact guarded.
  • Only JavaScript and TypeScript are linted. A secret in .env, in config/production.json, in a Terraform variables file or in a committed service-account key is invisible to any lint rule. That needs a file scanner in CI, plus GitHub secret scanning and push protection, which is also the only layer that stops a secret before it reaches the remote.
  • A tunnel to production on localhost looks local to every one of these rules, as it does to stayput. Database roles are what remains.

If a rule reports something wrong

Open an issue or a pull request here. If you need to move on in the meantime, disable the rule for that line with a reason, so the next reader knows why:

// eslint-disable-next-line bn-safety/no-remote-db-target -- mocked by nock, see BN-1234

One request: when a test fails because stayput refused a connection, or one of these rules reports a host outside this machine, the target is usually the problem and not the guard. Fix the target rather than widening the guard, and let a human decide about any exception.

Migrating from 2.X to 3.X

Version 3.X is published as an ES Module (ESM).

For ESM projects using import

If your project has "type": "module" in package.json, and you're already using import, then you don't have to make any changes.

For CommonJS projects using require

If your project does not have "type": "module", you need to use dynamic import() to import an ESM module, which is an asynchronous function:

Before (2.X):

"use strict";

const config = require("@bonniernews/eslint-config");

module.exports = [
  ...config,
  { ignores: [ "dist/**" ] },
]

After (3.X):

const { default: config } = await import("@bonniernews/eslint-config");

module.exports = [
  ...config,
  { ignores: [ "dist/**" ] },
]

Migrating from 1.X to 2.X

2.X introduces eslint 9 which has a different configuration format. It is recommended to read the eslint migration guide.

A major change from eslint 8 is that only one eslint.config.js file will be used, placing a specific configuration file in a folder will not behave in the same way as in 8 where it would inherit the configuration from files from the root folder, and the new recommendation is to just have one eslint.config.js at the root of the repository.

One major change from eslint 8 is that in order for ignores to be global they need to be added in a single config at the root level. If you just use the @bonniernews/eslint-config you will have it included, but if you construct your own set of rules you need to add it manually to your config file, otherwise eslint will run on files in terraform directories and such.

The different rule sets have changed name and behaviour:

  • @bonniernews/eslint-config will import configs and apply them to the respective targets
  • @bonniernews/eslint-config/js config for js files
  • @bonniernews/eslint-config/ts config for ts files
  • @bonniernews/eslint-config/jsx config for jsx files
  • @bonniernews/eslint-config/tsx config for tsx files
  • @bonniernews/eslint-config/test-js config for test js files using mocha-cakes-2 and chai
  • @bonniernews/eslint-config/test-ts config for test ts files using mocha-cakes-2 and chai
  • @bonniernews/eslint-config/ignores global ignores

Running eslint

Run with:

npx eslint .

Usage in an existing project

  • We advice to remove any husky hooks that uses libraries such as pretty-quick from your package.json
  • Subsequently remove any use of pretty-quick if possible.
  • Remove any previous use of sharable ESLint configs from package.json, i.e.:
    • eslint-config-airbnb
    • eslint-config-google
    • eslint-config-prettier
  • Remove eslint-plugin-prettier from package.json
  • If you get errors similar to the ones below, please update the eslint dependancy.
    • Definition for rule 'no-nonoctal-decimal-escape' was not found
    • Definition for rule 'no-unsafe-optional-chaining' was not found
  • If you still have issues; try updating npm (if you use nvm nvm install-latest-npm) & prettier as-well
  • Remove any 'eslint-disable-line no-unused-expressions' directives added because of chai assertions, they are not needed anymore (eslint-plugin-chai-friendly is used in test).
  • Remove any globals and special rules related to mocha-cakes-2 in your test configuration, they already exist in the @bonniernews/eslint-config/test-js and @bonniernews/eslint-config/test-ts configs.

Once you complete the steps above run the following:

npx eslint . --fix

Usage with Prettier

If you want to use Prettier, run it before eslint. ESLint should be the final judge, i.e. run:

npx prettier --write .
npx eslint . --fix

This will format the entire code base according to the rules of Prettier and the config.

Enable format on save

Changelog

Can be found here.

Publishing a new version

We automatically publish to both NPM and GitHub when you bump the version in package.json

License

Released under the MIT license.

版本与发布记录

@bonniernews/eslint-config 共发布 28 个版本,首次发布于 2023/09/11;过去 12 个月发布了 5 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.1.0latest2026/09/221 个月前
3.0.02026/04/296 个月前
2.0.52026/04/206 个月前
2.0.42026/02/258 个月前
2.0.32025/12/0311 个月前
2.0.22025/03/051.7 年前

其他发布通道:alpha → 2.0.0-alpha.12。

关于 @bonniernews/eslint-config 的常见问题

@bonniernews/eslint-config 是什么?

ESLint config

如何安装 @bonniernews/eslint-config?

在项目目录运行 npm install @bonniernews/eslint-config,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:eslint(>=9.19.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@bonniernews/eslint-config 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/bonniernews__eslint-config 是否存在。

@bonniernews/eslint-config 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bonniernews/eslint-config 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bonniernews/eslint-config 可以商用吗?

@bonniernews/eslint-config 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bonniernews/eslint-config 的下载量有多少?

根据 npm 官方下载统计,@bonniernews/eslint-config 在 2026/10/09 – 2026/11/07 这 30 天内被下载 20,891 次,日均约 696 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bonniernews/eslint-config 还在维护吗?

最新版本 3.1.0 发布于 2026/09/22(1 个月前);至今共发布 28 个版本,其中过去 12 个月发布了 5 个;首次发布于 2023/09/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。