diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index de2207d9..00000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,42 +0,0 @@
-"use strict";
-
-module.exports = {
- root: true,
- extends: [
- "eslint"
- ],
- parserOptions: {
- ecmaVersion: 2020
- },
-
- /*
- * it fixes eslint-plugin-jsdoc's reports: "Invalid JSDoc tag name "template" jsdoc/check-tag-names"
- * refs: https://github.com/gajus/eslint-plugin-jsdoc#check-tag-names
- */
- settings: {
- jsdoc: {
- mode: "typescript"
- }
- },
-
- overrides: [
- {
- files: ["tests/**/*"],
- env: { mocha: true },
- rules: {
- "no-restricted-syntax": ["error", {
- selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']",
- message: "`assert.doesNotThrow()` should be replaced with a comment next to the code."
- }],
-
- // Overcome https://github.com/mysticatea/eslint-plugin-node/issues/250
- "node/no-unsupported-features/es-syntax": ["error", {
- ignores: [
- "modules",
- "dynamicImport"
- ]
- }]
- }
- }
- ]
-};
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..1a950cb4
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+.release-please-manifest.json @eslint/eslint-tsc
+.github/workflows/release-please.yml @eslint/eslint-tsc
\ No newline at end of file
diff --git a/.github/workflows/add-to-triage.yml b/.github/workflows/add-to-triage.yml
new file mode 100644
index 00000000..06dd5b63
--- /dev/null
+++ b/.github/workflows/add-to-triage.yml
@@ -0,0 +1,19 @@
+name: add-to-triage
+
+on:
+ issues:
+ types:
+ - opened
+ - reopened
+ - transferred
+
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+
+jobs:
+ add-to-triage:
+ uses: eslint/workflows/.github/workflows/add-to-triage.yml@main
+ secrets:
+ project_bot_token: ${{ secrets.PROJECT_BOT_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4909f16e..06f78358 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,10 +10,10 @@ jobs:
name: Verify Files
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: '16.x'
+ node-version: "lts/*"
- name: Install Packages
run: npm install
- name: Lint Files
@@ -24,19 +24,32 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
- node: [21.x, 20.x, 19.x, 18.x, 17.x, 16.x, 14.x, 12.x, "12.22.0"]
+ node: [25.x, 24.x, 23.x, 22.x, 21.x, 20.x, 18.x, "18.18.0"]
include:
- os: windows-latest
- node: "12.x"
+ node: "lts/*"
- os: macOS-latest
- node: "12.x"
+ node: "lts/*"
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install Packages
run: npm install
- name: Test
run: npm test
+
+ test_types:
+ name: Test Types
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ - name: Install Packages
+ run: npm install
+ - name: Test
+ run: npm run test:types
diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml
index c60c453b..cef408df 100644
--- a/.github/workflows/release-please.yml
+++ b/.github/workflows/release-please.yml
@@ -2,6 +2,7 @@ on:
push:
branches:
- main
+
name: release-please
jobs:
release-please:
@@ -11,37 +12,27 @@ jobs:
pull-requests: write
id-token: write
steps:
- - uses: google-github-actions/release-please-action@v3
+ - uses: googleapis/release-please-action@v4
id: release
- with:
- release-type: node
- package-name: '@eslint/eslintrc'
- pull-request-title-pattern: 'chore: release ${version}'
- changelog-types: >
- [
- { "type": "feat", "section": "Features", "hidden": false },
- { "type": "fix", "section": "Bug Fixes", "hidden": false },
- { "type": "docs", "section": "Documentation", "hidden": false },
- { "type": "build", "section": "Build Related", "hidden": false },
- { "type": "chore", "section": "Chores", "hidden": false },
- { "type": "perf", "section": "Chores", "hidden": false },
- { "type": "ci", "section": "Chores", "hidden": false },
- { "type": "refactor", "section": "Chores", "hidden": false },
- { "type": "test", "section": "Chores", "hidden": false }
- ]
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
if: ${{ steps.release.outputs.release_created }}
- - uses: actions/setup-node@v3
+ - uses: actions/setup-node@v4
with:
node-version: lts/*
registry-url: https://registry.npmjs.org
if: ${{ steps.release.outputs.release_created }}
- - run: npm install
+
+ # npm 11.5.1 or later is required so update to latest to be sure
+ - name: Update npm
+ run: npm install -g npm@latest
if: ${{ steps.release.outputs.release_created }}
- - run: npm publish --provenance
- env:
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Publish to npm
+ run: |
+ npm install
+ npm publish --provenance
if: ${{ steps.release.outputs.release_created }}
+
- run: 'npx @humanwhocodes/tweet "eslint/eslintrc ${{ steps.release.outputs.tag_name }} has been released: ${{ steps.release.outputs.html_url }}"'
if: ${{ steps.release.outputs.release_created }}
env:
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 00000000..fe232e62
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,15 @@
+name: stale
+
+on:
+ schedule:
+ - cron: "31 22 * * *" # Runs every day at 10:31 PM UTC
+
+permissions:
+ issues: write
+ pull-requests: write
+
+jobs:
+ stale:
+ uses: eslint/workflows/.github/workflows/stale.yml@main
+ secrets:
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/update-readme.yml b/.github/workflows/update-readme.yml
new file mode 100644
index 00000000..96d09866
--- /dev/null
+++ b/.github/workflows/update-readme.yml
@@ -0,0 +1,13 @@
+name: update-readme
+
+on:
+ schedule:
+ - cron: "0 8 * * *" # Runs every day at 08:00 AM UTC
+
+ workflow_dispatch:
+
+jobs:
+ update-readme:
+ uses: eslint/workflows/.github/workflows/update-readme.yml@main
+ secrets:
+ workflow_push_bot_token: ${{ secrets.WORKFLOW_PUSH_BOT_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 5423bd14..38bb2a34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,3 +110,10 @@ pnpm-lock.yaml
# used in tests
/tmp/
+
+# IDEs and editors
+/.vscode
+*.code-workspace
+
+# Automatically generated files by GitHub Actions workflow
+/.shared-workflows
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 00000000..f5258250
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ ".": "3.3.3"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f1f1b32..b6593e9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,98 @@
# Changelog
+## [3.3.3](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.2...eslintrc-v3.3.3) (2025-11-28)
+
+
+### Bug Fixes
+
+* release v3.3.3 because publishing v3.3.2 failed ([#211](https://github.com/eslint/eslintrc/issues/211)) ([8aa555a](https://github.com/eslint/eslintrc/commit/8aa555a3f5fcfb7d99249fb57e819a7b6f635496))
+
+## [3.3.2](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.1...eslintrc-v3.3.2) (2025-11-25)
+
+
+### Bug Fixes
+
+* Remove name property from all and recommended configs ([#200](https://github.com/eslint/eslintrc/issues/200)) ([344da49](https://github.com/eslint/eslintrc/commit/344da491898a2a3595943d4528ba78fe2f238217))
+
+## [3.3.1](https://github.com/eslint/eslintrc/compare/v3.3.0...v3.3.1) (2025-03-11)
+
+
+### Bug Fixes
+
+* correct `types` field in package.json ([#184](https://github.com/eslint/eslintrc/issues/184)) ([2f4cf3f](https://github.com/eslint/eslintrc/commit/2f4cf3fe36ee0df93c1c53f32c030c58db1816a2))
+
+## [3.3.0](https://github.com/eslint/eslintrc/compare/v3.2.0...v3.3.0) (2025-02-21)
+
+
+### Features
+
+* Add types to package ([#179](https://github.com/eslint/eslintrc/issues/179)) ([cb546be](https://github.com/eslint/eslintrc/commit/cb546be8ba53abcb4c64ed2fdd3a729dd1337f61))
+
+## [3.2.0](https://github.com/eslint/eslintrc/compare/v3.1.0...v3.2.0) (2024-11-14)
+
+
+### Features
+
+* merge rule.meta.defaultOptions before validation ([#166](https://github.com/eslint/eslintrc/issues/166)) ([d02f914](https://github.com/eslint/eslintrc/commit/d02f91452b81caff971f7895237cc4fb002e31da))
+
+## [3.1.0](https://github.com/eslint/eslintrc/compare/v3.0.2...v3.1.0) (2024-05-17)
+
+
+### Features
+
+* Expose loadConfigFile() function ([#160](https://github.com/eslint/eslintrc/issues/160)) ([59e890f](https://github.com/eslint/eslintrc/commit/59e890fcd9e03663ac185640d5bed5f1a85bd39b))
+
+
+### Chores
+
+* run tests in Node.js 22 ([#154](https://github.com/eslint/eslintrc/issues/154)) ([5e526f2](https://github.com/eslint/eslintrc/commit/5e526f2e2897b87d7a704391cec74702d4bed38c))
+* update dependency shelljs to ^0.8.5 ([#156](https://github.com/eslint/eslintrc/issues/156)) ([903b887](https://github.com/eslint/eslintrc/commit/903b8875581ee731fd1a9424f83f785359cfb22e))
+
+## [3.0.2](https://github.com/eslint/eslintrc/compare/v3.0.1...v3.0.2) (2024-02-12)
+
+
+### Chores
+
+* maintenance update of `globals` to `v14` ([#152](https://github.com/eslint/eslintrc/issues/152)) ([4151865](https://github.com/eslint/eslintrc/commit/4151865b09084369e89d591eb2e01b9617287982))
+
+## [3.0.1](https://github.com/eslint/eslintrc/compare/v3.0.0...v3.0.1) (2024-02-09)
+
+
+### Documentation
+
+* fix changelog for v3.0.0 ([#144](https://github.com/eslint/eslintrc/issues/144)) ([a613847](https://github.com/eslint/eslintrc/commit/a61384731aff386a8260a80d9710c912e4f62aaa))
+* More explicit about all and recommended configs ([#150](https://github.com/eslint/eslintrc/issues/150)) ([0fabc74](https://github.com/eslint/eslintrc/commit/0fabc7406e5a281a4e72be33de6e3bf8642aa746))
+
+
+### Chores
+
+* upgrade espree@10.0.1 ([#151](https://github.com/eslint/eslintrc/issues/151)) ([8c39944](https://github.com/eslint/eslintrc/commit/8c399441f47009344888e181c6aa2ecdc74ce8ea))
+
+## [3.0.0](https://github.com/eslint/eslintrc/compare/v2.1.4...v3.0.0) (2023-12-27)
+
+
+### ⚠ BREAKING CHANGES
+
+* Require Node.js `^18.18.0 || ^20.9.0 || >=21.1.0` ([#142](https://github.com/eslint/eslintrc/issues/142))
+* Set default `schema: []`, drop support for function-style rules ([#139](https://github.com/eslint/eslintrc/issues/139))
+
+### Features
+
+* Require Node.js `^18.18.0 || ^20.9.0 || >=21.1.0` ([#142](https://github.com/eslint/eslintrc/issues/142)) ([737eb25](https://github.com/eslint/eslintrc/commit/737eb25ac686550020b838ccf6efd5cd2aaa449e))
+* Set default `schema: []`, drop support for function-style rules ([#139](https://github.com/eslint/eslintrc/issues/139)) ([a6c240d](https://github.com/eslint/eslintrc/commit/a6c240de244b0e94ace4a518f2c67876a91f5882))
+
+
+### Chores
+
+* upgrade github actions ([#143](https://github.com/eslint/eslintrc/issues/143)) ([de34faf](https://github.com/eslint/eslintrc/commit/de34fafed28aaf1936845d51309f28484ed29e2f))
+
+## [2.1.4](https://github.com/eslint/eslintrc/compare/v2.1.3...v2.1.4) (2023-11-27)
+
+
+### Bug Fixes
+
+* Use original plugin from disk in FlatCompat ([#137](https://github.com/eslint/eslintrc/issues/137)) ([1c4cf6a](https://github.com/eslint/eslintrc/commit/1c4cf6a71378d480323bfdd404c9585bd0d21d65))
+
## [2.1.3](https://github.com/eslint/eslintrc/compare/v2.1.2...v2.1.3) (2023-11-01)
diff --git a/README.md b/README.md
index 7641c741..846cd120 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,14 @@ This repository contains the legacy ESLintRC configuration file format for ESLin
You can install the package as follows:
-```
-npm install @eslint/eslintrc --save-dev
-
+```shell
+npm install @eslint/eslintrc -D
# or
-
yarn add @eslint/eslintrc -D
+# or
+pnpm install @eslint/eslintrc -D
+# or
+bun install @eslint/eslintrc -D
```
## Usage (ESM)
@@ -33,14 +35,14 @@ const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname, // optional; default: process.cwd()
resolvePluginsRelativeTo: __dirname, // optional
- recommendedConfig: js.configs.recommended, // optional
- allConfig: js.configs.all, // optional
+ recommendedConfig: js.configs.recommended, // optional unless you're using "eslint:recommended"
+ allConfig: js.configs.all, // optional unless you're using "eslint:all"
});
export default [
// mimic ESLintRC-style extends
- ...compat.extends("standard", "example"),
+ ...compat.extends("standard", "example", "plugin:react/recommended"),
// mimic environments
...compat.env({
@@ -49,11 +51,11 @@ export default [
}),
// mimic plugins
- ...compat.plugins("airbnb", "react"),
+ ...compat.plugins("jsx-a11y", "react"),
// translate an entire config
...compat.config({
- plugins: ["airbnb", "react"],
+ plugins: ["jsx-a11y", "react"],
extends: "standard",
env: {
es2020: true,
@@ -77,14 +79,14 @@ const js = require("@eslint/js");
const compat = new FlatCompat({
baseDirectory: __dirname, // optional; default: process.cwd()
resolvePluginsRelativeTo: __dirname, // optional
- recommendedConfig: js.configs.recommended, // optional
- allConfig: js.configs.all, // optional
+ recommendedConfig: js.configs.recommended, // optional unless using "eslint:recommended"
+ allConfig: js.configs.all, // optional unless using "eslint:all"
});
module.exports = [
// mimic ESLintRC-style extends
- ...compat.extends("standard", "example"),
+ ...compat.extends("standard", "example", "plugin:react/recommended"),
// mimic environments
...compat.env({
@@ -93,11 +95,11 @@ module.exports = [
}),
// mimic plugins
- ...compat.plugins("airbnb", "react"),
+ ...compat.plugins("jsx-a11y", "react"),
// translate an entire config
...compat.config({
- plugins: ["airbnb", "react"],
+ plugins: ["jsx-a11y", "react"],
extends: "standard",
env: {
es2020: true,
@@ -110,6 +112,34 @@ module.exports = [
];
```
+## Troubleshooting
+
+**TypeError: Missing parameter 'recommendedConfig' in FlatCompat constructor**
+
+The `recommendedConfig` option is required when any config uses `eslint:recommended`, including any config in an `extends` clause. To fix this, follow the example above using `@eslint/js` to provide the `eslint:recommended` config.
+
+**TypeError: Missing parameter 'allConfig' in FlatCompat constructor**
+
+The `allConfig` option is required when any config uses `eslint:all`, including any config in an `extends` clause. To fix this, follow the example above using `@eslint/js` to provide the `eslint:all` config.
+
## License
MIT License
+
+
+
+
+## Sponsors
+
+The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
+to get your logo on our READMEs and [website](https://eslint.org/sponsors).
+
+
Platinum Sponsors
+

Gold Sponsors
+

Silver Sponsors
+

Bronze Sponsors
+

+Technology Sponsors
+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.
+

+
diff --git a/conf/environments.js b/conf/environments.js
index 50d1b1d1..e296fae7 100644
--- a/conf/environments.js
+++ b/conf/environments.js
@@ -23,7 +23,7 @@ function getDiff(current, prev) {
const retv = {};
for (const [key, value] of Object.entries(current)) {
- if (!Object.hasOwnProperty.call(prev, key)) {
+ if (!Object.hasOwn(prev, key)) {
retv[key] = value;
}
}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..0981a822
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview ESLint configuration file
+ * @author Nicholas C. Zakas
+ */
+
+//-----------------------------------------------------------------------------
+// Imports
+//-----------------------------------------------------------------------------
+
+import eslintConfigESLint from "eslint-config-eslint";
+import eslintConfigESLintFormatting from "eslint-config-eslint/formatting";
+
+//-----------------------------------------------------------------------------
+// Config
+//-----------------------------------------------------------------------------
+
+export default [
+
+ {
+ ignores: [
+ "tests/fixtures",
+ "coverage",
+ "docs",
+ "jsdoc",
+ "dist"
+ ]
+ },
+
+ ...eslintConfigESLint,
+ eslintConfigESLintFormatting,
+ {
+ files: ["tests/**/*"],
+ languageOptions: {
+ globals: {
+ describe: "readonly",
+ xdescribe: "readonly",
+ it: "readonly",
+ xit: "readonly",
+ beforeEach: "readonly",
+ afterEach: "readonly",
+ before: "readonly",
+ after: "readonly"
+ }
+ },
+ rules: {
+ "no-restricted-syntax": ["error", {
+ selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']",
+ message: "`assert.doesNotThrow()` should be replaced with a comment next to the code."
+ }]
+ }
+ }
+];
diff --git a/lib/cascading-config-array-factory.js b/lib/cascading-config-array-factory.js
index 597352e4..71549107 100644
--- a/lib/cascading-config-array-factory.js
+++ b/lib/cascading-config-array-factory.js
@@ -23,8 +23,8 @@
//------------------------------------------------------------------------------
import debugOrig from "debug";
-import os from "os";
-import path from "path";
+import os from "node:os";
+import path from "node:path";
import { ConfigArrayFactory } from "./config-array-factory.js";
import {
@@ -193,7 +193,7 @@ function createCLIConfigArray({
*/
class ConfigurationNotFoundError extends Error {
- // eslint-disable-next-line jsdoc/require-description
+
/**
* @param {string} directoryPath The directory path.
*/
@@ -345,6 +345,7 @@ class CascadingConfigArrayFactory {
* @param {string} directoryPath The path to a leaf directory.
* @param {boolean} configsExistInSubdirs `true` if configurations exist in subdirectories.
* @returns {ConfigArray} The loaded config.
+ * @throws {Error} If a config file is invalid.
* @private
*/
_loadConfigInAncestors(directoryPath, configsExistInSubdirs = false) {
@@ -446,6 +447,7 @@ class CascadingConfigArrayFactory {
* @param {string} directoryPath The path to the leaf directory to find config files.
* @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`.
* @returns {ConfigArray} The loaded config.
+ * @throws {Error} If a config file is invalid.
* @private
*/
_finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) {
@@ -482,7 +484,7 @@ class CascadingConfigArrayFactory {
!directoryPath.startsWith(homePath)
) {
const lastElement =
- personalConfigArray[personalConfigArray.length - 1];
+ personalConfigArray.at(-1);
emitDeprecationWarning(
lastElement.filePath,
diff --git a/lib/config-array-factory.js b/lib/config-array-factory.js
index 23cb3529..4f55ae2e 100644
--- a/lib/config-array-factory.js
+++ b/lib/config-array-factory.js
@@ -39,10 +39,10 @@
//------------------------------------------------------------------------------
import debugOrig from "debug";
-import fs from "fs";
+import fs from "node:fs";
import importFresh from "import-fresh";
-import { createRequire } from "module";
-import path from "path";
+import { createRequire } from "node:module";
+import path from "node:path";
import stripComments from "strip-json-comments";
import {
@@ -254,7 +254,7 @@ function loadPackageJSONConfigFile(filePath) {
try {
const packageData = loadJSONConfigFile(filePath);
- if (!Object.hasOwnProperty.call(packageData, "eslintConfig")) {
+ if (!Object.hasOwn(packageData, "eslintConfig")) {
throw Object.assign(
new Error("package.json file doesn't have 'eslintConfig' field."),
{ code: "ESLINT_CONFIG_FIELD_NOT_FOUND" }
@@ -273,6 +273,7 @@ function loadPackageJSONConfigFile(filePath) {
* Loads a `.eslintignore` from a file.
* @param {string} filePath The filename to load.
* @returns {string[]} The ignore patterns from the file.
+ * @throws {Error} If the file cannot be read.
* @private
*/
function loadESLintIgnoreFile(filePath) {
@@ -345,7 +346,7 @@ function loadConfigFile(filePath) {
function writeDebugLogForLoading(request, relativeTo, filePath) {
/* istanbul ignore next */
if (debug.enabled) {
- let nameAndVersion = null;
+ let nameAndVersion = null; // eslint-disable-line no-useless-assignment -- known bug in the rule
try {
const packageJsonPath = ModuleResolver.resolve(
@@ -510,6 +511,7 @@ class ConfigArrayFactory {
* @param {Object} [options] The options.
* @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
* @param {string} [options.name] The config name.
+ * @throws {Error} If the config file is invalid.
* @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist.
*/
loadInDirectory(directoryPath, { basePath, name } = {}) {
@@ -595,6 +597,7 @@ class ConfigArrayFactory {
/**
* Load `.eslintignore` file in the current working directory.
* @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist.
+ * @throws {Error} If the ignore file is invalid.
*/
loadDefaultESLintIgnore() {
const slots = internalSlotsMap.get(this);
@@ -607,7 +610,7 @@ class ConfigArrayFactory {
if (fs.existsSync(packageJsonPath)) {
const data = loadJSONConfigFile(packageJsonPath);
- if (Object.hasOwnProperty.call(data, "eslintIgnore")) {
+ if (Object.hasOwn(data, "eslintIgnore")) {
if (!Array.isArray(data.eslintIgnore)) {
throw new Error("Package.json eslintIgnore property requires an array of paths");
}
@@ -796,6 +799,7 @@ class ConfigArrayFactory {
* @param {string} extendName The name of a base config.
* @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
* @returns {IterableIterator} The normalized config.
+ * @throws {Error} If the extended config file can't be loaded.
* @private
*/
_loadExtends(extendName, ctx) {
@@ -819,6 +823,7 @@ class ConfigArrayFactory {
* @param {string} extendName The name of a base config.
* @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
* @returns {IterableIterator} The normalized config.
+ * @throws {Error} If the extended config file can't be loaded.
* @private
*/
_loadExtendedBuiltInConfig(extendName, ctx) {
@@ -868,6 +873,7 @@ class ConfigArrayFactory {
* @param {string} extendName The name of a base config.
* @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
* @returns {IterableIterator} The normalized config.
+ * @throws {Error} If the extended config file can't be loaded.
* @private
*/
_loadExtendedPluginConfig(extendName, ctx) {
@@ -905,6 +911,7 @@ class ConfigArrayFactory {
* @param {string} extendName The name of a base config.
* @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
* @returns {IterableIterator} The normalized config.
+ * @throws {Error} If the extended config file can't be loaded.
* @private
*/
_loadExtendedShareableConfig(extendName, ctx) {
@@ -1106,6 +1113,7 @@ class ConfigArrayFactory {
if (plugin) {
return new ConfigDependency({
definition: normalizePlugin(plugin),
+ original: plugin,
filePath: "", // It's unknown where the plugin came from.
id,
importerName: ctx.name,
@@ -1142,6 +1150,7 @@ class ConfigArrayFactory {
return new ConfigDependency({
definition: normalizePlugin(pluginDefinition),
+ original: pluginDefinition,
filePath,
id,
importerName: ctx.name,
@@ -1219,4 +1228,8 @@ class ConfigArrayFactory {
}
}
-export { ConfigArrayFactory, createContext };
+export {
+ ConfigArrayFactory,
+ createContext,
+ loadConfigFile
+};
diff --git a/lib/config-array/config-array.js b/lib/config-array/config-array.js
index 133f5a24..8b3ec28c 100644
--- a/lib/config-array/config-array.js
+++ b/lib/config-array/config-array.js
@@ -178,6 +178,7 @@ class PluginConflictError extends Error {
* @param {Record} target The destination to merge
* @param {Record|undefined} source The source to merge.
* @returns {void}
+ * @throws {PluginConflictError} When a plugin was conflicted.
*/
function mergePlugins(target, source) {
if (!isNonNullObject(source)) {
@@ -258,6 +259,7 @@ function mergeRuleConfigs(target, source) {
* @param {ConfigArray} instance The config elements.
* @param {number[]} indices The indices to use.
* @returns {ExtractedConfig} The extracted config.
+ * @throws {Error} When a plugin is conflicted.
*/
function createConfig(instance, indices) {
const config = new ExtractedConfig();
@@ -319,31 +321,18 @@ function createConfig(instance, indices) {
* @param {string} pluginId The plugin ID for prefix.
* @param {Record} defs The definitions to collect.
* @param {Map} map The map to output.
- * @param {function(T): U} [normalize] The normalize function for each value.
* @returns {void}
*/
-function collect(pluginId, defs, map, normalize) {
+function collect(pluginId, defs, map) {
if (defs) {
const prefix = pluginId && `${pluginId}/`;
for (const [key, value] of Object.entries(defs)) {
- map.set(
- `${prefix}${key}`,
- normalize ? normalize(value) : value
- );
+ map.set(`${prefix}${key}`, value);
}
}
}
-/**
- * Normalize a rule definition.
- * @param {Function|Rule} rule The rule definition to normalize.
- * @returns {Rule} The normalized rule definition.
- */
-function normalizePluginRule(rule) {
- return typeof rule === "function" ? { create: rule } : rule;
-}
-
/**
* Delete the mutation methods from a given map.
* @param {Map} map The map object to delete.
@@ -385,7 +374,7 @@ function initPluginMemberMaps(elements, slots) {
collect(pluginId, plugin.environments, slots.envMap);
collect(pluginId, plugin.processors, slots.processorMap);
- collect(pluginId, plugin.rules, slots.ruleMap, normalizePluginRule);
+ collect(pluginId, plugin.rules, slots.ruleMap);
}
}
diff --git a/lib/config-array/config-dependency.js b/lib/config-array/config-dependency.js
index 2883c3a2..80968950 100644
--- a/lib/config-array/config-dependency.js
+++ b/lib/config-array/config-dependency.js
@@ -15,7 +15,7 @@
* @author Toru Nagashima
*/
-import util from "util";
+import util from "node:util";
/**
* The class is to store parsers or plugins.
@@ -28,6 +28,7 @@ class ConfigDependency {
* Initialize this instance.
* @param {Object} data The dependency data.
* @param {T} [data.definition] The dependency if the loading succeeded.
+ * @param {T} [data.original] The original, non-normalized dependency if the loading succeeded.
* @param {Error} [data.error] The error object if the loading failed.
* @param {string} [data.filePath] The actual path to the dependency if the loading succeeded.
* @param {string} data.id The ID of this dependency.
@@ -36,6 +37,7 @@ class ConfigDependency {
*/
constructor({
definition = null,
+ original = null,
error = null,
filePath = null,
id,
@@ -49,6 +51,12 @@ class ConfigDependency {
*/
this.definition = definition;
+ /**
+ * The original dependency as loaded directly from disk if the loading succeeded.
+ * @type {T|null}
+ */
+ this.original = original;
+
/**
* The error object if the loading failed.
* @type {Error|null}
@@ -80,8 +88,8 @@ class ConfigDependency {
this.importerPath = importerPath;
}
- // eslint-disable-next-line jsdoc/require-description
/**
+ * Converts this instance to a JSON compatible object.
* @returns {Object} a JSON compatible object.
*/
toJSON() {
@@ -95,13 +103,14 @@ class ConfigDependency {
return obj;
}
- // eslint-disable-next-line jsdoc/require-description
/**
+ * Custom inspect method for Node.js `console.log()`.
* @returns {Object} an object to display by `console.log()`.
*/
[util.inspect.custom]() {
const {
- definition: _ignore, // eslint-disable-line no-unused-vars
+ definition: _ignore1, // eslint-disable-line no-unused-vars -- needed to make `obj` correct
+ original: _ignore2, // eslint-disable-line no-unused-vars -- needed to make `obj` correct
...obj
} = this;
diff --git a/lib/config-array/extracted-config.js b/lib/config-array/extracted-config.js
index e93b0b67..65206f27 100644
--- a/lib/config-array/extracted-config.js
+++ b/lib/config-array/extracted-config.js
@@ -120,10 +120,10 @@ class ExtractedConfig {
*/
toCompatibleObjectAsConfigFileContent() {
const {
- /* eslint-disable no-unused-vars */
+ /* eslint-disable no-unused-vars -- needed to make `config` correct */
configNameOfNoInlineConfig: _ignore1,
processor: _ignore2,
- /* eslint-enable no-unused-vars */
+ /* eslint-enable no-unused-vars -- needed to make `config` correct */
ignores,
...config
} = this;
diff --git a/lib/config-array/ignore-pattern.js b/lib/config-array/ignore-pattern.js
index 3022ba9f..edb52872 100644
--- a/lib/config-array/ignore-pattern.js
+++ b/lib/config-array/ignore-pattern.js
@@ -32,8 +32,8 @@
// Requirements
//------------------------------------------------------------------------------
-import assert from "assert";
-import path from "path";
+import assert from "node:assert";
+import path from "node:path";
import ignore from "ignore";
import debugOrig from "debug";
@@ -117,6 +117,9 @@ const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
// Public
//------------------------------------------------------------------------------
+/**
+ * Represents a set of glob patterns to ignore against a base path.
+ */
class IgnorePattern {
/**
@@ -153,9 +156,7 @@ class IgnorePattern {
debug("Create with: %o", ignorePatterns);
const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath));
- const patterns = [].concat(
- ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath))
- );
+ const patterns = ignorePatterns.flatMap(p => p.getPatternsRelativeTo(basePath));
const ig = ignore({ allowRelativePaths: true }).add([...DotPatterns, ...patterns]);
const dotIg = ignore({ allowRelativePaths: true }).add(patterns);
diff --git a/lib/config-array/override-tester.js b/lib/config-array/override-tester.js
index 460aafcf..3a445b1a 100644
--- a/lib/config-array/override-tester.js
+++ b/lib/config-array/override-tester.js
@@ -17,9 +17,9 @@
* @author Toru Nagashima
*/
-import assert from "assert";
-import path from "path";
-import util from "util";
+import assert from "node:assert";
+import path from "node:path";
+import util from "node:util";
import minimatch from "minimatch";
const { Minimatch } = minimatch;
@@ -94,6 +94,7 @@ class OverrideTester {
* @param {string|string[]} excludedFiles The glob patterns for excluded files.
* @param {string} basePath The path to the base directory to test paths.
* @returns {OverrideTester|null} The created instance or `null`.
+ * @throws {Error} When invalid patterns are given.
*/
static create(files, excludedFiles, basePath) {
const includePatterns = normalizePatterns(files);
@@ -183,6 +184,7 @@ class OverrideTester {
* Test if a given path is matched or not.
* @param {string} filePath The absolute path to the target file.
* @returns {boolean} `true` if the path was matched.
+ * @throws {Error} When invalid `filePath` is given.
*/
test(filePath) {
if (typeof filePath !== "string" || !path.isAbsolute(filePath)) {
@@ -196,8 +198,8 @@ class OverrideTester {
));
}
- // eslint-disable-next-line jsdoc/require-description
/**
+ * Converts this instance to a JSON compatible object.
* @returns {Object} a JSON compatible object.
*/
toJSON() {
@@ -213,8 +215,8 @@ class OverrideTester {
};
}
- // eslint-disable-next-line jsdoc/require-description
/**
+ * Custom inspect method for Node.js `console.log()`.
* @returns {Object} an object to display by `console.log()`.
*/
[util.inspect.custom]() {
diff --git a/lib/flat-compat.js b/lib/flat-compat.js
index e0d7ab66..c5c4e1c2 100644
--- a/lib/flat-compat.js
+++ b/lib/flat-compat.js
@@ -8,7 +8,7 @@
//-----------------------------------------------------------------------------
import createDebug from "debug";
-import path from "path";
+import path from "node:path";
import environments from "../conf/environments.js";
import { ConfigArrayFactory } from "./config-array-factory.js";
@@ -37,6 +37,7 @@ const cafactory = Symbol("cafactory");
* @param {ReadOnlyMap} options.pluginProcessors A map of plugin processor
* names to objects.
* @returns {Object} A flag-config-style config object.
+ * @throws {Error} If a plugin or environment cannot be resolved.
*/
function translateESLintRC(eslintrcConfig, {
resolveConfigRelativeTo,
@@ -132,7 +133,7 @@ function translateESLintRC(eslintrcConfig, {
debug(`Translating plugin: ${pluginName}`);
debug(`Resolving plugin '${pluginName} relative to ${resolvePluginsRelativeTo}`);
- const { definition: plugin, error } = eslintrcConfig.plugins[pluginName];
+ const { original: plugin, error } = eslintrcConfig.plugins[pluginName];
if (error) {
throw error;
@@ -219,21 +220,31 @@ class FlatCompat {
this[cafactory] = new ConfigArrayFactory({
cwd: baseDirectory,
resolvePluginsRelativeTo,
- getEslintAllConfig: () => {
+ getEslintAllConfig() {
if (!allConfig) {
throw new TypeError("Missing parameter 'allConfig' in FlatCompat constructor.");
}
- return allConfig;
+ // remove name property if it exists
+ const config = { ...allConfig };
+
+ delete config.name;
+
+ return config;
},
- getEslintRecommendedConfig: () => {
+ getEslintRecommendedConfig() {
if (!recommendedConfig) {
throw new TypeError("Missing parameter 'recommendedConfig' in FlatCompat constructor.");
}
- return recommendedConfig;
+ // remove name property if it exists
+ const config = { ...recommendedConfig };
+
+ delete config.name;
+
+ return config;
}
});
}
diff --git a/lib/index.js b/lib/index.js
index 9e3d13f5..a37e5746 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -8,7 +8,8 @@
import {
ConfigArrayFactory,
- createContext as createConfigArrayFactoryContext
+ createContext as createConfigArrayFactoryContext,
+ loadConfigFile
} from "./config-array-factory.js";
import { CascadingConfigArrayFactory } from "./cascading-config-array-factory.js";
@@ -39,6 +40,7 @@ const Legacy = {
OverrideTester,
getUsedExtractedConfigs,
environments,
+ loadConfigFile,
// shared
ConfigOps,
diff --git a/lib/shared/ajv.js b/lib/shared/ajv.js
index b79ad36c..7e53d12a 100644
--- a/lib/shared/ajv.js
+++ b/lib/shared/ajv.js
@@ -184,7 +184,7 @@ export default (additionalOptions = {}) => {
});
ajv.addMetaSchema(metaSchema);
- // eslint-disable-next-line no-underscore-dangle
+ // eslint-disable-next-line no-underscore-dangle -- part of the API
ajv._opts.defaultMeta = metaSchema.id;
return ajv;
diff --git a/lib/shared/config-ops.js b/lib/shared/config-ops.js
index d203be0e..465d9b86 100644
--- a/lib/shared/config-ops.js
+++ b/lib/shared/config-ops.js
@@ -13,7 +13,7 @@ const RULE_SEVERITY_STRINGS = ["off", "warn", "error"],
map[value] = index;
return map;
}, {}),
- VALID_SEVERITIES = [0, 1, 2, "off", "warn", "error"];
+ VALID_SEVERITIES = new Set([0, 1, 2, "off", "warn", "error"]);
//------------------------------------------------------------------------------
// Public Interface
@@ -83,7 +83,7 @@ function isValidSeverity(ruleConfig) {
if (typeof severity === "string") {
severity = severity.toLowerCase();
}
- return VALID_SEVERITIES.indexOf(severity) !== -1;
+ return VALID_SEVERITIES.has(severity);
}
/**
diff --git a/lib/shared/config-validator.js b/lib/shared/config-validator.js
index 32174a56..6857e7a0 100644
--- a/lib/shared/config-validator.js
+++ b/lib/shared/config-validator.js
@@ -3,16 +3,23 @@
* @author Brandon Mills
*/
-/* eslint class-methods-use-this: "off" */
+/* eslint class-methods-use-this: "off" -- not needed in this file */
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/** @typedef {import("../shared/types").Rule} Rule */
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
-import util from "util";
+import util from "node:util";
import * as ConfigOps from "./config-ops.js";
import { emitDeprecationWarning } from "./deprecation-warnings.js";
import ajvOrig from "./ajv.js";
+import { deepMergeArrays } from "./deep-merge-arrays.js";
import configSchema from "../../conf/config-schema.js";
import BuiltInEnvironments from "../../conf/environments.js";
@@ -33,10 +40,20 @@ const severityMap = {
const validated = new WeakSet();
+// JSON schema that disallows passing any options
+const noOptionsSchema = Object.freeze({
+ type: "array",
+ minItems: 0,
+ maxItems: 0
+});
+
//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
+/**
+ * Validator for configuration objects.
+ */
export default class ConfigValidator {
constructor({ builtInRules = new Map() } = {}) {
this.builtInRules = builtInRules;
@@ -44,17 +61,36 @@ export default class ConfigValidator {
/**
* Gets a complete options schema for a rule.
- * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
- * @returns {Object} JSON Schema for the rule's options.
+ * @param {Rule} rule A rule object
+ * @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
+ * @returns {Object|null} JSON Schema for the rule's options.
+ * `null` if rule wasn't passed or its `meta.schema` is `false`.
*/
getRuleOptionsSchema(rule) {
if (!rule) {
return null;
}
- const schema = rule.schema || rule.meta && rule.meta.schema;
+ if (!rule.meta) {
+ return { ...noOptionsSchema }; // default if `meta.schema` is not specified
+ }
+
+ const schema = rule.meta.schema;
+
+ if (typeof schema === "undefined") {
+ return { ...noOptionsSchema }; // default if `meta.schema` is not specified
+ }
- // Given a tuple of schemas, insert warning level at the beginning
+ // `schema:false` is an allowed explicit opt-out of options validation for the rule
+ if (schema === false) {
+ return null;
+ }
+
+ if (typeof schema !== "object" || schema === null) {
+ throw new TypeError("Rule's `meta.schema` must be an array or object");
+ }
+
+ // ESLint-specific array form needs to be converted into a valid JSON Schema definition
if (Array.isArray(schema)) {
if (schema.length) {
return {
@@ -64,22 +100,20 @@ export default class ConfigValidator {
maxItems: schema.length
};
}
- return {
- type: "array",
- minItems: 0,
- maxItems: 0
- };
+ // `schema:[]` is an explicit way to specify that the rule does not accept any options
+ return { ...noOptionsSchema };
}
- // Given a full schema, leave it alone
- return schema || null;
+ // `schema: