Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d1118c6
copy functionality from compat
kirkwaiblinger Apr 8, 2026
d973693
deprecate old APIs
kirkwaiblinger Apr 8, 2026
eb8cada
more deprecation documentation
kirkwaiblinger Apr 8, 2026
24e10cb
changes
kirkwaiblinger Apr 8, 2026
66b5597
lol why is that even allowed
kirkwaiblinger Apr 8, 2026
8caf15a
feedback
kirkwaiblinger Apr 8, 2026
45dd1b1
extract to function
kirkwaiblinger Apr 8, 2026
819308f
Update packages/compat/README.md
kirkwaiblinger Apr 9, 2026
7f1ab85
fileoverview
kirkwaiblinger Apr 9, 2026
122711f
use standard markdown
kirkwaiblinger Apr 9, 2026
d00b460
update migrate-config
kirkwaiblinger Apr 10, 2026
15f1c93
remove docs for undocumented API
kirkwaiblinger Apr 11, 2026
b691d0f
Update packages/config-helpers/README.md
kirkwaiblinger Apr 11, 2026
0fae05c
Update packages/config-helpers/README.md
kirkwaiblinger Apr 11, 2026
1fb1fd8
Update packages/config-helpers/README.md
kirkwaiblinger Apr 11, 2026
2ceb6aa
fix remark about being cwd-relative in the docs
kirkwaiblinger Apr 11, 2026
5627708
gitignoreResolution: boolean
kirkwaiblinger Apr 13, 2026
c8e16b6
tweak doc
kirkwaiblinger Apr 13, 2026
5f6dc2e
overload correctly
kirkwaiblinger Apr 13, 2026
729f667
cleanup
kirkwaiblinger Apr 13, 2026
21aff8a
remove unused dev dep
kirkwaiblinger Apr 13, 2026
0095f6d
Merge branch 'main' into include-gitignore-in-path
kirkwaiblinger Apr 13, 2026
8dabd37
update stale comment
kirkwaiblinger Apr 15, 2026
ed0e79b
update typing and jsdoc
kirkwaiblinger Apr 15, 2026
4a63985
Update packages/config-helpers/src/ignore-file.js
kirkwaiblinger Apr 18, 2026
98141bd
Update packages/config-helpers/src/ignore-file.js
kirkwaiblinger Apr 18, 2026
a3af3c2
deprecated
kirkwaiblinger Apr 20, 2026
5ec1eb1
Update packages/config-helpers/tests/types/types.test.ts
kirkwaiblinger Apr 20, 2026
ab0c58e
convertIgnorePatternToMinimatch type tests
kirkwaiblinger Apr 20, 2026
c3a24c2
add redundant test that exports are exported
kirkwaiblinger Apr 20, 2026
a49ef42
true placeholder strings in type tests
kirkwaiblinger Apr 20, 2026
e2fd6b4
tweak input validation
kirkwaiblinger Apr 20, 2026
0f85748
Merge branch 'main' into include-gitignore-in-path
kirkwaiblinger Apr 20, 2026
b9ddf41
DRY tests and TypeErrors
kirkwaiblinger Apr 27, 2026
fa43e29
defineConfig[
kirkwaiblinger Apr 27, 2026
9d4461c
Update packages/config-helpers/README.md
kirkwaiblinger May 4, 2026
8f99a34
remove copilot prompt
kirkwaiblinger May 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/compat/README.md
Comment thread
lumirlumir marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -149,6 +149,8 @@ module.exports = defineConfig([

### Including Ignore Files

**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.

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:
Expand Down
6 changes: 6 additions & 0 deletions packages/compat/src/ignore-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
export function convertIgnorePatternToMinimatch(pattern) {
const isNegated = pattern.startsWith("!");
Expand Down Expand Up @@ -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)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/compat/tests/ignore-file.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* @filedescription Fixup tests
* @fileoverview Tests for `includeIgnoreFile()` and `convertIgnorePatternToMinimatch()`
* @author Nicholas C. Zakas
*/

//-----------------------------------------------------------------------------
Expand Down
53 changes: 53 additions & 0 deletions packages/config-helpers/README.md
Comment thread
lumirlumir marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,59 @@ export default defineConfig([
]);
```

### `includeIgnoreFile()`

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

import { defineConfig, includeIgnoreFile } from "@eslint/config-helpers";
import path from "node:path";

const ignorePath = path.join(import.meta.dirname, ".gitignore");

export default defineConfig([
includeIgnoreFile(ignorePath, {
gitignoreResolution: true,
}),
// ...
]);
```

#### Options

The second argument is an optional options object:

- **`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`.

#### 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"),
],
{ gitignoreResolution: true },
),
// ...
]);
```

## License

Apache 2.0
Expand Down
211 changes: 211 additions & 0 deletions packages/config-helpers/src/ignore-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/**
* @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
*/

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import fs from "node:fs";
import path from "node:path";

//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------

/** @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
*/
Comment thread
mdjermanovic marked this conversation as resolved.

//-----------------------------------------------------------------------------
// 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}`;
}

/**
* @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);
}

/**
* Helper to parse and validate the options to `includeIgnoreFile()`
*
* @param {string | { 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 { gitignoreResolution: false, name: options };
}

const optionsObject = options ?? {};
if (typeof optionsObject !== "object" || Array.isArray(optionsObject)) {
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 TypeError(
"The `gitignoreResolution` option must be specified a boolean or omitted",
);
}
Comment thread
mdjermanovic marked this conversation as resolved.

const name = optionsObject.name ?? `Imported .gitignore patterns`;
if (typeof name !== "string") {
throw new TypeError(
"The `name` option must be specified as a string or omitted.",
);
}

return { gitignoreResolution, name };
}

/**
* @overload
*
* Reads ignore files and returns objects with the ignore patterns.
*
* @param {string[]} ignoreFilePathArg The paths of ignore files to include.
* @param {IncludeIgnoreFileOptions} [options]
* @returns {Config[]}
*/

/**
* @overload
*
* Reads an ignore file and returns an object with the ignore patterns.
*
* @param {string} ignoreFilePathArg The path of the ignore file to include.
* @param {IncludeIgnoreFileOptions} [options]
* @returns {Config}
*/

/**
* @overload
*
* 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 {IncludeIgnoreFileOptions} [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 {IncludeIgnoreFileOptions} [options]
* @returns {Config[] | Config}
*/
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 TypeError(
"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 { gitignoreResolution, name } = parseOptions(options);

if (returnSingleObject) {
return {
name,
ignores: ignoreFilePathToPatterns(ignoreFilePathArg),
...(gitignoreResolution
? { basePath: path.dirname(ignoreFilePathArg) }
: {}),
};
}

return ignoreFilePaths.map((ignoreFilePath, i) => ({
name: `${name} (${i})`,
ignores: ignoreFilePathToPatterns(ignoreFilePath),
...(gitignoreResolution
? { basePath: path.dirname(ignoreFilePath) }
: {}),
}));
}
4 changes: 4 additions & 0 deletions packages/config-helpers/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@

export { defineConfig } from "./define-config.js";
export { globalIgnores } from "./global-ignores.js";
export {
includeIgnoreFile,
convertIgnorePatternToMinimatch,
} from "./ignore-file.js";
Comment thread
mdjermanovic marked this conversation as resolved.
17 changes: 17 additions & 0 deletions packages/config-helpers/tests/fixtures/ignore-files/gitignore1.txt
Original file line number Diff line number Diff line change
@@ -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/**
Loading