@w5s/eslint-config-ignore 1.2.7 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +34 -34
- package/dist/index.js +69 -35
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/eslintIgnores.ts +39 -38
- package/src/index.ts +1 -1
- package/src/internal/convertIgnorePatternToMinimatch.ts +39 -0
- package/src/internal/ignoreFileFind.ts +37 -14
- package/src/internal/ignoreRuleResolve.ts +27 -14
- package/src/meta.ts +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,44 +1,15 @@
|
|
|
1
|
-
//#region src/meta.d.ts
|
|
2
|
-
declare const meta: Readonly<{
|
|
3
|
-
name: string;
|
|
4
|
-
version: string;
|
|
5
|
-
buildNumber: number;
|
|
6
|
-
}>;
|
|
7
|
-
//#endregion
|
|
8
1
|
//#region src/eslintIgnores.d.ts
|
|
9
|
-
/**
|
|
10
|
-
* Create a new eslint configuration object
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // eslint.config.js
|
|
15
|
-
* export default [
|
|
16
|
-
* await eslintIgnores({
|
|
17
|
-
* ignores: [
|
|
18
|
-
* // Add custom paths here
|
|
19
|
-
* ]
|
|
20
|
-
* })
|
|
21
|
-
* ];
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* @param options
|
|
25
|
-
*/
|
|
26
|
-
declare function eslintIgnores(options?: ESLintIgnoreOptions): Promise<ESLintIgnoreConfig>;
|
|
27
2
|
interface ESLintIgnoreConfig {
|
|
28
|
-
/**
|
|
29
|
-
* The configuration name
|
|
30
|
-
*/
|
|
31
|
-
name: string;
|
|
32
3
|
/**
|
|
33
4
|
* The file globs to ignore
|
|
34
5
|
*/
|
|
35
6
|
ignores: Array<string>;
|
|
36
|
-
}
|
|
37
|
-
interface ESLintIgnoreOptions {
|
|
38
7
|
/**
|
|
39
|
-
*
|
|
8
|
+
* The configuration name
|
|
40
9
|
*/
|
|
41
|
-
name
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
interface ESLintIgnoreOptions {
|
|
42
13
|
/**
|
|
43
14
|
* Override current working directory
|
|
44
15
|
*/
|
|
@@ -48,12 +19,41 @@ interface ESLintIgnoreOptions {
|
|
|
48
19
|
* - If passed an array, it appends patterns to the merged ignore list.
|
|
49
20
|
* - If passed a function, it receives the merged list and returns the final array.
|
|
50
21
|
*/
|
|
51
|
-
ignores?: ESLintIgnoreConfig['ignores']
|
|
22
|
+
ignores?: ((ignores: ESLintIgnoreConfig['ignores']) => ESLintIgnoreConfig['ignores']) | ESLintIgnoreConfig['ignores'];
|
|
23
|
+
/**
|
|
24
|
+
* Override configuration name
|
|
25
|
+
*/
|
|
26
|
+
name?: ESLintIgnoreConfig['name'];
|
|
52
27
|
/**
|
|
53
28
|
* Include recommended settings and default ignored files
|
|
54
29
|
*/
|
|
55
30
|
recommended?: boolean | undefined;
|
|
56
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a new eslint configuration object
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* // eslint.config.js
|
|
38
|
+
* export default [
|
|
39
|
+
* await eslintIgnores({
|
|
40
|
+
* ignores: [
|
|
41
|
+
* // Add custom paths here
|
|
42
|
+
* ]
|
|
43
|
+
* })
|
|
44
|
+
* ];
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @param options
|
|
48
|
+
*/
|
|
49
|
+
declare function eslintIgnores(options?: ESLintIgnoreOptions): Promise<ESLintIgnoreConfig>;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/meta.d.ts
|
|
52
|
+
declare const meta: Readonly<{
|
|
53
|
+
buildNumber: number;
|
|
54
|
+
name: string;
|
|
55
|
+
version: string;
|
|
56
|
+
}>;
|
|
57
57
|
//#endregion
|
|
58
58
|
export { ESLintIgnoreConfig, ESLintIgnoreOptions, type ESLintIgnoreOptions as Options, eslintIgnores as default, eslintIgnores, meta };
|
|
59
59
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import nodePath from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import
|
|
4
|
+
import { minimatch } from "minimatch";
|
|
5
5
|
import fs$1 from "node:fs/promises";
|
|
6
|
-
|
|
7
|
-
const meta = Object.freeze({
|
|
8
|
-
name: "@w5s/eslint-config-ignore",
|
|
9
|
-
version: "1.2.7",
|
|
10
|
-
buildNumber: 1
|
|
11
|
-
});
|
|
12
|
-
//#endregion
|
|
6
|
+
import parseGitignore from "parse-gitignore";
|
|
13
7
|
//#region src/defaultIgnores.ts
|
|
14
8
|
const defaultIgnores = [
|
|
15
9
|
"**/package-lock.json",
|
|
@@ -50,32 +44,61 @@ function ignoreFileParse(input) {
|
|
|
50
44
|
return parseGitignore.parse(input).patterns;
|
|
51
45
|
}
|
|
52
46
|
//#endregion
|
|
47
|
+
//#region src/internal/convertIgnorePatternToMinimatch.ts
|
|
48
|
+
/**
|
|
49
|
+
* Converts a gitignore-style pattern to an ESLint-compatible minimatch pattern.
|
|
50
|
+
*
|
|
51
|
+
* Ported from `@eslint/config-helpers` (eslint/rewrite).
|
|
52
|
+
*
|
|
53
|
+
* @see https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/ignore-file.js
|
|
54
|
+
* @param pattern The .eslintignore or .gitignore pattern to convert.
|
|
55
|
+
* @returns {string} The converted minimatch pattern.
|
|
56
|
+
*/
|
|
57
|
+
function convertIgnorePatternToMinimatch(pattern) {
|
|
58
|
+
const isNegated = pattern.startsWith("!");
|
|
59
|
+
const negatedPrefix = isNegated ? "!" : "";
|
|
60
|
+
const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd();
|
|
61
|
+
if ([
|
|
62
|
+
"",
|
|
63
|
+
"**",
|
|
64
|
+
"**/",
|
|
65
|
+
"/**"
|
|
66
|
+
].includes(patternToTest)) return `${negatedPrefix}${patternToTest}`;
|
|
67
|
+
const firstIndexOfSlash = patternToTest.indexOf("/");
|
|
68
|
+
return `${negatedPrefix}${firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1 ? "**/" : ""}${(firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest).replaceAll(/(?=((?:\\.|[^{(])*))\1([{(])/guy, String.raw`$1\$2`)}${patternToTest.endsWith("/**") ? "/*" : ""}`;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
53
71
|
//#region src/internal/ignoreRuleResolve.ts
|
|
72
|
+
const ROOT_PREFIX_PATTERN = /^\.\/?/;
|
|
73
|
+
const normalizePath = (p) => p.replaceAll("\\", "/").replace(ROOT_PREFIX_PATTERN, "");
|
|
54
74
|
/**
|
|
55
|
-
* Resolve a raw ignore rule from a `.gitignore` file into a
|
|
56
|
-
* relative to the configured working directory.
|
|
75
|
+
* Resolve a raw ignore rule from a `.gitignore` file into a flat ESLint
|
|
76
|
+
* minimatch glob relative to the configured working directory.
|
|
57
77
|
*
|
|
58
78
|
* @example
|
|
59
79
|
* ```ts
|
|
60
80
|
* import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';
|
|
61
81
|
*
|
|
62
|
-
* ignoreRuleResolve('.', '
|
|
82
|
+
* ignoreRuleResolve('.', 'out'); // '**\/out'
|
|
63
83
|
* ignoreRuleResolve('.', '/dist'); // 'dist'
|
|
64
|
-
* ignoreRuleResolve('android', '
|
|
65
|
-
* ignoreRuleResolve('android', '
|
|
84
|
+
* ignoreRuleResolve('android', 'build'); // 'android/**\/build'
|
|
85
|
+
* ignoreRuleResolve('android', '/build'); // 'android/build'
|
|
86
|
+
* ignoreRuleResolve('android', '!build'); // '!android/**\/build'
|
|
66
87
|
* ```
|
|
67
88
|
*
|
|
68
89
|
* @internal
|
|
69
90
|
* @param prefix A path prefix that points to the directory containing the `.gitignore` file.
|
|
70
91
|
* @param rule The raw ignore rule parsed from `.gitignore`.
|
|
71
|
-
* @returns A normalized ignore pattern relative to the root `cwd`.
|
|
92
|
+
* @returns {string} A normalized ignore pattern relative to the root `cwd`.
|
|
72
93
|
*/
|
|
73
94
|
function ignoreRuleResolve(prefix, rule) {
|
|
74
95
|
const negated = rule.startsWith("!");
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
const raw = negated ? rule.slice(1) : rule;
|
|
97
|
+
const normalizedPrefix = prefix === "." || prefix === "" ? "" : normalizePath(prefix);
|
|
98
|
+
let converted;
|
|
99
|
+
if (raw.startsWith("/")) converted = convertIgnorePatternToMinimatch(`/${normalizedPrefix ? `${normalizedPrefix}/${raw.slice(1)}` : raw.slice(1)}`);
|
|
100
|
+
else converted = (normalizedPrefix ? `${normalizedPrefix}/` : "") + convertIgnorePatternToMinimatch(raw);
|
|
101
|
+
return negated ? `!${converted}` : converted;
|
|
79
102
|
}
|
|
80
103
|
//#endregion
|
|
81
104
|
//#region src/internal/ignoreFileFind.ts
|
|
@@ -92,25 +115,30 @@ const GITIGNORE_FILE = ".gitignore";
|
|
|
92
115
|
* @param options
|
|
93
116
|
*/
|
|
94
117
|
async function ignoreFileFind(rootDir, options) {
|
|
95
|
-
const excludeDirs = new Set(options?.excludeDirs ?? [
|
|
96
|
-
"node_modules",
|
|
97
|
-
".git",
|
|
98
|
-
"dist",
|
|
99
|
-
"build",
|
|
100
|
-
"out"
|
|
101
|
-
]);
|
|
118
|
+
const excludeDirs = new Set(options?.excludeDirs ?? ["node_modules", ".git"]);
|
|
102
119
|
const maxDepth = options?.maxDepth ?? 8;
|
|
103
120
|
const stopAtGitRoot = options?.stopAtGitRoot ?? true;
|
|
104
121
|
const absoluteRootDir = nodePath.resolve(rootDir);
|
|
105
122
|
const found = /* @__PURE__ */ new Set();
|
|
106
123
|
const normalize = (p) => p.replaceAll("\\", "/");
|
|
107
|
-
function
|
|
124
|
+
function patternMatchesPath(pattern, candidateRel) {
|
|
108
125
|
const normCandidate = normalize(candidateRel);
|
|
126
|
+
const normPattern = normalize(pattern);
|
|
127
|
+
if (minimatch(normCandidate, normPattern, { dot: true })) return true;
|
|
128
|
+
if (minimatch(normCandidate, `${normPattern}/**`, { dot: true })) return true;
|
|
129
|
+
if (normPattern.endsWith("/")) {
|
|
130
|
+
const withoutTrailing = normPattern.slice(0, -1);
|
|
131
|
+
if (minimatch(normCandidate, withoutTrailing, { dot: true })) return true;
|
|
132
|
+
if (minimatch(normCandidate, `${withoutTrailing}/**`, { dot: true })) return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
function lastMatchWins(patterns, candidateRel) {
|
|
109
137
|
let lastMatch = null;
|
|
110
138
|
for (const p of patterns) {
|
|
111
|
-
const
|
|
112
|
-
if (!
|
|
113
|
-
if (
|
|
139
|
+
const pat = p.startsWith("!") ? p.slice(1) : p;
|
|
140
|
+
if (!pat) continue;
|
|
141
|
+
if (patternMatchesPath(pat, candidateRel)) lastMatch = p;
|
|
114
142
|
}
|
|
115
143
|
return lastMatch;
|
|
116
144
|
}
|
|
@@ -155,12 +183,12 @@ async function ignoreFileFind(rootDir, options) {
|
|
|
155
183
|
found.add(gi);
|
|
156
184
|
}
|
|
157
185
|
const queue = [{
|
|
158
|
-
dir: absoluteRootDir,
|
|
159
186
|
depth: 0,
|
|
187
|
+
dir: absoluteRootDir,
|
|
160
188
|
patterns: [...initialPatterns]
|
|
161
189
|
}];
|
|
162
190
|
while (queue.length > 0) {
|
|
163
|
-
const { dir: currentDir,
|
|
191
|
+
const { depth, dir: currentDir, patterns } = queue.shift();
|
|
164
192
|
if (depth > maxDepth) continue;
|
|
165
193
|
let entries;
|
|
166
194
|
try {
|
|
@@ -177,8 +205,8 @@ async function ignoreFileFind(rootDir, options) {
|
|
|
177
205
|
const subdir = nodePath.join(currentDir, ent.name);
|
|
178
206
|
if (isIgnored(combinedPatterns, nodePath.relative(rootDir, subdir))) continue;
|
|
179
207
|
queue.push({
|
|
180
|
-
dir: subdir,
|
|
181
208
|
depth: depth + 1,
|
|
209
|
+
dir: subdir,
|
|
182
210
|
patterns: combinedPatterns
|
|
183
211
|
});
|
|
184
212
|
}
|
|
@@ -215,13 +243,19 @@ async function eslintIgnores(options = {}) {
|
|
|
215
243
|
return patterns.map((pattern) => ignoreRuleResolve(ignoreDirectoryRelative, pattern));
|
|
216
244
|
}));
|
|
217
245
|
const mergedIgnores = [...recommended ? defaultIgnores : [], ...ignoreGlobs.flat()];
|
|
218
|
-
const ignores = typeof options.ignores === "function" ? options.ignores(mergedIgnores) : options.ignores ? [...mergedIgnores, ...options.ignores] : mergedIgnores;
|
|
219
246
|
return {
|
|
220
|
-
|
|
221
|
-
|
|
247
|
+
ignores: typeof options.ignores === "function" ? options.ignores(mergedIgnores) : options.ignores ? [...mergedIgnores, ...options.ignores] : mergedIgnores,
|
|
248
|
+
name: options.name ?? "w5s/eslint-ignore"
|
|
222
249
|
};
|
|
223
250
|
}
|
|
224
251
|
//#endregion
|
|
252
|
+
//#region src/meta.ts
|
|
253
|
+
const meta = Object.freeze({
|
|
254
|
+
buildNumber: 1,
|
|
255
|
+
name: "@w5s/eslint-config-ignore",
|
|
256
|
+
version: "1.3.0"
|
|
257
|
+
});
|
|
258
|
+
//#endregion
|
|
225
259
|
export { eslintIgnores as default, eslintIgnores, meta };
|
|
226
260
|
|
|
227
261
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["fs"],"sources":["../src/meta.ts","../src/defaultIgnores.ts","../src/internal/ignoreFileParse.ts","../src/internal/ignoreRuleResolve.ts","../src/internal/ignoreFileFind.ts","../src/eslintIgnores.ts"],"sourcesContent":["export const meta = Object.freeze({\n // @ts-ignore - these variables are injected at build time\n name: (typeof __PACKAGE_NAME__ === 'undefined' ? '' : __PACKAGE_NAME__) as string,\n // @ts-ignore - these variables are injected at build time\n version: (typeof __PACKAGE_VERSION__ === 'undefined' ? '' : __PACKAGE_VERSION__) as string,\n // @ts-ignore - these variables are injected at build time\n buildNumber: 1 as number, // (typeof __PACKAGE_BUILD_NUMBER__ === 'undefined' ? 0 : __PACKAGE_BUILD_NUMBER__) as number,\n});\n","export const defaultIgnores = [\n // Lock files\n '**/package-lock.json',\n '**/yarn.lock',\n '**/pnpm-lock.yaml',\n '**/bun.lockb',\n\n // Commonly ignored\n '**/output',\n '**/.output',\n '**/coverage',\n '**/temp',\n '**/.temp',\n '**/tmp',\n '**/.tmp',\n '**/.cache',\n\n // Well known extensions to ignore\n '**/*.min.*',\n '**/*.timestamp-*.mjs',\n\n // Framework specific temporary folder\n '.go/',\n '.pnpm-store/',\n '**/.vitepress/cache',\n '**/.vite-inspect',\n '**/.history',\n '**/.nuxt',\n '**/.next',\n '**/.svelte-kit',\n '**/.vercel',\n '**/.idea',\n '**/.yarn',\n '**/__snapshots__/**',\n\n // git submodules (makefile-core / makefile-ci)\n '.modules/',\n\n // AI related\n '**/.context',\n '**/.claude',\n '**/.agents',\n '**/.*/skills',\n];\n","import parseGitignore from 'parse-gitignore';\n\nexport function ignoreFileParse(input: string): Array<string> {\n return parseGitignore.parse(input).patterns;\n};\n","import nodePath from 'node:path';\n\n/**\n * Resolve a raw ignore rule from a `.gitignore` file into a path\n * relative to the configured working directory.\n *\n * @example\n * ```ts\n * import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';\n *\n * ignoreRuleResolve('.', 'dist'); // 'dist'\n * ignoreRuleResolve('.', '/dist'); // 'dist'\n * ignoreRuleResolve('android', 'android-build'); // 'android/android-build'\n * ignoreRuleResolve('android', '!android-build'); // '!android/android-build'\n * ```\n *\n * @internal\n * @param prefix A path prefix that points to the directory containing the `.gitignore` file.\n * @param rule The raw ignore rule parsed from `.gitignore`.\n * @returns A normalized ignore pattern relative to the root `cwd`.\n */\nexport function ignoreRuleResolve(prefix: string, rule: string) {\n const negated = rule.startsWith('!');\n const normalizedPattern = negated ? rule.slice(1) : rule;\n const trimmedPattern = normalizedPattern.startsWith('/')\n ? normalizedPattern.slice(1)\n : normalizedPattern;\n const relativeIgnorePath = nodePath.join(prefix, trimmedPattern);\n\n return negated ? `!${relativeIgnorePath}` : relativeIgnorePath;\n}\n","/* eslint-disable unicorn/no-break-in-nested-loop */\n/* eslint-disable unicorn/prefer-await */\nimport nodePath from 'node:path';\nimport fs from 'node:fs/promises';\nimport { ignoreFileParse } from './ignoreFileParse.js';\nimport { ignoreRuleResolve } from './ignoreRuleResolve.js';\n\nconst GITIGNORE_FILE = '.gitignore';\n\nexport interface IgnoreFileFindOptions {\n /** Directory names to skip when searching downward from `rootDir`. */\n excludeDirs?: string[];\n /** Maximum recursion depth when searching downward. Defaults to 8. */\n maxDepth?: number;\n /** When true (default), stop ancestor traversal when a `.git` directory is found. */\n stopAtGitRoot?: boolean;\n}\n\n/**\n * Find `.gitignore` files relevant to `rootDir`.\n *\n * Behavior:\n * - Searches downwards from `rootDir` (BFS) for `.gitignore` files, skipping `excludeDirs`.\n * - Walks ancestors from `rootDir` up to the filesystem root (and stops at a `.git` folder when `stopAtGitRoot` is true).\n * - Returns relative paths to `rootDir`.\n *\n * @param rootDir\n * @param options\n */\nexport async function ignoreFileFind(\n rootDir: string,\n options?: IgnoreFileFindOptions,\n): Promise<Array<string>> {\n const excludeDirs = new Set(options?.excludeDirs ?? ['node_modules', '.git', 'dist', 'build', 'out']);\n const maxDepth = options?.maxDepth ?? 8;\n const stopAtGitRoot = options?.stopAtGitRoot ?? true;\n\n const absoluteRootDir = nodePath.resolve(rootDir);\n const found = new Set<string>();\n\n // --- Helpers (internal, not exported) ---------------------------------\n const normalize = (p: string) => p.replaceAll('\\\\', '/');\n\n function lastMatchWins(patterns: string[], candidateRel: string): string | null {\n const normCandidate = normalize(candidateRel);\n let lastMatch: string | null = null;\n for (const p of patterns) {\n const neg = p.startsWith('!');\n const pat = neg ? p.slice(1) : p;\n const patNorm = normalize(pat);\n if (!patNorm) continue;\n\n if (normCandidate === patNorm || normCandidate.startsWith(patNorm + '/')) {\n lastMatch = p;\n }\n }\n return lastMatch;\n }\n\n function isIgnored(patterns: string[], candidateRel: string): boolean {\n const m = lastMatchWins(patterns, candidateRel);\n if (!m) return false;\n return !m.startsWith('!');\n }\n\n async function collectAncestorGitignores(startDir: string): Promise<string[]> {\n const files: string[] = [];\n let dir = startDir;\n while (true) {\n const gi = nodePath.join(dir, GITIGNORE_FILE);\n try {\n const stat = await fs.stat(gi).catch(() => null);\n if (stat && stat.isFile()) files.push(gi);\n } catch {\n // ignore\n }\n\n if (stopAtGitRoot) {\n try {\n const gitStat = await fs.stat(nodePath.join(dir, '.git')).catch(() => null);\n if (gitStat && gitStat.isDirectory()) break;\n } catch {\n // ignore\n }\n }\n\n const parent = nodePath.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n // eslint-disable-next-line unicorn/no-array-reverse\n return files.reverse();\n }\n\n async function parseAndResolve(giPath: string): Promise<string[]> {\n try {\n const content = String(await fs.readFile(giPath, 'utf8'));\n const parsed = ignoreFileParse(content);\n const prefixRel = nodePath.relative(rootDir, nodePath.dirname(giPath));\n return parsed.map((p) => ignoreRuleResolve(prefixRel, p));\n } catch {\n return [];\n }\n }\n\n // --- Build initial patterns from ancestor .gitignore files -------------\n const ancestorFiles = await collectAncestorGitignores(absoluteRootDir);\n const initialPatterns: string[] = [];\n for (const gi of ancestorFiles) {\n const parsed = await parseAndResolve(gi);\n if (parsed.length > 0) initialPatterns.push(...parsed);\n found.add(gi);\n }\n\n // --- BFS downward carrying accumulated patterns -----------------------\n const queue: Array<{ dir: string; depth: number; patterns: string[] }> = [\n { dir: absoluteRootDir, depth: 0, patterns: [...initialPatterns] },\n ];\n\n while (queue.length > 0) {\n // eslint-disable-next-line ts/no-non-null-assertion\n const { dir: currentDir, depth, patterns } = queue.shift()!;\n if (depth > maxDepth) continue;\n\n let entries;\n try {\n entries = await fs.readdir(currentDir, { withFileTypes: true });\n } catch {\n continue; // unreadable\n }\n\n // If local .gitignore exists, parse and extend patterns\n\n const local = entries.find((e) => e.isFile() && e.name === GITIGNORE_FILE);\n const combinedPatterns = local ? [...patterns, ...(await parseAndResolve(nodePath.join(currentDir, GITIGNORE_FILE)))] : patterns;\n if (local) found.add(nodePath.join(currentDir, GITIGNORE_FILE));\n\n for (const ent of entries) {\n if (!ent.isDirectory()) continue;\n if (excludeDirs.has(ent.name)) continue;\n const subdir = nodePath.join(currentDir, ent.name);\n const rel = nodePath.relative(rootDir, subdir);\n if (isIgnored(combinedPatterns, rel)) continue;\n queue.push({ dir: subdir, depth: depth + 1, patterns: combinedPatterns });\n }\n }\n\n return [...found].map((p) => nodePath.relative(rootDir, p));\n}\n","import fs from 'node:fs';\nimport nodePath from 'node:path';\nimport process from 'node:process';\nimport { defaultIgnores } from './defaultIgnores.js';\nimport { ignoreFileParse } from './internal/ignoreFileParse.js';\nimport { ignoreFileFind } from './internal/ignoreFileFind.js';\nimport { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';\n\n/**\n * Create a new eslint configuration object\n *\n * @example\n * ```ts\n * // eslint.config.js\n * export default [\n * await eslintIgnores({\n * ignores: [\n * // Add custom paths here\n * ]\n * })\n * ];\n * ```\n *\n * @param options\n */\nexport async function eslintIgnores(options: ESLintIgnoreOptions = {}): Promise<ESLintIgnoreConfig> {\n const cwd = options.cwd ?? process.cwd();\n const recommended = options.recommended ?? true;\n const ignoreFilePaths = await ignoreFileFind(cwd);\n const ignoreGlobs = (await Promise.all(ignoreFilePaths.map(async (ignoreFilePathRelative) => {\n const ignoreFilePath = nodePath.join(cwd, ignoreFilePathRelative);\n const ignoreFileContent = String(await fs.promises.readFile(ignoreFilePath));\n const patterns = ignoreFileParse(ignoreFileContent);\n const ignoreDirectoryRelative = nodePath.dirname(ignoreFilePathRelative);\n\n return patterns.map((pattern) => ignoreRuleResolve(ignoreDirectoryRelative, pattern));\n })));\n\n const mergedIgnores = [\n ...(recommended ? defaultIgnores : []),\n ...ignoreGlobs.flat(),\n ];\n\n const ignores = typeof options.ignores === 'function'\n ? options.ignores(mergedIgnores)\n : options.ignores\n ? [...mergedIgnores, ...options.ignores]\n : mergedIgnores;\n\n return {\n name: options.name ?? 'w5s/eslint-ignore',\n ignores,\n };\n}\n\nexport interface ESLintIgnoreConfig {\n /**\n * The configuration name\n */\n name: string;\n\n /**\n * The file globs to ignore\n */\n ignores: Array<string>;\n}\n\nexport interface ESLintIgnoreOptions {\n /**\n * Override configuration name\n */\n name?: ESLintIgnoreConfig['name'];\n\n /**\n * Override current working directory\n */\n cwd?: string;\n\n /**\n * Override or customize ignore patterns.\n * - If passed an array, it appends patterns to the merged ignore list.\n * - If passed a function, it receives the merged list and returns the final array.\n */\n ignores?: ESLintIgnoreConfig['ignores'] | ((ignores: ESLintIgnoreConfig['ignores']) => ESLintIgnoreConfig['ignores']);\n\n /**\n * Include recommended settings and default ignored files\n */\n recommended?: boolean | undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["fs"],"sources":["../src/defaultIgnores.ts","../src/internal/ignoreFileParse.ts","../src/internal/convertIgnorePatternToMinimatch.ts","../src/internal/ignoreRuleResolve.ts","../src/internal/ignoreFileFind.ts","../src/eslintIgnores.ts","../src/meta.ts"],"sourcesContent":["export const defaultIgnores = [\n // Lock files\n '**/package-lock.json',\n '**/yarn.lock',\n '**/pnpm-lock.yaml',\n '**/bun.lockb',\n\n // Commonly ignored\n '**/output',\n '**/.output',\n '**/coverage',\n '**/temp',\n '**/.temp',\n '**/tmp',\n '**/.tmp',\n '**/.cache',\n\n // Well known extensions to ignore\n '**/*.min.*',\n '**/*.timestamp-*.mjs',\n\n // Framework specific temporary folder\n '.go/',\n '.pnpm-store/',\n '**/.vitepress/cache',\n '**/.vite-inspect',\n '**/.history',\n '**/.nuxt',\n '**/.next',\n '**/.svelte-kit',\n '**/.vercel',\n '**/.idea',\n '**/.yarn',\n '**/__snapshots__/**',\n\n // git submodules (makefile-core / makefile-ci)\n '.modules/',\n\n // AI related\n '**/.context',\n '**/.claude',\n '**/.agents',\n '**/.*/skills',\n];\n","import parseGitignore from 'parse-gitignore';\n\nexport function ignoreFileParse(input: string): Array<string> {\n return parseGitignore.parse(input).patterns;\n};\n","/**\n * Converts a gitignore-style pattern to an ESLint-compatible minimatch pattern.\n *\n * Ported from `@eslint/config-helpers` (eslint/rewrite).\n *\n * @see https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/ignore-file.js\n * @param pattern The .eslintignore or .gitignore pattern to convert.\n * @returns {string} The converted minimatch pattern.\n */\nexport function convertIgnorePatternToMinimatch(pattern: string): string {\n const isNegated = pattern.startsWith('!');\n const negatedPrefix = isNegated ? '!' : '';\n const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd();\n\n // special cases\n if (['', '**', '**/', '/**'].includes(patternToTest)) {\n return `${negatedPrefix}${patternToTest}`;\n }\n\n const firstIndexOfSlash = patternToTest.indexOf('/');\n\n const matchEverywherePrefix = firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1 ? '**/' : '';\n\n const patternWithoutLeadingSlash = firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest;\n\n /*\n * Escape `{` and `(` because in gitignore patterns they are just\n * literal characters without any specific syntactic meaning,\n * while in minimatch patterns they can form brace expansion or ext glob syntax.\n */\n const escapedPatternWithoutLeadingSlash = patternWithoutLeadingSlash.replaceAll(\n /(?=((?:\\\\.|[^{(])*))\\1([{(])/guy,\n String.raw`$1\\$2`,\n );\n\n const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : '';\n\n return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`;\n}\n","import { convertIgnorePatternToMinimatch } from './convertIgnorePatternToMinimatch.js';\n\nconst ROOT_PREFIX_PATTERN = /^\\.\\/?/;\n\nconst normalizePath = (p: string) => p.replaceAll('\\\\', '/').replace(ROOT_PREFIX_PATTERN, '');\n\n/**\n * Resolve a raw ignore rule from a `.gitignore` file into a flat ESLint\n * minimatch glob relative to the configured working directory.\n *\n * @example\n * ```ts\n * import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';\n *\n * ignoreRuleResolve('.', 'out'); // '**\\/out'\n * ignoreRuleResolve('.', '/dist'); // 'dist'\n * ignoreRuleResolve('android', 'build'); // 'android/**\\/build'\n * ignoreRuleResolve('android', '/build'); // 'android/build'\n * ignoreRuleResolve('android', '!build'); // '!android/**\\/build'\n * ```\n *\n * @internal\n * @param prefix A path prefix that points to the directory containing the `.gitignore` file.\n * @param rule The raw ignore rule parsed from `.gitignore`.\n * @returns {string} A normalized ignore pattern relative to the root `cwd`.\n */\nexport function ignoreRuleResolve(prefix: string, rule: string): string {\n const negated = rule.startsWith('!');\n const raw = negated ? rule.slice(1) : rule;\n const normalizedPrefix = prefix === '.' || prefix === '' ? '' : normalizePath(prefix);\n\n let converted: string;\n if (raw.startsWith('/')) {\n const joined = normalizedPrefix\n ? `${normalizedPrefix}/${raw.slice(1)}`\n : raw.slice(1);\n converted = convertIgnorePatternToMinimatch(`/${joined}`);\n } else {\n const dirPrefix = normalizedPrefix ? `${normalizedPrefix}/` : '';\n converted = dirPrefix + convertIgnorePatternToMinimatch(raw);\n }\n\n return negated ? `!${converted}` : converted;\n}\n","/* eslint-disable unicorn/prefer-await */\nimport { minimatch } from 'minimatch';\nimport fs from 'node:fs/promises';\nimport nodePath from 'node:path';\n\nimport { ignoreFileParse } from './ignoreFileParse.js';\nimport { ignoreRuleResolve } from './ignoreRuleResolve.js';\n\nconst GITIGNORE_FILE = '.gitignore';\n\nexport interface IgnoreFileFindOptions {\n /** Directory names to skip when searching downward from `rootDir`. */\n excludeDirs?: string[];\n /** Maximum recursion depth when searching downward. Defaults to 8. */\n maxDepth?: number;\n /** When true (default), stop ancestor traversal when a `.git` directory is found. */\n stopAtGitRoot?: boolean;\n}\n\n/**\n * Find `.gitignore` files relevant to `rootDir`.\n *\n * Behavior:\n * - Searches downwards from `rootDir` (BFS) for `.gitignore` files, skipping `excludeDirs`.\n * - Walks ancestors from `rootDir` up to the filesystem root (and stops at a `.git` folder when `stopAtGitRoot` is true).\n * - Returns relative paths to `rootDir`.\n *\n * @param rootDir\n * @param options\n */\nexport async function ignoreFileFind(\n rootDir: string,\n options?: IgnoreFileFindOptions,\n): Promise<Array<string>> {\n const excludeDirs = new Set(options?.excludeDirs ?? ['node_modules', '.git']);\n const maxDepth = options?.maxDepth ?? 8;\n const stopAtGitRoot = options?.stopAtGitRoot ?? true;\n\n const absoluteRootDir = nodePath.resolve(rootDir);\n const found = new Set<string>();\n\n // --- Helpers (internal, not exported) ---------------------------------\n const normalize = (p: string) => p.replaceAll('\\\\', '/');\n\n function patternMatchesPath(pattern: string, candidateRel: string): boolean {\n const normCandidate = normalize(candidateRel);\n const normPattern = normalize(pattern);\n\n if (minimatch(normCandidate, normPattern, { dot: true })) {\n return true;\n }\n\n if (minimatch(normCandidate, `${normPattern}/**`, { dot: true })) {\n return true;\n }\n\n if (normPattern.endsWith('/')) {\n const withoutTrailing = normPattern.slice(0, -1);\n if (minimatch(normCandidate, withoutTrailing, { dot: true })) {\n return true;\n }\n if (minimatch(normCandidate, `${withoutTrailing}/**`, { dot: true })) {\n return true;\n }\n }\n\n return false;\n }\n\n function lastMatchWins(patterns: string[], candidateRel: string): null | string {\n let lastMatch: null | string = null;\n for (const p of patterns) {\n const pat = p.startsWith('!') ? p.slice(1) : p;\n if (!pat) continue;\n\n if (patternMatchesPath(pat, candidateRel)) {\n lastMatch = p;\n }\n }\n return lastMatch;\n }\n\n function isIgnored(patterns: string[], candidateRel: string): boolean {\n const m = lastMatchWins(patterns, candidateRel);\n if (!m) return false;\n return !m.startsWith('!');\n }\n\n async function collectAncestorGitignores(startDir: string): Promise<string[]> {\n const files: string[] = [];\n let dir = startDir;\n while (true) {\n const gi = nodePath.join(dir, GITIGNORE_FILE);\n try {\n const stat = await fs.stat(gi).catch(() => null);\n if (stat && stat.isFile()) files.push(gi);\n } catch {\n // ignore\n }\n\n if (stopAtGitRoot) {\n try {\n const gitStat = await fs.stat(nodePath.join(dir, '.git')).catch(() => null);\n if (gitStat && gitStat.isDirectory()) break;\n } catch {\n // ignore\n }\n }\n\n const parent = nodePath.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n // eslint-disable-next-line unicorn/no-array-reverse\n return files.reverse();\n }\n\n async function parseAndResolve(giPath: string): Promise<string[]> {\n try {\n const content = String(await fs.readFile(giPath, 'utf8'));\n const parsed = ignoreFileParse(content);\n const prefixRel = nodePath.relative(rootDir, nodePath.dirname(giPath));\n return parsed.map((p) => ignoreRuleResolve(prefixRel, p));\n } catch {\n return [];\n }\n }\n\n // --- Build initial patterns from ancestor .gitignore files -------------\n const ancestorFiles = await collectAncestorGitignores(absoluteRootDir);\n const initialPatterns: string[] = [];\n for (const gi of ancestorFiles) {\n const parsed = await parseAndResolve(gi);\n if (parsed.length > 0) initialPatterns.push(...parsed);\n found.add(gi);\n }\n\n // --- BFS downward carrying accumulated patterns -----------------------\n const queue: Array<{ depth: number; dir: string; patterns: string[] }> = [\n { depth: 0, dir: absoluteRootDir, patterns: [...initialPatterns] },\n ];\n\n while (queue.length > 0) {\n // eslint-disable-next-line ts/no-non-null-assertion\n const { depth, dir: currentDir, patterns } = queue.shift()!;\n if (depth > maxDepth) continue;\n\n let entries;\n try {\n entries = await fs.readdir(currentDir, { withFileTypes: true });\n } catch {\n continue; // unreadable\n }\n\n // If local .gitignore exists, parse and extend patterns\n\n const local = entries.find((e) => e.isFile() && e.name === GITIGNORE_FILE);\n const combinedPatterns = local ? [...patterns, ...(await parseAndResolve(nodePath.join(currentDir, GITIGNORE_FILE)))] : patterns;\n if (local) found.add(nodePath.join(currentDir, GITIGNORE_FILE));\n\n for (const ent of entries) {\n if (!ent.isDirectory()) continue;\n if (excludeDirs.has(ent.name)) continue;\n const subdir = nodePath.join(currentDir, ent.name);\n const rel = nodePath.relative(rootDir, subdir);\n if (isIgnored(combinedPatterns, rel)) continue;\n queue.push({ depth: depth + 1, dir: subdir, patterns: combinedPatterns });\n }\n }\n\n return [...found].map((p) => nodePath.relative(rootDir, p));\n}\n","import fs from 'node:fs';\nimport nodePath from 'node:path';\nimport process from 'node:process';\n\nimport { defaultIgnores } from './defaultIgnores.js';\nimport { ignoreFileFind } from './internal/ignoreFileFind.js';\nimport { ignoreFileParse } from './internal/ignoreFileParse.js';\nimport { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';\n\nexport interface ESLintIgnoreConfig {\n /**\n * The file globs to ignore\n */\n ignores: Array<string>;\n\n /**\n * The configuration name\n */\n name: string;\n}\n\nexport interface ESLintIgnoreOptions {\n /**\n * Override current working directory\n */\n cwd?: string;\n\n /**\n * Override or customize ignore patterns.\n * - If passed an array, it appends patterns to the merged ignore list.\n * - If passed a function, it receives the merged list and returns the final array.\n */\n ignores?: ((ignores: ESLintIgnoreConfig['ignores']) => ESLintIgnoreConfig['ignores']) | ESLintIgnoreConfig['ignores'];\n\n /**\n * Override configuration name\n */\n name?: ESLintIgnoreConfig['name'];\n\n /**\n * Include recommended settings and default ignored files\n */\n recommended?: boolean | undefined;\n}\n\n/**\n * Create a new eslint configuration object\n *\n * @example\n * ```ts\n * // eslint.config.js\n * export default [\n * await eslintIgnores({\n * ignores: [\n * // Add custom paths here\n * ]\n * })\n * ];\n * ```\n *\n * @param options\n */\nexport async function eslintIgnores(options: ESLintIgnoreOptions = {}): Promise<ESLintIgnoreConfig> {\n const cwd = options.cwd ?? process.cwd();\n const recommended = options.recommended ?? true;\n const ignoreFilePaths = await ignoreFileFind(cwd);\n const ignoreGlobs = (await Promise.all(ignoreFilePaths.map(async (ignoreFilePathRelative) => {\n const ignoreFilePath = nodePath.join(cwd, ignoreFilePathRelative);\n const ignoreFileContent = String(await fs.promises.readFile(ignoreFilePath));\n const patterns = ignoreFileParse(ignoreFileContent);\n const ignoreDirectoryRelative = nodePath.dirname(ignoreFilePathRelative);\n\n return patterns.map((pattern) => ignoreRuleResolve(ignoreDirectoryRelative, pattern));\n })));\n\n const mergedIgnores = [\n ...(recommended ? defaultIgnores : []),\n ...ignoreGlobs.flat(),\n ];\n\n const ignores = typeof options.ignores === 'function'\n ? options.ignores(mergedIgnores)\n : options.ignores\n ? [...mergedIgnores, ...options.ignores]\n : mergedIgnores;\n\n return {\n ignores,\n name: options.name ?? 'w5s/eslint-ignore',\n };\n}\n","export const meta = Object.freeze({\n // @ts-ignore - these variables are injected at build time\n buildNumber: 1 as number, // (typeof __PACKAGE_BUILD_NUMBER__ === 'undefined' ? 0 : __PACKAGE_BUILD_NUMBER__) as number,\n // @ts-ignore - these variables are injected at build time\n name: (typeof __PACKAGE_NAME__ === 'undefined' ? '' : __PACKAGE_NAME__) as string,\n // @ts-ignore - these variables are injected at build time\n version: (typeof __PACKAGE_VERSION__ === 'undefined' ? '' : __PACKAGE_VERSION__) as string,\n});\n"],"mappings":";;;;;;;AAAA,MAAa,iBAAiB;CAE5B;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;AACF;;;ACzCA,SAAgB,gBAAgB,OAA8B;CAC5D,OAAO,eAAe,MAAM,KAAK,CAAC,CAAC;AACrC;;;;;;;;;;;;ACKA,SAAgB,gCAAgC,SAAyB;CACvE,MAAM,YAAY,QAAQ,WAAW,GAAG;CACxC,MAAM,gBAAgB,YAAY,MAAM;CACxC,MAAM,iBAAiB,YAAY,QAAQ,MAAM,CAAC,IAAI,QAAA,CAAS,QAAQ;CAGvE,IAAI;EAAC;EAAI;EAAM;EAAO;CAAK,CAAC,CAAC,SAAS,aAAa,GACjD,OAAO,GAAG,gBAAgB;CAG5B,MAAM,oBAAoB,cAAc,QAAQ,GAAG;CAkBnD,OAAO,GAAG,gBAhBoB,sBAAsB,MAAM,sBAAsB,cAAc,SAAS,IAAI,QAAQ,MAEhF,sBAAsB,IAAI,cAAc,MAAM,CAAC,IAAI,cAAA,CAOjB,WACnE,mCACA,OAAO,GAAG,OAKsE,IAFxD,cAAc,SAAS,KAAK,IAAI,OAAO;AAGnE;;;ACpCA,MAAM,sBAAsB;AAE5B,MAAM,iBAAiB,MAAc,EAAE,WAAW,MAAM,GAAG,CAAC,CAAC,QAAQ,qBAAqB,EAAE;;;;;;;;;;;;;;;;;;;;;AAsB5F,SAAgB,kBAAkB,QAAgB,MAAsB;CACtE,MAAM,UAAU,KAAK,WAAW,GAAG;CACnC,MAAM,MAAM,UAAU,KAAK,MAAM,CAAC,IAAI;CACtC,MAAM,mBAAmB,WAAW,OAAO,WAAW,KAAK,KAAK,cAAc,MAAM;CAEpF,IAAI;CACJ,IAAI,IAAI,WAAW,GAAG,GAIpB,YAAY,gCAAgC,IAH7B,mBACX,GAAG,iBAAiB,GAAG,IAAI,MAAM,CAAC,MAClC,IAAI,MAAM,CAAC,GACyC;MAGxD,aADkB,mBAAmB,GAAG,iBAAiB,KAAK,MACtC,gCAAgC,GAAG;CAG7D,OAAO,UAAU,IAAI,cAAc;AACrC;;;ACnCA,MAAM,iBAAiB;;;;;;;;;;;;AAsBvB,eAAsB,eACpB,SACA,SACwB;CACxB,MAAM,cAAc,IAAI,IAAI,SAAS,eAAe,CAAC,gBAAgB,MAAM,CAAC;CAC5E,MAAM,WAAW,SAAS,YAAY;CACtC,MAAM,gBAAgB,SAAS,iBAAiB;CAEhD,MAAM,kBAAkB,SAAS,QAAQ,OAAO;CAChD,MAAM,wBAAQ,IAAI,IAAY;CAG9B,MAAM,aAAa,MAAc,EAAE,WAAW,MAAM,GAAG;CAEvD,SAAS,mBAAmB,SAAiB,cAA+B;EAC1E,MAAM,gBAAgB,UAAU,YAAY;EAC5C,MAAM,cAAc,UAAU,OAAO;EAErC,IAAI,UAAU,eAAe,aAAa,EAAE,KAAK,KAAK,CAAC,GACrD,OAAO;EAGT,IAAI,UAAU,eAAe,GAAG,YAAY,MAAM,EAAE,KAAK,KAAK,CAAC,GAC7D,OAAO;EAGT,IAAI,YAAY,SAAS,GAAG,GAAG;GAC7B,MAAM,kBAAkB,YAAY,MAAM,GAAG,EAAE;GAC/C,IAAI,UAAU,eAAe,iBAAiB,EAAE,KAAK,KAAK,CAAC,GACzD,OAAO;GAET,IAAI,UAAU,eAAe,GAAG,gBAAgB,MAAM,EAAE,KAAK,KAAK,CAAC,GACjE,OAAO;EAEX;EAEA,OAAO;CACT;CAEA,SAAS,cAAc,UAAoB,cAAqC;EAC9E,IAAI,YAA2B;EAC/B,KAAK,MAAM,KAAK,UAAU;GACxB,MAAM,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,CAAC,IAAI;GAC7C,IAAI,CAAC,KAAK;GAEV,IAAI,mBAAmB,KAAK,YAAY,GACtC,YAAY;EAEhB;EACA,OAAO;CACT;CAEA,SAAS,UAAU,UAAoB,cAA+B;EACpE,MAAM,IAAI,cAAc,UAAU,YAAY;EAC9C,IAAI,CAAC,GAAG,OAAO;EACf,OAAO,CAAC,EAAE,WAAW,GAAG;CAC1B;CAEA,eAAe,0BAA0B,UAAqC;EAC5E,MAAM,QAAkB,CAAC;EACzB,IAAI,MAAM;EACV,OAAO,MAAM;GACX,MAAM,KAAK,SAAS,KAAK,KAAK,cAAc;GAC5C,IAAI;IACF,MAAM,OAAO,MAAMA,KAAG,KAAK,EAAE,CAAC,CAAC,YAAY,IAAI;IAC/C,IAAI,QAAQ,KAAK,OAAO,GAAG,MAAM,KAAK,EAAE;GAC1C,QAAQ,CAER;GAEA,IAAI,eACF,IAAI;IACF,MAAM,UAAU,MAAMA,KAAG,KAAK,SAAS,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI;IAC1E,IAAI,WAAW,QAAQ,YAAY,GAAG;GACxC,QAAQ,CAER;GAGF,MAAM,SAAS,SAAS,QAAQ,GAAG;GACnC,IAAI,WAAW,KAAK;GACpB,MAAM;EACR;EAEA,OAAO,MAAM,QAAQ;CACvB;CAEA,eAAe,gBAAgB,QAAmC;EAChE,IAAI;GAEF,MAAM,SAAS,gBADC,OAAO,MAAMA,KAAG,SAAS,QAAQ,MAAM,CAClB,CAAC;GACtC,MAAM,YAAY,SAAS,SAAS,SAAS,SAAS,QAAQ,MAAM,CAAC;GACrE,OAAO,OAAO,KAAK,MAAM,kBAAkB,WAAW,CAAC,CAAC;EAC1D,QAAQ;GACN,OAAO,CAAC;EACV;CACF;CAGA,MAAM,gBAAgB,MAAM,0BAA0B,eAAe;CACrE,MAAM,kBAA4B,CAAC;CACnC,KAAK,MAAM,MAAM,eAAe;EAC9B,MAAM,SAAS,MAAM,gBAAgB,EAAE;EACvC,IAAI,OAAO,SAAS,GAAG,gBAAgB,KAAK,GAAG,MAAM;EACrD,MAAM,IAAI,EAAE;CACd;CAGA,MAAM,QAAmE,CACvE;EAAE,OAAO;EAAG,KAAK;EAAiB,UAAU,CAAC,GAAG,eAAe;CAAE,CACnE;CAEA,OAAO,MAAM,SAAS,GAAG;EAEvB,MAAM,EAAE,OAAO,KAAK,YAAY,aAAa,MAAM,MAAM;EACzD,IAAI,QAAQ,UAAU;EAEtB,IAAI;EACJ,IAAI;GACF,UAAU,MAAMA,KAAG,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;EAChE,QAAQ;GACN;EACF;EAIA,MAAM,QAAQ,QAAQ,MAAM,MAAM,EAAE,OAAO,KAAK,EAAE,SAAS,cAAc;EACzE,MAAM,mBAAmB,QAAQ,CAAC,GAAG,UAAU,GAAI,MAAM,gBAAgB,SAAS,KAAK,YAAY,cAAc,CAAC,CAAE,IAAI;EACxH,IAAI,OAAO,MAAM,IAAI,SAAS,KAAK,YAAY,cAAc,CAAC;EAE9D,KAAK,MAAM,OAAO,SAAS;GACzB,IAAI,CAAC,IAAI,YAAY,GAAG;GACxB,IAAI,YAAY,IAAI,IAAI,IAAI,GAAG;GAC/B,MAAM,SAAS,SAAS,KAAK,YAAY,IAAI,IAAI;GAEjD,IAAI,UAAU,kBADF,SAAS,SAAS,SAAS,MACL,CAAC,GAAG;GACtC,MAAM,KAAK;IAAE,OAAO,QAAQ;IAAG,KAAK;IAAQ,UAAU;GAAiB,CAAC;EAC1E;CACF;CAEA,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,MAAM,SAAS,SAAS,SAAS,CAAC,CAAC;AAC5D;;;;;;;;;;;;;;;;;;;;AC7GA,eAAsB,cAAc,UAA+B,CAAC,GAAgC;CAClG,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,kBAAkB,MAAM,eAAe,GAAG;CAChD,MAAM,cAAe,MAAM,QAAQ,IAAI,gBAAgB,IAAI,OAAO,2BAA2B;EAC3F,MAAM,iBAAiB,SAAS,KAAK,KAAK,sBAAsB;EAEhE,MAAM,WAAW,gBADS,OAAO,MAAM,GAAG,SAAS,SAAS,cAAc,CACzB,CAAC;EAClD,MAAM,0BAA0B,SAAS,QAAQ,sBAAsB;EAEvE,OAAO,SAAS,KAAK,YAAY,kBAAkB,yBAAyB,OAAO,CAAC;CACtF,CAAC,CAAC;CAEF,MAAM,gBAAgB,CACpB,GAAI,cAAc,iBAAiB,CAAC,GACpC,GAAG,YAAY,KAAK,CACtB;CAQA,OAAO;EACL,SAPc,OAAO,QAAQ,YAAY,aACvC,QAAQ,QAAQ,aAAa,IAC7B,QAAQ,UACN,CAAC,GAAG,eAAe,GAAG,QAAQ,OAAO,IACrC;EAIJ,MAAM,QAAQ,QAAQ;CACxB;AACF;;;AC1FA,MAAa,OAAO,OAAO,OAAO;CAEhC,aAAa;CAEb,MAAA;CAEA,SAAA;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@w5s/eslint-config-ignore",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Shared ESLint ignore configuration generator",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"find-up": "^8.0.0",
|
|
40
|
+
"minimatch": "^10.2.0",
|
|
40
41
|
"parse-gitignore": "^2.0.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
@@ -58,5 +59,5 @@
|
|
|
58
59
|
"access": "public"
|
|
59
60
|
},
|
|
60
61
|
"sideEffect": false,
|
|
61
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "456aac7a7b05abae48365ae944a31c494ed078cf"
|
|
62
63
|
}
|
package/src/eslintIgnores.ts
CHANGED
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import nodePath from 'node:path';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
+
|
|
4
5
|
import { defaultIgnores } from './defaultIgnores.js';
|
|
5
|
-
import { ignoreFileParse } from './internal/ignoreFileParse.js';
|
|
6
6
|
import { ignoreFileFind } from './internal/ignoreFileFind.js';
|
|
7
|
+
import { ignoreFileParse } from './internal/ignoreFileParse.js';
|
|
7
8
|
import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';
|
|
8
9
|
|
|
10
|
+
export interface ESLintIgnoreConfig {
|
|
11
|
+
/**
|
|
12
|
+
* The file globs to ignore
|
|
13
|
+
*/
|
|
14
|
+
ignores: Array<string>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The configuration name
|
|
18
|
+
*/
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ESLintIgnoreOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Override current working directory
|
|
25
|
+
*/
|
|
26
|
+
cwd?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Override or customize ignore patterns.
|
|
30
|
+
* - If passed an array, it appends patterns to the merged ignore list.
|
|
31
|
+
* - If passed a function, it receives the merged list and returns the final array.
|
|
32
|
+
*/
|
|
33
|
+
ignores?: ((ignores: ESLintIgnoreConfig['ignores']) => ESLintIgnoreConfig['ignores']) | ESLintIgnoreConfig['ignores'];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Override configuration name
|
|
37
|
+
*/
|
|
38
|
+
name?: ESLintIgnoreConfig['name'];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Include recommended settings and default ignored files
|
|
42
|
+
*/
|
|
43
|
+
recommended?: boolean | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
9
46
|
/**
|
|
10
47
|
* Create a new eslint configuration object
|
|
11
48
|
*
|
|
@@ -48,43 +85,7 @@ export async function eslintIgnores(options: ESLintIgnoreOptions = {}): Promise<
|
|
|
48
85
|
: mergedIgnores;
|
|
49
86
|
|
|
50
87
|
return {
|
|
51
|
-
name: options.name ?? 'w5s/eslint-ignore',
|
|
52
88
|
ignores,
|
|
89
|
+
name: options.name ?? 'w5s/eslint-ignore',
|
|
53
90
|
};
|
|
54
91
|
}
|
|
55
|
-
|
|
56
|
-
export interface ESLintIgnoreConfig {
|
|
57
|
-
/**
|
|
58
|
-
* The configuration name
|
|
59
|
-
*/
|
|
60
|
-
name: string;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The file globs to ignore
|
|
64
|
-
*/
|
|
65
|
-
ignores: Array<string>;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface ESLintIgnoreOptions {
|
|
69
|
-
/**
|
|
70
|
-
* Override configuration name
|
|
71
|
-
*/
|
|
72
|
-
name?: ESLintIgnoreConfig['name'];
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Override current working directory
|
|
76
|
-
*/
|
|
77
|
-
cwd?: string;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Override or customize ignore patterns.
|
|
81
|
-
* - If passed an array, it appends patterns to the merged ignore list.
|
|
82
|
-
* - If passed a function, it receives the merged list and returns the final array.
|
|
83
|
-
*/
|
|
84
|
-
ignores?: ESLintIgnoreConfig['ignores'] | ((ignores: ESLintIgnoreConfig['ignores']) => ESLintIgnoreConfig['ignores']);
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Include recommended settings and default ignored files
|
|
88
|
-
*/
|
|
89
|
-
recommended?: boolean | undefined;
|
|
90
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a gitignore-style pattern to an ESLint-compatible minimatch pattern.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `@eslint/config-helpers` (eslint/rewrite).
|
|
5
|
+
*
|
|
6
|
+
* @see https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/ignore-file.js
|
|
7
|
+
* @param pattern The .eslintignore or .gitignore pattern to convert.
|
|
8
|
+
* @returns {string} The converted minimatch pattern.
|
|
9
|
+
*/
|
|
10
|
+
export function convertIgnorePatternToMinimatch(pattern: string): string {
|
|
11
|
+
const isNegated = pattern.startsWith('!');
|
|
12
|
+
const negatedPrefix = isNegated ? '!' : '';
|
|
13
|
+
const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd();
|
|
14
|
+
|
|
15
|
+
// special cases
|
|
16
|
+
if (['', '**', '**/', '/**'].includes(patternToTest)) {
|
|
17
|
+
return `${negatedPrefix}${patternToTest}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const firstIndexOfSlash = patternToTest.indexOf('/');
|
|
21
|
+
|
|
22
|
+
const matchEverywherePrefix = firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1 ? '**/' : '';
|
|
23
|
+
|
|
24
|
+
const patternWithoutLeadingSlash = firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest;
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* Escape `{` and `(` because in gitignore patterns they are just
|
|
28
|
+
* literal characters without any specific syntactic meaning,
|
|
29
|
+
* while in minimatch patterns they can form brace expansion or ext glob syntax.
|
|
30
|
+
*/
|
|
31
|
+
const escapedPatternWithoutLeadingSlash = patternWithoutLeadingSlash.replaceAll(
|
|
32
|
+
/(?=((?:\\.|[^{(])*))\1([{(])/guy,
|
|
33
|
+
String.raw`$1\$2`,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : '';
|
|
37
|
+
|
|
38
|
+
return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`;
|
|
39
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/* eslint-disable unicorn/no-break-in-nested-loop */
|
|
2
1
|
/* eslint-disable unicorn/prefer-await */
|
|
3
|
-
import
|
|
2
|
+
import { minimatch } from 'minimatch';
|
|
4
3
|
import fs from 'node:fs/promises';
|
|
4
|
+
import nodePath from 'node:path';
|
|
5
|
+
|
|
5
6
|
import { ignoreFileParse } from './ignoreFileParse.js';
|
|
6
7
|
import { ignoreRuleResolve } from './ignoreRuleResolve.js';
|
|
7
8
|
|
|
@@ -31,7 +32,7 @@ export async function ignoreFileFind(
|
|
|
31
32
|
rootDir: string,
|
|
32
33
|
options?: IgnoreFileFindOptions,
|
|
33
34
|
): Promise<Array<string>> {
|
|
34
|
-
const excludeDirs = new Set(options?.excludeDirs ?? ['node_modules', '.git'
|
|
35
|
+
const excludeDirs = new Set(options?.excludeDirs ?? ['node_modules', '.git']);
|
|
35
36
|
const maxDepth = options?.maxDepth ?? 8;
|
|
36
37
|
const stopAtGitRoot = options?.stopAtGitRoot ?? true;
|
|
37
38
|
|
|
@@ -41,16 +42,38 @@ export async function ignoreFileFind(
|
|
|
41
42
|
// --- Helpers (internal, not exported) ---------------------------------
|
|
42
43
|
const normalize = (p: string) => p.replaceAll('\\', '/');
|
|
43
44
|
|
|
44
|
-
function
|
|
45
|
+
function patternMatchesPath(pattern: string, candidateRel: string): boolean {
|
|
45
46
|
const normCandidate = normalize(candidateRel);
|
|
46
|
-
|
|
47
|
+
const normPattern = normalize(pattern);
|
|
48
|
+
|
|
49
|
+
if (minimatch(normCandidate, normPattern, { dot: true })) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (minimatch(normCandidate, `${normPattern}/**`, { dot: true })) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (normPattern.endsWith('/')) {
|
|
58
|
+
const withoutTrailing = normPattern.slice(0, -1);
|
|
59
|
+
if (minimatch(normCandidate, withoutTrailing, { dot: true })) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (minimatch(normCandidate, `${withoutTrailing}/**`, { dot: true })) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function lastMatchWins(patterns: string[], candidateRel: string): null | string {
|
|
71
|
+
let lastMatch: null | string = null;
|
|
47
72
|
for (const p of patterns) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const patNorm = normalize(pat);
|
|
51
|
-
if (!patNorm) continue;
|
|
73
|
+
const pat = p.startsWith('!') ? p.slice(1) : p;
|
|
74
|
+
if (!pat) continue;
|
|
52
75
|
|
|
53
|
-
if (
|
|
76
|
+
if (patternMatchesPath(pat, candidateRel)) {
|
|
54
77
|
lastMatch = p;
|
|
55
78
|
}
|
|
56
79
|
}
|
|
@@ -113,13 +136,13 @@ export async function ignoreFileFind(
|
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
// --- BFS downward carrying accumulated patterns -----------------------
|
|
116
|
-
const queue: Array<{
|
|
117
|
-
{
|
|
139
|
+
const queue: Array<{ depth: number; dir: string; patterns: string[] }> = [
|
|
140
|
+
{ depth: 0, dir: absoluteRootDir, patterns: [...initialPatterns] },
|
|
118
141
|
];
|
|
119
142
|
|
|
120
143
|
while (queue.length > 0) {
|
|
121
144
|
// eslint-disable-next-line ts/no-non-null-assertion
|
|
122
|
-
const { dir: currentDir,
|
|
145
|
+
const { depth, dir: currentDir, patterns } = queue.shift()!;
|
|
123
146
|
if (depth > maxDepth) continue;
|
|
124
147
|
|
|
125
148
|
let entries;
|
|
@@ -141,7 +164,7 @@ export async function ignoreFileFind(
|
|
|
141
164
|
const subdir = nodePath.join(currentDir, ent.name);
|
|
142
165
|
const rel = nodePath.relative(rootDir, subdir);
|
|
143
166
|
if (isIgnored(combinedPatterns, rel)) continue;
|
|
144
|
-
queue.push({
|
|
167
|
+
queue.push({ depth: depth + 1, dir: subdir, patterns: combinedPatterns });
|
|
145
168
|
}
|
|
146
169
|
}
|
|
147
170
|
|
|
@@ -1,31 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { convertIgnorePatternToMinimatch } from './convertIgnorePatternToMinimatch.js';
|
|
2
|
+
|
|
3
|
+
const ROOT_PREFIX_PATTERN = /^\.\/?/;
|
|
4
|
+
|
|
5
|
+
const normalizePath = (p: string) => p.replaceAll('\\', '/').replace(ROOT_PREFIX_PATTERN, '');
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
|
-
* Resolve a raw ignore rule from a `.gitignore` file into a
|
|
5
|
-
* relative to the configured working directory.
|
|
8
|
+
* Resolve a raw ignore rule from a `.gitignore` file into a flat ESLint
|
|
9
|
+
* minimatch glob relative to the configured working directory.
|
|
6
10
|
*
|
|
7
11
|
* @example
|
|
8
12
|
* ```ts
|
|
9
13
|
* import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';
|
|
10
14
|
*
|
|
11
|
-
* ignoreRuleResolve('.', '
|
|
15
|
+
* ignoreRuleResolve('.', 'out'); // '**\/out'
|
|
12
16
|
* ignoreRuleResolve('.', '/dist'); // 'dist'
|
|
13
|
-
* ignoreRuleResolve('android', '
|
|
14
|
-
* ignoreRuleResolve('android', '
|
|
17
|
+
* ignoreRuleResolve('android', 'build'); // 'android/**\/build'
|
|
18
|
+
* ignoreRuleResolve('android', '/build'); // 'android/build'
|
|
19
|
+
* ignoreRuleResolve('android', '!build'); // '!android/**\/build'
|
|
15
20
|
* ```
|
|
16
21
|
*
|
|
17
22
|
* @internal
|
|
18
23
|
* @param prefix A path prefix that points to the directory containing the `.gitignore` file.
|
|
19
24
|
* @param rule The raw ignore rule parsed from `.gitignore`.
|
|
20
|
-
* @returns A normalized ignore pattern relative to the root `cwd`.
|
|
25
|
+
* @returns {string} A normalized ignore pattern relative to the root `cwd`.
|
|
21
26
|
*/
|
|
22
|
-
export function ignoreRuleResolve(prefix: string, rule: string) {
|
|
27
|
+
export function ignoreRuleResolve(prefix: string, rule: string): string {
|
|
23
28
|
const negated = rule.startsWith('!');
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const raw = negated ? rule.slice(1) : rule;
|
|
30
|
+
const normalizedPrefix = prefix === '.' || prefix === '' ? '' : normalizePath(prefix);
|
|
31
|
+
|
|
32
|
+
let converted: string;
|
|
33
|
+
if (raw.startsWith('/')) {
|
|
34
|
+
const joined = normalizedPrefix
|
|
35
|
+
? `${normalizedPrefix}/${raw.slice(1)}`
|
|
36
|
+
: raw.slice(1);
|
|
37
|
+
converted = convertIgnorePatternToMinimatch(`/${joined}`);
|
|
38
|
+
} else {
|
|
39
|
+
const dirPrefix = normalizedPrefix ? `${normalizedPrefix}/` : '';
|
|
40
|
+
converted = dirPrefix + convertIgnorePatternToMinimatch(raw);
|
|
41
|
+
}
|
|
29
42
|
|
|
30
|
-
return negated ? `!${
|
|
43
|
+
return negated ? `!${converted}` : converted;
|
|
31
44
|
}
|
package/src/meta.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const meta = Object.freeze({
|
|
2
|
+
// @ts-ignore - these variables are injected at build time
|
|
3
|
+
buildNumber: 1 as number, // (typeof __PACKAGE_BUILD_NUMBER__ === 'undefined' ? 0 : __PACKAGE_BUILD_NUMBER__) as number,
|
|
2
4
|
// @ts-ignore - these variables are injected at build time
|
|
3
5
|
name: (typeof __PACKAGE_NAME__ === 'undefined' ? '' : __PACKAGE_NAME__) as string,
|
|
4
6
|
// @ts-ignore - these variables are injected at build time
|
|
5
7
|
version: (typeof __PACKAGE_VERSION__ === 'undefined' ? '' : __PACKAGE_VERSION__) as string,
|
|
6
|
-
// @ts-ignore - these variables are injected at build time
|
|
7
|
-
buildNumber: 1 as number, // (typeof __PACKAGE_BUILD_NUMBER__ === 'undefined' ? 0 : __PACKAGE_BUILD_NUMBER__) as number,
|
|
8
8
|
});
|