]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/cli-engine/config-array/ignore-pattern.js
exit with error also on warnings
[pve-eslint.git] / eslint / lib / cli-engine / config-array / ignore-pattern.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview `IgnorePattern` class.
3 *
4 * `IgnorePattern` class has the set of glob patterns and the base path.
5 *
6 * It provides two static methods.
7 *
8 * - `IgnorePattern.createDefaultIgnore(cwd)`
9 * Create the default predicate function.
10 * - `IgnorePattern.createIgnore(ignorePatterns)`
11 * Create the predicate function from multiple `IgnorePattern` objects.
12 *
13 * It provides two properties and a method.
14 *
15 * - `patterns`
16 * The glob patterns that ignore to lint.
17 * - `basePath`
18 * The base path of the glob patterns. If absolute paths existed in the
19 * glob patterns, those are handled as relative paths to the base path.
20 * - `getPatternsRelativeTo(basePath)`
21 * Get `patterns` as modified for a given base path. It modifies the
22 * absolute paths in the patterns as prepending the difference of two base
23 * paths.
24 *
25 * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes
26 * `ignorePatterns` properties.
27 *
28 * @author Toru Nagashima <https://github.com/mysticatea>
29 */
30"use strict";
31
32//------------------------------------------------------------------------------
33// Requirements
34//------------------------------------------------------------------------------
35
36const assert = require("assert");
37const path = require("path");
38const ignore = require("ignore");
39const debug = require("debug")("eslint:ignore-pattern");
40
41/** @typedef {ReturnType<import("ignore").default>} Ignore */
42
43//------------------------------------------------------------------------------
44// Helpers
45//------------------------------------------------------------------------------
46
47/**
48 * Get the path to the common ancestor directory of given paths.
49 * @param {string[]} sourcePaths The paths to calculate the common ancestor.
50 * @returns {string} The path to the common ancestor directory.
51 */
52function getCommonAncestorPath(sourcePaths) {
53 let result = sourcePaths[0];
54
55 for (let i = 1; i < sourcePaths.length; ++i) {
56 const a = result;
57 const b = sourcePaths[i];
58
59 // Set the shorter one (it's the common ancestor if one includes the other).
60 result = a.length < b.length ? a : b;
61
62 // Set the common ancestor.
63 for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) {
64 if (a[j] !== b[j]) {
65 result = a.slice(0, lastSepPos);
66 break;
67 }
68 if (a[j] === path.sep) {
69 lastSepPos = j;
70 }
71 }
72 }
73
56c4a2cb
DC
74 let resolvedResult = result || path.sep;
75
76 // if Windows common ancestor is root of drive must have trailing slash to be absolute.
77 if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") {
78 resolvedResult += path.sep;
79 }
80 return resolvedResult;
eb39fafa
DC
81}
82
83/**
84 * Make relative path.
85 * @param {string} from The source path to get relative path.
86 * @param {string} to The destination path to get relative path.
87 * @returns {string} The relative path.
88 */
89function relative(from, to) {
90 const relPath = path.relative(from, to);
91
92 if (path.sep === "/") {
93 return relPath;
94 }
95 return relPath.split(path.sep).join("/");
96}
97
98/**
99 * Get the trailing slash if existed.
100 * @param {string} filePath The path to check.
101 * @returns {string} The trailing slash if existed.
102 */
103function dirSuffix(filePath) {
104 const isDir = (
105 filePath.endsWith(path.sep) ||
106 (process.platform === "win32" && filePath.endsWith("/"))
107 );
108
109 return isDir ? "/" : "";
110}
111
112const DefaultPatterns = Object.freeze(["/**/node_modules/*"]);
113const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
114
115//------------------------------------------------------------------------------
116// Public
117//------------------------------------------------------------------------------
118
119class IgnorePattern {
120
121 /**
122 * The default patterns.
123 * @type {string[]}
124 */
125 static get DefaultPatterns() {
126 return DefaultPatterns;
127 }
128
129 /**
130 * Create the default predicate function.
131 * @param {string} cwd The current working directory.
132 * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}}
133 * The preficate function.
134 * The first argument is an absolute path that is checked.
135 * The second argument is the flag to not ignore dotfiles.
136 * If the predicate function returned `true`, it means the path should be ignored.
137 */
138 static createDefaultIgnore(cwd) {
139 return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]);
140 }
141
142 /**
143 * Create the predicate function from multiple `IgnorePattern` objects.
144 * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns.
145 * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}}
146 * The preficate function.
147 * The first argument is an absolute path that is checked.
148 * The second argument is the flag to not ignore dotfiles.
149 * If the predicate function returned `true`, it means the path should be ignored.
150 */
151 static createIgnore(ignorePatterns) {
152 debug("Create with: %o", ignorePatterns);
153
154 const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath));
155 const patterns = [].concat(
156 ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath))
157 );
158 const ig = ignore().add([...DotPatterns, ...patterns]);
159 const dotIg = ignore().add(patterns);
160
161 debug(" processed: %o", { basePath, patterns });
162
163 return Object.assign(
164 (filePath, dot = false) => {
165 assert(path.isAbsolute(filePath), "'filePath' should be an absolute path.");
166 const relPathRaw = relative(basePath, filePath);
167 const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath));
168 const adoptedIg = dot ? dotIg : ig;
169 const result = relPath !== "" && adoptedIg.ignores(relPath);
170
171 debug("Check", { filePath, dot, relativePath: relPath, result });
172 return result;
173 },
174 { basePath, patterns }
175 );
176 }
177
178 /**
179 * Initialize a new `IgnorePattern` instance.
180 * @param {string[]} patterns The glob patterns that ignore to lint.
181 * @param {string} basePath The base path of `patterns`.
182 */
183 constructor(patterns, basePath) {
184 assert(path.isAbsolute(basePath), "'basePath' should be an absolute path.");
185
186 /**
187 * The glob patterns that ignore to lint.
188 * @type {string[]}
189 */
190 this.patterns = patterns;
191
192 /**
193 * The base path of `patterns`.
194 * @type {string}
195 */
196 this.basePath = basePath;
197
198 /**
199 * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`.
200 *
201 * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility.
202 * It's `false` as-is for `ignorePatterns` property in config files.
203 * @type {boolean}
204 */
205 this.loose = false;
206 }
207
208 /**
209 * Get `patterns` as modified for a given base path. It modifies the
210 * absolute paths in the patterns as prepending the difference of two base
211 * paths.
212 * @param {string} newBasePath The base path.
213 * @returns {string[]} Modifired patterns.
214 */
215 getPatternsRelativeTo(newBasePath) {
216 assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path.");
217 const { basePath, loose, patterns } = this;
218
219 if (newBasePath === basePath) {
220 return patterns;
221 }
222 const prefix = `/${relative(newBasePath, basePath)}`;
223
224 return patterns.map(pattern => {
225 const negative = pattern.startsWith("!");
226 const head = negative ? "!" : "";
227 const body = negative ? pattern.slice(1) : pattern;
228
229 if (body.startsWith("/") || body.startsWith("../")) {
230 return `${head}${prefix}${body}`;
231 }
232 return loose ? pattern : `${head}${prefix}/**/${body}`;
233 });
234 }
235}
236
237module.exports = { IgnorePattern };