@w5s/eslint-config-ignore 1.2.8 → 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.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import nodePath from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import { minimatch } from "minimatch";
|
|
4
5
|
import fs$1 from "node:fs/promises";
|
|
5
6
|
import parseGitignore from "parse-gitignore";
|
|
6
7
|
//#region src/defaultIgnores.ts
|
|
@@ -43,32 +44,61 @@ function ignoreFileParse(input) {
|
|
|
43
44
|
return parseGitignore.parse(input).patterns;
|
|
44
45
|
}
|
|
45
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
|
|
46
71
|
//#region src/internal/ignoreRuleResolve.ts
|
|
72
|
+
const ROOT_PREFIX_PATTERN = /^\.\/?/;
|
|
73
|
+
const normalizePath = (p) => p.replaceAll("\\", "/").replace(ROOT_PREFIX_PATTERN, "");
|
|
47
74
|
/**
|
|
48
|
-
* Resolve a raw ignore rule from a `.gitignore` file into a
|
|
49
|
-
* 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.
|
|
50
77
|
*
|
|
51
78
|
* @example
|
|
52
79
|
* ```ts
|
|
53
80
|
* import { ignoreRuleResolve } from './internal/ignoreRuleResolve.js';
|
|
54
81
|
*
|
|
55
|
-
* ignoreRuleResolve('.', '
|
|
82
|
+
* ignoreRuleResolve('.', 'out'); // '**\/out'
|
|
56
83
|
* ignoreRuleResolve('.', '/dist'); // 'dist'
|
|
57
|
-
* ignoreRuleResolve('android', '
|
|
58
|
-
* ignoreRuleResolve('android', '
|
|
84
|
+
* ignoreRuleResolve('android', 'build'); // 'android/**\/build'
|
|
85
|
+
* ignoreRuleResolve('android', '/build'); // 'android/build'
|
|
86
|
+
* ignoreRuleResolve('android', '!build'); // '!android/**\/build'
|
|
59
87
|
* ```
|
|
60
88
|
*
|
|
61
89
|
* @internal
|
|
62
90
|
* @param prefix A path prefix that points to the directory containing the `.gitignore` file.
|
|
63
91
|
* @param rule The raw ignore rule parsed from `.gitignore`.
|
|
64
|
-
* @returns A normalized ignore pattern relative to the root `cwd`.
|
|
92
|
+
* @returns {string} A normalized ignore pattern relative to the root `cwd`.
|
|
65
93
|
*/
|
|
66
94
|
function ignoreRuleResolve(prefix, rule) {
|
|
67
95
|
const negated = rule.startsWith("!");
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
72
102
|
}
|
|
73
103
|
//#endregion
|
|
74
104
|
//#region src/internal/ignoreFileFind.ts
|
|
@@ -85,26 +115,30 @@ const GITIGNORE_FILE = ".gitignore";
|
|
|
85
115
|
* @param options
|
|
86
116
|
*/
|
|
87
117
|
async function ignoreFileFind(rootDir, options) {
|
|
88
|
-
const excludeDirs = new Set(options?.excludeDirs ?? [
|
|
89
|
-
"node_modules",
|
|
90
|
-
".git",
|
|
91
|
-
"dist",
|
|
92
|
-
"build",
|
|
93
|
-
"out"
|
|
94
|
-
]);
|
|
118
|
+
const excludeDirs = new Set(options?.excludeDirs ?? ["node_modules", ".git"]);
|
|
95
119
|
const maxDepth = options?.maxDepth ?? 8;
|
|
96
120
|
const stopAtGitRoot = options?.stopAtGitRoot ?? true;
|
|
97
121
|
const absoluteRootDir = nodePath.resolve(rootDir);
|
|
98
122
|
const found = /* @__PURE__ */ new Set();
|
|
99
123
|
const normalize = (p) => p.replaceAll("\\", "/");
|
|
100
|
-
function
|
|
124
|
+
function patternMatchesPath(pattern, candidateRel) {
|
|
101
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) {
|
|
102
137
|
let lastMatch = null;
|
|
103
138
|
for (const p of patterns) {
|
|
104
139
|
const pat = p.startsWith("!") ? p.slice(1) : p;
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
if (normCandidate === patNorm || normCandidate.startsWith(patNorm + "/")) lastMatch = p;
|
|
140
|
+
if (!pat) continue;
|
|
141
|
+
if (patternMatchesPath(pat, candidateRel)) lastMatch = p;
|
|
108
142
|
}
|
|
109
143
|
return lastMatch;
|
|
110
144
|
}
|
|
@@ -219,7 +253,7 @@ async function eslintIgnores(options = {}) {
|
|
|
219
253
|
const meta = Object.freeze({
|
|
220
254
|
buildNumber: 1,
|
|
221
255
|
name: "@w5s/eslint-config-ignore",
|
|
222
|
-
version: "1.
|
|
256
|
+
version: "1.3.0"
|
|
223
257
|
});
|
|
224
258
|
//#endregion
|
|
225
259
|
export { eslintIgnores as default, eslintIgnores, meta };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["fs"],"sources":["../src/defaultIgnores.ts","../src/internal/ignoreFileParse.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","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/prefer-await */\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', '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): null | string {\n const normCandidate = normalize(candidateRel);\n let lastMatch: null | string = 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<{ 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":"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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":"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"}
|
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
|
}
|
|
@@ -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,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable unicorn/prefer-await */
|
|
2
|
+
import { minimatch } from 'minimatch';
|
|
2
3
|
import fs from 'node:fs/promises';
|
|
3
4
|
import nodePath from 'node:path';
|
|
4
5
|
|
|
@@ -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);
|
|
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 {
|
|
46
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
|
}
|
|
@@ -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
|
}
|