source: imaps-frontend/node_modules/@eslint/eslintrc/lib/config-array/override-tester.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 6.7 KB
RevLine 
[d565449]1/**
2 * @fileoverview `OverrideTester` class.
3 *
4 * `OverrideTester` class handles `files` property and `excludedFiles` property
5 * of `overrides` config.
6 *
7 * It provides one method.
8 *
9 * - `test(filePath)`
10 * Test if a file path matches the pair of `files` property and
11 * `excludedFiles` property. The `filePath` argument must be an absolute
12 * path.
13 *
14 * `ConfigArrayFactory` creates `OverrideTester` objects when it processes
15 * `overrides` properties.
16 *
17 * @author Toru Nagashima <https://github.com/mysticatea>
18 */
19
20import assert from "assert";
21import path from "path";
22import util from "util";
23import minimatch from "minimatch";
24
25const { Minimatch } = minimatch;
26
27const minimatchOpts = { dot: true, matchBase: true };
28
29/**
30 * @typedef {Object} Pattern
31 * @property {InstanceType<Minimatch>[] | null} includes The positive matchers.
32 * @property {InstanceType<Minimatch>[] | null} excludes The negative matchers.
33 */
34
35/**
36 * Normalize a given pattern to an array.
37 * @param {string|string[]|undefined} patterns A glob pattern or an array of glob patterns.
38 * @returns {string[]|null} Normalized patterns.
39 * @private
40 */
41function normalizePatterns(patterns) {
42 if (Array.isArray(patterns)) {
43 return patterns.filter(Boolean);
44 }
45 if (typeof patterns === "string" && patterns) {
46 return [patterns];
47 }
48 return [];
49}
50
51/**
52 * Create the matchers of given patterns.
53 * @param {string[]} patterns The patterns.
54 * @returns {InstanceType<Minimatch>[] | null} The matchers.
55 */
56function toMatcher(patterns) {
57 if (patterns.length === 0) {
58 return null;
59 }
60 return patterns.map(pattern => {
61 if (/^\.[/\\]/u.test(pattern)) {
62 return new Minimatch(
63 pattern.slice(2),
64
65 // `./*.js` should not match with `subdir/foo.js`
66 { ...minimatchOpts, matchBase: false }
67 );
68 }
69 return new Minimatch(pattern, minimatchOpts);
70 });
71}
72
73/**
74 * Convert a given matcher to string.
75 * @param {Pattern} matchers The matchers.
76 * @returns {string} The string expression of the matcher.
77 */
78function patternToJson({ includes, excludes }) {
79 return {
80 includes: includes && includes.map(m => m.pattern),
81 excludes: excludes && excludes.map(m => m.pattern)
82 };
83}
84
85/**
86 * The class to test given paths are matched by the patterns.
87 */
88class OverrideTester {
89
90 /**
91 * Create a tester with given criteria.
92 * If there are no criteria, returns `null`.
93 * @param {string|string[]} files The glob patterns for included files.
94 * @param {string|string[]} excludedFiles The glob patterns for excluded files.
95 * @param {string} basePath The path to the base directory to test paths.
96 * @returns {OverrideTester|null} The created instance or `null`.
97 */
98 static create(files, excludedFiles, basePath) {
99 const includePatterns = normalizePatterns(files);
100 const excludePatterns = normalizePatterns(excludedFiles);
101 let endsWithWildcard = false;
102
103 if (includePatterns.length === 0) {
104 return null;
105 }
106
107 // Rejects absolute paths or relative paths to parents.
108 for (const pattern of includePatterns) {
109 if (path.isAbsolute(pattern) || pattern.includes("..")) {
110 throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
111 }
112 if (pattern.endsWith("*")) {
113 endsWithWildcard = true;
114 }
115 }
116 for (const pattern of excludePatterns) {
117 if (path.isAbsolute(pattern) || pattern.includes("..")) {
118 throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
119 }
120 }
121
122 const includes = toMatcher(includePatterns);
123 const excludes = toMatcher(excludePatterns);
124
125 return new OverrideTester(
126 [{ includes, excludes }],
127 basePath,
128 endsWithWildcard
129 );
130 }
131
132 /**
133 * Combine two testers by logical and.
134 * If either of the testers was `null`, returns the other tester.
135 * The `basePath` property of the two must be the same value.
136 * @param {OverrideTester|null} a A tester.
137 * @param {OverrideTester|null} b Another tester.
138 * @returns {OverrideTester|null} Combined tester.
139 */
140 static and(a, b) {
141 if (!b) {
142 return a && new OverrideTester(
143 a.patterns,
144 a.basePath,
145 a.endsWithWildcard
146 );
147 }
148 if (!a) {
149 return new OverrideTester(
150 b.patterns,
151 b.basePath,
152 b.endsWithWildcard
153 );
154 }
155
156 assert.strictEqual(a.basePath, b.basePath);
157 return new OverrideTester(
158 a.patterns.concat(b.patterns),
159 a.basePath,
160 a.endsWithWildcard || b.endsWithWildcard
161 );
162 }
163
164 /**
165 * Initialize this instance.
166 * @param {Pattern[]} patterns The matchers.
167 * @param {string} basePath The base path.
168 * @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`.
169 */
170 constructor(patterns, basePath, endsWithWildcard = false) {
171
172 /** @type {Pattern[]} */
173 this.patterns = patterns;
174
175 /** @type {string} */
176 this.basePath = basePath;
177
178 /** @type {boolean} */
179 this.endsWithWildcard = endsWithWildcard;
180 }
181
182 /**
183 * Test if a given path is matched or not.
184 * @param {string} filePath The absolute path to the target file.
185 * @returns {boolean} `true` if the path was matched.
186 */
187 test(filePath) {
188 if (typeof filePath !== "string" || !path.isAbsolute(filePath)) {
189 throw new Error(`'filePath' should be an absolute path, but got ${filePath}.`);
190 }
191 const relativePath = path.relative(this.basePath, filePath);
192
193 return this.patterns.every(({ includes, excludes }) => (
194 (!includes || includes.some(m => m.match(relativePath))) &&
195 (!excludes || !excludes.some(m => m.match(relativePath)))
196 ));
197 }
198
199 // eslint-disable-next-line jsdoc/require-description
200 /**
201 * @returns {Object} a JSON compatible object.
202 */
203 toJSON() {
204 if (this.patterns.length === 1) {
205 return {
206 ...patternToJson(this.patterns[0]),
207 basePath: this.basePath
208 };
209 }
210 return {
211 AND: this.patterns.map(patternToJson),
212 basePath: this.basePath
213 };
214 }
215
216 // eslint-disable-next-line jsdoc/require-description
217 /**
218 * @returns {Object} an object to display by `console.log()`.
219 */
220 [util.inspect.custom]() {
221 return this.toJSON();
222 }
223}
224
225export { OverrideTester };
Note: See TracBrowser for help on using the repository browser.