html-validate 6.5.0 → 6.7.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.d.ts +1 -1
- package/dist/cjs/browser.js +2 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +9 -5
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +14 -5
- package/dist/cjs/core.js +362 -123
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/html-validate.js +57 -5
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/jest-lib.d.ts +2 -1
- package/dist/cjs/jest-lib.js +52 -27
- package/dist/cjs/jest-lib.js.map +1 -1
- package/dist/cjs/jest.d.ts +2 -2
- package/dist/cjs/jest.js +2 -0
- package/dist/cjs/jest.js.map +1 -1
- package/dist/cjs/test-utils.d.ts +1 -1
- package/dist/cjs/test-utils.js.map +1 -1
- package/dist/es/browser.d.ts +1 -1
- package/dist/es/browser.js +2 -0
- package/dist/es/browser.js.map +1 -1
- package/dist/es/cli.js +9 -5
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +14 -5
- package/dist/es/core.js +340 -121
- package/dist/es/core.js.map +1 -1
- package/dist/es/html-validate.js +57 -5
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/index.d.ts +2 -2
- package/dist/es/index.js +2 -0
- package/dist/es/index.js.map +1 -1
- package/dist/es/jest-lib.d.ts +2 -1
- package/dist/es/jest-lib.js +52 -27
- package/dist/es/jest-lib.js.map +1 -1
- package/dist/es/jest.d.ts +2 -2
- package/dist/es/jest.js +2 -0
- package/dist/es/jest.js.map +1 -1
- package/dist/es/test-utils.d.ts +1 -1
- package/dist/es/test-utils.js.map +1 -1
- package/package.json +1 -152
package/dist/cjs/core.js
CHANGED
|
@@ -4,6 +4,8 @@ var fs = require('fs');
|
|
|
4
4
|
var betterAjvErrors = require('@sidvind/better-ajv-errors');
|
|
5
5
|
var Ajv = require('ajv');
|
|
6
6
|
var deepmerge = require('deepmerge');
|
|
7
|
+
var espree = require('espree');
|
|
8
|
+
var walk = require('acorn-walk');
|
|
7
9
|
var path = require('path');
|
|
8
10
|
var semver = require('semver');
|
|
9
11
|
var kleur = require('kleur');
|
|
@@ -12,10 +14,30 @@ var stylishImpl = require('@html-validate/stylish');
|
|
|
12
14
|
|
|
13
15
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
14
16
|
|
|
17
|
+
function _interopNamespace(e) {
|
|
18
|
+
if (e && e.__esModule) return e;
|
|
19
|
+
var n = Object.create(null);
|
|
20
|
+
if (e) {
|
|
21
|
+
Object.keys(e).forEach(function (k) {
|
|
22
|
+
if (k !== 'default') {
|
|
23
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
24
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return e[k]; }
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
n["default"] = e;
|
|
32
|
+
return Object.freeze(n);
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
16
36
|
var betterAjvErrors__default = /*#__PURE__*/_interopDefaultLegacy(betterAjvErrors);
|
|
17
37
|
var Ajv__default = /*#__PURE__*/_interopDefaultLegacy(Ajv);
|
|
18
38
|
var deepmerge__default = /*#__PURE__*/_interopDefaultLegacy(deepmerge);
|
|
39
|
+
var espree__namespace = /*#__PURE__*/_interopNamespace(espree);
|
|
40
|
+
var walk__namespace = /*#__PURE__*/_interopNamespace(walk);
|
|
19
41
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
20
42
|
var semver__default = /*#__PURE__*/_interopDefaultLegacy(semver);
|
|
21
43
|
var kleur__default = /*#__PURE__*/_interopDefaultLegacy(kleur);
|
|
@@ -256,7 +278,7 @@ class NestedError extends Error {
|
|
|
256
278
|
constructor(message, nested) {
|
|
257
279
|
super(message);
|
|
258
280
|
Error.captureStackTrace(this, NestedError);
|
|
259
|
-
if (nested) {
|
|
281
|
+
if (nested && nested.stack) {
|
|
260
282
|
this.stack += `\nCaused by: ${nested.stack}`;
|
|
261
283
|
}
|
|
262
284
|
}
|
|
@@ -1849,8 +1871,13 @@ exports.NodeClosed = void 0;
|
|
|
1849
1871
|
NodeClosed[NodeClosed["VoidSelfClosed"] = 3] = "VoidSelfClosed";
|
|
1850
1872
|
NodeClosed[NodeClosed["ImplicitClosed"] = 4] = "ImplicitClosed";
|
|
1851
1873
|
})(exports.NodeClosed || (exports.NodeClosed = {}));
|
|
1852
|
-
|
|
1853
|
-
|
|
1874
|
+
/**
|
|
1875
|
+
* Returns true if the node is an element node.
|
|
1876
|
+
*
|
|
1877
|
+
* @public
|
|
1878
|
+
*/
|
|
1879
|
+
function isElementNode(node) {
|
|
1880
|
+
return Boolean(node && node.nodeType === NodeType.ELEMENT_NODE);
|
|
1854
1881
|
}
|
|
1855
1882
|
function isValidTagName(tagName) {
|
|
1856
1883
|
return Boolean(tagName !== "" && tagName !== "*");
|
|
@@ -1891,10 +1918,13 @@ class HtmlElement extends DOMNode {
|
|
|
1891
1918
|
}
|
|
1892
1919
|
/**
|
|
1893
1920
|
* @internal
|
|
1921
|
+
*
|
|
1922
|
+
* @param namespace - If given it is appended to the tagName.
|
|
1894
1923
|
*/
|
|
1895
|
-
static fromTokens(startToken, endToken, parent, metaTable) {
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1924
|
+
static fromTokens(startToken, endToken, parent, metaTable, namespace = "") {
|
|
1925
|
+
const name = startToken.data[2];
|
|
1926
|
+
const tagName = namespace ? `${namespace}:${name}` : name;
|
|
1927
|
+
if (!name) {
|
|
1898
1928
|
throw new Error("tagName cannot be empty");
|
|
1899
1929
|
}
|
|
1900
1930
|
const meta = metaTable ? metaTable.getMetaFor(tagName) : null;
|
|
@@ -1921,7 +1951,7 @@ class HtmlElement extends DOMNode {
|
|
|
1921
1951
|
* Similar to childNodes but only elements.
|
|
1922
1952
|
*/
|
|
1923
1953
|
get childElements() {
|
|
1924
|
-
return this.childNodes.filter(
|
|
1954
|
+
return this.childNodes.filter(isElementNode);
|
|
1925
1955
|
}
|
|
1926
1956
|
/**
|
|
1927
1957
|
* Find the first ancestor matching a selector.
|
|
@@ -2182,8 +2212,9 @@ class HtmlElement extends DOMNode {
|
|
|
2182
2212
|
}, []);
|
|
2183
2213
|
}
|
|
2184
2214
|
querySelector(selector) {
|
|
2215
|
+
var _a;
|
|
2185
2216
|
const it = this.querySelectorImpl(selector);
|
|
2186
|
-
return it.next().value
|
|
2217
|
+
return (_a = it.next().value) !== null && _a !== void 0 ? _a : null; // eslint-disable-line @typescript-eslint/no-unsafe-return
|
|
2187
2218
|
}
|
|
2188
2219
|
querySelectorAll(selector) {
|
|
2189
2220
|
const it = this.querySelectorImpl(selector);
|
|
@@ -2750,8 +2781,6 @@ var configurationSchema = {
|
|
|
2750
2781
|
};
|
|
2751
2782
|
|
|
2752
2783
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2753
|
-
const espree = legacyRequire("espree");
|
|
2754
|
-
const walk = legacyRequire("acorn-walk");
|
|
2755
2784
|
function joinTemplateLiteral(nodes) {
|
|
2756
2785
|
let offset = nodes[0].start + 1;
|
|
2757
2786
|
let output = "";
|
|
@@ -2867,7 +2896,7 @@ class TemplateExtractor {
|
|
|
2867
2896
|
}
|
|
2868
2897
|
static fromFilename(filename) {
|
|
2869
2898
|
const source = fs__default["default"].readFileSync(filename, "utf-8");
|
|
2870
|
-
const ast =
|
|
2899
|
+
const ast = espree__namespace.parse(source, {
|
|
2871
2900
|
ecmaVersion: 2017,
|
|
2872
2901
|
sourceType: "module",
|
|
2873
2902
|
loc: true,
|
|
@@ -2886,7 +2915,7 @@ class TemplateExtractor {
|
|
|
2886
2915
|
* `Source`. Defauls to `"inline"`.
|
|
2887
2916
|
*/
|
|
2888
2917
|
static fromString(source, filename) {
|
|
2889
|
-
const ast =
|
|
2918
|
+
const ast = espree__namespace.parse(source, {
|
|
2890
2919
|
ecmaVersion: 2017,
|
|
2891
2920
|
sourceType: "module",
|
|
2892
2921
|
loc: true,
|
|
@@ -2932,7 +2961,8 @@ class TemplateExtractor {
|
|
|
2932
2961
|
extractObjectProperty(key) {
|
|
2933
2962
|
const result = [];
|
|
2934
2963
|
const { filename, data } = this;
|
|
2935
|
-
|
|
2964
|
+
const node = this.ast;
|
|
2965
|
+
walk__namespace.simple(node, {
|
|
2936
2966
|
Property(node) {
|
|
2937
2967
|
if (compareKey(node.key, key, filename)) {
|
|
2938
2968
|
const source = extractLiteral(node.value, filename, data);
|
|
@@ -2956,7 +2986,7 @@ var TRANSFORMER_API;
|
|
|
2956
2986
|
/** @public */
|
|
2957
2987
|
const name = "html-validate";
|
|
2958
2988
|
/** @public */
|
|
2959
|
-
const version = "6.
|
|
2989
|
+
const version = "6.7.0";
|
|
2960
2990
|
/** @public */
|
|
2961
2991
|
const homepage = "https://html-validate.org";
|
|
2962
2992
|
/** @public */
|
|
@@ -3277,7 +3307,7 @@ function ruleDocumentationUrl(filename) {
|
|
|
3277
3307
|
return `${homepage}/rules/${normalized}.html`;
|
|
3278
3308
|
}
|
|
3279
3309
|
|
|
3280
|
-
const defaults$
|
|
3310
|
+
const defaults$q = {
|
|
3281
3311
|
allowExternal: true,
|
|
3282
3312
|
allowRelative: true,
|
|
3283
3313
|
allowAbsolute: true,
|
|
@@ -3321,7 +3351,7 @@ function matchList(value, list) {
|
|
|
3321
3351
|
}
|
|
3322
3352
|
class AllowedLinks extends Rule {
|
|
3323
3353
|
constructor(options) {
|
|
3324
|
-
super({ ...defaults$
|
|
3354
|
+
super({ ...defaults$q, ...options });
|
|
3325
3355
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3326
3356
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3327
3357
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -3600,7 +3630,7 @@ class CaseStyle {
|
|
|
3600
3630
|
default: {
|
|
3601
3631
|
const last = names.slice(-1);
|
|
3602
3632
|
const rest = names.slice(0, -1);
|
|
3603
|
-
return `${rest.join(", ")} or ${last}`;
|
|
3633
|
+
return `${rest.join(", ")} or ${last[0]}`;
|
|
3604
3634
|
}
|
|
3605
3635
|
}
|
|
3606
3636
|
}
|
|
@@ -3616,19 +3646,19 @@ class CaseStyle {
|
|
|
3616
3646
|
case "camelcase":
|
|
3617
3647
|
return { pattern: /^[a-z][A-Za-z]*$/, name: "camelCase" };
|
|
3618
3648
|
default:
|
|
3619
|
-
throw new ConfigError(`Invalid style "${
|
|
3649
|
+
throw new ConfigError(`Invalid style "${cur}" for ${ruleId} rule`);
|
|
3620
3650
|
}
|
|
3621
3651
|
});
|
|
3622
3652
|
}
|
|
3623
3653
|
}
|
|
3624
3654
|
|
|
3625
|
-
const defaults$
|
|
3655
|
+
const defaults$p = {
|
|
3626
3656
|
style: "lowercase",
|
|
3627
3657
|
ignoreForeign: true,
|
|
3628
3658
|
};
|
|
3629
3659
|
class AttrCase extends Rule {
|
|
3630
3660
|
constructor(options) {
|
|
3631
|
-
super({ ...defaults$
|
|
3661
|
+
super({ ...defaults$p, ...options });
|
|
3632
3662
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3633
3663
|
}
|
|
3634
3664
|
static schema() {
|
|
@@ -3655,8 +3685,11 @@ class AttrCase extends Rule {
|
|
|
3655
3685
|
};
|
|
3656
3686
|
}
|
|
3657
3687
|
documentation() {
|
|
3688
|
+
const { style } = this.options;
|
|
3658
3689
|
return {
|
|
3659
|
-
description:
|
|
3690
|
+
description: Array.isArray(style)
|
|
3691
|
+
? [`Attribute name must be in one of:`, "", ...style.map((it) => `- ${it}`)].join("\n")
|
|
3692
|
+
: `Attribute name must be in ${style}.`,
|
|
3660
3693
|
url: ruleDocumentationUrl("@/rules/attr-case.ts"),
|
|
3661
3694
|
};
|
|
3662
3695
|
}
|
|
@@ -3947,7 +3980,7 @@ function isRelevant$3(event) {
|
|
|
3947
3980
|
class AttrDelimiter extends Rule {
|
|
3948
3981
|
documentation() {
|
|
3949
3982
|
return {
|
|
3950
|
-
description: `Attribute value
|
|
3983
|
+
description: `Attribute value must not be separated by whitespace.`,
|
|
3951
3984
|
url: ruleDocumentationUrl("@/rules/attr-delimiter.ts"),
|
|
3952
3985
|
};
|
|
3953
3986
|
}
|
|
@@ -3964,7 +3997,7 @@ class AttrDelimiter extends Rule {
|
|
|
3964
3997
|
}
|
|
3965
3998
|
|
|
3966
3999
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
3967
|
-
const defaults$
|
|
4000
|
+
const defaults$o = {
|
|
3968
4001
|
pattern: DEFAULT_PATTERN,
|
|
3969
4002
|
ignoreForeign: true,
|
|
3970
4003
|
};
|
|
@@ -4001,7 +4034,7 @@ function generateDescription(name, pattern) {
|
|
|
4001
4034
|
}
|
|
4002
4035
|
class AttrPattern extends Rule {
|
|
4003
4036
|
constructor(options) {
|
|
4004
|
-
super({ ...defaults$
|
|
4037
|
+
super({ ...defaults$o, ...options });
|
|
4005
4038
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4006
4039
|
}
|
|
4007
4040
|
static schema() {
|
|
@@ -4061,13 +4094,13 @@ var QuoteStyle;
|
|
|
4061
4094
|
QuoteStyle["DOUBLE_QUOTE"] = "\"";
|
|
4062
4095
|
QuoteStyle["AUTO_QUOTE"] = "auto";
|
|
4063
4096
|
})(QuoteStyle || (QuoteStyle = {}));
|
|
4064
|
-
const defaults$
|
|
4097
|
+
const defaults$n = {
|
|
4065
4098
|
style: "auto",
|
|
4066
4099
|
unquoted: false,
|
|
4067
4100
|
};
|
|
4068
4101
|
class AttrQuotes extends Rule {
|
|
4069
4102
|
constructor(options) {
|
|
4070
|
-
super({ ...defaults$
|
|
4103
|
+
super({ ...defaults$n, ...options });
|
|
4071
4104
|
this.style = parseStyle$4(this.options.style);
|
|
4072
4105
|
}
|
|
4073
4106
|
static schema() {
|
|
@@ -4232,12 +4265,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4232
4265
|
}
|
|
4233
4266
|
}
|
|
4234
4267
|
|
|
4235
|
-
const defaults$
|
|
4268
|
+
const defaults$m = {
|
|
4236
4269
|
style: "omit",
|
|
4237
4270
|
};
|
|
4238
4271
|
class AttributeBooleanStyle extends Rule {
|
|
4239
4272
|
constructor(options) {
|
|
4240
|
-
super({ ...defaults$
|
|
4273
|
+
super({ ...defaults$m, ...options });
|
|
4241
4274
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
4242
4275
|
}
|
|
4243
4276
|
static schema() {
|
|
@@ -4313,12 +4346,12 @@ function reportMessage$1(attr, style) {
|
|
|
4313
4346
|
return "";
|
|
4314
4347
|
}
|
|
4315
4348
|
|
|
4316
|
-
const defaults$
|
|
4349
|
+
const defaults$l = {
|
|
4317
4350
|
style: "omit",
|
|
4318
4351
|
};
|
|
4319
4352
|
class AttributeEmptyStyle extends Rule {
|
|
4320
4353
|
constructor(options) {
|
|
4321
|
-
super({ ...defaults$
|
|
4354
|
+
super({ ...defaults$l, ...options });
|
|
4322
4355
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4323
4356
|
}
|
|
4324
4357
|
static schema() {
|
|
@@ -4426,12 +4459,12 @@ function describePattern(pattern) {
|
|
|
4426
4459
|
}
|
|
4427
4460
|
}
|
|
4428
4461
|
|
|
4429
|
-
const defaults$
|
|
4462
|
+
const defaults$k = {
|
|
4430
4463
|
pattern: "kebabcase",
|
|
4431
4464
|
};
|
|
4432
4465
|
class ClassPattern extends Rule {
|
|
4433
4466
|
constructor(options) {
|
|
4434
|
-
super({ ...defaults$
|
|
4467
|
+
super({ ...defaults$k, ...options });
|
|
4435
4468
|
this.pattern = parsePattern(this.options.pattern);
|
|
4436
4469
|
}
|
|
4437
4470
|
static schema() {
|
|
@@ -4457,7 +4490,9 @@ class ClassPattern extends Rule {
|
|
|
4457
4490
|
classes.forEach((cur, index) => {
|
|
4458
4491
|
if (!cur.match(this.pattern)) {
|
|
4459
4492
|
const location = classes.location(index);
|
|
4460
|
-
|
|
4493
|
+
const pattern = this.pattern.toString();
|
|
4494
|
+
const message = `Class "${cur}" does not match required pattern "${pattern}"`;
|
|
4495
|
+
this.report(event.target, message, location);
|
|
4461
4496
|
}
|
|
4462
4497
|
});
|
|
4463
4498
|
});
|
|
@@ -4538,13 +4573,13 @@ class CloseOrder extends Rule {
|
|
|
4538
4573
|
}
|
|
4539
4574
|
}
|
|
4540
4575
|
|
|
4541
|
-
const defaults$
|
|
4576
|
+
const defaults$j = {
|
|
4542
4577
|
include: null,
|
|
4543
4578
|
exclude: null,
|
|
4544
4579
|
};
|
|
4545
4580
|
class Deprecated extends Rule {
|
|
4546
4581
|
constructor(options) {
|
|
4547
|
-
super({ ...defaults$
|
|
4582
|
+
super({ ...defaults$j, ...options });
|
|
4548
4583
|
}
|
|
4549
4584
|
static schema() {
|
|
4550
4585
|
return {
|
|
@@ -4707,12 +4742,12 @@ class NoStyleTag$1 extends Rule {
|
|
|
4707
4742
|
}
|
|
4708
4743
|
}
|
|
4709
4744
|
|
|
4710
|
-
const defaults$
|
|
4745
|
+
const defaults$i = {
|
|
4711
4746
|
style: "uppercase",
|
|
4712
4747
|
};
|
|
4713
4748
|
class DoctypeStyle extends Rule {
|
|
4714
4749
|
constructor(options) {
|
|
4715
|
-
super({ ...defaults$
|
|
4750
|
+
super({ ...defaults$i, ...options });
|
|
4716
4751
|
}
|
|
4717
4752
|
static schema() {
|
|
4718
4753
|
return {
|
|
@@ -4744,12 +4779,12 @@ class DoctypeStyle extends Rule {
|
|
|
4744
4779
|
}
|
|
4745
4780
|
}
|
|
4746
4781
|
|
|
4747
|
-
const defaults$
|
|
4782
|
+
const defaults$h = {
|
|
4748
4783
|
style: "lowercase",
|
|
4749
4784
|
};
|
|
4750
4785
|
class ElementCase extends Rule {
|
|
4751
4786
|
constructor(options) {
|
|
4752
|
-
super({ ...defaults$
|
|
4787
|
+
super({ ...defaults$h, ...options });
|
|
4753
4788
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4754
4789
|
}
|
|
4755
4790
|
static schema() {
|
|
@@ -4773,8 +4808,11 @@ class ElementCase extends Rule {
|
|
|
4773
4808
|
};
|
|
4774
4809
|
}
|
|
4775
4810
|
documentation() {
|
|
4811
|
+
const { style } = this.options;
|
|
4776
4812
|
return {
|
|
4777
|
-
description:
|
|
4813
|
+
description: Array.isArray(style)
|
|
4814
|
+
? [`Element tagname must be in one of:`, "", ...style.map((it) => `- ${it}`)].join("\n")
|
|
4815
|
+
: `Element tagname must be in ${style}.`,
|
|
4778
4816
|
url: ruleDocumentationUrl("@/rules/element-case.ts"),
|
|
4779
4817
|
};
|
|
4780
4818
|
}
|
|
@@ -4812,14 +4850,14 @@ class ElementCase extends Rule {
|
|
|
4812
4850
|
}
|
|
4813
4851
|
}
|
|
4814
4852
|
|
|
4815
|
-
const defaults$
|
|
4853
|
+
const defaults$g = {
|
|
4816
4854
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
4817
4855
|
whitelist: [],
|
|
4818
4856
|
blacklist: [],
|
|
4819
4857
|
};
|
|
4820
4858
|
class ElementName extends Rule {
|
|
4821
4859
|
constructor(options) {
|
|
4822
|
-
super({ ...defaults$
|
|
4860
|
+
super({ ...defaults$g, ...options });
|
|
4823
4861
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4824
4862
|
this.pattern = new RegExp(this.options.pattern);
|
|
4825
4863
|
}
|
|
@@ -4860,7 +4898,7 @@ class ElementName extends Rule {
|
|
|
4860
4898
|
...context.blacklist.map((cur) => `- ${cur}`),
|
|
4861
4899
|
];
|
|
4862
4900
|
}
|
|
4863
|
-
if (context.pattern !== defaults$
|
|
4901
|
+
if (context.pattern !== defaults$g.pattern) {
|
|
4864
4902
|
return [
|
|
4865
4903
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
4866
4904
|
"",
|
|
@@ -5262,7 +5300,7 @@ class EmptyTitle extends Rule {
|
|
|
5262
5300
|
}
|
|
5263
5301
|
}
|
|
5264
5302
|
|
|
5265
|
-
const defaults$
|
|
5303
|
+
const defaults$f = {
|
|
5266
5304
|
allowMultipleH1: false,
|
|
5267
5305
|
minInitialRank: "h1",
|
|
5268
5306
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
@@ -5293,7 +5331,7 @@ function parseMaxInitial(value) {
|
|
|
5293
5331
|
}
|
|
5294
5332
|
class HeadingLevel extends Rule {
|
|
5295
5333
|
constructor(options) {
|
|
5296
|
-
super({ ...defaults$
|
|
5334
|
+
super({ ...defaults$f, ...options });
|
|
5297
5335
|
this.stack = [];
|
|
5298
5336
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
5299
5337
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -5451,12 +5489,12 @@ class HeadingLevel extends Rule {
|
|
|
5451
5489
|
}
|
|
5452
5490
|
}
|
|
5453
5491
|
|
|
5454
|
-
const defaults$
|
|
5492
|
+
const defaults$e = {
|
|
5455
5493
|
pattern: "kebabcase",
|
|
5456
5494
|
};
|
|
5457
5495
|
class IdPattern extends Rule {
|
|
5458
5496
|
constructor(options) {
|
|
5459
|
-
super({ ...defaults$
|
|
5497
|
+
super({ ...defaults$e, ...options });
|
|
5460
5498
|
this.pattern = parsePattern(this.options.pattern);
|
|
5461
5499
|
}
|
|
5462
5500
|
static schema() {
|
|
@@ -5475,6 +5513,7 @@ class IdPattern extends Rule {
|
|
|
5475
5513
|
}
|
|
5476
5514
|
setup() {
|
|
5477
5515
|
this.on("attr", (event) => {
|
|
5516
|
+
var _a;
|
|
5478
5517
|
if (event.key.toLowerCase() !== "id") {
|
|
5479
5518
|
return;
|
|
5480
5519
|
}
|
|
@@ -5483,7 +5522,10 @@ class IdPattern extends Rule {
|
|
|
5483
5522
|
return;
|
|
5484
5523
|
}
|
|
5485
5524
|
if (!event.value || !event.value.match(this.pattern)) {
|
|
5486
|
-
|
|
5525
|
+
const value = (_a = event.value) !== null && _a !== void 0 ? _a : "";
|
|
5526
|
+
const pattern = this.pattern.toString();
|
|
5527
|
+
const message = `ID "${value}" does not match required pattern "${pattern}"`;
|
|
5528
|
+
this.report(event.target, message, event.valueLocation);
|
|
5487
5529
|
}
|
|
5488
5530
|
});
|
|
5489
5531
|
}
|
|
@@ -5803,12 +5845,12 @@ function findLabelByParent(el) {
|
|
|
5803
5845
|
return [];
|
|
5804
5846
|
}
|
|
5805
5847
|
|
|
5806
|
-
const defaults$
|
|
5848
|
+
const defaults$d = {
|
|
5807
5849
|
maxlength: 70,
|
|
5808
5850
|
};
|
|
5809
5851
|
class LongTitle extends Rule {
|
|
5810
5852
|
constructor(options) {
|
|
5811
|
-
super({ ...defaults$
|
|
5853
|
+
super({ ...defaults$d, ...options });
|
|
5812
5854
|
this.maxlength = this.options.maxlength;
|
|
5813
5855
|
}
|
|
5814
5856
|
static schema() {
|
|
@@ -5955,13 +5997,13 @@ class MultipleLabeledControls extends Rule {
|
|
|
5955
5997
|
}
|
|
5956
5998
|
}
|
|
5957
5999
|
|
|
5958
|
-
const defaults$
|
|
6000
|
+
const defaults$c = {
|
|
5959
6001
|
include: null,
|
|
5960
6002
|
exclude: null,
|
|
5961
6003
|
};
|
|
5962
6004
|
class NoAutoplay extends Rule {
|
|
5963
6005
|
constructor(options) {
|
|
5964
|
-
super({ ...defaults$
|
|
6006
|
+
super({ ...defaults$c, ...options });
|
|
5965
6007
|
}
|
|
5966
6008
|
documentation(context) {
|
|
5967
6009
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -6202,7 +6244,7 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
6202
6244
|
}
|
|
6203
6245
|
}
|
|
6204
6246
|
|
|
6205
|
-
const defaults$
|
|
6247
|
+
const defaults$b = {
|
|
6206
6248
|
include: null,
|
|
6207
6249
|
exclude: null,
|
|
6208
6250
|
allowedProperties: ["display"],
|
|
@@ -6219,7 +6261,7 @@ function getCSSDeclarations(value) {
|
|
|
6219
6261
|
}
|
|
6220
6262
|
class NoInlineStyle extends Rule {
|
|
6221
6263
|
constructor(options) {
|
|
6222
|
-
super({ ...defaults$
|
|
6264
|
+
super({ ...defaults$b, ...options });
|
|
6223
6265
|
}
|
|
6224
6266
|
static schema() {
|
|
6225
6267
|
return {
|
|
@@ -6424,24 +6466,24 @@ class NoMultipleMain extends Rule {
|
|
|
6424
6466
|
}
|
|
6425
6467
|
}
|
|
6426
6468
|
|
|
6427
|
-
const defaults$
|
|
6469
|
+
const defaults$a = {
|
|
6428
6470
|
relaxed: false,
|
|
6429
6471
|
};
|
|
6430
6472
|
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
|
|
6431
6473
|
const unquotedAttrRegexp = /([<>"'=`]|&(?![a-zA-Z0-9#]+;))/g;
|
|
6432
6474
|
const matchTemplate = /^(<%.*?%>|<\?.*?\?>|<\$.*?\$>)$/s;
|
|
6433
|
-
const replacementTable =
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6475
|
+
const replacementTable = {
|
|
6476
|
+
'"': """,
|
|
6477
|
+
"&": "&",
|
|
6478
|
+
"'": "'",
|
|
6479
|
+
"<": "<",
|
|
6480
|
+
"=": "=",
|
|
6481
|
+
">": ">",
|
|
6482
|
+
"`": "`",
|
|
6483
|
+
};
|
|
6442
6484
|
class NoRawCharacters extends Rule {
|
|
6443
6485
|
constructor(options) {
|
|
6444
|
-
super({ ...defaults$
|
|
6486
|
+
super({ ...defaults$a, ...options });
|
|
6445
6487
|
this.relaxed = this.options.relaxed;
|
|
6446
6488
|
}
|
|
6447
6489
|
static schema() {
|
|
@@ -6491,7 +6533,6 @@ class NoRawCharacters extends Rule {
|
|
|
6491
6533
|
* @param text - The full text to find unescaped raw characters in.
|
|
6492
6534
|
* @param location - Location of text.
|
|
6493
6535
|
* @param regexp - Regexp pattern to match using.
|
|
6494
|
-
* @param ignore - List of characters to ignore for this text.
|
|
6495
6536
|
*/
|
|
6496
6537
|
findRawChars(node, text, location, regexp) {
|
|
6497
6538
|
let match;
|
|
@@ -6507,7 +6548,7 @@ class NoRawCharacters extends Rule {
|
|
|
6507
6548
|
continue;
|
|
6508
6549
|
}
|
|
6509
6550
|
/* determine replacement character and location */
|
|
6510
|
-
const replacement = replacementTable
|
|
6551
|
+
const replacement = replacementTable[char];
|
|
6511
6552
|
const charLocation = sliceLocation(location, match.index, match.index + 1);
|
|
6512
6553
|
/* report as error */
|
|
6513
6554
|
this.report(node, `Raw "${char}" must be encoded as "${replacement}"`, charLocation);
|
|
@@ -6620,13 +6661,13 @@ class NoRedundantRole extends Rule {
|
|
|
6620
6661
|
}
|
|
6621
6662
|
|
|
6622
6663
|
const xmlns = /^(.+):.+$/;
|
|
6623
|
-
const defaults$
|
|
6664
|
+
const defaults$9 = {
|
|
6624
6665
|
ignoreForeign: true,
|
|
6625
6666
|
ignoreXML: true,
|
|
6626
6667
|
};
|
|
6627
6668
|
class NoSelfClosing extends Rule {
|
|
6628
6669
|
constructor(options) {
|
|
6629
|
-
super({ ...defaults$
|
|
6670
|
+
super({ ...defaults$9, ...options });
|
|
6630
6671
|
}
|
|
6631
6672
|
static schema() {
|
|
6632
6673
|
return {
|
|
@@ -6759,13 +6800,13 @@ const replacement = {
|
|
|
6759
6800
|
reset: '<button type="reset">',
|
|
6760
6801
|
image: '<button type="button">',
|
|
6761
6802
|
};
|
|
6762
|
-
const defaults$
|
|
6803
|
+
const defaults$8 = {
|
|
6763
6804
|
include: null,
|
|
6764
6805
|
exclude: null,
|
|
6765
6806
|
};
|
|
6766
6807
|
class PreferButton extends Rule {
|
|
6767
6808
|
constructor(options) {
|
|
6768
|
-
super({ ...defaults$
|
|
6809
|
+
super({ ...defaults$8, ...options });
|
|
6769
6810
|
}
|
|
6770
6811
|
static schema() {
|
|
6771
6812
|
return {
|
|
@@ -6840,7 +6881,7 @@ class PreferButton extends Rule {
|
|
|
6840
6881
|
}
|
|
6841
6882
|
}
|
|
6842
6883
|
|
|
6843
|
-
const defaults$
|
|
6884
|
+
const defaults$7 = {
|
|
6844
6885
|
mapping: {
|
|
6845
6886
|
article: "article",
|
|
6846
6887
|
banner: "header",
|
|
@@ -6870,7 +6911,7 @@ const defaults$6 = {
|
|
|
6870
6911
|
};
|
|
6871
6912
|
class PreferNativeElement extends Rule {
|
|
6872
6913
|
constructor(options) {
|
|
6873
|
-
super({ ...defaults$
|
|
6914
|
+
super({ ...defaults$7, ...options });
|
|
6874
6915
|
}
|
|
6875
6916
|
static schema() {
|
|
6876
6917
|
return {
|
|
@@ -6990,7 +7031,7 @@ class PreferTbody extends Rule {
|
|
|
6990
7031
|
}
|
|
6991
7032
|
}
|
|
6992
7033
|
|
|
6993
|
-
const defaults$
|
|
7034
|
+
const defaults$6 = {
|
|
6994
7035
|
target: "all",
|
|
6995
7036
|
};
|
|
6996
7037
|
const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
|
|
@@ -7000,7 +7041,7 @@ const supportSri = {
|
|
|
7000
7041
|
};
|
|
7001
7042
|
class RequireSri extends Rule {
|
|
7002
7043
|
constructor(options) {
|
|
7003
|
-
super({ ...defaults$
|
|
7044
|
+
super({ ...defaults$6, ...options });
|
|
7004
7045
|
this.target = this.options.target;
|
|
7005
7046
|
}
|
|
7006
7047
|
static schema() {
|
|
@@ -7128,6 +7169,155 @@ class SvgFocusable extends Rule {
|
|
|
7128
7169
|
}
|
|
7129
7170
|
}
|
|
7130
7171
|
|
|
7172
|
+
const defaults$5 = {
|
|
7173
|
+
characters: [
|
|
7174
|
+
{ pattern: " ", replacement: " ", description: "non-breaking space" },
|
|
7175
|
+
{ pattern: "-", replacement: "‑", description: "non-breaking hyphen" },
|
|
7176
|
+
],
|
|
7177
|
+
ignoreClasses: [],
|
|
7178
|
+
};
|
|
7179
|
+
function constructRegex(characters) {
|
|
7180
|
+
const disallowed = characters
|
|
7181
|
+
.map((it) => {
|
|
7182
|
+
return it.pattern;
|
|
7183
|
+
})
|
|
7184
|
+
.join("|");
|
|
7185
|
+
const pattern = `(${disallowed})`;
|
|
7186
|
+
/* eslint-disable-next-line security/detect-non-literal-regexp */
|
|
7187
|
+
return new RegExp(pattern, "g");
|
|
7188
|
+
}
|
|
7189
|
+
function getText(node) {
|
|
7190
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
7191
|
+
const match = node.textContent.match(/^(\s*)(.*)$/);
|
|
7192
|
+
const [, leading, text] = match;
|
|
7193
|
+
return [leading.length, text.trimEnd()];
|
|
7194
|
+
}
|
|
7195
|
+
/**
|
|
7196
|
+
* Node 12 does not support String.matchAll, this simulates it's behavior.
|
|
7197
|
+
*/
|
|
7198
|
+
function matchAll(text, regexp) {
|
|
7199
|
+
/* eslint-disable-next-line security/detect-non-literal-regexp */
|
|
7200
|
+
const copy = new RegExp(regexp);
|
|
7201
|
+
const matches = [];
|
|
7202
|
+
/* eslint-disable-next-line no-constant-condition */
|
|
7203
|
+
while (true) {
|
|
7204
|
+
const match = copy.exec(text);
|
|
7205
|
+
if (match === null) {
|
|
7206
|
+
break;
|
|
7207
|
+
}
|
|
7208
|
+
matches.push(match);
|
|
7209
|
+
}
|
|
7210
|
+
return matches;
|
|
7211
|
+
}
|
|
7212
|
+
class TelNonBreaking extends Rule {
|
|
7213
|
+
constructor(options) {
|
|
7214
|
+
super({ ...defaults$5, ...options });
|
|
7215
|
+
this.regex = constructRegex(this.options.characters);
|
|
7216
|
+
}
|
|
7217
|
+
static schema() {
|
|
7218
|
+
return {
|
|
7219
|
+
characters: {
|
|
7220
|
+
type: "array",
|
|
7221
|
+
items: {
|
|
7222
|
+
type: "object",
|
|
7223
|
+
additionalProperties: false,
|
|
7224
|
+
properties: {
|
|
7225
|
+
pattern: {
|
|
7226
|
+
type: "string",
|
|
7227
|
+
},
|
|
7228
|
+
replacement: {
|
|
7229
|
+
type: "string",
|
|
7230
|
+
},
|
|
7231
|
+
description: {
|
|
7232
|
+
type: "string",
|
|
7233
|
+
},
|
|
7234
|
+
},
|
|
7235
|
+
},
|
|
7236
|
+
},
|
|
7237
|
+
ignoreClasses: {
|
|
7238
|
+
type: "array",
|
|
7239
|
+
items: {
|
|
7240
|
+
type: "string",
|
|
7241
|
+
},
|
|
7242
|
+
},
|
|
7243
|
+
};
|
|
7244
|
+
}
|
|
7245
|
+
documentation(context) {
|
|
7246
|
+
const { characters } = this.options;
|
|
7247
|
+
const replacements = characters.map((it) => {
|
|
7248
|
+
return ` - \`${it.pattern}\` - replace with \`${it.replacement}\` (${it.description}).`;
|
|
7249
|
+
});
|
|
7250
|
+
return {
|
|
7251
|
+
description: [
|
|
7252
|
+
context
|
|
7253
|
+
? `The \`${context.pattern}\` character should be replaced with \`${context.replacement}\` character (${context.description}) when used in a telephone number.`
|
|
7254
|
+
: `Replace this character with a non-breaking version.`,
|
|
7255
|
+
"",
|
|
7256
|
+
"Unless non-breaking characters is used there could be a line break inserted at that character.",
|
|
7257
|
+
"Line breaks make is harder to read and understand the telephone number.",
|
|
7258
|
+
"",
|
|
7259
|
+
"The following characters should be avoided:",
|
|
7260
|
+
"",
|
|
7261
|
+
...replacements,
|
|
7262
|
+
].join("\n"),
|
|
7263
|
+
url: ruleDocumentationUrl("@/rules/tel-non-breaking.ts"),
|
|
7264
|
+
};
|
|
7265
|
+
}
|
|
7266
|
+
setup() {
|
|
7267
|
+
this.on("element:ready", this.isRelevant, (event) => {
|
|
7268
|
+
const { target } = event;
|
|
7269
|
+
const { ignoreClasses } = this.options;
|
|
7270
|
+
/* skip if element has a class in the ignore list */
|
|
7271
|
+
const isIgnored = ignoreClasses.some((it) => target.classList.contains(it));
|
|
7272
|
+
if (isIgnored) {
|
|
7273
|
+
return;
|
|
7274
|
+
}
|
|
7275
|
+
this.walk(target);
|
|
7276
|
+
});
|
|
7277
|
+
}
|
|
7278
|
+
isRelevant(event) {
|
|
7279
|
+
const { target } = event;
|
|
7280
|
+
/* should only deal with anchors */
|
|
7281
|
+
if (!target.is("a")) {
|
|
7282
|
+
return false;
|
|
7283
|
+
}
|
|
7284
|
+
/* ignore if anchor does not have tel href */
|
|
7285
|
+
const attr = target.getAttribute("href");
|
|
7286
|
+
if (!attr || !attr.valueMatches(/^tel:/, false)) {
|
|
7287
|
+
return false;
|
|
7288
|
+
}
|
|
7289
|
+
return true;
|
|
7290
|
+
}
|
|
7291
|
+
walk(node) {
|
|
7292
|
+
for (const child of node.childNodes) {
|
|
7293
|
+
if (isTextNode(child)) {
|
|
7294
|
+
this.detectDisallowed(child);
|
|
7295
|
+
}
|
|
7296
|
+
else if (isElementNode(child)) {
|
|
7297
|
+
this.walk(child);
|
|
7298
|
+
}
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
detectDisallowed(node) {
|
|
7302
|
+
const [offset, text] = getText(node);
|
|
7303
|
+
const matches = matchAll(text, this.regex);
|
|
7304
|
+
for (const match of matches) {
|
|
7305
|
+
const detected = match[0];
|
|
7306
|
+
const entry = this.options.characters.find((it) => it.pattern === detected);
|
|
7307
|
+
/* istanbul ignore next: should never happen and cannot be tested, just a sanity check */
|
|
7308
|
+
if (!entry) {
|
|
7309
|
+
throw new Error(`Failed to find entry for "${detected}" when searching text "${text}"`);
|
|
7310
|
+
}
|
|
7311
|
+
const message = `"${detected}" should be replaced with "${entry.replacement}" in telephone number`;
|
|
7312
|
+
const begin = offset + match.index;
|
|
7313
|
+
const end = begin + detected.length;
|
|
7314
|
+
const location = sliceLocation(node.location, begin, end);
|
|
7315
|
+
const context = entry;
|
|
7316
|
+
this.report(node, message, location, context);
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
}
|
|
7320
|
+
|
|
7131
7321
|
function hasAltText(image) {
|
|
7132
7322
|
const alt = image.getAttribute("alt");
|
|
7133
7323
|
/* missing or boolean */
|
|
@@ -9158,9 +9348,6 @@ function parseStyle$1(name) {
|
|
|
9158
9348
|
case "selfclose":
|
|
9159
9349
|
case "selfclosing":
|
|
9160
9350
|
return Style$1.AlwaysSelfclose;
|
|
9161
|
-
/* istanbul ignore next: covered by schema validation */
|
|
9162
|
-
default:
|
|
9163
|
-
throw new Error(`Invalid style "${name}" for "void" rule`);
|
|
9164
9351
|
}
|
|
9165
9352
|
}
|
|
9166
9353
|
|
|
@@ -9592,6 +9779,7 @@ const bundledRules = {
|
|
|
9592
9779
|
"script-element": ScriptElement,
|
|
9593
9780
|
"script-type": ScriptType,
|
|
9594
9781
|
"svg-focusable": SvgFocusable,
|
|
9782
|
+
"tel-non-breaking": TelNonBreaking,
|
|
9595
9783
|
"text-content": TextContent,
|
|
9596
9784
|
"unrecognized-char-ref": UnknownCharReference,
|
|
9597
9785
|
void: Void,
|
|
@@ -9616,7 +9804,7 @@ const config$3 = {
|
|
|
9616
9804
|
"no-redundant-for": "error",
|
|
9617
9805
|
"no-redundant-role": "error",
|
|
9618
9806
|
"prefer-native-element": "error",
|
|
9619
|
-
"svg-focusable": "
|
|
9807
|
+
"svg-focusable": "off",
|
|
9620
9808
|
"text-content": "error",
|
|
9621
9809
|
"wcag/h30": "error",
|
|
9622
9810
|
"wcag/h32": "error",
|
|
@@ -9687,7 +9875,8 @@ const config$1 = {
|
|
|
9687
9875
|
"prefer-tbody": "error",
|
|
9688
9876
|
"script-element": "error",
|
|
9689
9877
|
"script-type": "error",
|
|
9690
|
-
"svg-focusable": "
|
|
9878
|
+
"svg-focusable": "off",
|
|
9879
|
+
"tel-non-breaking": "error",
|
|
9691
9880
|
"text-content": "error",
|
|
9692
9881
|
"unrecognized-char-ref": "error",
|
|
9693
9882
|
void: "off",
|
|
@@ -9793,7 +9982,8 @@ class ResolvedConfig {
|
|
|
9793
9982
|
});
|
|
9794
9983
|
}
|
|
9795
9984
|
catch (err) {
|
|
9796
|
-
|
|
9985
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9986
|
+
throw new NestedError(`When transforming "${source.filename}": ${message}`, err);
|
|
9797
9987
|
}
|
|
9798
9988
|
}
|
|
9799
9989
|
else {
|
|
@@ -9869,9 +10059,10 @@ function loadFromFile(filename) {
|
|
|
9869
10059
|
}
|
|
9870
10060
|
/* expand any relative paths */
|
|
9871
10061
|
for (const key of ["extends", "elements", "plugins"]) {
|
|
9872
|
-
|
|
10062
|
+
const value = json[key];
|
|
10063
|
+
if (!value)
|
|
9873
10064
|
continue;
|
|
9874
|
-
json[key] =
|
|
10065
|
+
json[key] = value.map((ref) => {
|
|
9875
10066
|
return Config.expandRelative(ref, path__default["default"].dirname(filename));
|
|
9876
10067
|
});
|
|
9877
10068
|
}
|
|
@@ -10029,6 +10220,7 @@ class Config {
|
|
|
10029
10220
|
/**
|
|
10030
10221
|
* Get element metadata.
|
|
10031
10222
|
*/
|
|
10223
|
+
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10032
10224
|
getMetaTable() {
|
|
10033
10225
|
/* use cached table if it exists */
|
|
10034
10226
|
if (this.metaTable) {
|
|
@@ -10067,7 +10259,8 @@ class Config {
|
|
|
10067
10259
|
metaTable.loadFromObject(legacyRequire(entry));
|
|
10068
10260
|
}
|
|
10069
10261
|
catch (err) {
|
|
10070
|
-
|
|
10262
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10263
|
+
throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, err);
|
|
10071
10264
|
}
|
|
10072
10265
|
}
|
|
10073
10266
|
metaTable.init();
|
|
@@ -10143,7 +10336,8 @@ class Config {
|
|
|
10143
10336
|
return plugin;
|
|
10144
10337
|
}
|
|
10145
10338
|
catch (err) {
|
|
10146
|
-
|
|
10339
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10340
|
+
throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, err);
|
|
10147
10341
|
}
|
|
10148
10342
|
});
|
|
10149
10343
|
}
|
|
@@ -10465,6 +10659,9 @@ class ParserError extends Error {
|
|
|
10465
10659
|
function isAttrValueToken(token) {
|
|
10466
10660
|
return Boolean(token && token.type === exports.TokenType.ATTR_VALUE);
|
|
10467
10661
|
}
|
|
10662
|
+
function svgShouldRetainTag(foreignTagName, tagName) {
|
|
10663
|
+
return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
|
|
10664
|
+
}
|
|
10468
10665
|
/**
|
|
10469
10666
|
* Parse HTML document into a DOM tree.
|
|
10470
10667
|
*
|
|
@@ -10477,6 +10674,7 @@ class Parser {
|
|
|
10477
10674
|
* @param config - Configuration
|
|
10478
10675
|
*/
|
|
10479
10676
|
constructor(config) {
|
|
10677
|
+
this.currentNamespace = "";
|
|
10480
10678
|
this.event = new EventHandler();
|
|
10481
10679
|
this.dom = null;
|
|
10482
10680
|
this.metaTable = config.getMetaTable();
|
|
@@ -10487,7 +10685,6 @@ class Parser {
|
|
|
10487
10685
|
* @param source - HTML markup.
|
|
10488
10686
|
* @returns DOM tree representing the HTML markup.
|
|
10489
10687
|
*/
|
|
10490
|
-
// eslint-disable-next-line complexity
|
|
10491
10688
|
parseHtml(source) {
|
|
10492
10689
|
var _a, _b, _c, _d;
|
|
10493
10690
|
if (typeof source === "string") {
|
|
@@ -10518,40 +10715,7 @@ class Parser {
|
|
|
10518
10715
|
let it = this.next(tokenStream);
|
|
10519
10716
|
while (!it.done) {
|
|
10520
10717
|
const token = it.value;
|
|
10521
|
-
|
|
10522
|
-
case exports.TokenType.UNICODE_BOM:
|
|
10523
|
-
/* ignore */
|
|
10524
|
-
break;
|
|
10525
|
-
case exports.TokenType.TAG_OPEN:
|
|
10526
|
-
this.consumeTag(source, token, tokenStream);
|
|
10527
|
-
break;
|
|
10528
|
-
case exports.TokenType.WHITESPACE:
|
|
10529
|
-
this.trigger("whitespace", {
|
|
10530
|
-
text: token.data[0],
|
|
10531
|
-
location: token.location,
|
|
10532
|
-
});
|
|
10533
|
-
this.appendText(token.data[0], token.location);
|
|
10534
|
-
break;
|
|
10535
|
-
case exports.TokenType.DIRECTIVE:
|
|
10536
|
-
this.consumeDirective(token);
|
|
10537
|
-
break;
|
|
10538
|
-
case exports.TokenType.CONDITIONAL:
|
|
10539
|
-
this.consumeConditional(token);
|
|
10540
|
-
break;
|
|
10541
|
-
case exports.TokenType.COMMENT:
|
|
10542
|
-
this.consumeComment(token);
|
|
10543
|
-
break;
|
|
10544
|
-
case exports.TokenType.DOCTYPE_OPEN:
|
|
10545
|
-
this.consumeDoctype(token, tokenStream);
|
|
10546
|
-
break;
|
|
10547
|
-
case exports.TokenType.TEXT:
|
|
10548
|
-
case exports.TokenType.TEMPLATING:
|
|
10549
|
-
this.appendText(token.data[0], token.location);
|
|
10550
|
-
break;
|
|
10551
|
-
case exports.TokenType.EOF:
|
|
10552
|
-
this.closeTree(source, token.location);
|
|
10553
|
-
break;
|
|
10554
|
-
}
|
|
10718
|
+
this.consume(source, token, tokenStream);
|
|
10555
10719
|
it = this.next(tokenStream);
|
|
10556
10720
|
}
|
|
10557
10721
|
/* resolve any dynamic meta element properties */
|
|
@@ -10598,13 +10762,50 @@ class Parser {
|
|
|
10598
10762
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10599
10763
|
}
|
|
10600
10764
|
}
|
|
10765
|
+
/* eslint-disable-next-line complexity */
|
|
10766
|
+
consume(source, token, tokenStream) {
|
|
10767
|
+
switch (token.type) {
|
|
10768
|
+
case exports.TokenType.UNICODE_BOM:
|
|
10769
|
+
/* ignore */
|
|
10770
|
+
break;
|
|
10771
|
+
case exports.TokenType.TAG_OPEN:
|
|
10772
|
+
this.consumeTag(source, token, tokenStream);
|
|
10773
|
+
break;
|
|
10774
|
+
case exports.TokenType.WHITESPACE:
|
|
10775
|
+
this.trigger("whitespace", {
|
|
10776
|
+
text: token.data[0],
|
|
10777
|
+
location: token.location,
|
|
10778
|
+
});
|
|
10779
|
+
this.appendText(token.data[0], token.location);
|
|
10780
|
+
break;
|
|
10781
|
+
case exports.TokenType.DIRECTIVE:
|
|
10782
|
+
this.consumeDirective(token);
|
|
10783
|
+
break;
|
|
10784
|
+
case exports.TokenType.CONDITIONAL:
|
|
10785
|
+
this.consumeConditional(token);
|
|
10786
|
+
break;
|
|
10787
|
+
case exports.TokenType.COMMENT:
|
|
10788
|
+
this.consumeComment(token);
|
|
10789
|
+
break;
|
|
10790
|
+
case exports.TokenType.DOCTYPE_OPEN:
|
|
10791
|
+
this.consumeDoctype(token, tokenStream);
|
|
10792
|
+
break;
|
|
10793
|
+
case exports.TokenType.TEXT:
|
|
10794
|
+
case exports.TokenType.TEMPLATING:
|
|
10795
|
+
this.appendText(token.data[0], token.location);
|
|
10796
|
+
break;
|
|
10797
|
+
case exports.TokenType.EOF:
|
|
10798
|
+
this.closeTree(source, token.location);
|
|
10799
|
+
break;
|
|
10800
|
+
}
|
|
10801
|
+
}
|
|
10601
10802
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10602
10803
|
consumeTag(source, startToken, tokenStream) {
|
|
10603
10804
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_CLOSE, startToken.location));
|
|
10604
10805
|
const endToken = tokens.slice(-1)[0];
|
|
10605
10806
|
const closeOptional = this.closeOptional(startToken);
|
|
10606
10807
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10607
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10808
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10608
10809
|
const isStartTag = !startToken.data[1];
|
|
10609
10810
|
const isClosing = !isStartTag || node.closed !== exports.NodeClosed.Open;
|
|
10610
10811
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10709,6 +10910,15 @@ class Parser {
|
|
|
10709
10910
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_OPEN, errorLocation));
|
|
10710
10911
|
const [last] = tokens.slice(-1);
|
|
10711
10912
|
const [, tagClosed, tagName] = last.data;
|
|
10913
|
+
/* special case: svg <title> and <desc> should be intact as it affects accessibility */
|
|
10914
|
+
if (!tagClosed && svgShouldRetainTag(foreignTagName, tagName)) {
|
|
10915
|
+
const oldNamespace = this.currentNamespace;
|
|
10916
|
+
this.currentNamespace = "svg";
|
|
10917
|
+
this.consumeTag(source, last, tokenStream);
|
|
10918
|
+
this.consumeUntilMatchingTag(source, tokenStream, tagName);
|
|
10919
|
+
this.currentNamespace = oldNamespace;
|
|
10920
|
+
continue;
|
|
10921
|
+
}
|
|
10712
10922
|
/* keep going unless the new tag matches the foreign root element */
|
|
10713
10923
|
if (tagName !== foreignTagName) {
|
|
10714
10924
|
continue;
|
|
@@ -10907,6 +11117,35 @@ class Parser {
|
|
|
10907
11117
|
}
|
|
10908
11118
|
throw new ParserError(errorLocation, `stream ended before ${exports.TokenType[search]} token was found`);
|
|
10909
11119
|
}
|
|
11120
|
+
/**
|
|
11121
|
+
* Consumes tokens until a matching close-tag is found. Tags are appended to
|
|
11122
|
+
* the document.
|
|
11123
|
+
*
|
|
11124
|
+
* @internal
|
|
11125
|
+
*/
|
|
11126
|
+
consumeUntilMatchingTag(source, tokenStream, searchTag) {
|
|
11127
|
+
let numOpen = 1;
|
|
11128
|
+
let it = this.next(tokenStream);
|
|
11129
|
+
while (!it.done) {
|
|
11130
|
+
const token = it.value;
|
|
11131
|
+
this.consume(source, token, tokenStream);
|
|
11132
|
+
if (token.type === exports.TokenType.TAG_OPEN) {
|
|
11133
|
+
const [, close, tagName] = token.data;
|
|
11134
|
+
if (tagName === searchTag) {
|
|
11135
|
+
if (close) {
|
|
11136
|
+
numOpen--;
|
|
11137
|
+
}
|
|
11138
|
+
else {
|
|
11139
|
+
numOpen++;
|
|
11140
|
+
}
|
|
11141
|
+
if (numOpen === 0) {
|
|
11142
|
+
return;
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
}
|
|
11146
|
+
it = this.next(tokenStream);
|
|
11147
|
+
}
|
|
11148
|
+
}
|
|
10910
11149
|
next(tokenStream) {
|
|
10911
11150
|
const it = tokenStream.next();
|
|
10912
11151
|
if (!it.done) {
|
|
@@ -11102,7 +11341,7 @@ class Engine {
|
|
|
11102
11341
|
/**
|
|
11103
11342
|
* Lint sources and return report
|
|
11104
11343
|
*
|
|
11105
|
-
* @param
|
|
11344
|
+
* @param sources - Sources to lint.
|
|
11106
11345
|
* @returns Report output.
|
|
11107
11346
|
*/
|
|
11108
11347
|
lint(sources) {
|
|
@@ -11196,7 +11435,7 @@ class Engine {
|
|
|
11196
11435
|
const lines = [];
|
|
11197
11436
|
function decoration(node) {
|
|
11198
11437
|
let output = "";
|
|
11199
|
-
if (node.
|
|
11438
|
+
if (node.id) {
|
|
11200
11439
|
output += `#${node.id}`;
|
|
11201
11440
|
}
|
|
11202
11441
|
if (node.hasAttribute("class")) {
|