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/es/core.js
CHANGED
|
@@ -2,6 +2,8 @@ import fs from 'fs';
|
|
|
2
2
|
import betterAjvErrors from '@sidvind/better-ajv-errors';
|
|
3
3
|
import Ajv from 'ajv';
|
|
4
4
|
import deepmerge from 'deepmerge';
|
|
5
|
+
import * as espree from 'espree';
|
|
6
|
+
import * as walk from 'acorn-walk';
|
|
5
7
|
import path from 'path';
|
|
6
8
|
import semver from 'semver';
|
|
7
9
|
import kleur from 'kleur';
|
|
@@ -245,7 +247,7 @@ class NestedError extends Error {
|
|
|
245
247
|
constructor(message, nested) {
|
|
246
248
|
super(message);
|
|
247
249
|
Error.captureStackTrace(this, NestedError);
|
|
248
|
-
if (nested) {
|
|
250
|
+
if (nested && nested.stack) {
|
|
249
251
|
this.stack += `\nCaused by: ${nested.stack}`;
|
|
250
252
|
}
|
|
251
253
|
}
|
|
@@ -1838,8 +1840,13 @@ var NodeClosed;
|
|
|
1838
1840
|
NodeClosed[NodeClosed["VoidSelfClosed"] = 3] = "VoidSelfClosed";
|
|
1839
1841
|
NodeClosed[NodeClosed["ImplicitClosed"] = 4] = "ImplicitClosed";
|
|
1840
1842
|
})(NodeClosed || (NodeClosed = {}));
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
+
/**
|
|
1844
|
+
* Returns true if the node is an element node.
|
|
1845
|
+
*
|
|
1846
|
+
* @public
|
|
1847
|
+
*/
|
|
1848
|
+
function isElementNode(node) {
|
|
1849
|
+
return Boolean(node && node.nodeType === NodeType.ELEMENT_NODE);
|
|
1843
1850
|
}
|
|
1844
1851
|
function isValidTagName(tagName) {
|
|
1845
1852
|
return Boolean(tagName !== "" && tagName !== "*");
|
|
@@ -1880,10 +1887,13 @@ class HtmlElement extends DOMNode {
|
|
|
1880
1887
|
}
|
|
1881
1888
|
/**
|
|
1882
1889
|
* @internal
|
|
1890
|
+
*
|
|
1891
|
+
* @param namespace - If given it is appended to the tagName.
|
|
1883
1892
|
*/
|
|
1884
|
-
static fromTokens(startToken, endToken, parent, metaTable) {
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1893
|
+
static fromTokens(startToken, endToken, parent, metaTable, namespace = "") {
|
|
1894
|
+
const name = startToken.data[2];
|
|
1895
|
+
const tagName = namespace ? `${namespace}:${name}` : name;
|
|
1896
|
+
if (!name) {
|
|
1887
1897
|
throw new Error("tagName cannot be empty");
|
|
1888
1898
|
}
|
|
1889
1899
|
const meta = metaTable ? metaTable.getMetaFor(tagName) : null;
|
|
@@ -1910,7 +1920,7 @@ class HtmlElement extends DOMNode {
|
|
|
1910
1920
|
* Similar to childNodes but only elements.
|
|
1911
1921
|
*/
|
|
1912
1922
|
get childElements() {
|
|
1913
|
-
return this.childNodes.filter(
|
|
1923
|
+
return this.childNodes.filter(isElementNode);
|
|
1914
1924
|
}
|
|
1915
1925
|
/**
|
|
1916
1926
|
* Find the first ancestor matching a selector.
|
|
@@ -2171,8 +2181,9 @@ class HtmlElement extends DOMNode {
|
|
|
2171
2181
|
}, []);
|
|
2172
2182
|
}
|
|
2173
2183
|
querySelector(selector) {
|
|
2184
|
+
var _a;
|
|
2174
2185
|
const it = this.querySelectorImpl(selector);
|
|
2175
|
-
return it.next().value
|
|
2186
|
+
return (_a = it.next().value) !== null && _a !== void 0 ? _a : null; // eslint-disable-line @typescript-eslint/no-unsafe-return
|
|
2176
2187
|
}
|
|
2177
2188
|
querySelectorAll(selector) {
|
|
2178
2189
|
const it = this.querySelectorImpl(selector);
|
|
@@ -2739,8 +2750,6 @@ var configurationSchema = {
|
|
|
2739
2750
|
};
|
|
2740
2751
|
|
|
2741
2752
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2742
|
-
const espree = legacyRequire("espree");
|
|
2743
|
-
const walk = legacyRequire("acorn-walk");
|
|
2744
2753
|
function joinTemplateLiteral(nodes) {
|
|
2745
2754
|
let offset = nodes[0].start + 1;
|
|
2746
2755
|
let output = "";
|
|
@@ -2921,7 +2930,8 @@ class TemplateExtractor {
|
|
|
2921
2930
|
extractObjectProperty(key) {
|
|
2922
2931
|
const result = [];
|
|
2923
2932
|
const { filename, data } = this;
|
|
2924
|
-
|
|
2933
|
+
const node = this.ast;
|
|
2934
|
+
walk.simple(node, {
|
|
2925
2935
|
Property(node) {
|
|
2926
2936
|
if (compareKey(node.key, key, filename)) {
|
|
2927
2937
|
const source = extractLiteral(node.value, filename, data);
|
|
@@ -2945,7 +2955,7 @@ var TRANSFORMER_API;
|
|
|
2945
2955
|
/** @public */
|
|
2946
2956
|
const name = "html-validate";
|
|
2947
2957
|
/** @public */
|
|
2948
|
-
const version = "6.
|
|
2958
|
+
const version = "6.7.0";
|
|
2949
2959
|
/** @public */
|
|
2950
2960
|
const homepage = "https://html-validate.org";
|
|
2951
2961
|
/** @public */
|
|
@@ -3266,7 +3276,7 @@ function ruleDocumentationUrl(filename) {
|
|
|
3266
3276
|
return `${homepage}/rules/${normalized}.html`;
|
|
3267
3277
|
}
|
|
3268
3278
|
|
|
3269
|
-
const defaults$
|
|
3279
|
+
const defaults$q = {
|
|
3270
3280
|
allowExternal: true,
|
|
3271
3281
|
allowRelative: true,
|
|
3272
3282
|
allowAbsolute: true,
|
|
@@ -3310,7 +3320,7 @@ function matchList(value, list) {
|
|
|
3310
3320
|
}
|
|
3311
3321
|
class AllowedLinks extends Rule {
|
|
3312
3322
|
constructor(options) {
|
|
3313
|
-
super({ ...defaults$
|
|
3323
|
+
super({ ...defaults$q, ...options });
|
|
3314
3324
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3315
3325
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3316
3326
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -3589,7 +3599,7 @@ class CaseStyle {
|
|
|
3589
3599
|
default: {
|
|
3590
3600
|
const last = names.slice(-1);
|
|
3591
3601
|
const rest = names.slice(0, -1);
|
|
3592
|
-
return `${rest.join(", ")} or ${last}`;
|
|
3602
|
+
return `${rest.join(", ")} or ${last[0]}`;
|
|
3593
3603
|
}
|
|
3594
3604
|
}
|
|
3595
3605
|
}
|
|
@@ -3605,19 +3615,19 @@ class CaseStyle {
|
|
|
3605
3615
|
case "camelcase":
|
|
3606
3616
|
return { pattern: /^[a-z][A-Za-z]*$/, name: "camelCase" };
|
|
3607
3617
|
default:
|
|
3608
|
-
throw new ConfigError(`Invalid style "${
|
|
3618
|
+
throw new ConfigError(`Invalid style "${cur}" for ${ruleId} rule`);
|
|
3609
3619
|
}
|
|
3610
3620
|
});
|
|
3611
3621
|
}
|
|
3612
3622
|
}
|
|
3613
3623
|
|
|
3614
|
-
const defaults$
|
|
3624
|
+
const defaults$p = {
|
|
3615
3625
|
style: "lowercase",
|
|
3616
3626
|
ignoreForeign: true,
|
|
3617
3627
|
};
|
|
3618
3628
|
class AttrCase extends Rule {
|
|
3619
3629
|
constructor(options) {
|
|
3620
|
-
super({ ...defaults$
|
|
3630
|
+
super({ ...defaults$p, ...options });
|
|
3621
3631
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3622
3632
|
}
|
|
3623
3633
|
static schema() {
|
|
@@ -3644,8 +3654,11 @@ class AttrCase extends Rule {
|
|
|
3644
3654
|
};
|
|
3645
3655
|
}
|
|
3646
3656
|
documentation() {
|
|
3657
|
+
const { style } = this.options;
|
|
3647
3658
|
return {
|
|
3648
|
-
description:
|
|
3659
|
+
description: Array.isArray(style)
|
|
3660
|
+
? [`Attribute name must be in one of:`, "", ...style.map((it) => `- ${it}`)].join("\n")
|
|
3661
|
+
: `Attribute name must be in ${style}.`,
|
|
3649
3662
|
url: ruleDocumentationUrl("@/rules/attr-case.ts"),
|
|
3650
3663
|
};
|
|
3651
3664
|
}
|
|
@@ -3936,7 +3949,7 @@ function isRelevant$3(event) {
|
|
|
3936
3949
|
class AttrDelimiter extends Rule {
|
|
3937
3950
|
documentation() {
|
|
3938
3951
|
return {
|
|
3939
|
-
description: `Attribute value
|
|
3952
|
+
description: `Attribute value must not be separated by whitespace.`,
|
|
3940
3953
|
url: ruleDocumentationUrl("@/rules/attr-delimiter.ts"),
|
|
3941
3954
|
};
|
|
3942
3955
|
}
|
|
@@ -3953,7 +3966,7 @@ class AttrDelimiter extends Rule {
|
|
|
3953
3966
|
}
|
|
3954
3967
|
|
|
3955
3968
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
3956
|
-
const defaults$
|
|
3969
|
+
const defaults$o = {
|
|
3957
3970
|
pattern: DEFAULT_PATTERN,
|
|
3958
3971
|
ignoreForeign: true,
|
|
3959
3972
|
};
|
|
@@ -3990,7 +4003,7 @@ function generateDescription(name, pattern) {
|
|
|
3990
4003
|
}
|
|
3991
4004
|
class AttrPattern extends Rule {
|
|
3992
4005
|
constructor(options) {
|
|
3993
|
-
super({ ...defaults$
|
|
4006
|
+
super({ ...defaults$o, ...options });
|
|
3994
4007
|
this.pattern = generateRegexp(this.options.pattern);
|
|
3995
4008
|
}
|
|
3996
4009
|
static schema() {
|
|
@@ -4050,13 +4063,13 @@ var QuoteStyle;
|
|
|
4050
4063
|
QuoteStyle["DOUBLE_QUOTE"] = "\"";
|
|
4051
4064
|
QuoteStyle["AUTO_QUOTE"] = "auto";
|
|
4052
4065
|
})(QuoteStyle || (QuoteStyle = {}));
|
|
4053
|
-
const defaults$
|
|
4066
|
+
const defaults$n = {
|
|
4054
4067
|
style: "auto",
|
|
4055
4068
|
unquoted: false,
|
|
4056
4069
|
};
|
|
4057
4070
|
class AttrQuotes extends Rule {
|
|
4058
4071
|
constructor(options) {
|
|
4059
|
-
super({ ...defaults$
|
|
4072
|
+
super({ ...defaults$n, ...options });
|
|
4060
4073
|
this.style = parseStyle$4(this.options.style);
|
|
4061
4074
|
}
|
|
4062
4075
|
static schema() {
|
|
@@ -4221,12 +4234,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4221
4234
|
}
|
|
4222
4235
|
}
|
|
4223
4236
|
|
|
4224
|
-
const defaults$
|
|
4237
|
+
const defaults$m = {
|
|
4225
4238
|
style: "omit",
|
|
4226
4239
|
};
|
|
4227
4240
|
class AttributeBooleanStyle extends Rule {
|
|
4228
4241
|
constructor(options) {
|
|
4229
|
-
super({ ...defaults$
|
|
4242
|
+
super({ ...defaults$m, ...options });
|
|
4230
4243
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
4231
4244
|
}
|
|
4232
4245
|
static schema() {
|
|
@@ -4302,12 +4315,12 @@ function reportMessage$1(attr, style) {
|
|
|
4302
4315
|
return "";
|
|
4303
4316
|
}
|
|
4304
4317
|
|
|
4305
|
-
const defaults$
|
|
4318
|
+
const defaults$l = {
|
|
4306
4319
|
style: "omit",
|
|
4307
4320
|
};
|
|
4308
4321
|
class AttributeEmptyStyle extends Rule {
|
|
4309
4322
|
constructor(options) {
|
|
4310
|
-
super({ ...defaults$
|
|
4323
|
+
super({ ...defaults$l, ...options });
|
|
4311
4324
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4312
4325
|
}
|
|
4313
4326
|
static schema() {
|
|
@@ -4415,12 +4428,12 @@ function describePattern(pattern) {
|
|
|
4415
4428
|
}
|
|
4416
4429
|
}
|
|
4417
4430
|
|
|
4418
|
-
const defaults$
|
|
4431
|
+
const defaults$k = {
|
|
4419
4432
|
pattern: "kebabcase",
|
|
4420
4433
|
};
|
|
4421
4434
|
class ClassPattern extends Rule {
|
|
4422
4435
|
constructor(options) {
|
|
4423
|
-
super({ ...defaults$
|
|
4436
|
+
super({ ...defaults$k, ...options });
|
|
4424
4437
|
this.pattern = parsePattern(this.options.pattern);
|
|
4425
4438
|
}
|
|
4426
4439
|
static schema() {
|
|
@@ -4446,7 +4459,9 @@ class ClassPattern extends Rule {
|
|
|
4446
4459
|
classes.forEach((cur, index) => {
|
|
4447
4460
|
if (!cur.match(this.pattern)) {
|
|
4448
4461
|
const location = classes.location(index);
|
|
4449
|
-
|
|
4462
|
+
const pattern = this.pattern.toString();
|
|
4463
|
+
const message = `Class "${cur}" does not match required pattern "${pattern}"`;
|
|
4464
|
+
this.report(event.target, message, location);
|
|
4450
4465
|
}
|
|
4451
4466
|
});
|
|
4452
4467
|
});
|
|
@@ -4527,13 +4542,13 @@ class CloseOrder extends Rule {
|
|
|
4527
4542
|
}
|
|
4528
4543
|
}
|
|
4529
4544
|
|
|
4530
|
-
const defaults$
|
|
4545
|
+
const defaults$j = {
|
|
4531
4546
|
include: null,
|
|
4532
4547
|
exclude: null,
|
|
4533
4548
|
};
|
|
4534
4549
|
class Deprecated extends Rule {
|
|
4535
4550
|
constructor(options) {
|
|
4536
|
-
super({ ...defaults$
|
|
4551
|
+
super({ ...defaults$j, ...options });
|
|
4537
4552
|
}
|
|
4538
4553
|
static schema() {
|
|
4539
4554
|
return {
|
|
@@ -4696,12 +4711,12 @@ class NoStyleTag$1 extends Rule {
|
|
|
4696
4711
|
}
|
|
4697
4712
|
}
|
|
4698
4713
|
|
|
4699
|
-
const defaults$
|
|
4714
|
+
const defaults$i = {
|
|
4700
4715
|
style: "uppercase",
|
|
4701
4716
|
};
|
|
4702
4717
|
class DoctypeStyle extends Rule {
|
|
4703
4718
|
constructor(options) {
|
|
4704
|
-
super({ ...defaults$
|
|
4719
|
+
super({ ...defaults$i, ...options });
|
|
4705
4720
|
}
|
|
4706
4721
|
static schema() {
|
|
4707
4722
|
return {
|
|
@@ -4733,12 +4748,12 @@ class DoctypeStyle extends Rule {
|
|
|
4733
4748
|
}
|
|
4734
4749
|
}
|
|
4735
4750
|
|
|
4736
|
-
const defaults$
|
|
4751
|
+
const defaults$h = {
|
|
4737
4752
|
style: "lowercase",
|
|
4738
4753
|
};
|
|
4739
4754
|
class ElementCase extends Rule {
|
|
4740
4755
|
constructor(options) {
|
|
4741
|
-
super({ ...defaults$
|
|
4756
|
+
super({ ...defaults$h, ...options });
|
|
4742
4757
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4743
4758
|
}
|
|
4744
4759
|
static schema() {
|
|
@@ -4762,8 +4777,11 @@ class ElementCase extends Rule {
|
|
|
4762
4777
|
};
|
|
4763
4778
|
}
|
|
4764
4779
|
documentation() {
|
|
4780
|
+
const { style } = this.options;
|
|
4765
4781
|
return {
|
|
4766
|
-
description:
|
|
4782
|
+
description: Array.isArray(style)
|
|
4783
|
+
? [`Element tagname must be in one of:`, "", ...style.map((it) => `- ${it}`)].join("\n")
|
|
4784
|
+
: `Element tagname must be in ${style}.`,
|
|
4767
4785
|
url: ruleDocumentationUrl("@/rules/element-case.ts"),
|
|
4768
4786
|
};
|
|
4769
4787
|
}
|
|
@@ -4801,14 +4819,14 @@ class ElementCase extends Rule {
|
|
|
4801
4819
|
}
|
|
4802
4820
|
}
|
|
4803
4821
|
|
|
4804
|
-
const defaults$
|
|
4822
|
+
const defaults$g = {
|
|
4805
4823
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
4806
4824
|
whitelist: [],
|
|
4807
4825
|
blacklist: [],
|
|
4808
4826
|
};
|
|
4809
4827
|
class ElementName extends Rule {
|
|
4810
4828
|
constructor(options) {
|
|
4811
|
-
super({ ...defaults$
|
|
4829
|
+
super({ ...defaults$g, ...options });
|
|
4812
4830
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4813
4831
|
this.pattern = new RegExp(this.options.pattern);
|
|
4814
4832
|
}
|
|
@@ -4849,7 +4867,7 @@ class ElementName extends Rule {
|
|
|
4849
4867
|
...context.blacklist.map((cur) => `- ${cur}`),
|
|
4850
4868
|
];
|
|
4851
4869
|
}
|
|
4852
|
-
if (context.pattern !== defaults$
|
|
4870
|
+
if (context.pattern !== defaults$g.pattern) {
|
|
4853
4871
|
return [
|
|
4854
4872
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
4855
4873
|
"",
|
|
@@ -5251,7 +5269,7 @@ class EmptyTitle extends Rule {
|
|
|
5251
5269
|
}
|
|
5252
5270
|
}
|
|
5253
5271
|
|
|
5254
|
-
const defaults$
|
|
5272
|
+
const defaults$f = {
|
|
5255
5273
|
allowMultipleH1: false,
|
|
5256
5274
|
minInitialRank: "h1",
|
|
5257
5275
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
@@ -5282,7 +5300,7 @@ function parseMaxInitial(value) {
|
|
|
5282
5300
|
}
|
|
5283
5301
|
class HeadingLevel extends Rule {
|
|
5284
5302
|
constructor(options) {
|
|
5285
|
-
super({ ...defaults$
|
|
5303
|
+
super({ ...defaults$f, ...options });
|
|
5286
5304
|
this.stack = [];
|
|
5287
5305
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
5288
5306
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -5440,12 +5458,12 @@ class HeadingLevel extends Rule {
|
|
|
5440
5458
|
}
|
|
5441
5459
|
}
|
|
5442
5460
|
|
|
5443
|
-
const defaults$
|
|
5461
|
+
const defaults$e = {
|
|
5444
5462
|
pattern: "kebabcase",
|
|
5445
5463
|
};
|
|
5446
5464
|
class IdPattern extends Rule {
|
|
5447
5465
|
constructor(options) {
|
|
5448
|
-
super({ ...defaults$
|
|
5466
|
+
super({ ...defaults$e, ...options });
|
|
5449
5467
|
this.pattern = parsePattern(this.options.pattern);
|
|
5450
5468
|
}
|
|
5451
5469
|
static schema() {
|
|
@@ -5464,6 +5482,7 @@ class IdPattern extends Rule {
|
|
|
5464
5482
|
}
|
|
5465
5483
|
setup() {
|
|
5466
5484
|
this.on("attr", (event) => {
|
|
5485
|
+
var _a;
|
|
5467
5486
|
if (event.key.toLowerCase() !== "id") {
|
|
5468
5487
|
return;
|
|
5469
5488
|
}
|
|
@@ -5472,7 +5491,10 @@ class IdPattern extends Rule {
|
|
|
5472
5491
|
return;
|
|
5473
5492
|
}
|
|
5474
5493
|
if (!event.value || !event.value.match(this.pattern)) {
|
|
5475
|
-
|
|
5494
|
+
const value = (_a = event.value) !== null && _a !== void 0 ? _a : "";
|
|
5495
|
+
const pattern = this.pattern.toString();
|
|
5496
|
+
const message = `ID "${value}" does not match required pattern "${pattern}"`;
|
|
5497
|
+
this.report(event.target, message, event.valueLocation);
|
|
5476
5498
|
}
|
|
5477
5499
|
});
|
|
5478
5500
|
}
|
|
@@ -5792,12 +5814,12 @@ function findLabelByParent(el) {
|
|
|
5792
5814
|
return [];
|
|
5793
5815
|
}
|
|
5794
5816
|
|
|
5795
|
-
const defaults$
|
|
5817
|
+
const defaults$d = {
|
|
5796
5818
|
maxlength: 70,
|
|
5797
5819
|
};
|
|
5798
5820
|
class LongTitle extends Rule {
|
|
5799
5821
|
constructor(options) {
|
|
5800
|
-
super({ ...defaults$
|
|
5822
|
+
super({ ...defaults$d, ...options });
|
|
5801
5823
|
this.maxlength = this.options.maxlength;
|
|
5802
5824
|
}
|
|
5803
5825
|
static schema() {
|
|
@@ -5944,13 +5966,13 @@ class MultipleLabeledControls extends Rule {
|
|
|
5944
5966
|
}
|
|
5945
5967
|
}
|
|
5946
5968
|
|
|
5947
|
-
const defaults$
|
|
5969
|
+
const defaults$c = {
|
|
5948
5970
|
include: null,
|
|
5949
5971
|
exclude: null,
|
|
5950
5972
|
};
|
|
5951
5973
|
class NoAutoplay extends Rule {
|
|
5952
5974
|
constructor(options) {
|
|
5953
|
-
super({ ...defaults$
|
|
5975
|
+
super({ ...defaults$c, ...options });
|
|
5954
5976
|
}
|
|
5955
5977
|
documentation(context) {
|
|
5956
5978
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -6191,7 +6213,7 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
6191
6213
|
}
|
|
6192
6214
|
}
|
|
6193
6215
|
|
|
6194
|
-
const defaults$
|
|
6216
|
+
const defaults$b = {
|
|
6195
6217
|
include: null,
|
|
6196
6218
|
exclude: null,
|
|
6197
6219
|
allowedProperties: ["display"],
|
|
@@ -6208,7 +6230,7 @@ function getCSSDeclarations(value) {
|
|
|
6208
6230
|
}
|
|
6209
6231
|
class NoInlineStyle extends Rule {
|
|
6210
6232
|
constructor(options) {
|
|
6211
|
-
super({ ...defaults$
|
|
6233
|
+
super({ ...defaults$b, ...options });
|
|
6212
6234
|
}
|
|
6213
6235
|
static schema() {
|
|
6214
6236
|
return {
|
|
@@ -6413,24 +6435,24 @@ class NoMultipleMain extends Rule {
|
|
|
6413
6435
|
}
|
|
6414
6436
|
}
|
|
6415
6437
|
|
|
6416
|
-
const defaults$
|
|
6438
|
+
const defaults$a = {
|
|
6417
6439
|
relaxed: false,
|
|
6418
6440
|
};
|
|
6419
6441
|
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
|
|
6420
6442
|
const unquotedAttrRegexp = /([<>"'=`]|&(?![a-zA-Z0-9#]+;))/g;
|
|
6421
6443
|
const matchTemplate = /^(<%.*?%>|<\?.*?\?>|<\$.*?\$>)$/s;
|
|
6422
|
-
const replacementTable =
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6444
|
+
const replacementTable = {
|
|
6445
|
+
'"': """,
|
|
6446
|
+
"&": "&",
|
|
6447
|
+
"'": "'",
|
|
6448
|
+
"<": "<",
|
|
6449
|
+
"=": "=",
|
|
6450
|
+
">": ">",
|
|
6451
|
+
"`": "`",
|
|
6452
|
+
};
|
|
6431
6453
|
class NoRawCharacters extends Rule {
|
|
6432
6454
|
constructor(options) {
|
|
6433
|
-
super({ ...defaults$
|
|
6455
|
+
super({ ...defaults$a, ...options });
|
|
6434
6456
|
this.relaxed = this.options.relaxed;
|
|
6435
6457
|
}
|
|
6436
6458
|
static schema() {
|
|
@@ -6480,7 +6502,6 @@ class NoRawCharacters extends Rule {
|
|
|
6480
6502
|
* @param text - The full text to find unescaped raw characters in.
|
|
6481
6503
|
* @param location - Location of text.
|
|
6482
6504
|
* @param regexp - Regexp pattern to match using.
|
|
6483
|
-
* @param ignore - List of characters to ignore for this text.
|
|
6484
6505
|
*/
|
|
6485
6506
|
findRawChars(node, text, location, regexp) {
|
|
6486
6507
|
let match;
|
|
@@ -6496,7 +6517,7 @@ class NoRawCharacters extends Rule {
|
|
|
6496
6517
|
continue;
|
|
6497
6518
|
}
|
|
6498
6519
|
/* determine replacement character and location */
|
|
6499
|
-
const replacement = replacementTable
|
|
6520
|
+
const replacement = replacementTable[char];
|
|
6500
6521
|
const charLocation = sliceLocation(location, match.index, match.index + 1);
|
|
6501
6522
|
/* report as error */
|
|
6502
6523
|
this.report(node, `Raw "${char}" must be encoded as "${replacement}"`, charLocation);
|
|
@@ -6609,13 +6630,13 @@ class NoRedundantRole extends Rule {
|
|
|
6609
6630
|
}
|
|
6610
6631
|
|
|
6611
6632
|
const xmlns = /^(.+):.+$/;
|
|
6612
|
-
const defaults$
|
|
6633
|
+
const defaults$9 = {
|
|
6613
6634
|
ignoreForeign: true,
|
|
6614
6635
|
ignoreXML: true,
|
|
6615
6636
|
};
|
|
6616
6637
|
class NoSelfClosing extends Rule {
|
|
6617
6638
|
constructor(options) {
|
|
6618
|
-
super({ ...defaults$
|
|
6639
|
+
super({ ...defaults$9, ...options });
|
|
6619
6640
|
}
|
|
6620
6641
|
static schema() {
|
|
6621
6642
|
return {
|
|
@@ -6748,13 +6769,13 @@ const replacement = {
|
|
|
6748
6769
|
reset: '<button type="reset">',
|
|
6749
6770
|
image: '<button type="button">',
|
|
6750
6771
|
};
|
|
6751
|
-
const defaults$
|
|
6772
|
+
const defaults$8 = {
|
|
6752
6773
|
include: null,
|
|
6753
6774
|
exclude: null,
|
|
6754
6775
|
};
|
|
6755
6776
|
class PreferButton extends Rule {
|
|
6756
6777
|
constructor(options) {
|
|
6757
|
-
super({ ...defaults$
|
|
6778
|
+
super({ ...defaults$8, ...options });
|
|
6758
6779
|
}
|
|
6759
6780
|
static schema() {
|
|
6760
6781
|
return {
|
|
@@ -6829,7 +6850,7 @@ class PreferButton extends Rule {
|
|
|
6829
6850
|
}
|
|
6830
6851
|
}
|
|
6831
6852
|
|
|
6832
|
-
const defaults$
|
|
6853
|
+
const defaults$7 = {
|
|
6833
6854
|
mapping: {
|
|
6834
6855
|
article: "article",
|
|
6835
6856
|
banner: "header",
|
|
@@ -6859,7 +6880,7 @@ const defaults$6 = {
|
|
|
6859
6880
|
};
|
|
6860
6881
|
class PreferNativeElement extends Rule {
|
|
6861
6882
|
constructor(options) {
|
|
6862
|
-
super({ ...defaults$
|
|
6883
|
+
super({ ...defaults$7, ...options });
|
|
6863
6884
|
}
|
|
6864
6885
|
static schema() {
|
|
6865
6886
|
return {
|
|
@@ -6979,7 +7000,7 @@ class PreferTbody extends Rule {
|
|
|
6979
7000
|
}
|
|
6980
7001
|
}
|
|
6981
7002
|
|
|
6982
|
-
const defaults$
|
|
7003
|
+
const defaults$6 = {
|
|
6983
7004
|
target: "all",
|
|
6984
7005
|
};
|
|
6985
7006
|
const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
|
|
@@ -6989,7 +7010,7 @@ const supportSri = {
|
|
|
6989
7010
|
};
|
|
6990
7011
|
class RequireSri extends Rule {
|
|
6991
7012
|
constructor(options) {
|
|
6992
|
-
super({ ...defaults$
|
|
7013
|
+
super({ ...defaults$6, ...options });
|
|
6993
7014
|
this.target = this.options.target;
|
|
6994
7015
|
}
|
|
6995
7016
|
static schema() {
|
|
@@ -7117,6 +7138,155 @@ class SvgFocusable extends Rule {
|
|
|
7117
7138
|
}
|
|
7118
7139
|
}
|
|
7119
7140
|
|
|
7141
|
+
const defaults$5 = {
|
|
7142
|
+
characters: [
|
|
7143
|
+
{ pattern: " ", replacement: " ", description: "non-breaking space" },
|
|
7144
|
+
{ pattern: "-", replacement: "‑", description: "non-breaking hyphen" },
|
|
7145
|
+
],
|
|
7146
|
+
ignoreClasses: [],
|
|
7147
|
+
};
|
|
7148
|
+
function constructRegex(characters) {
|
|
7149
|
+
const disallowed = characters
|
|
7150
|
+
.map((it) => {
|
|
7151
|
+
return it.pattern;
|
|
7152
|
+
})
|
|
7153
|
+
.join("|");
|
|
7154
|
+
const pattern = `(${disallowed})`;
|
|
7155
|
+
/* eslint-disable-next-line security/detect-non-literal-regexp */
|
|
7156
|
+
return new RegExp(pattern, "g");
|
|
7157
|
+
}
|
|
7158
|
+
function getText(node) {
|
|
7159
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
7160
|
+
const match = node.textContent.match(/^(\s*)(.*)$/);
|
|
7161
|
+
const [, leading, text] = match;
|
|
7162
|
+
return [leading.length, text.trimEnd()];
|
|
7163
|
+
}
|
|
7164
|
+
/**
|
|
7165
|
+
* Node 12 does not support String.matchAll, this simulates it's behavior.
|
|
7166
|
+
*/
|
|
7167
|
+
function matchAll(text, regexp) {
|
|
7168
|
+
/* eslint-disable-next-line security/detect-non-literal-regexp */
|
|
7169
|
+
const copy = new RegExp(regexp);
|
|
7170
|
+
const matches = [];
|
|
7171
|
+
/* eslint-disable-next-line no-constant-condition */
|
|
7172
|
+
while (true) {
|
|
7173
|
+
const match = copy.exec(text);
|
|
7174
|
+
if (match === null) {
|
|
7175
|
+
break;
|
|
7176
|
+
}
|
|
7177
|
+
matches.push(match);
|
|
7178
|
+
}
|
|
7179
|
+
return matches;
|
|
7180
|
+
}
|
|
7181
|
+
class TelNonBreaking extends Rule {
|
|
7182
|
+
constructor(options) {
|
|
7183
|
+
super({ ...defaults$5, ...options });
|
|
7184
|
+
this.regex = constructRegex(this.options.characters);
|
|
7185
|
+
}
|
|
7186
|
+
static schema() {
|
|
7187
|
+
return {
|
|
7188
|
+
characters: {
|
|
7189
|
+
type: "array",
|
|
7190
|
+
items: {
|
|
7191
|
+
type: "object",
|
|
7192
|
+
additionalProperties: false,
|
|
7193
|
+
properties: {
|
|
7194
|
+
pattern: {
|
|
7195
|
+
type: "string",
|
|
7196
|
+
},
|
|
7197
|
+
replacement: {
|
|
7198
|
+
type: "string",
|
|
7199
|
+
},
|
|
7200
|
+
description: {
|
|
7201
|
+
type: "string",
|
|
7202
|
+
},
|
|
7203
|
+
},
|
|
7204
|
+
},
|
|
7205
|
+
},
|
|
7206
|
+
ignoreClasses: {
|
|
7207
|
+
type: "array",
|
|
7208
|
+
items: {
|
|
7209
|
+
type: "string",
|
|
7210
|
+
},
|
|
7211
|
+
},
|
|
7212
|
+
};
|
|
7213
|
+
}
|
|
7214
|
+
documentation(context) {
|
|
7215
|
+
const { characters } = this.options;
|
|
7216
|
+
const replacements = characters.map((it) => {
|
|
7217
|
+
return ` - \`${it.pattern}\` - replace with \`${it.replacement}\` (${it.description}).`;
|
|
7218
|
+
});
|
|
7219
|
+
return {
|
|
7220
|
+
description: [
|
|
7221
|
+
context
|
|
7222
|
+
? `The \`${context.pattern}\` character should be replaced with \`${context.replacement}\` character (${context.description}) when used in a telephone number.`
|
|
7223
|
+
: `Replace this character with a non-breaking version.`,
|
|
7224
|
+
"",
|
|
7225
|
+
"Unless non-breaking characters is used there could be a line break inserted at that character.",
|
|
7226
|
+
"Line breaks make is harder to read and understand the telephone number.",
|
|
7227
|
+
"",
|
|
7228
|
+
"The following characters should be avoided:",
|
|
7229
|
+
"",
|
|
7230
|
+
...replacements,
|
|
7231
|
+
].join("\n"),
|
|
7232
|
+
url: ruleDocumentationUrl("@/rules/tel-non-breaking.ts"),
|
|
7233
|
+
};
|
|
7234
|
+
}
|
|
7235
|
+
setup() {
|
|
7236
|
+
this.on("element:ready", this.isRelevant, (event) => {
|
|
7237
|
+
const { target } = event;
|
|
7238
|
+
const { ignoreClasses } = this.options;
|
|
7239
|
+
/* skip if element has a class in the ignore list */
|
|
7240
|
+
const isIgnored = ignoreClasses.some((it) => target.classList.contains(it));
|
|
7241
|
+
if (isIgnored) {
|
|
7242
|
+
return;
|
|
7243
|
+
}
|
|
7244
|
+
this.walk(target);
|
|
7245
|
+
});
|
|
7246
|
+
}
|
|
7247
|
+
isRelevant(event) {
|
|
7248
|
+
const { target } = event;
|
|
7249
|
+
/* should only deal with anchors */
|
|
7250
|
+
if (!target.is("a")) {
|
|
7251
|
+
return false;
|
|
7252
|
+
}
|
|
7253
|
+
/* ignore if anchor does not have tel href */
|
|
7254
|
+
const attr = target.getAttribute("href");
|
|
7255
|
+
if (!attr || !attr.valueMatches(/^tel:/, false)) {
|
|
7256
|
+
return false;
|
|
7257
|
+
}
|
|
7258
|
+
return true;
|
|
7259
|
+
}
|
|
7260
|
+
walk(node) {
|
|
7261
|
+
for (const child of node.childNodes) {
|
|
7262
|
+
if (isTextNode(child)) {
|
|
7263
|
+
this.detectDisallowed(child);
|
|
7264
|
+
}
|
|
7265
|
+
else if (isElementNode(child)) {
|
|
7266
|
+
this.walk(child);
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
detectDisallowed(node) {
|
|
7271
|
+
const [offset, text] = getText(node);
|
|
7272
|
+
const matches = matchAll(text, this.regex);
|
|
7273
|
+
for (const match of matches) {
|
|
7274
|
+
const detected = match[0];
|
|
7275
|
+
const entry = this.options.characters.find((it) => it.pattern === detected);
|
|
7276
|
+
/* istanbul ignore next: should never happen and cannot be tested, just a sanity check */
|
|
7277
|
+
if (!entry) {
|
|
7278
|
+
throw new Error(`Failed to find entry for "${detected}" when searching text "${text}"`);
|
|
7279
|
+
}
|
|
7280
|
+
const message = `"${detected}" should be replaced with "${entry.replacement}" in telephone number`;
|
|
7281
|
+
const begin = offset + match.index;
|
|
7282
|
+
const end = begin + detected.length;
|
|
7283
|
+
const location = sliceLocation(node.location, begin, end);
|
|
7284
|
+
const context = entry;
|
|
7285
|
+
this.report(node, message, location, context);
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7289
|
+
|
|
7120
7290
|
function hasAltText(image) {
|
|
7121
7291
|
const alt = image.getAttribute("alt");
|
|
7122
7292
|
/* missing or boolean */
|
|
@@ -9147,9 +9317,6 @@ function parseStyle$1(name) {
|
|
|
9147
9317
|
case "selfclose":
|
|
9148
9318
|
case "selfclosing":
|
|
9149
9319
|
return Style$1.AlwaysSelfclose;
|
|
9150
|
-
/* istanbul ignore next: covered by schema validation */
|
|
9151
|
-
default:
|
|
9152
|
-
throw new Error(`Invalid style "${name}" for "void" rule`);
|
|
9153
9320
|
}
|
|
9154
9321
|
}
|
|
9155
9322
|
|
|
@@ -9581,6 +9748,7 @@ const bundledRules = {
|
|
|
9581
9748
|
"script-element": ScriptElement,
|
|
9582
9749
|
"script-type": ScriptType,
|
|
9583
9750
|
"svg-focusable": SvgFocusable,
|
|
9751
|
+
"tel-non-breaking": TelNonBreaking,
|
|
9584
9752
|
"text-content": TextContent,
|
|
9585
9753
|
"unrecognized-char-ref": UnknownCharReference,
|
|
9586
9754
|
void: Void,
|
|
@@ -9605,7 +9773,7 @@ const config$3 = {
|
|
|
9605
9773
|
"no-redundant-for": "error",
|
|
9606
9774
|
"no-redundant-role": "error",
|
|
9607
9775
|
"prefer-native-element": "error",
|
|
9608
|
-
"svg-focusable": "
|
|
9776
|
+
"svg-focusable": "off",
|
|
9609
9777
|
"text-content": "error",
|
|
9610
9778
|
"wcag/h30": "error",
|
|
9611
9779
|
"wcag/h32": "error",
|
|
@@ -9676,7 +9844,8 @@ const config$1 = {
|
|
|
9676
9844
|
"prefer-tbody": "error",
|
|
9677
9845
|
"script-element": "error",
|
|
9678
9846
|
"script-type": "error",
|
|
9679
|
-
"svg-focusable": "
|
|
9847
|
+
"svg-focusable": "off",
|
|
9848
|
+
"tel-non-breaking": "error",
|
|
9680
9849
|
"text-content": "error",
|
|
9681
9850
|
"unrecognized-char-ref": "error",
|
|
9682
9851
|
void: "off",
|
|
@@ -9782,7 +9951,8 @@ class ResolvedConfig {
|
|
|
9782
9951
|
});
|
|
9783
9952
|
}
|
|
9784
9953
|
catch (err) {
|
|
9785
|
-
|
|
9954
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9955
|
+
throw new NestedError(`When transforming "${source.filename}": ${message}`, err);
|
|
9786
9956
|
}
|
|
9787
9957
|
}
|
|
9788
9958
|
else {
|
|
@@ -9858,9 +10028,10 @@ function loadFromFile(filename) {
|
|
|
9858
10028
|
}
|
|
9859
10029
|
/* expand any relative paths */
|
|
9860
10030
|
for (const key of ["extends", "elements", "plugins"]) {
|
|
9861
|
-
|
|
10031
|
+
const value = json[key];
|
|
10032
|
+
if (!value)
|
|
9862
10033
|
continue;
|
|
9863
|
-
json[key] =
|
|
10034
|
+
json[key] = value.map((ref) => {
|
|
9864
10035
|
return Config.expandRelative(ref, path.dirname(filename));
|
|
9865
10036
|
});
|
|
9866
10037
|
}
|
|
@@ -10018,6 +10189,7 @@ class Config {
|
|
|
10018
10189
|
/**
|
|
10019
10190
|
* Get element metadata.
|
|
10020
10191
|
*/
|
|
10192
|
+
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10021
10193
|
getMetaTable() {
|
|
10022
10194
|
/* use cached table if it exists */
|
|
10023
10195
|
if (this.metaTable) {
|
|
@@ -10056,7 +10228,8 @@ class Config {
|
|
|
10056
10228
|
metaTable.loadFromObject(legacyRequire(entry));
|
|
10057
10229
|
}
|
|
10058
10230
|
catch (err) {
|
|
10059
|
-
|
|
10231
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10232
|
+
throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, err);
|
|
10060
10233
|
}
|
|
10061
10234
|
}
|
|
10062
10235
|
metaTable.init();
|
|
@@ -10132,7 +10305,8 @@ class Config {
|
|
|
10132
10305
|
return plugin;
|
|
10133
10306
|
}
|
|
10134
10307
|
catch (err) {
|
|
10135
|
-
|
|
10308
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10309
|
+
throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, err);
|
|
10136
10310
|
}
|
|
10137
10311
|
});
|
|
10138
10312
|
}
|
|
@@ -10454,6 +10628,9 @@ class ParserError extends Error {
|
|
|
10454
10628
|
function isAttrValueToken(token) {
|
|
10455
10629
|
return Boolean(token && token.type === TokenType.ATTR_VALUE);
|
|
10456
10630
|
}
|
|
10631
|
+
function svgShouldRetainTag(foreignTagName, tagName) {
|
|
10632
|
+
return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
|
|
10633
|
+
}
|
|
10457
10634
|
/**
|
|
10458
10635
|
* Parse HTML document into a DOM tree.
|
|
10459
10636
|
*
|
|
@@ -10466,6 +10643,7 @@ class Parser {
|
|
|
10466
10643
|
* @param config - Configuration
|
|
10467
10644
|
*/
|
|
10468
10645
|
constructor(config) {
|
|
10646
|
+
this.currentNamespace = "";
|
|
10469
10647
|
this.event = new EventHandler();
|
|
10470
10648
|
this.dom = null;
|
|
10471
10649
|
this.metaTable = config.getMetaTable();
|
|
@@ -10476,7 +10654,6 @@ class Parser {
|
|
|
10476
10654
|
* @param source - HTML markup.
|
|
10477
10655
|
* @returns DOM tree representing the HTML markup.
|
|
10478
10656
|
*/
|
|
10479
|
-
// eslint-disable-next-line complexity
|
|
10480
10657
|
parseHtml(source) {
|
|
10481
10658
|
var _a, _b, _c, _d;
|
|
10482
10659
|
if (typeof source === "string") {
|
|
@@ -10507,40 +10684,7 @@ class Parser {
|
|
|
10507
10684
|
let it = this.next(tokenStream);
|
|
10508
10685
|
while (!it.done) {
|
|
10509
10686
|
const token = it.value;
|
|
10510
|
-
|
|
10511
|
-
case TokenType.UNICODE_BOM:
|
|
10512
|
-
/* ignore */
|
|
10513
|
-
break;
|
|
10514
|
-
case TokenType.TAG_OPEN:
|
|
10515
|
-
this.consumeTag(source, token, tokenStream);
|
|
10516
|
-
break;
|
|
10517
|
-
case TokenType.WHITESPACE:
|
|
10518
|
-
this.trigger("whitespace", {
|
|
10519
|
-
text: token.data[0],
|
|
10520
|
-
location: token.location,
|
|
10521
|
-
});
|
|
10522
|
-
this.appendText(token.data[0], token.location);
|
|
10523
|
-
break;
|
|
10524
|
-
case TokenType.DIRECTIVE:
|
|
10525
|
-
this.consumeDirective(token);
|
|
10526
|
-
break;
|
|
10527
|
-
case TokenType.CONDITIONAL:
|
|
10528
|
-
this.consumeConditional(token);
|
|
10529
|
-
break;
|
|
10530
|
-
case TokenType.COMMENT:
|
|
10531
|
-
this.consumeComment(token);
|
|
10532
|
-
break;
|
|
10533
|
-
case TokenType.DOCTYPE_OPEN:
|
|
10534
|
-
this.consumeDoctype(token, tokenStream);
|
|
10535
|
-
break;
|
|
10536
|
-
case TokenType.TEXT:
|
|
10537
|
-
case TokenType.TEMPLATING:
|
|
10538
|
-
this.appendText(token.data[0], token.location);
|
|
10539
|
-
break;
|
|
10540
|
-
case TokenType.EOF:
|
|
10541
|
-
this.closeTree(source, token.location);
|
|
10542
|
-
break;
|
|
10543
|
-
}
|
|
10687
|
+
this.consume(source, token, tokenStream);
|
|
10544
10688
|
it = this.next(tokenStream);
|
|
10545
10689
|
}
|
|
10546
10690
|
/* resolve any dynamic meta element properties */
|
|
@@ -10587,13 +10731,50 @@ class Parser {
|
|
|
10587
10731
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10588
10732
|
}
|
|
10589
10733
|
}
|
|
10734
|
+
/* eslint-disable-next-line complexity */
|
|
10735
|
+
consume(source, token, tokenStream) {
|
|
10736
|
+
switch (token.type) {
|
|
10737
|
+
case TokenType.UNICODE_BOM:
|
|
10738
|
+
/* ignore */
|
|
10739
|
+
break;
|
|
10740
|
+
case TokenType.TAG_OPEN:
|
|
10741
|
+
this.consumeTag(source, token, tokenStream);
|
|
10742
|
+
break;
|
|
10743
|
+
case TokenType.WHITESPACE:
|
|
10744
|
+
this.trigger("whitespace", {
|
|
10745
|
+
text: token.data[0],
|
|
10746
|
+
location: token.location,
|
|
10747
|
+
});
|
|
10748
|
+
this.appendText(token.data[0], token.location);
|
|
10749
|
+
break;
|
|
10750
|
+
case TokenType.DIRECTIVE:
|
|
10751
|
+
this.consumeDirective(token);
|
|
10752
|
+
break;
|
|
10753
|
+
case TokenType.CONDITIONAL:
|
|
10754
|
+
this.consumeConditional(token);
|
|
10755
|
+
break;
|
|
10756
|
+
case TokenType.COMMENT:
|
|
10757
|
+
this.consumeComment(token);
|
|
10758
|
+
break;
|
|
10759
|
+
case TokenType.DOCTYPE_OPEN:
|
|
10760
|
+
this.consumeDoctype(token, tokenStream);
|
|
10761
|
+
break;
|
|
10762
|
+
case TokenType.TEXT:
|
|
10763
|
+
case TokenType.TEMPLATING:
|
|
10764
|
+
this.appendText(token.data[0], token.location);
|
|
10765
|
+
break;
|
|
10766
|
+
case TokenType.EOF:
|
|
10767
|
+
this.closeTree(source, token.location);
|
|
10768
|
+
break;
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10590
10771
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10591
10772
|
consumeTag(source, startToken, tokenStream) {
|
|
10592
10773
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location));
|
|
10593
10774
|
const endToken = tokens.slice(-1)[0];
|
|
10594
10775
|
const closeOptional = this.closeOptional(startToken);
|
|
10595
10776
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10596
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10777
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10597
10778
|
const isStartTag = !startToken.data[1];
|
|
10598
10779
|
const isClosing = !isStartTag || node.closed !== NodeClosed.Open;
|
|
10599
10780
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10698,6 +10879,15 @@ class Parser {
|
|
|
10698
10879
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_OPEN, errorLocation));
|
|
10699
10880
|
const [last] = tokens.slice(-1);
|
|
10700
10881
|
const [, tagClosed, tagName] = last.data;
|
|
10882
|
+
/* special case: svg <title> and <desc> should be intact as it affects accessibility */
|
|
10883
|
+
if (!tagClosed && svgShouldRetainTag(foreignTagName, tagName)) {
|
|
10884
|
+
const oldNamespace = this.currentNamespace;
|
|
10885
|
+
this.currentNamespace = "svg";
|
|
10886
|
+
this.consumeTag(source, last, tokenStream);
|
|
10887
|
+
this.consumeUntilMatchingTag(source, tokenStream, tagName);
|
|
10888
|
+
this.currentNamespace = oldNamespace;
|
|
10889
|
+
continue;
|
|
10890
|
+
}
|
|
10701
10891
|
/* keep going unless the new tag matches the foreign root element */
|
|
10702
10892
|
if (tagName !== foreignTagName) {
|
|
10703
10893
|
continue;
|
|
@@ -10896,6 +11086,35 @@ class Parser {
|
|
|
10896
11086
|
}
|
|
10897
11087
|
throw new ParserError(errorLocation, `stream ended before ${TokenType[search]} token was found`);
|
|
10898
11088
|
}
|
|
11089
|
+
/**
|
|
11090
|
+
* Consumes tokens until a matching close-tag is found. Tags are appended to
|
|
11091
|
+
* the document.
|
|
11092
|
+
*
|
|
11093
|
+
* @internal
|
|
11094
|
+
*/
|
|
11095
|
+
consumeUntilMatchingTag(source, tokenStream, searchTag) {
|
|
11096
|
+
let numOpen = 1;
|
|
11097
|
+
let it = this.next(tokenStream);
|
|
11098
|
+
while (!it.done) {
|
|
11099
|
+
const token = it.value;
|
|
11100
|
+
this.consume(source, token, tokenStream);
|
|
11101
|
+
if (token.type === TokenType.TAG_OPEN) {
|
|
11102
|
+
const [, close, tagName] = token.data;
|
|
11103
|
+
if (tagName === searchTag) {
|
|
11104
|
+
if (close) {
|
|
11105
|
+
numOpen--;
|
|
11106
|
+
}
|
|
11107
|
+
else {
|
|
11108
|
+
numOpen++;
|
|
11109
|
+
}
|
|
11110
|
+
if (numOpen === 0) {
|
|
11111
|
+
return;
|
|
11112
|
+
}
|
|
11113
|
+
}
|
|
11114
|
+
}
|
|
11115
|
+
it = this.next(tokenStream);
|
|
11116
|
+
}
|
|
11117
|
+
}
|
|
10899
11118
|
next(tokenStream) {
|
|
10900
11119
|
const it = tokenStream.next();
|
|
10901
11120
|
if (!it.done) {
|
|
@@ -11091,7 +11310,7 @@ class Engine {
|
|
|
11091
11310
|
/**
|
|
11092
11311
|
* Lint sources and return report
|
|
11093
11312
|
*
|
|
11094
|
-
* @param
|
|
11313
|
+
* @param sources - Sources to lint.
|
|
11095
11314
|
* @returns Report output.
|
|
11096
11315
|
*/
|
|
11097
11316
|
lint(sources) {
|
|
@@ -11185,7 +11404,7 @@ class Engine {
|
|
|
11185
11404
|
const lines = [];
|
|
11186
11405
|
function decoration(node) {
|
|
11187
11406
|
let output = "";
|
|
11188
|
-
if (node.
|
|
11407
|
+
if (node.id) {
|
|
11189
11408
|
output += `#${node.id}`;
|
|
11190
11409
|
}
|
|
11191
11410
|
if (node.hasAttribute("class")) {
|