html-validate 9.0.0 → 9.1.0

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
@@ -1937,7 +1937,7 @@ const table = {
1937
1937
  "nth-child": nthChild,
1938
1938
  scope
1939
1939
  };
1940
- function factory(name, context) {
1940
+ function factory$1(name, context) {
1941
1941
  const fn = table[name];
1942
1942
  if (fn) {
1943
1943
  return fn.bind(context);
@@ -2036,7 +2036,7 @@ function isQuotationMark(ch) {
2036
2036
  function isPseudoElement(ch, buffer) {
2037
2037
  return ch === ":" && buffer === ":";
2038
2038
  }
2039
- function* splitPattern(pattern) {
2039
+ function* splitPattern$1(pattern) {
2040
2040
  if (pattern === "") {
2041
2041
  return;
2042
2042
  }
@@ -2138,7 +2138,7 @@ class PseudoClassMatcher extends Matcher {
2138
2138
  this.args = args;
2139
2139
  }
2140
2140
  match(node, context) {
2141
- const fn = factory(this.name, context);
2141
+ const fn = factory$1(this.name, context);
2142
2142
  return fn(node, this.args);
2143
2143
  }
2144
2144
  }
@@ -2156,7 +2156,7 @@ class Pattern {
2156
2156
  this.selector = pattern;
2157
2157
  this.combinator = parseCombinator(match.shift(), pattern);
2158
2158
  this.tagName = match.shift() || "*";
2159
- this.pattern = Array.from(splitPattern(match[0]), (it) => this.createMatcher(it));
2159
+ this.pattern = Array.from(splitPattern$1(match[0]), (it) => this.createMatcher(it));
2160
2160
  }
2161
2161
  match(node, context) {
2162
2162
  return node.is(this.tagName) && this.pattern.every((cur) => cur.match(node, context));
@@ -10723,24 +10723,59 @@ const presets = {
10723
10723
  "html-validate:standard": config
10724
10724
  };
10725
10725
 
10726
- function getNamedTransformerFromPlugin(name, plugins, pluginName, key) {
10727
- const plugin = plugins.find((cur) => cur.name === pluginName);
10728
- if (!plugin) {
10729
- throw new ConfigError(`No plugin named "${pluginName}" has been loaded`);
10726
+ class ResolvedConfig {
10727
+ metaTable;
10728
+ plugins;
10729
+ rules;
10730
+ transformers;
10731
+ /** The original data this resolved configuration was created from */
10732
+ original;
10733
+ /**
10734
+ * @internal
10735
+ */
10736
+ cache;
10737
+ /**
10738
+ * @internal
10739
+ */
10740
+ constructor({ metaTable, plugins, rules, transformers }, original) {
10741
+ this.metaTable = metaTable;
10742
+ this.plugins = plugins;
10743
+ this.rules = rules;
10744
+ this.transformers = transformers;
10745
+ this.cache = /* @__PURE__ */ new Map();
10746
+ this.original = original;
10730
10747
  }
10731
- if (!plugin.transformer) {
10732
- throw new ConfigError(`Plugin does not expose any transformers`);
10748
+ /**
10749
+ * Returns the (merged) configuration data used to create this resolved
10750
+ * configuration.
10751
+ */
10752
+ getConfigData() {
10753
+ return this.original;
10733
10754
  }
10734
- if (typeof plugin.transformer === "function") {
10735
- throw new ConfigError(
10736
- `Transformer "${name}" refers to named transformer but plugin exposes only unnamed, use "${pluginName}" instead.`
10737
- );
10755
+ getMetaTable() {
10756
+ return this.metaTable;
10738
10757
  }
10739
- const transformer = plugin.transformer[key];
10740
- if (!transformer) {
10741
- throw new ConfigError(`Plugin "${pluginName}" does not expose a transformer named "${key}".`);
10758
+ getPlugins() {
10759
+ return this.plugins;
10760
+ }
10761
+ getRules() {
10762
+ return this.rules;
10763
+ }
10764
+ /**
10765
+ * Returns true if a transformer matches given filename.
10766
+ *
10767
+ * @public
10768
+ */
10769
+ canTransform(filename) {
10770
+ return Boolean(this.findTransformer(filename));
10771
+ }
10772
+ /**
10773
+ * @internal
10774
+ */
10775
+ findTransformer(filename) {
10776
+ const match = this.transformers.find((entry) => entry.pattern.test(filename));
10777
+ return match ?? null;
10742
10778
  }
10743
- return transformer;
10744
10779
  }
10745
10780
 
10746
10781
  function haveResolver(key, value) {
@@ -10874,283 +10909,6 @@ function staticResolver(map = {}) {
10874
10909
  };
10875
10910
  }
10876
10911
 
10877
- function getTransformerFromModule(resolvers, name) {
10878
- return resolveTransformer(resolvers, name, { cache: true });
10879
- }
10880
-
10881
- function getUnnamedTransformerFromPlugin(name, plugin) {
10882
- if (!plugin.transformer) {
10883
- throw new ConfigError(`Plugin does not expose any transformers`);
10884
- }
10885
- if (typeof plugin.transformer !== "function") {
10886
- if (plugin.transformer.default) {
10887
- return plugin.transformer.default;
10888
- }
10889
- throw new ConfigError(
10890
- `Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
10891
- );
10892
- }
10893
- return plugin.transformer;
10894
- }
10895
-
10896
- const TRANSFORMER_API = {
10897
- VERSION: 1
10898
- };
10899
-
10900
- function validateTransformer(transformer) {
10901
- const version = transformer.api ?? 0;
10902
- if (version !== TRANSFORMER_API.VERSION) {
10903
- throw new ConfigError(
10904
- `Transformer uses API version ${String(version)} but only version ${String(TRANSFORMER_API.VERSION)} is supported`
10905
- );
10906
- }
10907
- }
10908
- function loadTransformerFunction(resolvers, name, plugins) {
10909
- const match = name.match(/(.*):(.*)/);
10910
- if (match) {
10911
- const [, pluginName, key] = match;
10912
- return getNamedTransformerFromPlugin(name, plugins, pluginName, key);
10913
- }
10914
- const plugin = plugins.find((cur) => cur.name === name);
10915
- if (plugin) {
10916
- return getUnnamedTransformerFromPlugin(name, plugin);
10917
- }
10918
- return getTransformerFromModule(resolvers, name);
10919
- }
10920
- function getTransformerFunction(resolvers, name, plugins) {
10921
- try {
10922
- const transformer = loadTransformerFunction(resolvers, name, plugins);
10923
- if (isThenable(transformer)) {
10924
- return transformer.then((transformer2) => {
10925
- validateTransformer(transformer2);
10926
- return transformer2;
10927
- });
10928
- } else {
10929
- validateTransformer(transformer);
10930
- return transformer;
10931
- }
10932
- } catch (err) {
10933
- if (err instanceof ConfigError) {
10934
- throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
10935
- } else {
10936
- throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
10937
- }
10938
- }
10939
- }
10940
- function getCachedTransformerFunction(cache, resolvers, name, plugins) {
10941
- const cached = cache.get(name);
10942
- if (cached) {
10943
- return cached;
10944
- } else {
10945
- const transformer = getTransformerFunction(resolvers, name, plugins);
10946
- if (isThenable(transformer)) {
10947
- return transformer.then((transformer2) => {
10948
- cache.set(name, transformer2);
10949
- return transformer2;
10950
- });
10951
- } else {
10952
- cache.set(name, transformer);
10953
- return transformer;
10954
- }
10955
- }
10956
- }
10957
-
10958
- function isIterable(value) {
10959
- return Boolean(value && typeof value === "object" && Symbol.iterator in value);
10960
- }
10961
- function isNonThenableArray(value) {
10962
- return !value.some(isThenable);
10963
- }
10964
- function toArray$1(value) {
10965
- return isIterable(value) ? Array.from(value) : [value];
10966
- }
10967
- const asyncInSyncTransformError = "Cannot use async transformer from sync function";
10968
- class ResolvedConfig {
10969
- metaTable;
10970
- plugins;
10971
- rules;
10972
- transformers;
10973
- cache;
10974
- /** The original data this resolved configuration was created from */
10975
- original;
10976
- /**
10977
- * @internal
10978
- */
10979
- constructor({ metaTable, plugins, rules, transformers }, original) {
10980
- this.metaTable = metaTable;
10981
- this.plugins = plugins;
10982
- this.rules = rules;
10983
- this.transformers = transformers;
10984
- this.cache = /* @__PURE__ */ new Map();
10985
- this.original = original;
10986
- }
10987
- /**
10988
- * Returns the (merged) configuration data used to create this resolved
10989
- * configuration.
10990
- */
10991
- getConfigData() {
10992
- return this.original;
10993
- }
10994
- getMetaTable() {
10995
- return this.metaTable;
10996
- }
10997
- getPlugins() {
10998
- return this.plugins;
10999
- }
11000
- getRules() {
11001
- return this.rules;
11002
- }
11003
- /**
11004
- * Transform a source.
11005
- *
11006
- * When transforming zero or more new sources will be generated.
11007
- *
11008
- * @internal
11009
- * @param source - Current source to transform.
11010
- * @param filename - If set it is the filename used to match
11011
- * transformer. Default is to use filename from source.
11012
- * @returns A list of transformed sources ready for validation.
11013
- */
11014
- async transformSource(resolvers, source, filename) {
11015
- const transformer = this.findTransformer(filename ?? source.filename);
11016
- const context = {
11017
- hasChain: (filename2) => {
11018
- return !!this.findTransformer(filename2);
11019
- },
11020
- chain: (source2, filename2) => {
11021
- return this.transformSource(resolvers, source2, filename2);
11022
- }
11023
- };
11024
- if (!transformer) {
11025
- return Promise.resolve([source]);
11026
- }
11027
- const fn = transformer.kind === "import" ? await getCachedTransformerFunction(this.cache, resolvers, transformer.name, this.plugins) : transformer.function;
11028
- const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
11029
- try {
11030
- const result = await fn.call(context, source);
11031
- const transformedSources = await Promise.all(toArray$1(result));
11032
- for (const source2 of transformedSources) {
11033
- source2.transformedBy ??= [];
11034
- source2.transformedBy.push(name);
11035
- }
11036
- return transformedSources;
11037
- } catch (err) {
11038
- const message = err instanceof Error ? err.message : String(err);
11039
- throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
11040
- }
11041
- }
11042
- /**
11043
- * Transform a source.
11044
- *
11045
- * When transforming zero or more new sources will be generated.
11046
- *
11047
- * @internal
11048
- * @param source - Current source to transform.
11049
- * @param filename - If set it is the filename used to match
11050
- * transformer. Default is to use filename from source.
11051
- * @returns A list of transformed sources ready for validation.
11052
- */
11053
- /* eslint-disable-next-line complexity -- there is many ifs'n buts here but
11054
- * hard to break this down without loosing the little clarity that is still
11055
- * left */
11056
- transformSourceSync(resolvers, source, filename) {
11057
- const transformer = this.findTransformer(filename ?? source.filename);
11058
- const context = {
11059
- hasChain: (filename2) => {
11060
- return !!this.findTransformer(filename2);
11061
- },
11062
- chain: (source2, filename2) => {
11063
- return this.transformSourceSync(resolvers, source2, filename2);
11064
- }
11065
- };
11066
- if (!transformer) {
11067
- return [source];
11068
- }
11069
- const fn = transformer.kind === "import" ? getCachedTransformerFunction(this.cache, resolvers, transformer.name, this.plugins) : transformer.function;
11070
- const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
11071
- if (isThenable(fn)) {
11072
- throw new UserError(asyncInSyncTransformError);
11073
- }
11074
- try {
11075
- const result = fn.call(context, source);
11076
- if (isThenable(result)) {
11077
- throw new UserError(asyncInSyncTransformError);
11078
- }
11079
- const transformedSources = toArray$1(result);
11080
- if (!isNonThenableArray(transformedSources)) {
11081
- throw new UserError(asyncInSyncTransformError);
11082
- }
11083
- for (const source2 of transformedSources) {
11084
- source2.transformedBy ??= [];
11085
- source2.transformedBy.push(name);
11086
- }
11087
- return transformedSources;
11088
- } catch (err) {
11089
- const message = err instanceof Error ? err.message : String(err);
11090
- throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
11091
- }
11092
- }
11093
- /**
11094
- * Wrapper around [[transformSource]] which reads a file before passing it
11095
- * as-is to transformSource.
11096
- *
11097
- * @internal
11098
- * @param filename - Filename to transform (according to configured
11099
- * transformations)
11100
- * @returns A list of transformed sources ready for validation.
11101
- */
11102
- transformFilename(resolvers, filename) {
11103
- const stdin = 0;
11104
- const src = filename !== "/dev/stdin" ? filename : stdin;
11105
- const data = fs.readFileSync(src, { encoding: "utf8" });
11106
- const source = {
11107
- data,
11108
- filename,
11109
- line: 1,
11110
- column: 1,
11111
- offset: 0,
11112
- originalData: data
11113
- };
11114
- return this.transformSource(resolvers, source, filename);
11115
- }
11116
- /**
11117
- * Wrapper around [[transformSource]] which reads a file before passing it
11118
- * as-is to transformSource.
11119
- *
11120
- * @internal
11121
- * @param filename - Filename to transform (according to configured
11122
- * transformations)
11123
- * @returns A list of transformed sources ready for validation.
11124
- */
11125
- transformFilenameSync(resolvers, filename) {
11126
- const stdin = 0;
11127
- const src = filename !== "/dev/stdin" ? filename : stdin;
11128
- const data = fs.readFileSync(src, { encoding: "utf8" });
11129
- const source = {
11130
- data,
11131
- filename,
11132
- line: 1,
11133
- column: 1,
11134
- offset: 0,
11135
- originalData: data
11136
- };
11137
- return this.transformSourceSync(resolvers, source, filename);
11138
- }
11139
- /**
11140
- * Returns true if a transformer matches given filename.
11141
- *
11142
- * @public
11143
- */
11144
- canTransform(filename) {
11145
- const entry = this.findTransformer(filename);
11146
- return !!entry;
11147
- }
11148
- findTransformer(filename) {
11149
- const match = this.transformers.find((entry) => entry.pattern.test(filename));
11150
- return match ?? null;
11151
- }
11152
- }
11153
-
11154
10912
  const ajv = new Ajv({ strict: true, strictTuples: true, strictTypes: true });
11155
10913
  ajv.addMetaSchema(ajvSchemaDraft);
11156
10914
  ajv.addKeyword(ajvFunctionKeyword);
@@ -11169,7 +10927,7 @@ function mergeInternal(base, rhs) {
11169
10927
  }
11170
10928
  return dst;
11171
10929
  }
11172
- function toArray(value) {
10930
+ function toArray$1(value) {
11173
10931
  if (Array.isArray(value)) {
11174
10932
  return value;
11175
10933
  } else {
@@ -11221,7 +10979,7 @@ class Config {
11221
10979
  * @param filename - The file to read from
11222
10980
  */
11223
10981
  static fromFile(resolvers, filename) {
11224
- const configData = resolveConfig(toArray(resolvers), filename, { cache: false });
10982
+ const configData = resolveConfig(toArray$1(resolvers), filename, { cache: false });
11225
10983
  if (isThenable(configData)) {
11226
10984
  return configData.then((configData2) => Config.fromObject(resolvers, configData2, filename));
11227
10985
  } else {
@@ -11307,7 +11065,7 @@ class Config {
11307
11065
  };
11308
11066
  this.config = mergeInternal(initial, options);
11309
11067
  this.configurations = /* @__PURE__ */ new Map();
11310
- this.resolvers = toArray(resolvers);
11068
+ this.resolvers = toArray$1(resolvers);
11311
11069
  this.metaTable = null;
11312
11070
  this.plugins = [];
11313
11071
  this.transformers = transformerEntries(this.config.transform ?? {});
@@ -12893,43 +12651,251 @@ class StaticConfigLoader extends ConfigLoader {
12893
12651
  return this._resolveConfig(override);
12894
12652
  }
12895
12653
  }
12896
- flushCache() {
12654
+ flushCache() {
12655
+ }
12656
+ defaultConfig() {
12657
+ return this.loadFromObject({
12658
+ extends: ["html-validate:recommended"],
12659
+ elements: ["html5"]
12660
+ });
12661
+ }
12662
+ _resolveConfig(override) {
12663
+ if (override.isRootFound()) {
12664
+ return override.resolve();
12665
+ }
12666
+ const globalConfig = this.getGlobalConfig();
12667
+ if (isThenable(globalConfig)) {
12668
+ return globalConfig.then((globalConfig2) => {
12669
+ const merged = globalConfig2.merge(this.resolvers, override);
12670
+ if (isThenable(merged)) {
12671
+ return merged.then((merged2) => {
12672
+ return merged2.resolve();
12673
+ });
12674
+ } else {
12675
+ return merged.resolve();
12676
+ }
12677
+ });
12678
+ } else {
12679
+ const merged = globalConfig.merge(this.resolvers, override);
12680
+ if (isThenable(merged)) {
12681
+ return merged.then((merged2) => {
12682
+ return merged2.resolve();
12683
+ });
12684
+ } else {
12685
+ return merged.resolve();
12686
+ }
12687
+ }
12688
+ }
12689
+ }
12690
+
12691
+ function getNamedTransformerFromPlugin(name, plugins, pluginName, key) {
12692
+ const plugin = plugins.find((cur) => cur.name === pluginName);
12693
+ if (!plugin) {
12694
+ throw new ConfigError(`No plugin named "${pluginName}" has been loaded`);
12695
+ }
12696
+ if (!plugin.transformer) {
12697
+ throw new ConfigError(`Plugin does not expose any transformers`);
12698
+ }
12699
+ if (typeof plugin.transformer === "function") {
12700
+ throw new ConfigError(
12701
+ `Transformer "${name}" refers to named transformer but plugin exposes only unnamed, use "${pluginName}" instead.`
12702
+ );
12703
+ }
12704
+ const transformer = plugin.transformer[key];
12705
+ if (!transformer) {
12706
+ throw new ConfigError(`Plugin "${pluginName}" does not expose a transformer named "${key}".`);
12707
+ }
12708
+ return transformer;
12709
+ }
12710
+
12711
+ function getTransformerFromModule(resolvers, name) {
12712
+ return resolveTransformer(resolvers, name, { cache: true });
12713
+ }
12714
+
12715
+ function getUnnamedTransformerFromPlugin(name, plugin) {
12716
+ if (!plugin.transformer) {
12717
+ throw new ConfigError(`Plugin does not expose any transformers`);
12718
+ }
12719
+ if (typeof plugin.transformer !== "function") {
12720
+ if (plugin.transformer.default) {
12721
+ return plugin.transformer.default;
12722
+ }
12723
+ throw new ConfigError(
12724
+ `Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
12725
+ );
12726
+ }
12727
+ return plugin.transformer;
12728
+ }
12729
+
12730
+ const TRANSFORMER_API = {
12731
+ VERSION: 1
12732
+ };
12733
+
12734
+ function validateTransformer(transformer) {
12735
+ const version = transformer.api ?? 0;
12736
+ if (version !== TRANSFORMER_API.VERSION) {
12737
+ throw new ConfigError(
12738
+ `Transformer uses API version ${String(version)} but only version ${String(TRANSFORMER_API.VERSION)} is supported`
12739
+ );
12740
+ }
12741
+ }
12742
+ function loadTransformerFunction(resolvers, name, plugins) {
12743
+ const match = name.match(/(.*):(.*)/);
12744
+ if (match) {
12745
+ const [, pluginName, key] = match;
12746
+ return getNamedTransformerFromPlugin(name, plugins, pluginName, key);
12747
+ }
12748
+ const plugin = plugins.find((cur) => cur.name === name);
12749
+ if (plugin) {
12750
+ return getUnnamedTransformerFromPlugin(name, plugin);
12751
+ }
12752
+ return getTransformerFromModule(resolvers, name);
12753
+ }
12754
+ function getTransformerFunction(resolvers, name, plugins) {
12755
+ try {
12756
+ const transformer = loadTransformerFunction(resolvers, name, plugins);
12757
+ if (isThenable(transformer)) {
12758
+ return transformer.then((transformer2) => {
12759
+ validateTransformer(transformer2);
12760
+ return transformer2;
12761
+ });
12762
+ } else {
12763
+ validateTransformer(transformer);
12764
+ return transformer;
12765
+ }
12766
+ } catch (err) {
12767
+ if (err instanceof ConfigError) {
12768
+ throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
12769
+ } else {
12770
+ throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
12771
+ }
12772
+ }
12773
+ }
12774
+ function getCachedTransformerFunction(cache, resolvers, name, plugins) {
12775
+ const cached = cache.get(name);
12776
+ if (cached) {
12777
+ return cached;
12778
+ } else {
12779
+ const transformer = getTransformerFunction(resolvers, name, plugins);
12780
+ if (isThenable(transformer)) {
12781
+ return transformer.then((transformer2) => {
12782
+ cache.set(name, transformer2);
12783
+ return transformer2;
12784
+ });
12785
+ } else {
12786
+ cache.set(name, transformer);
12787
+ return transformer;
12788
+ }
12789
+ }
12790
+ }
12791
+
12792
+ function isIterable(value) {
12793
+ return Boolean(value && typeof value === "object" && Symbol.iterator in value);
12794
+ }
12795
+ function toArray(value) {
12796
+ return isIterable(value) ? Array.from(value) : [value];
12797
+ }
12798
+ function isNonThenableArray(value) {
12799
+ return !value.some(isThenable);
12800
+ }
12801
+ const asyncInSyncTransformError = "Cannot use async transformer from sync function";
12802
+ async function transformSource(resolvers, config, source, filename) {
12803
+ const { cache } = config;
12804
+ const transformer = config.findTransformer(filename ?? source.filename);
12805
+ const context = {
12806
+ hasChain(filename2) {
12807
+ return config.canTransform(filename2);
12808
+ },
12809
+ chain(source2, filename2) {
12810
+ return transformSource(resolvers, config, source2, filename2);
12811
+ }
12812
+ };
12813
+ if (!transformer) {
12814
+ return Promise.resolve([source]);
12815
+ }
12816
+ const fn = transformer.kind === "import" ? await getCachedTransformerFunction(cache, resolvers, transformer.name, config.getPlugins()) : transformer.function;
12817
+ const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
12818
+ try {
12819
+ const result = await fn.call(context, source);
12820
+ const transformedSources = await Promise.all(toArray(result));
12821
+ for (const source2 of transformedSources) {
12822
+ source2.transformedBy ??= [];
12823
+ source2.transformedBy.push(name);
12824
+ }
12825
+ return transformedSources;
12826
+ } catch (err) {
12827
+ const message = err instanceof Error ? err.message : String(err);
12828
+ throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
12829
+ }
12830
+ }
12831
+ function transformSourceSync(resolvers, config, source, filename) {
12832
+ const { cache } = config;
12833
+ const transformer = config.findTransformer(filename ?? source.filename);
12834
+ const context = {
12835
+ hasChain(filename2) {
12836
+ return config.canTransform(filename2);
12837
+ },
12838
+ chain(source2, filename2) {
12839
+ return transformSourceSync(resolvers, config, source2, filename2);
12840
+ }
12841
+ };
12842
+ if (!transformer) {
12843
+ return [source];
12897
12844
  }
12898
- defaultConfig() {
12899
- return this.loadFromObject({
12900
- extends: ["html-validate:recommended"],
12901
- elements: ["html5"]
12902
- });
12845
+ const fn = transformer.kind === "import" ? getCachedTransformerFunction(cache, resolvers, transformer.name, config.getPlugins()) : transformer.function;
12846
+ const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
12847
+ if (isThenable(fn)) {
12848
+ throw new UserError(asyncInSyncTransformError);
12903
12849
  }
12904
- _resolveConfig(override) {
12905
- if (override.isRootFound()) {
12906
- return override.resolve();
12850
+ try {
12851
+ const result = fn.call(context, source);
12852
+ if (isThenable(result)) {
12853
+ throw new UserError(asyncInSyncTransformError);
12907
12854
  }
12908
- const globalConfig = this.getGlobalConfig();
12909
- if (isThenable(globalConfig)) {
12910
- return globalConfig.then((globalConfig2) => {
12911
- const merged = globalConfig2.merge(this.resolvers, override);
12912
- if (isThenable(merged)) {
12913
- return merged.then((merged2) => {
12914
- return merged2.resolve();
12915
- });
12916
- } else {
12917
- return merged.resolve();
12918
- }
12919
- });
12920
- } else {
12921
- const merged = globalConfig.merge(this.resolvers, override);
12922
- if (isThenable(merged)) {
12923
- return merged.then((merged2) => {
12924
- return merged2.resolve();
12925
- });
12926
- } else {
12927
- return merged.resolve();
12928
- }
12855
+ const transformedSources = toArray(result);
12856
+ if (!isNonThenableArray(transformedSources)) {
12857
+ throw new UserError(asyncInSyncTransformError);
12929
12858
  }
12859
+ for (const source2 of transformedSources) {
12860
+ source2.transformedBy ??= [];
12861
+ source2.transformedBy.push(name);
12862
+ }
12863
+ return transformedSources;
12864
+ } catch (err) {
12865
+ const message = err instanceof Error ? err.message : String(err);
12866
+ throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
12930
12867
  }
12931
12868
  }
12932
12869
 
12870
+ function transformFilename(resolvers, config, filename) {
12871
+ const stdin = 0;
12872
+ const src = filename !== "/dev/stdin" ? filename : stdin;
12873
+ const data = fs.readFileSync(src, { encoding: "utf8" });
12874
+ const source = {
12875
+ data,
12876
+ filename,
12877
+ line: 1,
12878
+ column: 1,
12879
+ offset: 0,
12880
+ originalData: data
12881
+ };
12882
+ return transformSource(resolvers, config, source, filename);
12883
+ }
12884
+ function transformFilenameSync(resolvers, config, filename) {
12885
+ const stdin = 0;
12886
+ const src = filename !== "/dev/stdin" ? filename : stdin;
12887
+ const data = fs.readFileSync(src, { encoding: "utf8" });
12888
+ const source = {
12889
+ data,
12890
+ filename,
12891
+ line: 1,
12892
+ column: 1,
12893
+ offset: 0,
12894
+ originalData: data
12895
+ };
12896
+ return transformSourceSync(resolvers, config, source, filename);
12897
+ }
12898
+
12933
12899
  function isSourceHooks(value) {
12934
12900
  if (!value || typeof value === "string") {
12935
12901
  return false;
@@ -12989,7 +12955,7 @@ class HtmlValidate {
12989
12955
  const source = normalizeSource(input);
12990
12956
  const config = await this.getConfigFor(source.filename, configOverride);
12991
12957
  const resolvers = this.configLoader.getResolvers();
12992
- const transformedSource = await config.transformSource(resolvers, source);
12958
+ const transformedSource = await transformSource(resolvers, config, source);
12993
12959
  const engine = new Engine(config, Parser);
12994
12960
  return engine.lint(transformedSource);
12995
12961
  }
@@ -13004,7 +12970,7 @@ class HtmlValidate {
13004
12970
  const source = normalizeSource(input);
13005
12971
  const config = this.getConfigForSync(source.filename, configOverride);
13006
12972
  const resolvers = this.configLoader.getResolvers();
13007
- const transformedSource = config.transformSourceSync(resolvers, source);
12973
+ const transformedSource = transformSourceSync(resolvers, config, source);
13008
12974
  const engine = new Engine(config, Parser);
13009
12975
  return engine.lint(transformedSource);
13010
12976
  }
@@ -13018,7 +12984,7 @@ class HtmlValidate {
13018
12984
  async validateFile(filename) {
13019
12985
  const config = await this.getConfigFor(filename);
13020
12986
  const resolvers = this.configLoader.getResolvers();
13021
- const source = await config.transformFilename(resolvers, filename);
12987
+ const source = await transformFilename(resolvers, config, filename);
13022
12988
  const engine = new Engine(config, Parser);
13023
12989
  return Promise.resolve(engine.lint(source));
13024
12990
  }
@@ -13032,7 +12998,7 @@ class HtmlValidate {
13032
12998
  validateFileSync(filename) {
13033
12999
  const config = this.getConfigForSync(filename);
13034
13000
  const resolvers = this.configLoader.getResolvers();
13035
- const source = config.transformFilenameSync(resolvers, filename);
13001
+ const source = transformFilenameSync(resolvers, config, filename);
13036
13002
  const engine = new Engine(config, Parser);
13037
13003
  return engine.lint(source);
13038
13004
  }
@@ -13044,8 +13010,7 @@ class HtmlValidate {
13044
13010
  * @returns Report output.
13045
13011
  */
13046
13012
  async validateMultipleFiles(filenames) {
13047
- const report = Reporter.merge(filenames.map((filename) => this.validateFileSync(filename)));
13048
- return Promise.resolve(report);
13013
+ return Reporter.merge(filenames.map((filename) => this.validateFile(filename)));
13049
13014
  }
13050
13015
  /**
13051
13016
  * Parse and validate HTML from multiple files. Result is merged together to a
@@ -13101,7 +13066,7 @@ class HtmlValidate {
13101
13066
  async dumpTokens(filename) {
13102
13067
  const config = await this.getConfigFor(filename);
13103
13068
  const resolvers = this.configLoader.getResolvers();
13104
- const source = await config.transformFilename(resolvers, filename);
13069
+ const source = await transformFilename(resolvers, config, filename);
13105
13070
  const engine = new Engine(config, Parser);
13106
13071
  return engine.dumpTokens(source);
13107
13072
  }
@@ -13117,7 +13082,7 @@ class HtmlValidate {
13117
13082
  async dumpEvents(filename) {
13118
13083
  const config = await this.getConfigFor(filename);
13119
13084
  const resolvers = this.configLoader.getResolvers();
13120
- const source = await config.transformFilename(resolvers, filename);
13085
+ const source = await transformFilename(resolvers, config, filename);
13121
13086
  const engine = new Engine(config, Parser);
13122
13087
  return engine.dumpEvents(source);
13123
13088
  }
@@ -13133,7 +13098,7 @@ class HtmlValidate {
13133
13098
  async dumpTree(filename) {
13134
13099
  const config = await this.getConfigFor(filename);
13135
13100
  const resolvers = this.configLoader.getResolvers();
13136
- const source = await config.transformFilename(resolvers, filename);
13101
+ const source = await transformFilename(resolvers, config, filename);
13137
13102
  const engine = new Engine(config, Parser);
13138
13103
  return engine.dumpTree(source);
13139
13104
  }
@@ -13149,7 +13114,7 @@ class HtmlValidate {
13149
13114
  async dumpSource(filename) {
13150
13115
  const config = await this.getConfigFor(filename);
13151
13116
  const resolvers = this.configLoader.getResolvers();
13152
- const sources = await config.transformFilename(resolvers, filename);
13117
+ const sources = await transformFilename(resolvers, config, filename);
13153
13118
  return sources.reduce((result, source) => {
13154
13119
  const line = String(source.line);
13155
13120
  const column = String(source.column);
@@ -13348,7 +13313,7 @@ class HtmlValidate {
13348
13313
  }
13349
13314
 
13350
13315
  const name = "html-validate";
13351
- const version = "9.0.0";
13316
+ const version = "9.1.0";
13352
13317
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
13353
13318
 
13354
13319
  function definePlugin(plugin) {
@@ -13664,653 +13629,776 @@ function compatibilityCheckImpl(name, declared, options) {
13664
13629
  return false;
13665
13630
  }
13666
13631
 
13667
- var ignore$1;
13668
- var hasRequiredIgnore;
13632
+ // A simple implementation of make-array
13633
+ function makeArray (subject) {
13634
+ return Array.isArray(subject)
13635
+ ? subject
13636
+ : [subject]
13637
+ }
13638
+
13639
+ const UNDEFINED = undefined;
13640
+ const EMPTY = '';
13641
+ const SPACE = ' ';
13642
+ const ESCAPE = '\\';
13643
+ const REGEX_TEST_BLANK_LINE = /^\s+$/;
13644
+ const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13645
+ const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13646
+ const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13647
+ const REGEX_SPLITALL_CRLF = /\r?\n/g;
13648
+
13649
+ // Invalid:
13650
+ // - /foo,
13651
+ // - ./foo,
13652
+ // - ../foo,
13653
+ // - .
13654
+ // - ..
13655
+ // Valid:
13656
+ // - .foo
13657
+ const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
13658
+
13659
+ const REGEX_TEST_TRAILING_SLASH = /\/$/;
13660
+
13661
+ const SLASH = '/';
13662
+
13663
+ // Do not use ternary expression here, since "istanbul ignore next" is buggy
13664
+ let TMP_KEY_IGNORE = 'node-ignore';
13665
+ /* istanbul ignore else */
13666
+ if (typeof Symbol !== 'undefined') {
13667
+ TMP_KEY_IGNORE = Symbol.for('node-ignore');
13668
+ }
13669
+ const KEY_IGNORE = TMP_KEY_IGNORE;
13670
+
13671
+ const define = (object, key, value) => {
13672
+ Object.defineProperty(object, key, {value});
13673
+ return value
13674
+ };
13669
13675
 
13670
- function requireIgnore () {
13671
- if (hasRequiredIgnore) return ignore$1;
13672
- hasRequiredIgnore = 1;
13673
- // A simple implementation of make-array
13674
- function makeArray (subject) {
13675
- return Array.isArray(subject)
13676
- ? subject
13677
- : [subject]
13678
- }
13676
+ const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13677
+
13678
+ const RETURN_FALSE = () => false;
13679
+
13680
+ // Sanitize the range of a regular expression
13681
+ // The cases are complicated, see test cases for details
13682
+ const sanitizeRange = range => range.replace(
13683
+ REGEX_REGEXP_RANGE,
13684
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13685
+ ? match
13686
+ // Invalid range (out of order) which is ok for gitignore rules but
13687
+ // fatal for JavaScript regular expression, so eliminate it.
13688
+ : EMPTY
13689
+ );
13690
+
13691
+ // See fixtures #59
13692
+ const cleanRangeBackSlash = slashes => {
13693
+ const {length} = slashes;
13694
+ return slashes.slice(0, length - length % 2)
13695
+ };
13679
13696
 
13680
- const EMPTY = '';
13681
- const SPACE = ' ';
13682
- const ESCAPE = '\\';
13683
- const REGEX_TEST_BLANK_LINE = /^\s+$/;
13684
- const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
13685
- const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
13686
- const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
13687
- const REGEX_SPLITALL_CRLF = /\r?\n/g;
13688
- // /foo,
13689
- // ./foo,
13690
- // ../foo,
13691
- // .
13692
- // ..
13693
- const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
13694
-
13695
- const SLASH = '/';
13696
-
13697
- // Do not use ternary expression here, since "istanbul ignore next" is buggy
13698
- let TMP_KEY_IGNORE = 'node-ignore';
13699
- /* istanbul ignore else */
13700
- if (typeof Symbol !== 'undefined') {
13701
- TMP_KEY_IGNORE = Symbol.for('node-ignore');
13702
- }
13703
- const KEY_IGNORE = TMP_KEY_IGNORE;
13704
-
13705
- const define = (object, key, value) =>
13706
- Object.defineProperty(object, key, {value});
13707
-
13708
- const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
13709
-
13710
- const RETURN_FALSE = () => false;
13711
-
13712
- // Sanitize the range of a regular expression
13713
- // The cases are complicated, see test cases for details
13714
- const sanitizeRange = range => range.replace(
13715
- REGEX_REGEXP_RANGE,
13716
- (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
13717
- ? match
13718
- // Invalid range (out of order) which is ok for gitignore rules but
13719
- // fatal for JavaScript regular expression, so eliminate it.
13720
- : EMPTY
13721
- );
13722
-
13723
- // See fixtures #59
13724
- const cleanRangeBackSlash = slashes => {
13725
- const {length} = slashes;
13726
- return slashes.slice(0, length - length % 2)
13727
- };
13697
+ // > If the pattern ends with a slash,
13698
+ // > it is removed for the purpose of the following description,
13699
+ // > but it would only find a match with a directory.
13700
+ // > In other words, foo/ will match a directory foo and paths underneath it,
13701
+ // > but will not match a regular file or a symbolic link foo
13702
+ // > (this is consistent with the way how pathspec works in general in Git).
13703
+ // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13704
+ // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13705
+ // you could use option `mark: true` with `glob`
13728
13706
 
13729
- // > If the pattern ends with a slash,
13730
- // > it is removed for the purpose of the following description,
13731
- // > but it would only find a match with a directory.
13732
- // > In other words, foo/ will match a directory foo and paths underneath it,
13733
- // > but will not match a regular file or a symbolic link foo
13734
- // > (this is consistent with the way how pathspec works in general in Git).
13735
- // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
13736
- // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
13737
- // you could use option `mark: true` with `glob`
13738
-
13739
- // '`foo/`' should not continue with the '`..`'
13740
- const REPLACERS = [
13741
-
13742
- [
13743
- // remove BOM
13744
- // TODO:
13745
- // Other similar zero-width characters?
13746
- /^\uFEFF/,
13747
- () => EMPTY
13748
- ],
13749
-
13750
- // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13751
- [
13752
- // (a\ ) -> (a )
13753
- // (a ) -> (a)
13754
- // (a ) -> (a)
13755
- // (a \ ) -> (a )
13756
- /((?:\\\\)*?)(\\?\s+)$/,
13757
- (_, m1, m2) => m1 + (
13758
- m2.indexOf('\\') === 0
13759
- ? SPACE
13760
- : EMPTY
13761
- )
13762
- ],
13763
-
13764
- // replace (\ ) with ' '
13765
- // (\ ) -> ' '
13766
- // (\\ ) -> '\\ '
13767
- // (\\\ ) -> '\\ '
13768
- [
13769
- /(\\+?)\s/g,
13770
- (_, m1) => {
13771
- const {length} = m1;
13772
- return m1.slice(0, length - length % 2) + SPACE
13773
- }
13774
- ],
13775
-
13776
- // Escape metacharacters
13777
- // which is written down by users but means special for regular expressions.
13778
-
13779
- // > There are 12 characters with special meanings:
13780
- // > - the backslash \,
13781
- // > - the caret ^,
13782
- // > - the dollar sign $,
13783
- // > - the period or dot .,
13784
- // > - the vertical bar or pipe symbol |,
13785
- // > - the question mark ?,
13786
- // > - the asterisk or star *,
13787
- // > - the plus sign +,
13788
- // > - the opening parenthesis (,
13789
- // > - the closing parenthesis ),
13790
- // > - and the opening square bracket [,
13791
- // > - the opening curly brace {,
13792
- // > These special characters are often called "metacharacters".
13793
- [
13794
- /[\\$.|*+(){^]/g,
13795
- match => `\\${match}`
13796
- ],
13797
-
13798
- [
13799
- // > a question mark (?) matches a single character
13800
- /(?!\\)\?/g,
13801
- () => '[^/]'
13802
- ],
13803
-
13804
- // leading slash
13805
- [
13806
-
13807
- // > A leading slash matches the beginning of the pathname.
13808
- // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13809
- // A leading slash matches the beginning of the pathname
13810
- /^\//,
13811
- () => '^'
13812
- ],
13813
-
13814
- // replace special metacharacter slash after the leading slash
13815
- [
13816
- /\//g,
13817
- () => '\\/'
13818
- ],
13819
-
13820
- [
13821
- // > A leading "**" followed by a slash means match in all directories.
13822
- // > For example, "**/foo" matches file or directory "foo" anywhere,
13823
- // > the same as pattern "foo".
13824
- // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13825
- // > under directory "foo".
13826
- // Notice that the '*'s have been replaced as '\\*'
13827
- /^\^*\\\*\\\*\\\//,
13828
-
13829
- // '**/foo' <-> 'foo'
13830
- () => '^(?:.*\\/)?'
13831
- ],
13832
-
13833
- // starting
13834
- [
13835
- // there will be no leading '/'
13836
- // (which has been replaced by section "leading slash")
13837
- // If starts with '**', adding a '^' to the regular expression also works
13838
- /^(?=[^^])/,
13839
- function startingReplacer () {
13840
- // If has a slash `/` at the beginning or middle
13841
- return !/\/(?!$)/.test(this)
13842
- // > Prior to 2.22.1
13843
- // > If the pattern does not contain a slash /,
13844
- // > Git treats it as a shell glob pattern
13845
- // Actually, if there is only a trailing slash,
13846
- // git also treats it as a shell glob pattern
13847
-
13848
- // After 2.22.1 (compatible but clearer)
13849
- // > If there is a separator at the beginning or middle (or both)
13850
- // > of the pattern, then the pattern is relative to the directory
13851
- // > level of the particular .gitignore file itself.
13852
- // > Otherwise the pattern may also match at any level below
13853
- // > the .gitignore level.
13854
- ? '(?:^|\\/)'
13855
-
13856
- // > Otherwise, Git treats the pattern as a shell glob suitable for
13857
- // > consumption by fnmatch(3)
13858
- : '^'
13859
- }
13860
- ],
13861
-
13862
- // two globstars
13863
- [
13864
- // Use lookahead assertions so that we could match more than one `'/**'`
13865
- /\\\/\\\*\\\*(?=\\\/|$)/g,
13866
-
13867
- // Zero, one or several directories
13868
- // should not use '*', or it will be replaced by the next replacer
13869
-
13870
- // Check if it is not the last `'/**'`
13871
- (_, index, str) => index + 6 < str.length
13872
-
13873
- // case: /**/
13874
- // > A slash followed by two consecutive asterisks then a slash matches
13875
- // > zero or more directories.
13876
- // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13877
- // '/**/'
13878
- ? '(?:\\/[^\\/]+)*'
13879
-
13880
- // case: /**
13881
- // > A trailing `"/**"` matches everything inside.
13882
-
13883
- // #21: everything inside but it should not include the current folder
13884
- : '\\/.+'
13885
- ],
13886
-
13887
- // normal intermediate wildcards
13888
- [
13889
- // Never replace escaped '*'
13890
- // ignore rule '\*' will match the path '*'
13891
-
13892
- // 'abc.*/' -> go
13893
- // 'abc.*' -> skip this rule,
13894
- // coz trailing single wildcard will be handed by [trailing wildcard]
13895
- /(^|[^\\]+)(\\\*)+(?=.+)/g,
13896
-
13897
- // '*.js' matches '.js'
13898
- // '*.js' doesn't match 'abc'
13899
- (_, p1, p2) => {
13900
- // 1.
13901
- // > An asterisk "*" matches anything except a slash.
13902
- // 2.
13903
- // > Other consecutive asterisks are considered regular asterisks
13904
- // > and will match according to the previous rules.
13905
- const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13906
- return p1 + unescaped
13907
- }
13908
- ],
13909
-
13910
- [
13911
- // unescape, revert step 3 except for back slash
13912
- // For example, if a user escape a '\\*',
13913
- // after step 3, the result will be '\\\\\\*'
13914
- /\\\\\\(?=[$.|*+(){^])/g,
13915
- () => ESCAPE
13916
- ],
13917
-
13918
- [
13919
- // '\\\\' -> '\\'
13920
- /\\\\/g,
13921
- () => ESCAPE
13922
- ],
13923
-
13924
- [
13925
- // > The range notation, e.g. [a-zA-Z],
13926
- // > can be used to match one of the characters in a range.
13927
-
13928
- // `\` is escaped by step 3
13929
- /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13930
- (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13931
- // '\\[bar]' -> '\\\\[bar\\]'
13932
- ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13933
- : close === ']'
13934
- ? endEscape.length % 2 === 0
13935
- // A normal case, and it is a range notation
13936
- // '[bar]'
13937
- // '[bar\\\\]'
13938
- ? `[${sanitizeRange(range)}${endEscape}]`
13939
- // Invalid range notaton
13940
- // '[bar\\]' -> '[bar\\\\]'
13941
- : '[]'
13942
- : '[]'
13943
- ],
13944
-
13945
- // ending
13946
- [
13947
- // 'js' will not match 'js.'
13948
- // 'ab' will not match 'abc'
13949
- /(?:[^*])$/,
13950
-
13951
- // WTF!
13952
- // https://git-scm.com/docs/gitignore
13953
- // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13954
- // which re-fixes #24, #38
13955
-
13956
- // > If there is a separator at the end of the pattern then the pattern
13957
- // > will only match directories, otherwise the pattern can match both
13958
- // > files and directories.
13959
-
13960
- // 'js*' will not match 'a.js'
13961
- // 'js/' will not match 'a.js'
13962
- // 'js' will match 'a.js' and 'a.js/'
13963
- match => /\/$/.test(match)
13964
- // foo/ will not match 'foo'
13965
- ? `${match}$`
13966
- // foo matches 'foo' and 'foo/'
13967
- : `${match}(?=$|\\/$)`
13968
- ],
13969
-
13970
- // trailing wildcard
13971
- [
13972
- /(\^|\\\/)?\\\*$/,
13973
- (_, p1) => {
13974
- const prefix = p1
13975
- // '\^':
13976
- // '/*' does not match EMPTY
13977
- // '/*' does not match everything
13978
-
13979
- // '\\\/':
13980
- // 'abc/*' does not match 'abc/'
13981
- ? `${p1}[^/]+`
13982
-
13983
- // 'a*' matches 'a'
13984
- // 'a*' matches 'aa'
13985
- : '[^/]*';
13986
-
13987
- return `${prefix}(?=$|\\/$)`
13988
- }
13989
- ],
13990
- ];
13991
-
13992
- // A simple cache, because an ignore rule only has only one certain meaning
13993
- const regexCache = Object.create(null);
13994
-
13995
- // @param {pattern}
13996
- const makeRegex = (pattern, ignoreCase) => {
13997
- let source = regexCache[pattern];
13998
-
13999
- if (!source) {
14000
- source = REPLACERS.reduce(
14001
- (prev, [matcher, replacer]) =>
14002
- prev.replace(matcher, replacer.bind(pattern)),
14003
- pattern
14004
- );
14005
- regexCache[pattern] = source;
14006
- }
14007
-
14008
- return ignoreCase
14009
- ? new RegExp(source, 'i')
14010
- : new RegExp(source)
14011
- };
13707
+ // '`foo/`' should not continue with the '`..`'
13708
+ const REPLACERS = [
14012
13709
 
14013
- const isString = subject => typeof subject === 'string';
14014
-
14015
- // > A blank line matches no files, so it can serve as a separator for readability.
14016
- const checkPattern = pattern => pattern
14017
- && isString(pattern)
14018
- && !REGEX_TEST_BLANK_LINE.test(pattern)
14019
- && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
14020
-
14021
- // > A line starting with # serves as a comment.
14022
- && pattern.indexOf('#') !== 0;
14023
-
14024
- const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF);
14025
-
14026
- class IgnoreRule {
14027
- constructor (
14028
- origin,
14029
- pattern,
14030
- negative,
14031
- regex
14032
- ) {
14033
- this.origin = origin;
14034
- this.pattern = pattern;
14035
- this.negative = negative;
14036
- this.regex = regex;
14037
- }
14038
- }
13710
+ [
13711
+ // Remove BOM
13712
+ // TODO:
13713
+ // Other similar zero-width characters?
13714
+ /^\uFEFF/,
13715
+ () => EMPTY
13716
+ ],
14039
13717
 
14040
- const createRule = (pattern, ignoreCase) => {
14041
- const origin = pattern;
14042
- let negative = false;
14043
-
14044
- // > An optional prefix "!" which negates the pattern;
14045
- if (pattern.indexOf('!') === 0) {
14046
- negative = true;
14047
- pattern = pattern.substr(1);
14048
- }
14049
-
14050
- pattern = pattern
14051
- // > Put a backslash ("\") in front of the first "!" for patterns that
14052
- // > begin with a literal "!", for example, `"\!important!.txt"`.
14053
- .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
14054
- // > Put a backslash ("\") in front of the first hash for patterns that
14055
- // > begin with a hash.
14056
- .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
14057
-
14058
- const regex = makeRegex(pattern, ignoreCase);
14059
-
14060
- return new IgnoreRule(
14061
- origin,
14062
- pattern,
14063
- negative,
14064
- regex
14065
- )
14066
- };
13718
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
13719
+ [
13720
+ // (a\ ) -> (a )
13721
+ // (a ) -> (a)
13722
+ // (a ) -> (a)
13723
+ // (a \ ) -> (a )
13724
+ /((?:\\\\)*?)(\\?\s+)$/,
13725
+ (_, m1, m2) => m1 + (
13726
+ m2.indexOf('\\') === 0
13727
+ ? SPACE
13728
+ : EMPTY
13729
+ )
13730
+ ],
14067
13731
 
14068
- const throwError = (message, Ctor) => {
14069
- throw new Ctor(message)
14070
- };
13732
+ // Replace (\ ) with ' '
13733
+ // (\ ) -> ' '
13734
+ // (\\ ) -> '\\ '
13735
+ // (\\\ ) -> '\\ '
13736
+ [
13737
+ /(\\+?)\s/g,
13738
+ (_, m1) => {
13739
+ const {length} = m1;
13740
+ return m1.slice(0, length - length % 2) + SPACE
13741
+ }
13742
+ ],
14071
13743
 
14072
- const checkPath = (path, originalPath, doThrow) => {
14073
- if (!isString(path)) {
14074
- return doThrow(
14075
- `path must be a string, but got \`${originalPath}\``,
14076
- TypeError
14077
- )
14078
- }
14079
-
14080
- // We don't know if we should ignore EMPTY, so throw
14081
- if (!path) {
14082
- return doThrow(`path must not be empty`, TypeError)
14083
- }
14084
-
14085
- // Check if it is a relative path
14086
- if (checkPath.isNotRelative(path)) {
14087
- const r = '`path.relative()`d';
14088
- return doThrow(
14089
- `path should be a ${r} string, but got "${originalPath}"`,
14090
- RangeError
14091
- )
14092
- }
14093
-
14094
- return true
14095
- };
13744
+ // Escape metacharacters
13745
+ // which is written down by users but means special for regular expressions.
13746
+
13747
+ // > There are 12 characters with special meanings:
13748
+ // > - the backslash \,
13749
+ // > - the caret ^,
13750
+ // > - the dollar sign $,
13751
+ // > - the period or dot .,
13752
+ // > - the vertical bar or pipe symbol |,
13753
+ // > - the question mark ?,
13754
+ // > - the asterisk or star *,
13755
+ // > - the plus sign +,
13756
+ // > - the opening parenthesis (,
13757
+ // > - the closing parenthesis ),
13758
+ // > - and the opening square bracket [,
13759
+ // > - the opening curly brace {,
13760
+ // > These special characters are often called "metacharacters".
13761
+ [
13762
+ /[\\$.|*+(){^]/g,
13763
+ match => `\\${match}`
13764
+ ],
14096
13765
 
14097
- const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
14098
-
14099
- checkPath.isNotRelative = isNotRelative;
14100
- checkPath.convert = p => p;
14101
-
14102
- class Ignore {
14103
- constructor ({
14104
- ignorecase = true,
14105
- ignoreCase = ignorecase,
14106
- allowRelativePaths = false
14107
- } = {}) {
14108
- define(this, KEY_IGNORE, true);
14109
-
14110
- this._rules = [];
14111
- this._ignoreCase = ignoreCase;
14112
- this._allowRelativePaths = allowRelativePaths;
14113
- this._initCache();
14114
- }
14115
-
14116
- _initCache () {
14117
- this._ignoreCache = Object.create(null);
14118
- this._testCache = Object.create(null);
14119
- }
14120
-
14121
- _addPattern (pattern) {
14122
- // #32
14123
- if (pattern && pattern[KEY_IGNORE]) {
14124
- this._rules = this._rules.concat(pattern._rules);
14125
- this._added = true;
14126
- return
14127
- }
14128
-
14129
- if (checkPattern(pattern)) {
14130
- const rule = createRule(pattern, this._ignoreCase);
14131
- this._added = true;
14132
- this._rules.push(rule);
14133
- }
14134
- }
14135
-
14136
- // @param {Array<string> | string | Ignore} pattern
14137
- add (pattern) {
14138
- this._added = false;
14139
-
14140
- makeArray(
14141
- isString(pattern)
14142
- ? splitPattern(pattern)
14143
- : pattern
14144
- ).forEach(this._addPattern, this);
14145
-
14146
- // Some rules have just added to the ignore,
14147
- // making the behavior changed.
14148
- if (this._added) {
14149
- this._initCache();
14150
- }
14151
-
14152
- return this
14153
- }
14154
-
14155
- // legacy
14156
- addPattern (pattern) {
14157
- return this.add(pattern)
14158
- }
14159
-
14160
- // | ignored : unignored
14161
- // negative | 0:0 | 0:1 | 1:0 | 1:1
14162
- // -------- | ------- | ------- | ------- | --------
14163
- // 0 | TEST | TEST | SKIP | X
14164
- // 1 | TESTIF | SKIP | TEST | X
14165
-
14166
- // - SKIP: always skip
14167
- // - TEST: always test
14168
- // - TESTIF: only test if checkUnignored
14169
- // - X: that never happen
14170
-
14171
- // @param {boolean} whether should check if the path is unignored,
14172
- // setting `checkUnignored` to `false` could reduce additional
14173
- // path matching.
14174
-
14175
- // @returns {TestResult} true if a file is ignored
14176
- _testOne (path, checkUnignored) {
14177
- let ignored = false;
14178
- let unignored = false;
14179
-
14180
- this._rules.forEach(rule => {
14181
- const {negative} = rule;
14182
- if (
14183
- unignored === negative && ignored !== unignored
14184
- || negative && !ignored && !unignored && !checkUnignored
14185
- ) {
14186
- return
14187
- }
14188
-
14189
- const matched = rule.regex.test(path);
14190
-
14191
- if (matched) {
14192
- ignored = !negative;
14193
- unignored = negative;
14194
- }
14195
- });
14196
-
14197
- return {
14198
- ignored,
14199
- unignored
14200
- }
14201
- }
14202
-
14203
- // @returns {TestResult}
14204
- _test (originalPath, cache, checkUnignored, slices) {
14205
- const path = originalPath
14206
- // Supports nullable path
14207
- && checkPath.convert(originalPath);
14208
-
14209
- checkPath(
14210
- path,
14211
- originalPath,
14212
- this._allowRelativePaths
14213
- ? RETURN_FALSE
14214
- : throwError
14215
- );
14216
-
14217
- return this._t(path, cache, checkUnignored, slices)
14218
- }
14219
-
14220
- _t (path, cache, checkUnignored, slices) {
14221
- if (path in cache) {
14222
- return cache[path]
14223
- }
14224
-
14225
- if (!slices) {
14226
- // path/to/a.js
14227
- // ['path', 'to', 'a.js']
14228
- slices = path.split(SLASH);
14229
- }
14230
-
14231
- slices.pop();
14232
-
14233
- // If the path has no parent directory, just test it
14234
- if (!slices.length) {
14235
- return cache[path] = this._testOne(path, checkUnignored)
14236
- }
14237
-
14238
- const parent = this._t(
14239
- slices.join(SLASH) + SLASH,
14240
- cache,
14241
- checkUnignored,
14242
- slices
14243
- );
14244
-
14245
- // If the path contains a parent directory, check the parent first
14246
- return cache[path] = parent.ignored
14247
- // > It is not possible to re-include a file if a parent directory of
14248
- // > that file is excluded.
14249
- ? parent
14250
- : this._testOne(path, checkUnignored)
14251
- }
14252
-
14253
- ignores (path) {
14254
- return this._test(path, this._ignoreCache, false).ignored
14255
- }
14256
-
14257
- createFilter () {
14258
- return path => !this.ignores(path)
14259
- }
14260
-
14261
- filter (paths) {
14262
- return makeArray(paths).filter(this.createFilter())
14263
- }
14264
-
14265
- // @returns {TestResult}
14266
- test (path) {
14267
- return this._test(path, this._testCache, true)
14268
- }
14269
- }
13766
+ [
13767
+ // > a question mark (?) matches a single character
13768
+ /(?!\\)\?/g,
13769
+ () => '[^/]'
13770
+ ],
14270
13771
 
14271
- const factory = options => new Ignore(options);
14272
-
14273
- const isPathValid = path =>
14274
- checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14275
-
14276
- factory.isPathValid = isPathValid;
14277
-
14278
- // Fixes typescript
14279
- factory.default = factory;
14280
-
14281
- ignore$1 = factory;
14282
-
14283
- // Windows
14284
- // --------------------------------------------------------------
14285
- /* istanbul ignore if */
14286
- if (
14287
- // Detect `process` so that it can run in browsers.
14288
- typeof process !== 'undefined'
14289
- && (
14290
- process.env && process.env.IGNORE_TEST_WIN32
14291
- || process.platform === 'win32'
14292
- )
14293
- ) {
14294
- /* eslint no-control-regex: "off" */
14295
- const makePosix = str => /^\\\\\?\\/.test(str)
14296
- || /["<>|\u0000-\u001F]+/u.test(str)
14297
- ? str
14298
- : str.replace(/\\/g, '/');
14299
-
14300
- checkPath.convert = makePosix;
14301
-
14302
- // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14303
- // 'd:\\foo'
14304
- const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14305
- checkPath.isNotRelative = path =>
14306
- REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path)
14307
- || isNotRelative(path);
14308
- }
14309
- return ignore$1;
13772
+ // leading slash
13773
+ [
13774
+
13775
+ // > A leading slash matches the beginning of the pathname.
13776
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
13777
+ // A leading slash matches the beginning of the pathname
13778
+ /^\//,
13779
+ () => '^'
13780
+ ],
13781
+
13782
+ // replace special metacharacter slash after the leading slash
13783
+ [
13784
+ /\//g,
13785
+ () => '\\/'
13786
+ ],
13787
+
13788
+ [
13789
+ // > A leading "**" followed by a slash means match in all directories.
13790
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
13791
+ // > the same as pattern "foo".
13792
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
13793
+ // > under directory "foo".
13794
+ // Notice that the '*'s have been replaced as '\\*'
13795
+ /^\^*\\\*\\\*\\\//,
13796
+
13797
+ // '**/foo' <-> 'foo'
13798
+ () => '^(?:.*\\/)?'
13799
+ ],
13800
+
13801
+ // starting
13802
+ [
13803
+ // there will be no leading '/'
13804
+ // (which has been replaced by section "leading slash")
13805
+ // If starts with '**', adding a '^' to the regular expression also works
13806
+ /^(?=[^^])/,
13807
+ function startingReplacer () {
13808
+ // If has a slash `/` at the beginning or middle
13809
+ return !/\/(?!$)/.test(this)
13810
+ // > Prior to 2.22.1
13811
+ // > If the pattern does not contain a slash /,
13812
+ // > Git treats it as a shell glob pattern
13813
+ // Actually, if there is only a trailing slash,
13814
+ // git also treats it as a shell glob pattern
13815
+
13816
+ // After 2.22.1 (compatible but clearer)
13817
+ // > If there is a separator at the beginning or middle (or both)
13818
+ // > of the pattern, then the pattern is relative to the directory
13819
+ // > level of the particular .gitignore file itself.
13820
+ // > Otherwise the pattern may also match at any level below
13821
+ // > the .gitignore level.
13822
+ ? '(?:^|\\/)'
13823
+
13824
+ // > Otherwise, Git treats the pattern as a shell glob suitable for
13825
+ // > consumption by fnmatch(3)
13826
+ : '^'
13827
+ }
13828
+ ],
13829
+
13830
+ // two globstars
13831
+ [
13832
+ // Use lookahead assertions so that we could match more than one `'/**'`
13833
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
13834
+
13835
+ // Zero, one or several directories
13836
+ // should not use '*', or it will be replaced by the next replacer
13837
+
13838
+ // Check if it is not the last `'/**'`
13839
+ (_, index, str) => index + 6 < str.length
13840
+
13841
+ // case: /**/
13842
+ // > A slash followed by two consecutive asterisks then a slash matches
13843
+ // > zero or more directories.
13844
+ // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
13845
+ // '/**/'
13846
+ ? '(?:\\/[^\\/]+)*'
13847
+
13848
+ // case: /**
13849
+ // > A trailing `"/**"` matches everything inside.
13850
+
13851
+ // #21: everything inside but it should not include the current folder
13852
+ : '\\/.+'
13853
+ ],
13854
+
13855
+ // normal intermediate wildcards
13856
+ [
13857
+ // Never replace escaped '*'
13858
+ // ignore rule '\*' will match the path '*'
13859
+
13860
+ // 'abc.*/' -> go
13861
+ // 'abc.*' -> skip this rule,
13862
+ // coz trailing single wildcard will be handed by [trailing wildcard]
13863
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
13864
+
13865
+ // '*.js' matches '.js'
13866
+ // '*.js' doesn't match 'abc'
13867
+ (_, p1, p2) => {
13868
+ // 1.
13869
+ // > An asterisk "*" matches anything except a slash.
13870
+ // 2.
13871
+ // > Other consecutive asterisks are considered regular asterisks
13872
+ // > and will match according to the previous rules.
13873
+ const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
13874
+ return p1 + unescaped
13875
+ }
13876
+ ],
13877
+
13878
+ [
13879
+ // unescape, revert step 3 except for back slash
13880
+ // For example, if a user escape a '\\*',
13881
+ // after step 3, the result will be '\\\\\\*'
13882
+ /\\\\\\(?=[$.|*+(){^])/g,
13883
+ () => ESCAPE
13884
+ ],
13885
+
13886
+ [
13887
+ // '\\\\' -> '\\'
13888
+ /\\\\/g,
13889
+ () => ESCAPE
13890
+ ],
13891
+
13892
+ [
13893
+ // > The range notation, e.g. [a-zA-Z],
13894
+ // > can be used to match one of the characters in a range.
13895
+
13896
+ // `\` is escaped by step 3
13897
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
13898
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
13899
+ // '\\[bar]' -> '\\\\[bar\\]'
13900
+ ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
13901
+ : close === ']'
13902
+ ? endEscape.length % 2 === 0
13903
+ // A normal case, and it is a range notation
13904
+ // '[bar]'
13905
+ // '[bar\\\\]'
13906
+ ? `[${sanitizeRange(range)}${endEscape}]`
13907
+ // Invalid range notaton
13908
+ // '[bar\\]' -> '[bar\\\\]'
13909
+ : '[]'
13910
+ : '[]'
13911
+ ],
13912
+
13913
+ // ending
13914
+ [
13915
+ // 'js' will not match 'js.'
13916
+ // 'ab' will not match 'abc'
13917
+ /(?:[^*])$/,
13918
+
13919
+ // WTF!
13920
+ // https://git-scm.com/docs/gitignore
13921
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
13922
+ // which re-fixes #24, #38
13923
+
13924
+ // > If there is a separator at the end of the pattern then the pattern
13925
+ // > will only match directories, otherwise the pattern can match both
13926
+ // > files and directories.
13927
+
13928
+ // 'js*' will not match 'a.js'
13929
+ // 'js/' will not match 'a.js'
13930
+ // 'js' will match 'a.js' and 'a.js/'
13931
+ match => /\/$/.test(match)
13932
+ // foo/ will not match 'foo'
13933
+ ? `${match}$`
13934
+ // foo matches 'foo' and 'foo/'
13935
+ : `${match}(?=$|\\/$)`
13936
+ ]
13937
+ ];
13938
+
13939
+ const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
13940
+ const MODE_IGNORE = 'regex';
13941
+ const MODE_CHECK_IGNORE = 'checkRegex';
13942
+ const UNDERSCORE = '_';
13943
+
13944
+ const TRAILING_WILD_CARD_REPLACERS = {
13945
+ [MODE_IGNORE] (_, p1) {
13946
+ const prefix = p1
13947
+ // '\^':
13948
+ // '/*' does not match EMPTY
13949
+ // '/*' does not match everything
13950
+
13951
+ // '\\\/':
13952
+ // 'abc/*' does not match 'abc/'
13953
+ ? `${p1}[^/]+`
13954
+
13955
+ // 'a*' matches 'a'
13956
+ // 'a*' matches 'aa'
13957
+ : '[^/]*';
13958
+
13959
+ return `${prefix}(?=$|\\/$)`
13960
+ },
13961
+
13962
+ [MODE_CHECK_IGNORE] (_, p1) {
13963
+ // When doing `git check-ignore`
13964
+ const prefix = p1
13965
+ // '\\\/':
13966
+ // 'abc/*' DOES match 'abc/' !
13967
+ ? `${p1}[^/]*`
13968
+
13969
+ // 'a*' matches 'a'
13970
+ // 'a*' matches 'aa'
13971
+ : '[^/]*';
13972
+
13973
+ return `${prefix}(?=$|\\/$)`
13974
+ }
13975
+ };
13976
+
13977
+ // @param {pattern}
13978
+ const makeRegexPrefix = pattern => REPLACERS.reduce(
13979
+ (prev, [matcher, replacer]) =>
13980
+ prev.replace(matcher, replacer.bind(pattern)),
13981
+ pattern
13982
+ );
13983
+
13984
+ const isString = subject => typeof subject === 'string';
13985
+
13986
+ // > A blank line matches no files, so it can serve as a separator for readability.
13987
+ const checkPattern = pattern => pattern
13988
+ && isString(pattern)
13989
+ && !REGEX_TEST_BLANK_LINE.test(pattern)
13990
+ && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
13991
+
13992
+ // > A line starting with # serves as a comment.
13993
+ && pattern.indexOf('#') !== 0;
13994
+
13995
+ const splitPattern = pattern => pattern
13996
+ .split(REGEX_SPLITALL_CRLF)
13997
+ .filter(Boolean);
13998
+
13999
+ class IgnoreRule {
14000
+ constructor (
14001
+ pattern,
14002
+ mark,
14003
+ body,
14004
+ ignoreCase,
14005
+ negative,
14006
+ prefix
14007
+ ) {
14008
+ this.pattern = pattern;
14009
+ this.mark = mark;
14010
+ this.negative = negative;
14011
+
14012
+ define(this, 'body', body);
14013
+ define(this, 'ignoreCase', ignoreCase);
14014
+ define(this, 'regexPrefix', prefix);
14015
+ }
14016
+
14017
+ get regex () {
14018
+ const key = UNDERSCORE + MODE_IGNORE;
14019
+
14020
+ if (this[key]) {
14021
+ return this[key]
14022
+ }
14023
+
14024
+ return this._make(MODE_IGNORE, key)
14025
+ }
14026
+
14027
+ get checkRegex () {
14028
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
14029
+
14030
+ if (this[key]) {
14031
+ return this[key]
14032
+ }
14033
+
14034
+ return this._make(MODE_CHECK_IGNORE, key)
14035
+ }
14036
+
14037
+ _make (mode, key) {
14038
+ const str = this.regexPrefix.replace(
14039
+ REGEX_REPLACE_TRAILING_WILDCARD,
14040
+
14041
+ // It does not need to bind pattern
14042
+ TRAILING_WILD_CARD_REPLACERS[mode]
14043
+ );
14044
+
14045
+ const regex = this.ignoreCase
14046
+ ? new RegExp(str, 'i')
14047
+ : new RegExp(str);
14048
+
14049
+ return define(this, key, regex)
14050
+ }
14051
+ }
14052
+
14053
+ const createRule = ({
14054
+ pattern,
14055
+ mark
14056
+ }, ignoreCase) => {
14057
+ let negative = false;
14058
+ let body = pattern;
14059
+
14060
+ // > An optional prefix "!" which negates the pattern;
14061
+ if (body.indexOf('!') === 0) {
14062
+ negative = true;
14063
+ body = body.substr(1);
14064
+ }
14065
+
14066
+ body = body
14067
+ // > Put a backslash ("\") in front of the first "!" for patterns that
14068
+ // > begin with a literal "!", for example, `"\!important!.txt"`.
14069
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
14070
+ // > Put a backslash ("\") in front of the first hash for patterns that
14071
+ // > begin with a hash.
14072
+ .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
14073
+
14074
+ const regexPrefix = makeRegexPrefix(body);
14075
+
14076
+ return new IgnoreRule(
14077
+ pattern,
14078
+ mark,
14079
+ body,
14080
+ ignoreCase,
14081
+ negative,
14082
+ regexPrefix
14083
+ )
14084
+ };
14085
+
14086
+ class RuleManager {
14087
+ constructor (ignoreCase) {
14088
+ this._ignoreCase = ignoreCase;
14089
+ this._rules = [];
14090
+ }
14091
+
14092
+ _add (pattern) {
14093
+ // #32
14094
+ if (pattern && pattern[KEY_IGNORE]) {
14095
+ this._rules = this._rules.concat(pattern._rules._rules);
14096
+ this._added = true;
14097
+ return
14098
+ }
14099
+
14100
+ if (isString(pattern)) {
14101
+ pattern = {
14102
+ pattern
14103
+ };
14104
+ }
14105
+
14106
+ if (checkPattern(pattern.pattern)) {
14107
+ const rule = createRule(pattern, this._ignoreCase);
14108
+ this._added = true;
14109
+ this._rules.push(rule);
14110
+ }
14111
+ }
14112
+
14113
+ // @param {Array<string> | string | Ignore} pattern
14114
+ add (pattern) {
14115
+ this._added = false;
14116
+
14117
+ makeArray(
14118
+ isString(pattern)
14119
+ ? splitPattern(pattern)
14120
+ : pattern
14121
+ ).forEach(this._add, this);
14122
+
14123
+ return this._added
14124
+ }
14125
+
14126
+ // Test one single path without recursively checking parent directories
14127
+ //
14128
+ // - checkUnignored `boolean` whether should check if the path is unignored,
14129
+ // setting `checkUnignored` to `false` could reduce additional
14130
+ // path matching.
14131
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
14132
+
14133
+ // @returns {TestResult} true if a file is ignored
14134
+ test (path, checkUnignored, mode) {
14135
+ let ignored = false;
14136
+ let unignored = false;
14137
+ let matchedRule;
14138
+
14139
+ this._rules.forEach(rule => {
14140
+ const {negative} = rule;
14141
+
14142
+ // | ignored : unignored
14143
+ // -------- | ---------------------------------------
14144
+ // negative | 0:0 | 0:1 | 1:0 | 1:1
14145
+ // -------- | ------- | ------- | ------- | --------
14146
+ // 0 | TEST | TEST | SKIP | X
14147
+ // 1 | TESTIF | SKIP | TEST | X
14148
+
14149
+ // - SKIP: always skip
14150
+ // - TEST: always test
14151
+ // - TESTIF: only test if checkUnignored
14152
+ // - X: that never happen
14153
+ if (
14154
+ unignored === negative && ignored !== unignored
14155
+ || negative && !ignored && !unignored && !checkUnignored
14156
+ ) {
14157
+ return
14158
+ }
14159
+
14160
+ const matched = rule[mode].test(path);
14161
+
14162
+ if (!matched) {
14163
+ return
14164
+ }
14165
+
14166
+ ignored = !negative;
14167
+ unignored = negative;
14168
+
14169
+ matchedRule = negative
14170
+ ? UNDEFINED
14171
+ : rule;
14172
+ });
14173
+
14174
+ const ret = {
14175
+ ignored,
14176
+ unignored
14177
+ };
14178
+
14179
+ if (matchedRule) {
14180
+ ret.rule = matchedRule;
14181
+ }
14182
+
14183
+ return ret
14184
+ }
14185
+ }
14186
+
14187
+ const throwError = (message, Ctor) => {
14188
+ throw new Ctor(message)
14189
+ };
14190
+
14191
+ const checkPath = (path, originalPath, doThrow) => {
14192
+ if (!isString(path)) {
14193
+ return doThrow(
14194
+ `path must be a string, but got \`${originalPath}\``,
14195
+ TypeError
14196
+ )
14197
+ }
14198
+
14199
+ // We don't know if we should ignore EMPTY, so throw
14200
+ if (!path) {
14201
+ return doThrow(`path must not be empty`, TypeError)
14202
+ }
14203
+
14204
+ // Check if it is a relative path
14205
+ if (checkPath.isNotRelative(path)) {
14206
+ const r = '`path.relative()`d';
14207
+ return doThrow(
14208
+ `path should be a ${r} string, but got "${originalPath}"`,
14209
+ RangeError
14210
+ )
14211
+ }
14212
+
14213
+ return true
14214
+ };
14215
+
14216
+ const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path);
14217
+
14218
+ checkPath.isNotRelative = isNotRelative;
14219
+
14220
+ // On windows, the following function will be replaced
14221
+ /* istanbul ignore next */
14222
+ checkPath.convert = p => p;
14223
+
14224
+
14225
+ class Ignore {
14226
+ constructor ({
14227
+ ignorecase = true,
14228
+ ignoreCase = ignorecase,
14229
+ allowRelativePaths = false
14230
+ } = {}) {
14231
+ define(this, KEY_IGNORE, true);
14232
+
14233
+ this._rules = new RuleManager(ignoreCase);
14234
+ this._strictPathCheck = !allowRelativePaths;
14235
+ this._initCache();
14236
+ }
14237
+
14238
+ _initCache () {
14239
+ // A cache for the result of `.ignores()`
14240
+ this._ignoreCache = Object.create(null);
14241
+
14242
+ // A cache for the result of `.test()`
14243
+ this._testCache = Object.create(null);
14244
+ }
14245
+
14246
+ add (pattern) {
14247
+ if (this._rules.add(pattern)) {
14248
+ // Some rules have just added to the ignore,
14249
+ // making the behavior changed,
14250
+ // so we need to re-initialize the result cache
14251
+ this._initCache();
14252
+ }
14253
+
14254
+ return this
14255
+ }
14256
+
14257
+ // legacy
14258
+ addPattern (pattern) {
14259
+ return this.add(pattern)
14260
+ }
14261
+
14262
+ // @returns {TestResult}
14263
+ _test (originalPath, cache, checkUnignored, slices) {
14264
+ const path = originalPath
14265
+ // Supports nullable path
14266
+ && checkPath.convert(originalPath);
14267
+
14268
+ checkPath(
14269
+ path,
14270
+ originalPath,
14271
+ this._strictPathCheck
14272
+ ? throwError
14273
+ : RETURN_FALSE
14274
+ );
14275
+
14276
+ return this._t(path, cache, checkUnignored, slices)
14277
+ }
14278
+
14279
+ checkIgnore (path) {
14280
+ // If the path doest not end with a slash, `.ignores()` is much equivalent
14281
+ // to `git check-ignore`
14282
+ if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
14283
+ return this.test(path)
14284
+ }
14285
+
14286
+ const slices = path.split(SLASH).filter(Boolean);
14287
+ slices.pop();
14288
+
14289
+ if (slices.length) {
14290
+ const parent = this._t(
14291
+ slices.join(SLASH) + SLASH,
14292
+ this._testCache,
14293
+ true,
14294
+ slices
14295
+ );
14296
+
14297
+ if (parent.ignored) {
14298
+ return parent
14299
+ }
14300
+ }
14301
+
14302
+ return this._rules.test(path, false, MODE_CHECK_IGNORE)
14303
+ }
14304
+
14305
+ _t (
14306
+ // The path to be tested
14307
+ path,
14308
+
14309
+ // The cache for the result of a certain checking
14310
+ cache,
14311
+
14312
+ // Whether should check if the path is unignored
14313
+ checkUnignored,
14314
+
14315
+ // The path slices
14316
+ slices
14317
+ ) {
14318
+ if (path in cache) {
14319
+ return cache[path]
14320
+ }
14321
+
14322
+ if (!slices) {
14323
+ // path/to/a.js
14324
+ // ['path', 'to', 'a.js']
14325
+ slices = path.split(SLASH).filter(Boolean);
14326
+ }
14327
+
14328
+ slices.pop();
14329
+
14330
+ // If the path has no parent directory, just test it
14331
+ if (!slices.length) {
14332
+ return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
14333
+ }
14334
+
14335
+ const parent = this._t(
14336
+ slices.join(SLASH) + SLASH,
14337
+ cache,
14338
+ checkUnignored,
14339
+ slices
14340
+ );
14341
+
14342
+ // If the path contains a parent directory, check the parent first
14343
+ return cache[path] = parent.ignored
14344
+ // > It is not possible to re-include a file if a parent directory of
14345
+ // > that file is excluded.
14346
+ ? parent
14347
+ : this._rules.test(path, checkUnignored, MODE_IGNORE)
14348
+ }
14349
+
14350
+ ignores (path) {
14351
+ return this._test(path, this._ignoreCache, false).ignored
14352
+ }
14353
+
14354
+ createFilter () {
14355
+ return path => !this.ignores(path)
14356
+ }
14357
+
14358
+ filter (paths) {
14359
+ return makeArray(paths).filter(this.createFilter())
14360
+ }
14361
+
14362
+ // @returns {TestResult}
14363
+ test (path) {
14364
+ return this._test(path, this._testCache, true)
14365
+ }
14310
14366
  }
14311
14367
 
14312
- var ignoreExports = /*@__PURE__*/ requireIgnore();
14313
- var ignore = /*@__PURE__*/getDefaultExportFromCjs(ignoreExports);
14368
+ const factory = options => new Ignore(options);
14369
+
14370
+ const isPathValid = path =>
14371
+ checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
14372
+
14373
+ factory.isPathValid = isPathValid;
14374
+
14375
+
14376
+ // Windows
14377
+ // --------------------------------------------------------------
14378
+ /* istanbul ignore next */
14379
+ if (
14380
+ // Detect `process` so that it can run in browsers.
14381
+ typeof process !== 'undefined'
14382
+ && (
14383
+ process.env && process.env.IGNORE_TEST_WIN32
14384
+ || process.platform === 'win32'
14385
+ )
14386
+ ) {
14387
+ /* eslint no-control-regex: "off" */
14388
+ const makePosix = str => /^\\\\\?\\/.test(str)
14389
+ || /["<>|\u0000-\u001F]+/u.test(str)
14390
+ ? str
14391
+ : str.replace(/\\/g, '/');
14392
+
14393
+ checkPath.convert = makePosix;
14394
+
14395
+ // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
14396
+ // 'd:\\foo'
14397
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
14398
+ checkPath.isNotRelative = path =>
14399
+ REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
14400
+ || isNotRelative(path);
14401
+ }
14314
14402
 
14315
14403
  function importFunction(id) {
14316
14404
  return import(id);
@@ -14343,5 +14431,5 @@ function esmResolver() {
14343
14431
 
14344
14432
  var workerPath = "./jest-worker.js";
14345
14433
 
14346
- export { Attribute as A, Reporter as B, Config as C, DOMNode as D, definePlugin as E, ruleExists as F, walk as G, HtmlValidate as H, EventHandler as I, compatibilityCheckImpl as J, isThenable as K, workerPath as L, MetaCopyableProperty as M, NodeClosed as N, codeframe as O, Parser as P, esmResolver as Q, ResolvedConfig as R, Severity as S, TextNode as T, UserError as U, Validator as V, WrappedError as W, name as X, bugs as Y, ConfigError as a, ConfigLoader as b, defineConfig as c, deepmerge as d, ensureError as e, StaticConfigLoader as f, getFormatter as g, DOMTokenList as h, ignore as i, DOMTree as j, DynamicValue as k, HtmlElement as l, NodeType as m, NestedError as n, SchemaValidationError as o, presets as p, MetaTable as q, TextContent$1 as r, staticResolver as s, Rule as t, ariaNaming as u, version as v, TextClassification as w, classifyNodeText as x, keywordPatternMatcher as y, sliceLocation as z };
14434
+ export { Attribute as A, Reporter as B, Config as C, DOMNode as D, definePlugin as E, ruleExists as F, walk as G, HtmlValidate as H, EventHandler as I, compatibilityCheckImpl as J, isThenable as K, workerPath as L, MetaCopyableProperty as M, NodeClosed as N, codeframe as O, Parser as P, esmResolver as Q, ResolvedConfig as R, Severity as S, TextNode as T, UserError as U, Validator as V, WrappedError as W, name as X, bugs as Y, ConfigError as a, ConfigLoader as b, defineConfig as c, deepmerge as d, ensureError as e, factory as f, getFormatter as g, StaticConfigLoader as h, DOMTokenList as i, DOMTree as j, DynamicValue as k, HtmlElement as l, NodeType as m, NestedError as n, SchemaValidationError as o, presets as p, MetaTable as q, TextContent$1 as r, staticResolver as s, Rule as t, ariaNaming as u, version as v, TextClassification as w, classifyNodeText as x, keywordPatternMatcher as y, sliceLocation as z };
14347
14435
  //# sourceMappingURL=core.js.map