html-validate 9.1.3 → 9.2.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/cjs/core.js CHANGED
@@ -435,6 +435,11 @@ class UserError extends NestedError {
435
435
  super(message, nested);
436
436
  Error.captureStackTrace(this, UserError);
437
437
  this.name = UserError.name;
438
+ Object.defineProperty(this, "isUserError", {
439
+ value: true,
440
+ enumerable: false,
441
+ writable: false
442
+ });
438
443
  }
439
444
  /**
440
445
  * @public
@@ -473,6 +478,10 @@ class InheritError extends UserError {
473
478
  }
474
479
  }
475
480
 
481
+ function isUserError(error) {
482
+ return Boolean(error && typeof error === "object" && "isUserError" in error);
483
+ }
484
+
476
485
  function getSummary(schema, obj, errors) {
477
486
  const output = betterAjvErrors__default.default(schema, obj, errors, {
478
487
  format: "js"
@@ -1947,7 +1956,7 @@ const table = {
1947
1956
  "nth-child": nthChild,
1948
1957
  scope
1949
1958
  };
1950
- function factory$1(name, context) {
1959
+ function factory(name, context) {
1951
1960
  const fn = table[name];
1952
1961
  if (fn) {
1953
1962
  return fn.bind(context);
@@ -2046,7 +2055,7 @@ function isQuotationMark(ch) {
2046
2055
  function isPseudoElement(ch, buffer) {
2047
2056
  return ch === ":" && buffer === ":";
2048
2057
  }
2049
- function* splitPattern$1(pattern) {
2058
+ function* splitPattern(pattern) {
2050
2059
  if (pattern === "") {
2051
2060
  return;
2052
2061
  }
@@ -2148,7 +2157,7 @@ class PseudoClassMatcher extends Matcher {
2148
2157
  this.args = args;
2149
2158
  }
2150
2159
  match(node, context) {
2151
- const fn = factory$1(this.name, context);
2160
+ const fn = factory(this.name, context);
2152
2161
  return fn(node, this.args);
2153
2162
  }
2154
2163
  }
@@ -2166,7 +2175,7 @@ class Pattern {
2166
2175
  this.selector = pattern;
2167
2176
  this.combinator = parseCombinator(match.shift(), pattern);
2168
2177
  this.tagName = match.shift() || "*";
2169
- this.pattern = Array.from(splitPattern$1(match[0]), (it) => this.createMatcher(it));
2178
+ this.pattern = Array.from(splitPattern(match[0]), (it) => this.createMatcher(it));
2170
2179
  }
2171
2180
  match(node, context) {
2172
2181
  return node.is(this.tagName) && this.pattern.every((cur) => cur.match(node, context));
@@ -3677,9 +3686,7 @@ function classifyNodeText(node, options = {}) {
3677
3686
  return node.cacheSet(cacheKey, 0 /* EMPTY_TEXT */);
3678
3687
  }
3679
3688
  const text = findTextNodes(node, {
3680
- ...options,
3681
- ignoreHiddenRoot: false
3682
- });
3689
+ ...options});
3683
3690
  if (text.some((cur) => cur.isDynamic)) {
3684
3691
  return node.cacheSet(cacheKey, 1 /* DYNAMIC_TEXT */);
3685
3692
  }
@@ -8701,6 +8708,22 @@ const supportSri = {
8701
8708
  link: "href",
8702
8709
  script: "src"
8703
8710
  };
8711
+ const supportedRel = ["stylesheet", "preload", "modulepreload"];
8712
+ const supportedPreload = ["style", "script"];
8713
+ function linkSupportsSri(node) {
8714
+ const rel = node.getAttribute("rel");
8715
+ if (typeof rel?.value !== "string") {
8716
+ return false;
8717
+ }
8718
+ if (!supportedRel.includes(rel.value)) {
8719
+ return false;
8720
+ }
8721
+ if (rel.value === "preload") {
8722
+ const as = node.getAttribute("as");
8723
+ return typeof as?.value === "string" && supportedPreload.includes(as.value);
8724
+ }
8725
+ return true;
8726
+ }
8704
8727
  class RequireSri extends Rule {
8705
8728
  target;
8706
8729
  constructor(options) {
@@ -8767,6 +8790,9 @@ class RequireSri extends Rule {
8767
8790
  return Object.keys(supportSri).includes(node.tagName);
8768
8791
  }
8769
8792
  needSri(node) {
8793
+ if (node.is("link") && !linkSupportsSri(node)) {
8794
+ return false;
8795
+ }
8770
8796
  const attr = this.elementSourceAttr(node);
8771
8797
  if (!attr) {
8772
8798
  return false;
@@ -12347,7 +12373,7 @@ class Engine {
12347
12373
  rules
12348
12374
  };
12349
12375
  parser.trigger("config:ready", configEvent);
12350
- const { hooks: _, ...sourceData } = source;
12376
+ const { ...sourceData } = source;
12351
12377
  const sourceEvent = {
12352
12378
  location,
12353
12379
  source: sourceData
@@ -13323,7 +13349,7 @@ class HtmlValidate {
13323
13349
  }
13324
13350
 
13325
13351
  const name = "html-validate";
13326
- const version = "9.1.3";
13352
+ const version = "9.2.1";
13327
13353
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
13328
13354
 
13329
13355
  function definePlugin(plugin) {
@@ -13639,777 +13665,802 @@ function compatibilityCheckImpl(name, declared, options) {
13639
13665
  return false;
13640
13666
  }
13641
13667
 
13642
- // A simple implementation of make-array
13643
- function makeArray (subject) {
13644
- return Array.isArray(subject)
13645
- ? subject
13646
- : [subject]
13647
- }
13648
-
13649
- const UNDEFINED = undefined;
13650
- const EMPTY = '';
13651
- const SPACE = ' ';
13652
- const ESCAPE = '\\';
13653
- const REGEX_TEST_BLANK_LINE = /^\s+$/;
13654
- const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13655
- const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13656
- const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13657
- const REGEX_SPLITALL_CRLF = /\r?\n/g;
13658
-
13659
- // Invalid:
13660
- // - /foo,
13661
- // - ./foo,
13662
- // - ../foo,
13663
- // - .
13664
- // - ..
13665
- // Valid:
13666
- // - .foo
13667
- const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
13668
-
13669
- const REGEX_TEST_TRAILING_SLASH = /\/$/;
13670
-
13671
- const SLASH = '/';
13672
-
13673
- // Do not use ternary expression here, since "istanbul ignore next" is buggy
13674
- let TMP_KEY_IGNORE = 'node-ignore';
13675
- /* istanbul ignore else */
13676
- if (typeof Symbol !== 'undefined') {
13677
- TMP_KEY_IGNORE = Symbol.for('node-ignore');
13678
- }
13679
- const KEY_IGNORE = TMP_KEY_IGNORE;
13680
-
13681
- const define = (object, key, value) => {
13682
- Object.defineProperty(object, key, {value});
13683
- return value
13684
- };
13685
-
13686
- const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13687
-
13688
- const RETURN_FALSE = () => false;
13689
-
13690
- // Sanitize the range of a regular expression
13691
- // The cases are complicated, see test cases for details
13692
- const sanitizeRange = range => range.replace(
13693
- REGEX_REGEXP_RANGE,
13694
- (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13695
- ? match
13696
- // Invalid range (out of order) which is ok for gitignore rules but
13697
- // fatal for JavaScript regular expression, so eliminate it.
13698
- : EMPTY
13699
- );
13700
-
13701
- // See fixtures #59
13702
- const cleanRangeBackSlash = slashes => {
13703
- const {length} = slashes;
13704
- return slashes.slice(0, length - length % 2)
13705
- };
13706
-
13707
- // > If the pattern ends with a slash,
13708
- // > it is removed for the purpose of the following description,
13709
- // > but it would only find a match with a directory.
13710
- // > In other words, foo/ will match a directory foo and paths underneath it,
13711
- // > but will not match a regular file or a symbolic link foo
13712
- // > (this is consistent with the way how pathspec works in general in Git).
13713
- // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13714
- // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13715
- // you could use option `mark: true` with `glob`
13716
-
13717
- // '`foo/`' should not continue with the '`..`'
13718
- const REPLACERS = [
13719
-
13720
- [
13721
- // Remove BOM
13722
- // TODO:
13723
- // Other similar zero-width characters?
13724
- /^\uFEFF/,
13725
- () => EMPTY
13726
- ],
13727
-
13728
- // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13729
- [
13730
- // (a\ ) -> (a )
13731
- // (a ) -> (a)
13732
- // (a ) -> (a)
13733
- // (a \ ) -> (a )
13734
- /((?:\\\\)*?)(\\?\s+)$/,
13735
- (_, m1, m2) => m1 + (
13736
- m2.indexOf('\\') === 0
13737
- ? SPACE
13738
- : EMPTY
13739
- )
13740
- ],
13741
-
13742
- // Replace (\ ) with ' '
13743
- // (\ ) -> ' '
13744
- // (\\ ) -> '\\ '
13745
- // (\\\ ) -> '\\ '
13746
- [
13747
- /(\\+?)\s/g,
13748
- (_, m1) => {
13749
- const {length} = m1;
13750
- return m1.slice(0, length - length % 2) + SPACE
13751
- }
13752
- ],
13753
-
13754
- // Escape metacharacters
13755
- // which is written down by users but means special for regular expressions.
13756
-
13757
- // > There are 12 characters with special meanings:
13758
- // > - the backslash \,
13759
- // > - the caret ^,
13760
- // > - the dollar sign $,
13761
- // > - the period or dot .,
13762
- // > - the vertical bar or pipe symbol |,
13763
- // > - the question mark ?,
13764
- // > - the asterisk or star *,
13765
- // > - the plus sign +,
13766
- // > - the opening parenthesis (,
13767
- // > - the closing parenthesis ),
13768
- // > - and the opening square bracket [,
13769
- // > - the opening curly brace {,
13770
- // > These special characters are often called "metacharacters".
13771
- [
13772
- /[\\$.|*+(){^]/g,
13773
- match => `\\${match}`
13774
- ],
13775
-
13776
- [
13777
- // > a question mark (?) matches a single character
13778
- /(?!\\)\?/g,
13779
- () => '[^/]'
13780
- ],
13781
-
13782
- // leading slash
13783
- [
13784
-
13785
- // > A leading slash matches the beginning of the pathname.
13786
- // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13787
- // A leading slash matches the beginning of the pathname
13788
- /^\//,
13789
- () => '^'
13790
- ],
13791
-
13792
- // replace special metacharacter slash after the leading slash
13793
- [
13794
- /\//g,
13795
- () => '\\/'
13796
- ],
13797
-
13798
- [
13799
- // > A leading "**" followed by a slash means match in all directories.
13800
- // > For example, "**/foo" matches file or directory "foo" anywhere,
13801
- // > the same as pattern "foo".
13802
- // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13803
- // > under directory "foo".
13804
- // Notice that the '*'s have been replaced as '\\*'
13805
- /^\^*\\\*\\\*\\\//,
13806
-
13807
- // '**/foo' <-> 'foo'
13808
- () => '^(?:.*\\/)?'
13809
- ],
13810
-
13811
- // starting
13812
- [
13813
- // there will be no leading '/'
13814
- // (which has been replaced by section "leading slash")
13815
- // If starts with '**', adding a '^' to the regular expression also works
13816
- /^(?=[^^])/,
13817
- function startingReplacer () {
13818
- // If has a slash `/` at the beginning or middle
13819
- return !/\/(?!$)/.test(this)
13820
- // > Prior to 2.22.1
13821
- // > If the pattern does not contain a slash /,
13822
- // > Git treats it as a shell glob pattern
13823
- // Actually, if there is only a trailing slash,
13824
- // git also treats it as a shell glob pattern
13825
-
13826
- // After 2.22.1 (compatible but clearer)
13827
- // > If there is a separator at the beginning or middle (or both)
13828
- // > of the pattern, then the pattern is relative to the directory
13829
- // > level of the particular .gitignore file itself.
13830
- // > Otherwise the pattern may also match at any level below
13831
- // > the .gitignore level.
13832
- ? '(?:^|\\/)'
13833
-
13834
- // > Otherwise, Git treats the pattern as a shell glob suitable for
13835
- // > consumption by fnmatch(3)
13836
- : '^'
13837
- }
13838
- ],
13839
-
13840
- // two globstars
13841
- [
13842
- // Use lookahead assertions so that we could match more than one `'/**'`
13843
- /\\\/\\\*\\\*(?=\\\/|$)/g,
13844
-
13845
- // Zero, one or several directories
13846
- // should not use '*', or it will be replaced by the next replacer
13847
-
13848
- // Check if it is not the last `'/**'`
13849
- (_, index, str) => index + 6 < str.length
13850
-
13851
- // case: /**/
13852
- // > A slash followed by two consecutive asterisks then a slash matches
13853
- // > zero or more directories.
13854
- // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13855
- // '/**/'
13856
- ? '(?:\\/[^\\/]+)*'
13668
+ var ignore$1 = {exports: {}};
13857
13669
 
13858
- // case: /**
13859
- // > A trailing `"/**"` matches everything inside.
13670
+ var hasRequiredIgnore;
13860
13671
 
13861
- // #21: everything inside but it should not include the current folder
13862
- : '\\/.+'
13863
- ],
13864
-
13865
- // normal intermediate wildcards
13866
- [
13867
- // Never replace escaped '*'
13868
- // ignore rule '\*' will match the path '*'
13869
-
13870
- // 'abc.*/' -> go
13871
- // 'abc.*' -> skip this rule,
13872
- // coz trailing single wildcard will be handed by [trailing wildcard]
13873
- /(^|[^\\]+)(\\\*)+(?=.+)/g,
13874
-
13875
- // '*.js' matches '.js'
13876
- // '*.js' doesn't match 'abc'
13877
- (_, p1, p2) => {
13878
- // 1.
13879
- // > An asterisk "*" matches anything except a slash.
13880
- // 2.
13881
- // > Other consecutive asterisks are considered regular asterisks
13882
- // > and will match according to the previous rules.
13883
- const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13884
- return p1 + unescaped
13885
- }
13886
- ],
13887
-
13888
- [
13889
- // unescape, revert step 3 except for back slash
13890
- // For example, if a user escape a '\\*',
13891
- // after step 3, the result will be '\\\\\\*'
13892
- /\\\\\\(?=[$.|*+(){^])/g,
13893
- () => ESCAPE
13894
- ],
13895
-
13896
- [
13897
- // '\\\\' -> '\\'
13898
- /\\\\/g,
13899
- () => ESCAPE
13900
- ],
13901
-
13902
- [
13903
- // > The range notation, e.g. [a-zA-Z],
13904
- // > can be used to match one of the characters in a range.
13905
-
13906
- // `\` is escaped by step 3
13907
- /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13908
- (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13909
- // '\\[bar]' -> '\\\\[bar\\]'
13910
- ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13911
- : close === ']'
13912
- ? endEscape.length % 2 === 0
13913
- // A normal case, and it is a range notation
13914
- // '[bar]'
13915
- // '[bar\\\\]'
13916
- ? `[${sanitizeRange(range)}${endEscape}]`
13917
- // Invalid range notaton
13918
- // '[bar\\]' -> '[bar\\\\]'
13919
- : '[]'
13920
- : '[]'
13921
- ],
13922
-
13923
- // ending
13924
- [
13925
- // 'js' will not match 'js.'
13926
- // 'ab' will not match 'abc'
13927
- /(?:[^*])$/,
13928
-
13929
- // WTF!
13930
- // https://git-scm.com/docs/gitignore
13931
- // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13932
- // which re-fixes #24, #38
13933
-
13934
- // > If there is a separator at the end of the pattern then the pattern
13935
- // > will only match directories, otherwise the pattern can match both
13936
- // > files and directories.
13937
-
13938
- // 'js*' will not match 'a.js'
13939
- // 'js/' will not match 'a.js'
13940
- // 'js' will match 'a.js' and 'a.js/'
13941
- match => /\/$/.test(match)
13942
- // foo/ will not match 'foo'
13943
- ? `${match}$`
13944
- // foo matches 'foo' and 'foo/'
13945
- : `${match}(?=$|\\/$)`
13946
- ]
13947
- ];
13948
-
13949
- const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
13950
- const MODE_IGNORE = 'regex';
13951
- const MODE_CHECK_IGNORE = 'checkRegex';
13952
- const UNDERSCORE = '_';
13953
-
13954
- const TRAILING_WILD_CARD_REPLACERS = {
13955
- [MODE_IGNORE] (_, p1) {
13956
- const prefix = p1
13957
- // '\^':
13958
- // '/*' does not match EMPTY
13959
- // '/*' does not match everything
13960
-
13961
- // '\\\/':
13962
- // 'abc/*' does not match 'abc/'
13963
- ? `${p1}[^/]+`
13964
-
13965
- // 'a*' matches 'a'
13966
- // 'a*' matches 'aa'
13967
- : '[^/]*';
13968
-
13969
- return `${prefix}(?=$|\\/$)`
13970
- },
13971
-
13972
- [MODE_CHECK_IGNORE] (_, p1) {
13973
- // When doing `git check-ignore`
13974
- const prefix = p1
13975
- // '\\\/':
13976
- // 'abc/*' DOES match 'abc/' !
13977
- ? `${p1}[^/]*`
13978
-
13979
- // 'a*' matches 'a'
13980
- // 'a*' matches 'aa'
13981
- : '[^/]*';
13982
-
13983
- return `${prefix}(?=$|\\/$)`
13984
- }
13985
- };
13986
-
13987
- // @param {pattern}
13988
- const makeRegexPrefix = pattern => REPLACERS.reduce(
13989
- (prev, [matcher, replacer]) =>
13990
- prev.replace(matcher, replacer.bind(pattern)),
13991
- pattern
13992
- );
13993
-
13994
- const isString = subject => typeof subject === 'string';
13995
-
13996
- // > A blank line matches no files, so it can serve as a separator for readability.
13997
- const checkPattern = pattern => pattern
13998
- && isString(pattern)
13999
- && !REGEX_TEST_BLANK_LINE.test(pattern)
14000
- && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
14001
-
14002
- // > A line starting with # serves as a comment.
14003
- && pattern.indexOf('#') !== 0;
14004
-
14005
- const splitPattern = pattern => pattern
14006
- .split(REGEX_SPLITALL_CRLF)
14007
- .filter(Boolean);
14008
-
14009
- class IgnoreRule {
14010
- constructor (
14011
- pattern,
14012
- mark,
14013
- body,
14014
- ignoreCase,
14015
- negative,
14016
- prefix
14017
- ) {
14018
- this.pattern = pattern;
14019
- this.mark = mark;
14020
- this.negative = negative;
14021
-
14022
- define(this, 'body', body);
14023
- define(this, 'ignoreCase', ignoreCase);
14024
- define(this, 'regexPrefix', prefix);
14025
- }
14026
-
14027
- get regex () {
14028
- const key = UNDERSCORE + MODE_IGNORE;
14029
-
14030
- if (this[key]) {
14031
- return this[key]
14032
- }
14033
-
14034
- return this._make(MODE_IGNORE, key)
14035
- }
14036
-
14037
- get checkRegex () {
14038
- const key = UNDERSCORE + MODE_CHECK_IGNORE;
14039
-
14040
- if (this[key]) {
14041
- return this[key]
14042
- }
14043
-
14044
- return this._make(MODE_CHECK_IGNORE, key)
14045
- }
14046
-
14047
- _make (mode, key) {
14048
- const str = this.regexPrefix.replace(
14049
- REGEX_REPLACE_TRAILING_WILDCARD,
14050
-
14051
- // It does not need to bind pattern
14052
- TRAILING_WILD_CARD_REPLACERS[mode]
14053
- );
14054
-
14055
- const regex = this.ignoreCase
14056
- ? new RegExp(str, 'i')
14057
- : new RegExp(str);
14058
-
14059
- return define(this, key, regex)
14060
- }
14061
- }
14062
-
14063
- const createRule = ({
14064
- pattern,
14065
- mark
14066
- }, ignoreCase) => {
14067
- let negative = false;
14068
- let body = pattern;
14069
-
14070
- // > An optional prefix "!" which negates the pattern;
14071
- if (body.indexOf('!') === 0) {
14072
- negative = true;
14073
- body = body.substr(1);
14074
- }
14075
-
14076
- body = body
14077
- // > Put a backslash ("\") in front of the first "!" for patterns that
14078
- // > begin with a literal "!", for example, `"\!important!.txt"`.
14079
- .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
14080
- // > Put a backslash ("\") in front of the first hash for patterns that
14081
- // > begin with a hash.
14082
- .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
14083
-
14084
- const regexPrefix = makeRegexPrefix(body);
14085
-
14086
- return new IgnoreRule(
14087
- pattern,
14088
- mark,
14089
- body,
14090
- ignoreCase,
14091
- negative,
14092
- regexPrefix
14093
- )
14094
- };
14095
-
14096
- class RuleManager {
14097
- constructor (ignoreCase) {
14098
- this._ignoreCase = ignoreCase;
14099
- this._rules = [];
14100
- }
14101
-
14102
- _add (pattern) {
14103
- // #32
14104
- if (pattern && pattern[KEY_IGNORE]) {
14105
- this._rules = this._rules.concat(pattern._rules._rules);
14106
- this._added = true;
14107
- return
14108
- }
14109
-
14110
- if (isString(pattern)) {
14111
- pattern = {
14112
- pattern
14113
- };
14114
- }
14115
-
14116
- if (checkPattern(pattern.pattern)) {
14117
- const rule = createRule(pattern, this._ignoreCase);
14118
- this._added = true;
14119
- this._rules.push(rule);
14120
- }
14121
- }
13672
+ function requireIgnore () {
13673
+ if (hasRequiredIgnore) return ignore$1.exports;
13674
+ hasRequiredIgnore = 1;
13675
+ // A simple implementation of make-array
13676
+ function makeArray (subject) {
13677
+ return Array.isArray(subject)
13678
+ ? subject
13679
+ : [subject]
13680
+ }
14122
13681
 
14123
- // @param {Array<string> | string | Ignore} pattern
14124
- add (pattern) {
14125
- this._added = false;
13682
+ const UNDEFINED = undefined;
13683
+ const EMPTY = '';
13684
+ const SPACE = ' ';
13685
+ const ESCAPE = '\\';
13686
+ const REGEX_TEST_BLANK_LINE = /^\s+$/;
13687
+ const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13688
+ const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13689
+ const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13690
+ const REGEX_SPLITALL_CRLF = /\r?\n/g;
13691
+
13692
+ // Invalid:
13693
+ // - /foo,
13694
+ // - ./foo,
13695
+ // - ../foo,
13696
+ // - .
13697
+ // - ..
13698
+ // Valid:
13699
+ // - .foo
13700
+ const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
13701
+
13702
+ const REGEX_TEST_TRAILING_SLASH = /\/$/;
13703
+
13704
+ const SLASH = '/';
13705
+
13706
+ // Do not use ternary expression here, since "istanbul ignore next" is buggy
13707
+ let TMP_KEY_IGNORE = 'node-ignore';
13708
+ /* istanbul ignore else */
13709
+ if (typeof Symbol !== 'undefined') {
13710
+ TMP_KEY_IGNORE = Symbol.for('node-ignore');
13711
+ }
13712
+ const KEY_IGNORE = TMP_KEY_IGNORE;
14126
13713
 
14127
- makeArray(
14128
- isString(pattern)
14129
- ? splitPattern(pattern)
14130
- : pattern
14131
- ).forEach(this._add, this);
13714
+ const define = (object, key, value) => {
13715
+ Object.defineProperty(object, key, {value});
13716
+ return value
13717
+ };
14132
13718
 
14133
- return this._added
14134
- }
13719
+ const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13720
+
13721
+ const RETURN_FALSE = () => false;
13722
+
13723
+ // Sanitize the range of a regular expression
13724
+ // The cases are complicated, see test cases for details
13725
+ const sanitizeRange = range => range.replace(
13726
+ REGEX_REGEXP_RANGE,
13727
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13728
+ ? match
13729
+ // Invalid range (out of order) which is ok for gitignore rules but
13730
+ // fatal for JavaScript regular expression, so eliminate it.
13731
+ : EMPTY
13732
+ );
13733
+
13734
+ // See fixtures #59
13735
+ const cleanRangeBackSlash = slashes => {
13736
+ const {length} = slashes;
13737
+ return slashes.slice(0, length - length % 2)
13738
+ };
14135
13739
 
14136
- // Test one single path without recursively checking parent directories
14137
- //
14138
- // - checkUnignored `boolean` whether should check if the path is unignored,
14139
- // setting `checkUnignored` to `false` could reduce additional
14140
- // path matching.
14141
- // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
13740
+ // > If the pattern ends with a slash,
13741
+ // > it is removed for the purpose of the following description,
13742
+ // > but it would only find a match with a directory.
13743
+ // > In other words, foo/ will match a directory foo and paths underneath it,
13744
+ // > but will not match a regular file or a symbolic link foo
13745
+ // > (this is consistent with the way how pathspec works in general in Git).
13746
+ // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13747
+ // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13748
+ // you could use option `mark: true` with `glob`
13749
+
13750
+ // '`foo/`' should not continue with the '`..`'
13751
+ const REPLACERS = [
13752
+
13753
+ [
13754
+ // Remove BOM
13755
+ // TODO:
13756
+ // Other similar zero-width characters?
13757
+ /^\uFEFF/,
13758
+ () => EMPTY
13759
+ ],
13760
+
13761
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13762
+ [
13763
+ // (a\ ) -> (a )
13764
+ // (a ) -> (a)
13765
+ // (a ) -> (a)
13766
+ // (a \ ) -> (a )
13767
+ /((?:\\\\)*?)(\\?\s+)$/,
13768
+ (_, m1, m2) => m1 + (
13769
+ m2.indexOf('\\') === 0
13770
+ ? SPACE
13771
+ : EMPTY
13772
+ )
13773
+ ],
13774
+
13775
+ // Replace (\ ) with ' '
13776
+ // (\ ) -> ' '
13777
+ // (\\ ) -> '\\ '
13778
+ // (\\\ ) -> '\\ '
13779
+ [
13780
+ /(\\+?)\s/g,
13781
+ (_, m1) => {
13782
+ const {length} = m1;
13783
+ return m1.slice(0, length - length % 2) + SPACE
13784
+ }
13785
+ ],
13786
+
13787
+ // Escape metacharacters
13788
+ // which is written down by users but means special for regular expressions.
13789
+
13790
+ // > There are 12 characters with special meanings:
13791
+ // > - the backslash \,
13792
+ // > - the caret ^,
13793
+ // > - the dollar sign $,
13794
+ // > - the period or dot .,
13795
+ // > - the vertical bar or pipe symbol |,
13796
+ // > - the question mark ?,
13797
+ // > - the asterisk or star *,
13798
+ // > - the plus sign +,
13799
+ // > - the opening parenthesis (,
13800
+ // > - the closing parenthesis ),
13801
+ // > - and the opening square bracket [,
13802
+ // > - the opening curly brace {,
13803
+ // > These special characters are often called "metacharacters".
13804
+ [
13805
+ /[\\$.|*+(){^]/g,
13806
+ match => `\\${match}`
13807
+ ],
13808
+
13809
+ [
13810
+ // > a question mark (?) matches a single character
13811
+ /(?!\\)\?/g,
13812
+ () => '[^/]'
13813
+ ],
13814
+
13815
+ // leading slash
13816
+ [
13817
+
13818
+ // > A leading slash matches the beginning of the pathname.
13819
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13820
+ // A leading slash matches the beginning of the pathname
13821
+ /^\//,
13822
+ () => '^'
13823
+ ],
13824
+
13825
+ // replace special metacharacter slash after the leading slash
13826
+ [
13827
+ /\//g,
13828
+ () => '\\/'
13829
+ ],
13830
+
13831
+ [
13832
+ // > A leading "**" followed by a slash means match in all directories.
13833
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
13834
+ // > the same as pattern "foo".
13835
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13836
+ // > under directory "foo".
13837
+ // Notice that the '*'s have been replaced as '\\*'
13838
+ /^\^*\\\*\\\*\\\//,
13839
+
13840
+ // '**/foo' <-> 'foo'
13841
+ () => '^(?:.*\\/)?'
13842
+ ],
13843
+
13844
+ // starting
13845
+ [
13846
+ // there will be no leading '/'
13847
+ // (which has been replaced by section "leading slash")
13848
+ // If starts with '**', adding a '^' to the regular expression also works
13849
+ /^(?=[^^])/,
13850
+ function startingReplacer () {
13851
+ // If has a slash `/` at the beginning or middle
13852
+ return !/\/(?!$)/.test(this)
13853
+ // > Prior to 2.22.1
13854
+ // > If the pattern does not contain a slash /,
13855
+ // > Git treats it as a shell glob pattern
13856
+ // Actually, if there is only a trailing slash,
13857
+ // git also treats it as a shell glob pattern
13858
+
13859
+ // After 2.22.1 (compatible but clearer)
13860
+ // > If there is a separator at the beginning or middle (or both)
13861
+ // > of the pattern, then the pattern is relative to the directory
13862
+ // > level of the particular .gitignore file itself.
13863
+ // > Otherwise the pattern may also match at any level below
13864
+ // > the .gitignore level.
13865
+ ? '(?:^|\\/)'
13866
+
13867
+ // > Otherwise, Git treats the pattern as a shell glob suitable for
13868
+ // > consumption by fnmatch(3)
13869
+ : '^'
13870
+ }
13871
+ ],
13872
+
13873
+ // two globstars
13874
+ [
13875
+ // Use lookahead assertions so that we could match more than one `'/**'`
13876
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
13877
+
13878
+ // Zero, one or several directories
13879
+ // should not use '*', or it will be replaced by the next replacer
13880
+
13881
+ // Check if it is not the last `'/**'`
13882
+ (_, index, str) => index + 6 < str.length
13883
+
13884
+ // case: /**/
13885
+ // > A slash followed by two consecutive asterisks then a slash matches
13886
+ // > zero or more directories.
13887
+ // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13888
+ // '/**/'
13889
+ ? '(?:\\/[^\\/]+)*'
13890
+
13891
+ // case: /**
13892
+ // > A trailing `"/**"` matches everything inside.
13893
+
13894
+ // #21: everything inside but it should not include the current folder
13895
+ : '\\/.+'
13896
+ ],
13897
+
13898
+ // normal intermediate wildcards
13899
+ [
13900
+ // Never replace escaped '*'
13901
+ // ignore rule '\*' will match the path '*'
13902
+
13903
+ // 'abc.*/' -> go
13904
+ // 'abc.*' -> skip this rule,
13905
+ // coz trailing single wildcard will be handed by [trailing wildcard]
13906
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
13907
+
13908
+ // '*.js' matches '.js'
13909
+ // '*.js' doesn't match 'abc'
13910
+ (_, p1, p2) => {
13911
+ // 1.
13912
+ // > An asterisk "*" matches anything except a slash.
13913
+ // 2.
13914
+ // > Other consecutive asterisks are considered regular asterisks
13915
+ // > and will match according to the previous rules.
13916
+ const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13917
+ return p1 + unescaped
13918
+ }
13919
+ ],
13920
+
13921
+ [
13922
+ // unescape, revert step 3 except for back slash
13923
+ // For example, if a user escape a '\\*',
13924
+ // after step 3, the result will be '\\\\\\*'
13925
+ /\\\\\\(?=[$.|*+(){^])/g,
13926
+ () => ESCAPE
13927
+ ],
13928
+
13929
+ [
13930
+ // '\\\\' -> '\\'
13931
+ /\\\\/g,
13932
+ () => ESCAPE
13933
+ ],
13934
+
13935
+ [
13936
+ // > The range notation, e.g. [a-zA-Z],
13937
+ // > can be used to match one of the characters in a range.
13938
+
13939
+ // `\` is escaped by step 3
13940
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13941
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13942
+ // '\\[bar]' -> '\\\\[bar\\]'
13943
+ ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13944
+ : close === ']'
13945
+ ? endEscape.length % 2 === 0
13946
+ // A normal case, and it is a range notation
13947
+ // '[bar]'
13948
+ // '[bar\\\\]'
13949
+ ? `[${sanitizeRange(range)}${endEscape}]`
13950
+ // Invalid range notaton
13951
+ // '[bar\\]' -> '[bar\\\\]'
13952
+ : '[]'
13953
+ : '[]'
13954
+ ],
13955
+
13956
+ // ending
13957
+ [
13958
+ // 'js' will not match 'js.'
13959
+ // 'ab' will not match 'abc'
13960
+ /(?:[^*])$/,
13961
+
13962
+ // WTF!
13963
+ // https://git-scm.com/docs/gitignore
13964
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13965
+ // which re-fixes #24, #38
13966
+
13967
+ // > If there is a separator at the end of the pattern then the pattern
13968
+ // > will only match directories, otherwise the pattern can match both
13969
+ // > files and directories.
13970
+
13971
+ // 'js*' will not match 'a.js'
13972
+ // 'js/' will not match 'a.js'
13973
+ // 'js' will match 'a.js' and 'a.js/'
13974
+ match => /\/$/.test(match)
13975
+ // foo/ will not match 'foo'
13976
+ ? `${match}$`
13977
+ // foo matches 'foo' and 'foo/'
13978
+ : `${match}(?=$|\\/$)`
13979
+ ]
13980
+ ];
13981
+
13982
+ const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
13983
+ const MODE_IGNORE = 'regex';
13984
+ const MODE_CHECK_IGNORE = 'checkRegex';
13985
+ const UNDERSCORE = '_';
13986
+
13987
+ const TRAILING_WILD_CARD_REPLACERS = {
13988
+ [MODE_IGNORE] (_, p1) {
13989
+ const prefix = p1
13990
+ // '\^':
13991
+ // '/*' does not match EMPTY
13992
+ // '/*' does not match everything
13993
+
13994
+ // '\\\/':
13995
+ // 'abc/*' does not match 'abc/'
13996
+ ? `${p1}[^/]+`
13997
+
13998
+ // 'a*' matches 'a'
13999
+ // 'a*' matches 'aa'
14000
+ : '[^/]*';
14001
+
14002
+ return `${prefix}(?=$|\\/$)`
14003
+ },
14004
+
14005
+ [MODE_CHECK_IGNORE] (_, p1) {
14006
+ // When doing `git check-ignore`
14007
+ const prefix = p1
14008
+ // '\\\/':
14009
+ // 'abc/*' DOES match 'abc/' !
14010
+ ? `${p1}[^/]*`
14011
+
14012
+ // 'a*' matches 'a'
14013
+ // 'a*' matches 'aa'
14014
+ : '[^/]*';
14015
+
14016
+ return `${prefix}(?=$|\\/$)`
14017
+ }
14018
+ };
14142
14019
 
14143
- // @returns {TestResult} true if a file is ignored
14144
- test (path, checkUnignored, mode) {
14145
- let ignored = false;
14146
- let unignored = false;
14147
- let matchedRule;
14020
+ // @param {pattern}
14021
+ const makeRegexPrefix = pattern => REPLACERS.reduce(
14022
+ (prev, [matcher, replacer]) =>
14023
+ prev.replace(matcher, replacer.bind(pattern)),
14024
+ pattern
14025
+ );
14026
+
14027
+ const isString = subject => typeof subject === 'string';
14028
+
14029
+ // > A blank line matches no files, so it can serve as a separator for readability.
14030
+ const checkPattern = pattern => pattern
14031
+ && isString(pattern)
14032
+ && !REGEX_TEST_BLANK_LINE.test(pattern)
14033
+ && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
14034
+
14035
+ // > A line starting with # serves as a comment.
14036
+ && pattern.indexOf('#') !== 0;
14037
+
14038
+ const splitPattern = pattern => pattern
14039
+ .split(REGEX_SPLITALL_CRLF)
14040
+ .filter(Boolean);
14041
+
14042
+ class IgnoreRule {
14043
+ constructor (
14044
+ pattern,
14045
+ mark,
14046
+ body,
14047
+ ignoreCase,
14048
+ negative,
14049
+ prefix
14050
+ ) {
14051
+ this.pattern = pattern;
14052
+ this.mark = mark;
14053
+ this.negative = negative;
14054
+
14055
+ define(this, 'body', body);
14056
+ define(this, 'ignoreCase', ignoreCase);
14057
+ define(this, 'regexPrefix', prefix);
14058
+ }
14059
+
14060
+ get regex () {
14061
+ const key = UNDERSCORE + MODE_IGNORE;
14062
+
14063
+ if (this[key]) {
14064
+ return this[key]
14065
+ }
14066
+
14067
+ return this._make(MODE_IGNORE, key)
14068
+ }
14069
+
14070
+ get checkRegex () {
14071
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
14072
+
14073
+ if (this[key]) {
14074
+ return this[key]
14075
+ }
14076
+
14077
+ return this._make(MODE_CHECK_IGNORE, key)
14078
+ }
14079
+
14080
+ _make (mode, key) {
14081
+ const str = this.regexPrefix.replace(
14082
+ REGEX_REPLACE_TRAILING_WILDCARD,
14083
+
14084
+ // It does not need to bind pattern
14085
+ TRAILING_WILD_CARD_REPLACERS[mode]
14086
+ );
14087
+
14088
+ const regex = this.ignoreCase
14089
+ ? new RegExp(str, 'i')
14090
+ : new RegExp(str);
14091
+
14092
+ return define(this, key, regex)
14093
+ }
14094
+ }
14148
14095
 
14149
- this._rules.forEach(rule => {
14150
- const {negative} = rule;
14096
+ const createRule = ({
14097
+ pattern,
14098
+ mark
14099
+ }, ignoreCase) => {
14100
+ let negative = false;
14101
+ let body = pattern;
14102
+
14103
+ // > An optional prefix "!" which negates the pattern;
14104
+ if (body.indexOf('!') === 0) {
14105
+ negative = true;
14106
+ body = body.substr(1);
14107
+ }
14108
+
14109
+ body = body
14110
+ // > Put a backslash ("\") in front of the first "!" for patterns that
14111
+ // > begin with a literal "!", for example, `"\!important!.txt"`.
14112
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
14113
+ // > Put a backslash ("\") in front of the first hash for patterns that
14114
+ // > begin with a hash.
14115
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
14116
+
14117
+ const regexPrefix = makeRegexPrefix(body);
14118
+
14119
+ return new IgnoreRule(
14120
+ pattern,
14121
+ mark,
14122
+ body,
14123
+ ignoreCase,
14124
+ negative,
14125
+ regexPrefix
14126
+ )
14127
+ };
14151
14128
 
14152
- // | ignored : unignored
14153
- // -------- | ---------------------------------------
14154
- // negative | 0:0 | 0:1 | 1:0 | 1:1
14155
- // -------- | ------- | ------- | ------- | --------
14156
- // 0 | TEST | TEST | SKIP | X
14157
- // 1 | TESTIF | SKIP | TEST | X
14129
+ class RuleManager {
14130
+ constructor (ignoreCase) {
14131
+ this._ignoreCase = ignoreCase;
14132
+ this._rules = [];
14133
+ }
14134
+
14135
+ _add (pattern) {
14136
+ // #32
14137
+ if (pattern && pattern[KEY_IGNORE]) {
14138
+ this._rules = this._rules.concat(pattern._rules._rules);
14139
+ this._added = true;
14140
+ return
14141
+ }
14142
+
14143
+ if (isString(pattern)) {
14144
+ pattern = {
14145
+ pattern
14146
+ };
14147
+ }
14148
+
14149
+ if (checkPattern(pattern.pattern)) {
14150
+ const rule = createRule(pattern, this._ignoreCase);
14151
+ this._added = true;
14152
+ this._rules.push(rule);
14153
+ }
14154
+ }
14155
+
14156
+ // @param {Array<string> | string | Ignore} pattern
14157
+ add (pattern) {
14158
+ this._added = false;
14159
+
14160
+ makeArray(
14161
+ isString(pattern)
14162
+ ? splitPattern(pattern)
14163
+ : pattern
14164
+ ).forEach(this._add, this);
14165
+
14166
+ return this._added
14167
+ }
14168
+
14169
+ // Test one single path without recursively checking parent directories
14170
+ //
14171
+ // - checkUnignored `boolean` whether should check if the path is unignored,
14172
+ // setting `checkUnignored` to `false` could reduce additional
14173
+ // path matching.
14174
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
14175
+
14176
+ // @returns {TestResult} true if a file is ignored
14177
+ test (path, checkUnignored, mode) {
14178
+ let ignored = false;
14179
+ let unignored = false;
14180
+ let matchedRule;
14181
+
14182
+ this._rules.forEach(rule => {
14183
+ const {negative} = rule;
14184
+
14185
+ // | ignored : unignored
14186
+ // -------- | ---------------------------------------
14187
+ // negative | 0:0 | 0:1 | 1:0 | 1:1
14188
+ // -------- | ------- | ------- | ------- | --------
14189
+ // 0 | TEST | TEST | SKIP | X
14190
+ // 1 | TESTIF | SKIP | TEST | X
14191
+
14192
+ // - SKIP: always skip
14193
+ // - TEST: always test
14194
+ // - TESTIF: only test if checkUnignored
14195
+ // - X: that never happen
14196
+ if (
14197
+ unignored === negative && ignored !== unignored
14198
+ || negative && !ignored && !unignored && !checkUnignored
14199
+ ) {
14200
+ return
14201
+ }
14202
+
14203
+ const matched = rule[mode].test(path);
14204
+
14205
+ if (!matched) {
14206
+ return
14207
+ }
14208
+
14209
+ ignored = !negative;
14210
+ unignored = negative;
14211
+
14212
+ matchedRule = negative
14213
+ ? UNDEFINED
14214
+ : rule;
14215
+ });
14216
+
14217
+ const ret = {
14218
+ ignored,
14219
+ unignored
14220
+ };
14221
+
14222
+ if (matchedRule) {
14223
+ ret.rule = matchedRule;
14224
+ }
14225
+
14226
+ return ret
14227
+ }
14228
+ }
14158
14229
 
14159
- // - SKIP: always skip
14160
- // - TEST: always test
14161
- // - TESTIF: only test if checkUnignored
14162
- // - X: that never happen
14163
- if (
14164
- unignored === negative && ignored !== unignored
14165
- || negative && !ignored && !unignored && !checkUnignored
14166
- ) {
14167
- return
14168
- }
14230
+ const throwError = (message, Ctor) => {
14231
+ throw new Ctor(message)
14232
+ };
14169
14233
 
14170
- const matched = rule[mode].test(path);
14234
+ const checkPath = (path, originalPath, doThrow) => {
14235
+ if (!isString(path)) {
14236
+ return doThrow(
14237
+ `path must be a string, but got \`${originalPath}\``,
14238
+ TypeError
14239
+ )
14240
+ }
14241
+
14242
+ // We don't know if we should ignore EMPTY, so throw
14243
+ if (!path) {
14244
+ return doThrow(`path must not be empty`, TypeError)
14245
+ }
14246
+
14247
+ // Check if it is a relative path
14248
+ if (checkPath.isNotRelative(path)) {
14249
+ const r = '`path.relative()`d';
14250
+ return doThrow(
14251
+ `path should be a ${r} string, but got "${originalPath}"`,
14252
+ RangeError
14253
+ )
14254
+ }
14255
+
14256
+ return true
14257
+ };
14171
14258
 
14172
- if (!matched) {
14173
- return
14174
- }
14259
+ const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
14260
+
14261
+ checkPath.isNotRelative = isNotRelative;
14262
+
14263
+ // On windows, the following function will be replaced
14264
+ /* istanbul ignore next */
14265
+ checkPath.convert = p => p;
14266
+
14267
+
14268
+ class Ignore {
14269
+ constructor ({
14270
+ ignorecase = true,
14271
+ ignoreCase = ignorecase,
14272
+ allowRelativePaths = false
14273
+ } = {}) {
14274
+ define(this, KEY_IGNORE, true);
14275
+
14276
+ this._rules = new RuleManager(ignoreCase);
14277
+ this._strictPathCheck = !allowRelativePaths;
14278
+ this._initCache();
14279
+ }
14280
+
14281
+ _initCache () {
14282
+ // A cache for the result of `.ignores()`
14283
+ this._ignoreCache = Object.create(null);
14284
+
14285
+ // A cache for the result of `.test()`
14286
+ this._testCache = Object.create(null);
14287
+ }
14288
+
14289
+ add (pattern) {
14290
+ if (this._rules.add(pattern)) {
14291
+ // Some rules have just added to the ignore,
14292
+ // making the behavior changed,
14293
+ // so we need to re-initialize the result cache
14294
+ this._initCache();
14295
+ }
14296
+
14297
+ return this
14298
+ }
14299
+
14300
+ // legacy
14301
+ addPattern (pattern) {
14302
+ return this.add(pattern)
14303
+ }
14304
+
14305
+ // @returns {TestResult}
14306
+ _test (originalPath, cache, checkUnignored, slices) {
14307
+ const path = originalPath
14308
+ // Supports nullable path
14309
+ && checkPath.convert(originalPath);
14310
+
14311
+ checkPath(
14312
+ path,
14313
+ originalPath,
14314
+ this._strictPathCheck
14315
+ ? throwError
14316
+ : RETURN_FALSE
14317
+ );
14318
+
14319
+ return this._t(path, cache, checkUnignored, slices)
14320
+ }
14321
+
14322
+ checkIgnore (path) {
14323
+ // If the path doest not end with a slash, `.ignores()` is much equivalent
14324
+ // to `git check-ignore`
14325
+ if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
14326
+ return this.test(path)
14327
+ }
14328
+
14329
+ const slices = path.split(SLASH).filter(Boolean);
14330
+ slices.pop();
14331
+
14332
+ if (slices.length) {
14333
+ const parent = this._t(
14334
+ slices.join(SLASH) + SLASH,
14335
+ this._testCache,
14336
+ true,
14337
+ slices
14338
+ );
14339
+
14340
+ if (parent.ignored) {
14341
+ return parent
14342
+ }
14343
+ }
14344
+
14345
+ return this._rules.test(path, false, MODE_CHECK_IGNORE)
14346
+ }
14347
+
14348
+ _t (
14349
+ // The path to be tested
14350
+ path,
14351
+
14352
+ // The cache for the result of a certain checking
14353
+ cache,
14354
+
14355
+ // Whether should check if the path is unignored
14356
+ checkUnignored,
14357
+
14358
+ // The path slices
14359
+ slices
14360
+ ) {
14361
+ if (path in cache) {
14362
+ return cache[path]
14363
+ }
14364
+
14365
+ if (!slices) {
14366
+ // path/to/a.js
14367
+ // ['path', 'to', 'a.js']
14368
+ slices = path.split(SLASH).filter(Boolean);
14369
+ }
14370
+
14371
+ slices.pop();
14372
+
14373
+ // If the path has no parent directory, just test it
14374
+ if (!slices.length) {
14375
+ return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
14376
+ }
14377
+
14378
+ const parent = this._t(
14379
+ slices.join(SLASH) + SLASH,
14380
+ cache,
14381
+ checkUnignored,
14382
+ slices
14383
+ );
14384
+
14385
+ // If the path contains a parent directory, check the parent first
14386
+ return cache[path] = parent.ignored
14387
+ // > It is not possible to re-include a file if a parent directory of
14388
+ // > that file is excluded.
14389
+ ? parent
14390
+ : this._rules.test(path, checkUnignored, MODE_IGNORE)
14391
+ }
14392
+
14393
+ ignores (path) {
14394
+ return this._test(path, this._ignoreCache, false).ignored
14395
+ }
14396
+
14397
+ createFilter () {
14398
+ return path => !this.ignores(path)
14399
+ }
14400
+
14401
+ filter (paths) {
14402
+ return makeArray(paths).filter(this.createFilter())
14403
+ }
14404
+
14405
+ // @returns {TestResult}
14406
+ test (path) {
14407
+ return this._test(path, this._testCache, true)
14408
+ }
14409
+ }
14175
14410
 
14176
- ignored = !negative;
14177
- unignored = negative;
14411
+ const factory = options => new Ignore(options);
14412
+
14413
+ const isPathValid = path =>
14414
+ checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14415
+
14416
+
14417
+ // Windows
14418
+ // --------------------------------------------------------------
14419
+ /* istanbul ignore next */
14420
+ if (
14421
+ // Detect `process` so that it can run in browsers.
14422
+ typeof process !== 'undefined'
14423
+ && (
14424
+ process.env && process.env.IGNORE_TEST_WIN32
14425
+ || process.platform === 'win32'
14426
+ )
14427
+ ) {
14428
+ /* eslint no-control-regex: "off" */
14429
+ const makePosix = str => /^\\\\\?\\/.test(str)
14430
+ || /["<>|\u0000-\u001F]+/u.test(str)
14431
+ ? str
14432
+ : str.replace(/\\/g, '/');
14433
+
14434
+ checkPath.convert = makePosix;
14435
+
14436
+ // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14437
+ // 'd:\\foo'
14438
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14439
+ checkPath.isNotRelative = path =>
14440
+ REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
14441
+ || isNotRelative(path);
14442
+ }
14178
14443
 
14179
- matchedRule = negative
14180
- ? UNDEFINED
14181
- : rule;
14182
- });
14444
+ // COMMONJS_EXPORTS ////////////////////////////////////////////////////////////
14183
14445
 
14184
- const ret = {
14185
- ignored,
14186
- unignored
14187
- };
14446
+ ignore$1.exports = factory;
14188
14447
 
14189
- if (matchedRule) {
14190
- ret.rule = matchedRule;
14191
- }
14448
+ // Although it is an anti-pattern,
14449
+ // it is still widely misused by a lot of libraries in github
14450
+ // Ref: https://github.com/search?q=ignore.default%28%29&type=code
14451
+ factory.default = factory;
14192
14452
 
14193
- return ret
14194
- }
14453
+ ignore$1.exports.isPathValid = isPathValid;
14454
+ return ignore$1.exports;
14195
14455
  }
14196
14456
 
14197
- const throwError = (message, Ctor) => {
14198
- throw new Ctor(message)
14199
- };
14200
-
14201
- const checkPath = (path, originalPath, doThrow) => {
14202
- if (!isString(path)) {
14203
- return doThrow(
14204
- `path must be a string, but got \`${originalPath}\``,
14205
- TypeError
14206
- )
14207
- }
14208
-
14209
- // We don't know if we should ignore EMPTY, so throw
14210
- if (!path) {
14211
- return doThrow(`path must not be empty`, TypeError)
14212
- }
14213
-
14214
- // Check if it is a relative path
14215
- if (checkPath.isNotRelative(path)) {
14216
- const r = '`path.relative()`d';
14217
- return doThrow(
14218
- `path should be a ${r} string, but got "${originalPath}"`,
14219
- RangeError
14220
- )
14221
- }
14457
+ var ignoreExports = /*@__PURE__*/ requireIgnore();
14458
+ var ignore = /*@__PURE__*/getDefaultExportFromCjs(ignoreExports);
14222
14459
 
14223
- return true
14460
+ const engines = {
14461
+ node: "^18.19.0 || >= 20.6.0"
14224
14462
  };
14225
14463
 
14226
- const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
14227
-
14228
- checkPath.isNotRelative = isNotRelative;
14229
-
14230
- // On windows, the following function will be replaced
14231
- /* istanbul ignore next */
14232
- checkPath.convert = p => p;
14233
-
14234
-
14235
- class Ignore {
14236
- constructor ({
14237
- ignorecase = true,
14238
- ignoreCase = ignorecase,
14239
- allowRelativePaths = false
14240
- } = {}) {
14241
- define(this, KEY_IGNORE, true);
14242
-
14243
- this._rules = new RuleManager(ignoreCase);
14244
- this._strictPathCheck = !allowRelativePaths;
14245
- this._initCache();
14246
- }
14247
-
14248
- _initCache () {
14249
- // A cache for the result of `.ignores()`
14250
- this._ignoreCache = Object.create(null);
14251
-
14252
- // A cache for the result of `.test()`
14253
- this._testCache = Object.create(null);
14254
- }
14255
-
14256
- add (pattern) {
14257
- if (this._rules.add(pattern)) {
14258
- // Some rules have just added to the ignore,
14259
- // making the behavior changed,
14260
- // so we need to re-initialize the result cache
14261
- this._initCache();
14262
- }
14263
-
14264
- return this
14265
- }
14266
-
14267
- // legacy
14268
- addPattern (pattern) {
14269
- return this.add(pattern)
14270
- }
14271
-
14272
- // @returns {TestResult}
14273
- _test (originalPath, cache, checkUnignored, slices) {
14274
- const path = originalPath
14275
- // Supports nullable path
14276
- && checkPath.convert(originalPath);
14277
-
14278
- checkPath(
14279
- path,
14280
- originalPath,
14281
- this._strictPathCheck
14282
- ? throwError
14283
- : RETURN_FALSE
14284
- );
14285
-
14286
- return this._t(path, cache, checkUnignored, slices)
14287
- }
14288
-
14289
- checkIgnore (path) {
14290
- // If the path doest not end with a slash, `.ignores()` is much equivalent
14291
- // to `git check-ignore`
14292
- if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
14293
- return this.test(path)
14294
- }
14295
-
14296
- const slices = path.split(SLASH).filter(Boolean);
14297
- slices.pop();
14298
-
14299
- if (slices.length) {
14300
- const parent = this._t(
14301
- slices.join(SLASH) + SLASH,
14302
- this._testCache,
14303
- true,
14304
- slices
14305
- );
14306
-
14307
- if (parent.ignored) {
14308
- return parent
14309
- }
14310
- }
14311
-
14312
- return this._rules.test(path, false, MODE_CHECK_IGNORE)
14313
- }
14314
-
14315
- _t (
14316
- // The path to be tested
14317
- path,
14318
-
14319
- // The cache for the result of a certain checking
14320
- cache,
14321
-
14322
- // Whether should check if the path is unignored
14323
- checkUnignored,
14324
-
14325
- // The path slices
14326
- slices
14327
- ) {
14328
- if (path in cache) {
14329
- return cache[path]
14330
- }
14331
-
14332
- if (!slices) {
14333
- // path/to/a.js
14334
- // ['path', 'to', 'a.js']
14335
- slices = path.split(SLASH).filter(Boolean);
14336
- }
14337
-
14338
- slices.pop();
14339
-
14340
- // If the path has no parent directory, just test it
14341
- if (!slices.length) {
14342
- return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
14343
- }
14344
-
14345
- const parent = this._t(
14346
- slices.join(SLASH) + SLASH,
14347
- cache,
14348
- checkUnignored,
14349
- slices
14350
- );
14351
-
14352
- // If the path contains a parent directory, check the parent first
14353
- return cache[path] = parent.ignored
14354
- // > It is not possible to re-include a file if a parent directory of
14355
- // > that file is excluded.
14356
- ? parent
14357
- : this._rules.test(path, checkUnignored, MODE_IGNORE)
14358
- }
14359
-
14360
- ignores (path) {
14361
- return this._test(path, this._ignoreCache, false).ignored
14362
- }
14363
-
14364
- createFilter () {
14365
- return path => !this.ignores(path)
14366
- }
14367
-
14368
- filter (paths) {
14369
- return makeArray(paths).filter(this.createFilter())
14370
- }
14371
-
14372
- // @returns {TestResult}
14373
- test (path) {
14374
- return this._test(path, this._testCache, true)
14375
- }
14376
- }
14377
-
14378
- const factory = options => new Ignore(options);
14379
-
14380
- const isPathValid = path =>
14381
- checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14382
-
14383
- factory.isPathValid = isPathValid;
14384
-
14385
-
14386
- // Windows
14387
- // --------------------------------------------------------------
14388
- /* istanbul ignore next */
14389
- if (
14390
- // Detect `process` so that it can run in browsers.
14391
- typeof process !== 'undefined'
14392
- && (
14393
- process.env && process.env.IGNORE_TEST_WIN32
14394
- || process.platform === 'win32'
14395
- )
14396
- ) {
14397
- /* eslint no-control-regex: "off" */
14398
- const makePosix = str => /^\\\\\?\\/.test(str)
14399
- || /["<>|\u0000-\u001F]+/u.test(str)
14400
- ? str
14401
- : str.replace(/\\/g, '/');
14402
-
14403
- checkPath.convert = makePosix;
14404
-
14405
- // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14406
- // 'd:\\foo'
14407
- const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14408
- checkPath.isNotRelative = path =>
14409
- REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
14410
- || isNotRelative(path);
14411
- }
14412
-
14413
14464
  var workerPath = "./jest-worker.js";
14414
14465
 
14415
14466
  exports.Attribute = Attribute;
@@ -14449,10 +14500,12 @@ exports.compatibilityCheckImpl = compatibilityCheckImpl;
14449
14500
  exports.deepmerge = deepmerge;
14450
14501
  exports.defineConfig = defineConfig;
14451
14502
  exports.definePlugin = definePlugin;
14503
+ exports.engines = engines;
14452
14504
  exports.ensureError = ensureError;
14453
- exports.factory = factory;
14454
14505
  exports.getFormatter = getFormatter;
14506
+ exports.ignore = ignore;
14455
14507
  exports.isThenable = isThenable;
14508
+ exports.isUserError = isUserError;
14456
14509
  exports.keywordPatternMatcher = keywordPatternMatcher;
14457
14510
  exports.name = name;
14458
14511
  exports.presets = presets;