html-validate 10.14.0 → 10.16.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/browser.js +1 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +50 -4
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core-browser.js +60 -11
- package/dist/cjs/core-browser.js.map +1 -1
- package/dist/cjs/core-nodejs.js +60 -11
- package/dist/cjs/core-nodejs.js.map +1 -1
- package/dist/cjs/core.js +353 -204
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +9 -0
- package/dist/cjs/elements.js.map +1 -1
- package/dist/cjs/html-validate.js +4 -1
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/browser.js +1 -1
- package/dist/esm/cli.js +51 -5
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/core-browser.js +61 -12
- package/dist/esm/core-browser.js.map +1 -1
- package/dist/esm/core-nodejs.js +61 -12
- package/dist/esm/core-nodejs.js.map +1 -1
- package/dist/esm/core.js +353 -205
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/elements.js +9 -0
- package/dist/esm/elements.js.map +1 -1
- package/dist/esm/html-validate.js +5 -2
- package/dist/esm/html-validate.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/matcher-utils.js +1 -1
- package/dist/esm/matchers.js +1 -1
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/index.d.ts +15 -0
- package/package.json +1 -1
package/dist/esm/core.js
CHANGED
|
@@ -1195,9 +1195,6 @@ function clone(value) {
|
|
|
1195
1195
|
return JSON.parse(JSON.stringify(value));
|
|
1196
1196
|
}
|
|
1197
1197
|
}
|
|
1198
|
-
function overwriteMerge$1(_a, b) {
|
|
1199
|
-
return b;
|
|
1200
|
-
}
|
|
1201
1198
|
class MetaTable {
|
|
1202
1199
|
elements;
|
|
1203
1200
|
schema;
|
|
@@ -1370,15 +1367,22 @@ class MetaTable {
|
|
|
1370
1367
|
}
|
|
1371
1368
|
}
|
|
1372
1369
|
mergeElement(a, b) {
|
|
1373
|
-
const merged =
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1370
|
+
const merged = { ...a, ...b };
|
|
1371
|
+
const mergedAttrs = {
|
|
1372
|
+
...a.attributes,
|
|
1373
|
+
...b.attributes
|
|
1374
|
+
};
|
|
1375
|
+
for (const [name, attr] of Object.entries(mergedAttrs)) {
|
|
1376
|
+
if (attr.delete) {
|
|
1377
|
+
delete mergedAttrs[name];
|
|
1378
|
+
} else {
|
|
1379
|
+
delete attr.delete;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
merged.attributes = mergedAttrs;
|
|
1383
|
+
if (a.aria) {
|
|
1384
|
+
merged.aria = { ...a.aria, ...b.aria };
|
|
1385
|
+
}
|
|
1382
1386
|
return merged;
|
|
1383
1387
|
}
|
|
1384
1388
|
/**
|
|
@@ -1959,71 +1963,68 @@ function factory(name, context) {
|
|
|
1959
1963
|
function stripslashes(value) {
|
|
1960
1964
|
return value.replaceAll(/\\(.)/g, "$1");
|
|
1961
1965
|
}
|
|
1962
|
-
|
|
1966
|
+
function createClassCondition(classname) {
|
|
1967
|
+
return {
|
|
1968
|
+
kind: "class",
|
|
1969
|
+
classname,
|
|
1970
|
+
match(node) {
|
|
1971
|
+
return node.classList.contains(classname);
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1963
1974
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
}
|
|
1975
|
+
function createIdCondition(raw) {
|
|
1976
|
+
const id = stripslashes(raw);
|
|
1977
|
+
return {
|
|
1978
|
+
kind: "id",
|
|
1979
|
+
id,
|
|
1980
|
+
match(node) {
|
|
1981
|
+
return node.id === id;
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1973
1984
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1985
|
+
function createAttributeCondition(attr) {
|
|
1986
|
+
const match = /^(.+?)(?:([$*^|~]?=)"([^"]+?)")?$/.exec(attr);
|
|
1987
|
+
const key = match[1];
|
|
1988
|
+
const op = match[2];
|
|
1989
|
+
const rawValue = match[3];
|
|
1990
|
+
const value = typeof rawValue === "string" ? stripslashes(rawValue) : rawValue;
|
|
1991
|
+
return {
|
|
1992
|
+
kind: "attribute",
|
|
1993
|
+
key,
|
|
1994
|
+
op,
|
|
1995
|
+
value,
|
|
1996
|
+
match(node) {
|
|
1997
|
+
const attrs = node.getAttribute(key, true);
|
|
1998
|
+
return attrs.some((cur) => {
|
|
1999
|
+
switch (op) {
|
|
2000
|
+
case void 0:
|
|
2001
|
+
return true;
|
|
2002
|
+
/* attribute exists */
|
|
2003
|
+
case "=":
|
|
2004
|
+
return cur.value === value;
|
|
2005
|
+
default:
|
|
2006
|
+
throw new Error(`Attribute selector operator ${op} is not implemented yet`);
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
1983
2011
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
constructor(attr) {
|
|
1989
|
-
super();
|
|
1990
|
-
const [, key, op, value] = /^(.+?)(?:([$*^|~]?=)"([^"]+?)")?$/.exec(attr);
|
|
1991
|
-
this.key = key;
|
|
1992
|
-
this.op = op;
|
|
1993
|
-
this.value = typeof value === "string" ? stripslashes(value) : value;
|
|
1994
|
-
}
|
|
1995
|
-
match(node) {
|
|
1996
|
-
const attr = node.getAttribute(this.key, true);
|
|
1997
|
-
return attr.some((cur) => {
|
|
1998
|
-
switch (this.op) {
|
|
1999
|
-
case void 0:
|
|
2000
|
-
return true;
|
|
2001
|
-
/* attribute exists */
|
|
2002
|
-
case "=":
|
|
2003
|
-
return cur.value === this.value;
|
|
2004
|
-
default:
|
|
2005
|
-
throw new Error(`Attribute selector operator ${this.op} is not implemented yet`);
|
|
2006
|
-
}
|
|
2007
|
-
});
|
|
2012
|
+
function createPseudoClassCondition(pseudoclass, context) {
|
|
2013
|
+
const match = /^([^(]+)(?:\((.*)\))?$/.exec(pseudoclass);
|
|
2014
|
+
if (!match) {
|
|
2015
|
+
throw new Error(`Missing pseudo-class after colon in selector pattern "${context}"`);
|
|
2008
2016
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2017
|
+
const name = match[1];
|
|
2018
|
+
const args = match[2];
|
|
2019
|
+
return {
|
|
2020
|
+
kind: "pseudo",
|
|
2021
|
+
name,
|
|
2022
|
+
args,
|
|
2023
|
+
match(node, selectorContext) {
|
|
2024
|
+
const fn = factory(name, selectorContext);
|
|
2025
|
+
return fn(node, args);
|
|
2018
2026
|
}
|
|
2019
|
-
|
|
2020
|
-
this.name = name;
|
|
2021
|
-
this.args = args;
|
|
2022
|
-
}
|
|
2023
|
-
match(node, context) {
|
|
2024
|
-
const fn = factory(this.name, context);
|
|
2025
|
-
return fn(node, this.args);
|
|
2026
|
-
}
|
|
2027
|
+
};
|
|
2027
2028
|
}
|
|
2028
2029
|
|
|
2029
2030
|
function isDelimiter(ch) {
|
|
@@ -2098,37 +2099,84 @@ class Compound {
|
|
|
2098
2099
|
createCondition(pattern) {
|
|
2099
2100
|
switch (pattern[0]) {
|
|
2100
2101
|
case ".":
|
|
2101
|
-
return
|
|
2102
|
+
return createClassCondition(pattern.slice(1));
|
|
2102
2103
|
case "#":
|
|
2103
|
-
return
|
|
2104
|
+
return createIdCondition(pattern.slice(1));
|
|
2104
2105
|
case "[":
|
|
2105
|
-
return
|
|
2106
|
+
return createAttributeCondition(pattern.slice(1, -1));
|
|
2106
2107
|
case ":":
|
|
2107
|
-
return
|
|
2108
|
+
return createPseudoClassCondition(pattern.slice(1), this.selector);
|
|
2108
2109
|
default:
|
|
2109
2110
|
throw new Error(`Failed to create selector condition for "${pattern}"`);
|
|
2110
2111
|
}
|
|
2111
2112
|
}
|
|
2112
2113
|
}
|
|
2113
2114
|
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
if (codepoints[ch]) {
|
|
2122
|
-
return codepoints[ch];
|
|
2123
|
-
} else {
|
|
2124
|
-
return `\\${ch}`;
|
|
2115
|
+
const escapedCodepoints = /* @__PURE__ */ new Set(["9", "a", "d"]);
|
|
2116
|
+
function* splitSelectorElements(selector) {
|
|
2117
|
+
let begin = 0;
|
|
2118
|
+
let end = 0;
|
|
2119
|
+
function initialState(ch, p) {
|
|
2120
|
+
if (ch === "\\") {
|
|
2121
|
+
return 1 /* ESCAPED */;
|
|
2125
2122
|
}
|
|
2126
|
-
|
|
2123
|
+
if (ch === " ") {
|
|
2124
|
+
end = p;
|
|
2125
|
+
return 2 /* WHITESPACE */;
|
|
2126
|
+
}
|
|
2127
|
+
return 0 /* INITIAL */;
|
|
2128
|
+
}
|
|
2129
|
+
function escapedState(ch) {
|
|
2130
|
+
if (escapedCodepoints.has(ch)) {
|
|
2131
|
+
return 1 /* ESCAPED */;
|
|
2132
|
+
}
|
|
2133
|
+
return 0 /* INITIAL */;
|
|
2134
|
+
}
|
|
2135
|
+
function* whitespaceState(ch, p) {
|
|
2136
|
+
if (ch === " ") {
|
|
2137
|
+
return 2 /* WHITESPACE */;
|
|
2138
|
+
}
|
|
2139
|
+
yield selector.slice(begin, end);
|
|
2140
|
+
begin = p;
|
|
2141
|
+
end = p;
|
|
2142
|
+
return 0 /* INITIAL */;
|
|
2143
|
+
}
|
|
2144
|
+
let state = 0 /* INITIAL */;
|
|
2145
|
+
for (let p = 0; p < selector.length; p++) {
|
|
2146
|
+
const ch = selector[p];
|
|
2147
|
+
switch (state) {
|
|
2148
|
+
case 0 /* INITIAL */:
|
|
2149
|
+
state = initialState(ch, p);
|
|
2150
|
+
break;
|
|
2151
|
+
case 1 /* ESCAPED */:
|
|
2152
|
+
state = escapedState(ch);
|
|
2153
|
+
break;
|
|
2154
|
+
case 2 /* WHITESPACE */:
|
|
2155
|
+
state = yield* whitespaceState(ch, p);
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
if (begin !== selector.length) {
|
|
2160
|
+
yield selector.slice(begin);
|
|
2161
|
+
}
|
|
2127
2162
|
}
|
|
2128
2163
|
|
|
2129
|
-
function
|
|
2130
|
-
const
|
|
2131
|
-
|
|
2164
|
+
function unescapeCodepoint(value) {
|
|
2165
|
+
const replacement = {
|
|
2166
|
+
"\\9 ": " ",
|
|
2167
|
+
"\\a ": "\n",
|
|
2168
|
+
"\\d ": "\r"
|
|
2169
|
+
};
|
|
2170
|
+
return value.replaceAll(
|
|
2171
|
+
/(\\[9ad] )/g,
|
|
2172
|
+
(_, codepoint) => replacement[codepoint]
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
function getCompounds(selector) {
|
|
2176
|
+
selector = selector.replaceAll(/([+>~]) /g, "$1");
|
|
2177
|
+
return Array.from(splitSelectorElements(selector), (element) => {
|
|
2178
|
+
return new Compound(unescapeCodepoint(element));
|
|
2179
|
+
});
|
|
2132
2180
|
}
|
|
2133
2181
|
|
|
2134
2182
|
function* ancestors$1(element) {
|
|
@@ -2193,70 +2241,16 @@ function matchElement(element, compounds, context) {
|
|
|
2193
2241
|
return false;
|
|
2194
2242
|
}
|
|
2195
2243
|
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
function initialState(ch, p) {
|
|
2201
|
-
if (ch === "\\") {
|
|
2202
|
-
return 1 /* ESCAPED */;
|
|
2203
|
-
}
|
|
2204
|
-
if (ch === " ") {
|
|
2205
|
-
end = p;
|
|
2206
|
-
return 2 /* WHITESPACE */;
|
|
2207
|
-
}
|
|
2208
|
-
return 0 /* INITIAL */;
|
|
2244
|
+
class ComplexSelector {
|
|
2245
|
+
compounds;
|
|
2246
|
+
constructor(compounds) {
|
|
2247
|
+
this.compounds = compounds;
|
|
2209
2248
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
return 1 /* ESCAPED */;
|
|
2213
|
-
}
|
|
2214
|
-
return 0 /* INITIAL */;
|
|
2249
|
+
static fromString(selector) {
|
|
2250
|
+
return new ComplexSelector(getCompounds(selector));
|
|
2215
2251
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
return 2 /* WHITESPACE */;
|
|
2219
|
-
}
|
|
2220
|
-
yield selector.slice(begin, end);
|
|
2221
|
-
begin = p;
|
|
2222
|
-
end = p;
|
|
2223
|
-
return 0 /* INITIAL */;
|
|
2224
|
-
}
|
|
2225
|
-
let state = 0 /* INITIAL */;
|
|
2226
|
-
for (let p = 0; p < selector.length; p++) {
|
|
2227
|
-
const ch = selector[p];
|
|
2228
|
-
switch (state) {
|
|
2229
|
-
case 0 /* INITIAL */:
|
|
2230
|
-
state = initialState(ch, p);
|
|
2231
|
-
break;
|
|
2232
|
-
case 1 /* ESCAPED */:
|
|
2233
|
-
state = escapedState(ch);
|
|
2234
|
-
break;
|
|
2235
|
-
case 2 /* WHITESPACE */:
|
|
2236
|
-
state = yield* whitespaceState(ch, p);
|
|
2237
|
-
break;
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
if (begin !== selector.length) {
|
|
2241
|
-
yield selector.slice(begin);
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
function unescapeCodepoint(value) {
|
|
2246
|
-
const replacement = {
|
|
2247
|
-
"\\9 ": " ",
|
|
2248
|
-
"\\a ": "\n",
|
|
2249
|
-
"\\d ": "\r"
|
|
2250
|
-
};
|
|
2251
|
-
return value.replaceAll(
|
|
2252
|
-
/(\\[9ad] )/g,
|
|
2253
|
-
(_, codepoint) => replacement[codepoint]
|
|
2254
|
-
);
|
|
2255
|
-
}
|
|
2256
|
-
class Selector {
|
|
2257
|
-
pattern;
|
|
2258
|
-
constructor(selector) {
|
|
2259
|
-
this.pattern = Selector.parse(selector);
|
|
2252
|
+
static fromCompounds(compounds) {
|
|
2253
|
+
return new ComplexSelector(compounds);
|
|
2260
2254
|
}
|
|
2261
2255
|
/**
|
|
2262
2256
|
* Match this selector against a HtmlElement.
|
|
@@ -2273,15 +2267,15 @@ class Selector {
|
|
|
2273
2267
|
*/
|
|
2274
2268
|
matchElement(element) {
|
|
2275
2269
|
const context = { scope: null };
|
|
2276
|
-
return matchElement(element, this.
|
|
2270
|
+
return matchElement(element, this.compounds, context);
|
|
2277
2271
|
}
|
|
2278
2272
|
*matchInternal(root, level, context) {
|
|
2279
|
-
if (level >= this.
|
|
2273
|
+
if (level >= this.compounds.length) {
|
|
2280
2274
|
yield root;
|
|
2281
2275
|
return;
|
|
2282
2276
|
}
|
|
2283
|
-
const pattern = this.
|
|
2284
|
-
const matches =
|
|
2277
|
+
const pattern = this.compounds[level];
|
|
2278
|
+
const matches = ComplexSelector.findCandidates(root, pattern);
|
|
2285
2279
|
for (const node of matches) {
|
|
2286
2280
|
if (!pattern.match(node, context)) {
|
|
2287
2281
|
continue;
|
|
@@ -2289,12 +2283,6 @@ class Selector {
|
|
|
2289
2283
|
yield* this.matchInternal(node, level + 1, context);
|
|
2290
2284
|
}
|
|
2291
2285
|
}
|
|
2292
|
-
static parse(selector) {
|
|
2293
|
-
selector = selector.replaceAll(/([+>~]) /g, "$1");
|
|
2294
|
-
return Array.from(splitSelectorElements(selector), (element) => {
|
|
2295
|
-
return new Compound(unescapeCodepoint(element));
|
|
2296
|
-
});
|
|
2297
|
-
}
|
|
2298
2286
|
static findCandidates(root, pattern) {
|
|
2299
2287
|
switch (pattern.combinator) {
|
|
2300
2288
|
case Combinator.DESCENDANT:
|
|
@@ -2302,9 +2290,9 @@ class Selector {
|
|
|
2302
2290
|
case Combinator.CHILD:
|
|
2303
2291
|
return root.childElements.filter((node) => node.is(pattern.tagName));
|
|
2304
2292
|
case Combinator.ADJACENT_SIBLING:
|
|
2305
|
-
return
|
|
2293
|
+
return ComplexSelector.findAdjacentSibling(root);
|
|
2306
2294
|
case Combinator.GENERAL_SIBLING:
|
|
2307
|
-
return
|
|
2295
|
+
return ComplexSelector.findGeneralSibling(root);
|
|
2308
2296
|
case Combinator.SCOPE:
|
|
2309
2297
|
return [root];
|
|
2310
2298
|
}
|
|
@@ -2316,7 +2304,7 @@ class Selector {
|
|
|
2316
2304
|
adjacent = false;
|
|
2317
2305
|
return true;
|
|
2318
2306
|
}
|
|
2319
|
-
if (cur
|
|
2307
|
+
if (cur.isSameNode(node)) {
|
|
2320
2308
|
adjacent = true;
|
|
2321
2309
|
}
|
|
2322
2310
|
return false;
|
|
@@ -2328,7 +2316,7 @@ class Selector {
|
|
|
2328
2316
|
if (after) {
|
|
2329
2317
|
return true;
|
|
2330
2318
|
}
|
|
2331
|
-
if (cur
|
|
2319
|
+
if (cur.isSameNode(node)) {
|
|
2332
2320
|
after = true;
|
|
2333
2321
|
}
|
|
2334
2322
|
return false;
|
|
@@ -2336,6 +2324,31 @@ class Selector {
|
|
|
2336
2324
|
}
|
|
2337
2325
|
}
|
|
2338
2326
|
|
|
2327
|
+
const codepoints = {
|
|
2328
|
+
" ": "\\9 ",
|
|
2329
|
+
"\n": "\\a ",
|
|
2330
|
+
"\r": "\\d "
|
|
2331
|
+
};
|
|
2332
|
+
function escapeSelectorComponent(text) {
|
|
2333
|
+
return text.toString().replaceAll(/([\t\n\r]|[^\w-])/gi, (_, ch) => {
|
|
2334
|
+
if (codepoints[ch]) {
|
|
2335
|
+
return codepoints[ch];
|
|
2336
|
+
} else {
|
|
2337
|
+
return `\\${ch}`;
|
|
2338
|
+
}
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
function generateIdSelector(id) {
|
|
2343
|
+
const escaped = escapeSelectorComponent(id);
|
|
2344
|
+
return /^\d/.test(escaped) ? `[id="${escaped}"]` : `#${escaped}`;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
function parseSelector(selector) {
|
|
2348
|
+
const compounds = getCompounds(selector);
|
|
2349
|
+
return ComplexSelector.fromCompounds(compounds);
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2339
2352
|
const TEXT_NODE_NAME = "#text";
|
|
2340
2353
|
function isTextNode(node) {
|
|
2341
2354
|
return node?.nodeType === NodeType.TEXT_NODE;
|
|
@@ -2649,7 +2662,7 @@ class HtmlElement extends DOMNode {
|
|
|
2649
2662
|
*/
|
|
2650
2663
|
matches(selectorList) {
|
|
2651
2664
|
return selectorList.split(",").some((it) => {
|
|
2652
|
-
const selector =
|
|
2665
|
+
const selector = parseSelector(it.trim());
|
|
2653
2666
|
return selector.matchElement(this);
|
|
2654
2667
|
});
|
|
2655
2668
|
}
|
|
@@ -2882,9 +2895,9 @@ class HtmlElement extends DOMNode {
|
|
|
2882
2895
|
if (!selectorList) {
|
|
2883
2896
|
return;
|
|
2884
2897
|
}
|
|
2885
|
-
for (const
|
|
2886
|
-
const
|
|
2887
|
-
yield*
|
|
2898
|
+
for (const selectorString of selectorList.split(/(?<!\\),\s*/)) {
|
|
2899
|
+
const selector = parseSelector(selectorString);
|
|
2900
|
+
yield* selector.match(this);
|
|
2888
2901
|
}
|
|
2889
2902
|
}
|
|
2890
2903
|
/**
|
|
@@ -3737,6 +3750,7 @@ class Rule {
|
|
|
3737
3750
|
severity;
|
|
3738
3751
|
// rule severity
|
|
3739
3752
|
event;
|
|
3753
|
+
tracker;
|
|
3740
3754
|
/**
|
|
3741
3755
|
* Rule name. Defaults to filename without extension but can be overwritten by
|
|
3742
3756
|
* subclasses.
|
|
@@ -3756,6 +3770,7 @@ class Rule {
|
|
|
3756
3770
|
this.blockers = [];
|
|
3757
3771
|
this.severity = Severity.DISABLED;
|
|
3758
3772
|
this.name = "";
|
|
3773
|
+
this.tracker = null;
|
|
3759
3774
|
}
|
|
3760
3775
|
getSeverity() {
|
|
3761
3776
|
return this.severity;
|
|
@@ -3931,7 +3946,15 @@ class Rule {
|
|
|
3931
3946
|
return this.parser.on(event, (_event, data) => {
|
|
3932
3947
|
if (this.isEnabled() && filter(data)) {
|
|
3933
3948
|
this.event = data;
|
|
3934
|
-
|
|
3949
|
+
const { tracker } = this;
|
|
3950
|
+
if (tracker) {
|
|
3951
|
+
const start = performance.now();
|
|
3952
|
+
callback(data);
|
|
3953
|
+
const end = performance.now();
|
|
3954
|
+
tracker.trackRule(this.name, end - start);
|
|
3955
|
+
} else {
|
|
3956
|
+
callback(data);
|
|
3957
|
+
}
|
|
3935
3958
|
}
|
|
3936
3959
|
});
|
|
3937
3960
|
}
|
|
@@ -3948,6 +3971,14 @@ class Rule {
|
|
|
3948
3971
|
this.severity = severity;
|
|
3949
3972
|
this.meta = meta;
|
|
3950
3973
|
}
|
|
3974
|
+
/**
|
|
3975
|
+
* Set (or clear) the performance tracker.
|
|
3976
|
+
*
|
|
3977
|
+
* @internal
|
|
3978
|
+
*/
|
|
3979
|
+
setTracker(tracker) {
|
|
3980
|
+
this.tracker = tracker;
|
|
3981
|
+
}
|
|
3951
3982
|
/**
|
|
3952
3983
|
* Validate rule options against schema. Throws error if object does not validate.
|
|
3953
3984
|
*
|
|
@@ -4330,24 +4361,27 @@ const defaults$A = {
|
|
|
4330
4361
|
}
|
|
4331
4362
|
};
|
|
4332
4363
|
const allowlist = /* @__PURE__ */ new Set([
|
|
4333
|
-
|
|
4334
|
-
"
|
|
4335
|
-
"table",
|
|
4336
|
-
"td",
|
|
4337
|
-
"th",
|
|
4364
|
+
/* landmark elements */
|
|
4365
|
+
"article",
|
|
4338
4366
|
"aside",
|
|
4339
|
-
"header",
|
|
4340
4367
|
"footer",
|
|
4368
|
+
"form",
|
|
4369
|
+
"header",
|
|
4370
|
+
"main",
|
|
4371
|
+
"nav",
|
|
4372
|
+
"search",
|
|
4341
4373
|
"section",
|
|
4342
|
-
|
|
4374
|
+
/* other allowed elements */
|
|
4375
|
+
"area",
|
|
4343
4376
|
"dialog",
|
|
4344
|
-
"
|
|
4377
|
+
"fieldset",
|
|
4378
|
+
"figure",
|
|
4345
4379
|
"iframe",
|
|
4346
4380
|
"img",
|
|
4347
|
-
"area",
|
|
4348
|
-
"fieldset",
|
|
4349
4381
|
"summary",
|
|
4350
|
-
"
|
|
4382
|
+
"table",
|
|
4383
|
+
"td",
|
|
4384
|
+
"th"
|
|
4351
4385
|
]);
|
|
4352
4386
|
function isValidUsage(target, meta) {
|
|
4353
4387
|
const explicit = meta.attributes["aria-label"];
|
|
@@ -7677,7 +7711,7 @@ class InputMissingLabel extends Rule {
|
|
|
7677
7711
|
if (hasAccessibleName(root, elem)) {
|
|
7678
7712
|
return;
|
|
7679
7713
|
}
|
|
7680
|
-
let label
|
|
7714
|
+
let label;
|
|
7681
7715
|
if ((label = findLabelById(root, elem.id)).length > 0) {
|
|
7682
7716
|
this.validateLabel(root, elem, label);
|
|
7683
7717
|
return;
|
|
@@ -8391,7 +8425,8 @@ class NoImplicitInputType extends Rule {
|
|
|
8391
8425
|
const defaults$g = {
|
|
8392
8426
|
include: null,
|
|
8393
8427
|
exclude: null,
|
|
8394
|
-
allowedProperties: ["display"]
|
|
8428
|
+
allowedProperties: ["display"],
|
|
8429
|
+
allowVariables: true
|
|
8395
8430
|
};
|
|
8396
8431
|
class NoInlineStyle extends Rule {
|
|
8397
8432
|
constructor(options) {
|
|
@@ -8430,17 +8465,26 @@ class NoInlineStyle extends Rule {
|
|
|
8430
8465
|
type: "string"
|
|
8431
8466
|
},
|
|
8432
8467
|
type: "array"
|
|
8468
|
+
},
|
|
8469
|
+
allowVariables: {
|
|
8470
|
+
type: "boolean"
|
|
8433
8471
|
}
|
|
8434
8472
|
};
|
|
8435
8473
|
}
|
|
8436
8474
|
documentation() {
|
|
8475
|
+
const { allowVariables, allowedProperties } = this.options;
|
|
8437
8476
|
const text = [
|
|
8438
8477
|
"Inline style is not allowed.\n",
|
|
8439
8478
|
"Inline style is a sign of unstructured CSS. Use class or ID with a separate stylesheet.\n"
|
|
8440
8479
|
];
|
|
8441
|
-
if (
|
|
8480
|
+
if (allowedProperties.length > 0 || allowVariables) {
|
|
8442
8481
|
text.push("Under the current configuration the following CSS properties are allowed:\n");
|
|
8443
|
-
|
|
8482
|
+
}
|
|
8483
|
+
if (allowedProperties.length > 0) {
|
|
8484
|
+
text.push(allowedProperties.map((it) => `- \`${it}\``).join("\n"));
|
|
8485
|
+
}
|
|
8486
|
+
if (allowVariables) {
|
|
8487
|
+
text.push("- CSS variables (custom properties starting with `--`).\n");
|
|
8444
8488
|
}
|
|
8445
8489
|
return {
|
|
8446
8490
|
description: text.join("\n"),
|
|
@@ -8475,13 +8519,16 @@ class NoInlineStyle extends Rule {
|
|
|
8475
8519
|
return true;
|
|
8476
8520
|
}
|
|
8477
8521
|
allPropertiesAllowed(value) {
|
|
8478
|
-
const
|
|
8479
|
-
if (
|
|
8522
|
+
const { allowedProperties, allowVariables } = this.options;
|
|
8523
|
+
if (allowedProperties.length === 0 && !allowVariables) {
|
|
8480
8524
|
return false;
|
|
8481
8525
|
}
|
|
8482
8526
|
const declarations = Object.keys(parseCssDeclaration(value));
|
|
8483
8527
|
return declarations.length > 0 && declarations.every((it) => {
|
|
8484
|
-
|
|
8528
|
+
if (allowVariables && it.startsWith("--")) {
|
|
8529
|
+
return true;
|
|
8530
|
+
}
|
|
8531
|
+
return allowedProperties.includes(it);
|
|
8485
8532
|
});
|
|
8486
8533
|
}
|
|
8487
8534
|
}
|
|
@@ -8584,7 +8631,7 @@ class NoMultipleMain extends Rule {
|
|
|
8584
8631
|
const defaults$f = {
|
|
8585
8632
|
relaxed: false
|
|
8586
8633
|
};
|
|
8587
|
-
const textRegexp = /(
|
|
8634
|
+
const textRegexp = /(<|&(?![\d#A-Za-z]+;))/g;
|
|
8588
8635
|
const unquotedAttrRegexp = /(["'<=>`]|&(?![\d#A-Za-z]+;))/g;
|
|
8589
8636
|
const matchTemplate = /^(<%.*?%>|<\?.*?\?>|<\$.*?\$>)$/s;
|
|
8590
8637
|
const replacementTable = {
|
|
@@ -8611,7 +8658,7 @@ class NoRawCharacters extends Rule {
|
|
|
8611
8658
|
}
|
|
8612
8659
|
documentation() {
|
|
8613
8660
|
return {
|
|
8614
|
-
description: `Some characters such as
|
|
8661
|
+
description: `Some characters such as \`<\` and \`&\` hold special meaning in HTML and must be escaped using a character reference (HTML entity).`,
|
|
8615
8662
|
url: "https://html-validate.org/rules/no-raw-characters.html"
|
|
8616
8663
|
};
|
|
8617
8664
|
}
|
|
@@ -10026,14 +10073,17 @@ class UnknownCharReference extends Rule {
|
|
|
10026
10073
|
if (found && (terminated || !requireSemicolon)) {
|
|
10027
10074
|
return;
|
|
10028
10075
|
}
|
|
10029
|
-
if (
|
|
10030
|
-
const
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
entity
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10076
|
+
if (!terminated) {
|
|
10077
|
+
const isKnownName = found || this.entities.includes(`${entity};`);
|
|
10078
|
+
if (isKnownName) {
|
|
10079
|
+
const entityLocation2 = getLocation(location, entity, match);
|
|
10080
|
+
const message2 = `Character reference "{{ entity }}" must be terminated by a semicolon`;
|
|
10081
|
+
const context2 = {
|
|
10082
|
+
entity: raw,
|
|
10083
|
+
terminated: false
|
|
10084
|
+
};
|
|
10085
|
+
this.report(node, message2, entityLocation2, context2);
|
|
10086
|
+
}
|
|
10037
10087
|
return;
|
|
10038
10088
|
}
|
|
10039
10089
|
const entityLocation = getLocation(location, entity, match);
|
|
@@ -12462,8 +12512,10 @@ class StaticConfigLoader extends ConfigLoader {
|
|
|
12462
12512
|
|
|
12463
12513
|
class EventHandler {
|
|
12464
12514
|
listeners;
|
|
12515
|
+
tracker;
|
|
12465
12516
|
constructor() {
|
|
12466
12517
|
this.listeners = {};
|
|
12518
|
+
this.tracker = null;
|
|
12467
12519
|
}
|
|
12468
12520
|
/**
|
|
12469
12521
|
* Add an event listener.
|
|
@@ -12502,6 +12554,14 @@ class EventHandler {
|
|
|
12502
12554
|
});
|
|
12503
12555
|
return deregister;
|
|
12504
12556
|
}
|
|
12557
|
+
/**
|
|
12558
|
+
* Set (or clear) the performance tracker.
|
|
12559
|
+
*
|
|
12560
|
+
* @internal
|
|
12561
|
+
*/
|
|
12562
|
+
setTracker(tracker) {
|
|
12563
|
+
this.tracker = tracker;
|
|
12564
|
+
}
|
|
12505
12565
|
/**
|
|
12506
12566
|
* Trigger event causing all listeners to be called.
|
|
12507
12567
|
*
|
|
@@ -12510,8 +12570,18 @@ class EventHandler {
|
|
|
12510
12570
|
*/
|
|
12511
12571
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any -- technical debt, should be made typesafe */
|
|
12512
12572
|
trigger(event, data) {
|
|
12513
|
-
|
|
12514
|
-
|
|
12573
|
+
const { tracker } = this;
|
|
12574
|
+
if (tracker) {
|
|
12575
|
+
const start = performance.now();
|
|
12576
|
+
for (const listener of this.getCallbacks(event)) {
|
|
12577
|
+
listener.call(null, event, data);
|
|
12578
|
+
}
|
|
12579
|
+
const end = performance.now();
|
|
12580
|
+
tracker.trackEvent(event, end - start);
|
|
12581
|
+
} else {
|
|
12582
|
+
for (const listener of this.getCallbacks(event)) {
|
|
12583
|
+
listener.call(null, event, data);
|
|
12584
|
+
}
|
|
12515
12585
|
}
|
|
12516
12586
|
}
|
|
12517
12587
|
getCallbacks(event) {
|
|
@@ -12523,7 +12593,7 @@ class EventHandler {
|
|
|
12523
12593
|
}
|
|
12524
12594
|
|
|
12525
12595
|
const name = "html-validate";
|
|
12526
|
-
const version = "10.
|
|
12596
|
+
const version = "10.16.0";
|
|
12527
12597
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12528
12598
|
|
|
12529
12599
|
function freeze(src) {
|
|
@@ -13457,6 +13527,78 @@ class Parser {
|
|
|
13457
13527
|
}
|
|
13458
13528
|
}
|
|
13459
13529
|
|
|
13530
|
+
class PerformanceTracker {
|
|
13531
|
+
eventData;
|
|
13532
|
+
ruleData;
|
|
13533
|
+
startTime;
|
|
13534
|
+
accConfigTime;
|
|
13535
|
+
accTransformTime;
|
|
13536
|
+
constructor() {
|
|
13537
|
+
this.eventData = /* @__PURE__ */ new Map();
|
|
13538
|
+
this.ruleData = /* @__PURE__ */ new Map();
|
|
13539
|
+
this.startTime = performance.now();
|
|
13540
|
+
this.accConfigTime = 0;
|
|
13541
|
+
this.accTransformTime = 0;
|
|
13542
|
+
}
|
|
13543
|
+
/**
|
|
13544
|
+
* Record a single event trigger with the time it took to run all listeners.
|
|
13545
|
+
*/
|
|
13546
|
+
trackEvent(name, time) {
|
|
13547
|
+
const existing = this.eventData.get(name);
|
|
13548
|
+
if (existing) {
|
|
13549
|
+
existing.count += 1;
|
|
13550
|
+
existing.time += time;
|
|
13551
|
+
} else {
|
|
13552
|
+
this.eventData.set(name, { count: 1, time });
|
|
13553
|
+
}
|
|
13554
|
+
}
|
|
13555
|
+
/**
|
|
13556
|
+
* Record time spent loading configuration.
|
|
13557
|
+
*/
|
|
13558
|
+
trackConfig(time) {
|
|
13559
|
+
this.accConfigTime += time;
|
|
13560
|
+
}
|
|
13561
|
+
/**
|
|
13562
|
+
* Record time spent in transformers.
|
|
13563
|
+
*/
|
|
13564
|
+
trackTransform(time) {
|
|
13565
|
+
this.accTransformTime += time;
|
|
13566
|
+
}
|
|
13567
|
+
/**
|
|
13568
|
+
* Record a single rule callback invocation with its execution time.
|
|
13569
|
+
*/
|
|
13570
|
+
trackRule(ruleName, time) {
|
|
13571
|
+
const existing = this.ruleData.get(ruleName);
|
|
13572
|
+
if (existing) {
|
|
13573
|
+
existing.count += 1;
|
|
13574
|
+
existing.time += time;
|
|
13575
|
+
} else {
|
|
13576
|
+
this.ruleData.set(ruleName, { count: 1, time });
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
/**
|
|
13580
|
+
* Returns a snapshot of the recorded performance data, with both arrays
|
|
13581
|
+
* sorted by time (descending).
|
|
13582
|
+
*/
|
|
13583
|
+
getResult() {
|
|
13584
|
+
const events = Array.from(
|
|
13585
|
+
this.eventData.entries(),
|
|
13586
|
+
([event, { count, time }]) => ({ event, count, time })
|
|
13587
|
+
).toSorted((a, b) => b.time - a.time);
|
|
13588
|
+
const rules = Array.from(
|
|
13589
|
+
this.ruleData.entries(),
|
|
13590
|
+
([rule, { count, time }]) => ({ rule, count, time })
|
|
13591
|
+
).toSorted((a, b) => b.time - a.time);
|
|
13592
|
+
return {
|
|
13593
|
+
events,
|
|
13594
|
+
rules,
|
|
13595
|
+
configTime: this.accConfigTime,
|
|
13596
|
+
transformTime: this.accTransformTime,
|
|
13597
|
+
totalTime: performance.now() - this.startTime
|
|
13598
|
+
};
|
|
13599
|
+
}
|
|
13600
|
+
}
|
|
13601
|
+
|
|
13460
13602
|
const ruleIds = new Set(Object.keys(bundledRules));
|
|
13461
13603
|
function ruleExists(ruleId) {
|
|
13462
13604
|
return ruleIds.has(ruleId);
|
|
@@ -13504,10 +13646,12 @@ class Engine {
|
|
|
13504
13646
|
config;
|
|
13505
13647
|
ParserClass;
|
|
13506
13648
|
availableRules;
|
|
13507
|
-
|
|
13649
|
+
tracker;
|
|
13650
|
+
constructor(config, ParserClass, options) {
|
|
13508
13651
|
this.report = new Reporter();
|
|
13509
13652
|
this.config = config;
|
|
13510
13653
|
this.ParserClass = ParserClass;
|
|
13654
|
+
this.tracker = options.tracker;
|
|
13511
13655
|
const result = this.initPlugins(this.config);
|
|
13512
13656
|
this.availableRules = {
|
|
13513
13657
|
...bundledRules,
|
|
@@ -13523,6 +13667,9 @@ class Engine {
|
|
|
13523
13667
|
lint(sources) {
|
|
13524
13668
|
for (const source of sources) {
|
|
13525
13669
|
const parser = this.instantiateParser();
|
|
13670
|
+
if (this.tracker) {
|
|
13671
|
+
parser.getEventHandler().setTracker(this.tracker);
|
|
13672
|
+
}
|
|
13526
13673
|
const { rules } = this.setupPlugins(source, this.config, parser);
|
|
13527
13674
|
const noUnusedDisable = rules["no-unused-disable"];
|
|
13528
13675
|
const directiveContext = {
|
|
@@ -13799,6 +13946,7 @@ class Engine {
|
|
|
13799
13946
|
const rule = this.instantiateRule(ruleId, options);
|
|
13800
13947
|
rule.name = ruleId;
|
|
13801
13948
|
rule.init(parser, report, severity, meta);
|
|
13949
|
+
rule.setTracker(this.tracker);
|
|
13802
13950
|
if (rule.setup) {
|
|
13803
13951
|
rule.setup();
|
|
13804
13952
|
}
|
|
@@ -15157,5 +15305,5 @@ const engines = {
|
|
|
15157
15305
|
|
|
15158
15306
|
var workerPath = "./jest-worker.js";
|
|
15159
15307
|
|
|
15160
|
-
export {
|
|
15308
|
+
export { walk as $, Attribute as A, Severity as B, ConfigLoader as C, DOMNode as D, Engine as E, TextContent$1 as F, TextNode as G, HtmlElement as H, ariaNaming as I, classifyNodeText as J, presets as K, defineConfig as L, MetaCopyableProperty as M, NestedError as N, definePlugin as O, PerformanceTracker as P, isUserError as Q, Reporter as R, StaticConfigLoader as S, TextClassification as T, UserError as U, Validator as V, WrappedError as W, keywordPatternMatcher as X, ruleExists as Y, sliceLocation as Z, staticResolver as _, Parser as a, engines as a0, codeFrameColumns as a1, getEndLocation as a2, getStartLocation as a3, workerPath as a4, name as a5, bugs as a6, transformSourceSync as b, transformFilename as c, transformFilenameSync as d, configurationSchema as e, ConfigError as f, Config as g, compatibilityCheckImpl as h, isThenable as i, ensureError as j, getFormatter as k, deepmerge as l, ignore as m, normalizeSource as n, DOMTokenList as o, DOMTree as p, DynamicValue as q, EventHandler as r, MetaTable as s, transformSource as t, NodeClosed as u, version as v, NodeType as w, ResolvedConfig as x, Rule as y, SchemaValidationError as z };
|
|
15161
15309
|
//# sourceMappingURL=core.js.map
|