html-validate 9.5.5 → 9.6.1

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/es/core.js CHANGED
@@ -6020,6 +6020,9 @@ class ElementName extends Rule {
6020
6020
  }
6021
6021
  }
6022
6022
 
6023
+ function isNativeTemplate(node) {
6024
+ return Boolean(node.tagName === "template" && node.meta?.scriptSupporting);
6025
+ }
6023
6026
  function getTransparentChildren(node, transparent) {
6024
6027
  if (typeof transparent === "boolean") {
6025
6028
  return node.childElements;
@@ -6093,7 +6096,7 @@ class ElementPermittedContent extends Rule {
6093
6096
  return false;
6094
6097
  }
6095
6098
  validatePermittedDescendant(node, parent) {
6096
- for (let cur = parent; cur && !cur.isRootElement() && cur.tagName !== "template"; cur = /* istanbul ignore next */
6099
+ for (let cur = parent; cur && !cur.isRootElement() && !isNativeTemplate(cur); cur = /* istanbul ignore next */
6097
6100
  cur.parent ?? null) {
6098
6101
  const meta = cur.meta;
6099
6102
  if (!meta) {
@@ -11720,7 +11723,7 @@ class EventHandler {
11720
11723
  }
11721
11724
 
11722
11725
  const name = "html-validate";
11723
- const version = "9.5.5";
11726
+ const version = "9.6.1";
11724
11727
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11725
11728
 
11726
11729
  function freeze(src) {
@@ -13324,785 +13327,792 @@ var hasRequiredIgnore;
13324
13327
  function requireIgnore () {
13325
13328
  if (hasRequiredIgnore) return ignore$1.exports;
13326
13329
  hasRequiredIgnore = 1;
13327
- // A simple implementation of make-array
13328
- function makeArray (subject) {
13329
- return Array.isArray(subject)
13330
- ? subject
13331
- : [subject]
13332
- }
13333
-
13334
- const UNDEFINED = undefined;
13335
- const EMPTY = '';
13336
- const SPACE = ' ';
13337
- const ESCAPE = '\\';
13338
- const REGEX_TEST_BLANK_LINE = /^\s+$/;
13339
- const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13340
- const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13341
- const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13342
- const REGEX_SPLITALL_CRLF = /\r?\n/g;
13343
-
13344
- // Invalid:
13345
- // - /foo,
13346
- // - ./foo,
13347
- // - ../foo,
13348
- // - .
13349
- // - ..
13350
- // Valid:
13351
- // - .foo
13352
- const REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
13353
-
13354
- const REGEX_TEST_TRAILING_SLASH = /\/$/;
13355
-
13356
- const SLASH = '/';
13357
-
13358
- // Do not use ternary expression here, since "istanbul ignore next" is buggy
13359
- let TMP_KEY_IGNORE = 'node-ignore';
13360
- /* istanbul ignore else */
13361
- if (typeof Symbol !== 'undefined') {
13362
- TMP_KEY_IGNORE = Symbol.for('node-ignore');
13363
- }
13364
- const KEY_IGNORE = TMP_KEY_IGNORE;
13365
-
13366
- const define = (object, key, value) => {
13367
- Object.defineProperty(object, key, {value});
13368
- return value
13369
- };
13370
-
13371
- const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13372
-
13373
- const RETURN_FALSE = () => false;
13374
-
13375
- // Sanitize the range of a regular expression
13376
- // The cases are complicated, see test cases for details
13377
- const sanitizeRange = range => range.replace(
13378
- REGEX_REGEXP_RANGE,
13379
- (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13380
- ? match
13381
- // Invalid range (out of order) which is ok for gitignore rules but
13382
- // fatal for JavaScript regular expression, so eliminate it.
13383
- : EMPTY
13384
- );
13385
-
13386
- // See fixtures #59
13387
- const cleanRangeBackSlash = slashes => {
13388
- const {length} = slashes;
13389
- return slashes.slice(0, length - length % 2)
13390
- };
13391
-
13392
- // > If the pattern ends with a slash,
13393
- // > it is removed for the purpose of the following description,
13394
- // > but it would only find a match with a directory.
13395
- // > In other words, foo/ will match a directory foo and paths underneath it,
13396
- // > but will not match a regular file or a symbolic link foo
13397
- // > (this is consistent with the way how pathspec works in general in Git).
13398
- // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13399
- // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13400
- // you could use option `mark: true` with `glob`
13401
-
13402
- // '`foo/`' should not continue with the '`..`'
13403
- const REPLACERS = [
13404
-
13405
- [
13406
- // Remove BOM
13407
- // TODO:
13408
- // Other similar zero-width characters?
13409
- /^\uFEFF/,
13410
- () => EMPTY
13411
- ],
13412
-
13413
- // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13414
- [
13415
- // (a\ ) -> (a )
13416
- // (a ) -> (a)
13417
- // (a ) -> (a)
13418
- // (a \ ) -> (a )
13419
- /((?:\\\\)*?)(\\?\s+)$/,
13420
- (_, m1, m2) => m1 + (
13421
- m2.indexOf('\\') === 0
13422
- ? SPACE
13423
- : EMPTY
13424
- )
13425
- ],
13426
-
13427
- // Replace (\ ) with ' '
13428
- // (\ ) -> ' '
13429
- // (\\ ) -> '\\ '
13430
- // (\\\ ) -> '\\ '
13431
- [
13432
- /(\\+?)\s/g,
13433
- (_, m1) => {
13434
- const {length} = m1;
13435
- return m1.slice(0, length - length % 2) + SPACE
13436
- }
13437
- ],
13438
-
13439
- // Escape metacharacters
13440
- // which is written down by users but means special for regular expressions.
13441
-
13442
- // > There are 12 characters with special meanings:
13443
- // > - the backslash \,
13444
- // > - the caret ^,
13445
- // > - the dollar sign $,
13446
- // > - the period or dot .,
13447
- // > - the vertical bar or pipe symbol |,
13448
- // > - the question mark ?,
13449
- // > - the asterisk or star *,
13450
- // > - the plus sign +,
13451
- // > - the opening parenthesis (,
13452
- // > - the closing parenthesis ),
13453
- // > - and the opening square bracket [,
13454
- // > - the opening curly brace {,
13455
- // > These special characters are often called "metacharacters".
13456
- [
13457
- /[\\$.|*+(){^]/g,
13458
- match => `\\${match}`
13459
- ],
13460
-
13461
- [
13462
- // > a question mark (?) matches a single character
13463
- /(?!\\)\?/g,
13464
- () => '[^/]'
13465
- ],
13466
-
13467
- // leading slash
13468
- [
13469
-
13470
- // > A leading slash matches the beginning of the pathname.
13471
- // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13472
- // A leading slash matches the beginning of the pathname
13473
- /^\//,
13474
- () => '^'
13475
- ],
13476
-
13477
- // replace special metacharacter slash after the leading slash
13478
- [
13479
- /\//g,
13480
- () => '\\/'
13481
- ],
13482
-
13483
- [
13484
- // > A leading "**" followed by a slash means match in all directories.
13485
- // > For example, "**/foo" matches file or directory "foo" anywhere,
13486
- // > the same as pattern "foo".
13487
- // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13488
- // > under directory "foo".
13489
- // Notice that the '*'s have been replaced as '\\*'
13490
- /^\^*\\\*\\\*\\\//,
13491
-
13492
- // '**/foo' <-> 'foo'
13493
- () => '^(?:.*\\/)?'
13494
- ],
13495
-
13496
- // starting
13497
- [
13498
- // there will be no leading '/'
13499
- // (which has been replaced by section "leading slash")
13500
- // If starts with '**', adding a '^' to the regular expression also works
13501
- /^(?=[^^])/,
13502
- function startingReplacer () {
13503
- // If has a slash `/` at the beginning or middle
13504
- return !/\/(?!$)/.test(this)
13505
- // > Prior to 2.22.1
13506
- // > If the pattern does not contain a slash /,
13507
- // > Git treats it as a shell glob pattern
13508
- // Actually, if there is only a trailing slash,
13509
- // git also treats it as a shell glob pattern
13510
-
13511
- // After 2.22.1 (compatible but clearer)
13512
- // > If there is a separator at the beginning or middle (or both)
13513
- // > of the pattern, then the pattern is relative to the directory
13514
- // > level of the particular .gitignore file itself.
13515
- // > Otherwise the pattern may also match at any level below
13516
- // > the .gitignore level.
13517
- ? '(?:^|\\/)'
13518
-
13519
- // > Otherwise, Git treats the pattern as a shell glob suitable for
13520
- // > consumption by fnmatch(3)
13521
- : '^'
13522
- }
13523
- ],
13524
-
13525
- // two globstars
13526
- [
13527
- // Use lookahead assertions so that we could match more than one `'/**'`
13528
- /\\\/\\\*\\\*(?=\\\/|$)/g,
13529
-
13530
- // Zero, one or several directories
13531
- // should not use '*', or it will be replaced by the next replacer
13532
-
13533
- // Check if it is not the last `'/**'`
13534
- (_, index, str) => index + 6 < str.length
13535
-
13536
- // case: /**/
13537
- // > A slash followed by two consecutive asterisks then a slash matches
13538
- // > zero or more directories.
13539
- // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13540
- // '/**/'
13541
- ? '(?:\\/[^\\/]+)*'
13542
-
13543
- // case: /**
13544
- // > A trailing `"/**"` matches everything inside.
13545
-
13546
- // #21: everything inside but it should not include the current folder
13547
- : '\\/.+'
13548
- ],
13549
-
13550
- // normal intermediate wildcards
13551
- [
13552
- // Never replace escaped '*'
13553
- // ignore rule '\*' will match the path '*'
13554
-
13555
- // 'abc.*/' -> go
13556
- // 'abc.*' -> skip this rule,
13557
- // coz trailing single wildcard will be handed by [trailing wildcard]
13558
- /(^|[^\\]+)(\\\*)+(?=.+)/g,
13559
-
13560
- // '*.js' matches '.js'
13561
- // '*.js' doesn't match 'abc'
13562
- (_, p1, p2) => {
13563
- // 1.
13564
- // > An asterisk "*" matches anything except a slash.
13565
- // 2.
13566
- // > Other consecutive asterisks are considered regular asterisks
13567
- // > and will match according to the previous rules.
13568
- const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13569
- return p1 + unescaped
13570
- }
13571
- ],
13572
-
13573
- [
13574
- // unescape, revert step 3 except for back slash
13575
- // For example, if a user escape a '\\*',
13576
- // after step 3, the result will be '\\\\\\*'
13577
- /\\\\\\(?=[$.|*+(){^])/g,
13578
- () => ESCAPE
13579
- ],
13580
-
13581
- [
13582
- // '\\\\' -> '\\'
13583
- /\\\\/g,
13584
- () => ESCAPE
13585
- ],
13586
-
13587
- [
13588
- // > The range notation, e.g. [a-zA-Z],
13589
- // > can be used to match one of the characters in a range.
13590
-
13591
- // `\` is escaped by step 3
13592
- /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13593
- (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13594
- // '\\[bar]' -> '\\\\[bar\\]'
13595
- ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13596
- : close === ']'
13597
- ? endEscape.length % 2 === 0
13598
- // A normal case, and it is a range notation
13599
- // '[bar]'
13600
- // '[bar\\\\]'
13601
- ? `[${sanitizeRange(range)}${endEscape}]`
13602
- // Invalid range notaton
13603
- // '[bar\\]' -> '[bar\\\\]'
13604
- : '[]'
13605
- : '[]'
13606
- ],
13607
-
13608
- // ending
13609
- [
13610
- // 'js' will not match 'js.'
13611
- // 'ab' will not match 'abc'
13612
- /(?:[^*])$/,
13613
-
13614
- // WTF!
13615
- // https://git-scm.com/docs/gitignore
13616
- // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13617
- // which re-fixes #24, #38
13618
-
13619
- // > If there is a separator at the end of the pattern then the pattern
13620
- // > will only match directories, otherwise the pattern can match both
13621
- // > files and directories.
13622
-
13623
- // 'js*' will not match 'a.js'
13624
- // 'js/' will not match 'a.js'
13625
- // 'js' will match 'a.js' and 'a.js/'
13626
- match => /\/$/.test(match)
13627
- // foo/ will not match 'foo'
13628
- ? `${match}$`
13629
- // foo matches 'foo' and 'foo/'
13630
- : `${match}(?=$|\\/$)`
13631
- ]
13632
- ];
13633
-
13634
- const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
13635
- const MODE_IGNORE = 'regex';
13636
- const MODE_CHECK_IGNORE = 'checkRegex';
13637
- const UNDERSCORE = '_';
13638
-
13639
- const TRAILING_WILD_CARD_REPLACERS = {
13640
- [MODE_IGNORE] (_, p1) {
13641
- const prefix = p1
13642
- // '\^':
13643
- // '/*' does not match EMPTY
13644
- // '/*' does not match everything
13645
-
13646
- // '\\\/':
13647
- // 'abc/*' does not match 'abc/'
13648
- ? `${p1}[^/]+`
13649
-
13650
- // 'a*' matches 'a'
13651
- // 'a*' matches 'aa'
13652
- : '[^/]*';
13653
-
13654
- return `${prefix}(?=$|\\/$)`
13655
- },
13656
-
13657
- [MODE_CHECK_IGNORE] (_, p1) {
13658
- // When doing `git check-ignore`
13659
- const prefix = p1
13660
- // '\\\/':
13661
- // 'abc/*' DOES match 'abc/' !
13662
- ? `${p1}[^/]*`
13663
-
13664
- // 'a*' matches 'a'
13665
- // 'a*' matches 'aa'
13666
- : '[^/]*';
13667
-
13668
- return `${prefix}(?=$|\\/$)`
13669
- }
13670
- };
13671
-
13672
- // @param {pattern}
13673
- const makeRegexPrefix = pattern => REPLACERS.reduce(
13674
- (prev, [matcher, replacer]) =>
13675
- prev.replace(matcher, replacer.bind(pattern)),
13676
- pattern
13677
- );
13678
-
13679
- const isString = subject => typeof subject === 'string';
13680
-
13681
- // > A blank line matches no files, so it can serve as a separator for readability.
13682
- const checkPattern = pattern => pattern
13683
- && isString(pattern)
13684
- && !REGEX_TEST_BLANK_LINE.test(pattern)
13685
- && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
13686
-
13687
- // > A line starting with # serves as a comment.
13688
- && pattern.indexOf('#') !== 0;
13689
-
13690
- const splitPattern = pattern => pattern
13691
- .split(REGEX_SPLITALL_CRLF)
13692
- .filter(Boolean);
13693
-
13694
- class IgnoreRule {
13695
- constructor (
13696
- pattern,
13697
- mark,
13698
- body,
13699
- ignoreCase,
13700
- negative,
13701
- prefix
13702
- ) {
13703
- this.pattern = pattern;
13704
- this.mark = mark;
13705
- this.negative = negative;
13706
-
13707
- define(this, 'body', body);
13708
- define(this, 'ignoreCase', ignoreCase);
13709
- define(this, 'regexPrefix', prefix);
13710
- }
13711
-
13712
- get regex () {
13713
- const key = UNDERSCORE + MODE_IGNORE;
13714
-
13715
- if (this[key]) {
13716
- return this[key]
13717
- }
13718
-
13719
- return this._make(MODE_IGNORE, key)
13720
- }
13721
-
13722
- get checkRegex () {
13723
- const key = UNDERSCORE + MODE_CHECK_IGNORE;
13724
-
13725
- if (this[key]) {
13726
- return this[key]
13727
- }
13728
-
13729
- return this._make(MODE_CHECK_IGNORE, key)
13730
- }
13731
-
13732
- _make (mode, key) {
13733
- const str = this.regexPrefix.replace(
13734
- REGEX_REPLACE_TRAILING_WILDCARD,
13735
-
13736
- // It does not need to bind pattern
13737
- TRAILING_WILD_CARD_REPLACERS[mode]
13738
- );
13739
-
13740
- const regex = this.ignoreCase
13741
- ? new RegExp(str, 'i')
13742
- : new RegExp(str);
13743
-
13744
- return define(this, key, regex)
13745
- }
13746
- }
13747
-
13748
- const createRule = ({
13749
- pattern,
13750
- mark
13751
- }, ignoreCase) => {
13752
- let negative = false;
13753
- let body = pattern;
13754
-
13755
- // > An optional prefix "!" which negates the pattern;
13756
- if (body.indexOf('!') === 0) {
13757
- negative = true;
13758
- body = body.substr(1);
13759
- }
13760
-
13761
- body = body
13762
- // > Put a backslash ("\") in front of the first "!" for patterns that
13763
- // > begin with a literal "!", for example, `"\!important!.txt"`.
13764
- .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
13765
- // > Put a backslash ("\") in front of the first hash for patterns that
13766
- // > begin with a hash.
13767
- .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
13768
-
13769
- const regexPrefix = makeRegexPrefix(body);
13770
-
13771
- return new IgnoreRule(
13772
- pattern,
13773
- mark,
13774
- body,
13775
- ignoreCase,
13776
- negative,
13777
- regexPrefix
13778
- )
13779
- };
13330
+ (function (module) {
13331
+ // A simple implementation of make-array
13332
+ function makeArray (subject) {
13333
+ return Array.isArray(subject)
13334
+ ? subject
13335
+ : [subject]
13336
+ }
13780
13337
 
13781
- class RuleManager {
13782
- constructor (ignoreCase) {
13783
- this._ignoreCase = ignoreCase;
13784
- this._rules = [];
13785
- }
13786
-
13787
- _add (pattern) {
13788
- // #32
13789
- if (pattern && pattern[KEY_IGNORE]) {
13790
- this._rules = this._rules.concat(pattern._rules._rules);
13791
- this._added = true;
13792
- return
13793
- }
13794
-
13795
- if (isString(pattern)) {
13796
- pattern = {
13797
- pattern
13798
- };
13799
- }
13800
-
13801
- if (checkPattern(pattern.pattern)) {
13802
- const rule = createRule(pattern, this._ignoreCase);
13803
- this._added = true;
13804
- this._rules.push(rule);
13805
- }
13806
- }
13807
-
13808
- // @param {Array<string> | string | Ignore} pattern
13809
- add (pattern) {
13810
- this._added = false;
13811
-
13812
- makeArray(
13813
- isString(pattern)
13814
- ? splitPattern(pattern)
13815
- : pattern
13816
- ).forEach(this._add, this);
13817
-
13818
- return this._added
13819
- }
13820
-
13821
- // Test one single path without recursively checking parent directories
13822
- //
13823
- // - checkUnignored `boolean` whether should check if the path is unignored,
13824
- // setting `checkUnignored` to `false` could reduce additional
13825
- // path matching.
13826
- // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
13827
-
13828
- // @returns {TestResult} true if a file is ignored
13829
- test (path, checkUnignored, mode) {
13830
- let ignored = false;
13831
- let unignored = false;
13832
- let matchedRule;
13833
-
13834
- this._rules.forEach(rule => {
13835
- const {negative} = rule;
13836
-
13837
- // | ignored : unignored
13838
- // -------- | ---------------------------------------
13839
- // negative | 0:0 | 0:1 | 1:0 | 1:1
13840
- // -------- | ------- | ------- | ------- | --------
13841
- // 0 | TEST | TEST | SKIP | X
13842
- // 1 | TESTIF | SKIP | TEST | X
13843
-
13844
- // - SKIP: always skip
13845
- // - TEST: always test
13846
- // - TESTIF: only test if checkUnignored
13847
- // - X: that never happen
13848
- if (
13849
- unignored === negative && ignored !== unignored
13850
- || negative && !ignored && !unignored && !checkUnignored
13851
- ) {
13852
- return
13853
- }
13854
-
13855
- const matched = rule[mode].test(path);
13856
-
13857
- if (!matched) {
13858
- return
13859
- }
13860
-
13861
- ignored = !negative;
13862
- unignored = negative;
13863
-
13864
- matchedRule = negative
13865
- ? UNDEFINED
13866
- : rule;
13867
- });
13868
-
13869
- const ret = {
13870
- ignored,
13871
- unignored
13872
- };
13873
-
13874
- if (matchedRule) {
13875
- ret.rule = matchedRule;
13876
- }
13877
-
13878
- return ret
13879
- }
13880
- }
13338
+ const UNDEFINED = undefined;
13339
+ const EMPTY = '';
13340
+ const SPACE = ' ';
13341
+ const ESCAPE = '\\';
13342
+ const REGEX_TEST_BLANK_LINE = /^\s+$/;
13343
+ const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13344
+ const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13345
+ const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13346
+ const REGEX_SPLITALL_CRLF = /\r?\n/g;
13347
+
13348
+ // Invalid:
13349
+ // - /foo,
13350
+ // - ./foo,
13351
+ // - ../foo,
13352
+ // - .
13353
+ // - ..
13354
+ // Valid:
13355
+ // - .foo
13356
+ const REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
13357
+
13358
+ const REGEX_TEST_TRAILING_SLASH = /\/$/;
13359
+
13360
+ const SLASH = '/';
13361
+
13362
+ // Do not use ternary expression here, since "istanbul ignore next" is buggy
13363
+ let TMP_KEY_IGNORE = 'node-ignore';
13364
+ /* istanbul ignore else */
13365
+ if (typeof Symbol !== 'undefined') {
13366
+ TMP_KEY_IGNORE = Symbol.for('node-ignore');
13367
+ }
13368
+ const KEY_IGNORE = TMP_KEY_IGNORE;
13369
+
13370
+ const define = (object, key, value) => {
13371
+ Object.defineProperty(object, key, {value});
13372
+ return value
13373
+ };
13374
+
13375
+ const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13376
+
13377
+ const RETURN_FALSE = () => false;
13378
+
13379
+ // Sanitize the range of a regular expression
13380
+ // The cases are complicated, see test cases for details
13381
+ const sanitizeRange = range => range.replace(
13382
+ REGEX_REGEXP_RANGE,
13383
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13384
+ ? match
13385
+ // Invalid range (out of order) which is ok for gitignore rules but
13386
+ // fatal for JavaScript regular expression, so eliminate it.
13387
+ : EMPTY
13388
+ );
13389
+
13390
+ // See fixtures #59
13391
+ const cleanRangeBackSlash = slashes => {
13392
+ const {length} = slashes;
13393
+ return slashes.slice(0, length - length % 2)
13394
+ };
13395
+
13396
+ // > If the pattern ends with a slash,
13397
+ // > it is removed for the purpose of the following description,
13398
+ // > but it would only find a match with a directory.
13399
+ // > In other words, foo/ will match a directory foo and paths underneath it,
13400
+ // > but will not match a regular file or a symbolic link foo
13401
+ // > (this is consistent with the way how pathspec works in general in Git).
13402
+ // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13403
+ // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13404
+ // you could use option `mark: true` with `glob`
13405
+
13406
+ // '`foo/`' should not continue with the '`..`'
13407
+ const REPLACERS = [
13408
+
13409
+ [
13410
+ // Remove BOM
13411
+ // TODO:
13412
+ // Other similar zero-width characters?
13413
+ /^\uFEFF/,
13414
+ () => EMPTY
13415
+ ],
13416
+
13417
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13418
+ [
13419
+ // (a\ ) -> (a )
13420
+ // (a ) -> (a)
13421
+ // (a ) -> (a)
13422
+ // (a \ ) -> (a )
13423
+ /((?:\\\\)*?)(\\?\s+)$/,
13424
+ (_, m1, m2) => m1 + (
13425
+ m2.indexOf('\\') === 0
13426
+ ? SPACE
13427
+ : EMPTY
13428
+ )
13429
+ ],
13430
+
13431
+ // Replace (\ ) with ' '
13432
+ // (\ ) -> ' '
13433
+ // (\\ ) -> '\\ '
13434
+ // (\\\ ) -> '\\ '
13435
+ [
13436
+ /(\\+?)\s/g,
13437
+ (_, m1) => {
13438
+ const {length} = m1;
13439
+ return m1.slice(0, length - length % 2) + SPACE
13440
+ }
13441
+ ],
13442
+
13443
+ // Escape metacharacters
13444
+ // which is written down by users but means special for regular expressions.
13445
+
13446
+ // > There are 12 characters with special meanings:
13447
+ // > - the backslash \,
13448
+ // > - the caret ^,
13449
+ // > - the dollar sign $,
13450
+ // > - the period or dot .,
13451
+ // > - the vertical bar or pipe symbol |,
13452
+ // > - the question mark ?,
13453
+ // > - the asterisk or star *,
13454
+ // > - the plus sign +,
13455
+ // > - the opening parenthesis (,
13456
+ // > - the closing parenthesis ),
13457
+ // > - and the opening square bracket [,
13458
+ // > - the opening curly brace {,
13459
+ // > These special characters are often called "metacharacters".
13460
+ [
13461
+ /[\\$.|*+(){^]/g,
13462
+ match => `\\${match}`
13463
+ ],
13464
+
13465
+ [
13466
+ // > a question mark (?) matches a single character
13467
+ /(?!\\)\?/g,
13468
+ () => '[^/]'
13469
+ ],
13470
+
13471
+ // leading slash
13472
+ [
13473
+
13474
+ // > A leading slash matches the beginning of the pathname.
13475
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13476
+ // A leading slash matches the beginning of the pathname
13477
+ /^\//,
13478
+ () => '^'
13479
+ ],
13480
+
13481
+ // replace special metacharacter slash after the leading slash
13482
+ [
13483
+ /\//g,
13484
+ () => '\\/'
13485
+ ],
13486
+
13487
+ [
13488
+ // > A leading "**" followed by a slash means match in all directories.
13489
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
13490
+ // > the same as pattern "foo".
13491
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13492
+ // > under directory "foo".
13493
+ // Notice that the '*'s have been replaced as '\\*'
13494
+ /^\^*\\\*\\\*\\\//,
13495
+
13496
+ // '**/foo' <-> 'foo'
13497
+ () => '^(?:.*\\/)?'
13498
+ ],
13499
+
13500
+ // starting
13501
+ [
13502
+ // there will be no leading '/'
13503
+ // (which has been replaced by section "leading slash")
13504
+ // If starts with '**', adding a '^' to the regular expression also works
13505
+ /^(?=[^^])/,
13506
+ function startingReplacer () {
13507
+ // If has a slash `/` at the beginning or middle
13508
+ return !/\/(?!$)/.test(this)
13509
+ // > Prior to 2.22.1
13510
+ // > If the pattern does not contain a slash /,
13511
+ // > Git treats it as a shell glob pattern
13512
+ // Actually, if there is only a trailing slash,
13513
+ // git also treats it as a shell glob pattern
13514
+
13515
+ // After 2.22.1 (compatible but clearer)
13516
+ // > If there is a separator at the beginning or middle (or both)
13517
+ // > of the pattern, then the pattern is relative to the directory
13518
+ // > level of the particular .gitignore file itself.
13519
+ // > Otherwise the pattern may also match at any level below
13520
+ // > the .gitignore level.
13521
+ ? '(?:^|\\/)'
13522
+
13523
+ // > Otherwise, Git treats the pattern as a shell glob suitable for
13524
+ // > consumption by fnmatch(3)
13525
+ : '^'
13526
+ }
13527
+ ],
13528
+
13529
+ // two globstars
13530
+ [
13531
+ // Use lookahead assertions so that we could match more than one `'/**'`
13532
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
13533
+
13534
+ // Zero, one or several directories
13535
+ // should not use '*', or it will be replaced by the next replacer
13536
+
13537
+ // Check if it is not the last `'/**'`
13538
+ (_, index, str) => index + 6 < str.length
13539
+
13540
+ // case: /**/
13541
+ // > A slash followed by two consecutive asterisks then a slash matches
13542
+ // > zero or more directories.
13543
+ // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13544
+ // '/**/'
13545
+ ? '(?:\\/[^\\/]+)*'
13546
+
13547
+ // case: /**
13548
+ // > A trailing `"/**"` matches everything inside.
13549
+
13550
+ // #21: everything inside but it should not include the current folder
13551
+ : '\\/.+'
13552
+ ],
13553
+
13554
+ // normal intermediate wildcards
13555
+ [
13556
+ // Never replace escaped '*'
13557
+ // ignore rule '\*' will match the path '*'
13558
+
13559
+ // 'abc.*/' -> go
13560
+ // 'abc.*' -> skip this rule,
13561
+ // coz trailing single wildcard will be handed by [trailing wildcard]
13562
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
13563
+
13564
+ // '*.js' matches '.js'
13565
+ // '*.js' doesn't match 'abc'
13566
+ (_, p1, p2) => {
13567
+ // 1.
13568
+ // > An asterisk "*" matches anything except a slash.
13569
+ // 2.
13570
+ // > Other consecutive asterisks are considered regular asterisks
13571
+ // > and will match according to the previous rules.
13572
+ const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13573
+ return p1 + unescaped
13574
+ }
13575
+ ],
13576
+
13577
+ [
13578
+ // unescape, revert step 3 except for back slash
13579
+ // For example, if a user escape a '\\*',
13580
+ // after step 3, the result will be '\\\\\\*'
13581
+ /\\\\\\(?=[$.|*+(){^])/g,
13582
+ () => ESCAPE
13583
+ ],
13584
+
13585
+ [
13586
+ // '\\\\' -> '\\'
13587
+ /\\\\/g,
13588
+ () => ESCAPE
13589
+ ],
13590
+
13591
+ [
13592
+ // > The range notation, e.g. [a-zA-Z],
13593
+ // > can be used to match one of the characters in a range.
13594
+
13595
+ // `\` is escaped by step 3
13596
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13597
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13598
+ // '\\[bar]' -> '\\\\[bar\\]'
13599
+ ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13600
+ : close === ']'
13601
+ ? endEscape.length % 2 === 0
13602
+ // A normal case, and it is a range notation
13603
+ // '[bar]'
13604
+ // '[bar\\\\]'
13605
+ ? `[${sanitizeRange(range)}${endEscape}]`
13606
+ // Invalid range notaton
13607
+ // '[bar\\]' -> '[bar\\\\]'
13608
+ : '[]'
13609
+ : '[]'
13610
+ ],
13611
+
13612
+ // ending
13613
+ [
13614
+ // 'js' will not match 'js.'
13615
+ // 'ab' will not match 'abc'
13616
+ /(?:[^*])$/,
13617
+
13618
+ // WTF!
13619
+ // https://git-scm.com/docs/gitignore
13620
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13621
+ // which re-fixes #24, #38
13622
+
13623
+ // > If there is a separator at the end of the pattern then the pattern
13624
+ // > will only match directories, otherwise the pattern can match both
13625
+ // > files and directories.
13626
+
13627
+ // 'js*' will not match 'a.js'
13628
+ // 'js/' will not match 'a.js'
13629
+ // 'js' will match 'a.js' and 'a.js/'
13630
+ match => /\/$/.test(match)
13631
+ // foo/ will not match 'foo'
13632
+ ? `${match}$`
13633
+ // foo matches 'foo' and 'foo/'
13634
+ : `${match}(?=$|\\/$)`
13635
+ ]
13636
+ ];
13637
+
13638
+ const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
13639
+ const MODE_IGNORE = 'regex';
13640
+ const MODE_CHECK_IGNORE = 'checkRegex';
13641
+ const UNDERSCORE = '_';
13642
+
13643
+ const TRAILING_WILD_CARD_REPLACERS = {
13644
+ [MODE_IGNORE] (_, p1) {
13645
+ const prefix = p1
13646
+ // '\^':
13647
+ // '/*' does not match EMPTY
13648
+ // '/*' does not match everything
13649
+
13650
+ // '\\\/':
13651
+ // 'abc/*' does not match 'abc/'
13652
+ ? `${p1}[^/]+`
13653
+
13654
+ // 'a*' matches 'a'
13655
+ // 'a*' matches 'aa'
13656
+ : '[^/]*';
13657
+
13658
+ return `${prefix}(?=$|\\/$)`
13659
+ },
13660
+
13661
+ [MODE_CHECK_IGNORE] (_, p1) {
13662
+ // When doing `git check-ignore`
13663
+ const prefix = p1
13664
+ // '\\\/':
13665
+ // 'abc/*' DOES match 'abc/' !
13666
+ ? `${p1}[^/]*`
13667
+
13668
+ // 'a*' matches 'a'
13669
+ // 'a*' matches 'aa'
13670
+ : '[^/]*';
13671
+
13672
+ return `${prefix}(?=$|\\/$)`
13673
+ }
13674
+ };
13675
+
13676
+ // @param {pattern}
13677
+ const makeRegexPrefix = pattern => REPLACERS.reduce(
13678
+ (prev, [matcher, replacer]) =>
13679
+ prev.replace(matcher, replacer.bind(pattern)),
13680
+ pattern
13681
+ );
13682
+
13683
+ const isString = subject => typeof subject === 'string';
13684
+
13685
+ // > A blank line matches no files, so it can serve as a separator for readability.
13686
+ const checkPattern = pattern => pattern
13687
+ && isString(pattern)
13688
+ && !REGEX_TEST_BLANK_LINE.test(pattern)
13689
+ && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
13690
+
13691
+ // > A line starting with # serves as a comment.
13692
+ && pattern.indexOf('#') !== 0;
13693
+
13694
+ const splitPattern = pattern => pattern
13695
+ .split(REGEX_SPLITALL_CRLF)
13696
+ .filter(Boolean);
13697
+
13698
+ class IgnoreRule {
13699
+ constructor (
13700
+ pattern,
13701
+ mark,
13702
+ body,
13703
+ ignoreCase,
13704
+ negative,
13705
+ prefix
13706
+ ) {
13707
+ this.pattern = pattern;
13708
+ this.mark = mark;
13709
+ this.negative = negative;
13710
+
13711
+ define(this, 'body', body);
13712
+ define(this, 'ignoreCase', ignoreCase);
13713
+ define(this, 'regexPrefix', prefix);
13714
+ }
13715
+
13716
+ get regex () {
13717
+ const key = UNDERSCORE + MODE_IGNORE;
13718
+
13719
+ if (this[key]) {
13720
+ return this[key]
13721
+ }
13722
+
13723
+ return this._make(MODE_IGNORE, key)
13724
+ }
13725
+
13726
+ get checkRegex () {
13727
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
13728
+
13729
+ if (this[key]) {
13730
+ return this[key]
13731
+ }
13732
+
13733
+ return this._make(MODE_CHECK_IGNORE, key)
13734
+ }
13735
+
13736
+ _make (mode, key) {
13737
+ const str = this.regexPrefix.replace(
13738
+ REGEX_REPLACE_TRAILING_WILDCARD,
13739
+
13740
+ // It does not need to bind pattern
13741
+ TRAILING_WILD_CARD_REPLACERS[mode]
13742
+ );
13743
+
13744
+ const regex = this.ignoreCase
13745
+ ? new RegExp(str, 'i')
13746
+ : new RegExp(str);
13747
+
13748
+ return define(this, key, regex)
13749
+ }
13750
+ }
13881
13751
 
13882
- const throwError = (message, Ctor) => {
13883
- throw new Ctor(message)
13884
- };
13752
+ const createRule = ({
13753
+ pattern,
13754
+ mark
13755
+ }, ignoreCase) => {
13756
+ let negative = false;
13757
+ let body = pattern;
13758
+
13759
+ // > An optional prefix "!" which negates the pattern;
13760
+ if (body.indexOf('!') === 0) {
13761
+ negative = true;
13762
+ body = body.substr(1);
13763
+ }
13764
+
13765
+ body = body
13766
+ // > Put a backslash ("\") in front of the first "!" for patterns that
13767
+ // > begin with a literal "!", for example, `"\!important!.txt"`.
13768
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
13769
+ // > Put a backslash ("\") in front of the first hash for patterns that
13770
+ // > begin with a hash.
13771
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
13772
+
13773
+ const regexPrefix = makeRegexPrefix(body);
13774
+
13775
+ return new IgnoreRule(
13776
+ pattern,
13777
+ mark,
13778
+ body,
13779
+ ignoreCase,
13780
+ negative,
13781
+ regexPrefix
13782
+ )
13783
+ };
13784
+
13785
+ class RuleManager {
13786
+ constructor (ignoreCase) {
13787
+ this._ignoreCase = ignoreCase;
13788
+ this._rules = [];
13789
+ }
13790
+
13791
+ _add (pattern) {
13792
+ // #32
13793
+ if (pattern && pattern[KEY_IGNORE]) {
13794
+ this._rules = this._rules.concat(pattern._rules._rules);
13795
+ this._added = true;
13796
+ return
13797
+ }
13798
+
13799
+ if (isString(pattern)) {
13800
+ pattern = {
13801
+ pattern
13802
+ };
13803
+ }
13804
+
13805
+ if (checkPattern(pattern.pattern)) {
13806
+ const rule = createRule(pattern, this._ignoreCase);
13807
+ this._added = true;
13808
+ this._rules.push(rule);
13809
+ }
13810
+ }
13811
+
13812
+ // @param {Array<string> | string | Ignore} pattern
13813
+ add (pattern) {
13814
+ this._added = false;
13815
+
13816
+ makeArray(
13817
+ isString(pattern)
13818
+ ? splitPattern(pattern)
13819
+ : pattern
13820
+ ).forEach(this._add, this);
13821
+
13822
+ return this._added
13823
+ }
13824
+
13825
+ // Test one single path without recursively checking parent directories
13826
+ //
13827
+ // - checkUnignored `boolean` whether should check if the path is unignored,
13828
+ // setting `checkUnignored` to `false` could reduce additional
13829
+ // path matching.
13830
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
13831
+
13832
+ // @returns {TestResult} true if a file is ignored
13833
+ test (path, checkUnignored, mode) {
13834
+ let ignored = false;
13835
+ let unignored = false;
13836
+ let matchedRule;
13837
+
13838
+ this._rules.forEach(rule => {
13839
+ const {negative} = rule;
13840
+
13841
+ // | ignored : unignored
13842
+ // -------- | ---------------------------------------
13843
+ // negative | 0:0 | 0:1 | 1:0 | 1:1
13844
+ // -------- | ------- | ------- | ------- | --------
13845
+ // 0 | TEST | TEST | SKIP | X
13846
+ // 1 | TESTIF | SKIP | TEST | X
13847
+
13848
+ // - SKIP: always skip
13849
+ // - TEST: always test
13850
+ // - TESTIF: only test if checkUnignored
13851
+ // - X: that never happen
13852
+ if (
13853
+ unignored === negative && ignored !== unignored
13854
+ || negative && !ignored && !unignored && !checkUnignored
13855
+ ) {
13856
+ return
13857
+ }
13858
+
13859
+ const matched = rule[mode].test(path);
13860
+
13861
+ if (!matched) {
13862
+ return
13863
+ }
13864
+
13865
+ ignored = !negative;
13866
+ unignored = negative;
13867
+
13868
+ matchedRule = negative
13869
+ ? UNDEFINED
13870
+ : rule;
13871
+ });
13872
+
13873
+ const ret = {
13874
+ ignored,
13875
+ unignored
13876
+ };
13877
+
13878
+ if (matchedRule) {
13879
+ ret.rule = matchedRule;
13880
+ }
13881
+
13882
+ return ret
13883
+ }
13884
+ }
13885
13885
 
13886
- const checkPath = (path, originalPath, doThrow) => {
13887
- if (!isString(path)) {
13888
- return doThrow(
13889
- `path must be a string, but got \`${originalPath}\``,
13890
- TypeError
13891
- )
13892
- }
13893
-
13894
- // We don't know if we should ignore EMPTY, so throw
13895
- if (!path) {
13896
- return doThrow(`path must not be empty`, TypeError)
13897
- }
13898
-
13899
- // Check if it is a relative path
13900
- if (checkPath.isNotRelative(path)) {
13901
- const r = '`path.relative()`d';
13902
- return doThrow(
13903
- `path should be a ${r} string, but got "${originalPath}"`,
13904
- RangeError
13905
- )
13906
- }
13907
-
13908
- return true
13909
- };
13886
+ const throwError = (message, Ctor) => {
13887
+ throw new Ctor(message)
13888
+ };
13889
+
13890
+ const checkPath = (path, originalPath, doThrow) => {
13891
+ if (!isString(path)) {
13892
+ return doThrow(
13893
+ `path must be a string, but got \`${originalPath}\``,
13894
+ TypeError
13895
+ )
13896
+ }
13897
+
13898
+ // We don't know if we should ignore EMPTY, so throw
13899
+ if (!path) {
13900
+ return doThrow(`path must not be empty`, TypeError)
13901
+ }
13902
+
13903
+ // Check if it is a relative path
13904
+ if (checkPath.isNotRelative(path)) {
13905
+ const r = '`path.relative()`d';
13906
+ return doThrow(
13907
+ `path should be a ${r} string, but got "${originalPath}"`,
13908
+ RangeError
13909
+ )
13910
+ }
13911
+
13912
+ return true
13913
+ };
13914
+
13915
+ const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
13916
+
13917
+ checkPath.isNotRelative = isNotRelative;
13918
+
13919
+ // On windows, the following function will be replaced
13920
+ /* istanbul ignore next */
13921
+ checkPath.convert = p => p;
13922
+
13923
+
13924
+ class Ignore {
13925
+ constructor ({
13926
+ ignorecase = true,
13927
+ ignoreCase = ignorecase,
13928
+ allowRelativePaths = false
13929
+ } = {}) {
13930
+ define(this, KEY_IGNORE, true);
13931
+
13932
+ this._rules = new RuleManager(ignoreCase);
13933
+ this._strictPathCheck = !allowRelativePaths;
13934
+ this._initCache();
13935
+ }
13936
+
13937
+ _initCache () {
13938
+ // A cache for the result of `.ignores()`
13939
+ this._ignoreCache = Object.create(null);
13940
+
13941
+ // A cache for the result of `.test()`
13942
+ this._testCache = Object.create(null);
13943
+ }
13944
+
13945
+ add (pattern) {
13946
+ if (this._rules.add(pattern)) {
13947
+ // Some rules have just added to the ignore,
13948
+ // making the behavior changed,
13949
+ // so we need to re-initialize the result cache
13950
+ this._initCache();
13951
+ }
13952
+
13953
+ return this
13954
+ }
13955
+
13956
+ // legacy
13957
+ addPattern (pattern) {
13958
+ return this.add(pattern)
13959
+ }
13960
+
13961
+ // @returns {TestResult}
13962
+ _test (originalPath, cache, checkUnignored, slices) {
13963
+ const path = originalPath
13964
+ // Supports nullable path
13965
+ && checkPath.convert(originalPath);
13966
+
13967
+ checkPath(
13968
+ path,
13969
+ originalPath,
13970
+ this._strictPathCheck
13971
+ ? throwError
13972
+ : RETURN_FALSE
13973
+ );
13974
+
13975
+ return this._t(path, cache, checkUnignored, slices)
13976
+ }
13977
+
13978
+ checkIgnore (path) {
13979
+ // If the path doest not end with a slash, `.ignores()` is much equivalent
13980
+ // to `git check-ignore`
13981
+ if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
13982
+ return this.test(path)
13983
+ }
13984
+
13985
+ const slices = path.split(SLASH).filter(Boolean);
13986
+ slices.pop();
13987
+
13988
+ if (slices.length) {
13989
+ const parent = this._t(
13990
+ slices.join(SLASH) + SLASH,
13991
+ this._testCache,
13992
+ true,
13993
+ slices
13994
+ );
13995
+
13996
+ if (parent.ignored) {
13997
+ return parent
13998
+ }
13999
+ }
14000
+
14001
+ return this._rules.test(path, false, MODE_CHECK_IGNORE)
14002
+ }
14003
+
14004
+ _t (
14005
+ // The path to be tested
14006
+ path,
14007
+
14008
+ // The cache for the result of a certain checking
14009
+ cache,
14010
+
14011
+ // Whether should check if the path is unignored
14012
+ checkUnignored,
14013
+
14014
+ // The path slices
14015
+ slices
14016
+ ) {
14017
+ if (path in cache) {
14018
+ return cache[path]
14019
+ }
14020
+
14021
+ if (!slices) {
14022
+ // path/to/a.js
14023
+ // ['path', 'to', 'a.js']
14024
+ slices = path.split(SLASH).filter(Boolean);
14025
+ }
14026
+
14027
+ slices.pop();
14028
+
14029
+ // If the path has no parent directory, just test it
14030
+ if (!slices.length) {
14031
+ return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
14032
+ }
14033
+
14034
+ const parent = this._t(
14035
+ slices.join(SLASH) + SLASH,
14036
+ cache,
14037
+ checkUnignored,
14038
+ slices
14039
+ );
14040
+
14041
+ // If the path contains a parent directory, check the parent first
14042
+ return cache[path] = parent.ignored
14043
+ // > It is not possible to re-include a file if a parent directory of
14044
+ // > that file is excluded.
14045
+ ? parent
14046
+ : this._rules.test(path, checkUnignored, MODE_IGNORE)
14047
+ }
14048
+
14049
+ ignores (path) {
14050
+ return this._test(path, this._ignoreCache, false).ignored
14051
+ }
14052
+
14053
+ createFilter () {
14054
+ return path => !this.ignores(path)
14055
+ }
14056
+
14057
+ filter (paths) {
14058
+ return makeArray(paths).filter(this.createFilter())
14059
+ }
14060
+
14061
+ // @returns {TestResult}
14062
+ test (path) {
14063
+ return this._test(path, this._testCache, true)
14064
+ }
14065
+ }
13910
14066
 
13911
- const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
13912
-
13913
- checkPath.isNotRelative = isNotRelative;
13914
-
13915
- // On windows, the following function will be replaced
13916
- /* istanbul ignore next */
13917
- checkPath.convert = p => p;
13918
-
13919
-
13920
- class Ignore {
13921
- constructor ({
13922
- ignorecase = true,
13923
- ignoreCase = ignorecase,
13924
- allowRelativePaths = false
13925
- } = {}) {
13926
- define(this, KEY_IGNORE, true);
13927
-
13928
- this._rules = new RuleManager(ignoreCase);
13929
- this._strictPathCheck = !allowRelativePaths;
13930
- this._initCache();
13931
- }
13932
-
13933
- _initCache () {
13934
- // A cache for the result of `.ignores()`
13935
- this._ignoreCache = Object.create(null);
13936
-
13937
- // A cache for the result of `.test()`
13938
- this._testCache = Object.create(null);
13939
- }
13940
-
13941
- add (pattern) {
13942
- if (this._rules.add(pattern)) {
13943
- // Some rules have just added to the ignore,
13944
- // making the behavior changed,
13945
- // so we need to re-initialize the result cache
13946
- this._initCache();
13947
- }
13948
-
13949
- return this
13950
- }
13951
-
13952
- // legacy
13953
- addPattern (pattern) {
13954
- return this.add(pattern)
13955
- }
13956
-
13957
- // @returns {TestResult}
13958
- _test (originalPath, cache, checkUnignored, slices) {
13959
- const path = originalPath
13960
- // Supports nullable path
13961
- && checkPath.convert(originalPath);
13962
-
13963
- checkPath(
13964
- path,
13965
- originalPath,
13966
- this._strictPathCheck
13967
- ? throwError
13968
- : RETURN_FALSE
13969
- );
13970
-
13971
- return this._t(path, cache, checkUnignored, slices)
13972
- }
13973
-
13974
- checkIgnore (path) {
13975
- // If the path doest not end with a slash, `.ignores()` is much equivalent
13976
- // to `git check-ignore`
13977
- if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
13978
- return this.test(path)
13979
- }
13980
-
13981
- const slices = path.split(SLASH).filter(Boolean);
13982
- slices.pop();
13983
-
13984
- if (slices.length) {
13985
- const parent = this._t(
13986
- slices.join(SLASH) + SLASH,
13987
- this._testCache,
13988
- true,
13989
- slices
13990
- );
13991
-
13992
- if (parent.ignored) {
13993
- return parent
13994
- }
13995
- }
13996
-
13997
- return this._rules.test(path, false, MODE_CHECK_IGNORE)
13998
- }
13999
-
14000
- _t (
14001
- // The path to be tested
14002
- path,
14003
-
14004
- // The cache for the result of a certain checking
14005
- cache,
14006
-
14007
- // Whether should check if the path is unignored
14008
- checkUnignored,
14009
-
14010
- // The path slices
14011
- slices
14012
- ) {
14013
- if (path in cache) {
14014
- return cache[path]
14015
- }
14016
-
14017
- if (!slices) {
14018
- // path/to/a.js
14019
- // ['path', 'to', 'a.js']
14020
- slices = path.split(SLASH).filter(Boolean);
14021
- }
14022
-
14023
- slices.pop();
14024
-
14025
- // If the path has no parent directory, just test it
14026
- if (!slices.length) {
14027
- return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
14028
- }
14029
-
14030
- const parent = this._t(
14031
- slices.join(SLASH) + SLASH,
14032
- cache,
14033
- checkUnignored,
14034
- slices
14035
- );
14036
-
14037
- // If the path contains a parent directory, check the parent first
14038
- return cache[path] = parent.ignored
14039
- // > It is not possible to re-include a file if a parent directory of
14040
- // > that file is excluded.
14041
- ? parent
14042
- : this._rules.test(path, checkUnignored, MODE_IGNORE)
14043
- }
14044
-
14045
- ignores (path) {
14046
- return this._test(path, this._ignoreCache, false).ignored
14047
- }
14048
-
14049
- createFilter () {
14050
- return path => !this.ignores(path)
14051
- }
14052
-
14053
- filter (paths) {
14054
- return makeArray(paths).filter(this.createFilter())
14055
- }
14056
-
14057
- // @returns {TestResult}
14058
- test (path) {
14059
- return this._test(path, this._testCache, true)
14060
- }
14061
- }
14067
+ const factory = options => new Ignore(options);
14068
+
14069
+ const isPathValid = path =>
14070
+ checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14071
+
14072
+ /* istanbul ignore next */
14073
+ const setupWindows = () => {
14074
+ /* eslint no-control-regex: "off" */
14075
+ const makePosix = str => /^\\\\\?\\/.test(str)
14076
+ || /["<>|\u0000-\u001F]+/u.test(str)
14077
+ ? str
14078
+ : str.replace(/\\/g, '/');
14079
+
14080
+ checkPath.convert = makePosix;
14081
+
14082
+ // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14083
+ // 'd:\\foo'
14084
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14085
+ checkPath.isNotRelative = path =>
14086
+ REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
14087
+ || isNotRelative(path);
14088
+ };
14089
+
14090
+
14091
+ // Windows
14092
+ // --------------------------------------------------------------
14093
+ /* istanbul ignore next */
14094
+ if (
14095
+ // Detect `process` so that it can run in browsers.
14096
+ typeof process !== 'undefined'
14097
+ && process.platform === 'win32'
14098
+ ) {
14099
+ setupWindows();
14100
+ }
14062
14101
 
14063
- const factory = options => new Ignore(options);
14064
-
14065
- const isPathValid = path =>
14066
- checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14067
-
14068
-
14069
- // Windows
14070
- // --------------------------------------------------------------
14071
- /* istanbul ignore next */
14072
- if (
14073
- // Detect `process` so that it can run in browsers.
14074
- typeof process !== 'undefined'
14075
- && (
14076
- process.env && process.env.IGNORE_TEST_WIN32
14077
- || process.platform === 'win32'
14078
- )
14079
- ) {
14080
- /* eslint no-control-regex: "off" */
14081
- const makePosix = str => /^\\\\\?\\/.test(str)
14082
- || /["<>|\u0000-\u001F]+/u.test(str)
14083
- ? str
14084
- : str.replace(/\\/g, '/');
14085
-
14086
- checkPath.convert = makePosix;
14087
-
14088
- // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14089
- // 'd:\\foo'
14090
- const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14091
- checkPath.isNotRelative = path =>
14092
- REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
14093
- || isNotRelative(path);
14094
- }
14102
+ // COMMONJS_EXPORTS ////////////////////////////////////////////////////////////
14095
14103
 
14096
- // COMMONJS_EXPORTS ////////////////////////////////////////////////////////////
14104
+ module.exports = factory;
14097
14105
 
14098
- ignore$1.exports = factory;
14106
+ // Although it is an anti-pattern,
14107
+ // it is still widely misused by a lot of libraries in github
14108
+ // Ref: https://github.com/search?q=ignore.default%28%29&type=code
14109
+ factory.default = factory;
14099
14110
 
14100
- // Although it is an anti-pattern,
14101
- // it is still widely misused by a lot of libraries in github
14102
- // Ref: https://github.com/search?q=ignore.default%28%29&type=code
14103
- factory.default = factory;
14111
+ module.exports.isPathValid = isPathValid;
14104
14112
 
14105
- ignore$1.exports.isPathValid = isPathValid;
14113
+ // For testing purposes
14114
+ define(module.exports, Symbol.for('setupWindows'), setupWindows);
14115
+ } (ignore$1));
14106
14116
  return ignore$1.exports;
14107
14117
  }
14108
14118