Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: eslint/eslint
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v9.13.0
Choose a base ref
...
head repository: eslint/eslint
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: db0b844a66ee25483f9619d04346de1a2a0d79fa
Choose a head ref
  • 20 commits
  • 38 files changed
  • 9 contributors

Commits on Oct 20, 2024

  1. docs: Clarify global ignores in config migration guide (#19032)

    * docs: Clarify global ignores in config migration guide
    
    * better wording
    
    Co-authored-by: Amaresh  S M  <amareshsm13@gmail.com>
    
    ---------
    
    Co-authored-by: Amaresh  S M <amareshsm13@gmail.com>
    mdjermanovic and amareshsm authored Oct 20, 2024
    Copy the full SHA
    50f03a1 View commit details

Commits on Oct 21, 2024

  1. test: ensure tmp directory cleanup in check-emfile-handling.js (#19036

    )
    
    * test: ensure tmp directory cleanup in `check-emfile-handling.js`
    
    * increase `maxRetries`
    
    * increase `maxRetries` further
    
    * add delay
    
    * use `Promise.allSettled` to ensure all `readFile`s are settled
    LiviaMedeiros authored Oct 21, 2024
    Copy the full SHA
    7259627 View commit details

Commits on Oct 23, 2024

  1. fix: avoid call stack overflow while processing globs (#19035)

    * fix: avoid call stack overflow by processing file paths in chunks
    
    * use `flatMap()`
    
    * add comments
    LiviaMedeiros authored Oct 23, 2024
    Copy the full SHA
    d474443 View commit details

Commits on Oct 24, 2024

  1. docs: change link from @types/eslint to lib/types (#19049)

    * Change link from @types/eslint to lib/types
    
    * Update docs/src/use/configure/migration-guide.md
    
    Co-authored-by: Francesco Trotta <github@fasttime.org>
    
    ---------
    
    Co-authored-by: Francesco Trotta <github@fasttime.org>
    karlhorky and fasttime authored Oct 24, 2024
    Copy the full SHA
    ee0a77e View commit details

Commits on Oct 25, 2024

  1. docs: Update README

    GitHub Actions Bot committed Oct 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f16e846 View commit details
  2. ci: run tests in Node.js 23 (#19055)

    fasttime authored Oct 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3d44b3c View commit details

Commits on Oct 28, 2024

  1. perf: Fix caching in config loaders (#19042)

    * perf: Fix caching in `LegacyConfigLoader`
    
    * add comment
    
    * refactor
    
    * refactor
    
    * fix caching in `ConfigLoader`
    
    * add test
    
    * add test for `getCachedConfigArrayForPath`
    
    * add unit tests, remove eslint tests
    
    * update error messages for promises in sync methods
    mdjermanovic authored Oct 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    425202e View commit details
  2. build: exclude flawed dendency versions (#19065)

    * build: exclude flawed dendency versions
    
    * pin `trunk-io/trunk-action` in more places
    fasttime authored Oct 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    35a8858 View commit details

Commits on Oct 29, 2024

  1. fix: Don't crash when directory is deleted during traversal. (#19067)

    fixes #18955
    nzakas authored Oct 29, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b442067 View commit details
  2. build: update @wdio/* dependencies (#19068)

    fasttime authored Oct 29, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    37c9177 View commit details
  3. test: add no-invalid-regexp tests with RegExp Modifiers (#19075)

    mdjermanovic authored Oct 29, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f48a2a0 View commit details

Commits on Oct 31, 2024

  1. docs: Update README

    GitHub Actions Bot committed Oct 31, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dc34f94 View commit details
  2. docs: update context.languageOptions.parser description (#19084)

    snitin315 authored Oct 31, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    151c965 View commit details

Commits on Nov 1, 2024

  1. feat: add types for the @eslint/js package (#19010)

    * feat: add types for `@eslint/js` package
    
    * fix: add eslint as a dependency for types
    
    * chore: add test for types
    
    * chore: add test for types
    
    * ci: fix path
    
    * chore: remove eslint dependency
    
    * chore: update knip & trunk configs
    
    * chore: update eslint.config.js
    
    Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
    
    ---------
    
    Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
    snitin315 and mdjermanovic authored Nov 1, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b0faee3 View commit details
  2. feat: add support for Import Attributes and RegExp Modifiers (#19076)

    * add tests for `astUtils.needsPrecedingSemicolon`
    
    * add tests for `prefer-regex-literals`
    
    * add tests for `prefer-named-capture-group`
    
    * update `id-denylist`
    
    * update dependencies
    
    * update `id-length`
    
    * update `id-match`
    
    * update `camelcase`
    
    * add tests for `no-underscore-dangle`
    
    * refactor `isImportAttributeKey`
    mdjermanovic authored Nov 1, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3fa009f View commit details
  3. fix: enable retry concurrency limit for readFile() (#19077)

    * fix: enable retry concurrency limit for readFile()
    
    * Fix package.json
    
    * Update @humanwhocodes/retry
    
    * Move signal to correct location
    nzakas authored Nov 1, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    24d0172 View commit details
  4. chore: package.json update for @eslint/js release

    Jenkins committed Nov 1, 2024
    Copy the full SHA
    28be447 View commit details
  5. chore: upgrade @eslint/js@9.14.0 (#19086)

    mdjermanovic authored Nov 1, 2024
    Copy the full SHA
    f36cb16 View commit details
  6. Build: changelog update for 9.14.0

    Jenkins committed Nov 1, 2024
    Copy the full SHA
    20b0da1 View commit details
  7. 9.14.0

    Jenkins committed Nov 1, 2024
    Copy the full SHA
    db0b844 View commit details
Showing with 981 additions and 234 deletions.
  1. +1 −1 .github/workflows/annotate_pr.yaml
  2. +9 −6 .github/workflows/ci.yml
  3. +21 −0 CHANGELOG.md
  4. +2 −2 README.md
  5. +1 −1 docs/package.json
  6. +1 −1 docs/src/_data/versions.json
  7. +1 −1 docs/src/extend/custom-rules.md
  8. +4 −3 docs/src/use/configure/migration-guide.md
  9. +1 −1 docs/src/use/formatters/html-formatter-example.html
  10. +2 −2 eslint.config.js
  11. +3 −1 knip.jsonc
  12. +193 −177 lib/config/config-loader.js
  13. +9 −9 lib/eslint/eslint-helpers.js
  14. +2 −2 lib/eslint/eslint.js
  15. +9 −2 lib/rules/camelcase.js
  16. +12 −0 lib/rules/id-denylist.js
  17. +2 −2 lib/rules/id-length.js
  18. +7 −1 lib/rules/id-match.js
  19. +45 −2 lib/rules/utils/ast-utils.js
  20. +12 −12 package.json
  21. +7 −3 packages/js/package.json
  22. +21 −0 packages/js/tests/types/tsconfig.json
  23. +72 −0 packages/js/tests/types/types.test.ts
  24. +10 −0 packages/js/types/index.d.ts
  25. +1 −0 tests/fixtures/simple-valid-project-2/bar.js
  26. +5 −0 tests/fixtures/simple-valid-project-2/eslint.config.js
  27. +1 −0 tests/fixtures/simple-valid-project-2/foo.js
  28. +125 −0 tests/lib/config/config-loader.js
  29. +95 −0 tests/lib/rules/camelcase.js
  30. +58 −0 tests/lib/rules/id-denylist.js
  31. +44 −0 tests/lib/rules/id-length.js
  32. +70 −0 tests/lib/rules/id-match.js
  33. +21 −0 tests/lib/rules/no-array-constructor.js
  34. +52 −0 tests/lib/rules/no-invalid-regexp.js
  35. +9 −1 tests/lib/rules/no-underscore-dangle.js
  36. +13 −1 tests/lib/rules/prefer-named-capture-group.js
  37. +28 −0 tests/lib/rules/prefer-regex-literals.js
  38. +12 −3 tools/check-emfile-handling.js
2 changes: 1 addition & 1 deletion .github/workflows/annotate_pr.yaml
Original file line number Diff line number Diff line change
@@ -20,6 +20,6 @@ jobs:
uses: actions/checkout@v4

- name: Trunk Check
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@v1.1.17
with:
post-annotations: true # only for fork PRs
15 changes: 9 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -25,15 +25,15 @@ jobs:
run: npm install

- name: Lint Files (eslint)
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@v1.1.17
with:

# Run on everything except the docs folder.
arguments: --ignore=docs/** --filter=eslint
check-mode: all

- name: Lint Files (other)
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@v1.1.17
with:

# Run on everything except the docs folder.
@@ -46,15 +46,15 @@ jobs:
run: node Makefile checkLicenses

- name: Lint Docs Files (eslint)
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@v1.1.17
with:

# Run only on the docs folder.
arguments: --ignore=** --ignore=!docs/** --filter=eslint
check-mode: all

- name: Lint Docs Files (other)
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@v1.1.17
with:

# Run only on the docs folder.
@@ -71,7 +71,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node: [22.x, 21.x, 20.x, 18.x, "18.18.0"]
node: [23.x, 22.x, 21.x, 20.x, 18.x, "18.18.0"]
include:
- os: windows-latest
node: "lts/*"
@@ -123,5 +123,8 @@ jobs:
node-version: "lts/*"
- name: Install Packages
run: npm install
- name: Test
- name: Test eslint types
run: npm run test:types
- name: Test @eslint/js types
run: npm run test:types
working-directory: packages/js
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
v9.14.0 - November 1, 2024

* [`f36cb16`](https://github.com/eslint/eslint/commit/f36cb1649a85028fb3999ee2056ee467a907c061) chore: upgrade @eslint/js@9.14.0 (#19086) (Milos Djermanovic)
* [`28be447`](https://github.com/eslint/eslint/commit/28be4471f6eb61b4304ae3d17ea7eeacc6364bbe) chore: package.json update for @eslint/js release (Jenkins)
* [`24d0172`](https://github.com/eslint/eslint/commit/24d0172bbfb92cac663cb1631bd04e7539262066) fix: enable retry concurrency limit for readFile() (#19077) (Nicholas C. Zakas)
* [`3fa009f`](https://github.com/eslint/eslint/commit/3fa009f25992d3d305437205be0ca145a0fb53f4) feat: add support for Import Attributes and RegExp Modifiers (#19076) (Milos Djermanovic)
* [`b0faee3`](https://github.com/eslint/eslint/commit/b0faee30e007a89bd7bdbc22a70223fabb99a541) feat: add types for the `@eslint/js` package (#19010) (Nitin Kumar)
* [`151c965`](https://github.com/eslint/eslint/commit/151c965aec1c46000ac7dfc67a1c04802112aafc) docs: update `context.languageOptions.parser` description (#19084) (Nitin Kumar)
* [`dc34f94`](https://github.com/eslint/eslint/commit/dc34f94a2ed25b37ac4aafcabed7bfae582db77e) docs: Update README (GitHub Actions Bot)
* [`f48a2a0`](https://github.com/eslint/eslint/commit/f48a2a0e9bf4a659b9af5e70e873fb631430c1ba) test: add `no-invalid-regexp` tests with RegExp Modifiers (#19075) (Milos Djermanovic)
* [`37c9177`](https://github.com/eslint/eslint/commit/37c9177aa07296a7a794c4b4ef5333e16fa22415) build: update `@wdio/*` dependencies (#19068) (Francesco Trotta)
* [`b442067`](https://github.com/eslint/eslint/commit/b44206725247d30b10cd58859c388949f5489087) fix: Don't crash when directory is deleted during traversal. (#19067) (Nicholas C. Zakas)
* [`35a8858`](https://github.com/eslint/eslint/commit/35a8858d62cb050fa0b56702e55c94ffaaf6956d) build: exclude flawed dendency versions (#19065) (Francesco Trotta)
* [`425202e`](https://github.com/eslint/eslint/commit/425202ed49a1372c1719d4e7b48d0fbdda8af9fa) perf: Fix caching in config loaders (#19042) (Milos Djermanovic)
* [`3d44b3c`](https://github.com/eslint/eslint/commit/3d44b3c4751e4c44c32b879b65a723faee9c1c29) ci: run tests in Node.js 23 (#19055) (Francesco Trotta)
* [`f16e846`](https://github.com/eslint/eslint/commit/f16e846ac004bc32e52cd3991d14d7a89374bbb5) docs: Update README (GitHub Actions Bot)
* [`ee0a77e`](https://github.com/eslint/eslint/commit/ee0a77ea3caa5838bab704b54a577eefbed58f68) docs: change link from @types/eslint to lib/types (#19049) (Karl Horky)
* [`d474443`](https://github.com/eslint/eslint/commit/d474443109762f3b92811df0411965cf64f595c2) fix: avoid call stack overflow while processing globs (#19035) (Livia Medeiros)
* [`7259627`](https://github.com/eslint/eslint/commit/725962731538eaa38d5d78b9e82ce3fccc9762d0) test: ensure tmp directory cleanup in `check-emfile-handling.js` (#19036) (Livia Medeiros)
* [`50f03a1`](https://github.com/eslint/eslint/commit/50f03a119e6827c03b1d6c86d3aa1f4820b609e8) docs: Clarify global ignores in config migration guide (#19032) (Milos Djermanovic)

v9.13.0 - October 18, 2024

* [`68d2d9d`](https://github.com/eslint/eslint/commit/68d2d9dfd63401b6a9b413f11ac2c4b583e4897a) chore: upgrade to `@eslint/js@9.13.0` and `@eslint/core@^0.7.0` (#19034) (Francesco Trotta)
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -299,8 +299,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
<h3>Platinum Sponsors</h3>
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
<p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340?v=4" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
<p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340?v=4" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>
<h3>Technology Sponsors</h3>
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "docs-eslint",
"private": true,
"version": "9.13.0",
"version": "9.14.0",
"description": "",
"main": "index.js",
"keywords": [],
2 changes: 1 addition & 1 deletion docs/src/_data/versions.json
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
"path": "/docs/head/"
},
{
"version": "9.13.0",
"version": "9.14.0",
"branch": "latest",
"path": "/docs/latest/"
},
2 changes: 1 addition & 1 deletion docs/src/extend/custom-rules.md
Original file line number Diff line number Diff line change
@@ -135,7 +135,7 @@ The `context` object has the following properties:
* `languageOptions`: (`object`) more details for each property [here](../use/configure/language-options)
* `sourceType`: (`'script' | 'module' | 'commonjs'`) The mode for the current file.
* `ecmaVersion`: (`number`) The ECMA version used to parse the current file.
* `parser`: (`object`|`string`): Either the parser used to parse the current file for flat config or its name for legacy config.
* `parser`: (`object`): The parser used to parse the current file.
* `parserOptions`: (`object`) The parser options configured for this file.
* `globals`: (`object`) The specified globals.
* `parserPath`: (`string`, **Removed** Use `context.languageOptions.parser` instead.) The name of the `parser` from the configuration.
7 changes: 4 additions & 3 deletions docs/src/use/configure/migration-guide.md
Original file line number Diff line number Diff line change
@@ -537,7 +537,7 @@ For more information about the `FlatCompat` class, please see the [package READM
With eslintrc, you can make ESLint ignore files by creating a separate `.eslintignore` file in the root of your project. The `.eslintignore` file uses the same glob pattern syntax as `.gitignore` files. Alternatively, you can use an `ignorePatterns` property in your eslintrc file.
To ignore files with flat config, you can use the `ignores` property in a config object. The `ignores` property accepts an array of glob patterns. Flat config does not support loading ignore patterns from `.eslintignore` files, so you'll need to migrate those patterns directly into flat config.
To ignore files with flat config, you can use the `ignores` property in a config object with no other properties. The `ignores` property accepts an array of glob patterns. Flat config does not support loading ignore patterns from `.eslintignore` files, so you'll need to migrate those patterns directly into flat config.
For example, here's a `.eslintignore` example you can use with an eslintrc config:
@@ -564,6 +564,7 @@ The equivalent ignore patterns in flat config look like this:
export default [
// ...other config
{
// Note: there should be no other properties in this object
ignores: ["**/temp.js", "config/*"]
}
];
@@ -686,9 +687,9 @@ The following changes have been made from the eslintrc to the flat config file f
## TypeScript Types for Flat Config Files
You can see the TypeScript types for the flat config file format in the DefinitelyTyped project. The interface for the objects in the config’s array is called the `FlatConfig`.
You can see the TypeScript types for the flat config file format in the [`lib/types` source folder on GitHub](https://github.com/eslint/eslint/tree/main/lib/types). The interface for the objects in the config’s array is called `Linter.Config`.
You can view the type definitions in the [DefinitelyTyped repository on GitHub](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/eslint/index.d.ts).
You can view the type definitions in [`lib/types/index.d.ts`](https://github.com/eslint/eslint/blob/main/lib/types/index.d.ts).
## Visual Studio Code Support
2 changes: 1 addition & 1 deletion docs/src/use/formatters/html-formatter-example.html
Original file line number Diff line number Diff line change
@@ -118,7 +118,7 @@
<div id="overview" class="bg-2">
<h1>ESLint Report</h1>
<div>
<span>8 problems (4 errors, 4 warnings)</span> - Generated on Fri Oct 18 2024 20:55:25 GMT+0000 (Coordinated Universal Time)
<span>8 problems (4 errors, 4 warnings)</span> - Generated on Fri Nov 01 2024 18:52:21 GMT+0000 (Coordinated Universal Time)
</div>
</div>
<table>
4 changes: 2 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -292,11 +292,11 @@ module.exports = [
})),
{
name: "eslint/ts-rules",
files: ["tests/lib/types/*.ts"],
files: ["tests/lib/types/*.ts", "packages/**/*.{ts,mts,cts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: {
project: "tests/lib/types/tsconfig.json"
project: ["tests/lib/types/tsconfig.json", "packages/js/tests/types/tsconfig.json"]
}
},
plugins: {
4 changes: 3 additions & 1 deletion knip.jsonc
Original file line number Diff line number Diff line change
@@ -34,7 +34,9 @@
"ignore": ["src/assets/js/search.js", "_examples/**"]
},
// Workspaces with default configs:
"packages/*": {},
"packages/*": {
"ignore": ["tests/types/**"]
},
"tools/internal-rules": {}
}
}
370 changes: 193 additions & 177 deletions lib/config/config-loader.js

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions lib/eslint/eslint-helpers.js
Original file line number Diff line number Diff line change
@@ -421,20 +421,19 @@ async function globMultiSearch({ searches, configLoader, errorOnUnmatchedPattern
)
);

const filePaths = [];

/*
* The first loop handles errors from the glob searches. Since we can't
* use `await` inside `flatMap`, we process errors separately in this loop.
* This results in two iterations over `results`, but since the length is
* less than or equal to the number of globs and directories passed on the
* command line, the performance impact should be minimal.
*/
for (let i = 0; i < results.length; i++) {

const result = results[i];
const currentSearch = normalizedSearches[i];

if (result.status === "fulfilled") {

// if the search was successful just add the results
if (result.value.length > 0) {
filePaths.push(...result.value);
}

continue;
}

@@ -457,7 +456,8 @@ async function globMultiSearch({ searches, configLoader, errorOnUnmatchedPattern

}

return filePaths;
// second loop for `fulfulled` results
return results.flatMap(result => result.value);

}

4 changes: 2 additions & 2 deletions lib/eslint/eslint.js
Original file line number Diff line number Diff line change
@@ -746,7 +746,7 @@ class ESLint {
});
const controller = new AbortController();
const retryCodes = new Set(["ENFILE", "EMFILE"]);
const retrier = new Retrier(error => retryCodes.has(error.code));
const retrier = new Retrier(error => retryCodes.has(error.code), { concurrency: 100 });

debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);

@@ -830,7 +830,7 @@ class ESLint {
}

return result;
}))
}), { signal: controller.signal })
.catch(error => {
controller.abort(error);
throw error;
11 changes: 9 additions & 2 deletions lib/rules/camelcase.js
Original file line number Diff line number Diff line change
@@ -238,6 +238,13 @@ module.exports = {
return;
}

/*
* Import attribute keys are always ignored
*/
if (astUtils.isImportAttributeKey(node)) {
return;
}

report(node);
}

@@ -272,7 +279,7 @@ module.exports = {
for (const reference of scope.through) {
const id = reference.identifier;

if (isGoodName(id.name)) {
if (isGoodName(id.name) || astUtils.isImportAttributeKey(id)) {
continue;
}

@@ -326,7 +333,7 @@ module.exports = {
"MethodDefinition > PrivateIdentifier.key",
"PropertyDefinition > PrivateIdentifier.key"
]](node) {
if (properties === "never" || isGoodName(node.name)) {
if (properties === "never" || astUtils.isImportAttributeKey(node) || isGoodName(node.name)) {
return;
}
report(node);
12 changes: 12 additions & 0 deletions lib/rules/id-denylist.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,12 @@

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
@@ -154,6 +160,12 @@ module.exports = {
* @returns {boolean} `true` if the node should be checked.
*/
function shouldCheck(node) {

// Import attributes are defined by environments, so naming conventions shouldn't apply to them
if (astUtils.isImportAttributeKey(node)) {
return false;
}

const parent = node.parent;

/*
4 changes: 2 additions & 2 deletions lib/rules/id-length.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
//------------------------------------------------------------------------------

const { getGraphemeCount } = require("../shared/string-utils");
const { getModuleExportName } = require("./utils/ast-utils");
const { getModuleExportName, isImportAttributeKey } = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
@@ -115,7 +115,7 @@ module.exports = {
isKeyAndValueSame && parent.key === node && properties
);
}
return properties && !parent.computed && parent.key.name === node.name;
return properties && !isImportAttributeKey(node) && !parent.computed && parent.key.name === node.name;
},
ImportSpecifier(parent, node) {
return (
8 changes: 7 additions & 1 deletion lib/rules/id-match.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,12 @@

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@@ -180,7 +186,7 @@ module.exports = {
parent = node.parent,
effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;

if (isReferenceToGlobalVariable(node)) {
if (isReferenceToGlobalVariable(node) || astUtils.isImportAttributeKey(node)) {
return;
}

47 changes: 45 additions & 2 deletions lib/rules/utils/ast-utils.js
Original file line number Diff line number Diff line change
@@ -1054,7 +1054,7 @@ let needsPrecedingSemicolon;
{
const BREAK_OR_CONTINUE = new Set(["BreakStatement", "ContinueStatement"]);

// Declaration types that must contain a string Literal node at the end.
// Declaration types that cannot be continued by a punctuator when ending with a string Literal that is a direct child.
const DECLARATIONS = new Set(["ExportAllDeclaration", "ExportNamedDeclaration", "ImportDeclaration"]);

const IDENTIFIER_OR_KEYWORD = new Set(["Identifier", "Keyword"]);
@@ -1132,6 +1132,48 @@ let needsPrecedingSemicolon;
};
}

/**
* Checks if a node is used as an import attribute key, either in a static or dynamic import.
* @param {ASTNode} node The node to check.
* @returns {boolean} Whether the node is used as an import attribute key.
*/
function isImportAttributeKey(node) {
const { parent } = node;

// static import/re-export
if (parent.type === "ImportAttribute" && parent.key === node) {
return true;
}

// dynamic import
if (
parent.type === "Property" &&
!parent.computed &&
(parent.key === node || parent.value === node && parent.shorthand && !parent.method) &&
parent.parent.type === "ObjectExpression"
) {
const objectExpression = parent.parent;
const objectExpressionParent = objectExpression.parent;

if (
objectExpressionParent.type === "ImportExpression" &&
objectExpressionParent.options === objectExpression
) {
return true;
}

// nested key
if (
objectExpressionParent.type === "Property" &&
objectExpressionParent.value === objectExpression
) {
return isImportAttributeKey(objectExpressionParent.key);
}
}

return false;
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
@@ -2288,5 +2330,6 @@ module.exports = {
isTopLevelExpressionStatement,
isDirective,
isStartOfExpressionStatement,
needsPrecedingSemicolon
needsPrecedingSemicolon,
isImportAttributeKey
};
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "9.13.0",
"version": "9.14.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
"description": "An AST-based pattern checker for JavaScript.",
"type": "commonjs",
@@ -98,25 +98,25 @@
"bugs": "https://github.com/eslint/eslint/issues/",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.18.0",
"@eslint/core": "^0.7.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "9.13.0",
"@eslint/js": "9.14.0",
"@eslint/plugin-kit": "^0.2.0",
"@humanfs/node": "^0.16.5",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.1",
"@humanwhocodes/retry": "^0.4.0",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.1.0",
"eslint-visitor-keys": "^4.1.0",
"espree": "^10.2.0",
"eslint-scope": "^8.2.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -141,10 +141,10 @@
"@trunkio/launcher": "^1.3.0",
"@types/node": "^20.11.5",
"@typescript-eslint/parser": "^8.4.0",
"@wdio/browser-runner": "^9.0.5",
"@wdio/cli": "^9.0.5",
"@wdio/concise-reporter": "^9.0.4",
"@wdio/mocha-framework": "^9.0.5",
"@wdio/browser-runner": "^9.2.4",
"@wdio/cli": "^9.2.4",
"@wdio/concise-reporter": "^9.2.2",
"@wdio/mocha-framework": "^9.2.2",
"babel-loader": "^8.0.5",
"c8": "^7.12.0",
"chai": "^4.0.1",
10 changes: 7 additions & 3 deletions packages/js/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
{
"name": "@eslint/js",
"version": "9.13.0",
"version": "9.14.0",
"description": "ESLint JavaScript language implementation",
"main": "./src/index.js",
"scripts": {},
"types": "./types/index.d.ts",
"scripts": {
"test:types": "tsc -p tests/types/tsconfig.json"
},
"files": [
"LICENSE",
"README.md",
"src"
"src",
"types"
],
"publishConfig": {
"access": "public"
21 changes: 21 additions & 0 deletions packages/js/tests/types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "node16",
"lib": [
"dom",
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"exactOptionalPropertyTypes": true
},
"files": [
"../../types/index.d.ts",
"types.test.ts"
]
}
72 changes: 72 additions & 0 deletions packages/js/tests/types/types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @fileoverview This file contains code intended to test our types.
* It was initially extracted from the `@types/eslint__js` package.
*/

/*
* MIT License
* Copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE
*/

import type { Linter } from "eslint";
import js from "../../";

let config: Linter.Config[];

config = [js.configs.recommended];

config = [js.configs.all];

config = [js.configs.recommended, js.configs.all];

config = [
{
...js.configs.recommended,
files: ["blah"],
},
{
...js.configs.all,
files: ["meh"],
},
{
files: ["foo"],
},
];

config = [
{
files: ["**/*.js"],
rules: js.configs.recommended.rules,
},
{
files: ["**/*.js"],
rules: {
...js.configs.recommended.rules,
"no-unused-vars": "warn",
},
},
{
files: ["**/*.js"],
rules: {
...js.configs.all.rules,
"no-unused-vars": "warn",
},
},
];
10 changes: 10 additions & 0 deletions packages/js/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Linter } from "eslint";

declare const js: {
readonly configs: {
readonly recommended: { readonly rules: Readonly<Linter.RulesRecord> };
readonly all: { readonly rules: Readonly<Linter.RulesRecord> };
};
};

export = js;
1 change: 1 addition & 0 deletions tests/fixtures/simple-valid-project-2/bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
var a = 'b';
5 changes: 5 additions & 0 deletions tests/fixtures/simple-valid-project-2/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = [{
rules: {
quotes: ['error', 'single']
}
}];
1 change: 1 addition & 0 deletions tests/fixtures/simple-valid-project-2/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
var a = 'b';
125 changes: 125 additions & 0 deletions tests/lib/config/config-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @fileoverview Tests for config loader classes.
* @author Milos Djermanovic
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const assert = require("node:assert");
const path = require("node:path");
const sinon = require("sinon");
const { ConfigLoader, LegacyConfigLoader } = require("../../../lib/config/config-loader");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const fixtureDir = path.resolve(__dirname, "../../fixtures");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

describe("Config loaders", () => {
afterEach(() => {
sinon.restore();
});

[ConfigLoader, LegacyConfigLoader].forEach(ConfigLoaderClass => {
describe(`${ConfigLoaderClass.name} class`, () => {

describe("findConfigFileForPath()", () => {

it("should lookup config file only once for multiple files in same directory", async () => {
const cwd = path.resolve(fixtureDir, "simple-valid-project-2");

const locateConfigFileToUse = sinon.spy(ConfigLoader, "locateConfigFileToUse");

const configLoader = new ConfigLoaderClass({
cwd,
ignoreEnabled: true
});

const [path1, path2] = await Promise.all([
configLoader.findConfigFileForPath(path.resolve(cwd, "foo.js")),
configLoader.findConfigFileForPath(path.resolve(cwd, "bar.js"))
]);

const configFile = path.resolve(cwd, "eslint.config.js");

assert.strictEqual(path1, configFile);
assert.strictEqual(path2, configFile);

assert.strictEqual(locateConfigFileToUse.callCount, 1, "Expected `ConfigLoader.locateConfigFileToUse` to be called exactly once");
});
});

describe("loadConfigArrayForFile()", () => {

// https://github.com/eslint/eslint/issues/19025
it("should lookup config file only once and create config array only once for multiple files in same directory", async () => {
const cwd = path.resolve(fixtureDir, "simple-valid-project-2");

const locateConfigFileToUse = sinon.spy(ConfigLoader, "locateConfigFileToUse");
const calculateConfigArray = sinon.spy(ConfigLoader, "calculateConfigArray");

const configLoader = new ConfigLoaderClass({
cwd,
ignoreEnabled: true
});

const [configArray1, configArray2] = await Promise.all([
configLoader.loadConfigArrayForFile(path.resolve(cwd, "foo.js")),
configLoader.loadConfigArrayForFile(path.resolve(cwd, "bar.js"))
]);

assert(Array.isArray(configArray1), "Expected `loadConfigArrayForFile()` to return a config array");
assert(configArray1 === configArray2, "Expected config array instances for `foo.js` and `bar.js` to be the same");

assert.strictEqual(locateConfigFileToUse.callCount, 1, "Expected `ConfigLoader.locateConfigFileToUse` to be called exactly once");
assert.strictEqual(calculateConfigArray.callCount, 1, "Expected `ConfigLoader.calculateConfigArray` to be called exactly once");
});
});

describe("getCachedConfigArrayForFile()", () => {

it("should throw an error if calculating the config array is not yet complete", async () => {
let error;

const cwd = path.resolve(fixtureDir, "simple-valid-project-2");
const filePath = path.resolve(cwd, "foo.js");

const configLoader = new ConfigLoaderClass({
cwd,
ignoreEnabled: true
});

const originalCalculateConfigArray = ConfigLoader.calculateConfigArray;

sinon.stub(ConfigLoader, "calculateConfigArray").callsFake((...args) => {
process.nextTick(() => {
try {
configLoader.getCachedConfigArrayForFile(filePath);
} catch (e) {
error = e;
}
});

return originalCalculateConfigArray(...args);

});

await configLoader.loadConfigArrayForFile(filePath);

assert(error, "An error was expected");
assert.match(error.message, /has not yet been calculated/u);
});
});

});
});
});
95 changes: 95 additions & 0 deletions tests/lib/rules/camelcase.js
Original file line number Diff line number Diff line change
@@ -445,6 +445,69 @@ ruleTester.run("camelcase", rule, {
`,
options: [{ properties: "never", ignoreDestructuring: true }],
languageOptions: { ecmaVersion: 2022 }
},

// Import attribute keys
{
code: "import foo from 'foo.json' with { my_type: 'json' }",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "export * from 'foo.json' with { my_type: 'json' }",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "export { default } from 'foo.json' with { my_type: 'json' }",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "import('foo.json', { my_with: { my_type: 'json' } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { 'with': { my_type: 'json' } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { my_with: { my_type } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { my_with: { my_type } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: {
ecmaVersion: 2025,
globals: {
my_type: true // eslint-disable-line camelcase -- for testing
}
}
}
],
invalid: [
@@ -1519,6 +1582,38 @@ ruleTester.run("camelcase", rule, {
column: 27
}
]
},

// Not an import attribute key
{
code: "import('foo.json', { my_with: { [my_type]: 'json' } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
messageId: "notCamelCase",
data: { name: "my_type" },
type: "Identifier"
}
]
},
{
code: "import('foo.json', { my_with: { my_type: my_json } })",
options: [{
properties: "always",
ignoreImports: false
}],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
messageId: "notCamelCase",
data: { name: "my_json" },
type: "Identifier"
}
]
}
]
});
58 changes: 58 additions & 0 deletions tests/lib/rules/id-denylist.js
Original file line number Diff line number Diff line change
@@ -231,6 +231,38 @@ ruleTester.run("id-denylist", rule, {
code: "class C { snake_case; #snake_case; #snake_case2() {} }",
options: ["foo"],
languageOptions: { ecmaVersion: 2022 }
},

// Import attribute keys
{
code: "import foo from 'foo.json' with { type: 'json' }",
options: ["type"],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "export * from 'foo.json' with { type: 'json' }",
options: ["type"],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "export { default } from 'foo.json' with { type: 'json' }",
options: ["type"],
languageOptions: { ecmaVersion: 2025, sourceType: "module" }
},
{
code: "import('foo.json', { with: { type: 'json' } })",
options: ["with", "type"],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { 'with': { type: 'json' } })",
options: ["type"],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { with: { type } })",
options: ["type"],
languageOptions: { ecmaVersion: 2025 }
}
],
invalid: [
@@ -1429,6 +1461,32 @@ ruleTester.run("id-denylist", rule, {
}
]

},

// Not an import attribute key
{
code: "import('foo.json', { with: { [type]: 'json' } })",
options: ["type"],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
messageId: "restricted",
data: { name: "type" },
type: "Identifier"
}
]
},
{
code: "import('foo.json', { with: { type: json } })",
options: ["json"],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
messageId: "restricted",
data: { name: "json" },
type: "Identifier"
}
]
}
]
});
44 changes: 44 additions & 0 deletions tests/lib/rules/id-length.js
Original file line number Diff line number Diff line change
@@ -237,6 +237,50 @@ ruleTester.run("id-length", rule, {
languageOptions: {
ecmaVersion: 6
}
},

// Import attribute keys
{
code: "import foo from 'foo.json' with { type: 'json' }",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
},
{
code: "export * from 'foo.json' with { type: 'json' }",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
},
{
code: "export { default } from 'foo.json' with { type: 'json' }",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
},
{
code: "import('foo.json', { with: { type: 'json' } })",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
},
{
code: "import('foo.json', { 'with': { type: 'json' } })",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
},
{
code: "import('foo.json', { with: { type } })",
options: [{ min: 1, max: 3, properties: "always" }],
languageOptions: {
ecmaVersion: 2025
}
}
],
invalid: [
70 changes: 70 additions & 0 deletions tests/lib/rules/id-match.js
Original file line number Diff line number Diff line change
@@ -276,6 +276,50 @@ ruleTester.run("id-match", rule, {
classFields: false
}],
languageOptions: { ecmaVersion: 2022 }
},

// Import attribute keys
{
code: "import foo from 'foo.json' with { type: 'json' }",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "export * from 'foo.json' with { type: 'json' }",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "export { default } from 'foo.json' with { type: 'json' }",
options: ["^def", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { with: { type: 'json' } })",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { 'with': { type: 'json' } })",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
},
{
code: "import('foo.json', { with: { type } })",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 }
}

],
@@ -914,6 +958,32 @@ ruleTester.run("id-match", rule, {
type: "Identifier"
}
]
},

// Not an import attribute key
{
code: "import('foo.json', { with: { [type]: 'json' } })",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
message: "Identifier 'type' does not match the pattern '^foo'."
}
]
},
{
code: "import('foo.json', { with: { type: json } })",
options: ["^foo", {
properties: true
}],
languageOptions: { ecmaVersion: 2025 },
errors: [
{
message: "Identifier 'json' does not match the pattern '^foo'."
}
]
}
]
});
21 changes: 21 additions & 0 deletions tests/lib/rules/no-array-constructor.js
Original file line number Diff line number Diff line change
@@ -374,20 +374,41 @@ ruleTester.run("no-array-constructor", rule, {
`,
languageOptions: { sourceType: "module" }
},
{
code: `
export { foo } from 'bar' with { type: "json" }
Array()
`,
languageOptions: { sourceType: "module" }
},
{
code: `
export * as foo from 'bar'
Array()
`,
languageOptions: { sourceType: "module" }
},
{
code: `
export * as foo from 'bar' with { type: "json" }
Array()
`,
languageOptions: { sourceType: "module" }
},
{
code: `
import foo from 'bar'
Array()
`,
languageOptions: { sourceType: "module" }
},
{
code: `
import foo from 'bar' with { type: "json" }
Array()
`,
languageOptions: { sourceType: "module" }
},
{
code: `
var yield = 5;
52 changes: 52 additions & 0 deletions tests/lib/rules/no-invalid-regexp.js
Original file line number Diff line number Diff line change
@@ -91,6 +91,10 @@ ruleTester.run("no-invalid-regexp", rule, {

// ES2025
"new RegExp('((?<k>a)|(?<k>b))')",
"new RegExp('(?ims:foo)')",
"new RegExp('(?ims-:foo)')",
"new RegExp('(?-ims:foo)')",
"new RegExp('(?s-i:foo)')",

// allowConstructorFlags
{
@@ -441,6 +445,54 @@ ruleTester.run("no-invalid-regexp", rule, {
data: { message: "Invalid regular expression: /(?<k>a)(?<k>b)/: Duplicate capture group name" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?ii:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?ii:foo)/: Duplicated flag 'i'" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?-ii:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?-ii:foo)/: Duplicated flag 'i'" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?i-i:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?i-i:foo)/: Duplicated flag 'i'" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?-:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?-:foo)/: Invalid empty flags" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?g:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?g:foo)/: Invalid group" },
type: "NewExpression"
}]
},
{
code: "new RegExp('(?-u:foo)')",
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /(?-u:foo)/: Invalid group" },
type: "NewExpression"
}]
}
]
});
10 changes: 9 additions & 1 deletion tests/lib/rules/no-underscore-dangle.js
Original file line number Diff line number Diff line change
@@ -85,7 +85,15 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "class foo { _field; }", options: [{ enforceInClassFields: false }], languageOptions: { ecmaVersion: 2022 } },
{ code: "class foo { #_field; }", languageOptions: { ecmaVersion: 2022 } },
{ code: "class foo { #_field; }", options: [{ enforceInClassFields: false }], languageOptions: { ecmaVersion: 2022 } },
{ code: "class foo { _field; }", options: [{}], languageOptions: { ecmaVersion: 2022 } }
{ code: "class foo { _field; }", options: [{}], languageOptions: { ecmaVersion: 2022 } },

// Import attribute keys
{ code: "import foo from 'foo.json' with { _type: 'json' }", languageOptions: { ecmaVersion: 2025 } },
{ code: "export * from 'foo.json' with { _type: 'json' }", languageOptions: { ecmaVersion: 2025 } },
{ code: "export { default } from 'foo.json' with { _type: 'json' }", languageOptions: { ecmaVersion: 2025 } },
{ code: "import('foo.json', { _with: { _type: 'json' } })", languageOptions: { ecmaVersion: 2025 } },
{ code: "import('foo.json', { 'with': { _type: 'json' } })", languageOptions: { ecmaVersion: 2025 } },
{ code: "import('foo.json', { _with: { _type } })", languageOptions: { ecmaVersion: 2025 } }
],
invalid: [
{ code: "var _foo = 1", errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" }, type: "VariableDeclarator" }] },
14 changes: 13 additions & 1 deletion tests/lib/rules/prefer-named-capture-group.js
Original file line number Diff line number Diff line change
@@ -80,7 +80,19 @@ ruleTester.run("prefer-named-capture-group", rule, {
* Without the v flag, `([\q])` is considered a valid regex and the rule reports,
* but if the v flag is understood correctly the rule does not because of a syntax error.
*/
String.raw`new RegExp('([\\q])', 'v')` // SyntaxError
String.raw`new RegExp('([\\q])', 'v')`, // SyntaxError

// ES2025
{
code: "/(?i:foo)bar/",
languageOptions: { ecmaVersion: 2025 }
},
"new RegExp('(?i:foo)bar')",
{
code: "/(?-i:foo)bar/",
languageOptions: { ecmaVersion: 2025 }
},
"new RegExp('(?-i:foo)bar')"
],

invalid: [
28 changes: 28 additions & 0 deletions tests/lib/rules/prefer-regex-literals.js
Original file line number Diff line number Diff line change
@@ -3006,6 +3006,34 @@ ruleTester.run("prefer-regex-literals", rule, {
}
]
},

// ES2025
{
code: "new RegExp('(?i:foo)bar')",
languageOptions: { ecmaVersion: 2025 },
errors: [
{
messageId: "unexpectedRegExp",
suggestions: [
{
messageId: "replaceWithLiteral",
output: "/(?i:foo)bar/"
}
]
}
]
},
{
code: "new RegExp('(?i:foo)bar')",
languageOptions: { ecmaVersion: 2024 },
errors: [
{
messageId: "unexpectedRegExp",
suggestions: null
}
]
},

{
code: "var regex = new RegExp('foo', 'u');",
languageOptions: {
15 changes: 12 additions & 3 deletions tools/check-emfile-handling.js
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ if (os.platform() !== "win32") {
*/
function generateFiles() {

fs.rmSync(OUTPUT_DIRECTORY, { recursive: true, force: true, maxRetries: 8 });
fs.mkdirSync(OUTPUT_DIRECTORY, { recursive: true });

for (let i = 0; i < FILE_COUNT; i++) {
@@ -69,16 +70,21 @@ function generateFiles() {

/**
* Generates an EMFILE error by reading all files in the output directory.
* @returns {Promise<Buffer[]>} A promise that resolves with the contents of all files.
* @returns {undefined}
*/
function generateEmFileError() {
return Promise.all(
async function generateEmFileError() {
const results = await Promise.allSettled(
Array.from({ length: FILE_COUNT }, (_, i) => {
const fileName = `file_${i}.js`;

return readFile(`${OUTPUT_DIRECTORY}/${fileName}`);
})
);
const failedResult = results.find(({ status }) => status === "rejected");

if (failedResult?.reason) {
throw failedResult.reason;
}
}

//------------------------------------------------------------------------------
@@ -106,4 +112,7 @@ generateEmFileError()
console.error("❌ Unexpected error encountered:", error.message);
throw error;
}
})
.finally(() => {
fs.rmSync(OUTPUT_DIRECTORY, { recursive: true, force: true, maxRetries: 8 });
});