source: trip-planner-front/node_modules/ignore/index.js@ 571e0df

Last change on this file since 571e0df was e29cc2e, checked in by Ema <ema_spirova@…>, 3 years ago

primeNG components

  • Property mode set to 100644
File size: 14.9 KB
RevLine 
[6a3a178]1// A simple implementation of make-array
2function makeArray (subject) {
3 return Array.isArray(subject)
4 ? subject
5 : [subject]
6}
7
8const EMPTY = ''
9const SPACE = ' '
10const ESCAPE = '\\'
11const REGEX_TEST_BLANK_LINE = /^\s+$/
12const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/
13const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/
14const REGEX_SPLITALL_CRLF = /\r?\n/g
15// /foo,
16// ./foo,
17// ../foo,
18// .
19// ..
20const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/
21
22const SLASH = '/'
23const KEY_IGNORE = typeof Symbol !== 'undefined'
24 ? Symbol.for('node-ignore')
25 /* istanbul ignore next */
26 : 'node-ignore'
27
28const define = (object, key, value) =>
29 Object.defineProperty(object, key, {value})
30
31const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g
32
33// Sanitize the range of a regular expression
34// The cases are complicated, see test cases for details
35const sanitizeRange = range => range.replace(
36 REGEX_REGEXP_RANGE,
37 (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
38 ? match
39 // Invalid range (out of order) which is ok for gitignore rules but
40 // fatal for JavaScript regular expression, so eliminate it.
41 : EMPTY
42)
43
44// See fixtures #59
45const cleanRangeBackSlash = slashes => {
46 const {length} = slashes
47 return slashes.slice(0, length - length % 2)
48}
49
50// > If the pattern ends with a slash,
51// > it is removed for the purpose of the following description,
52// > but it would only find a match with a directory.
53// > In other words, foo/ will match a directory foo and paths underneath it,
54// > but will not match a regular file or a symbolic link foo
55// > (this is consistent with the way how pathspec works in general in Git).
56// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
57// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
58// you could use option `mark: true` with `glob`
59
60// '`foo/`' should not continue with the '`..`'
61const REPLACERS = [
62
63 // > Trailing spaces are ignored unless they are quoted with backslash ("\")
64 [
65 // (a\ ) -> (a )
66 // (a ) -> (a)
67 // (a \ ) -> (a )
68 /\\?\s+$/,
69 match => match.indexOf('\\') === 0
70 ? SPACE
71 : EMPTY
72 ],
73
74 // replace (\ ) with ' '
75 [
76 /\\\s/g,
77 () => SPACE
78 ],
79
80 // Escape metacharacters
81 // which is written down by users but means special for regular expressions.
82
83 // > There are 12 characters with special meanings:
84 // > - the backslash \,
85 // > - the caret ^,
86 // > - the dollar sign $,
87 // > - the period or dot .,
88 // > - the vertical bar or pipe symbol |,
89 // > - the question mark ?,
90 // > - the asterisk or star *,
91 // > - the plus sign +,
92 // > - the opening parenthesis (,
93 // > - the closing parenthesis ),
94 // > - and the opening square bracket [,
95 // > - the opening curly brace {,
96 // > These special characters are often called "metacharacters".
97 [
98 /[\\$.|*+(){^]/g,
99 match => `\\${match}`
100 ],
101
102 [
103 // > a question mark (?) matches a single character
104 /(?!\\)\?/g,
105 () => '[^/]'
106 ],
107
108 // leading slash
109 [
110
111 // > A leading slash matches the beginning of the pathname.
112 // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
113 // A leading slash matches the beginning of the pathname
114 /^\//,
115 () => '^'
116 ],
117
118 // replace special metacharacter slash after the leading slash
119 [
120 /\//g,
121 () => '\\/'
122 ],
123
124 [
125 // > A leading "**" followed by a slash means match in all directories.
126 // > For example, "**/foo" matches file or directory "foo" anywhere,
127 // > the same as pattern "foo".
128 // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
129 // > under directory "foo".
130 // Notice that the '*'s have been replaced as '\\*'
131 /^\^*\\\*\\\*\\\//,
132
133 // '**/foo' <-> 'foo'
134 () => '^(?:.*\\/)?'
135 ],
136
137 // starting
138 [
139 // there will be no leading '/'
140 // (which has been replaced by section "leading slash")
141 // If starts with '**', adding a '^' to the regular expression also works
142 /^(?=[^^])/,
143 function startingReplacer () {
144 // If has a slash `/` at the beginning or middle
145 return !/\/(?!$)/.test(this)
146 // > Prior to 2.22.1
147 // > If the pattern does not contain a slash /,
148 // > Git treats it as a shell glob pattern
149 // Actually, if there is only a trailing slash,
150 // git also treats it as a shell glob pattern
151
152 // After 2.22.1 (compatible but clearer)
153 // > If there is a separator at the beginning or middle (or both)
154 // > of the pattern, then the pattern is relative to the directory
155 // > level of the particular .gitignore file itself.
156 // > Otherwise the pattern may also match at any level below
157 // > the .gitignore level.
158 ? '(?:^|\\/)'
159
160 // > Otherwise, Git treats the pattern as a shell glob suitable for
161 // > consumption by fnmatch(3)
162 : '^'
163 }
164 ],
165
166 // two globstars
167 [
168 // Use lookahead assertions so that we could match more than one `'/**'`
169 /\\\/\\\*\\\*(?=\\\/|$)/g,
170
171 // Zero, one or several directories
172 // should not use '*', or it will be replaced by the next replacer
173
174 // Check if it is not the last `'/**'`
175 (_, index, str) => index + 6 < str.length
176
177 // case: /**/
178 // > A slash followed by two consecutive asterisks then a slash matches
179 // > zero or more directories.
180 // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
181 // '/**/'
182 ? '(?:\\/[^\\/]+)*'
183
184 // case: /**
185 // > A trailing `"/**"` matches everything inside.
186
187 // #21: everything inside but it should not include the current folder
188 : '\\/.+'
189 ],
190
191 // intermediate wildcards
192 [
193 // Never replace escaped '*'
194 // ignore rule '\*' will match the path '*'
195
196 // 'abc.*/' -> go
197 // 'abc.*' -> skip this rule
198 /(^|[^\\]+)\\\*(?=.+)/g,
199
200 // '*.js' matches '.js'
201 // '*.js' doesn't match 'abc'
202 (_, p1) => `${p1}[^\\/]*`
203 ],
204
205 [
206 // unescape, revert step 3 except for back slash
207 // For example, if a user escape a '\\*',
208 // after step 3, the result will be '\\\\\\*'
209 /\\\\\\(?=[$.|*+(){^])/g,
210 () => ESCAPE
211 ],
212
213 [
214 // '\\\\' -> '\\'
215 /\\\\/g,
216 () => ESCAPE
217 ],
218
219 [
220 // > The range notation, e.g. [a-zA-Z],
221 // > can be used to match one of the characters in a range.
222
223 // `\` is escaped by step 3
224 /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
225 (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
226 // '\\[bar]' -> '\\\\[bar\\]'
227 ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
228 : close === ']'
229 ? endEscape.length % 2 === 0
230 // A normal case, and it is a range notation
231 // '[bar]'
232 // '[bar\\\\]'
233 ? `[${sanitizeRange(range)}${endEscape}]`
234 // Invalid range notaton
235 // '[bar\\]' -> '[bar\\\\]'
236 : '[]'
237 : '[]'
238 ],
239
240 // ending
241 [
242 // 'js' will not match 'js.'
243 // 'ab' will not match 'abc'
244 /(?:[^*])$/,
245
246 // WTF!
247 // https://git-scm.com/docs/gitignore
248 // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
249 // which re-fixes #24, #38
250
251 // > If there is a separator at the end of the pattern then the pattern
252 // > will only match directories, otherwise the pattern can match both
253 // > files and directories.
254
255 // 'js*' will not match 'a.js'
256 // 'js/' will not match 'a.js'
257 // 'js' will match 'a.js' and 'a.js/'
258 match => /\/$/.test(match)
259 // foo/ will not match 'foo'
260 ? `${match}$`
261 // foo matches 'foo' and 'foo/'
262 : `${match}(?=$|\\/$)`
263 ],
264
265 // trailing wildcard
266 [
267 /(\^|\\\/)?\\\*$/,
268 (_, p1) => {
269 const prefix = p1
270 // '\^':
271 // '/*' does not match EMPTY
272 // '/*' does not match everything
273
274 // '\\\/':
275 // 'abc/*' does not match 'abc/'
276 ? `${p1}[^/]+`
277
278 // 'a*' matches 'a'
279 // 'a*' matches 'aa'
280 : '[^/]*'
281
282 return `${prefix}(?=$|\\/$)`
283 }
284 ],
285]
286
287// A simple cache, because an ignore rule only has only one certain meaning
288const regexCache = Object.create(null)
289
290// @param {pattern}
[e29cc2e]291const makeRegex = (pattern, ignorecase) => {
292 let source = regexCache[pattern]
[6a3a178]293
[e29cc2e]294 if (!source) {
295 source = REPLACERS.reduce(
296 (prev, current) => prev.replace(current[0], current[1].bind(pattern)),
297 pattern
298 )
299 regexCache[pattern] = source
300 }
[6a3a178]301
[e29cc2e]302 return ignorecase
[6a3a178]303 ? new RegExp(source, 'i')
304 : new RegExp(source)
305}
306
307const isString = subject => typeof subject === 'string'
308
309// > A blank line matches no files, so it can serve as a separator for readability.
310const checkPattern = pattern => pattern
311 && isString(pattern)
312 && !REGEX_TEST_BLANK_LINE.test(pattern)
313
314 // > A line starting with # serves as a comment.
315 && pattern.indexOf('#') !== 0
316
317const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF)
318
319class IgnoreRule {
320 constructor (
321 origin,
322 pattern,
323 negative,
324 regex
325 ) {
326 this.origin = origin
327 this.pattern = pattern
328 this.negative = negative
329 this.regex = regex
330 }
331}
332
333const createRule = (pattern, ignorecase) => {
334 const origin = pattern
335 let negative = false
336
337 // > An optional prefix "!" which negates the pattern;
338 if (pattern.indexOf('!') === 0) {
339 negative = true
340 pattern = pattern.substr(1)
341 }
342
343 pattern = pattern
344 // > Put a backslash ("\") in front of the first "!" for patterns that
345 // > begin with a literal "!", for example, `"\!important!.txt"`.
346 .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
347 // > Put a backslash ("\") in front of the first hash for patterns that
348 // > begin with a hash.
349 .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#')
350
[e29cc2e]351 const regex = makeRegex(pattern, ignorecase)
[6a3a178]352
353 return new IgnoreRule(
354 origin,
355 pattern,
356 negative,
357 regex
358 )
359}
360
361const throwError = (message, Ctor) => {
362 throw new Ctor(message)
363}
364
365const checkPath = (path, originalPath, doThrow) => {
366 if (!isString(path)) {
367 return doThrow(
368 `path must be a string, but got \`${originalPath}\``,
369 TypeError
370 )
371 }
372
373 // We don't know if we should ignore EMPTY, so throw
374 if (!path) {
375 return doThrow(`path must not be empty`, TypeError)
376 }
377
378 // Check if it is a relative path
379 if (checkPath.isNotRelative(path)) {
380 const r = '`path.relative()`d'
381 return doThrow(
382 `path should be a ${r} string, but got "${originalPath}"`,
383 RangeError
384 )
385 }
386
387 return true
388}
389
390const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path)
391
392checkPath.isNotRelative = isNotRelative
393checkPath.convert = p => p
394
395class Ignore {
396 constructor ({
397 ignorecase = true
398 } = {}) {
[e29cc2e]399 define(this, KEY_IGNORE, true)
400
[6a3a178]401 this._rules = []
402 this._ignorecase = ignorecase
403 this._initCache()
404 }
405
406 _initCache () {
407 this._ignoreCache = Object.create(null)
408 this._testCache = Object.create(null)
409 }
410
411 _addPattern (pattern) {
412 // #32
413 if (pattern && pattern[KEY_IGNORE]) {
414 this._rules = this._rules.concat(pattern._rules)
415 this._added = true
416 return
417 }
418
419 if (checkPattern(pattern)) {
420 const rule = createRule(pattern, this._ignorecase)
421 this._added = true
422 this._rules.push(rule)
423 }
424 }
425
426 // @param {Array<string> | string | Ignore} pattern
427 add (pattern) {
428 this._added = false
429
430 makeArray(
431 isString(pattern)
432 ? splitPattern(pattern)
433 : pattern
434 ).forEach(this._addPattern, this)
435
436 // Some rules have just added to the ignore,
437 // making the behavior changed.
438 if (this._added) {
439 this._initCache()
440 }
441
442 return this
443 }
444
445 // legacy
446 addPattern (pattern) {
447 return this.add(pattern)
448 }
449
450 // | ignored : unignored
451 // negative | 0:0 | 0:1 | 1:0 | 1:1
452 // -------- | ------- | ------- | ------- | --------
453 // 0 | TEST | TEST | SKIP | X
454 // 1 | TESTIF | SKIP | TEST | X
455
456 // - SKIP: always skip
457 // - TEST: always test
458 // - TESTIF: only test if checkUnignored
459 // - X: that never happen
460
461 // @param {boolean} whether should check if the path is unignored,
462 // setting `checkUnignored` to `false` could reduce additional
463 // path matching.
464
465 // @returns {TestResult} true if a file is ignored
466 _testOne (path, checkUnignored) {
467 let ignored = false
468 let unignored = false
469
470 this._rules.forEach(rule => {
471 const {negative} = rule
472 if (
473 unignored === negative && ignored !== unignored
474 || negative && !ignored && !unignored && !checkUnignored
475 ) {
476 return
477 }
478
479 const matched = rule.regex.test(path)
480
481 if (matched) {
482 ignored = !negative
483 unignored = negative
484 }
485 })
486
487 return {
488 ignored,
489 unignored
490 }
491 }
492
493 // @returns {TestResult}
494 _test (originalPath, cache, checkUnignored, slices) {
495 const path = originalPath
496 // Supports nullable path
497 && checkPath.convert(originalPath)
498
499 checkPath(path, originalPath, throwError)
500
501 return this._t(path, cache, checkUnignored, slices)
502 }
503
504 _t (path, cache, checkUnignored, slices) {
505 if (path in cache) {
506 return cache[path]
507 }
508
509 if (!slices) {
510 // path/to/a.js
511 // ['path', 'to', 'a.js']
512 slices = path.split(SLASH)
513 }
514
515 slices.pop()
516
517 // If the path has no parent directory, just test it
518 if (!slices.length) {
519 return cache[path] = this._testOne(path, checkUnignored)
520 }
521
522 const parent = this._t(
523 slices.join(SLASH) + SLASH,
524 cache,
525 checkUnignored,
526 slices
527 )
528
529 // If the path contains a parent directory, check the parent first
530 return cache[path] = parent.ignored
531 // > It is not possible to re-include a file if a parent directory of
532 // > that file is excluded.
533 ? parent
534 : this._testOne(path, checkUnignored)
535 }
536
537 ignores (path) {
538 return this._test(path, this._ignoreCache, false).ignored
539 }
540
541 createFilter () {
542 return path => !this.ignores(path)
543 }
544
545 filter (paths) {
546 return makeArray(paths).filter(this.createFilter())
547 }
548
549 // @returns {TestResult}
550 test (path) {
551 return this._test(path, this._testCache, true)
552 }
553}
554
555const factory = options => new Ignore(options)
556
557const returnFalse = () => false
558
559const isPathValid = path =>
560 checkPath(path && checkPath.convert(path), path, returnFalse)
561
562factory.isPathValid = isPathValid
563
564// Fixes typescript
565factory.default = factory
566
567module.exports = factory
568
569// Windows
570// --------------------------------------------------------------
571/* istanbul ignore if */
572if (
573 // Detect `process` so that it can run in browsers.
574 typeof process !== 'undefined'
575 && (
576 process.env && process.env.IGNORE_TEST_WIN32
577 || process.platform === 'win32'
578 )
579) {
580 /* eslint no-control-regex: "off" */
581 const makePosix = str => /^\\\\\?\\/.test(str)
582 || /["<>|\u0000-\u001F]+/u.test(str)
583 ? str
584 : str.replace(/\\/g, '/')
585
586 checkPath.convert = makePosix
587
588 // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
589 // 'd:\\foo'
590 const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i
591 checkPath.isNotRelative = path =>
592 REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path)
593 || isNotRelative(path)
594}
Note: See TracBrowser for help on using the repository browser.