.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / @eslint / eslintrc / lib / config-array / ignore-pattern.js
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
36 const assert = require("assert");
37 const path = require("path");
38 const ignore = require("ignore");
39 const debug = require("debug")("eslintrc: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  */
52 function 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
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;
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  */
89 function 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  */
103 function dirSuffix(filePath) {
104     const isDir = (
105         filePath.endsWith(path.sep) ||
106         (process.platform === "win32" && filePath.endsWith("/"))
107     );
108
109     return isDir ? "/" : "";
110 }
111
112 const DefaultPatterns = Object.freeze(["/**/node_modules/*"]);
113 const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
114
115 //------------------------------------------------------------------------------
116 // Public
117 //------------------------------------------------------------------------------
118
119 class 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
237 module.exports = { IgnorePattern };