html-validate 8.14.0 → 8.15.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/core.js +522 -22
- package/dist/cjs/core.js.map +1 -1
- package/dist/es/core.js +522 -22
- package/dist/es/core.js.map +1 -1
- package/package.json +5 -5
package/dist/es/core.js
CHANGED
|
@@ -6546,26 +6546,6 @@ class IdPattern extends Rule {
|
|
|
6546
6546
|
const restricted = /* @__PURE__ */ new Map([
|
|
6547
6547
|
["accept", ["file"]],
|
|
6548
6548
|
["alt", ["image"]],
|
|
6549
|
-
[
|
|
6550
|
-
"autocomplete",
|
|
6551
|
-
[
|
|
6552
|
-
"hidden",
|
|
6553
|
-
"text",
|
|
6554
|
-
"search",
|
|
6555
|
-
"url",
|
|
6556
|
-
"tel",
|
|
6557
|
-
"email",
|
|
6558
|
-
"password",
|
|
6559
|
-
"date",
|
|
6560
|
-
"month",
|
|
6561
|
-
"week",
|
|
6562
|
-
"time",
|
|
6563
|
-
"datetime-local",
|
|
6564
|
-
"number",
|
|
6565
|
-
"range",
|
|
6566
|
-
"color"
|
|
6567
|
-
]
|
|
6568
|
-
],
|
|
6569
6549
|
["capture", ["file"]],
|
|
6570
6550
|
["checked", ["checkbox", "radio"]],
|
|
6571
6551
|
["dirname", ["text", "search"]],
|
|
@@ -6946,7 +6926,7 @@ class MetaRefresh extends Rule {
|
|
|
6946
6926
|
}
|
|
6947
6927
|
}
|
|
6948
6928
|
function parseContent(text) {
|
|
6949
|
-
const match = text.match(/^(\d+)(?:\s*;\s*url=(.*))?/);
|
|
6929
|
+
const match = text.match(/^(\d+)(?:\s*;\s*url=(.*))?/i);
|
|
6950
6930
|
if (match) {
|
|
6951
6931
|
return {
|
|
6952
6932
|
delay: parseInt(match[1], 10),
|
|
@@ -9033,6 +9013,522 @@ class UnknownCharReference extends Rule {
|
|
|
9033
9013
|
}
|
|
9034
9014
|
}
|
|
9035
9015
|
|
|
9016
|
+
const expectedOrder = ["section", "hint", "contact", "field1", "field2", "webauthn"];
|
|
9017
|
+
const fieldNames1 = [
|
|
9018
|
+
"name",
|
|
9019
|
+
"honorific-prefix",
|
|
9020
|
+
"given-name",
|
|
9021
|
+
"additional-name",
|
|
9022
|
+
"family-name",
|
|
9023
|
+
"honorific-suffix",
|
|
9024
|
+
"nickname",
|
|
9025
|
+
"username",
|
|
9026
|
+
"new-password",
|
|
9027
|
+
"current-password",
|
|
9028
|
+
"one-time-code",
|
|
9029
|
+
"organization-title",
|
|
9030
|
+
"organization",
|
|
9031
|
+
"street-address",
|
|
9032
|
+
"address-line1",
|
|
9033
|
+
"address-line2",
|
|
9034
|
+
"address-line3",
|
|
9035
|
+
"address-level4",
|
|
9036
|
+
"address-level3",
|
|
9037
|
+
"address-level2",
|
|
9038
|
+
"address-level1",
|
|
9039
|
+
"country",
|
|
9040
|
+
"country-name",
|
|
9041
|
+
"postal-code",
|
|
9042
|
+
"cc-name",
|
|
9043
|
+
"cc-given-name",
|
|
9044
|
+
"cc-additional-name",
|
|
9045
|
+
"cc-family-name",
|
|
9046
|
+
"cc-number",
|
|
9047
|
+
"cc-exp",
|
|
9048
|
+
"cc-exp-month",
|
|
9049
|
+
"cc-exp-year",
|
|
9050
|
+
"cc-csc",
|
|
9051
|
+
"cc-type",
|
|
9052
|
+
"transaction-currency",
|
|
9053
|
+
"transaction-amount",
|
|
9054
|
+
"language",
|
|
9055
|
+
"bday",
|
|
9056
|
+
"bday-day",
|
|
9057
|
+
"bday-month",
|
|
9058
|
+
"bday-year",
|
|
9059
|
+
"sex",
|
|
9060
|
+
"url",
|
|
9061
|
+
"photo"
|
|
9062
|
+
];
|
|
9063
|
+
const fieldNames2 = [
|
|
9064
|
+
"tel",
|
|
9065
|
+
"tel-country-code",
|
|
9066
|
+
"tel-national",
|
|
9067
|
+
"tel-area-code",
|
|
9068
|
+
"tel-local",
|
|
9069
|
+
"tel-local-prefix",
|
|
9070
|
+
"tel-local-suffix",
|
|
9071
|
+
"tel-extension",
|
|
9072
|
+
"email",
|
|
9073
|
+
"impp"
|
|
9074
|
+
];
|
|
9075
|
+
const fieldNameGroup = {
|
|
9076
|
+
name: "text",
|
|
9077
|
+
"honorific-prefix": "text",
|
|
9078
|
+
"given-name": "text",
|
|
9079
|
+
"additional-name": "text",
|
|
9080
|
+
"family-name": "text",
|
|
9081
|
+
"honorific-suffix": "text",
|
|
9082
|
+
nickname: "text",
|
|
9083
|
+
username: "username",
|
|
9084
|
+
"new-password": "password",
|
|
9085
|
+
"current-password": "password",
|
|
9086
|
+
"one-time-code": "password",
|
|
9087
|
+
"organization-title": "text",
|
|
9088
|
+
organization: "text",
|
|
9089
|
+
"street-address": "multiline",
|
|
9090
|
+
"address-line1": "text",
|
|
9091
|
+
"address-line2": "text",
|
|
9092
|
+
"address-line3": "text",
|
|
9093
|
+
"address-level4": "text",
|
|
9094
|
+
"address-level3": "text",
|
|
9095
|
+
"address-level2": "text",
|
|
9096
|
+
"address-level1": "text",
|
|
9097
|
+
country: "text",
|
|
9098
|
+
"country-name": "text",
|
|
9099
|
+
"postal-code": "text",
|
|
9100
|
+
"cc-name": "text",
|
|
9101
|
+
"cc-given-name": "text",
|
|
9102
|
+
"cc-additional-name": "text",
|
|
9103
|
+
"cc-family-name": "text",
|
|
9104
|
+
"cc-number": "text",
|
|
9105
|
+
"cc-exp": "month",
|
|
9106
|
+
"cc-exp-month": "numeric",
|
|
9107
|
+
"cc-exp-year": "numeric",
|
|
9108
|
+
"cc-csc": "text",
|
|
9109
|
+
"cc-type": "text",
|
|
9110
|
+
"transaction-currency": "text",
|
|
9111
|
+
"transaction-amount": "numeric",
|
|
9112
|
+
language: "text",
|
|
9113
|
+
bday: "date",
|
|
9114
|
+
"bday-day": "numeric",
|
|
9115
|
+
"bday-month": "numeric",
|
|
9116
|
+
"bday-year": "numeric",
|
|
9117
|
+
sex: "text",
|
|
9118
|
+
url: "url",
|
|
9119
|
+
photo: "url",
|
|
9120
|
+
tel: "tel",
|
|
9121
|
+
"tel-country-code": "text",
|
|
9122
|
+
"tel-national": "text",
|
|
9123
|
+
"tel-area-code": "text",
|
|
9124
|
+
"tel-local": "text",
|
|
9125
|
+
"tel-local-prefix": "text",
|
|
9126
|
+
"tel-local-suffix": "text",
|
|
9127
|
+
"tel-extension": "text",
|
|
9128
|
+
email: "username",
|
|
9129
|
+
impp: "url"
|
|
9130
|
+
};
|
|
9131
|
+
const disallowedInputTypes = ["checkbox", "radio", "file", "submit", "image", "reset", "button"];
|
|
9132
|
+
function matchSection(token) {
|
|
9133
|
+
return token.startsWith("section-");
|
|
9134
|
+
}
|
|
9135
|
+
function matchHint(token) {
|
|
9136
|
+
return token === "shipping" || token === "billing";
|
|
9137
|
+
}
|
|
9138
|
+
function matchFieldNames1(token) {
|
|
9139
|
+
return fieldNames1.includes(token);
|
|
9140
|
+
}
|
|
9141
|
+
function matchContact(token) {
|
|
9142
|
+
const haystack = ["home", "work", "mobile", "fax", "pager"];
|
|
9143
|
+
return haystack.includes(token);
|
|
9144
|
+
}
|
|
9145
|
+
function matchFieldNames2(token) {
|
|
9146
|
+
return fieldNames2.includes(token);
|
|
9147
|
+
}
|
|
9148
|
+
function matchWebauthn(token) {
|
|
9149
|
+
return token === "webauthn";
|
|
9150
|
+
}
|
|
9151
|
+
function matchToken(token) {
|
|
9152
|
+
if (matchSection(token)) {
|
|
9153
|
+
return "section";
|
|
9154
|
+
}
|
|
9155
|
+
if (matchHint(token)) {
|
|
9156
|
+
return "hint";
|
|
9157
|
+
}
|
|
9158
|
+
if (matchFieldNames1(token)) {
|
|
9159
|
+
return "field1";
|
|
9160
|
+
}
|
|
9161
|
+
if (matchFieldNames2(token)) {
|
|
9162
|
+
return "field2";
|
|
9163
|
+
}
|
|
9164
|
+
if (matchContact(token)) {
|
|
9165
|
+
return "contact";
|
|
9166
|
+
}
|
|
9167
|
+
if (matchWebauthn(token)) {
|
|
9168
|
+
return "webauthn";
|
|
9169
|
+
}
|
|
9170
|
+
return null;
|
|
9171
|
+
}
|
|
9172
|
+
function getControlGroups(type) {
|
|
9173
|
+
const allGroups = [
|
|
9174
|
+
"text",
|
|
9175
|
+
"multiline",
|
|
9176
|
+
"password",
|
|
9177
|
+
"url",
|
|
9178
|
+
"username",
|
|
9179
|
+
"tel",
|
|
9180
|
+
"numeric",
|
|
9181
|
+
"month",
|
|
9182
|
+
"date"
|
|
9183
|
+
];
|
|
9184
|
+
const mapping = {
|
|
9185
|
+
hidden: allGroups,
|
|
9186
|
+
text: allGroups.filter((it) => it !== "multiline"),
|
|
9187
|
+
search: allGroups.filter((it) => it !== "multiline"),
|
|
9188
|
+
password: ["password"],
|
|
9189
|
+
url: ["url"],
|
|
9190
|
+
email: ["username"],
|
|
9191
|
+
tel: ["tel"],
|
|
9192
|
+
number: ["numeric"],
|
|
9193
|
+
month: ["month"],
|
|
9194
|
+
date: ["date"]
|
|
9195
|
+
};
|
|
9196
|
+
const groups = mapping[type];
|
|
9197
|
+
if (groups) {
|
|
9198
|
+
return groups;
|
|
9199
|
+
}
|
|
9200
|
+
return [];
|
|
9201
|
+
}
|
|
9202
|
+
function isDisallowedType(node, type) {
|
|
9203
|
+
if (!node.is("input")) {
|
|
9204
|
+
return false;
|
|
9205
|
+
}
|
|
9206
|
+
return disallowedInputTypes.includes(type);
|
|
9207
|
+
}
|
|
9208
|
+
function getTerminalMessage(context) {
|
|
9209
|
+
switch (context.msg) {
|
|
9210
|
+
case 0 /* InvalidAttribute */:
|
|
9211
|
+
return "autocomplete attribute cannot be used on {{ what }}";
|
|
9212
|
+
case 1 /* InvalidValue */:
|
|
9213
|
+
return '"{{ value }}" cannot be used on {{ what }}';
|
|
9214
|
+
case 2 /* InvalidOrder */:
|
|
9215
|
+
return '"{{ second }}" must appear before "{{ first }}"';
|
|
9216
|
+
case 3 /* InvalidToken */:
|
|
9217
|
+
return '"{{ token }}" is not a valid autocomplete token or field name';
|
|
9218
|
+
case 4 /* InvalidCombination */:
|
|
9219
|
+
return '"{{ second }}" cannot be combined with "{{ first }}"';
|
|
9220
|
+
case 5 /* MissingField */:
|
|
9221
|
+
return "autocomplete attribute is missing field name";
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
function getMarkdownMessage(context) {
|
|
9225
|
+
switch (context.msg) {
|
|
9226
|
+
case 0 /* InvalidAttribute */:
|
|
9227
|
+
return [
|
|
9228
|
+
`\`autocomplete\` attribute cannot be used on \`${context.what}\``,
|
|
9229
|
+
"",
|
|
9230
|
+
"The following input types cannot use the `autocomplete` attribute:",
|
|
9231
|
+
"",
|
|
9232
|
+
...disallowedInputTypes.map((it) => `- \`${it}\``)
|
|
9233
|
+
].join("\n");
|
|
9234
|
+
case 1 /* InvalidValue */: {
|
|
9235
|
+
const message = `\`"${context.value}"\` cannot be used on \`${context.what}\``;
|
|
9236
|
+
if (context.type === "form") {
|
|
9237
|
+
return [
|
|
9238
|
+
message,
|
|
9239
|
+
"",
|
|
9240
|
+
'The `<form>` element can only use the values `"on"` and `"off"`.'
|
|
9241
|
+
].join("\n");
|
|
9242
|
+
}
|
|
9243
|
+
if (context.type === "hidden") {
|
|
9244
|
+
return [
|
|
9245
|
+
message,
|
|
9246
|
+
"",
|
|
9247
|
+
'`<input type="hidden">` cannot use the values `"on"` and `"off"`.'
|
|
9248
|
+
].join("\n");
|
|
9249
|
+
}
|
|
9250
|
+
const controlGroups = getControlGroups(context.type);
|
|
9251
|
+
const currentGroup = fieldNameGroup[context.value];
|
|
9252
|
+
return [
|
|
9253
|
+
message,
|
|
9254
|
+
"",
|
|
9255
|
+
`\`${context.what}\` allows autocomplete fields from the following group${controlGroups.length > 1 ? "s" : ""}:`,
|
|
9256
|
+
"",
|
|
9257
|
+
...controlGroups.map((it) => `- ${it}`),
|
|
9258
|
+
"",
|
|
9259
|
+
`The field \`"${context.value}"\` belongs to the group /${currentGroup}/ which cannot be used with this input type.`
|
|
9260
|
+
].join("\n");
|
|
9261
|
+
}
|
|
9262
|
+
case 2 /* InvalidOrder */:
|
|
9263
|
+
return [
|
|
9264
|
+
`\`"${context.second}"\` must appear before \`"${context.first}"\``,
|
|
9265
|
+
"",
|
|
9266
|
+
"The autocomplete tokens must appear in the following order:",
|
|
9267
|
+
"",
|
|
9268
|
+
"- Optional section name (`section-` prefix).",
|
|
9269
|
+
"- Optional `shipping` or `billing` token.",
|
|
9270
|
+
"- Optional `home`, `work`, `mobile`, `fax` or `pager` token (for fields supporting it).",
|
|
9271
|
+
"- Field name",
|
|
9272
|
+
"- Optional `webauthn` token."
|
|
9273
|
+
].join("\n");
|
|
9274
|
+
case 3 /* InvalidToken */:
|
|
9275
|
+
return `\`"${context.token}"\` is not a valid autocomplete token or field name`;
|
|
9276
|
+
case 4 /* InvalidCombination */:
|
|
9277
|
+
return `\`"${context.second}"\` cannot be combined with \`"${context.first}"\``;
|
|
9278
|
+
case 5 /* MissingField */:
|
|
9279
|
+
return "Autocomplete attribute is missing field name";
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
9282
|
+
class ValidAutocomplete extends Rule {
|
|
9283
|
+
documentation(context) {
|
|
9284
|
+
return {
|
|
9285
|
+
description: getMarkdownMessage(context),
|
|
9286
|
+
url: "https://html-validate.org/rules/valid-autocomplete.html"
|
|
9287
|
+
};
|
|
9288
|
+
}
|
|
9289
|
+
setup() {
|
|
9290
|
+
this.on("dom:ready", (event) => {
|
|
9291
|
+
const { document } = event;
|
|
9292
|
+
const elements = document.querySelectorAll("[autocomplete]");
|
|
9293
|
+
for (const element of elements) {
|
|
9294
|
+
const autocomplete = element.getAttribute("autocomplete");
|
|
9295
|
+
if (autocomplete.value === null || autocomplete.value instanceof DynamicValue) {
|
|
9296
|
+
continue;
|
|
9297
|
+
}
|
|
9298
|
+
const location = autocomplete.valueLocation;
|
|
9299
|
+
const value = autocomplete.value.toLowerCase();
|
|
9300
|
+
const tokens = new DOMTokenList(value, location);
|
|
9301
|
+
if (tokens.length === 0) {
|
|
9302
|
+
continue;
|
|
9303
|
+
}
|
|
9304
|
+
this.validate(element, value, tokens, autocomplete.keyLocation, location);
|
|
9305
|
+
}
|
|
9306
|
+
});
|
|
9307
|
+
}
|
|
9308
|
+
validate(node, value, tokens, keyLocation, valueLocation) {
|
|
9309
|
+
switch (node.tagName) {
|
|
9310
|
+
case "form":
|
|
9311
|
+
this.validateFormAutocomplete(node, value, valueLocation);
|
|
9312
|
+
break;
|
|
9313
|
+
case "input":
|
|
9314
|
+
case "textarea":
|
|
9315
|
+
case "select":
|
|
9316
|
+
this.validateControlAutocomplete(node, tokens, keyLocation);
|
|
9317
|
+
break;
|
|
9318
|
+
}
|
|
9319
|
+
}
|
|
9320
|
+
validateControlAutocomplete(node, tokens, keyLocation) {
|
|
9321
|
+
const type = node.getAttributeValue("type") ?? "text";
|
|
9322
|
+
const mantle = type !== "hidden" ? "expectation" : "anchor";
|
|
9323
|
+
if (isDisallowedType(node, type)) {
|
|
9324
|
+
const context = {
|
|
9325
|
+
msg: 0 /* InvalidAttribute */,
|
|
9326
|
+
what: `<input type="${type}">`
|
|
9327
|
+
};
|
|
9328
|
+
this.report({
|
|
9329
|
+
node,
|
|
9330
|
+
message: getTerminalMessage(context),
|
|
9331
|
+
location: keyLocation,
|
|
9332
|
+
context
|
|
9333
|
+
});
|
|
9334
|
+
return;
|
|
9335
|
+
}
|
|
9336
|
+
if (tokens.includes("on") || tokens.includes("off")) {
|
|
9337
|
+
this.validateOnOff(node, mantle, tokens);
|
|
9338
|
+
return;
|
|
9339
|
+
}
|
|
9340
|
+
this.validateTokens(node, tokens, keyLocation);
|
|
9341
|
+
}
|
|
9342
|
+
validateFormAutocomplete(node, value, location) {
|
|
9343
|
+
const trimmed = value.trim();
|
|
9344
|
+
if (["on", "off"].includes(trimmed)) {
|
|
9345
|
+
return;
|
|
9346
|
+
}
|
|
9347
|
+
const context = {
|
|
9348
|
+
msg: 1 /* InvalidValue */,
|
|
9349
|
+
type: "form",
|
|
9350
|
+
value: trimmed,
|
|
9351
|
+
what: "<form>"
|
|
9352
|
+
};
|
|
9353
|
+
this.report({
|
|
9354
|
+
node,
|
|
9355
|
+
message: getTerminalMessage(context),
|
|
9356
|
+
location,
|
|
9357
|
+
context
|
|
9358
|
+
});
|
|
9359
|
+
}
|
|
9360
|
+
validateOnOff(node, mantle, tokens) {
|
|
9361
|
+
const index = tokens.findIndex((it) => it === "on" || it === "off");
|
|
9362
|
+
const value = tokens.item(index);
|
|
9363
|
+
const location = tokens.location(index);
|
|
9364
|
+
if (tokens.length > 1) {
|
|
9365
|
+
const context = {
|
|
9366
|
+
msg: 4 /* InvalidCombination */,
|
|
9367
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- it must be present of it wouldn't be found */
|
|
9368
|
+
first: tokens.item(index > 0 ? 0 : 1),
|
|
9369
|
+
second: value
|
|
9370
|
+
};
|
|
9371
|
+
this.report({
|
|
9372
|
+
node,
|
|
9373
|
+
message: getTerminalMessage(context),
|
|
9374
|
+
location,
|
|
9375
|
+
context
|
|
9376
|
+
});
|
|
9377
|
+
}
|
|
9378
|
+
switch (mantle) {
|
|
9379
|
+
case "expectation":
|
|
9380
|
+
return;
|
|
9381
|
+
case "anchor": {
|
|
9382
|
+
const context = {
|
|
9383
|
+
msg: 1 /* InvalidValue */,
|
|
9384
|
+
type: "hidden",
|
|
9385
|
+
value,
|
|
9386
|
+
what: `<input type="hidden">`
|
|
9387
|
+
};
|
|
9388
|
+
this.report({
|
|
9389
|
+
node,
|
|
9390
|
+
message: getTerminalMessage(context),
|
|
9391
|
+
location: tokens.location(0),
|
|
9392
|
+
context
|
|
9393
|
+
});
|
|
9394
|
+
}
|
|
9395
|
+
}
|
|
9396
|
+
}
|
|
9397
|
+
validateTokens(node, tokens, keyLocation) {
|
|
9398
|
+
const order = [];
|
|
9399
|
+
for (const { item, location } of tokens.iterator()) {
|
|
9400
|
+
const tokenType = matchToken(item);
|
|
9401
|
+
if (tokenType) {
|
|
9402
|
+
order.push(tokenType);
|
|
9403
|
+
} else {
|
|
9404
|
+
const context = {
|
|
9405
|
+
msg: 3 /* InvalidToken */,
|
|
9406
|
+
token: item
|
|
9407
|
+
};
|
|
9408
|
+
this.report({
|
|
9409
|
+
node,
|
|
9410
|
+
message: getTerminalMessage(context),
|
|
9411
|
+
location,
|
|
9412
|
+
context
|
|
9413
|
+
});
|
|
9414
|
+
return;
|
|
9415
|
+
}
|
|
9416
|
+
}
|
|
9417
|
+
const fieldTokens = order.map((it) => it === "field1" || it === "field2");
|
|
9418
|
+
this.validateFieldPresence(node, tokens, fieldTokens, keyLocation);
|
|
9419
|
+
this.validateContact(node, tokens, order);
|
|
9420
|
+
this.validateOrder(node, tokens, order);
|
|
9421
|
+
this.validateControlGroup(node, tokens, fieldTokens);
|
|
9422
|
+
}
|
|
9423
|
+
/**
|
|
9424
|
+
* Ensure that exactly one field name is present from the two field lists.
|
|
9425
|
+
*/
|
|
9426
|
+
validateFieldPresence(node, tokens, fieldTokens, keyLocation) {
|
|
9427
|
+
const numFields = fieldTokens.filter(Boolean).length;
|
|
9428
|
+
if (numFields === 0) {
|
|
9429
|
+
const context = {
|
|
9430
|
+
msg: 5 /* MissingField */
|
|
9431
|
+
};
|
|
9432
|
+
this.report({
|
|
9433
|
+
node,
|
|
9434
|
+
message: getTerminalMessage(context),
|
|
9435
|
+
location: keyLocation,
|
|
9436
|
+
context
|
|
9437
|
+
});
|
|
9438
|
+
} else if (numFields > 1) {
|
|
9439
|
+
const a = fieldTokens.indexOf(true);
|
|
9440
|
+
const b = fieldTokens.lastIndexOf(true);
|
|
9441
|
+
const context = {
|
|
9442
|
+
msg: 4 /* InvalidCombination */,
|
|
9443
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- it must be present of it wouldn't be found */
|
|
9444
|
+
first: tokens.item(a),
|
|
9445
|
+
second: tokens.item(b)
|
|
9446
|
+
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
|
9447
|
+
};
|
|
9448
|
+
this.report({
|
|
9449
|
+
node,
|
|
9450
|
+
message: getTerminalMessage(context),
|
|
9451
|
+
location: tokens.location(b),
|
|
9452
|
+
context
|
|
9453
|
+
});
|
|
9454
|
+
}
|
|
9455
|
+
}
|
|
9456
|
+
/**
|
|
9457
|
+
* Ensure contact token is only used with field names from the second list.
|
|
9458
|
+
*/
|
|
9459
|
+
validateContact(node, tokens, order) {
|
|
9460
|
+
if (order.includes("contact") && order.includes("field1")) {
|
|
9461
|
+
const a = order.indexOf("field1");
|
|
9462
|
+
const b = order.indexOf("contact");
|
|
9463
|
+
const context = {
|
|
9464
|
+
msg: 4 /* InvalidCombination */,
|
|
9465
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- it must be present of it wouldn't be found */
|
|
9466
|
+
first: tokens.item(a),
|
|
9467
|
+
second: tokens.item(b)
|
|
9468
|
+
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
|
9469
|
+
};
|
|
9470
|
+
this.report({
|
|
9471
|
+
node,
|
|
9472
|
+
message: getTerminalMessage(context),
|
|
9473
|
+
location: tokens.location(b),
|
|
9474
|
+
context
|
|
9475
|
+
});
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
validateOrder(node, tokens, order) {
|
|
9479
|
+
const indicies = order.map((it) => expectedOrder.indexOf(it));
|
|
9480
|
+
for (let i = 0; i < indicies.length - 1; i++) {
|
|
9481
|
+
if (indicies[0] > indicies[i + 1]) {
|
|
9482
|
+
const context = {
|
|
9483
|
+
msg: 2 /* InvalidOrder */,
|
|
9484
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- it must be present of it wouldn't be found */
|
|
9485
|
+
first: tokens.item(i),
|
|
9486
|
+
second: tokens.item(i + 1)
|
|
9487
|
+
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
|
9488
|
+
};
|
|
9489
|
+
this.report({
|
|
9490
|
+
node,
|
|
9491
|
+
message: getTerminalMessage(context),
|
|
9492
|
+
location: tokens.location(i + 1),
|
|
9493
|
+
context
|
|
9494
|
+
});
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
validateControlGroup(node, tokens, fieldTokens) {
|
|
9499
|
+
const numFields = fieldTokens.filter(Boolean).length;
|
|
9500
|
+
if (numFields === 0) {
|
|
9501
|
+
return;
|
|
9502
|
+
}
|
|
9503
|
+
if (!node.is("input")) {
|
|
9504
|
+
return;
|
|
9505
|
+
}
|
|
9506
|
+
const attr = node.getAttribute("type");
|
|
9507
|
+
const type = (attr == null ? void 0 : attr.value) ?? "text";
|
|
9508
|
+
if (type instanceof DynamicValue) {
|
|
9509
|
+
return;
|
|
9510
|
+
}
|
|
9511
|
+
const controlGroups = getControlGroups(type);
|
|
9512
|
+
const fieldIndex = fieldTokens.indexOf(true);
|
|
9513
|
+
const fieldToken = tokens.item(fieldIndex);
|
|
9514
|
+
const fieldGroup = fieldNameGroup[fieldToken];
|
|
9515
|
+
if (!controlGroups.includes(fieldGroup)) {
|
|
9516
|
+
const context = {
|
|
9517
|
+
msg: 1 /* InvalidValue */,
|
|
9518
|
+
type,
|
|
9519
|
+
value: fieldToken,
|
|
9520
|
+
what: `<input type="${type}">`
|
|
9521
|
+
};
|
|
9522
|
+
this.report({
|
|
9523
|
+
node,
|
|
9524
|
+
message: getTerminalMessage(context),
|
|
9525
|
+
location: tokens.location(fieldIndex),
|
|
9526
|
+
context
|
|
9527
|
+
});
|
|
9528
|
+
}
|
|
9529
|
+
}
|
|
9530
|
+
}
|
|
9531
|
+
|
|
9036
9532
|
const defaults$3 = {
|
|
9037
9533
|
relaxed: false
|
|
9038
9534
|
};
|
|
@@ -9587,6 +10083,7 @@ const bundledRules = {
|
|
|
9587
10083
|
"text-content": TextContent,
|
|
9588
10084
|
"unique-landmark": UniqueLandmark,
|
|
9589
10085
|
"unrecognized-char-ref": UnknownCharReference,
|
|
10086
|
+
"valid-autocomplete": ValidAutocomplete,
|
|
9590
10087
|
"valid-id": ValidID,
|
|
9591
10088
|
"void-content": VoidContent,
|
|
9592
10089
|
"void-style": VoidStyle,
|
|
@@ -9617,6 +10114,7 @@ const config$4 = {
|
|
|
9617
10114
|
"svg-focusable": "off",
|
|
9618
10115
|
"text-content": "error",
|
|
9619
10116
|
"unique-landmark": "error",
|
|
10117
|
+
"valid-autocomplete": "error",
|
|
9620
10118
|
"wcag/h30": "error",
|
|
9621
10119
|
"wcag/h32": "error",
|
|
9622
10120
|
"wcag/h36": "error",
|
|
@@ -9716,6 +10214,7 @@ const config$1 = {
|
|
|
9716
10214
|
"text-content": "error",
|
|
9717
10215
|
"unique-landmark": "error",
|
|
9718
10216
|
"unrecognized-char-ref": "error",
|
|
10217
|
+
"valid-autocomplete": "error",
|
|
9719
10218
|
"valid-id": ["error", { relaxed: false }],
|
|
9720
10219
|
void: "off",
|
|
9721
10220
|
"void-content": "error",
|
|
@@ -9763,6 +10262,7 @@ const config = {
|
|
|
9763
10262
|
"no-unused-disable": "error",
|
|
9764
10263
|
"script-element": "error",
|
|
9765
10264
|
"unrecognized-char-ref": "error",
|
|
10265
|
+
"valid-autocomplete": "error",
|
|
9766
10266
|
"valid-id": ["error", { relaxed: true }],
|
|
9767
10267
|
"void-content": "error"
|
|
9768
10268
|
}
|
|
@@ -11988,7 +12488,7 @@ class HtmlValidate {
|
|
|
11988
12488
|
}
|
|
11989
12489
|
|
|
11990
12490
|
const name = "html-validate";
|
|
11991
|
-
const version = "8.
|
|
12491
|
+
const version = "8.15.0";
|
|
11992
12492
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
11993
12493
|
|
|
11994
12494
|
function definePlugin(plugin) {
|