html-validate 7.11.1 → 7.12.1
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/README.md +1 -1
- package/dist/cjs/browser.d.ts +1 -1
- package/dist/cjs/browser.js +1 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +1 -0
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +27 -4
- package/dist/cjs/core.js +217 -4
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +25 -8
- package/dist/cjs/elements.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/jest.d.ts +1 -1
- package/dist/cjs/meta-helper.d.ts +1 -1
- package/dist/cjs/rules-helper.d.ts +1 -1
- package/dist/cjs/rules-helper.js +25 -0
- package/dist/cjs/rules-helper.js.map +1 -1
- package/dist/cjs/test-utils.d.ts +1 -1
- package/dist/es/browser.d.ts +1 -1
- package/dist/es/browser.js +1 -1
- package/dist/es/cli.js +1 -0
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +27 -4
- package/dist/es/core.js +218 -6
- package/dist/es/core.js.map +1 -1
- package/dist/es/elements.js +25 -8
- package/dist/es/elements.js.map +1 -1
- package/dist/es/index.d.ts +3 -3
- package/dist/es/index.js +1 -1
- package/dist/es/jest.d.ts +1 -1
- package/dist/es/meta-helper.d.ts +1 -1
- package/dist/es/rules-helper.d.ts +1 -1
- package/dist/es/rules-helper.js +25 -1
- package/dist/es/rules-helper.js.map +1 -1
- package/dist/es/test-utils.d.ts +1 -1
- package/dist/schema/elements.json +16 -0
- package/package.json +7 -7
package/dist/es/core.d.ts
CHANGED
|
@@ -343,6 +343,14 @@ declare abstract class Rule<ContextType = void, OptionsType = void> {
|
|
|
343
343
|
isKeywordIgnored<T extends IncludeExcludeOptions>(this: {
|
|
344
344
|
options: T;
|
|
345
345
|
}, keyword: string, matcher?: (list: string[], it: string) => boolean): boolean;
|
|
346
|
+
/**
|
|
347
|
+
* Get [[MetaElement]] for the given tag. If no specific metadata is present
|
|
348
|
+
* the global metadata is returned or null if no global is present.
|
|
349
|
+
*
|
|
350
|
+
* @public
|
|
351
|
+
* @returns A shallow copy of metadata.
|
|
352
|
+
*/
|
|
353
|
+
getMetaFor(tagName: string): MetaElement | null;
|
|
346
354
|
/**
|
|
347
355
|
* Find all tags which has enabled given property.
|
|
348
356
|
*/
|
|
@@ -1002,13 +1010,16 @@ declare class Attribute {
|
|
|
1002
1010
|
/**
|
|
1003
1011
|
* Test attribute value.
|
|
1004
1012
|
*
|
|
1005
|
-
* @param pattern - Pattern to match value against.
|
|
1013
|
+
* @param pattern - Pattern to match value against. Can be a RegExp, literal
|
|
1014
|
+
* string or an array of strings (returns true if any value matches the
|
|
1015
|
+
* array).
|
|
1006
1016
|
* @param dynamicMatches - If true `DynamicValue` will always match, if false
|
|
1007
1017
|
* it never matches.
|
|
1008
1018
|
* @returns `true` if attribute value matches pattern.
|
|
1009
1019
|
*/
|
|
1010
1020
|
valueMatches(pattern: RegExp, dynamicMatches?: boolean): boolean;
|
|
1011
1021
|
valueMatches(pattern: string, dynamicMatches?: boolean): boolean;
|
|
1022
|
+
valueMatches(pattern: string[], dynamicMatches?: boolean): boolean;
|
|
1012
1023
|
}
|
|
1013
1024
|
|
|
1014
1025
|
interface CSSStyleDeclaration {
|
|
@@ -1688,8 +1699,10 @@ declare enum TextContent {
|
|
|
1688
1699
|
* description if there is an error.
|
|
1689
1700
|
*
|
|
1690
1701
|
* @public
|
|
1702
|
+
* @param node - The node the attribute belongs to.
|
|
1703
|
+
* @param attr - The current attribute being validated.
|
|
1691
1704
|
*/
|
|
1692
|
-
type MetaAttributeAllowedCallback = (node: HtmlElement) => string | null | undefined;
|
|
1705
|
+
type MetaAttributeAllowedCallback = (node: HtmlElement, attr: Attribute) => string | null | undefined;
|
|
1693
1706
|
/**
|
|
1694
1707
|
* @public
|
|
1695
1708
|
*/
|
|
@@ -1708,6 +1721,10 @@ interface DeprecatedElement {
|
|
|
1708
1721
|
documentation?: string;
|
|
1709
1722
|
source?: string;
|
|
1710
1723
|
}
|
|
1724
|
+
interface FormAssociated {
|
|
1725
|
+
/** Listed elements have a name attribute and is listed in the form and fieldset elements property. */
|
|
1726
|
+
listed: boolean;
|
|
1727
|
+
}
|
|
1711
1728
|
/**
|
|
1712
1729
|
* @public
|
|
1713
1730
|
*/
|
|
@@ -1727,6 +1744,8 @@ interface MetaData {
|
|
|
1727
1744
|
implicitClosed?: string[];
|
|
1728
1745
|
scriptSupporting?: boolean;
|
|
1729
1746
|
form?: boolean;
|
|
1747
|
+
/** Mark element as a form-associated element */
|
|
1748
|
+
formAssociated?: Partial<FormAssociated>;
|
|
1730
1749
|
labelable?: boolean | PropertyExpression;
|
|
1731
1750
|
deprecatedAttributes?: string[];
|
|
1732
1751
|
requiredAttributes?: string[];
|
|
@@ -1743,7 +1762,7 @@ interface MetaData {
|
|
|
1743
1762
|
* Properties listed here can be used to reverse search elements with the given
|
|
1744
1763
|
* property enabled. See [[MetaTable.getTagsWithProperty]].
|
|
1745
1764
|
*/
|
|
1746
|
-
type MetaLookupableProperty = "metadata" | "flow" | "sectioning" | "heading" | "phrasing" | "embedded" | "interactive" | "deprecated" | "foreign" | "void" | "transparent" | "scriptSupporting" | "form" | "labelable";
|
|
1765
|
+
type MetaLookupableProperty = "metadata" | "flow" | "sectioning" | "heading" | "phrasing" | "embedded" | "interactive" | "deprecated" | "foreign" | "void" | "transparent" | "scriptSupporting" | "form" | "formAssociated" | "labelable";
|
|
1747
1766
|
/**
|
|
1748
1767
|
* Properties listed here can be copied (loaded) onto another element using
|
|
1749
1768
|
* [[HtmlElement.loadMeta]].
|
|
@@ -1756,6 +1775,7 @@ declare const MetaCopyableProperty: Array<keyof MetaElement>;
|
|
|
1756
1775
|
*/
|
|
1757
1776
|
interface MetaElement extends Omit<MetaData, "deprecatedAttributes" | "requiredAttributes"> {
|
|
1758
1777
|
tagName: string;
|
|
1778
|
+
formAssociated?: FormAssociated;
|
|
1759
1779
|
attributes: Record<string, MetaAttribute>;
|
|
1760
1780
|
textContent?: TextContent;
|
|
1761
1781
|
}
|
|
@@ -2042,6 +2062,9 @@ declare class NestedError extends Error {
|
|
|
2042
2062
|
*/
|
|
2043
2063
|
declare class UserError extends NestedError {
|
|
2044
2064
|
constructor(message: string, nested?: Error);
|
|
2065
|
+
/**
|
|
2066
|
+
* @public
|
|
2067
|
+
*/
|
|
2045
2068
|
prettyFormat(): string | undefined;
|
|
2046
2069
|
}
|
|
2047
2070
|
|
|
@@ -2396,4 +2419,4 @@ interface AvailableFormatters {
|
|
|
2396
2419
|
declare function getFormatter(name: keyof AvailableFormatters): Formatter;
|
|
2397
2420
|
declare function getFormatter(name: string): Formatter | null;
|
|
2398
2421
|
|
|
2399
|
-
export {
|
|
2422
|
+
export { EventCallback as $, AttributeData as A, Reporter as B, Config as C, DynamicValue as D, EventDump as E, Message as F, Result as G, HtmlValidate as H, DeferredMessage as I, TransformContext as J, Transformer as K, Location as L, MetaData as M, NodeClosed as N, TemplateExtractor as O, ProcessAttributeCallback as P, Plugin as Q, Rule as R, Severity as S, TextNode as T, UserError as U, SchemaValidationPatch as V, WrappedError as W, definePlugin as X, Parser as Y, ruleExists as Z, EventHandler as _, ConfigData as a, Event as a0, ConfigReadyEvent as a1, SourceReadyEvent as a2, TokenEvent as a3, TagStartEvent as a4, TagOpenEvent as a5, TagEndEvent as a6, TagCloseEvent as a7, TagReadyEvent as a8, ElementReadyEvent as a9, AttributeEvent as aa, WhitespaceEvent as ab, ConditionalEvent as ac, DirectiveEvent as ad, DoctypeEvent as ae, DOMLoadEvent as af, DOMReadyEvent as ag, TriggerEventMap as ah, ListenEventMap as ai, FileSystemConfigLoader as aj, Formatter as ak, getFormatter as al, compatibilityCheck as am, CompatibilityOptions as an, ConfigError as b, ConfigLoader as c, StaticConfigLoader as d, Attribute as e, HtmlElement as f, CSSStyleDeclaration as g, TokenDump as h, SchemaValidationError as i, NestedError as j, MetaDataTable as k, MetaElement as l, MetaAttribute as m, MetaAttributeAllowedCallback as n, MetaTable as o, presets as p, MetaCopyableProperty as q, RuleConstructor as r, RuleDocumentation as s, Source as t, SourceHooks as u, version as v, ProcessElementCallback as w, ProcessElementContext as x, sliceLocation as y, Report as z };
|
package/dist/es/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import betterAjvErrors from '@sidvind/better-ajv-errors';
|
|
3
|
-
import { i as isKeywordIgnored, C as CaseStyle, n as naturalJoin, c as classifyNodeText, T as TextClassification, h as hasAltText, a as isHTMLHidden, b as isAriaHidden, d as hasAccessibleName, k as keywordPatternMatcher, e as inAccessibilityTree, f as hasAriaLabel } from './rules-helper.js';
|
|
3
|
+
import { i as isKeywordIgnored, C as CaseStyle, n as naturalJoin, c as classifyNodeText, T as TextClassification, h as hasAltText, p as partition, a as isHTMLHidden, b as isAriaHidden, d as hasAccessibleName, k as keywordPatternMatcher, e as inAccessibilityTree, f as hasAriaLabel } from './rules-helper.js';
|
|
4
4
|
import Ajv from 'ajv';
|
|
5
5
|
import deepmerge from 'deepmerge';
|
|
6
6
|
import * as espree from 'espree';
|
|
@@ -303,6 +303,10 @@ class Attribute {
|
|
|
303
303
|
if (this.value instanceof DynamicValue) {
|
|
304
304
|
return dynamicMatches;
|
|
305
305
|
}
|
|
306
|
+
/* test against an array of keywords */
|
|
307
|
+
if (Array.isArray(pattern)) {
|
|
308
|
+
return pattern.includes(this.value);
|
|
309
|
+
}
|
|
306
310
|
/* test value against pattern */
|
|
307
311
|
if (pattern instanceof RegExp) {
|
|
308
312
|
return this.value.match(pattern) !== null;
|
|
@@ -466,6 +470,7 @@ const MetaCopyableProperty = [
|
|
|
466
470
|
"interactive",
|
|
467
471
|
"transparent",
|
|
468
472
|
"form",
|
|
473
|
+
"formAssociated",
|
|
469
474
|
"labelable",
|
|
470
475
|
"attributes",
|
|
471
476
|
"permittedContent",
|
|
@@ -1656,6 +1661,10 @@ class UserError extends NestedError {
|
|
|
1656
1661
|
Error.captureStackTrace(this, UserError);
|
|
1657
1662
|
this.name = UserError.name;
|
|
1658
1663
|
}
|
|
1664
|
+
/**
|
|
1665
|
+
* @public
|
|
1666
|
+
*/
|
|
1667
|
+
/* istanbul ignore next: default implementation */
|
|
1659
1668
|
prettyFormat() {
|
|
1660
1669
|
return undefined;
|
|
1661
1670
|
}
|
|
@@ -1878,6 +1887,10 @@ const patternProperties = {
|
|
|
1878
1887
|
title: "Mark element as a submittable form element",
|
|
1879
1888
|
type: "boolean"
|
|
1880
1889
|
},
|
|
1890
|
+
formAssociated: {
|
|
1891
|
+
title: "Mark element as a form-associated element",
|
|
1892
|
+
$ref: "#/definitions/FormAssociated"
|
|
1893
|
+
},
|
|
1881
1894
|
labelable: {
|
|
1882
1895
|
title: "Mark this element as labelable",
|
|
1883
1896
|
description: "This element may contain an associated label element.",
|
|
@@ -2023,6 +2036,16 @@ const definitions = {
|
|
|
2023
2036
|
}
|
|
2024
2037
|
}
|
|
2025
2038
|
},
|
|
2039
|
+
FormAssociated: {
|
|
2040
|
+
type: "object",
|
|
2041
|
+
additionalProperties: false,
|
|
2042
|
+
properties: {
|
|
2043
|
+
listed: {
|
|
2044
|
+
type: "boolean",
|
|
2045
|
+
title: "Listed elements have a name attribute and is listed in the form and fieldset elements property."
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
},
|
|
2026
2049
|
Permitted: {
|
|
2027
2050
|
type: "array",
|
|
2028
2051
|
items: {
|
|
@@ -2281,6 +2304,9 @@ function migrateAttributes(src) {
|
|
|
2281
2304
|
function migrateElement(src) {
|
|
2282
2305
|
const result = {
|
|
2283
2306
|
...src,
|
|
2307
|
+
...{
|
|
2308
|
+
formAssociated: undefined,
|
|
2309
|
+
},
|
|
2284
2310
|
attributes: migrateAttributes(src),
|
|
2285
2311
|
textContent: src.textContent,
|
|
2286
2312
|
};
|
|
@@ -2291,6 +2317,14 @@ function migrateElement(src) {
|
|
|
2291
2317
|
if (!result.textContent) {
|
|
2292
2318
|
delete result.textContent;
|
|
2293
2319
|
}
|
|
2320
|
+
if (src.formAssociated) {
|
|
2321
|
+
result.formAssociated = {
|
|
2322
|
+
listed: Boolean(src.formAssociated.listed),
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
else {
|
|
2326
|
+
delete result.formAssociated;
|
|
2327
|
+
}
|
|
2294
2328
|
return result;
|
|
2295
2329
|
}
|
|
2296
2330
|
|
|
@@ -3453,6 +3487,16 @@ class Rule {
|
|
|
3453
3487
|
isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
|
|
3454
3488
|
return isKeywordIgnored(this.options, keyword, matcher);
|
|
3455
3489
|
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Get [[MetaElement]] for the given tag. If no specific metadata is present
|
|
3492
|
+
* the global metadata is returned or null if no global is present.
|
|
3493
|
+
*
|
|
3494
|
+
* @public
|
|
3495
|
+
* @returns A shallow copy of metadata.
|
|
3496
|
+
*/
|
|
3497
|
+
getMetaFor(tagName) {
|
|
3498
|
+
return this.meta.getMetaFor(tagName);
|
|
3499
|
+
}
|
|
3456
3500
|
/**
|
|
3457
3501
|
* Find all tags which has enabled given property.
|
|
3458
3502
|
*/
|
|
@@ -4863,7 +4907,7 @@ class AttributeMisuse extends Rule {
|
|
|
4863
4907
|
if (!meta || !meta.allowed) {
|
|
4864
4908
|
return;
|
|
4865
4909
|
}
|
|
4866
|
-
const details = meta.allowed(node);
|
|
4910
|
+
const details = meta.allowed(node, attr);
|
|
4867
4911
|
if (details) {
|
|
4868
4912
|
this.report({
|
|
4869
4913
|
node,
|
|
@@ -5887,12 +5931,135 @@ class EmptyTitle extends Rule {
|
|
|
5887
5931
|
}
|
|
5888
5932
|
}
|
|
5889
5933
|
|
|
5934
|
+
const UNIQUE_CACHE_KEY = Symbol("form-elements-unique");
|
|
5935
|
+
const SHARED_CACHE_KEY = Symbol("form-elements-shared");
|
|
5936
|
+
function haveName(name) {
|
|
5937
|
+
return typeof name === "string" && name !== "";
|
|
5938
|
+
}
|
|
5939
|
+
function allowSharedName(node) {
|
|
5940
|
+
const type = node.getAttribute("type");
|
|
5941
|
+
return Boolean(type && type.valueMatches(["radio", "checkbox"], false));
|
|
5942
|
+
}
|
|
5943
|
+
class FormDupName extends Rule {
|
|
5944
|
+
documentation() {
|
|
5945
|
+
return {
|
|
5946
|
+
description: "Each form control must have a unique name.",
|
|
5947
|
+
url: "https://html-validate.org/rules/form-dup-name.html",
|
|
5948
|
+
};
|
|
5949
|
+
}
|
|
5950
|
+
setup() {
|
|
5951
|
+
const selector = this.getSelector();
|
|
5952
|
+
this.on("dom:ready", (event) => {
|
|
5953
|
+
var _a, _b;
|
|
5954
|
+
const { document } = event;
|
|
5955
|
+
const controls = document.querySelectorAll(selector);
|
|
5956
|
+
const [sharedControls, uniqueControls] = partition(controls, allowSharedName);
|
|
5957
|
+
/* validate all form controls which require unique elements first so each
|
|
5958
|
+
* form has a populated list of unique names */
|
|
5959
|
+
for (const control of uniqueControls) {
|
|
5960
|
+
const attr = control.getAttribute("name");
|
|
5961
|
+
const name = attr === null || attr === void 0 ? void 0 : attr.value;
|
|
5962
|
+
if (!attr || !haveName(name)) {
|
|
5963
|
+
continue;
|
|
5964
|
+
}
|
|
5965
|
+
const form = (_a = control.closest("form")) !== null && _a !== void 0 ? _a : document.root;
|
|
5966
|
+
this.validateUniqueName(control, form, attr, name);
|
|
5967
|
+
}
|
|
5968
|
+
/* validate all form controls which allows shared names to ensure there is
|
|
5969
|
+
* no collision with other form controls */
|
|
5970
|
+
for (const control of sharedControls) {
|
|
5971
|
+
const attr = control.getAttribute("name");
|
|
5972
|
+
const name = attr === null || attr === void 0 ? void 0 : attr.value;
|
|
5973
|
+
if (!attr || !haveName(name)) {
|
|
5974
|
+
continue;
|
|
5975
|
+
}
|
|
5976
|
+
const form = (_b = control.closest("form")) !== null && _b !== void 0 ? _b : document.root;
|
|
5977
|
+
this.validateSharedName(control, form, attr, name);
|
|
5978
|
+
}
|
|
5979
|
+
});
|
|
5980
|
+
}
|
|
5981
|
+
validateUniqueName(control, form, attr, name) {
|
|
5982
|
+
const elements = this.getUniqueElements(form);
|
|
5983
|
+
if (elements.has(name)) {
|
|
5984
|
+
const context = {
|
|
5985
|
+
name,
|
|
5986
|
+
};
|
|
5987
|
+
this.report({
|
|
5988
|
+
node: control,
|
|
5989
|
+
location: attr.valueLocation,
|
|
5990
|
+
message: 'Duplicate form control name "{{ name }}"',
|
|
5991
|
+
context,
|
|
5992
|
+
});
|
|
5993
|
+
}
|
|
5994
|
+
else {
|
|
5995
|
+
elements.add(name);
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
validateSharedName(control, form, attr, name) {
|
|
5999
|
+
var _a;
|
|
6000
|
+
const uniqueElements = this.getUniqueElements(form);
|
|
6001
|
+
const sharedElements = this.getSharedElements(form);
|
|
6002
|
+
/* istanbul ignore next: type will always be set or shared name wouldn't be allowed */
|
|
6003
|
+
const type = (_a = control.getAttributeValue("type")) !== null && _a !== void 0 ? _a : "";
|
|
6004
|
+
if (uniqueElements.has(name) ||
|
|
6005
|
+
(sharedElements.has(name) && sharedElements.get(name) !== type)) {
|
|
6006
|
+
const context = {
|
|
6007
|
+
name,
|
|
6008
|
+
};
|
|
6009
|
+
this.report({
|
|
6010
|
+
node: control,
|
|
6011
|
+
location: attr.valueLocation,
|
|
6012
|
+
message: 'Duplicate form control name "{{ name }}"',
|
|
6013
|
+
context,
|
|
6014
|
+
});
|
|
6015
|
+
}
|
|
6016
|
+
sharedElements.set(name, type);
|
|
6017
|
+
}
|
|
6018
|
+
getSelector() {
|
|
6019
|
+
const tags = this.getTagsWithProperty("formAssociated").filter((it) => {
|
|
6020
|
+
return this.isListedElement(it);
|
|
6021
|
+
});
|
|
6022
|
+
return tags.join(", ");
|
|
6023
|
+
}
|
|
6024
|
+
isListedElement(tagName) {
|
|
6025
|
+
const meta = this.getMetaFor(tagName);
|
|
6026
|
+
/* istanbul ignore if: the earlier check for getTagsWithProperty ensures
|
|
6027
|
+
* these will actually be set so this is just an untestable fallback */
|
|
6028
|
+
if (!meta || !meta.formAssociated) {
|
|
6029
|
+
return false;
|
|
6030
|
+
}
|
|
6031
|
+
return meta.formAssociated.listed;
|
|
6032
|
+
}
|
|
6033
|
+
getUniqueElements(form) {
|
|
6034
|
+
const existing = form.cacheGet(UNIQUE_CACHE_KEY);
|
|
6035
|
+
if (existing) {
|
|
6036
|
+
return existing;
|
|
6037
|
+
}
|
|
6038
|
+
else {
|
|
6039
|
+
const elements = new Set();
|
|
6040
|
+
form.cacheSet(UNIQUE_CACHE_KEY, elements);
|
|
6041
|
+
return elements;
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
getSharedElements(form) {
|
|
6045
|
+
const existing = form.cacheGet(SHARED_CACHE_KEY);
|
|
6046
|
+
if (existing) {
|
|
6047
|
+
return existing;
|
|
6048
|
+
}
|
|
6049
|
+
else {
|
|
6050
|
+
const elements = new Map();
|
|
6051
|
+
form.cacheSet(SHARED_CACHE_KEY, elements);
|
|
6052
|
+
return elements;
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
|
|
5890
6057
|
const defaults$j = {
|
|
5891
6058
|
allowMultipleH1: false,
|
|
5892
6059
|
minInitialRank: "h1",
|
|
5893
6060
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
5894
6061
|
};
|
|
5895
|
-
function isRelevant$
|
|
6062
|
+
function isRelevant$3(event) {
|
|
5896
6063
|
const node = event.target;
|
|
5897
6064
|
return Boolean(node.meta && node.meta.heading);
|
|
5898
6065
|
}
|
|
@@ -5960,7 +6127,7 @@ class HeadingLevel extends Rule {
|
|
|
5960
6127
|
};
|
|
5961
6128
|
}
|
|
5962
6129
|
setup() {
|
|
5963
|
-
this.on("tag:start", isRelevant$
|
|
6130
|
+
this.on("tag:start", isRelevant$3, (event) => this.onTagStart(event));
|
|
5964
6131
|
this.on("tag:ready", (event) => this.onTagReady(event));
|
|
5965
6132
|
this.on("tag:close", (event) => this.onTagClose(event));
|
|
5966
6133
|
}
|
|
@@ -6494,6 +6661,42 @@ class MapDupName extends Rule {
|
|
|
6494
6661
|
}
|
|
6495
6662
|
}
|
|
6496
6663
|
|
|
6664
|
+
function isRelevant$2(event) {
|
|
6665
|
+
return event.target.is("map");
|
|
6666
|
+
}
|
|
6667
|
+
function hasStaticValue(attr) {
|
|
6668
|
+
return Boolean(attr && !(attr.value instanceof DynamicValue));
|
|
6669
|
+
}
|
|
6670
|
+
class MapIdName extends Rule {
|
|
6671
|
+
documentation() {
|
|
6672
|
+
return {
|
|
6673
|
+
description: "When the `id` attribute is present on a `<map>` element it must be equal to the `name` attribute.",
|
|
6674
|
+
url: "https://html-validate.org/rules/map-id-name.html",
|
|
6675
|
+
};
|
|
6676
|
+
}
|
|
6677
|
+
setup() {
|
|
6678
|
+
this.on("tag:ready", isRelevant$2, (event) => {
|
|
6679
|
+
var _a;
|
|
6680
|
+
const { target } = event;
|
|
6681
|
+
const id = target.getAttribute("id");
|
|
6682
|
+
const name = target.getAttribute("name");
|
|
6683
|
+
// /* ignore missing attributes or dynamic value */
|
|
6684
|
+
if (!hasStaticValue(id) || !hasStaticValue(name)) {
|
|
6685
|
+
return;
|
|
6686
|
+
}
|
|
6687
|
+
/* ignore when id and name is the same */
|
|
6688
|
+
if (id.value === name.value) {
|
|
6689
|
+
return;
|
|
6690
|
+
}
|
|
6691
|
+
this.report({
|
|
6692
|
+
node: event.target,
|
|
6693
|
+
message: `"id" and "name" attribute must be the same on <map> elements`,
|
|
6694
|
+
location: (_a = id.valueLocation) !== null && _a !== void 0 ? _a : name.valueLocation,
|
|
6695
|
+
});
|
|
6696
|
+
});
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
|
|
6497
6700
|
class MissingDoctype extends Rule {
|
|
6498
6701
|
documentation() {
|
|
6499
6702
|
return {
|
|
@@ -8958,12 +9161,14 @@ const bundledRules = {
|
|
|
8958
9161
|
"element-required-content": ElementRequiredContent,
|
|
8959
9162
|
"empty-heading": EmptyHeading,
|
|
8960
9163
|
"empty-title": EmptyTitle,
|
|
9164
|
+
"form-dup-name": FormDupName,
|
|
8961
9165
|
"heading-level": HeadingLevel,
|
|
8962
9166
|
"id-pattern": IdPattern,
|
|
8963
9167
|
"input-attributes": InputAttributes,
|
|
8964
9168
|
"input-missing-label": InputMissingLabel,
|
|
8965
9169
|
"long-title": LongTitle,
|
|
8966
9170
|
"map-dup-name": MapDupName,
|
|
9171
|
+
"map-id-name": MapIdName,
|
|
8967
9172
|
"meta-refresh": MetaRefresh,
|
|
8968
9173
|
"missing-doctype": MissingDoctype,
|
|
8969
9174
|
"multiple-labeled-controls": MultipleLabeledControls,
|
|
@@ -9072,9 +9277,11 @@ const config$1 = {
|
|
|
9072
9277
|
"element-required-content": "error",
|
|
9073
9278
|
"empty-heading": "error",
|
|
9074
9279
|
"empty-title": "error",
|
|
9280
|
+
"form-dup-name": "error",
|
|
9075
9281
|
"input-attributes": "error",
|
|
9076
9282
|
"long-title": "error",
|
|
9077
9283
|
"map-dup-name": "error",
|
|
9284
|
+
"map-id-name": "error",
|
|
9078
9285
|
"meta-refresh": "error",
|
|
9079
9286
|
"multiple-labeled-controls": "error",
|
|
9080
9287
|
"no-autoplay": ["error", { include: ["audio", "video"] }],
|
|
@@ -9135,6 +9342,7 @@ const config = {
|
|
|
9135
9342
|
"element-required-attributes": "error",
|
|
9136
9343
|
"element-required-content": "error",
|
|
9137
9344
|
"map-dup-name": "error",
|
|
9345
|
+
"map-id-name": "error",
|
|
9138
9346
|
"multiple-labeled-controls": "error",
|
|
9139
9347
|
"no-deprecated-attr": "error",
|
|
9140
9348
|
"no-dup-attr": "error",
|
|
@@ -9964,6 +10172,8 @@ class Parser {
|
|
|
9964
10172
|
}
|
|
9965
10173
|
/* resolve any dynamic meta element properties */
|
|
9966
10174
|
this.dom.resolveMeta(this.metaTable);
|
|
10175
|
+
/* enable cache on root element, all children already have cached enabled */
|
|
10176
|
+
this.dom.root.cacheEnable();
|
|
9967
10177
|
/* trigger any rules waiting for DOM ready */
|
|
9968
10178
|
this.trigger("dom:ready", {
|
|
9969
10179
|
document: this.dom,
|
|
@@ -10795,6 +11005,8 @@ class Engine {
|
|
|
10795
11005
|
var _a, _b;
|
|
10796
11006
|
/* wait for a tag to open and find the current block by using its parent */
|
|
10797
11007
|
if (directiveBlock === null) {
|
|
11008
|
+
/* istanbul ignore next: there will always be a parent (root element if
|
|
11009
|
+
* nothing else) but typescript doesn't know that */
|
|
10798
11010
|
directiveBlock = (_b = (_a = data.target.parent) === null || _a === void 0 ? void 0 : _a.unique) !== null && _b !== void 0 ? _b : null;
|
|
10799
11011
|
}
|
|
10800
11012
|
/* disable rules directly on the node so it will be recorded for later,
|
|
@@ -11223,7 +11435,7 @@ class HtmlValidate {
|
|
|
11223
11435
|
/** @public */
|
|
11224
11436
|
const name = "html-validate";
|
|
11225
11437
|
/** @public */
|
|
11226
|
-
const version = "7.
|
|
11438
|
+
const version = "7.12.1";
|
|
11227
11439
|
/** @public */
|
|
11228
11440
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
11229
11441
|
|
|
@@ -11666,5 +11878,5 @@ function getFormatter(name) {
|
|
|
11666
11878
|
return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
|
|
11667
11879
|
}
|
|
11668
11880
|
|
|
11669
|
-
export { Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, definePlugin as j, getFormatter as k, legacyRequire as l, ensureError as m, configDataFromFile as n, compatibilityCheck as o, presets as p, codeframe as q, ruleExists as r, sliceLocation as s, isTextNode as t, isElementNode as u, version as v, generateIdSelector as w, name as x, bugs as y };
|
|
11881
|
+
export { Attribute as A, Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, definePlugin as j, getFormatter as k, legacyRequire as l, ensureError as m, configDataFromFile as n, compatibilityCheck as o, presets as p, codeframe as q, ruleExists as r, sliceLocation as s, isTextNode as t, isElementNode as u, version as v, generateIdSelector as w, name as x, bugs as y };
|
|
11670
11882
|
//# sourceMappingURL=core.js.map
|