From d1118c645b37404d4ddcdd84f278fd21acbfe79f Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:40:44 -0600 Subject: [PATCH 01/35] copy functionality from compat --- packages/config-helpers/src/ignore-file.js | 91 ++++++++++++++ packages/config-helpers/src/index.js | 4 + .../ignore-files/a-folder/gitignore2.txt | 0 .../fixtures/ignore-files/gitignore1.txt | 17 +++ .../config-helpers/tests/ignore-file.test.js | 115 ++++++++++++++++++ 5 files changed, 227 insertions(+) create mode 100644 packages/config-helpers/src/ignore-file.js create mode 100644 packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt create mode 100644 packages/config-helpers/tests/fixtures/ignore-files/gitignore1.txt create mode 100644 packages/config-helpers/tests/ignore-file.test.js diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js new file mode 100644 index 000000000..a3f3b4474 --- /dev/null +++ b/packages/config-helpers/src/ignore-file.js @@ -0,0 +1,91 @@ +/** + * @fileoverview Ignore file utilities for the compat package. + * @author Nicholas C. Zakas + */ + +//----------------------------------------------------------------------------- +// Imports +//----------------------------------------------------------------------------- + +import fs from "node:fs"; +import path from "node:path"; + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +/** @typedef {import("@eslint/core").ConfigObject} FlatConfig */ + +//----------------------------------------------------------------------------- +// Exports +//----------------------------------------------------------------------------- + +/** + * Converts an ESLint ignore pattern to a minimatch pattern. + * @param {string} pattern The .eslintignore or .gitignore pattern to convert. + * @returns {string} The converted pattern. + */ +export function convertIgnorePatternToMinimatch(pattern) { + const isNegated = pattern.startsWith("!"); + const negatedPrefix = isNegated ? "!" : ""; + const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd(); + + // special cases + if (["", "**", "/**", "**/"].includes(patternToTest)) { + return `${negatedPrefix}${patternToTest}`; + } + + const firstIndexOfSlash = patternToTest.indexOf("/"); + + const matchEverywherePrefix = + firstIndexOfSlash < 0 || firstIndexOfSlash === patternToTest.length - 1 + ? "**/" + : ""; + + const patternWithoutLeadingSlash = + firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest; + + /* + * Escape `{` and `(` because in gitignore patterns they are just + * literal characters without any specific syntactic meaning, + * while in minimatch patterns they can form brace expansion or extglob syntax. + * + * For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`. + * But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`. + * Minimatch pattern `src/\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`. + */ + const escapedPatternWithoutLeadingSlash = + patternWithoutLeadingSlash.replaceAll( + // eslint-disable-next-line regexp/no-empty-lookarounds-assertion -- False positive + /(?=((?:\\.|[^{(])*))\1([{(])/guy, + "$1\\$2", + ); + + const matchInsideSuffix = patternToTest.endsWith("/**") ? "/*" : ""; + + return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`; +} + +/** + * Reads an ignore file and returns an object with the ignore patterns. + * @param {string} ignoreFilePath The absolute path to the ignore file. + * @param {string} [name] The name of the ignore file config. + * @returns {FlatConfig} An object with an `ignores` property that is an array of ignore patterns. + * @throws {Error} If the ignore file path is not an absolute path. + */ +export function includeIgnoreFile(ignoreFilePath, name) { + if (!path.isAbsolute(ignoreFilePath)) { + throw new Error("The ignore file location must be an absolute path."); + } + + const ignoreFile = fs.readFileSync(ignoreFilePath, "utf8"); + const lines = ignoreFile.split(/\r?\n/u); + + return { + name: name || "Imported .gitignore patterns", + ignores: lines + .map(line => line.trim()) + .filter(line => line && !line.startsWith("#")) + .map(convertIgnorePatternToMinimatch), + }; +} diff --git a/packages/config-helpers/src/index.js b/packages/config-helpers/src/index.js index da8412aab..20d8d4e73 100644 --- a/packages/config-helpers/src/index.js +++ b/packages/config-helpers/src/index.js @@ -4,3 +4,7 @@ export { defineConfig } from "./define-config.js"; export { globalIgnores } from "./global-ignores.js"; +export { + includeIgnoreFile, + convertIgnorePatternToMinimatch, +} from "./ignore-file.js"; diff --git a/packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt b/packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt new file mode 100644 index 000000000..e69de29bb diff --git a/packages/config-helpers/tests/fixtures/ignore-files/gitignore1.txt b/packages/config-helpers/tests/fixtures/ignore-files/gitignore1.txt new file mode 100644 index 000000000..f6b5f5754 --- /dev/null +++ b/packages/config-helpers/tests/fixtures/ignore-files/gitignore1.txt @@ -0,0 +1,17 @@ +# Node.js +node_modules +!/fixtures/node_modules +/dist + +# Logs +*.log + +# Gatsby files +.cache/ + +# vuepress build output +.vuepress/dist + +# other +*/foo.js +dir/** diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js new file mode 100644 index 000000000..60a48a8a6 --- /dev/null +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -0,0 +1,115 @@ +/** + * @filedescription Fixup tests + */ + +//----------------------------------------------------------------------------- +// Imports +//----------------------------------------------------------------------------- + +import assert from "node:assert"; +import { + includeIgnoreFile, + convertIgnorePatternToMinimatch, +} from "../src/ignore-file.js"; +import { fileURLToPath } from "node:url"; + +//----------------------------------------------------------------------------- +// Tests +//----------------------------------------------------------------------------- + +describe("@eslint/compat", () => { + describe("convertIgnorePatternToMinimatch", () => { + const tests = [ + ["", ""], + ["**", "**"], + ["/**", "/**"], + ["**/", "**/"], + ["src/", "**/src/"], + ["src", "**/src"], + ["src/**", "src/**/*"], + ["!src/", "!**/src/"], + ["!src", "!**/src"], + ["!src/**", "!src/**/*"], + ["*/foo.js", "*/foo.js"], + ["*/foo.js/", "*/foo.js/"], + ["src/{a,b}.js", "src/\\{a,b}.js"], + ["src/?(a)b.js", "src/?\\(a)b.js"], + ["{.js", "**/\\{.js"], + ["(.js", "**/\\(.js"], + ["(.js", "**/\\(.js"], + ["{(.js", "**/\\{\\(.js"], + ["{bar}/{baz}", "\\{bar}/\\{baz}"], + ["\\[foo]/{bar}/{baz}", "\\[foo]/\\{bar}/\\{baz}"], + ["src/\\{a}", "src/\\{a}"], + ["src/\\(a)", "src/\\(a)"], + ["src/\\{a}/{b}", "src/\\{a}/\\{b}"], + ["src/\\(a)/(b)", "src/\\(a)/\\(b)"], + ["a\\bc{de(f\\gh\\{i\\(j{(", "**/a\\bc\\{de\\(f\\gh\\{i\\(j\\{\\("], + ]; + + tests.forEach(([pattern, expected]) => { + it(`should convert "${pattern}" to "${expected}"`, () => { + assert.strictEqual( + convertIgnorePatternToMinimatch(pattern), + expected, + ); + }); + }); + }); + + describe("includeIgnoreFile", () => { + it("should throw an error when a relative path is passed", () => { + const ignoreFilePath = + "../tests/fixtures/ignore-files/gitignore1.txt"; + assert.throws(() => { + includeIgnoreFile(ignoreFilePath); + }, /The ignore file location must be an absolute path./u); + }); + + it("should return an object with an `ignores` property", () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + const result = includeIgnoreFile(ignoreFilePath); + assert.deepStrictEqual(result, { + name: "Imported .gitignore patterns", + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }); + }); + + it("should return an object with a custom name", () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + const result = includeIgnoreFile(ignoreFilePath, "Custom Name"); + assert.deepStrictEqual(result, { + name: "Custom Name", + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }); + }); + }); +}); From d973693d0032e94561fcad8aaaabacda234f6b86 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:53:26 -0600 Subject: [PATCH 02/35] deprecate old APIs --- packages/compat/src/ignore-file.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/compat/src/ignore-file.js b/packages/compat/src/ignore-file.js index a3f3b4474..1f26476ce 100644 --- a/packages/compat/src/ignore-file.js +++ b/packages/compat/src/ignore-file.js @@ -24,6 +24,9 @@ import path from "node:path"; * Converts an ESLint ignore pattern to a minimatch pattern. * @param {string} pattern The .eslintignore or .gitignore pattern to convert. * @returns {string} The converted pattern. + * + * @deprecated Use the `convertIgnorePatternToMinimatch()` function exported by + * `@eslint/config-helpers` instead (also available at `eslint/config`). */ export function convertIgnorePatternToMinimatch(pattern) { const isNegated = pattern.startsWith("!"); @@ -72,6 +75,9 @@ export function convertIgnorePatternToMinimatch(pattern) { * @param {string} [name] The name of the ignore file config. * @returns {FlatConfig} An object with an `ignores` property that is an array of ignore patterns. * @throws {Error} If the ignore file path is not an absolute path. + * + * @deprecated Use the `includeIgnoreFile()` function exported by + * `@eslint/config-helpers` instead (also available at `eslint/config`). */ export function includeIgnoreFile(ignoreFilePath, name) { if (!path.isAbsolute(ignoreFilePath)) { From eb8cada456496d0d8c757f7709aca657760e4b6c Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:58:14 -0600 Subject: [PATCH 03/35] more deprecation documentation --- packages/compat/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/compat/README.md b/packages/compat/README.md index f855a9204..fb43748d7 100644 --- a/packages/compat/README.md +++ b/packages/compat/README.md @@ -149,6 +149,9 @@ module.exports = defineConfig([ ### Including Ignore Files +> [!WARNING] +> The `includeIgnoreFile()` exported by this package has been deprecated ([eslint/rewrite#329](https://github.com/eslint/rewrite/issues/329)). Use the `convertIgnorePatternToMinimatch()` function exported by `@eslint/config-helpers` instead (also available at `eslint/config`). This section is only preserved for historical reasons. + If you were using an alternate ignore file in ESLint v8.x, such as using `--ignore-path .gitignore` on the command line, you can include those patterns programmatically in your config file using the `includeIgnoreFile()` function. The `includeIgnoreFile()` function also accepts a second optional `name` parameter that allows you to set a custom name for this configuration object. If not specified, it defaults to `"Imported .gitignore patterns"`. For example: From 24e10cb477d419a0295433c9a8cb105de4961151 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:42:36 -0600 Subject: [PATCH 04/35] changes --- packages/compat/tests/ignore-file.test.js | 3 +- packages/config-helpers/README.md | 78 ++++++++++ packages/config-helpers/src/ignore-file.js | 137 +++++++++++++++--- .../ignore-files/a-folder/gitignore2.txt | 0 .../config-helpers/tests/ignore-file.test.js | 85 ++++++++++- 5 files changed, 277 insertions(+), 26 deletions(-) delete mode 100644 packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt diff --git a/packages/compat/tests/ignore-file.test.js b/packages/compat/tests/ignore-file.test.js index 60a48a8a6..ff9f1fa81 100644 --- a/packages/compat/tests/ignore-file.test.js +++ b/packages/compat/tests/ignore-file.test.js @@ -1,5 +1,6 @@ /** - * @filedescription Fixup tests + * @filedescription Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` + * @author Nicholas C. Zakas */ //----------------------------------------------------------------------------- diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index 5e6fda459..bd86f58c1 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -74,6 +74,84 @@ export default defineConfig([ ]); ``` +### `includeIgnoreFile()` + +The `includeIgnoreFile()` function reads an ignore file (such as a `.gitignore`) and returns a config object with the patterns converted to a global ignores object. Pass the absolute path to the ignore file as the first argument: + +```js +// eslint.config.js + +import { includeIgnoreFile } from "@eslint/config-helpers"; +import path from "node:path"; + +const ignorePath = path.join(import.meta.dirname, ".gitignore"); + +export default defineConfig( + includeIgnoreFile(ignorePath, { + mode: "gitignore", + }), + // ... +); +``` + +#### Options + +The second argument is an optional options object: + +- **`mode`** : Controls how ignore patterns are interpreted. + - `"eslintignore"` (default) — patterns are resolved relative to the current working directory, matching the behavior of `.eslintignore` files. + - `"gitignore"` — patterns are resolved relative to the ignore file itself (via `basePath`), matching the behavior of `.gitignore` files. +- **`name`** (`string`): A custom name for the resulting config object. + +For backwards compatibility with `includeIgnoreFile()` from `@eslint/compat`, passing a string instead of an object as the second argument is treated as equivalent to providing a value for `name`. + +#### Multiple files + +You can also pass an array of absolute paths to include multiple ignore files at once. In this case an array of config objects is returned: + +```js +// eslint.config.js + +import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers"; +import path from "node:path"; + +export default defineConfig( + includeIgnoreFile( + [ + path.join(import.meta.dirname, ".gitignore"), + path.join(import.meta.dirname, "packages/lib/.gitignore"), + ], + { mode: "gitignore" }, + ), + // ... +); +``` + +### `convertIgnorePatternToMinimatch()` + +This is used under the hood by `includeIgnoreFile()` to convert patterns found in eslintignore/gitignore files. You can use it to construct your own ignore objects if you wish: + +```js +// eslint.config.js + +import { + defineConfig, + convertIgnorePatternToMinimatch, +} from "@eslint/config-helpers"; + +export default defineConfig( + // approximate implementation of `includeIgnoreFile()` in eslintrc mode. + { + ignores: fs + .readFileSync("some/path", "utf8") + .split(/\r?\n/u) + .map(line => line.trim()) + .filter(line => line && !line.startsWith("#")) + .map(convertIgnorePatternToMinimatch), + }, +); +``` + ## License Apache 2.0 diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index a3f3b4474..5b25a94b2 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -1,6 +1,9 @@ /** - * @fileoverview Ignore file utilities for the compat package. + * @fileoverview Ignore file utilities for the config-helpers package. + * This file was forked from the source code for the compat package. + * * @author Nicholas C. Zakas + * @author Kirk Waiblinger */ //----------------------------------------------------------------------------- @@ -14,7 +17,7 @@ import path from "node:path"; // Types //----------------------------------------------------------------------------- -/** @typedef {import("@eslint/core").ConfigObject} FlatConfig */ +/** @typedef {import("@eslint/core").ConfigObject} Config */ //----------------------------------------------------------------------------- // Exports @@ -67,25 +70,123 @@ export function convertIgnorePatternToMinimatch(pattern) { } /** + * @param {string} ignoreFilePath + * @returns {string[]} + */ +function ignoreFilePathToPatterns(ignoreFilePath) { + const ignoreFile = fs.readFileSync(ignoreFilePath, "utf8"); + const lines = ignoreFile.split(/\r?\n/u); + + return lines + .map(line => line.trim()) + .filter(line => line && !line.startsWith("#")) + .map(convertIgnorePatternToMinimatch); +} + +/** + * @override + * + * Reads ignore files and returns objects with the ignore patterns. + * + * @param {string[]} ignoreFilePathArg + * @param {object} [options] + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. + * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd + * - mode: "gitignore": Interprets the ignore patterns relative to the file + * + * @param {string} [options.name] The name to give the output config objects. + * + * @returns {Config[]} + */ + +/** + * @override + * * Reads an ignore file and returns an object with the ignore patterns. - * @param {string} ignoreFilePath The absolute path to the ignore file. - * @param {string} [name] The name of the ignore file config. - * @returns {FlatConfig} An object with an `ignores` property that is an array of ignore patterns. - * @throws {Error} If the ignore file path is not an absolute path. + * + * @param {string} ignoreFilePathArg + * @param {object} [options] + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. + * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd + * - mode: "gitignore": Interprets the ignore patterns relative to the file + * + * @param {string} [options.name] The name to give the output config object. + * + * @returns {Config} + */ + +/** + * @override + * + * Reads an ignore file(s) and returns an object(s) with the ignore patterns. + * + * @param {string[] | string} ignoreFilePathArg + * @param {object} [options] + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. + * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd + * - mode: "gitignore": Interprets the ignore patterns relative to the file + * + * @param {string} [options.name] The name to give the output config objects. + * + * @returns {Config[] | Config} */ -export function includeIgnoreFile(ignoreFilePath, name) { - if (!path.isAbsolute(ignoreFilePath)) { - throw new Error("The ignore file location must be an absolute path."); +export function includeIgnoreFile(ignoreFilePathArg, options) { + const returnSingleObject = !Array.isArray(ignoreFilePathArg); + const ignoreFilePaths = Array.isArray(ignoreFilePathArg) + ? ignoreFilePathArg + : [ignoreFilePathArg]; + for (const ignorePath of ignoreFilePaths) { + if (typeof ignorePath !== "string") { + throw new Error( + "The first argument to `includeIgnoreFile()` should be a string or array of strings", + ); + } + if (!path.isAbsolute(ignorePath)) { + throw new Error( + `The ignore file location must be an absolute path. Received ${ignorePath}`, + ); + } } - const ignoreFile = fs.readFileSync(ignoreFilePath, "utf8"); - const lines = ignoreFile.split(/\r?\n/u); + const { mode, name } = (() => { + // legacy compatibility with @eslint/compat's `includeIgnoreFile` + if (typeof options === "string") { + return { mode: "eslintignore", name: options }; + } + // eslint-disable-next-line no-shadow -- shadowing during initialization + const mode = options?.mode ?? "eslintignore"; + if (!(mode === "gitignore" || mode === "eslintignore")) { + throw new Error( + 'The `mode` option must be specified as "gitignore" or "eslintignore"', + ); + } + + // eslint-disable-next-line no-shadow -- shadowing during initialization + const name = options?.name ?? `Imported .${mode} patterns`; + if (typeof name !== "string") { + throw new Error( + "The `name` option must be specified as a string or omitted.", + ); + } + + return { mode, name }; + })(); + + if (returnSingleObject) { + return { + name, + ignores: ignoreFilePathToPatterns(ignoreFilePathArg), + ...(mode === "gitignore" + ? { basePath: path.dirname(ignoreFilePathArg) } + : {}), + }; + } - return { - name: name || "Imported .gitignore patterns", - ignores: lines - .map(line => line.trim()) - .filter(line => line && !line.startsWith("#")) - .map(convertIgnorePatternToMinimatch), - }; + return ignoreFilePaths.map((ignoreFilePath, i) => ({ + name: `${name} (${i})`, + ignores: ignoreFilePathToPatterns(ignoreFilePath), + ...(mode === "gitignore" + ? { basePath: path.dirname(ignoreFilePath) } + : {}), + })); } diff --git a/packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt b/packages/config-helpers/tests/fixtures/ignore-files/a-folder/gitignore2.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index 60a48a8a6..0669b6b3c 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -1,5 +1,7 @@ /** - * @filedescription Fixup tests + * @filedescription Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` + * @author Nicholas C. Zakas + * @author Kirk Waiblinger */ //----------------------------------------------------------------------------- @@ -17,7 +19,7 @@ import { fileURLToPath } from "node:url"; // Tests //----------------------------------------------------------------------------- -describe("@eslint/compat", () => { +describe("@eslint/config-helpers", () => { describe("convertIgnorePatternToMinimatch", () => { const tests = [ ["", ""], @@ -58,12 +60,13 @@ describe("@eslint/compat", () => { }); describe("includeIgnoreFile", () => { - it("should throw an error when a relative path is passed", () => { - const ignoreFilePath = - "../tests/fixtures/ignore-files/gitignore1.txt"; + it("should throw an error when an array of relative paths is passed", () => { + const ignoreFilePath = [ + "../tests/fixtures/ignore-files/gitignore1.txt", + ]; assert.throws(() => { includeIgnoreFile(ignoreFilePath); - }, /The ignore file location must be an absolute path./u); + }, /The ignore file location must be an absolute path. Received .*/u); }); it("should return an object with an `ignores` property", () => { @@ -73,7 +76,13 @@ describe("@eslint/compat", () => { import.meta.url, ), ); - const result = includeIgnoreFile(ignoreFilePath); + const result = includeIgnoreFile(ignoreFilePath, { + mode: "gitignore", + }); + const basePath = fileURLToPath( + new URL("../tests/fixtures/ignore-files", import.meta.url), + ); + assert.deepStrictEqual(result, { name: "Imported .gitignore patterns", ignores: [ @@ -86,6 +95,7 @@ describe("@eslint/compat", () => { "*/foo.js", "dir/**/*", ], + basePath, }); }); @@ -113,3 +123,64 @@ describe("@eslint/compat", () => { }); }); }); + +// These tests ensure that the `includeIgnoreFile` is a superset of the +// functionality of the `includeIgnoreFile` in `@eslint/compat`. The only +// discrepancy is the default name has been changed to reflect that it is +// really eslintignore mode by default. +describe("`includeIgnoreFile` compat with @eslint/compat", () => { + it("should throw an error when a relative path is passed", () => { + const ignoreFilePath = "../tests/fixtures/ignore-files/gitignore1.txt"; + assert.throws(() => { + includeIgnoreFile(ignoreFilePath); + }, /The ignore file location must be an absolute path./u); + }); + + it("should return an object with an `ignores` property", () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + const result = includeIgnoreFile(ignoreFilePath); + assert.deepStrictEqual(result, { + name: "Imported .eslintignore patterns", + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }); + }); + + it("should return an object with a custom name", () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + const result = includeIgnoreFile(ignoreFilePath, "Custom Name"); + assert.deepStrictEqual(result, { + name: "Custom Name", + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }); + }); + + it("should "); +}); From 66b559741d04c160c4dbe4f4d1d351e74fad8077 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:39:54 -0600 Subject: [PATCH 05/35] lol why is that even allowed --- packages/config-helpers/tests/ignore-file.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index 0669b6b3c..f11e30cdc 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -181,6 +181,4 @@ describe("`includeIgnoreFile` compat with @eslint/compat", () => { ], }); }); - - it("should "); }); From 8caf15a15fdc8e4026be21cb62e184e6761607d5 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:34:40 -0600 Subject: [PATCH 06/35] feedback --- .../config-helpers/tests/ignore-file.test.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index f11e30cdc..1e6551e20 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -121,6 +121,53 @@ describe("@eslint/config-helpers", () => { ], }); }); + + it("should handle when both name and mode are specified", () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + const basePath = fileURLToPath( + new URL("../tests/fixtures/ignore-files", import.meta.url), + ); + + const result = includeIgnoreFile([ignoreFilePath, ignoreFilePath], { + mode: "gitignore", + name: "Custom Name", + }); + assert.deepStrictEqual(result, [ + { + name: "Custom Name (0)", + basePath, + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }, + { + name: "Custom Name (1)", + basePath, + ignores: [ + "**/node_modules", + "!fixtures/node_modules", + "dist", + "**/*.log", + "**/.cache/", + ".vuepress/dist", + "*/foo.js", + "dir/**/*", + ], + }, + ]); + }); }); }); From 45dd1b155c3da9cc2da5beb1b2ad2aee716e8ef4 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:40:33 -0600 Subject: [PATCH 07/35] extract to function --- packages/config-helpers/src/ignore-file.js | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 5b25a94b2..7f90295f6 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -83,6 +83,35 @@ function ignoreFilePathToPatterns(ignoreFilePath) { .map(convertIgnorePatternToMinimatch); } +/** + * Helper to parse and validate the options to `includeIgnoreFile()` + * + * @param {{ mode?: unknown, name?: unknown } | undefined} options + * @returns {{ mode: "eslintignore" | "gitignore", name: string }} + */ +function parseOptions(options) { + // legacy compatibility with @eslint/compat's `includeIgnoreFile` + if (typeof options === "string") { + return { mode: "eslintignore", name: options }; + } + + const mode = options?.mode ?? "eslintignore"; + if (!(mode === "gitignore" || mode === "eslintignore")) { + throw new Error( + 'The `mode` option must be specified as "gitignore" or "eslintignore"', + ); + } + + const name = options?.name ?? `Imported .${mode} patterns`; + if (typeof name !== "string") { + throw new Error( + "The `name` option must be specified as a string or omitted.", + ); + } + + return { mode, name }; +} + /** * @override * @@ -148,29 +177,7 @@ export function includeIgnoreFile(ignoreFilePathArg, options) { } } - const { mode, name } = (() => { - // legacy compatibility with @eslint/compat's `includeIgnoreFile` - if (typeof options === "string") { - return { mode: "eslintignore", name: options }; - } - // eslint-disable-next-line no-shadow -- shadowing during initialization - const mode = options?.mode ?? "eslintignore"; - if (!(mode === "gitignore" || mode === "eslintignore")) { - throw new Error( - 'The `mode` option must be specified as "gitignore" or "eslintignore"', - ); - } - - // eslint-disable-next-line no-shadow -- shadowing during initialization - const name = options?.name ?? `Imported .${mode} patterns`; - if (typeof name !== "string") { - throw new Error( - "The `name` option must be specified as a string or omitted.", - ); - } - - return { mode, name }; - })(); + const { mode, name } = parseOptions(options); if (returnSingleObject) { return { From 819308f6b06c3a1a4b3c7913503ef0c4ae6891d6 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Thu, 9 Apr 2026 02:10:17 -0600 Subject: [PATCH 08/35] Update packages/compat/README.md Co-authored-by: Milos Djermanovic --- packages/compat/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compat/README.md b/packages/compat/README.md index fb43748d7..3305e550c 100644 --- a/packages/compat/README.md +++ b/packages/compat/README.md @@ -150,7 +150,7 @@ module.exports = defineConfig([ ### Including Ignore Files > [!WARNING] -> The `includeIgnoreFile()` exported by this package has been deprecated ([eslint/rewrite#329](https://github.com/eslint/rewrite/issues/329)). Use the `convertIgnorePatternToMinimatch()` function exported by `@eslint/config-helpers` instead (also available at `eslint/config`). This section is only preserved for historical reasons. +> The `includeIgnoreFile()` exported by this package has been deprecated ([eslint/rewrite#329](https://github.com/eslint/rewrite/issues/329)). Use the `includeIgnoreFile()` function exported by `@eslint/config-helpers` instead (also available at `eslint/config`). This section is only preserved for historical reasons. If you were using an alternate ignore file in ESLint v8.x, such as using `--ignore-path .gitignore` on the command line, you can include those patterns programmatically in your config file using the `includeIgnoreFile()` function. From 7f1ab850be2d2f4b0fd1236c3eb512225810b944 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:20:03 -0600 Subject: [PATCH 09/35] fileoverview --- packages/compat/tests/ignore-file.test.js | 2 +- packages/config-helpers/tests/ignore-file.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compat/tests/ignore-file.test.js b/packages/compat/tests/ignore-file.test.js index ff9f1fa81..407110103 100644 --- a/packages/compat/tests/ignore-file.test.js +++ b/packages/compat/tests/ignore-file.test.js @@ -1,5 +1,5 @@ /** - * @filedescription Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` + * @fileoverview Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` * @author Nicholas C. Zakas */ diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index 1e6551e20..517ce39cb 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -1,5 +1,5 @@ /** - * @filedescription Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` + * @fileoverview Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()` * @author Nicholas C. Zakas * @author Kirk Waiblinger */ From 122711f8e7650d840d56a153032198be9d997d4d Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:25:13 -0600 Subject: [PATCH 10/35] use standard markdown --- packages/compat/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/compat/README.md b/packages/compat/README.md index 3305e550c..703ccfe5e 100644 --- a/packages/compat/README.md +++ b/packages/compat/README.md @@ -149,8 +149,7 @@ module.exports = defineConfig([ ### Including Ignore Files -> [!WARNING] -> The `includeIgnoreFile()` exported by this package has been deprecated ([eslint/rewrite#329](https://github.com/eslint/rewrite/issues/329)). Use the `includeIgnoreFile()` function exported by `@eslint/config-helpers` instead (also available at `eslint/config`). This section is only preserved for historical reasons. +**Deprecated**: The `includeIgnoreFile()` exported by this package has been deprecated ([eslint/rewrite#329](https://github.com/eslint/rewrite/issues/329)). Use the `includeIgnoreFile()` function exported by `@eslint/config-helpers` instead (also available at `eslint/config`). This section is only preserved for historical reference. If you were using an alternate ignore file in ESLint v8.x, such as using `--ignore-path .gitignore` on the command line, you can include those patterns programmatically in your config file using the `includeIgnoreFile()` function. From d00b460ce4679fb4d1db2261c53fad89743eadbc Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:26:25 -0600 Subject: [PATCH 11/35] update migrate-config --- packages/migrate-config/package.json | 2 +- packages/migrate-config/src/migrate-config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/migrate-config/package.json b/packages/migrate-config/package.json index 4a308cf06..52d068d84 100644 --- a/packages/migrate-config/package.json +++ b/packages/migrate-config/package.json @@ -47,7 +47,7 @@ "node": "^20.19.0 || ^22.13.0 || >=24" }, "dependencies": { - "@eslint/compat": "^2.0.5", + "@eslint/config-helpers": "^0.5.5", "@eslint/eslintrc": "^3.3.5", "camelcase": "^8.0.0", "espree": "^10.4.0", diff --git a/packages/migrate-config/src/migrate-config.js b/packages/migrate-config/src/migrate-config.js index b1c33fd7b..acc3d55ce 100644 --- a/packages/migrate-config/src/migrate-config.js +++ b/packages/migrate-config/src/migrate-config.js @@ -13,7 +13,7 @@ import { Legacy } from "@eslint/eslintrc"; import camelCase from "camelcase"; import pluginsNeedingCompat from "./compat-plugins.js"; import configsNeedingCompat from "./compat-configs.js"; -import { convertIgnorePatternToMinimatch } from "@eslint/compat"; +import { convertIgnorePatternToMinimatch } from "@eslint/config-helpers"; import * as espree from "espree"; //----------------------------------------------------------------------------- From 15f1c933a9c3d6b4c2d2b14b7c947a87f89e4a1f Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 11 Apr 2026 10:24:36 -0600 Subject: [PATCH 12/35] remove docs for undocumented API --- packages/config-helpers/README.md | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index bd86f58c1..e5596a7ac 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -127,31 +127,6 @@ export default defineConfig( ); ``` -### `convertIgnorePatternToMinimatch()` - -This is used under the hood by `includeIgnoreFile()` to convert patterns found in eslintignore/gitignore files. You can use it to construct your own ignore objects if you wish: - -```js -// eslint.config.js - -import { - defineConfig, - convertIgnorePatternToMinimatch, -} from "@eslint/config-helpers"; - -export default defineConfig( - // approximate implementation of `includeIgnoreFile()` in eslintrc mode. - { - ignores: fs - .readFileSync("some/path", "utf8") - .split(/\r?\n/u) - .map(line => line.trim()) - .filter(line => line && !line.startsWith("#")) - .map(convertIgnorePatternToMinimatch), - }, -); -``` - ## License Apache 2.0 From b691d0f925e8f52bb578826b7d7b64046113c9bc Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 11 Apr 2026 10:24:49 -0600 Subject: [PATCH 13/35] Update packages/config-helpers/README.md Co-authored-by: Milos Djermanovic --- packages/config-helpers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index e5596a7ac..ef0e9f57f 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -76,7 +76,7 @@ export default defineConfig([ ### `includeIgnoreFile()` -The `includeIgnoreFile()` function reads an ignore file (such as a `.gitignore`) and returns a config object with the patterns converted to a global ignores object. Pass the absolute path to the ignore file as the first argument: +The `includeIgnoreFile()` function reads a file with gitignore-style patterns (such as a `.gitignore`) and returns a config object with the patterns converted to a global ignores object. Pass the absolute path to the ignore file as the first argument: ```js // eslint.config.js From 0fae05c71a7ea7a6eff9fb0655340647409bd063 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 11 Apr 2026 10:25:03 -0600 Subject: [PATCH 14/35] Update packages/config-helpers/README.md Co-authored-by: Milos Djermanovic --- packages/config-helpers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index ef0e9f57f..5fe6e5962 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -81,7 +81,7 @@ The `includeIgnoreFile()` function reads a file with gitignore-style patterns (s ```js // eslint.config.js -import { includeIgnoreFile } from "@eslint/config-helpers"; +import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers"; import path from "node:path"; const ignorePath = path.join(import.meta.dirname, ".gitignore"); From 1fb1fd8feda58e1d611efc4dfcb15a6ff5612788 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 11 Apr 2026 10:43:59 -0600 Subject: [PATCH 15/35] Update packages/config-helpers/README.md Co-authored-by: Milos Djermanovic --- packages/config-helpers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index 5fe6e5962..3afbfbec5 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -99,7 +99,7 @@ export default defineConfig( The second argument is an optional options object: - **`mode`** : Controls how ignore patterns are interpreted. - - `"eslintignore"` (default) — patterns are resolved relative to the current working directory, matching the behavior of `.eslintignore` files. + - `"eslintignore"` (default) — patterns are resolved relative to the location of the configuration file. - `"gitignore"` — patterns are resolved relative to the ignore file itself (via `basePath`), matching the behavior of `.gitignore` files. - **`name`** (`string`): A custom name for the resulting config object. From 2ceb6aae3e4327c9a3b07e64e71d31d70a12f15d Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 11 Apr 2026 11:02:58 -0600 Subject: [PATCH 16/35] fix remark about being cwd-relative in the docs --- packages/config-helpers/src/ignore-file.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 7f90295f6..967ee6448 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -119,9 +119,9 @@ function parseOptions(options) { * * @param {string[]} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. - * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd - * - mode: "gitignore": Interprets the ignore patterns relative to the file + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file + * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config objects. * @@ -135,9 +135,9 @@ function parseOptions(options) { * * @param {string} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. - * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd - * - mode: "gitignore": Interprets the ignore patterns relative to the file + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file + * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config object. * @@ -151,9 +151,9 @@ function parseOptions(options) { * * @param {string[] | string} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the cwd or the ignore file. - * - mode: "eslintrc" (default): Interprets the ignore patterns relative to the cwd - * - mode: "gitignore": Interprets the ignore patterns relative to the file + * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file + * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config objects. * From 56277084fe655fb4c1b345b88d09709378ef6b87 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:30:49 -0600 Subject: [PATCH 17/35] gitignoreResolution: boolean --- packages/config-helpers/README.md | 10 ++--- packages/config-helpers/src/ignore-file.js | 40 +++++++++---------- .../config-helpers/tests/ignore-file.test.js | 14 +++---- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index 3afbfbec5..257df4062 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -88,7 +88,7 @@ const ignorePath = path.join(import.meta.dirname, ".gitignore"); export default defineConfig( includeIgnoreFile(ignorePath, { - mode: "gitignore", + gitignoreResolution: true, }), // ... ); @@ -98,9 +98,9 @@ export default defineConfig( The second argument is an optional options object: -- **`mode`** : Controls how ignore patterns are interpreted. - - `"eslintignore"` (default) — patterns are resolved relative to the location of the configuration file. - - `"gitignore"` — patterns are resolved relative to the ignore file itself (via `basePath`), matching the behavior of `.gitignore` files. +- **`gitignoreResolution`** (`boolean`) : Controls how ignore patterns are interpreted. + - `false` (default) — patterns are resolved relative to the location of the configuration file. + - `true` — patterns are resolved relative to the location of the ignore file, matching the behavior of `.gitignore` files. - **`name`** (`string`): A custom name for the resulting config object. For backwards compatibility with `includeIgnoreFile()` from `@eslint/compat`, passing a string instead of an object as the second argument is treated as equivalent to providing a value for `name`. @@ -121,7 +121,7 @@ export default defineConfig( path.join(import.meta.dirname, ".gitignore"), path.join(import.meta.dirname, "packages/lib/.gitignore"), ], - { mode: "gitignore" }, + { gitignoreResolution: true }, ), // ... ); diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 967ee6448..1cd93d95a 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -86,30 +86,30 @@ function ignoreFilePathToPatterns(ignoreFilePath) { /** * Helper to parse and validate the options to `includeIgnoreFile()` * - * @param {{ mode?: unknown, name?: unknown } | undefined} options - * @returns {{ mode: "eslintignore" | "gitignore", name: string }} + * @param {{ gitignoreResolution?: unknown, name?: unknown } | undefined} options + * @returns {{ gitignoreResolution: boolean, name: string }} */ function parseOptions(options) { // legacy compatibility with @eslint/compat's `includeIgnoreFile` if (typeof options === "string") { - return { mode: "eslintignore", name: options }; + return { gitignoreResolution: false, name: options }; } - const mode = options?.mode ?? "eslintignore"; - if (!(mode === "gitignore" || mode === "eslintignore")) { + const gitignoreResolution = options?.gitignoreResolution ?? false; + if (typeof gitignoreResolution !== "boolean") { throw new Error( - 'The `mode` option must be specified as "gitignore" or "eslintignore"', + "The `gitignoreResolution` option must be specified a boolean or omitted", ); } - const name = options?.name ?? `Imported .${mode} patterns`; + const name = options?.name ?? `Imported .gitignore patterns`; if (typeof name !== "string") { throw new Error( "The `name` option must be specified as a string or omitted.", ); } - return { mode, name }; + return { gitignoreResolution, name }; } /** @@ -119,9 +119,9 @@ function parseOptions(options) { * * @param {string[]} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file - * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file + * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file + * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config objects. * @@ -135,9 +135,9 @@ function parseOptions(options) { * * @param {string} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file - * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file + * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file + * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config object. * @@ -151,9 +151,9 @@ function parseOptions(options) { * * @param {string[] | string} ignoreFilePathArg * @param {object} [options] - * @param {"eslintignore" | "gitignore"} [options.mode] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - mode: "eslintignore" (default): Interprets the ignore patterns relative to the config file - * - mode: "gitignore": Interprets the ignore patterns relative to the ignore file + * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file + * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file * * @param {string} [options.name] The name to give the output config objects. * @@ -177,13 +177,13 @@ export function includeIgnoreFile(ignoreFilePathArg, options) { } } - const { mode, name } = parseOptions(options); + const { gitignoreResolution, name } = parseOptions(options); if (returnSingleObject) { return { name, ignores: ignoreFilePathToPatterns(ignoreFilePathArg), - ...(mode === "gitignore" + ...(gitignoreResolution ? { basePath: path.dirname(ignoreFilePathArg) } : {}), }; @@ -192,7 +192,7 @@ export function includeIgnoreFile(ignoreFilePathArg, options) { return ignoreFilePaths.map((ignoreFilePath, i) => ({ name: `${name} (${i})`, ignores: ignoreFilePathToPatterns(ignoreFilePath), - ...(mode === "gitignore" + ...(gitignoreResolution ? { basePath: path.dirname(ignoreFilePath) } : {}), })); diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index 517ce39cb..3bdfc1f11 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -77,7 +77,7 @@ describe("@eslint/config-helpers", () => { ), ); const result = includeIgnoreFile(ignoreFilePath, { - mode: "gitignore", + gitignoreResolution: true, }); const basePath = fileURLToPath( new URL("../tests/fixtures/ignore-files", import.meta.url), @@ -122,7 +122,7 @@ describe("@eslint/config-helpers", () => { }); }); - it("should handle when both name and mode are specified", () => { + it("should handle when both name and gitignoreResolution are specified", () => { const ignoreFilePath = fileURLToPath( new URL( "../tests/fixtures/ignore-files/gitignore1.txt", @@ -134,7 +134,7 @@ describe("@eslint/config-helpers", () => { ); const result = includeIgnoreFile([ignoreFilePath, ignoreFilePath], { - mode: "gitignore", + gitignoreResolution: true, name: "Custom Name", }); assert.deepStrictEqual(result, [ @@ -171,10 +171,8 @@ describe("@eslint/config-helpers", () => { }); }); -// These tests ensure that the `includeIgnoreFile` is a superset of the -// functionality of the `includeIgnoreFile` in `@eslint/compat`. The only -// discrepancy is the default name has been changed to reflect that it is -// really eslintignore mode by default. +// These tests are copied from @eslint/compat and ensure that the `includeIgnoreFile` is a superset of the +// functionality of the `includeIgnoreFile` in `@eslint/compat`. describe("`includeIgnoreFile` compat with @eslint/compat", () => { it("should throw an error when a relative path is passed", () => { const ignoreFilePath = "../tests/fixtures/ignore-files/gitignore1.txt"; @@ -192,7 +190,7 @@ describe("`includeIgnoreFile` compat with @eslint/compat", () => { ); const result = includeIgnoreFile(ignoreFilePath); assert.deepStrictEqual(result, { - name: "Imported .eslintignore patterns", + name: "Imported .gitignore patterns", ignores: [ "**/node_modules", "!fixtures/node_modules", From c8e16b619c4d7fde91a141ec50b003acd3667456 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:36:55 -0600 Subject: [PATCH 18/35] tweak doc --- packages/config-helpers/src/ignore-file.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 1cd93d95a..f9f551832 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -117,7 +117,7 @@ function parseOptions(options) { * * Reads ignore files and returns objects with the ignore patterns. * - * @param {string[]} ignoreFilePathArg + * @param {string[]} ignoreFilePathArg The paths of ignore files to include * @param {object} [options] * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file @@ -133,7 +133,7 @@ function parseOptions(options) { * * Reads an ignore file and returns an object with the ignore patterns. * - * @param {string} ignoreFilePathArg + * @param {string} ignoreFilePathArg The path of the ignore file to include * @param {object} [options] * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file @@ -149,13 +149,13 @@ function parseOptions(options) { * * Reads an ignore file(s) and returns an object(s) with the ignore patterns. * - * @param {string[] | string} ignoreFilePathArg + * @param {string[] | string} ignoreFilePathArg The path(s) of the ignore file(s) to include. * @param {object} [options] * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file * - * @param {string} [options.name] The name to give the output config objects. + * @param {string} [options.name] The name to give the output config object(s). * * @returns {Config[] | Config} */ From 5f6dc2e6061cc6580b4ee0366908dbfcb1faf26e Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:52:02 -0600 Subject: [PATCH 19/35] overload correctly --- packages/config-helpers/src/ignore-file.js | 20 ++++++++++-- .../config-helpers/tests/types/types.test.ts | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index f9f551832..a81296bd9 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -113,7 +113,7 @@ function parseOptions(options) { } /** - * @override + * @overload * * Reads ignore files and returns objects with the ignore patterns. * @@ -129,7 +129,7 @@ function parseOptions(options) { */ /** - * @override + * @overload * * Reads an ignore file and returns an object with the ignore patterns. * @@ -145,7 +145,7 @@ function parseOptions(options) { */ /** - * @override + * @overload * * Reads an ignore file(s) and returns an object(s) with the ignore patterns. * @@ -159,6 +159,20 @@ function parseOptions(options) { * * @returns {Config[] | Config} */ + +/** + * Reads an ignore file(s) and returns an object(s) with the ignore patterns. + * + * @param {string[] | string} ignoreFilePathArg The path(s) of the ignore file(s) to include. + * @param {object} [options] + * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. + * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file + * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file + * + * @param {string} [options.name] The name to give the output config object(s). + * + * @returns {Config[] | Config} + */ export function includeIgnoreFile(ignoreFilePathArg, options) { const returnSingleObject = !Array.isArray(ignoreFilePathArg); const ignoreFilePaths = Array.isArray(ignoreFilePathArg) diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index c8f5c0e88..84083340a 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -13,6 +13,7 @@ import { type ConfigWithExtends, type ExtensionConfigObject, globalIgnores, + includeIgnoreFile, } from "@eslint/config-helpers"; //----------------------------------------------------------------------------- @@ -229,3 +230,34 @@ globalIgnores([1]); globalIgnores(["node_modules"], 1); // #endregion globalIgnores + +//----------------------------------------------------------------------------- +// Tests for includeIgnoreFile() +//----------------------------------------------------------------------------- + +// #region includeIgnoreFile + +// string path should return a single config object + +includeIgnoreFile(".gitignore").ignores; +includeIgnoreFile(".gitignore", {}).ignores; +includeIgnoreFile(".gitignore", { gitignoreResolution: true, name: "falafel" }) + .ignores; + +// array of string paths should return an array of config objects +includeIgnoreFile([".gitignore", ".eslintignore"]).map( + config => config.ignores, +); + +declare const pathOrPaths: string | string[]; +includeIgnoreFile(pathOrPaths, { gitignoreResolution: true, name: "falafel" }); +// @ts-expect-error -- return type shouldn't be able to access field of config object +includeIgnoreFile(pathOrPaths, { gitignoreResolution: true, name: "falafel" }) + .ignores; +// @ts-expect-error -- return type shouldn't be able to access array method +includeIgnoreFile(pathOrPaths, { + gitignoreResolution: true, + name: "falafel", +}).map(config => config.ignores); + +// #endregion includeIgnoreFile From 729f667b5d5a6925953cd6bf2fe1859f482235e9 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:54:44 -0600 Subject: [PATCH 20/35] cleanup --- packages/config-helpers/tests/types/types.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index 84083340a..ace493545 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -251,13 +251,16 @@ includeIgnoreFile([".gitignore", ".eslintignore"]).map( declare const pathOrPaths: string | string[]; includeIgnoreFile(pathOrPaths, { gitignoreResolution: true, name: "falafel" }); -// @ts-expect-error -- return type shouldn't be able to access field of config object + +// prettier-ignore includeIgnoreFile(pathOrPaths, { gitignoreResolution: true, name: "falafel" }) + // @ts-expect-error -- return type shouldn't be able to access field of config object .ignores; -// @ts-expect-error -- return type shouldn't be able to access array method includeIgnoreFile(pathOrPaths, { gitignoreResolution: true, name: "falafel", -}).map(config => config.ignores); +}) + // @ts-expect-error -- return type shouldn't be able to access array method + .map(config => config.ignores); // #endregion includeIgnoreFile From 21aff8abd324e9f0bd8fa2d24e60e722ddd6396a Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:58:54 -0600 Subject: [PATCH 21/35] remove unused dev dep --- packages/migrate-config/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/migrate-config/package.json b/packages/migrate-config/package.json index 52d068d84..fce48ab0e 100644 --- a/packages/migrate-config/package.json +++ b/packages/migrate-config/package.json @@ -40,8 +40,7 @@ }, "homepage": "https://github.com/eslint/rewrite/tree/main/packages/migrate-config#readme", "devDependencies": { - "@eslint/core": "^1.2.1", - "eslint": "^10.0.3" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" From 8dabd37001a1f971092c2aadbc52f47e174ef400 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:45:14 -0600 Subject: [PATCH 22/35] update stale comment --- packages/compat/src/ignore-file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compat/src/ignore-file.js b/packages/compat/src/ignore-file.js index 1f26476ce..d8af09112 100644 --- a/packages/compat/src/ignore-file.js +++ b/packages/compat/src/ignore-file.js @@ -26,7 +26,7 @@ import path from "node:path"; * @returns {string} The converted pattern. * * @deprecated Use the `convertIgnorePatternToMinimatch()` function exported by - * `@eslint/config-helpers` instead (also available at `eslint/config`). + * `@eslint/config-helpers` instead. */ export function convertIgnorePatternToMinimatch(pattern) { const isNegated = pattern.startsWith("!"); From ed0e79bce2875d1d159dbea70817f00e0423ce27 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 15 Apr 2026 11:02:47 -0600 Subject: [PATCH 23/35] update typing and jsdoc --- packages/config-helpers/src/ignore-file.js | 49 ++++++++----------- .../config-helpers/tests/types/types.test.ts | 6 +++ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index a81296bd9..6a36a842c 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -19,6 +19,21 @@ import path from "node:path"; /** @typedef {import("@eslint/core").ConfigObject} Config */ +/** + * @typedef {object} IncludeIgnoreFileOptionsObject + * @property {boolean} [gitignoreResolution] Whether to interpret the contents of an ignore file relative to the config file or the ignore file. + * - gitignoreResolution: false (default): Interprets ignore patterns relative to the config file + * - gitignoreResolution: true: Interprets the ignore patterns in a file relative to the ignore file + * @property {string} [name] The name to give the output config object(s). + */ + +/** + * Options for `includeIgnoreFile()`. May be provided as an object or, for + * legacy compatibility with `@eslint/compat`, as a string which is treated as + * the `name` option. + * @typedef {IncludeIgnoreFileOptionsObject | string} IncludeIgnoreFileOptions + */ + //----------------------------------------------------------------------------- // Exports //----------------------------------------------------------------------------- @@ -86,7 +101,7 @@ function ignoreFilePathToPatterns(ignoreFilePath) { /** * Helper to parse and validate the options to `includeIgnoreFile()` * - * @param {{ gitignoreResolution?: unknown, name?: unknown } | undefined} options + * @param {string | { gitignoreResolution?: unknown, name?: unknown } | undefined} options * @returns {{ gitignoreResolution: boolean, name: string }} */ function parseOptions(options) { @@ -118,13 +133,7 @@ function parseOptions(options) { * Reads ignore files and returns objects with the ignore patterns. * * @param {string[]} ignoreFilePathArg The paths of ignore files to include - * @param {object} [options] - * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file - * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file - * - * @param {string} [options.name] The name to give the output config objects. - * + * @param {IncludeIgnoreFileOptions} [options] * @returns {Config[]} */ @@ -134,13 +143,7 @@ function parseOptions(options) { * Reads an ignore file and returns an object with the ignore patterns. * * @param {string} ignoreFilePathArg The path of the ignore file to include - * @param {object} [options] - * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file - * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file - * - * @param {string} [options.name] The name to give the output config object. - * + * @param {IncludeIgnoreFileOptions} [options] * @returns {Config} */ @@ -150,13 +153,7 @@ function parseOptions(options) { * Reads an ignore file(s) and returns an object(s) with the ignore patterns. * * @param {string[] | string} ignoreFilePathArg The path(s) of the ignore file(s) to include. - * @param {object} [options] - * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file - * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file - * - * @param {string} [options.name] The name to give the output config object(s). - * + * @param {IncludeIgnoreFileOptions} [options] * @returns {Config[] | Config} */ @@ -164,13 +161,7 @@ function parseOptions(options) { * Reads an ignore file(s) and returns an object(s) with the ignore patterns. * * @param {string[] | string} ignoreFilePathArg The path(s) of the ignore file(s) to include. - * @param {object} [options] - * @param {boolean} [options.gitignoreResolution] Whether to interpret the contents of the ignore file relative to the config file or the ignore file. - * - gitignoreResolution: false (default): Interprets the ignore patterns relative to the config file - * - gitignoreResolution: true: Interprets the ignore patterns relative to the ignore file - * - * @param {string} [options.name] The name to give the output config object(s). - * + * @param {IncludeIgnoreFileOptions} [options] * @returns {Config[] | Config} */ export function includeIgnoreFile(ignoreFilePathArg, options) { diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index ace493545..83666495c 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -263,4 +263,10 @@ includeIgnoreFile(pathOrPaths, { // @ts-expect-error -- return type shouldn't be able to access array method .map(config => config.ignores); +// should be able to provide a string options argument for compatibility reasons. +includeIgnoreFile("foo", "string-name"); + +// @ts-expect-error -- options should not a number. +includeIgnoreFile("foo", 22); + // #endregion includeIgnoreFile From 4a63985a1229a49cc1e5ba9249178af661481a6d Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:30:32 -0600 Subject: [PATCH 24/35] Update packages/config-helpers/src/ignore-file.js Co-authored-by: Francesco Trotta --- packages/config-helpers/src/ignore-file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 6a36a842c..7dc201cbc 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -142,7 +142,7 @@ function parseOptions(options) { * * Reads an ignore file and returns an object with the ignore patterns. * - * @param {string} ignoreFilePathArg The path of the ignore file to include + * @param {string} ignoreFilePathArg The path of the ignore file to include. * @param {IncludeIgnoreFileOptions} [options] * @returns {Config} */ From 98141bd95c1d0b507282c3fb0c8fcfbf61af4983 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:31:00 -0600 Subject: [PATCH 25/35] Update packages/config-helpers/src/ignore-file.js Co-authored-by: Francesco Trotta --- packages/config-helpers/src/ignore-file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 7dc201cbc..027035bb7 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -132,7 +132,7 @@ function parseOptions(options) { * * Reads ignore files and returns objects with the ignore patterns. * - * @param {string[]} ignoreFilePathArg The paths of ignore files to include + * @param {string[]} ignoreFilePathArg The paths of ignore files to include. * @param {IncludeIgnoreFileOptions} [options] * @returns {Config[]} */ From a3af3c2a954c483dad038716e05db16f35ea11ed Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:47:42 -0600 Subject: [PATCH 26/35] deprecated --- packages/compat/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compat/README.md b/packages/compat/README.md index 03a82c5d8..25b7ccd5d 100644 --- a/packages/compat/README.md +++ b/packages/compat/README.md @@ -33,7 +33,7 @@ This package exports the following functions in both ESM and CommonJS format: - `fixupRule(rule)` - wraps the given rule in a compatibility layer and returns the result - `fixupPluginRules(plugin)` - wraps each rule in the given plugin using `fixupRule()` and returns a new object that represents the plugin with the fixed-up rules - `fixupConfigRules(configs)` - wraps all plugins found in an array of config objects using `fixupPluginRules()` -- `includeIgnoreFile(path)` - reads an ignore file (like `.gitignore`) and converts the patterns into the correct format for the config file +- `includeIgnoreFile(path)` (deprecated) - reads an ignore file (like `.gitignore`) and converts the patterns into the correct format for the config file ### Fixing Rules From 5ec1eb1cf663f5e32babf4941947d987512369f5 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:48:21 -0600 Subject: [PATCH 27/35] Update packages/config-helpers/tests/types/types.test.ts Co-authored-by: Francesco Trotta --- packages/config-helpers/tests/types/types.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index 83666495c..23174a2c5 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -266,7 +266,7 @@ includeIgnoreFile(pathOrPaths, { // should be able to provide a string options argument for compatibility reasons. includeIgnoreFile("foo", "string-name"); -// @ts-expect-error -- options should not a number. +// @ts-expect-error -- options should not be a number. includeIgnoreFile("foo", 22); // #endregion includeIgnoreFile From ab0c58e3cf5e7d6c31d7522b3b11ff86f006723d Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:52:06 -0600 Subject: [PATCH 28/35] convertIgnorePatternToMinimatch type tests --- packages/config-helpers/tests/types/types.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index 23174a2c5..8709921d2 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -14,6 +14,7 @@ import { type ExtensionConfigObject, globalIgnores, includeIgnoreFile, + convertIgnorePatternToMinimatch, } from "@eslint/config-helpers"; //----------------------------------------------------------------------------- @@ -270,3 +271,17 @@ includeIgnoreFile("foo", "string-name"); includeIgnoreFile("foo", 22); // #endregion includeIgnoreFile + +//----------------------------------------------------------------------------- +// Tests for convertIgnorePatternToMinimatch() +//----------------------------------------------------------------------------- + +// #region convertIgnorePatternToMinimatch + +// should be string => string. +convertIgnorePatternToMinimatch("foo") satisfies string; + +// @ts-expect-error -- input should be a string. +convertIgnorePatternToMinimatch(12345); + +// #endregion convertIgnorePatternToMinimatch From c3a24c2186c8586ca72a02a366096b3ae5c05ef7 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:53:51 -0600 Subject: [PATCH 29/35] add redundant test that exports are exported --- packages/config-helpers/tests/index.test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/config-helpers/tests/index.test.js b/packages/config-helpers/tests/index.test.js index 5aa842f0a..9bf2a3d2b 100644 --- a/packages/config-helpers/tests/index.test.js +++ b/packages/config-helpers/tests/index.test.js @@ -21,4 +21,15 @@ describe("index", () => { it("should export globalIgnores()", () => { assert.strictEqual(typeof api.globalIgnores, "function"); }); + + it("should export includeIgnoreFile()", () => { + assert.strictEqual(typeof api.includeIgnoreFile, "function"); + }); + + it("should export convertIgnorePatternToMinimatch()", () => { + assert.strictEqual( + typeof api.convertIgnorePatternToMinimatch, + "function", + ); + }); }); From a49ef42a9389c1c5904df9067b740d95c34a3067 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:55:23 -0600 Subject: [PATCH 30/35] true placeholder strings in type tests --- packages/config-helpers/tests/types/types.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/config-helpers/tests/types/types.test.ts b/packages/config-helpers/tests/types/types.test.ts index 8709921d2..24f89ed59 100644 --- a/packages/config-helpers/tests/types/types.test.ts +++ b/packages/config-helpers/tests/types/types.test.ts @@ -240,13 +240,13 @@ globalIgnores(["node_modules"], 1); // string path should return a single config object -includeIgnoreFile(".gitignore").ignores; -includeIgnoreFile(".gitignore", {}).ignores; -includeIgnoreFile(".gitignore", { gitignoreResolution: true, name: "falafel" }) +includeIgnoreFile("some-string").ignores; +includeIgnoreFile("some-string", {}).ignores; +includeIgnoreFile("some-string", { gitignoreResolution: true, name: "falafel" }) .ignores; // array of string paths should return an array of config objects -includeIgnoreFile([".gitignore", ".eslintignore"]).map( +includeIgnoreFile(["some-string", "some-other-string"]).map( config => config.ignores, ); @@ -268,7 +268,7 @@ includeIgnoreFile(pathOrPaths, { includeIgnoreFile("foo", "string-name"); // @ts-expect-error -- options should not be a number. -includeIgnoreFile("foo", 22); +includeIgnoreFile("bar", 22); // #endregion includeIgnoreFile From e2fd6b476e46e3d6070d4b881e2b22af8c569408 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:17:34 -0600 Subject: [PATCH 31/35] tweak input validation --- packages/config-helpers/src/ignore-file.js | 11 +++++++++-- packages/config-helpers/tests/ignore-file.test.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index 027035bb7..aeeb83d17 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -110,14 +110,21 @@ function parseOptions(options) { return { gitignoreResolution: false, name: options }; } - const gitignoreResolution = options?.gitignoreResolution ?? false; + const optionsObject = options ?? {}; + if (typeof optionsObject !== "object" || Array.isArray(optionsObject)) { + throw new Error( + "The options argument to `includeIgnoreFile()` should be an object or a string.", + ); + } + + const gitignoreResolution = optionsObject.gitignoreResolution ?? false; if (typeof gitignoreResolution !== "boolean") { throw new Error( "The `gitignoreResolution` option must be specified a boolean or omitted", ); } - const name = options?.name ?? `Imported .gitignore patterns`; + const name = optionsObject.name ?? `Imported .gitignore patterns`; if (typeof name !== "string") { throw new Error( "The `name` option must be specified as a string or omitted.", diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index 3bdfc1f11..f307d60dd 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -168,6 +168,21 @@ describe("@eslint/config-helpers", () => { }, ]); }); + + // convert above to for ... of loop + for (const value of [true, 1, 123n, [1, 2, 3]]) { + it(`should throw an error when the second argument is ${value}`, () => { + const ignoreFilePath = fileURLToPath( + new URL( + "../tests/fixtures/ignore-files/gitignore1.txt", + import.meta.url, + ), + ); + assert.throws(() => { + includeIgnoreFile(ignoreFilePath, value); + }, /The options argument to `includeIgnoreFile\(\)` should be an object or a string./u); + }); + } }); }); From b9ddf418e081fa6de70b6d176275c168edb6b2e7 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:05:19 -0600 Subject: [PATCH 32/35] DRY tests and TypeErrors --- packages/config-helpers/src/ignore-file.js | 8 +- .../config-helpers/tests/ignore-file.test.js | 87 +++++++++---------- 2 files changed, 45 insertions(+), 50 deletions(-) diff --git a/packages/config-helpers/src/ignore-file.js b/packages/config-helpers/src/ignore-file.js index aeeb83d17..2e1674e3d 100644 --- a/packages/config-helpers/src/ignore-file.js +++ b/packages/config-helpers/src/ignore-file.js @@ -112,21 +112,21 @@ function parseOptions(options) { const optionsObject = options ?? {}; if (typeof optionsObject !== "object" || Array.isArray(optionsObject)) { - throw new Error( + throw new TypeError( "The options argument to `includeIgnoreFile()` should be an object or a string.", ); } const gitignoreResolution = optionsObject.gitignoreResolution ?? false; if (typeof gitignoreResolution !== "boolean") { - throw new Error( + throw new TypeError( "The `gitignoreResolution` option must be specified a boolean or omitted", ); } const name = optionsObject.name ?? `Imported .gitignore patterns`; if (typeof name !== "string") { - throw new Error( + throw new TypeError( "The `name` option must be specified as a string or omitted.", ); } @@ -178,7 +178,7 @@ export function includeIgnoreFile(ignoreFilePathArg, options) { : [ignoreFilePathArg]; for (const ignorePath of ignoreFilePaths) { if (typeof ignorePath !== "string") { - throw new Error( + throw new TypeError( "The first argument to `includeIgnoreFile()` should be a string or array of strings", ); } diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index f307d60dd..e9f5dae55 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -19,6 +19,15 @@ import { fileURLToPath } from "node:url"; // Tests //----------------------------------------------------------------------------- +const gitignore1FixturePathRelative = + "../tests/fixtures/ignore-files/gitignore1.txt"; +const gitignore1FixturePathAbsolute = fileURLToPath( + import.meta.resolve(gitignore1FixturePathRelative), +); +const gitignore1FixtureDir = fileURLToPath( + import.meta.resolve("../tests/fixtures/ignore-files"), +); + describe("@eslint/config-helpers", () => { describe("convertIgnorePatternToMinimatch", () => { const tests = [ @@ -61,27 +70,22 @@ describe("@eslint/config-helpers", () => { describe("includeIgnoreFile", () => { it("should throw an error when an array of relative paths is passed", () => { - const ignoreFilePath = [ - "../tests/fixtures/ignore-files/gitignore1.txt", - ]; - assert.throws(() => { - includeIgnoreFile(ignoreFilePath); - }, /The ignore file location must be an absolute path. Received .*/u); + assert.throws( + () => { + includeIgnoreFile([gitignore1FixturePathRelative]); + }, + { + name: "Error", + message: + /The ignore file location must be an absolute path. Received .*/u, + }, + ); }); it("should return an object with an `ignores` property", () => { - const ignoreFilePath = fileURLToPath( - new URL( - "../tests/fixtures/ignore-files/gitignore1.txt", - import.meta.url, - ), - ); - const result = includeIgnoreFile(ignoreFilePath, { + const result = includeIgnoreFile(gitignore1FixturePathAbsolute, { gitignoreResolution: true, }); - const basePath = fileURLToPath( - new URL("../tests/fixtures/ignore-files", import.meta.url), - ); assert.deepStrictEqual(result, { name: "Imported .gitignore patterns", @@ -95,18 +99,15 @@ describe("@eslint/config-helpers", () => { "*/foo.js", "dir/**/*", ], - basePath, + basePath: gitignore1FixtureDir, }); }); it("should return an object with a custom name", () => { - const ignoreFilePath = fileURLToPath( - new URL( - "../tests/fixtures/ignore-files/gitignore1.txt", - import.meta.url, - ), + const result = includeIgnoreFile( + gitignore1FixturePathAbsolute, + "Custom Name", ); - const result = includeIgnoreFile(ignoreFilePath, "Custom Name"); assert.deepStrictEqual(result, { name: "Custom Name", ignores: [ @@ -123,24 +124,17 @@ describe("@eslint/config-helpers", () => { }); it("should handle when both name and gitignoreResolution are specified", () => { - const ignoreFilePath = fileURLToPath( - new URL( - "../tests/fixtures/ignore-files/gitignore1.txt", - import.meta.url, - ), - ); - const basePath = fileURLToPath( - new URL("../tests/fixtures/ignore-files", import.meta.url), + const result = includeIgnoreFile( + [gitignore1FixturePathAbsolute, gitignore1FixturePathAbsolute], + { + gitignoreResolution: true, + name: "Custom Name", + }, ); - - const result = includeIgnoreFile([ignoreFilePath, ignoreFilePath], { - gitignoreResolution: true, - name: "Custom Name", - }); assert.deepStrictEqual(result, [ { name: "Custom Name (0)", - basePath, + basePath: gitignore1FixtureDir, ignores: [ "**/node_modules", "!fixtures/node_modules", @@ -154,7 +148,7 @@ describe("@eslint/config-helpers", () => { }, { name: "Custom Name (1)", - basePath, + basePath: gitignore1FixtureDir, ignores: [ "**/node_modules", "!fixtures/node_modules", @@ -172,15 +166,16 @@ describe("@eslint/config-helpers", () => { // convert above to for ... of loop for (const value of [true, 1, 123n, [1, 2, 3]]) { it(`should throw an error when the second argument is ${value}`, () => { - const ignoreFilePath = fileURLToPath( - new URL( - "../tests/fixtures/ignore-files/gitignore1.txt", - import.meta.url, - ), + assert.throws( + () => { + includeIgnoreFile(gitignore1FixturePathAbsolute, value); + }, + { + name: "TypeError", + message: + /The options argument to `includeIgnoreFile\(\)` should be an object or a string./u, + }, ); - assert.throws(() => { - includeIgnoreFile(ignoreFilePath, value); - }, /The options argument to `includeIgnoreFile\(\)` should be an object or a string./u); }); } }); From fa43e29eac771dddbda843f83cb19a18fd1ff991 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:08:43 -0600 Subject: [PATCH 33/35] defineConfig[ --- packages/config-helpers/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index 7110e0a58..af411e45c 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -86,12 +86,12 @@ import path from "node:path"; const ignorePath = path.join(import.meta.dirname, ".gitignore"); -export default defineConfig( +export default defineConfig([ includeIgnoreFile(ignorePath, { gitignoreResolution: true, }), // ... -); +]); ``` #### Options @@ -115,7 +115,7 @@ You can also pass an array of absolute paths to include multiple ignore files at import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers"; import path from "node:path"; -export default defineConfig( +export default defineConfig([ includeIgnoreFile( [ path.join(import.meta.dirname, ".gitignore"), @@ -124,7 +124,7 @@ export default defineConfig( { gitignoreResolution: true }, ), // ... -); +]); ``` ## License From 9d4461c09def50cfbfbaa5f6efd6bfa9e5f51361 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 4 May 2026 09:21:27 -0600 Subject: [PATCH 34/35] Update packages/config-helpers/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 루밀LuMir --- packages/config-helpers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config-helpers/README.md b/packages/config-helpers/README.md index af411e45c..59c6fe7c6 100644 --- a/packages/config-helpers/README.md +++ b/packages/config-helpers/README.md @@ -98,7 +98,7 @@ export default defineConfig([ The second argument is an optional options object: -- **`gitignoreResolution`** (`boolean`) : Controls how ignore patterns are interpreted. +- **`gitignoreResolution`** (`boolean`): Controls how ignore patterns are interpreted. - `false` (default) — patterns are resolved relative to the location of the configuration file. - `true` — patterns are resolved relative to the location of the ignore file, matching the behavior of `.gitignore` files. - **`name`** (`string`): A custom name for the resulting config object. From 8f99a344dfd4c378a1e9c6c5c2ea63170457a0c4 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 4 May 2026 15:25:44 -0600 Subject: [PATCH 35/35] remove copilot prompt --- packages/config-helpers/tests/ignore-file.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/config-helpers/tests/ignore-file.test.js b/packages/config-helpers/tests/ignore-file.test.js index e9f5dae55..3785295bf 100644 --- a/packages/config-helpers/tests/ignore-file.test.js +++ b/packages/config-helpers/tests/ignore-file.test.js @@ -163,7 +163,6 @@ describe("@eslint/config-helpers", () => { ]); }); - // convert above to for ... of loop for (const value of [true, 1, 123n, [1, 2, 3]]) { it(`should throw an error when the second argument is ${value}`, () => { assert.throws(