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/cjs/cli.js +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.js +1064 -976
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/jest.js +0 -3
- package/dist/cjs/jest.js.map +1 -1
- package/dist/cjs/matchers.js +2 -6
- package/dist/cjs/matchers.js.map +1 -1
- package/dist/cjs/vitest.js +0 -3
- package/dist/cjs/vitest.js.map +1 -1
- package/dist/es/browser.js +1 -1
- package/dist/es/cli.js +2 -2
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.js +1064 -976
- package/dist/es/core.js.map +1 -1
- package/dist/es/index.js +1 -1
- package/dist/es/jest.js +0 -4
- package/dist/es/jest.js.map +1 -1
- package/dist/es/matchers.js +3 -7
- package/dist/es/matchers.js.map +1 -1
- package/dist/es/vitest.js +0 -4
- package/dist/es/vitest.js.map +1 -1
- package/dist/types/browser.d.ts +7 -6
- package/dist/types/index.d.ts +7 -6
- package/package.json +1 -1
package/dist/cjs/core.js
CHANGED
|
@@ -1947,7 +1947,7 @@ const table = {
|
|
|
1947
1947
|
"nth-child": nthChild,
|
|
1948
1948
|
scope
|
|
1949
1949
|
};
|
|
1950
|
-
function factory(name, context) {
|
|
1950
|
+
function factory$1(name, context) {
|
|
1951
1951
|
const fn = table[name];
|
|
1952
1952
|
if (fn) {
|
|
1953
1953
|
return fn.bind(context);
|
|
@@ -2046,7 +2046,7 @@ function isQuotationMark(ch) {
|
|
|
2046
2046
|
function isPseudoElement(ch, buffer) {
|
|
2047
2047
|
return ch === ":" && buffer === ":";
|
|
2048
2048
|
}
|
|
2049
|
-
function* splitPattern(pattern) {
|
|
2049
|
+
function* splitPattern$1(pattern) {
|
|
2050
2050
|
if (pattern === "") {
|
|
2051
2051
|
return;
|
|
2052
2052
|
}
|
|
@@ -2148,7 +2148,7 @@ class PseudoClassMatcher extends Matcher {
|
|
|
2148
2148
|
this.args = args;
|
|
2149
2149
|
}
|
|
2150
2150
|
match(node, context) {
|
|
2151
|
-
const fn = factory(this.name, context);
|
|
2151
|
+
const fn = factory$1(this.name, context);
|
|
2152
2152
|
return fn(node, this.args);
|
|
2153
2153
|
}
|
|
2154
2154
|
}
|
|
@@ -2166,7 +2166,7 @@ class Pattern {
|
|
|
2166
2166
|
this.selector = pattern;
|
|
2167
2167
|
this.combinator = parseCombinator(match.shift(), pattern);
|
|
2168
2168
|
this.tagName = match.shift() || "*";
|
|
2169
|
-
this.pattern = Array.from(splitPattern(match[0]), (it) => this.createMatcher(it));
|
|
2169
|
+
this.pattern = Array.from(splitPattern$1(match[0]), (it) => this.createMatcher(it));
|
|
2170
2170
|
}
|
|
2171
2171
|
match(node, context) {
|
|
2172
2172
|
return node.is(this.tagName) && this.pattern.every((cur) => cur.match(node, context));
|
|
@@ -10733,24 +10733,59 @@ const presets = {
|
|
|
10733
10733
|
"html-validate:standard": config
|
|
10734
10734
|
};
|
|
10735
10735
|
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10736
|
+
class ResolvedConfig {
|
|
10737
|
+
metaTable;
|
|
10738
|
+
plugins;
|
|
10739
|
+
rules;
|
|
10740
|
+
transformers;
|
|
10741
|
+
/** The original data this resolved configuration was created from */
|
|
10742
|
+
original;
|
|
10743
|
+
/**
|
|
10744
|
+
* @internal
|
|
10745
|
+
*/
|
|
10746
|
+
cache;
|
|
10747
|
+
/**
|
|
10748
|
+
* @internal
|
|
10749
|
+
*/
|
|
10750
|
+
constructor({ metaTable, plugins, rules, transformers }, original) {
|
|
10751
|
+
this.metaTable = metaTable;
|
|
10752
|
+
this.plugins = plugins;
|
|
10753
|
+
this.rules = rules;
|
|
10754
|
+
this.transformers = transformers;
|
|
10755
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
10756
|
+
this.original = original;
|
|
10740
10757
|
}
|
|
10741
|
-
|
|
10742
|
-
|
|
10758
|
+
/**
|
|
10759
|
+
* Returns the (merged) configuration data used to create this resolved
|
|
10760
|
+
* configuration.
|
|
10761
|
+
*/
|
|
10762
|
+
getConfigData() {
|
|
10763
|
+
return this.original;
|
|
10743
10764
|
}
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
`Transformer "${name}" refers to named transformer but plugin exposes only unnamed, use "${pluginName}" instead.`
|
|
10747
|
-
);
|
|
10765
|
+
getMetaTable() {
|
|
10766
|
+
return this.metaTable;
|
|
10748
10767
|
}
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10768
|
+
getPlugins() {
|
|
10769
|
+
return this.plugins;
|
|
10770
|
+
}
|
|
10771
|
+
getRules() {
|
|
10772
|
+
return this.rules;
|
|
10773
|
+
}
|
|
10774
|
+
/**
|
|
10775
|
+
* Returns true if a transformer matches given filename.
|
|
10776
|
+
*
|
|
10777
|
+
* @public
|
|
10778
|
+
*/
|
|
10779
|
+
canTransform(filename) {
|
|
10780
|
+
return Boolean(this.findTransformer(filename));
|
|
10781
|
+
}
|
|
10782
|
+
/**
|
|
10783
|
+
* @internal
|
|
10784
|
+
*/
|
|
10785
|
+
findTransformer(filename) {
|
|
10786
|
+
const match = this.transformers.find((entry) => entry.pattern.test(filename));
|
|
10787
|
+
return match ?? null;
|
|
10752
10788
|
}
|
|
10753
|
-
return transformer;
|
|
10754
10789
|
}
|
|
10755
10790
|
|
|
10756
10791
|
function haveResolver(key, value) {
|
|
@@ -10884,283 +10919,6 @@ function staticResolver(map = {}) {
|
|
|
10884
10919
|
};
|
|
10885
10920
|
}
|
|
10886
10921
|
|
|
10887
|
-
function getTransformerFromModule(resolvers, name) {
|
|
10888
|
-
return resolveTransformer(resolvers, name, { cache: true });
|
|
10889
|
-
}
|
|
10890
|
-
|
|
10891
|
-
function getUnnamedTransformerFromPlugin(name, plugin) {
|
|
10892
|
-
if (!plugin.transformer) {
|
|
10893
|
-
throw new ConfigError(`Plugin does not expose any transformers`);
|
|
10894
|
-
}
|
|
10895
|
-
if (typeof plugin.transformer !== "function") {
|
|
10896
|
-
if (plugin.transformer.default) {
|
|
10897
|
-
return plugin.transformer.default;
|
|
10898
|
-
}
|
|
10899
|
-
throw new ConfigError(
|
|
10900
|
-
`Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
|
|
10901
|
-
);
|
|
10902
|
-
}
|
|
10903
|
-
return plugin.transformer;
|
|
10904
|
-
}
|
|
10905
|
-
|
|
10906
|
-
const TRANSFORMER_API = {
|
|
10907
|
-
VERSION: 1
|
|
10908
|
-
};
|
|
10909
|
-
|
|
10910
|
-
function validateTransformer(transformer) {
|
|
10911
|
-
const version = transformer.api ?? 0;
|
|
10912
|
-
if (version !== TRANSFORMER_API.VERSION) {
|
|
10913
|
-
throw new ConfigError(
|
|
10914
|
-
`Transformer uses API version ${String(version)} but only version ${String(TRANSFORMER_API.VERSION)} is supported`
|
|
10915
|
-
);
|
|
10916
|
-
}
|
|
10917
|
-
}
|
|
10918
|
-
function loadTransformerFunction(resolvers, name, plugins) {
|
|
10919
|
-
const match = name.match(/(.*):(.*)/);
|
|
10920
|
-
if (match) {
|
|
10921
|
-
const [, pluginName, key] = match;
|
|
10922
|
-
return getNamedTransformerFromPlugin(name, plugins, pluginName, key);
|
|
10923
|
-
}
|
|
10924
|
-
const plugin = plugins.find((cur) => cur.name === name);
|
|
10925
|
-
if (plugin) {
|
|
10926
|
-
return getUnnamedTransformerFromPlugin(name, plugin);
|
|
10927
|
-
}
|
|
10928
|
-
return getTransformerFromModule(resolvers, name);
|
|
10929
|
-
}
|
|
10930
|
-
function getTransformerFunction(resolvers, name, plugins) {
|
|
10931
|
-
try {
|
|
10932
|
-
const transformer = loadTransformerFunction(resolvers, name, plugins);
|
|
10933
|
-
if (isThenable(transformer)) {
|
|
10934
|
-
return transformer.then((transformer2) => {
|
|
10935
|
-
validateTransformer(transformer2);
|
|
10936
|
-
return transformer2;
|
|
10937
|
-
});
|
|
10938
|
-
} else {
|
|
10939
|
-
validateTransformer(transformer);
|
|
10940
|
-
return transformer;
|
|
10941
|
-
}
|
|
10942
|
-
} catch (err) {
|
|
10943
|
-
if (err instanceof ConfigError) {
|
|
10944
|
-
throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
|
|
10945
|
-
} else {
|
|
10946
|
-
throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
|
|
10947
|
-
}
|
|
10948
|
-
}
|
|
10949
|
-
}
|
|
10950
|
-
function getCachedTransformerFunction(cache, resolvers, name, plugins) {
|
|
10951
|
-
const cached = cache.get(name);
|
|
10952
|
-
if (cached) {
|
|
10953
|
-
return cached;
|
|
10954
|
-
} else {
|
|
10955
|
-
const transformer = getTransformerFunction(resolvers, name, plugins);
|
|
10956
|
-
if (isThenable(transformer)) {
|
|
10957
|
-
return transformer.then((transformer2) => {
|
|
10958
|
-
cache.set(name, transformer2);
|
|
10959
|
-
return transformer2;
|
|
10960
|
-
});
|
|
10961
|
-
} else {
|
|
10962
|
-
cache.set(name, transformer);
|
|
10963
|
-
return transformer;
|
|
10964
|
-
}
|
|
10965
|
-
}
|
|
10966
|
-
}
|
|
10967
|
-
|
|
10968
|
-
function isIterable(value) {
|
|
10969
|
-
return Boolean(value && typeof value === "object" && Symbol.iterator in value);
|
|
10970
|
-
}
|
|
10971
|
-
function isNonThenableArray(value) {
|
|
10972
|
-
return !value.some(isThenable);
|
|
10973
|
-
}
|
|
10974
|
-
function toArray$1(value) {
|
|
10975
|
-
return isIterable(value) ? Array.from(value) : [value];
|
|
10976
|
-
}
|
|
10977
|
-
const asyncInSyncTransformError = "Cannot use async transformer from sync function";
|
|
10978
|
-
class ResolvedConfig {
|
|
10979
|
-
metaTable;
|
|
10980
|
-
plugins;
|
|
10981
|
-
rules;
|
|
10982
|
-
transformers;
|
|
10983
|
-
cache;
|
|
10984
|
-
/** The original data this resolved configuration was created from */
|
|
10985
|
-
original;
|
|
10986
|
-
/**
|
|
10987
|
-
* @internal
|
|
10988
|
-
*/
|
|
10989
|
-
constructor({ metaTable, plugins, rules, transformers }, original) {
|
|
10990
|
-
this.metaTable = metaTable;
|
|
10991
|
-
this.plugins = plugins;
|
|
10992
|
-
this.rules = rules;
|
|
10993
|
-
this.transformers = transformers;
|
|
10994
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
10995
|
-
this.original = original;
|
|
10996
|
-
}
|
|
10997
|
-
/**
|
|
10998
|
-
* Returns the (merged) configuration data used to create this resolved
|
|
10999
|
-
* configuration.
|
|
11000
|
-
*/
|
|
11001
|
-
getConfigData() {
|
|
11002
|
-
return this.original;
|
|
11003
|
-
}
|
|
11004
|
-
getMetaTable() {
|
|
11005
|
-
return this.metaTable;
|
|
11006
|
-
}
|
|
11007
|
-
getPlugins() {
|
|
11008
|
-
return this.plugins;
|
|
11009
|
-
}
|
|
11010
|
-
getRules() {
|
|
11011
|
-
return this.rules;
|
|
11012
|
-
}
|
|
11013
|
-
/**
|
|
11014
|
-
* Transform a source.
|
|
11015
|
-
*
|
|
11016
|
-
* When transforming zero or more new sources will be generated.
|
|
11017
|
-
*
|
|
11018
|
-
* @internal
|
|
11019
|
-
* @param source - Current source to transform.
|
|
11020
|
-
* @param filename - If set it is the filename used to match
|
|
11021
|
-
* transformer. Default is to use filename from source.
|
|
11022
|
-
* @returns A list of transformed sources ready for validation.
|
|
11023
|
-
*/
|
|
11024
|
-
async transformSource(resolvers, source, filename) {
|
|
11025
|
-
const transformer = this.findTransformer(filename ?? source.filename);
|
|
11026
|
-
const context = {
|
|
11027
|
-
hasChain: (filename2) => {
|
|
11028
|
-
return !!this.findTransformer(filename2);
|
|
11029
|
-
},
|
|
11030
|
-
chain: (source2, filename2) => {
|
|
11031
|
-
return this.transformSource(resolvers, source2, filename2);
|
|
11032
|
-
}
|
|
11033
|
-
};
|
|
11034
|
-
if (!transformer) {
|
|
11035
|
-
return Promise.resolve([source]);
|
|
11036
|
-
}
|
|
11037
|
-
const fn = transformer.kind === "import" ? await getCachedTransformerFunction(this.cache, resolvers, transformer.name, this.plugins) : transformer.function;
|
|
11038
|
-
const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
|
|
11039
|
-
try {
|
|
11040
|
-
const result = await fn.call(context, source);
|
|
11041
|
-
const transformedSources = await Promise.all(toArray$1(result));
|
|
11042
|
-
for (const source2 of transformedSources) {
|
|
11043
|
-
source2.transformedBy ??= [];
|
|
11044
|
-
source2.transformedBy.push(name);
|
|
11045
|
-
}
|
|
11046
|
-
return transformedSources;
|
|
11047
|
-
} catch (err) {
|
|
11048
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
11049
|
-
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
11050
|
-
}
|
|
11051
|
-
}
|
|
11052
|
-
/**
|
|
11053
|
-
* Transform a source.
|
|
11054
|
-
*
|
|
11055
|
-
* When transforming zero or more new sources will be generated.
|
|
11056
|
-
*
|
|
11057
|
-
* @internal
|
|
11058
|
-
* @param source - Current source to transform.
|
|
11059
|
-
* @param filename - If set it is the filename used to match
|
|
11060
|
-
* transformer. Default is to use filename from source.
|
|
11061
|
-
* @returns A list of transformed sources ready for validation.
|
|
11062
|
-
*/
|
|
11063
|
-
/* eslint-disable-next-line complexity -- there is many ifs'n buts here but
|
|
11064
|
-
* hard to break this down without loosing the little clarity that is still
|
|
11065
|
-
* left */
|
|
11066
|
-
transformSourceSync(resolvers, source, filename) {
|
|
11067
|
-
const transformer = this.findTransformer(filename ?? source.filename);
|
|
11068
|
-
const context = {
|
|
11069
|
-
hasChain: (filename2) => {
|
|
11070
|
-
return !!this.findTransformer(filename2);
|
|
11071
|
-
},
|
|
11072
|
-
chain: (source2, filename2) => {
|
|
11073
|
-
return this.transformSourceSync(resolvers, source2, filename2);
|
|
11074
|
-
}
|
|
11075
|
-
};
|
|
11076
|
-
if (!transformer) {
|
|
11077
|
-
return [source];
|
|
11078
|
-
}
|
|
11079
|
-
const fn = transformer.kind === "import" ? getCachedTransformerFunction(this.cache, resolvers, transformer.name, this.plugins) : transformer.function;
|
|
11080
|
-
const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
|
|
11081
|
-
if (isThenable(fn)) {
|
|
11082
|
-
throw new UserError(asyncInSyncTransformError);
|
|
11083
|
-
}
|
|
11084
|
-
try {
|
|
11085
|
-
const result = fn.call(context, source);
|
|
11086
|
-
if (isThenable(result)) {
|
|
11087
|
-
throw new UserError(asyncInSyncTransformError);
|
|
11088
|
-
}
|
|
11089
|
-
const transformedSources = toArray$1(result);
|
|
11090
|
-
if (!isNonThenableArray(transformedSources)) {
|
|
11091
|
-
throw new UserError(asyncInSyncTransformError);
|
|
11092
|
-
}
|
|
11093
|
-
for (const source2 of transformedSources) {
|
|
11094
|
-
source2.transformedBy ??= [];
|
|
11095
|
-
source2.transformedBy.push(name);
|
|
11096
|
-
}
|
|
11097
|
-
return transformedSources;
|
|
11098
|
-
} catch (err) {
|
|
11099
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
11100
|
-
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
11101
|
-
}
|
|
11102
|
-
}
|
|
11103
|
-
/**
|
|
11104
|
-
* Wrapper around [[transformSource]] which reads a file before passing it
|
|
11105
|
-
* as-is to transformSource.
|
|
11106
|
-
*
|
|
11107
|
-
* @internal
|
|
11108
|
-
* @param filename - Filename to transform (according to configured
|
|
11109
|
-
* transformations)
|
|
11110
|
-
* @returns A list of transformed sources ready for validation.
|
|
11111
|
-
*/
|
|
11112
|
-
transformFilename(resolvers, filename) {
|
|
11113
|
-
const stdin = 0;
|
|
11114
|
-
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
11115
|
-
const data = fs__default.default.readFileSync(src, { encoding: "utf8" });
|
|
11116
|
-
const source = {
|
|
11117
|
-
data,
|
|
11118
|
-
filename,
|
|
11119
|
-
line: 1,
|
|
11120
|
-
column: 1,
|
|
11121
|
-
offset: 0,
|
|
11122
|
-
originalData: data
|
|
11123
|
-
};
|
|
11124
|
-
return this.transformSource(resolvers, source, filename);
|
|
11125
|
-
}
|
|
11126
|
-
/**
|
|
11127
|
-
* Wrapper around [[transformSource]] which reads a file before passing it
|
|
11128
|
-
* as-is to transformSource.
|
|
11129
|
-
*
|
|
11130
|
-
* @internal
|
|
11131
|
-
* @param filename - Filename to transform (according to configured
|
|
11132
|
-
* transformations)
|
|
11133
|
-
* @returns A list of transformed sources ready for validation.
|
|
11134
|
-
*/
|
|
11135
|
-
transformFilenameSync(resolvers, filename) {
|
|
11136
|
-
const stdin = 0;
|
|
11137
|
-
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
11138
|
-
const data = fs__default.default.readFileSync(src, { encoding: "utf8" });
|
|
11139
|
-
const source = {
|
|
11140
|
-
data,
|
|
11141
|
-
filename,
|
|
11142
|
-
line: 1,
|
|
11143
|
-
column: 1,
|
|
11144
|
-
offset: 0,
|
|
11145
|
-
originalData: data
|
|
11146
|
-
};
|
|
11147
|
-
return this.transformSourceSync(resolvers, source, filename);
|
|
11148
|
-
}
|
|
11149
|
-
/**
|
|
11150
|
-
* Returns true if a transformer matches given filename.
|
|
11151
|
-
*
|
|
11152
|
-
* @public
|
|
11153
|
-
*/
|
|
11154
|
-
canTransform(filename) {
|
|
11155
|
-
const entry = this.findTransformer(filename);
|
|
11156
|
-
return !!entry;
|
|
11157
|
-
}
|
|
11158
|
-
findTransformer(filename) {
|
|
11159
|
-
const match = this.transformers.find((entry) => entry.pattern.test(filename));
|
|
11160
|
-
return match ?? null;
|
|
11161
|
-
}
|
|
11162
|
-
}
|
|
11163
|
-
|
|
11164
10922
|
const ajv = new Ajv__default.default({ strict: true, strictTuples: true, strictTypes: true });
|
|
11165
10923
|
ajv.addMetaSchema(ajvSchemaDraft);
|
|
11166
10924
|
ajv.addKeyword(ajvFunctionKeyword);
|
|
@@ -11179,7 +10937,7 @@ function mergeInternal(base, rhs) {
|
|
|
11179
10937
|
}
|
|
11180
10938
|
return dst;
|
|
11181
10939
|
}
|
|
11182
|
-
function toArray(value) {
|
|
10940
|
+
function toArray$1(value) {
|
|
11183
10941
|
if (Array.isArray(value)) {
|
|
11184
10942
|
return value;
|
|
11185
10943
|
} else {
|
|
@@ -11231,7 +10989,7 @@ class Config {
|
|
|
11231
10989
|
* @param filename - The file to read from
|
|
11232
10990
|
*/
|
|
11233
10991
|
static fromFile(resolvers, filename) {
|
|
11234
|
-
const configData = resolveConfig(toArray(resolvers), filename, { cache: false });
|
|
10992
|
+
const configData = resolveConfig(toArray$1(resolvers), filename, { cache: false });
|
|
11235
10993
|
if (isThenable(configData)) {
|
|
11236
10994
|
return configData.then((configData2) => Config.fromObject(resolvers, configData2, filename));
|
|
11237
10995
|
} else {
|
|
@@ -11317,7 +11075,7 @@ class Config {
|
|
|
11317
11075
|
};
|
|
11318
11076
|
this.config = mergeInternal(initial, options);
|
|
11319
11077
|
this.configurations = /* @__PURE__ */ new Map();
|
|
11320
|
-
this.resolvers = toArray(resolvers);
|
|
11078
|
+
this.resolvers = toArray$1(resolvers);
|
|
11321
11079
|
this.metaTable = null;
|
|
11322
11080
|
this.plugins = [];
|
|
11323
11081
|
this.transformers = transformerEntries(this.config.transform ?? {});
|
|
@@ -12903,43 +12661,251 @@ class StaticConfigLoader extends ConfigLoader {
|
|
|
12903
12661
|
return this._resolveConfig(override);
|
|
12904
12662
|
}
|
|
12905
12663
|
}
|
|
12906
|
-
flushCache() {
|
|
12664
|
+
flushCache() {
|
|
12665
|
+
}
|
|
12666
|
+
defaultConfig() {
|
|
12667
|
+
return this.loadFromObject({
|
|
12668
|
+
extends: ["html-validate:recommended"],
|
|
12669
|
+
elements: ["html5"]
|
|
12670
|
+
});
|
|
12671
|
+
}
|
|
12672
|
+
_resolveConfig(override) {
|
|
12673
|
+
if (override.isRootFound()) {
|
|
12674
|
+
return override.resolve();
|
|
12675
|
+
}
|
|
12676
|
+
const globalConfig = this.getGlobalConfig();
|
|
12677
|
+
if (isThenable(globalConfig)) {
|
|
12678
|
+
return globalConfig.then((globalConfig2) => {
|
|
12679
|
+
const merged = globalConfig2.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
|
+
} else {
|
|
12689
|
+
const merged = globalConfig.merge(this.resolvers, override);
|
|
12690
|
+
if (isThenable(merged)) {
|
|
12691
|
+
return merged.then((merged2) => {
|
|
12692
|
+
return merged2.resolve();
|
|
12693
|
+
});
|
|
12694
|
+
} else {
|
|
12695
|
+
return merged.resolve();
|
|
12696
|
+
}
|
|
12697
|
+
}
|
|
12698
|
+
}
|
|
12699
|
+
}
|
|
12700
|
+
|
|
12701
|
+
function getNamedTransformerFromPlugin(name, plugins, pluginName, key) {
|
|
12702
|
+
const plugin = plugins.find((cur) => cur.name === pluginName);
|
|
12703
|
+
if (!plugin) {
|
|
12704
|
+
throw new ConfigError(`No plugin named "${pluginName}" has been loaded`);
|
|
12705
|
+
}
|
|
12706
|
+
if (!plugin.transformer) {
|
|
12707
|
+
throw new ConfigError(`Plugin does not expose any transformers`);
|
|
12708
|
+
}
|
|
12709
|
+
if (typeof plugin.transformer === "function") {
|
|
12710
|
+
throw new ConfigError(
|
|
12711
|
+
`Transformer "${name}" refers to named transformer but plugin exposes only unnamed, use "${pluginName}" instead.`
|
|
12712
|
+
);
|
|
12713
|
+
}
|
|
12714
|
+
const transformer = plugin.transformer[key];
|
|
12715
|
+
if (!transformer) {
|
|
12716
|
+
throw new ConfigError(`Plugin "${pluginName}" does not expose a transformer named "${key}".`);
|
|
12717
|
+
}
|
|
12718
|
+
return transformer;
|
|
12719
|
+
}
|
|
12720
|
+
|
|
12721
|
+
function getTransformerFromModule(resolvers, name) {
|
|
12722
|
+
return resolveTransformer(resolvers, name, { cache: true });
|
|
12723
|
+
}
|
|
12724
|
+
|
|
12725
|
+
function getUnnamedTransformerFromPlugin(name, plugin) {
|
|
12726
|
+
if (!plugin.transformer) {
|
|
12727
|
+
throw new ConfigError(`Plugin does not expose any transformers`);
|
|
12728
|
+
}
|
|
12729
|
+
if (typeof plugin.transformer !== "function") {
|
|
12730
|
+
if (plugin.transformer.default) {
|
|
12731
|
+
return plugin.transformer.default;
|
|
12732
|
+
}
|
|
12733
|
+
throw new ConfigError(
|
|
12734
|
+
`Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
|
|
12735
|
+
);
|
|
12736
|
+
}
|
|
12737
|
+
return plugin.transformer;
|
|
12738
|
+
}
|
|
12739
|
+
|
|
12740
|
+
const TRANSFORMER_API = {
|
|
12741
|
+
VERSION: 1
|
|
12742
|
+
};
|
|
12743
|
+
|
|
12744
|
+
function validateTransformer(transformer) {
|
|
12745
|
+
const version = transformer.api ?? 0;
|
|
12746
|
+
if (version !== TRANSFORMER_API.VERSION) {
|
|
12747
|
+
throw new ConfigError(
|
|
12748
|
+
`Transformer uses API version ${String(version)} but only version ${String(TRANSFORMER_API.VERSION)} is supported`
|
|
12749
|
+
);
|
|
12750
|
+
}
|
|
12751
|
+
}
|
|
12752
|
+
function loadTransformerFunction(resolvers, name, plugins) {
|
|
12753
|
+
const match = name.match(/(.*):(.*)/);
|
|
12754
|
+
if (match) {
|
|
12755
|
+
const [, pluginName, key] = match;
|
|
12756
|
+
return getNamedTransformerFromPlugin(name, plugins, pluginName, key);
|
|
12757
|
+
}
|
|
12758
|
+
const plugin = plugins.find((cur) => cur.name === name);
|
|
12759
|
+
if (plugin) {
|
|
12760
|
+
return getUnnamedTransformerFromPlugin(name, plugin);
|
|
12761
|
+
}
|
|
12762
|
+
return getTransformerFromModule(resolvers, name);
|
|
12763
|
+
}
|
|
12764
|
+
function getTransformerFunction(resolvers, name, plugins) {
|
|
12765
|
+
try {
|
|
12766
|
+
const transformer = loadTransformerFunction(resolvers, name, plugins);
|
|
12767
|
+
if (isThenable(transformer)) {
|
|
12768
|
+
return transformer.then((transformer2) => {
|
|
12769
|
+
validateTransformer(transformer2);
|
|
12770
|
+
return transformer2;
|
|
12771
|
+
});
|
|
12772
|
+
} else {
|
|
12773
|
+
validateTransformer(transformer);
|
|
12774
|
+
return transformer;
|
|
12775
|
+
}
|
|
12776
|
+
} catch (err) {
|
|
12777
|
+
if (err instanceof ConfigError) {
|
|
12778
|
+
throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
|
|
12779
|
+
} else {
|
|
12780
|
+
throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
|
|
12781
|
+
}
|
|
12782
|
+
}
|
|
12783
|
+
}
|
|
12784
|
+
function getCachedTransformerFunction(cache, resolvers, name, plugins) {
|
|
12785
|
+
const cached = cache.get(name);
|
|
12786
|
+
if (cached) {
|
|
12787
|
+
return cached;
|
|
12788
|
+
} else {
|
|
12789
|
+
const transformer = getTransformerFunction(resolvers, name, plugins);
|
|
12790
|
+
if (isThenable(transformer)) {
|
|
12791
|
+
return transformer.then((transformer2) => {
|
|
12792
|
+
cache.set(name, transformer2);
|
|
12793
|
+
return transformer2;
|
|
12794
|
+
});
|
|
12795
|
+
} else {
|
|
12796
|
+
cache.set(name, transformer);
|
|
12797
|
+
return transformer;
|
|
12798
|
+
}
|
|
12799
|
+
}
|
|
12800
|
+
}
|
|
12801
|
+
|
|
12802
|
+
function isIterable(value) {
|
|
12803
|
+
return Boolean(value && typeof value === "object" && Symbol.iterator in value);
|
|
12804
|
+
}
|
|
12805
|
+
function toArray(value) {
|
|
12806
|
+
return isIterable(value) ? Array.from(value) : [value];
|
|
12807
|
+
}
|
|
12808
|
+
function isNonThenableArray(value) {
|
|
12809
|
+
return !value.some(isThenable);
|
|
12810
|
+
}
|
|
12811
|
+
const asyncInSyncTransformError = "Cannot use async transformer from sync function";
|
|
12812
|
+
async function transformSource(resolvers, config, source, filename) {
|
|
12813
|
+
const { cache } = config;
|
|
12814
|
+
const transformer = config.findTransformer(filename ?? source.filename);
|
|
12815
|
+
const context = {
|
|
12816
|
+
hasChain(filename2) {
|
|
12817
|
+
return config.canTransform(filename2);
|
|
12818
|
+
},
|
|
12819
|
+
chain(source2, filename2) {
|
|
12820
|
+
return transformSource(resolvers, config, source2, filename2);
|
|
12821
|
+
}
|
|
12822
|
+
};
|
|
12823
|
+
if (!transformer) {
|
|
12824
|
+
return Promise.resolve([source]);
|
|
12825
|
+
}
|
|
12826
|
+
const fn = transformer.kind === "import" ? await getCachedTransformerFunction(cache, resolvers, transformer.name, config.getPlugins()) : transformer.function;
|
|
12827
|
+
const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
|
|
12828
|
+
try {
|
|
12829
|
+
const result = await fn.call(context, source);
|
|
12830
|
+
const transformedSources = await Promise.all(toArray(result));
|
|
12831
|
+
for (const source2 of transformedSources) {
|
|
12832
|
+
source2.transformedBy ??= [];
|
|
12833
|
+
source2.transformedBy.push(name);
|
|
12834
|
+
}
|
|
12835
|
+
return transformedSources;
|
|
12836
|
+
} catch (err) {
|
|
12837
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12838
|
+
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
12839
|
+
}
|
|
12840
|
+
}
|
|
12841
|
+
function transformSourceSync(resolvers, config, source, filename) {
|
|
12842
|
+
const { cache } = config;
|
|
12843
|
+
const transformer = config.findTransformer(filename ?? source.filename);
|
|
12844
|
+
const context = {
|
|
12845
|
+
hasChain(filename2) {
|
|
12846
|
+
return config.canTransform(filename2);
|
|
12847
|
+
},
|
|
12848
|
+
chain(source2, filename2) {
|
|
12849
|
+
return transformSourceSync(resolvers, config, source2, filename2);
|
|
12850
|
+
}
|
|
12851
|
+
};
|
|
12852
|
+
if (!transformer) {
|
|
12853
|
+
return [source];
|
|
12907
12854
|
}
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
});
|
|
12855
|
+
const fn = transformer.kind === "import" ? getCachedTransformerFunction(cache, resolvers, transformer.name, config.getPlugins()) : transformer.function;
|
|
12856
|
+
const name = transformer.kind === "import" ? transformer.name : transformer.function.name;
|
|
12857
|
+
if (isThenable(fn)) {
|
|
12858
|
+
throw new UserError(asyncInSyncTransformError);
|
|
12913
12859
|
}
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12860
|
+
try {
|
|
12861
|
+
const result = fn.call(context, source);
|
|
12862
|
+
if (isThenable(result)) {
|
|
12863
|
+
throw new UserError(asyncInSyncTransformError);
|
|
12917
12864
|
}
|
|
12918
|
-
const
|
|
12919
|
-
if (
|
|
12920
|
-
|
|
12921
|
-
const merged = globalConfig2.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
|
-
}
|
|
12929
|
-
});
|
|
12930
|
-
} else {
|
|
12931
|
-
const merged = globalConfig.merge(this.resolvers, override);
|
|
12932
|
-
if (isThenable(merged)) {
|
|
12933
|
-
return merged.then((merged2) => {
|
|
12934
|
-
return merged2.resolve();
|
|
12935
|
-
});
|
|
12936
|
-
} else {
|
|
12937
|
-
return merged.resolve();
|
|
12938
|
-
}
|
|
12865
|
+
const transformedSources = toArray(result);
|
|
12866
|
+
if (!isNonThenableArray(transformedSources)) {
|
|
12867
|
+
throw new UserError(asyncInSyncTransformError);
|
|
12939
12868
|
}
|
|
12869
|
+
for (const source2 of transformedSources) {
|
|
12870
|
+
source2.transformedBy ??= [];
|
|
12871
|
+
source2.transformedBy.push(name);
|
|
12872
|
+
}
|
|
12873
|
+
return transformedSources;
|
|
12874
|
+
} catch (err) {
|
|
12875
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12876
|
+
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
12940
12877
|
}
|
|
12941
12878
|
}
|
|
12942
12879
|
|
|
12880
|
+
function transformFilename(resolvers, config, filename) {
|
|
12881
|
+
const stdin = 0;
|
|
12882
|
+
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
12883
|
+
const data = fs__default.default.readFileSync(src, { encoding: "utf8" });
|
|
12884
|
+
const source = {
|
|
12885
|
+
data,
|
|
12886
|
+
filename,
|
|
12887
|
+
line: 1,
|
|
12888
|
+
column: 1,
|
|
12889
|
+
offset: 0,
|
|
12890
|
+
originalData: data
|
|
12891
|
+
};
|
|
12892
|
+
return transformSource(resolvers, config, source, filename);
|
|
12893
|
+
}
|
|
12894
|
+
function transformFilenameSync(resolvers, config, filename) {
|
|
12895
|
+
const stdin = 0;
|
|
12896
|
+
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
12897
|
+
const data = fs__default.default.readFileSync(src, { encoding: "utf8" });
|
|
12898
|
+
const source = {
|
|
12899
|
+
data,
|
|
12900
|
+
filename,
|
|
12901
|
+
line: 1,
|
|
12902
|
+
column: 1,
|
|
12903
|
+
offset: 0,
|
|
12904
|
+
originalData: data
|
|
12905
|
+
};
|
|
12906
|
+
return transformSourceSync(resolvers, config, source, filename);
|
|
12907
|
+
}
|
|
12908
|
+
|
|
12943
12909
|
function isSourceHooks(value) {
|
|
12944
12910
|
if (!value || typeof value === "string") {
|
|
12945
12911
|
return false;
|
|
@@ -12999,7 +12965,7 @@ class HtmlValidate {
|
|
|
12999
12965
|
const source = normalizeSource(input);
|
|
13000
12966
|
const config = await this.getConfigFor(source.filename, configOverride);
|
|
13001
12967
|
const resolvers = this.configLoader.getResolvers();
|
|
13002
|
-
const transformedSource = await
|
|
12968
|
+
const transformedSource = await transformSource(resolvers, config, source);
|
|
13003
12969
|
const engine = new Engine(config, Parser);
|
|
13004
12970
|
return engine.lint(transformedSource);
|
|
13005
12971
|
}
|
|
@@ -13014,7 +12980,7 @@ class HtmlValidate {
|
|
|
13014
12980
|
const source = normalizeSource(input);
|
|
13015
12981
|
const config = this.getConfigForSync(source.filename, configOverride);
|
|
13016
12982
|
const resolvers = this.configLoader.getResolvers();
|
|
13017
|
-
const transformedSource =
|
|
12983
|
+
const transformedSource = transformSourceSync(resolvers, config, source);
|
|
13018
12984
|
const engine = new Engine(config, Parser);
|
|
13019
12985
|
return engine.lint(transformedSource);
|
|
13020
12986
|
}
|
|
@@ -13028,7 +12994,7 @@ class HtmlValidate {
|
|
|
13028
12994
|
async validateFile(filename) {
|
|
13029
12995
|
const config = await this.getConfigFor(filename);
|
|
13030
12996
|
const resolvers = this.configLoader.getResolvers();
|
|
13031
|
-
const source = await
|
|
12997
|
+
const source = await transformFilename(resolvers, config, filename);
|
|
13032
12998
|
const engine = new Engine(config, Parser);
|
|
13033
12999
|
return Promise.resolve(engine.lint(source));
|
|
13034
13000
|
}
|
|
@@ -13042,7 +13008,7 @@ class HtmlValidate {
|
|
|
13042
13008
|
validateFileSync(filename) {
|
|
13043
13009
|
const config = this.getConfigForSync(filename);
|
|
13044
13010
|
const resolvers = this.configLoader.getResolvers();
|
|
13045
|
-
const source =
|
|
13011
|
+
const source = transformFilenameSync(resolvers, config, filename);
|
|
13046
13012
|
const engine = new Engine(config, Parser);
|
|
13047
13013
|
return engine.lint(source);
|
|
13048
13014
|
}
|
|
@@ -13054,8 +13020,7 @@ class HtmlValidate {
|
|
|
13054
13020
|
* @returns Report output.
|
|
13055
13021
|
*/
|
|
13056
13022
|
async validateMultipleFiles(filenames) {
|
|
13057
|
-
|
|
13058
|
-
return Promise.resolve(report);
|
|
13023
|
+
return Reporter.merge(filenames.map((filename) => this.validateFile(filename)));
|
|
13059
13024
|
}
|
|
13060
13025
|
/**
|
|
13061
13026
|
* Parse and validate HTML from multiple files. Result is merged together to a
|
|
@@ -13111,7 +13076,7 @@ class HtmlValidate {
|
|
|
13111
13076
|
async dumpTokens(filename) {
|
|
13112
13077
|
const config = await this.getConfigFor(filename);
|
|
13113
13078
|
const resolvers = this.configLoader.getResolvers();
|
|
13114
|
-
const source = await
|
|
13079
|
+
const source = await transformFilename(resolvers, config, filename);
|
|
13115
13080
|
const engine = new Engine(config, Parser);
|
|
13116
13081
|
return engine.dumpTokens(source);
|
|
13117
13082
|
}
|
|
@@ -13127,7 +13092,7 @@ class HtmlValidate {
|
|
|
13127
13092
|
async dumpEvents(filename) {
|
|
13128
13093
|
const config = await this.getConfigFor(filename);
|
|
13129
13094
|
const resolvers = this.configLoader.getResolvers();
|
|
13130
|
-
const source = await
|
|
13095
|
+
const source = await transformFilename(resolvers, config, filename);
|
|
13131
13096
|
const engine = new Engine(config, Parser);
|
|
13132
13097
|
return engine.dumpEvents(source);
|
|
13133
13098
|
}
|
|
@@ -13143,7 +13108,7 @@ class HtmlValidate {
|
|
|
13143
13108
|
async dumpTree(filename) {
|
|
13144
13109
|
const config = await this.getConfigFor(filename);
|
|
13145
13110
|
const resolvers = this.configLoader.getResolvers();
|
|
13146
|
-
const source = await
|
|
13111
|
+
const source = await transformFilename(resolvers, config, filename);
|
|
13147
13112
|
const engine = new Engine(config, Parser);
|
|
13148
13113
|
return engine.dumpTree(source);
|
|
13149
13114
|
}
|
|
@@ -13159,7 +13124,7 @@ class HtmlValidate {
|
|
|
13159
13124
|
async dumpSource(filename) {
|
|
13160
13125
|
const config = await this.getConfigFor(filename);
|
|
13161
13126
|
const resolvers = this.configLoader.getResolvers();
|
|
13162
|
-
const sources = await
|
|
13127
|
+
const sources = await transformFilename(resolvers, config, filename);
|
|
13163
13128
|
return sources.reduce((result, source) => {
|
|
13164
13129
|
const line = String(source.line);
|
|
13165
13130
|
const column = String(source.column);
|
|
@@ -13358,7 +13323,7 @@ class HtmlValidate {
|
|
|
13358
13323
|
}
|
|
13359
13324
|
|
|
13360
13325
|
const name = "html-validate";
|
|
13361
|
-
const version = "9.
|
|
13326
|
+
const version = "9.1.0";
|
|
13362
13327
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
13363
13328
|
|
|
13364
13329
|
function definePlugin(plugin) {
|
|
@@ -13674,653 +13639,776 @@ function compatibilityCheckImpl(name, declared, options) {
|
|
|
13674
13639
|
return false;
|
|
13675
13640
|
}
|
|
13676
13641
|
|
|
13677
|
-
|
|
13678
|
-
|
|
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
|
+
};
|
|
13679
13685
|
|
|
13680
|
-
|
|
13681
|
-
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
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
|
+
};
|
|
13689
13706
|
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
// ./foo,
|
|
13700
|
-
// ../foo,
|
|
13701
|
-
// .
|
|
13702
|
-
// ..
|
|
13703
|
-
const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
|
|
13704
|
-
|
|
13705
|
-
const SLASH = '/';
|
|
13706
|
-
|
|
13707
|
-
// Do not use ternary expression here, since "istanbul ignore next" is buggy
|
|
13708
|
-
let TMP_KEY_IGNORE = 'node-ignore';
|
|
13709
|
-
/* istanbul ignore else */
|
|
13710
|
-
if (typeof Symbol !== 'undefined') {
|
|
13711
|
-
TMP_KEY_IGNORE = Symbol.for('node-ignore');
|
|
13712
|
-
}
|
|
13713
|
-
const KEY_IGNORE = TMP_KEY_IGNORE;
|
|
13714
|
-
|
|
13715
|
-
const define = (object, key, value) =>
|
|
13716
|
-
Object.defineProperty(object, key, {value});
|
|
13717
|
-
|
|
13718
|
-
const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
13719
|
-
|
|
13720
|
-
const RETURN_FALSE = () => false;
|
|
13721
|
-
|
|
13722
|
-
// Sanitize the range of a regular expression
|
|
13723
|
-
// The cases are complicated, see test cases for details
|
|
13724
|
-
const sanitizeRange = range => range.replace(
|
|
13725
|
-
REGEX_REGEXP_RANGE,
|
|
13726
|
-
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
|
|
13727
|
-
? match
|
|
13728
|
-
// Invalid range (out of order) which is ok for gitignore rules but
|
|
13729
|
-
// fatal for JavaScript regular expression, so eliminate it.
|
|
13730
|
-
: EMPTY
|
|
13731
|
-
);
|
|
13732
|
-
|
|
13733
|
-
// See fixtures #59
|
|
13734
|
-
const cleanRangeBackSlash = slashes => {
|
|
13735
|
-
const {length} = slashes;
|
|
13736
|
-
return slashes.slice(0, length - length % 2)
|
|
13737
|
-
};
|
|
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`
|
|
13738
13716
|
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
// > but it would only find a match with a directory.
|
|
13742
|
-
// > In other words, foo/ will match a directory foo and paths underneath it,
|
|
13743
|
-
// > but will not match a regular file or a symbolic link foo
|
|
13744
|
-
// > (this is consistent with the way how pathspec works in general in Git).
|
|
13745
|
-
// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
|
|
13746
|
-
// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
|
|
13747
|
-
// you could use option `mark: true` with `glob`
|
|
13748
|
-
|
|
13749
|
-
// '`foo/`' should not continue with the '`..`'
|
|
13750
|
-
const REPLACERS = [
|
|
13751
|
-
|
|
13752
|
-
[
|
|
13753
|
-
// remove BOM
|
|
13754
|
-
// TODO:
|
|
13755
|
-
// Other similar zero-width characters?
|
|
13756
|
-
/^\uFEFF/,
|
|
13757
|
-
() => EMPTY
|
|
13758
|
-
],
|
|
13759
|
-
|
|
13760
|
-
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
13761
|
-
[
|
|
13762
|
-
// (a\ ) -> (a )
|
|
13763
|
-
// (a ) -> (a)
|
|
13764
|
-
// (a ) -> (a)
|
|
13765
|
-
// (a \ ) -> (a )
|
|
13766
|
-
/((?:\\\\)*?)(\\?\s+)$/,
|
|
13767
|
-
(_, m1, m2) => m1 + (
|
|
13768
|
-
m2.indexOf('\\') === 0
|
|
13769
|
-
? SPACE
|
|
13770
|
-
: EMPTY
|
|
13771
|
-
)
|
|
13772
|
-
],
|
|
13773
|
-
|
|
13774
|
-
// replace (\ ) with ' '
|
|
13775
|
-
// (\ ) -> ' '
|
|
13776
|
-
// (\\ ) -> '\\ '
|
|
13777
|
-
// (\\\ ) -> '\\ '
|
|
13778
|
-
[
|
|
13779
|
-
/(\\+?)\s/g,
|
|
13780
|
-
(_, m1) => {
|
|
13781
|
-
const {length} = m1;
|
|
13782
|
-
return m1.slice(0, length - length % 2) + SPACE
|
|
13783
|
-
}
|
|
13784
|
-
],
|
|
13785
|
-
|
|
13786
|
-
// Escape metacharacters
|
|
13787
|
-
// which is written down by users but means special for regular expressions.
|
|
13788
|
-
|
|
13789
|
-
// > There are 12 characters with special meanings:
|
|
13790
|
-
// > - the backslash \,
|
|
13791
|
-
// > - the caret ^,
|
|
13792
|
-
// > - the dollar sign $,
|
|
13793
|
-
// > - the period or dot .,
|
|
13794
|
-
// > - the vertical bar or pipe symbol |,
|
|
13795
|
-
// > - the question mark ?,
|
|
13796
|
-
// > - the asterisk or star *,
|
|
13797
|
-
// > - the plus sign +,
|
|
13798
|
-
// > - the opening parenthesis (,
|
|
13799
|
-
// > - the closing parenthesis ),
|
|
13800
|
-
// > - and the opening square bracket [,
|
|
13801
|
-
// > - the opening curly brace {,
|
|
13802
|
-
// > These special characters are often called "metacharacters".
|
|
13803
|
-
[
|
|
13804
|
-
/[\\$.|*+(){^]/g,
|
|
13805
|
-
match => `\\${match}`
|
|
13806
|
-
],
|
|
13807
|
-
|
|
13808
|
-
[
|
|
13809
|
-
// > a question mark (?) matches a single character
|
|
13810
|
-
/(?!\\)\?/g,
|
|
13811
|
-
() => '[^/]'
|
|
13812
|
-
],
|
|
13813
|
-
|
|
13814
|
-
// leading slash
|
|
13815
|
-
[
|
|
13816
|
-
|
|
13817
|
-
// > A leading slash matches the beginning of the pathname.
|
|
13818
|
-
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
13819
|
-
// A leading slash matches the beginning of the pathname
|
|
13820
|
-
/^\//,
|
|
13821
|
-
() => '^'
|
|
13822
|
-
],
|
|
13823
|
-
|
|
13824
|
-
// replace special metacharacter slash after the leading slash
|
|
13825
|
-
[
|
|
13826
|
-
/\//g,
|
|
13827
|
-
() => '\\/'
|
|
13828
|
-
],
|
|
13829
|
-
|
|
13830
|
-
[
|
|
13831
|
-
// > A leading "**" followed by a slash means match in all directories.
|
|
13832
|
-
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
13833
|
-
// > the same as pattern "foo".
|
|
13834
|
-
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
13835
|
-
// > under directory "foo".
|
|
13836
|
-
// Notice that the '*'s have been replaced as '\\*'
|
|
13837
|
-
/^\^*\\\*\\\*\\\//,
|
|
13838
|
-
|
|
13839
|
-
// '**/foo' <-> 'foo'
|
|
13840
|
-
() => '^(?:.*\\/)?'
|
|
13841
|
-
],
|
|
13842
|
-
|
|
13843
|
-
// starting
|
|
13844
|
-
[
|
|
13845
|
-
// there will be no leading '/'
|
|
13846
|
-
// (which has been replaced by section "leading slash")
|
|
13847
|
-
// If starts with '**', adding a '^' to the regular expression also works
|
|
13848
|
-
/^(?=[^^])/,
|
|
13849
|
-
function startingReplacer () {
|
|
13850
|
-
// If has a slash `/` at the beginning or middle
|
|
13851
|
-
return !/\/(?!$)/.test(this)
|
|
13852
|
-
// > Prior to 2.22.1
|
|
13853
|
-
// > If the pattern does not contain a slash /,
|
|
13854
|
-
// > Git treats it as a shell glob pattern
|
|
13855
|
-
// Actually, if there is only a trailing slash,
|
|
13856
|
-
// git also treats it as a shell glob pattern
|
|
13857
|
-
|
|
13858
|
-
// After 2.22.1 (compatible but clearer)
|
|
13859
|
-
// > If there is a separator at the beginning or middle (or both)
|
|
13860
|
-
// > of the pattern, then the pattern is relative to the directory
|
|
13861
|
-
// > level of the particular .gitignore file itself.
|
|
13862
|
-
// > Otherwise the pattern may also match at any level below
|
|
13863
|
-
// > the .gitignore level.
|
|
13864
|
-
? '(?:^|\\/)'
|
|
13865
|
-
|
|
13866
|
-
// > Otherwise, Git treats the pattern as a shell glob suitable for
|
|
13867
|
-
// > consumption by fnmatch(3)
|
|
13868
|
-
: '^'
|
|
13869
|
-
}
|
|
13870
|
-
],
|
|
13871
|
-
|
|
13872
|
-
// two globstars
|
|
13873
|
-
[
|
|
13874
|
-
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
13875
|
-
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
13876
|
-
|
|
13877
|
-
// Zero, one or several directories
|
|
13878
|
-
// should not use '*', or it will be replaced by the next replacer
|
|
13879
|
-
|
|
13880
|
-
// Check if it is not the last `'/**'`
|
|
13881
|
-
(_, index, str) => index + 6 < str.length
|
|
13882
|
-
|
|
13883
|
-
// case: /**/
|
|
13884
|
-
// > A slash followed by two consecutive asterisks then a slash matches
|
|
13885
|
-
// > zero or more directories.
|
|
13886
|
-
// > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
|
|
13887
|
-
// '/**/'
|
|
13888
|
-
? '(?:\\/[^\\/]+)*'
|
|
13889
|
-
|
|
13890
|
-
// case: /**
|
|
13891
|
-
// > A trailing `"/**"` matches everything inside.
|
|
13892
|
-
|
|
13893
|
-
// #21: everything inside but it should not include the current folder
|
|
13894
|
-
: '\\/.+'
|
|
13895
|
-
],
|
|
13896
|
-
|
|
13897
|
-
// normal intermediate wildcards
|
|
13898
|
-
[
|
|
13899
|
-
// Never replace escaped '*'
|
|
13900
|
-
// ignore rule '\*' will match the path '*'
|
|
13901
|
-
|
|
13902
|
-
// 'abc.*/' -> go
|
|
13903
|
-
// 'abc.*' -> skip this rule,
|
|
13904
|
-
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
13905
|
-
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
13906
|
-
|
|
13907
|
-
// '*.js' matches '.js'
|
|
13908
|
-
// '*.js' doesn't match 'abc'
|
|
13909
|
-
(_, p1, p2) => {
|
|
13910
|
-
// 1.
|
|
13911
|
-
// > An asterisk "*" matches anything except a slash.
|
|
13912
|
-
// 2.
|
|
13913
|
-
// > Other consecutive asterisks are considered regular asterisks
|
|
13914
|
-
// > and will match according to the previous rules.
|
|
13915
|
-
const unescaped = p2.replace(/\\\*/g, '[^\\/]*');
|
|
13916
|
-
return p1 + unescaped
|
|
13917
|
-
}
|
|
13918
|
-
],
|
|
13919
|
-
|
|
13920
|
-
[
|
|
13921
|
-
// unescape, revert step 3 except for back slash
|
|
13922
|
-
// For example, if a user escape a '\\*',
|
|
13923
|
-
// after step 3, the result will be '\\\\\\*'
|
|
13924
|
-
/\\\\\\(?=[$.|*+(){^])/g,
|
|
13925
|
-
() => ESCAPE
|
|
13926
|
-
],
|
|
13927
|
-
|
|
13928
|
-
[
|
|
13929
|
-
// '\\\\' -> '\\'
|
|
13930
|
-
/\\\\/g,
|
|
13931
|
-
() => ESCAPE
|
|
13932
|
-
],
|
|
13933
|
-
|
|
13934
|
-
[
|
|
13935
|
-
// > The range notation, e.g. [a-zA-Z],
|
|
13936
|
-
// > can be used to match one of the characters in a range.
|
|
13937
|
-
|
|
13938
|
-
// `\` is escaped by step 3
|
|
13939
|
-
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
13940
|
-
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
|
|
13941
|
-
// '\\[bar]' -> '\\\\[bar\\]'
|
|
13942
|
-
? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
|
|
13943
|
-
: close === ']'
|
|
13944
|
-
? endEscape.length % 2 === 0
|
|
13945
|
-
// A normal case, and it is a range notation
|
|
13946
|
-
// '[bar]'
|
|
13947
|
-
// '[bar\\\\]'
|
|
13948
|
-
? `[${sanitizeRange(range)}${endEscape}]`
|
|
13949
|
-
// Invalid range notaton
|
|
13950
|
-
// '[bar\\]' -> '[bar\\\\]'
|
|
13951
|
-
: '[]'
|
|
13952
|
-
: '[]'
|
|
13953
|
-
],
|
|
13954
|
-
|
|
13955
|
-
// ending
|
|
13956
|
-
[
|
|
13957
|
-
// 'js' will not match 'js.'
|
|
13958
|
-
// 'ab' will not match 'abc'
|
|
13959
|
-
/(?:[^*])$/,
|
|
13960
|
-
|
|
13961
|
-
// WTF!
|
|
13962
|
-
// https://git-scm.com/docs/gitignore
|
|
13963
|
-
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
13964
|
-
// which re-fixes #24, #38
|
|
13965
|
-
|
|
13966
|
-
// > If there is a separator at the end of the pattern then the pattern
|
|
13967
|
-
// > will only match directories, otherwise the pattern can match both
|
|
13968
|
-
// > files and directories.
|
|
13969
|
-
|
|
13970
|
-
// 'js*' will not match 'a.js'
|
|
13971
|
-
// 'js/' will not match 'a.js'
|
|
13972
|
-
// 'js' will match 'a.js' and 'a.js/'
|
|
13973
|
-
match => /\/$/.test(match)
|
|
13974
|
-
// foo/ will not match 'foo'
|
|
13975
|
-
? `${match}$`
|
|
13976
|
-
// foo matches 'foo' and 'foo/'
|
|
13977
|
-
: `${match}(?=$|\\/$)`
|
|
13978
|
-
],
|
|
13979
|
-
|
|
13980
|
-
// trailing wildcard
|
|
13981
|
-
[
|
|
13982
|
-
/(\^|\\\/)?\\\*$/,
|
|
13983
|
-
(_, p1) => {
|
|
13984
|
-
const prefix = p1
|
|
13985
|
-
// '\^':
|
|
13986
|
-
// '/*' does not match EMPTY
|
|
13987
|
-
// '/*' does not match everything
|
|
13988
|
-
|
|
13989
|
-
// '\\\/':
|
|
13990
|
-
// 'abc/*' does not match 'abc/'
|
|
13991
|
-
? `${p1}[^/]+`
|
|
13992
|
-
|
|
13993
|
-
// 'a*' matches 'a'
|
|
13994
|
-
// 'a*' matches 'aa'
|
|
13995
|
-
: '[^/]*';
|
|
13996
|
-
|
|
13997
|
-
return `${prefix}(?=$|\\/$)`
|
|
13998
|
-
}
|
|
13999
|
-
],
|
|
14000
|
-
];
|
|
14001
|
-
|
|
14002
|
-
// A simple cache, because an ignore rule only has only one certain meaning
|
|
14003
|
-
const regexCache = Object.create(null);
|
|
14004
|
-
|
|
14005
|
-
// @param {pattern}
|
|
14006
|
-
const makeRegex = (pattern, ignoreCase) => {
|
|
14007
|
-
let source = regexCache[pattern];
|
|
14008
|
-
|
|
14009
|
-
if (!source) {
|
|
14010
|
-
source = REPLACERS.reduce(
|
|
14011
|
-
(prev, [matcher, replacer]) =>
|
|
14012
|
-
prev.replace(matcher, replacer.bind(pattern)),
|
|
14013
|
-
pattern
|
|
14014
|
-
);
|
|
14015
|
-
regexCache[pattern] = source;
|
|
14016
|
-
}
|
|
14017
|
-
|
|
14018
|
-
return ignoreCase
|
|
14019
|
-
? new RegExp(source, 'i')
|
|
14020
|
-
: new RegExp(source)
|
|
14021
|
-
};
|
|
13717
|
+
// '`foo/`' should not continue with the '`..`'
|
|
13718
|
+
const REPLACERS = [
|
|
14022
13719
|
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
// > A line starting with # serves as a comment.
|
|
14032
|
-
&& pattern.indexOf('#') !== 0;
|
|
14033
|
-
|
|
14034
|
-
const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF);
|
|
14035
|
-
|
|
14036
|
-
class IgnoreRule {
|
|
14037
|
-
constructor (
|
|
14038
|
-
origin,
|
|
14039
|
-
pattern,
|
|
14040
|
-
negative,
|
|
14041
|
-
regex
|
|
14042
|
-
) {
|
|
14043
|
-
this.origin = origin;
|
|
14044
|
-
this.pattern = pattern;
|
|
14045
|
-
this.negative = negative;
|
|
14046
|
-
this.regex = regex;
|
|
14047
|
-
}
|
|
14048
|
-
}
|
|
13720
|
+
[
|
|
13721
|
+
// Remove BOM
|
|
13722
|
+
// TODO:
|
|
13723
|
+
// Other similar zero-width characters?
|
|
13724
|
+
/^\uFEFF/,
|
|
13725
|
+
() => EMPTY
|
|
13726
|
+
],
|
|
14049
13727
|
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
|
|
14062
|
-
|
|
14063
|
-
.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
|
|
14064
|
-
// > Put a backslash ("\") in front of the first hash for patterns that
|
|
14065
|
-
// > begin with a hash.
|
|
14066
|
-
.replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
|
|
14067
|
-
|
|
14068
|
-
const regex = makeRegex(pattern, ignoreCase);
|
|
14069
|
-
|
|
14070
|
-
return new IgnoreRule(
|
|
14071
|
-
origin,
|
|
14072
|
-
pattern,
|
|
14073
|
-
negative,
|
|
14074
|
-
regex
|
|
14075
|
-
)
|
|
14076
|
-
};
|
|
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
|
+
],
|
|
14077
13741
|
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
|
|
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
|
+
],
|
|
14081
13753
|
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
return true
|
|
14105
|
-
};
|
|
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
|
+
],
|
|
14106
13775
|
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
class Ignore {
|
|
14113
|
-
constructor ({
|
|
14114
|
-
ignorecase = true,
|
|
14115
|
-
ignoreCase = ignorecase,
|
|
14116
|
-
allowRelativePaths = false
|
|
14117
|
-
} = {}) {
|
|
14118
|
-
define(this, KEY_IGNORE, true);
|
|
14119
|
-
|
|
14120
|
-
this._rules = [];
|
|
14121
|
-
this._ignoreCase = ignoreCase;
|
|
14122
|
-
this._allowRelativePaths = allowRelativePaths;
|
|
14123
|
-
this._initCache();
|
|
14124
|
-
}
|
|
14125
|
-
|
|
14126
|
-
_initCache () {
|
|
14127
|
-
this._ignoreCache = Object.create(null);
|
|
14128
|
-
this._testCache = Object.create(null);
|
|
14129
|
-
}
|
|
14130
|
-
|
|
14131
|
-
_addPattern (pattern) {
|
|
14132
|
-
// #32
|
|
14133
|
-
if (pattern && pattern[KEY_IGNORE]) {
|
|
14134
|
-
this._rules = this._rules.concat(pattern._rules);
|
|
14135
|
-
this._added = true;
|
|
14136
|
-
return
|
|
14137
|
-
}
|
|
14138
|
-
|
|
14139
|
-
if (checkPattern(pattern)) {
|
|
14140
|
-
const rule = createRule(pattern, this._ignoreCase);
|
|
14141
|
-
this._added = true;
|
|
14142
|
-
this._rules.push(rule);
|
|
14143
|
-
}
|
|
14144
|
-
}
|
|
14145
|
-
|
|
14146
|
-
// @param {Array<string> | string | Ignore} pattern
|
|
14147
|
-
add (pattern) {
|
|
14148
|
-
this._added = false;
|
|
14149
|
-
|
|
14150
|
-
makeArray(
|
|
14151
|
-
isString(pattern)
|
|
14152
|
-
? splitPattern(pattern)
|
|
14153
|
-
: pattern
|
|
14154
|
-
).forEach(this._addPattern, this);
|
|
14155
|
-
|
|
14156
|
-
// Some rules have just added to the ignore,
|
|
14157
|
-
// making the behavior changed.
|
|
14158
|
-
if (this._added) {
|
|
14159
|
-
this._initCache();
|
|
14160
|
-
}
|
|
14161
|
-
|
|
14162
|
-
return this
|
|
14163
|
-
}
|
|
14164
|
-
|
|
14165
|
-
// legacy
|
|
14166
|
-
addPattern (pattern) {
|
|
14167
|
-
return this.add(pattern)
|
|
14168
|
-
}
|
|
14169
|
-
|
|
14170
|
-
// | ignored : unignored
|
|
14171
|
-
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
|
14172
|
-
// -------- | ------- | ------- | ------- | --------
|
|
14173
|
-
// 0 | TEST | TEST | SKIP | X
|
|
14174
|
-
// 1 | TESTIF | SKIP | TEST | X
|
|
14175
|
-
|
|
14176
|
-
// - SKIP: always skip
|
|
14177
|
-
// - TEST: always test
|
|
14178
|
-
// - TESTIF: only test if checkUnignored
|
|
14179
|
-
// - X: that never happen
|
|
14180
|
-
|
|
14181
|
-
// @param {boolean} whether should check if the path is unignored,
|
|
14182
|
-
// setting `checkUnignored` to `false` could reduce additional
|
|
14183
|
-
// path matching.
|
|
14184
|
-
|
|
14185
|
-
// @returns {TestResult} true if a file is ignored
|
|
14186
|
-
_testOne (path, checkUnignored) {
|
|
14187
|
-
let ignored = false;
|
|
14188
|
-
let unignored = false;
|
|
14189
|
-
|
|
14190
|
-
this._rules.forEach(rule => {
|
|
14191
|
-
const {negative} = rule;
|
|
14192
|
-
if (
|
|
14193
|
-
unignored === negative && ignored !== unignored
|
|
14194
|
-
|| negative && !ignored && !unignored && !checkUnignored
|
|
14195
|
-
) {
|
|
14196
|
-
return
|
|
14197
|
-
}
|
|
14198
|
-
|
|
14199
|
-
const matched = rule.regex.test(path);
|
|
14200
|
-
|
|
14201
|
-
if (matched) {
|
|
14202
|
-
ignored = !negative;
|
|
14203
|
-
unignored = negative;
|
|
14204
|
-
}
|
|
14205
|
-
});
|
|
14206
|
-
|
|
14207
|
-
return {
|
|
14208
|
-
ignored,
|
|
14209
|
-
unignored
|
|
14210
|
-
}
|
|
14211
|
-
}
|
|
14212
|
-
|
|
14213
|
-
// @returns {TestResult}
|
|
14214
|
-
_test (originalPath, cache, checkUnignored, slices) {
|
|
14215
|
-
const path = originalPath
|
|
14216
|
-
// Supports nullable path
|
|
14217
|
-
&& checkPath.convert(originalPath);
|
|
14218
|
-
|
|
14219
|
-
checkPath(
|
|
14220
|
-
path,
|
|
14221
|
-
originalPath,
|
|
14222
|
-
this._allowRelativePaths
|
|
14223
|
-
? RETURN_FALSE
|
|
14224
|
-
: throwError
|
|
14225
|
-
);
|
|
14226
|
-
|
|
14227
|
-
return this._t(path, cache, checkUnignored, slices)
|
|
14228
|
-
}
|
|
14229
|
-
|
|
14230
|
-
_t (path, cache, checkUnignored, slices) {
|
|
14231
|
-
if (path in cache) {
|
|
14232
|
-
return cache[path]
|
|
14233
|
-
}
|
|
14234
|
-
|
|
14235
|
-
if (!slices) {
|
|
14236
|
-
// path/to/a.js
|
|
14237
|
-
// ['path', 'to', 'a.js']
|
|
14238
|
-
slices = path.split(SLASH);
|
|
14239
|
-
}
|
|
14240
|
-
|
|
14241
|
-
slices.pop();
|
|
14242
|
-
|
|
14243
|
-
// If the path has no parent directory, just test it
|
|
14244
|
-
if (!slices.length) {
|
|
14245
|
-
return cache[path] = this._testOne(path, checkUnignored)
|
|
14246
|
-
}
|
|
14247
|
-
|
|
14248
|
-
const parent = this._t(
|
|
14249
|
-
slices.join(SLASH) + SLASH,
|
|
14250
|
-
cache,
|
|
14251
|
-
checkUnignored,
|
|
14252
|
-
slices
|
|
14253
|
-
);
|
|
14254
|
-
|
|
14255
|
-
// If the path contains a parent directory, check the parent first
|
|
14256
|
-
return cache[path] = parent.ignored
|
|
14257
|
-
// > It is not possible to re-include a file if a parent directory of
|
|
14258
|
-
// > that file is excluded.
|
|
14259
|
-
? parent
|
|
14260
|
-
: this._testOne(path, checkUnignored)
|
|
14261
|
-
}
|
|
14262
|
-
|
|
14263
|
-
ignores (path) {
|
|
14264
|
-
return this._test(path, this._ignoreCache, false).ignored
|
|
14265
|
-
}
|
|
14266
|
-
|
|
14267
|
-
createFilter () {
|
|
14268
|
-
return path => !this.ignores(path)
|
|
14269
|
-
}
|
|
14270
|
-
|
|
14271
|
-
filter (paths) {
|
|
14272
|
-
return makeArray(paths).filter(this.createFilter())
|
|
14273
|
-
}
|
|
14274
|
-
|
|
14275
|
-
// @returns {TestResult}
|
|
14276
|
-
test (path) {
|
|
14277
|
-
return this._test(path, this._testCache, true)
|
|
14278
|
-
}
|
|
14279
|
-
}
|
|
13776
|
+
[
|
|
13777
|
+
// > a question mark (?) matches a single character
|
|
13778
|
+
/(?!\\)\?/g,
|
|
13779
|
+
() => '[^/]'
|
|
13780
|
+
],
|
|
14280
13781
|
|
|
14281
|
-
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14285
|
-
|
|
14286
|
-
|
|
14287
|
-
|
|
14288
|
-
|
|
14289
|
-
|
|
14290
|
-
|
|
14291
|
-
|
|
14292
|
-
|
|
14293
|
-
|
|
14294
|
-
|
|
14295
|
-
|
|
14296
|
-
|
|
14297
|
-
|
|
14298
|
-
|
|
14299
|
-
|
|
14300
|
-
|
|
14301
|
-
|
|
14302
|
-
|
|
14303
|
-
|
|
14304
|
-
|
|
14305
|
-
|
|
14306
|
-
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
|
|
14310
|
-
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
|
|
14314
|
-
|
|
14315
|
-
|
|
14316
|
-
|
|
14317
|
-
|
|
14318
|
-
|
|
14319
|
-
|
|
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
|
+
? '(?:\\/[^\\/]+)*'
|
|
13857
|
+
|
|
13858
|
+
// case: /**
|
|
13859
|
+
// > A trailing `"/**"` matches everything inside.
|
|
13860
|
+
|
|
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
|
+
}
|
|
14122
|
+
|
|
14123
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
14124
|
+
add (pattern) {
|
|
14125
|
+
this._added = false;
|
|
14126
|
+
|
|
14127
|
+
makeArray(
|
|
14128
|
+
isString(pattern)
|
|
14129
|
+
? splitPattern(pattern)
|
|
14130
|
+
: pattern
|
|
14131
|
+
).forEach(this._add, this);
|
|
14132
|
+
|
|
14133
|
+
return this._added
|
|
14134
|
+
}
|
|
14135
|
+
|
|
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`
|
|
14142
|
+
|
|
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;
|
|
14148
|
+
|
|
14149
|
+
this._rules.forEach(rule => {
|
|
14150
|
+
const {negative} = rule;
|
|
14151
|
+
|
|
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
|
|
14158
|
+
|
|
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
|
+
}
|
|
14169
|
+
|
|
14170
|
+
const matched = rule[mode].test(path);
|
|
14171
|
+
|
|
14172
|
+
if (!matched) {
|
|
14173
|
+
return
|
|
14174
|
+
}
|
|
14175
|
+
|
|
14176
|
+
ignored = !negative;
|
|
14177
|
+
unignored = negative;
|
|
14178
|
+
|
|
14179
|
+
matchedRule = negative
|
|
14180
|
+
? UNDEFINED
|
|
14181
|
+
: rule;
|
|
14182
|
+
});
|
|
14183
|
+
|
|
14184
|
+
const ret = {
|
|
14185
|
+
ignored,
|
|
14186
|
+
unignored
|
|
14187
|
+
};
|
|
14188
|
+
|
|
14189
|
+
if (matchedRule) {
|
|
14190
|
+
ret.rule = matchedRule;
|
|
14191
|
+
}
|
|
14192
|
+
|
|
14193
|
+
return ret
|
|
14194
|
+
}
|
|
14195
|
+
}
|
|
14196
|
+
|
|
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
|
+
}
|
|
14222
|
+
|
|
14223
|
+
return true
|
|
14224
|
+
};
|
|
14225
|
+
|
|
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
|
+
}
|
|
14320
14376
|
}
|
|
14321
14377
|
|
|
14322
|
-
|
|
14323
|
-
|
|
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
|
+
}
|
|
14324
14412
|
|
|
14325
14413
|
function importFunction(id) {
|
|
14326
14414
|
return import(id);
|
|
@@ -14392,8 +14480,8 @@ exports.defineConfig = defineConfig;
|
|
|
14392
14480
|
exports.definePlugin = definePlugin;
|
|
14393
14481
|
exports.ensureError = ensureError;
|
|
14394
14482
|
exports.esmResolver = esmResolver;
|
|
14483
|
+
exports.factory = factory;
|
|
14395
14484
|
exports.getFormatter = getFormatter;
|
|
14396
|
-
exports.ignore = ignore;
|
|
14397
14485
|
exports.isThenable = isThenable;
|
|
14398
14486
|
exports.keywordPatternMatcher = keywordPatternMatcher;
|
|
14399
14487
|
exports.name = name;
|