Socket
Socket
Sign inDemoInstall

@rushstack/eslint-patch

Package Overview
Dependencies
Maintainers
3
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rushstack/eslint-patch - npm Package Compare versions

Comparing version 1.7.0 to 1.7.1

15

package.json
{
"name": "@rushstack/eslint-patch",
"version": "1.7.0",
"description": "A patch that improves how ESLint loads plugins when working in a monorepo with a reusable toolchain",
"version": "1.7.1",
"description": "Enhance ESLint with better support for large scale monorepos",
"main": "lib/usage.js",

@@ -21,7 +21,12 @@ "license": "MIT",

"relative",
"package"
"package",
"bulk",
"suppressions",
"monorepo",
"monkey",
"patch"
],
"devDependencies": {
"@rushstack/heft": "0.62.0",
"@rushstack/heft-node-rig": "2.3.2",
"@rushstack/heft": "0.64.0",
"@rushstack/heft-node-rig": "2.4.0",
"@types/node": "18.17.15",

@@ -28,0 +33,0 @@ "typescript": "~5.3.3",

# @rushstack/eslint-patch
A patch that improves how ESLint loads plugins when working in a monorepo with a reusable toolchain
Enhance your [ESLint](https://eslint.org/) with better support for large scale monorepos!
# modern-module-resolution
This is a runtime patch that enables new/experimental features for ESLint. It operates as a "monkey patch"
that gets loaded with **.eslintrc.js** and modifies the ESLint engine in memory. This approach works
with your existing ESLint version (no need to install a forked ESLint), and is fully interoperable with
companion tools such as the ESLint extensions for VS Code and WebStorm.
## What it does
This package provides several independently loadable features:
This patch is a workaround for a longstanding [ESLint feature request](https://github.com/eslint/eslint/issues/3458)
that would allow a shared ESLint config to bring along its own plugins, rather than imposing peer dependencies
on every consumer of the config. In a monorepo scenario, this enables your lint setup to be consolidated in a
single NPM package. Doing so greatly reduces the copy+pasting and version management for all the other projects
that use your standard lint rule set, but don't want to be bothered with the details.
- `eslint-bulk-suppressions` enables you to roll out new lint rules in your monorepo without having to
clutter up source files with thousands of machine-generated `// eslint-ignore-next-line` directives.
Instead, the "bulk suppressions" for legacy violations are managed in a separate file called
**.eslint-bulk-suppressions.json**.
ESLint provides partial solutions such as the `--resolve-plugins-relative-to` CLI option, however they are
awkward to use. For example, the VS Code extension for ESLint must be manually configured with this CLI option.
If some developers use other editors such as WebStorm, a different manual configuration is needed.
Also, the `--resolve-plugins-relative-to` parameter does not support multiple paths, for example if a config package
builds upon another package that also provides plugins. See
[this discussion](https://github.com/eslint/eslint/issues/3458#issuecomment-516666620)
for additional technical background.
- `modern-module-resolution` allows an ESLint config package to provide plugin dependencies, avoiding the
problem where hundreds of projects in a monorepo need to copy+paste the same `"devDependencies"` in
every **package.json** file.
> NOTE: ESLint 8.21.0 has now introduced a new `ESLINT_USE_FLAT_CONFIG` mode that may reduce the need
for the `modern-module-resolution` patch.
## Why it's a patch
- `custom-config-package-names` enables [rig packages](https://heft.rushstack.io/pages/intro/rig_packages/)
to provide shareable configs for ESLint, by removing the requirement that `eslint-config` must appear in
the NPM package name.
ESLint's long awaited module resolver overhaul still has not materialized as of ESLint 8. As a stopgap,
we created a small **.eslintrc.js** patch that solves the problem adequately for most real world scenarios.
This patch was proposed as an ESLint feature with [PR 12460](https://github.com/eslint/eslint/pull/12460), however
the maintainers were not able to accept it unless it is reworked into a fully correct design. Such a requirement
would impose the same hurdles as the original GitHub issue; thus, it seems best to stay with the patch approach.
Contributions welcome! If you have more ideas for experimental ESLint enhancements that might benefit
large scale monorepos, consider adding them to this patch.
Since the patch is now in wide use, we've converted it into a proper NPM package to simplify maintenance.
# eslint-bulk-suppressions feature
## How to use it
<!-- ## is correct here, but ### looks better in NPM's rendering -->
Add a `require()` call to the to top of the **.eslintrc.js** file for each project that depends on your shared
ESLint config, for example:
### What it does
**.eslintrc.js**
```ts
require("@rushstack/eslint-patch/modern-module-resolution");
As your monorepo evolves and grows, there's an ongoing need to expand and improve lint rules. But whenever a
new rule is enabled, there may be hundreds or thousands of "legacy violations" in existing source files.
How to handle that? We could fix the old code, but that's often prohibitively expensive and may even cause
regressions. We could disable the rule for those projects or files, but we want new code to follow the rule.
An effective solution is to inject thousands of `// eslint-ignore-next-line` lines, but these "bulk suppressions"
have an unintended side effect: It normalizes the practice of suppressing lint rules. If people get used to
seeing `// eslint-ignore-next-line` everywhere, nobody will notice when humans suppress the rules for new code.
That would undermine the mission of establishing better code standards.
// Add your "extends" boilerplate here, for example:
module.exports = {
extends: ['@your-company/eslint-config'],
parserOptions: { tsconfigRootDir: __dirname }
};
```
The `eslint-bulk-suppressions` feature introduces a way to store machine-generated suppressions in a separate
file **.eslint-bulk-suppressions.json** which can even be protected using `CODEOWNERS` policies, since that file
will generally only change when new lint rules are introduced, or in occasional circumstances when existing files
are being moved or renamed. In this way `// eslint-ignore-next-line` remains a directive written by humans
and hopefully rarely needed.
With this change, the local project no longer needs any ESLint plugins in its **package.json** file.
Instead, the hypothetical `@your-company/eslint-config` NPM package would declare the plugins as its
own dependencies.
This patch works by modifying the ESLint engine so that its module resolver will load relative to the folder of
the referencing config file, rather than the project folder. The patch is compatible with ESLint 6, 7, and 8.
It also works with any editor extensions that load ESLint as a library.
### Why it's a patch
There is a second patch in this package that removes the restriction on eslint configuration package names.
Similarly to the first, this patch is applied by adding a `require()` call to the top of the **.eslintrc.js**,
for example:
As with `modern-module-resolution`, our hope is for this feature to eventually be incorporated as an official
feature of ESLint. Starting out as an unofficial patch allows faster iteration and community feedback.
**.eslintrc.js**
```ts
require("@rushstack/eslint-patch/modern-module-resolution");
require("@rushstack/eslint-patch/custom-config-package-names"); // <-- Add this line
// Add your "extends" boilerplate here, for example:
module.exports = {
extends: [
'@your-company/build-rig/profile/default/includes/eslint/node' // Notice the package name does not start with "eslint-config-"
],
parserOptions: { tsconfigRootDir: __dirname }
};
```
### How to use it
For an even leaner setup, `@your-company/eslint-config` can provide the patches as its own dependency. See
[@rushstack/eslint-config](https://www.npmjs.com/package/@rushstack/eslint-config) for a real world example
and recommended approach.
1. Add `@rushstack/eslint-patch` as a dependency of your project:
# eslint-bulk-suppressions
```bash
cd your-project
npm install --save-dev @rushstack/eslint-patch
```
A tool that allows bulk suppression of ESLint warnings/errors in a large, old codebase when introducing new ESLint rules.
2. Globally install the [`@rushstack/eslint-bulk`](https://www.npmjs.com/package/@rushstack/eslint-bulk)
command line interface (CLI) package. For example:
## What it does
```bash
npm install --global @rushstack/eslint-bulk
```
This tool is designed to address the issue of introducing new ESLint rules to a large, old codebase, which often
results in hundreds to tens of thousands of retroactive issues being reported by ESLint. This can clutter the
ESLint output, making it difficult to read and potentially causing developers to overlook new ESLint issues. It
also makes it impractical to use merge request pipelines that block ESLint warnings/errors.
This installs the `eslint-bulk` shell command for managing the **.eslint-bulk-suppressions.json** files.
With it you can generate new suppressions as well as "prune" old suppressions that are no longer needed.
The tool provides a mechanism for recording all retroactively introduced ESLint warnings/errors in a
"bulk suppressions" file, hiding them from the ESLint output. This allows developers to still get most of the
benefits of ESLint, as any new code written will be annotated by ESLint and can be fixed in bite-sized portions.
It also allows the use of merge request pipelines to block newly written error-prone code without blocking legacy
code that has been battle-tested.
3. Load the patch by adding the following `require()` statement as the first line of
your **.eslintrc.js** file. For example:
## Why it's a patch
The bulk suppressions feature is implemented as a monkey-patch, inspired by the modern-module-resolution
implementation. We prefer it as a patch because it allows users to opt-in to using the tool at their own discretion.
Similar to modern-module-resolution, the use case is much more pronounced in large codebases where ESLint
warnings/errors can appear at magnitudes of thousands rather than tens. Besides reducing bundle size, this also
allows us to gauge interest in this tool.
**.eslintrc.js**
```js
require("@rushstack/eslint-patch/eslint-bulk-suppressions"); // 👈 add this line
This approach inevitably results in forwards compatibility issues with versions of ESLint. The patch has
some logic to determine which version of ESLint you're using and uses the corresponding patch file.
module.exports = {
rules: {
rule1: 'error',
rule2: 'warning'
},
parserOptions: { tsconfigRootDir: __dirname }
};
```
## How to use it
Typical workflow:
To use the tool, you need to add a `require()` call to the top of the **.eslintrc.js** file for each project
that you want to use the tool with, for example:
1. Checkout your `main` branch, which is in a clean state where ESLint reports no violations.
2. Update your configuration to enable the latest lint rules; ESLint now reports thousands of legacy violations.
3. Run `eslint-bulk suppress --all ./src` to update **.eslint-bulk-suppressions.json.**
4. ESLint now no longer reports violations, so commit the results to Git and merge your pull request.
5. Over time, engineers may improve some of the suppressed code, in which case the associated suppressions are no longer needed.
6. Run `eslint-bulk prune` periodically to find and remove unnecessary suppressions from **.eslint-bulk-suppressions.json**, ensuring that new violations will now get caught in those scopes.
**.eslintrc.js**
```js
require("@rushstack/eslint-patch/eslint-bulk-suppressions");
### "eslint-bulk suppress" command
module.exports = {
rules: {
rule1: 'error',
rule2: 'warning'
},
parserOptions: { tsconfigRootDir: __dirname }
};
```bash
eslint-bulk suppress --rule NAME1 [--rule NAME2...] PATH1 [PATH2...]
eslint-bulk suppress --all PATH1 [PATH2...]
```
We also highly recommend globally installing the companion CLI tool to your local system with
Use this command to automatically generate bulk suppressions for the specified lint rules and file paths.
The path argument is a [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) with the same syntax
as path arguments for the `eslint` command.
### "eslint-bulk prune" command
Use this command to automatically delete all unnecessary suppression entries in all
**.eslint-bulk-suppressions.json** files under the current working directory.
```bash
npm i -g @rushstack/eslint-bulk
eslint-bulk prune
```
The **eslint-bulk** package is a set of command line tools to use with the ESLint bulk suppressions patch.
eslint-bulk commands must be run in the same current working directory containing your package's pertaining
.eslintrc.js or .eslintrc.cjs file.
### Implementation notes
## eslint-bulk suppress
The `eslint-bulk` command is a thin wrapper whose behavior is actually provided by the patch itself.
In this way, if your monorepo contains projects using different versions of this package, the same globally
installed `eslint-bulk` command can be used under any project folder, and it will always invoke the correct
version of the engine compatible with that project. Because the patch is loaded by ESLint, the `eslint-bulk`
command must be invoked in a project folder that contains an **.eslintrc.js** configuration with correctly
installed **package.json** dependencies.
Use this command to automatically generate bulk suppressions for the given files and given rules.
Supply the paths as the main argument. The paths argument is a glob pattern that follows the same
rules as the "files" argument in the "eslint" command.
Here's an example of the bulk suppressions file content:
```bash
eslint-bulk suppress --rule NAME1 [--rule NAME2...] PATH1 [PATH2...]
eslint-bulk suppress --all PATH1 [PATH2...]
**.eslint-bulk-suppressions.json**
```js
{
"suppressions": [
{
"rule": "no-var",
"file": "./src/your-file.ts",
"scopeId": ".ExampleClass.exampleMethod"
}
]
}
```
The `rule` field is the ESLint rule name. The `file` field is the source file path, relative to the **eslintrc.js** file. The `scopeId` is a special string built from the names of containing structures. (For implementation details, take a look at the [calculateScopeId()](https://github.com/microsoft/rushstack/blob/e95c51088341f01516ee5a7639d57c3f6dce8772/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-patch.ts#L52) function.) The `scopeId` identifies a region of code where the rule should be suppressed, while being reasonably stable across edits of the source file.
## eslint-bulk prune
# modern-module-resolution feature
Use this command to automatically delete all unused suppression entries in all .eslint-bulk-suppressions.json
files under the current working directory.
### What it does
```bash
eslint-bulk prune
This patch is a workaround for a longstanding [ESLint feature request](https://github.com/eslint/eslint/issues/3458)
that would allow a shareable ESLint config to bring along its own plugins, rather than imposing peer dependencies
on every consumer of the config. In a monorepo scenario, this enables your lint setup to be consolidated in a
single NPM package. Doing so greatly reduces the copy+pasting and version management for all the other projects
that use your standard lint rule set, but don't want to be bothered with the details.
> **NOTE:** ESLint 8.21.0 has now introduced a new `ESLINT_USE_FLAT_CONFIG` mode that may reduce the need
> for this patch.
### Why it's a patch
We initially proposed this feature in a pull request for the official ESLint back in 2019, however the
maintainers preferred to implement a more comprehensive overhaul of the ESLint config engine. It ultimately
shipped with the experimental new `ESLINT_USE_FLAT_CONFIG` mode (still opt-in as of ESLint 8).
While waiting for that, Rush Stack's `modern-module-resolution` patch provided a reliable interim solution.
We will continue to maintain this patch as long as it is being widely used, but we encourage you to check out
`ESLINT_USE_FLAT_CONFIG` and see if it meets your needs.
### How to use it
1. Add `@rushstack/eslint-patch` as a dependency of your project:
```bash
cd your-project
npm install --save-dev @rushstack/eslint-patch
```
2. Add a `require()` call to the to top of the **.eslintrc.js** file for each project that depends
on your shareable ESLint config, for example:
**.eslintrc.js**
```ts
require("@rushstack/eslint-patch/modern-module-resolution"); // 👈 add this line
// Add your "extends" boilerplate here, for example:
module.exports = {
extends: ['@your-company/eslint-config'],
parserOptions: { tsconfigRootDir: __dirname }
};
```
With this change, the local project no longer needs any ESLint plugins in its **package.json** file.
Instead, the hypothetical `@your-company/eslint-config` NPM package would declare the plugins as its
own dependencies.
This patch works by modifying the ESLint engine so that its module resolver will load relative to the folder of
the referencing config file, rather than the project folder. The patch is compatible with ESLint 6, 7, and 8.
It also works with any editor extensions that load ESLint as a library.
For an even leaner setup, `@your-company/eslint-config` can provide the patches as its own dependency.
See [@rushstack/eslint-config](https://github.com/microsoft/rushstack/blob/main/eslint/eslint-config/patch/modern-module-resolution.js) for a real world example.
# custom-config-package-names feature
### What it does
Load the `custom-config-package-names` patch to remove ESLint's
[naming requirement](https://eslint.org/docs/latest/extend/shareable-configs)
that `eslint-config` must be part of the NPM package name for shareable configs.
This is useful because Rush Stack's [rig package](https://heft.rushstack.io/pages/intro/rig_packages/)
specification defines a way for many different tooling configurations and dependencies to be shared
via a single NPM package, for example
[`@rushstack/heft-web-rig`](https://www.npmjs.com/package/@rushstack/heft-web-rig).
Rigs avoid a lot of copy+pasting of dependencies in a large scale monorepo.
Rig packages always include the `-rig` suffix in their name. It doesn't make sense to enforce
that `eslint-config` should also appear in the name of a package that includes shareable configs
for many other tools besides ESLint.
### How to use it
Continuing the example above, to load this patch you would add a second line to your config file:
**.eslintrc.js**
```ts
require("@rushstack/eslint-patch/modern-module-resolution");
require("@rushstack/eslint-patch/custom-config-package-names"); // 👈 add this line
// Add your "extends" boilerplate here, for example:
module.exports = {
extends: [
'@your-company/build-rig/profile/default/includes/eslint/node' // Notice the package name does not start with "eslint-config-"
],
parserOptions: { tsconfigRootDir: __dirname }
};
```
# Links

@@ -160,2 +250,4 @@

- [`@rushstack/eslint-bulk`](https://www.npmjs.com/package/@rushstack/eslint-bulk) CLI package
`@rushstack/eslint-patch` is part of the [Rush Stack](https://rushstack.io/) family of projects.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc