From 691585f9ee3509d74f164fefa95dd1cba832702a Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 20 Aug 2025 11:01:53 +0200 Subject: [PATCH 01/14] docs: multithread linting in v9.34.0 --- .../blog/2025-08-22-multithread-linting.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/content/blog/2025-08-22-multithread-linting.md diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md new file mode 100644 index 000000000..c4a30654b --- /dev/null +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -0,0 +1,116 @@ +--- +layout: post +title: "New in ESLint v9.34.0: Multithread Linting" +teaser: "Make use of your CPU: multithreaded linting in ESLint v9.34.0 speeds up linting large projects." +tags: + - Concurrency + - Multithreading + - Performance +draft: true +authors: + - fasttime +categories: + - Storytime +--- + +## Introduction + +With ESLint v9.34.0, linting can now happen in true parallel. By spawning worker threads, ESLint can process multiple files at the same time, dramatically reducing lint times for large projects. + +On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. + +## CLI Multithreading Support + +Multithreading is enabled through the new `--concurrency` flag. You can set it in a few different ways: + +- **A positive integer** (e.g., `4`) tells ESLint the maximum number of threads to use. It will never spawn more threads than there are files to lint. +- **`auto`** lets ESLint decide the optimal number of threads based on your CPU and file count. +- **`off`** (the default) disables multithreading, equivalent to `--concurrency=1`. + +The biggest gains come when linting many files on a multi-core machine with fast I/O. That said, if your configuration or plugins take a long time to initialize, using too many threads can actually slow things down. In CI runners, performance can be biased by limits of the host environment. + +## Node.js API Support + +If you're using the [Node.js API](https://eslint.org/docs/latest/integrate/nodejs-api), multithread linting is available via the new `concurrency` option in the `ESLint` constructor. The accepted values — numbers, `"auto"`, and `"off"` — work exactly as they do in the CLI. + +### Cloneability Requirement + +When concurrency is enabled, ESLint passes options to worker threads using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This means only cloneable values — such as primitives, plain objects, and arrays — can be sent. Functions and custom class instances are not cloneable. + +Some options, like `plugin`, `baseConfig`, and `overrideConfig`, can contain arbitrary values, while others, like `fix` and `ruleFilter`, can be functions. If you set an uncloneable value while concurrency is enabled, ESLint will throw an error: + +> The option "ruleFilter" cannot be cloned. When concurrency is enabled, all options must be cloneable. Remove uncloneable options or use an options module. + +### Using Options Modules + +To work around cloneability limits, you can define your ESLint options in an ECMAScript module: + +```js +// eslint-options.js +import config from "./my-eslint-config.js"; + +export default { + concurrency: "auto", + overrideConfig: config, // may include non-cloneable values + overrideConfigFile: true, + stats: true, +}; +``` + +Then create an instance with [`ESLint.fromOptionsModule()`](https://eslint.org/docs/head/integrate/nodejs-api#-eslintfromoptionsmoduleoptionsurl): + +```js +const optionsURL = new URL("./eslint-options.js", import.meta.url); +const eslint = await ESLint.fromOptionsModule(optionsURL); +``` + +In multithread mode, each worker imports the same module rather than receiving a cloned copy, so cloneability is no longer an issue. + +You can even inline your options module using a data URL: + +```js +const optionsModuleText = ` +import config from "./my-eslint-config.js"; + +export default { + concurrency: "auto", + overrideConfig: config, + overrideConfigFile: true, + stats: true, +}; +`; +const optionsURL = new URL(`data:text/javascript,${encodeURIComponent(optionsModuleText)}`); +const eslint = await ESLint.fromOptionsModule(optionsURL); +``` + +`ESLint.fromOptionsModule()` works with any URL type accessible to worker threads. While designed for multithread linting, options modules are a standalone feature you can use even without concurrency. + +## How It Works + +For years, ESLint has benefited from Node.js's event-loop architecture, which allows asynchronous tasks — like reading files from disk — to run in parallel. But CPU-intensive work, such as parsing files and applying rules, has always been synchronous, meaning only one file could be processed at a time. + +In a typical run, file I/O is a small fraction of the total time; most of it is spent on parsing and rule execution. These tasks cannot be parallelized with a single thread. Multithread linting changes that. Each worker thread processes one file at a time, but multiple threads can work simultaneously. When all threads finish, their results are gathered in the main thread and returned together. + +## History + +The idea of parallelizing the linting process has been around for well over a decade, long before Node.js offered built-in worker threads. Early discussions wrestled with fundamental questions: which tools should be used for parallelization, how far should the scope extend, and what architectural changes would be required to make it possible. There were also concerns about the potential downsides — the extra maintenance burden, the risk of slowing down projects with only a handful of files if parallelization were enabled by default, and the possibility of blocking other desirable features such as project-based linting, which would require ESLint to understand relationships between multiple files even when linting them individually. + +In the meantime, other tools forged ahead. Test runners like Ava, Mocha, and Jest began to ship with built-in parallel execution, and inventive developers found ways to run ESLint in parallel using wrappers and external scripts. These experiments proved that parallel linting could be valuable, but they also highlighted the limitations of bolting it on from the outside. Ultimately, the only way to make multithread linting truly seamless was to integrate it directly into ESLint's core. + +## Challenges + +Turning that long-standing idea into a practical, user-friendly feature was far from trivial. The first step was to gather the many threads of past conversations — most notably the proposals and debates in [issue #3565](https://github.com/eslint/eslint/issues/3565) — and weave them into a coherent design that could work equally well from the CLI and the Node.js API. The goal was to introduce multithreading with as little disruption as possible for existing consumers. + +Technical hurdles soon followed. Chief among them was the requirement that all options passed to worker threads be cloneable, a restriction that ruled out certain common patterns such as passing functions or complex plugin objects directly. This limitation ultimately inspired the creation of options modules, which sidestep the cloneability problem entirely by letting each worker import the same module rather than receiving a cloned copy of the options. + +Bringing multithread linting to life was a collaborative effort, shaped by the ideas, feedback, and testing of both the ESLint team and the wider community. We are thankful to everyone involved in the process of making this concept become a reality. + +## Way Forward + +We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt the feature, we expect to hear about issues, and we're committed to fixing them quickly. + +## Further Tips + +- Benchmark lint times before and after enabling concurrency to measure the impact. +- Try different `--concurrency` values on each machine to find the ideal setting. +- Combine `--cache` with multithreading for even faster incremental runs. From e8212a7d71023d3764772b3a1abf747cf7e99a10 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 20 Aug 2025 11:11:32 +0200 Subject: [PATCH 02/14] fix formatting --- src/content/blog/2025-08-22-multithread-linting.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index c4a30654b..18a911310 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -23,9 +23,9 @@ On machines with multiple CPU cores and fast storage, this change can make a not Multithreading is enabled through the new `--concurrency` flag. You can set it in a few different ways: -- **A positive integer** (e.g., `4`) tells ESLint the maximum number of threads to use. It will never spawn more threads than there are files to lint. -- **`auto`** lets ESLint decide the optimal number of threads based on your CPU and file count. -- **`off`** (the default) disables multithreading, equivalent to `--concurrency=1`. +* **A positive integer** (e.g., `4`) tells ESLint the maximum number of threads to use. It will never spawn more threads than there are files to lint. +* **`auto`** lets ESLint decide the optimal number of threads based on your CPU and file count. +* **`off`** (the default) disables multithreading, equivalent to `--concurrency=1`. The biggest gains come when linting many files on a multi-core machine with fast I/O. That said, if your configuration or plugins take a long time to initialize, using too many threads can actually slow things down. In CI runners, performance can be biased by limits of the host environment. @@ -111,6 +111,6 @@ We know that introducing multithread linting won't be without its hiccups. Some ## Further Tips -- Benchmark lint times before and after enabling concurrency to measure the impact. -- Try different `--concurrency` values on each machine to find the ideal setting. -- Combine `--cache` with multithreading for even faster incremental runs. +* Benchmark lint times before and after enabling concurrency to measure the impact. +* Try different `--concurrency` values on each machine to find the ideal setting. +* Combine `--cache` with multithreading for even faster incremental runs. From aa5317349f37dddb6ebb13f855d96f3ac2354046 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 20 Aug 2025 11:35:42 +0200 Subject: [PATCH 03/14] change category --- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 18a911310..a2eac9371 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -10,7 +10,7 @@ draft: true authors: - fasttime categories: - - Storytime + - Announcements --- ## Introduction From 5b93d5ae7ad5cff463d4d2617cecdfbc531f7629 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Thu, 21 Aug 2025 07:04:49 +0200 Subject: [PATCH 04/14] update --- src/_data/blog-dates.json | 3 +- .../blog/2025-08-22-multithread-linting.md | 60 +++++++++++-------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 94cd0b6dd..06678eee3 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -439,5 +439,6 @@ "2025-07-01-eslint-v9.30.1-released.md": "2025-07-01T20:27:20.092Z", "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", - "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z" + "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", + "2025-08-22-multithread-linting.md": "2025-08-21T05:04:50.174Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index a2eac9371..a98cbef88 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -13,11 +13,37 @@ categories: - Announcements --- -## Introduction +ESLint v9.34.0 introduces multithread linting, concluding a feature that's been in the making over ten years. +By spawning several worker threads, ESLint can now process multiple files at the same time, dramatically reducing lint times for large projects. +On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. -With ESLint v9.34.0, linting can now happen in true parallel. By spawning worker threads, ESLint can process multiple files at the same time, dramatically reducing lint times for large projects. +## History -On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. +The idea of parallelizing the linting process has been around for well over a decade, long before Node.js offered built-in worker threads. Early discussions wrestled with fundamental questions: which tools should be used for parallelization, how far should the scope extend, and what architectural changes would be required to make it possible. There were also concerns about the potential downsides — the extra maintenance burden, the risk of slowing down projects with only a handful of files if parallelization were enabled by default, and the possibility of blocking other desirable features such as project-based linting, which would require ESLint to understand relationships between multiple files even when linting them individually. + +In the meantime, other tools forged ahead. Test runners like Ava, Mocha, and Jest began to ship with built-in parallel execution, and inventive developers found ways to run ESLint in parallel using wrappers and external scripts. These experiments proved that parallel linting could be valuable, but they also highlighted the limitations of bolting it on from the outside. Ultimately, the only way to make multithread linting truly seamless was to integrate it directly into ESLint's core. + +## Challenges + +Turning that long-standing idea into a practical, user-friendly feature was far from trivial. The first step was to gather the many threads of past conversations — most notably the proposals and debates in [issue #3565](https://github.com/eslint/eslint/issues/3565) — and weave them into a coherent design that could work equally well from the CLI and the Node.js API. The goal was to introduce multithreading with as little disruption as possible for existing consumers, so that the only change they would notice was faster builds and warmer processors. Alongside that, we wanted an automatic (`auto`) concurrency mode that could choose sensible defaults in most situations, as well as a way to warn users when their chosen settings might actually be slowing things down. + +### Overcoming Cloneability Constraints + +Technical hurdles soon followed. Chief among them was the requirement that all options passed to worker threads be cloneable, a restriction that ruled out certain common patterns such as passing functions or complex plugin objects directly. This limitation ultimately inspired the creation of options modules, which sidestep the cloneability problem entirely by letting each worker import the same module rather than receiving a cloned copy of the options. + +### Making `auto` Work Seamlessly + +The `auto` mode brought its own set of challenges. To pick the right number of threads, ESLint needed to know how many files it would be linting — information that only becomes available after file enumeration. This meant refactoring the code so that the thread count could be calculated between enumerating files and creating threads, something an external wrapper could never have done cleanly. + +### Detecting Suboptimal Concurrency + +Finally, there was the question of how to warn users when their concurrency setting was actually hurting performance. Our approach was to measure the duration of different operations across threads, compare the results, and look for patterns that suggested a slowdown. It's not perfect — and we may want to refine it over time — but it gives us a starting point for helping users get the best out of multithread linting. + +## How It Works + +In a sense, ESLint has been doing "parallel" linting for years, thanks to Node.js's event-loop architecture, which allows asynchronous tasks — like reading files from disk — to run concurrently. However, CPU-intensive work such as parsing files and applying rules has always been synchronous, meaning only one file could be processed at a time. + +In a typical run, file I/O is a small fraction of the total time; most of it is spent on parsing and rule execution. These tasks cannot be parallelized with a single thread. Multithread linting changes that. Each worker thread processes one file at a time, but multiple threads can work simultaneously. When all threads finish, their results are gathered in the controlling thread and returned together. ## CLI Multithreading Support @@ -85,32 +111,14 @@ const eslint = await ESLint.fromOptionsModule(optionsURL); `ESLint.fromOptionsModule()` works with any URL type accessible to worker threads. While designed for multithread linting, options modules are a standalone feature you can use even without concurrency. -## How It Works - -For years, ESLint has benefited from Node.js's event-loop architecture, which allows asynchronous tasks — like reading files from disk — to run in parallel. But CPU-intensive work, such as parsing files and applying rules, has always been synchronous, meaning only one file could be processed at a time. - -In a typical run, file I/O is a small fraction of the total time; most of it is spent on parsing and rule execution. These tasks cannot be parallelized with a single thread. Multithread linting changes that. Each worker thread processes one file at a time, but multiple threads can work simultaneously. When all threads finish, their results are gathered in the main thread and returned together. - -## History - -The idea of parallelizing the linting process has been around for well over a decade, long before Node.js offered built-in worker threads. Early discussions wrestled with fundamental questions: which tools should be used for parallelization, how far should the scope extend, and what architectural changes would be required to make it possible. There were also concerns about the potential downsides — the extra maintenance burden, the risk of slowing down projects with only a handful of files if parallelization were enabled by default, and the possibility of blocking other desirable features such as project-based linting, which would require ESLint to understand relationships between multiple files even when linting them individually. - -In the meantime, other tools forged ahead. Test runners like Ava, Mocha, and Jest began to ship with built-in parallel execution, and inventive developers found ways to run ESLint in parallel using wrappers and external scripts. These experiments proved that parallel linting could be valuable, but they also highlighted the limitations of bolting it on from the outside. Ultimately, the only way to make multithread linting truly seamless was to integrate it directly into ESLint's core. - -## Challenges +## Further Tips -Turning that long-standing idea into a practical, user-friendly feature was far from trivial. The first step was to gather the many threads of past conversations — most notably the proposals and debates in [issue #3565](https://github.com/eslint/eslint/issues/3565) — and weave them into a coherent design that could work equally well from the CLI and the Node.js API. The goal was to introduce multithreading with as little disruption as possible for existing consumers. +* Benchmark lint times before and after enabling concurrency to measure the impact. +* Try different `--concurrency` values on each machine to find the ideal setting. +* Combine `--cache` with multithreading for even faster incremental runs. -Technical hurdles soon followed. Chief among them was the requirement that all options passed to worker threads be cloneable, a restriction that ruled out certain common patterns such as passing functions or complex plugin objects directly. This limitation ultimately inspired the creation of options modules, which sidestep the cloneability problem entirely by letting each worker import the same module rather than receiving a cloned copy of the options. +## Way Forward Bringing multithread linting to life was a collaborative effort, shaped by the ideas, feedback, and testing of both the ESLint team and the wider community. We are thankful to everyone involved in the process of making this concept become a reality. -## Way Forward - We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt the feature, we expect to hear about issues, and we're committed to fixing them quickly. - -## Further Tips - -* Benchmark lint times before and after enabling concurrency to measure the impact. -* Try different `--concurrency` values on each machine to find the ideal setting. -* Combine `--cache` with multithreading for even faster incremental runs. From e0a9853157c5488a034cf79ae4a7c0845477fc77 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Thu, 21 Aug 2025 07:44:31 +0200 Subject: [PATCH 05/14] fix --- src/_data/blog-dates.json | 2 +- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 06678eee3..4eb68f9ac 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-21T05:04:50.174Z" + "2025-08-22-multithread-linting.md": "2025-08-21T05:44:31.557Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index a98cbef88..ca6e7935c 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -121,4 +121,4 @@ const eslint = await ESLint.fromOptionsModule(optionsURL); Bringing multithread linting to life was a collaborative effort, shaped by the ideas, feedback, and testing of both the ESLint team and the wider community. We are thankful to everyone involved in the process of making this concept become a reality. -We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt the feature, we expect to hear about issues, and we're committed to fixing them quickly. +We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt it in the feature, we expect to hear about issues, and we're committed to fixing them quickly. From 67ae945402465f8f436ae7ffa85b2e49a38899d4 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Thu, 21 Aug 2025 13:09:05 +0200 Subject: [PATCH 06/14] Limitations of `auto` Concurrency --- src/_data/blog-dates.json | 2 +- .../blog/2025-08-22-multithread-linting.md | 21 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 4eb68f9ac..c7b4a88fb 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-21T05:44:31.557Z" + "2025-08-22-multithread-linting.md": "2025-08-21T11:09:06.557Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index ca6e7935c..0c8990f91 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -14,7 +14,8 @@ categories: --- ESLint v9.34.0 introduces multithread linting, concluding a feature that's been in the making over ten years. -By spawning several worker threads, ESLint can now process multiple files at the same time, dramatically reducing lint times for large projects. +By spawning several worker threads, ESLint can now process multiple files at the same time, dramatically reducing lint times for large projects. + On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. ## History @@ -49,12 +50,22 @@ In a typical run, file I/O is a small fraction of the total time; most of it is Multithreading is enabled through the new `--concurrency` flag. You can set it in a few different ways: -* **A positive integer** (e.g., `4`) tells ESLint the maximum number of threads to use. It will never spawn more threads than there are files to lint. -* **`auto`** lets ESLint decide the optimal number of threads based on your CPU and file count. +* **A positive integer** (e.g., `4`) tells ESLint the maximum number of threads to use. It will never spawn more threads than there are files to lint. +* **`auto`** lets ESLint decide the number of threads based on your CPU and file count. For sufficiently large projects, this heuristic defaults to half the number of reported CPUs. * **`off`** (the default) disables multithreading, equivalent to `--concurrency=1`. The biggest gains come when linting many files on a multi-core machine with fast I/O. That said, if your configuration or plugins take a long time to initialize, using too many threads can actually slow things down. In CI runners, performance can be biased by limits of the host environment. +### Limitations of `auto` Concurrency + +By design, for sufficiently large projects, `--concurrency=auto` calculates the thread count as half of the number of available CPU cores. In many cases, this setting hits the sweet spot. + +However, hardware varies. Node.js can't reliably tell physical from logical (hyperthreaded) cores or high-performance from efficiency-optimized cores, so the `auto` heuristic can sometimes under- or overshoot. + +If the target machine and project size are known in advance, it's better to try out different numeric `--concurrency` values (2, 3, 4, etc.) and choose the fastest. + +Looking forward, we may explore ways to improve the `auto` heuristic to better take into account the nuances of real-world hardware. + ## Node.js API Support If you're using the [Node.js API](https://eslint.org/docs/latest/integrate/nodejs-api), multithread linting is available via the new `concurrency` option in the `ESLint` constructor. The accepted values — numbers, `"auto"`, and `"off"` — work exactly as they do in the CLI. @@ -113,8 +124,8 @@ const eslint = await ESLint.fromOptionsModule(optionsURL); ## Further Tips -* Benchmark lint times before and after enabling concurrency to measure the impact. -* Try different `--concurrency` values on each machine to find the ideal setting. +* Benchmark lint times before and after enabling concurrency to measure the impact. +* Try different `--concurrency` values on each machine to find the ideal setting—don't rely solely on `auto`. * Combine `--cache` with multithreading for even faster incremental runs. ## Way Forward From 80deeb582c9e9d1e512a0e3d556b7934b7c7db82 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 11:15:02 +0200 Subject: [PATCH 07/14] Apply suggestions from code review Co-authored-by: Nicholas C. Zakas --- src/content/blog/2025-08-22-multithread-linting.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 0c8990f91..5de25fd42 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -13,7 +13,7 @@ categories: - Announcements --- -ESLint v9.34.0 introduces multithread linting, concluding a feature that's been in the making over ten years. +ESLint v9.34.0 introduces multithread linting, concluding a feature that's been in the making for [over ten years](https://github.com/eslint/eslint/issues/3565). By spawning several worker threads, ESLint can now process multiple files at the same time, dramatically reducing lint times for large projects. On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. @@ -34,7 +34,7 @@ Technical hurdles soon followed. Chief among them was the requirement that all o ### Making `auto` Work Seamlessly -The `auto` mode brought its own set of challenges. To pick the right number of threads, ESLint needed to know how many files it would be linting — information that only becomes available after file enumeration. This meant refactoring the code so that the thread count could be calculated between enumerating files and creating threads, something an external wrapper could never have done cleanly. +The `auto` mode brought its own set of challenges. To pick the right number of threads, ESLint needed to know how many files it would be linting — information that only becomes available after file enumeration. This meant refactoring the code so that the thread count could be calculated after enumerating files and before creating threads, something an external wrapper can't accomplish cleanly. ### Detecting Suboptimal Concurrency @@ -128,7 +128,7 @@ const eslint = await ESLint.fromOptionsModule(optionsURL); * Try different `--concurrency` values on each machine to find the ideal setting—don't rely solely on `auto`. * Combine `--cache` with multithreading for even faster incremental runs. -## Way Forward +## Conclusion Bringing multithread linting to life was a collaborative effort, shaped by the ideas, feedback, and testing of both the ESLint team and the wider community. We are thankful to everyone involved in the process of making this concept become a reality. From 9ca8ae87e3fc71af382bff5a07c65544e1d83d0d Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 11:21:21 +0200 Subject: [PATCH 08/14] Update intro Co-authored-by: Nicholas C. Zakas --- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 5de25fd42..86b684072 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -16,7 +16,7 @@ categories: ESLint v9.34.0 introduces multithread linting, concluding a feature that's been in the making for [over ten years](https://github.com/eslint/eslint/issues/3565). By spawning several worker threads, ESLint can now process multiple files at the same time, dramatically reducing lint times for large projects. -On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. +On machines with multiple CPU cores and fast storage, this change can make a noticeable difference — especially when you're linting hundreds or thousands of files. Our early testers have seen a speedup of around [1.30x](https://github.com/eslint/eslint/pull/19794#issuecomment-2970812265) as a starting point, while one even reported a [3.01x improvement](https://github.com/eslint/eslint/pull/19794#issuecomment-2969486788). ## History From ab5a0f5b1bc9d061cdb2d38472bd379f8453ccc2 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 11:25:43 +0200 Subject: [PATCH 09/14] Apply more suggestions --- src/_data/blog-dates.json | 2 +- src/content/blog/2025-08-22-multithread-linting.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index c7b4a88fb..08d1e0b5d 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-21T11:09:06.557Z" + "2025-08-22-multithread-linting.md": "2025-08-22T09:25:46.276Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 86b684072..a330a61f1 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -22,11 +22,11 @@ On machines with multiple CPU cores and fast storage, this change can make a not The idea of parallelizing the linting process has been around for well over a decade, long before Node.js offered built-in worker threads. Early discussions wrestled with fundamental questions: which tools should be used for parallelization, how far should the scope extend, and what architectural changes would be required to make it possible. There were also concerns about the potential downsides — the extra maintenance burden, the risk of slowing down projects with only a handful of files if parallelization were enabled by default, and the possibility of blocking other desirable features such as project-based linting, which would require ESLint to understand relationships between multiple files even when linting them individually. -In the meantime, other tools forged ahead. Test runners like Ava, Mocha, and Jest began to ship with built-in parallel execution, and inventive developers found ways to run ESLint in parallel using wrappers and external scripts. These experiments proved that parallel linting could be valuable, but they also highlighted the limitations of bolting it on from the outside. Ultimately, the only way to make multithread linting truly seamless was to integrate it directly into ESLint's core. +In the meantime, other tools forged ahead. Test runners like [AVA](https://github.com/avajs/ava), [Mocha](https://mochajs.org/), and [Jest](https://jestjs.io/) began to ship with built-in parallel execution, and inventive developers found ways to run ESLint in parallel using wrappers and external scripts. These experiments proved that parallel linting could be valuable, but they also highlighted the limitations of bolting it on from the outside. Ultimately, the only way to make multithread linting truly seamless was to integrate it directly into ESLint's core. ## Challenges -Turning that long-standing idea into a practical, user-friendly feature was far from trivial. The first step was to gather the many threads of past conversations — most notably the proposals and debates in [issue #3565](https://github.com/eslint/eslint/issues/3565) — and weave them into a coherent design that could work equally well from the CLI and the Node.js API. The goal was to introduce multithreading with as little disruption as possible for existing consumers, so that the only change they would notice was faster builds and warmer processors. Alongside that, we wanted an automatic (`auto`) concurrency mode that could choose sensible defaults in most situations, as well as a way to warn users when their chosen settings might actually be slowing things down. +Turning that long-standing idea into a practical, user-friendly feature was far from trivial. The first step was to gather the many threads of past conversations — most notably the proposals and debates in [issue #3565](https://github.com/eslint/eslint/issues/3565) — and weave them into a coherent design that could work equally well from the CLI and the Node.js API. The goal was to introduce multithreading with as little disruption as possible for existing users, so the only noticeable change would be faster builds and slightly warmer processors. Alongside that, we wanted an automatic (`auto`) concurrency mode that could choose sensible defaults in most situations, as well as a way to warn users when their chosen settings might actually be slowing things down. ### Overcoming Cloneability Constraints From 4385f2f20f187d042e7502c39e171e051dc2b89c Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 14:17:06 +0200 Subject: [PATCH 10/14] Elaborate Further Tips --- src/_data/blog-dates.json | 2 +- .../blog/2025-08-22-multithread-linting.md | 32 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 08d1e0b5d..49d20b11b 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-22T09:25:46.276Z" + "2025-08-22-multithread-linting.md": "2025-08-22T12:17:09.052Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index a330a61f1..23b3326a0 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -1,7 +1,7 @@ --- layout: post title: "New in ESLint v9.34.0: Multithread Linting" -teaser: "Make use of your CPU: multithreaded linting in ESLint v9.34.0 speeds up linting large projects." +teaser: "Make use of your CPU: multithread linting in ESLint v9.34.0 speeds up linting large projects." tags: - Concurrency - Multithreading @@ -54,7 +54,7 @@ Multithreading is enabled through the new `--concurrency` flag. You can set it i * **`auto`** lets ESLint decide the number of threads based on your CPU and file count. For sufficiently large projects, this heuristic defaults to half the number of reported CPUs. * **`off`** (the default) disables multithreading, equivalent to `--concurrency=1`. -The biggest gains come when linting many files on a multi-core machine with fast I/O. That said, if your configuration or plugins take a long time to initialize, using too many threads can actually slow things down. In CI runners, performance can be biased by limits of the host environment. +The biggest gains come when linting many files on a multi-core machine with fast I/O. That said, if your configuration or plugins take a long time to initialize, using too many threads can actually slow things down. ### Limitations of `auto` Concurrency @@ -124,9 +124,31 @@ const eslint = await ESLint.fromOptionsModule(optionsURL); ## Further Tips -* Benchmark lint times before and after enabling concurrency to measure the impact. -* Try different `--concurrency` values on each machine to find the ideal setting—don't rely solely on `auto`. -* Combine `--cache` with multithreading for even faster incremental runs. +Enhance linting performance by following these practical tips: + +### 1. Benchmark Your Setup + +Measure linting times with different `--concurrency` settings. For example, use [hyperfine](https://github.com/sharkdp/hyperfine) like this: + +```shell +hyperfine --parameter-list concurrency off,2,3,4 "node node_modules/eslint/bin/eslint --concurrency {concurrency}" +``` + +This yields comparable results for various thread counts. + +### 2. Adjust Concurrency per Machine + +* If feasible, try different numeric `--concurrency` values on each machine instead of relying on `auto`. +* Start with half your machine's physical cores, then test higher or lower values. +* Include `--concurrency=off` to see how single-threaded runs perform. + +### 3. Leverage Cahing + +Use `--cache` alongside—or in place of—`--concurrency` to speed up incremental runs. + +### 4. CI and Container Environments + +Be aware that CI runners and containers may throttle resources or use virtualized CPU cores. Re-benchmark in those environments to verify whether multithread linting delivers a meaningful performance improvement. ## Conclusion From 845ea02409c19c19b04a6c1f91a71f3a17c3a0df Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 15:11:08 +0200 Subject: [PATCH 11/14] fix typo --- src/_data/blog-dates.json | 2 +- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 49d20b11b..085321092 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-22T12:17:09.052Z" + "2025-08-22-multithread-linting.md": "2025-08-22T13:11:10.796Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 23b3326a0..ede312153 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -142,7 +142,7 @@ This yields comparable results for various thread counts. * Start with half your machine's physical cores, then test higher or lower values. * Include `--concurrency=off` to see how single-threaded runs perform. -### 3. Leverage Cahing +### 3. Leverage Caching Use `--cache` alongside—or in place of—`--concurrency` to speed up incremental runs. From 1ae5f3680b1b1f13715e42275391198a126e347a Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 17:30:06 +0200 Subject: [PATCH 12/14] update link to `ESLint.fromOptionsModule()` --- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index ede312153..95f2930aa 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -94,7 +94,7 @@ export default { }; ``` -Then create an instance with [`ESLint.fromOptionsModule()`](https://eslint.org/docs/head/integrate/nodejs-api#-eslintfromoptionsmoduleoptionsurl): +Then create an instance with [`ESLint.fromOptionsModule()`](/docs/latest/integrate/nodejs-api#-eslintfromoptionsmoduleoptionsurl): ```js const optionsURL = new URL("./eslint-options.js", import.meta.url); From c34498f419353767dfc35d976ad5414712af3340 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 17:43:44 +0200 Subject: [PATCH 13/14] Update src/content/blog/2025-08-22-multithread-linting.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/content/blog/2025-08-22-multithread-linting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 95f2930aa..435cefad3 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -154,4 +154,4 @@ Be aware that CI runners and containers may throttle resources or use virtualize Bringing multithread linting to life was a collaborative effort, shaped by the ideas, feedback, and testing of both the ESLint team and the wider community. We are thankful to everyone involved in the process of making this concept become a reality. -We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt it in the feature, we expect to hear about issues, and we're committed to fixing them quickly. +We know that introducing multithread linting won't be without its hiccups. Some tools and configurations may not play nicely at first. As users adopt it in the future, we expect to hear about issues, and we're committed to fixing them quickly. From 655bea4c3497ba192f9ae1eb7d6585a9db0ced9d Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 22 Aug 2025 21:45:00 +0200 Subject: [PATCH 14/14] fix example code --- src/_data/blog-dates.json | 2 +- src/content/blog/2025-08-22-multithread-linting.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/_data/blog-dates.json b/src/_data/blog-dates.json index 085321092..c766a38f5 100644 --- a/src/_data/blog-dates.json +++ b/src/_data/blog-dates.json @@ -440,5 +440,5 @@ "2025-07-11-eslint-v9.31.0-released.md": "2025-07-11T20:55:46.114Z", "2025-07-25-eslint-v9.32.0-released.md": "2025-07-25T15:03:42.032Z", "2025-08-08-eslint-v9.33.0-released.md": "2025-08-08T20:50:12.305Z", - "2025-08-22-multithread-linting.md": "2025-08-22T13:11:10.796Z" + "2025-08-22-multithread-linting.md": "2025-08-22T19:45:00.455Z" } \ No newline at end of file diff --git a/src/content/blog/2025-08-22-multithread-linting.md b/src/content/blog/2025-08-22-multithread-linting.md index 435cefad3..99f97f73b 100644 --- a/src/content/blog/2025-08-22-multithread-linting.md +++ b/src/content/blog/2025-08-22-multithread-linting.md @@ -106,8 +106,9 @@ In multithread mode, each worker imports the same module rather than receiving a You can even inline your options module using a data URL: ```js +const configURL = new URL("./my-eslint.config.js", import.meta.url).href; const optionsModuleText = ` -import config from "./my-eslint-config.js"; +import config from ${JSON.stringify(configURL)}; export default { concurrency: "auto",