@markuplint/rules 4.11.2 → 4.18.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/CHANGELOG.md +12 -1
- package/SKILL.md +76 -0
- package/lib/attr-check.d.ts +41 -5
- package/lib/attr-check.js +29 -5
- package/lib/attr-duplication/index.d.ts +7 -0
- package/lib/attr-duplication/index.js +7 -0
- package/lib/attr-duplication/meta.d.ts +1 -0
- package/lib/attr-duplication/meta.js +1 -0
- package/lib/attr-value-quotes/index.d.ts +10 -0
- package/lib/attr-value-quotes/index.js +10 -0
- package/lib/attr-value-quotes/meta.d.ts +1 -0
- package/lib/attr-value-quotes/meta.js +1 -0
- package/lib/case-sensitive-attr-name/index.d.ts +9 -0
- package/lib/case-sensitive-attr-name/index.js +8 -0
- package/lib/case-sensitive-attr-name/meta.d.ts +1 -0
- package/lib/case-sensitive-attr-name/meta.js +1 -0
- package/lib/case-sensitive-tag-name/index.d.ts +9 -0
- package/lib/case-sensitive-tag-name/index.js +8 -0
- package/lib/case-sensitive-tag-name/meta.d.ts +1 -0
- package/lib/case-sensitive-tag-name/meta.js +1 -0
- package/lib/character-reference/index.d.ts +8 -0
- package/lib/character-reference/index.js +16 -0
- package/lib/character-reference/meta.d.ts +1 -0
- package/lib/character-reference/meta.js +1 -0
- package/lib/class-naming/index.d.ts +9 -0
- package/lib/class-naming/index.js +8 -0
- package/lib/class-naming/meta.d.ts +1 -0
- package/lib/class-naming/meta.js +1 -0
- package/lib/create-message.d.ts +27 -0
- package/lib/create-message.js +56 -1
- package/lib/debug.d.ts +9 -0
- package/lib/debug.js +5 -0
- package/lib/deprecated-attr/index.d.ts +6 -0
- package/lib/deprecated-attr/index.js +6 -0
- package/lib/deprecated-attr/meta.d.ts +1 -0
- package/lib/deprecated-attr/meta.js +1 -0
- package/lib/deprecated-element/index.d.ts +8 -0
- package/lib/deprecated-element/index.js +8 -0
- package/lib/deprecated-element/meta.d.ts +1 -0
- package/lib/deprecated-element/meta.js +1 -0
- package/lib/disallowed-element/index.d.ts +7 -0
- package/lib/disallowed-element/index.js +7 -0
- package/lib/disallowed-element/meta.d.ts +1 -0
- package/lib/disallowed-element/meta.js +1 -0
- package/lib/doctype/index.d.ts +10 -0
- package/lib/doctype/index.js +8 -0
- package/lib/doctype/meta.d.ts +1 -0
- package/lib/doctype/meta.js +1 -0
- package/lib/end-tag/index.d.ts +7 -0
- package/lib/end-tag/index.js +7 -0
- package/lib/end-tag/meta.d.ts +1 -0
- package/lib/end-tag/meta.js +1 -0
- package/lib/heading-levels/index.d.ts +6 -0
- package/lib/heading-levels/index.js +6 -0
- package/lib/heading-levels/meta.d.ts +1 -0
- package/lib/heading-levels/meta.js +1 -0
- package/lib/helpers.d.ts +104 -0
- package/lib/helpers.js +105 -2
- package/lib/id-duplication/index.d.ts +7 -0
- package/lib/id-duplication/index.js +7 -0
- package/lib/id-duplication/meta.d.ts +1 -0
- package/lib/id-duplication/meta.js +1 -0
- package/lib/index.d.ts +23 -10
- package/lib/index.js +12 -0
- package/lib/ineffective-attr/index.d.ts +7 -0
- package/lib/ineffective-attr/index.js +7 -0
- package/lib/ineffective-attr/meta.d.ts +1 -0
- package/lib/ineffective-attr/meta.js +1 -0
- package/lib/invalid-attr/index.d.ts +24 -0
- package/lib/invalid-attr/index.js +17 -0
- package/lib/invalid-attr/meta.d.ts +1 -0
- package/lib/invalid-attr/meta.js +1 -0
- package/lib/label-has-control/index.d.ts +7 -0
- package/lib/label-has-control/index.js +11 -0
- package/lib/label-has-control/meta.d.ts +1 -0
- package/lib/label-has-control/meta.js +1 -0
- package/lib/landmark-roles/index.d.ts +14 -0
- package/lib/landmark-roles/index.js +21 -0
- package/lib/landmark-roles/meta.d.ts +1 -0
- package/lib/landmark-roles/meta.js +1 -0
- package/lib/neighbor-popovers/index.d.ts +8 -0
- package/lib/neighbor-popovers/index.js +26 -2
- package/lib/neighbor-popovers/meta.d.ts +1 -0
- package/lib/neighbor-popovers/meta.js +1 -0
- package/lib/no-ambiguous-navigable-target-names/index.d.ts +8 -0
- package/lib/no-ambiguous-navigable-target-names/index.js +8 -0
- package/lib/no-ambiguous-navigable-target-names/meta.d.ts +1 -0
- package/lib/no-ambiguous-navigable-target-names/meta.js +1 -0
- package/lib/no-boolean-attr-value/index.d.ts +7 -0
- package/lib/no-boolean-attr-value/index.js +7 -0
- package/lib/no-boolean-attr-value/meta.d.ts +1 -0
- package/lib/no-boolean-attr-value/meta.js +1 -0
- package/lib/no-consecutive-br/index.d.ts +8 -0
- package/lib/no-consecutive-br/index.js +8 -0
- package/lib/no-consecutive-br/meta.d.ts +1 -0
- package/lib/no-consecutive-br/meta.js +1 -0
- package/lib/no-default-value/index.d.ts +7 -0
- package/lib/no-default-value/index.js +7 -0
- package/lib/no-default-value/meta.d.ts +1 -0
- package/lib/no-default-value/meta.js +1 -0
- package/lib/no-duplicate-dt/index.d.ts +6 -0
- package/lib/no-duplicate-dt/index.js +6 -0
- package/lib/no-duplicate-dt/meta.d.ts +1 -0
- package/lib/no-duplicate-dt/meta.js +1 -0
- package/lib/no-empty-palpable-content/index.d.ts +13 -0
- package/lib/no-empty-palpable-content/index.js +12 -0
- package/lib/no-empty-palpable-content/meta.d.ts +1 -0
- package/lib/no-empty-palpable-content/meta.js +1 -0
- package/lib/no-hard-code-id/index.d.ts +8 -0
- package/lib/no-hard-code-id/index.js +8 -0
- package/lib/no-hard-code-id/meta.d.ts +1 -0
- package/lib/no-hard-code-id/meta.js +1 -0
- package/lib/no-orphaned-end-tag/index.d.ts +7 -0
- package/lib/no-orphaned-end-tag/index.js +7 -0
- package/lib/no-orphaned-end-tag/meta.d.ts +1 -0
- package/lib/no-orphaned-end-tag/meta.js +1 -0
- package/lib/no-refer-to-non-existent-id/index.d.ts +8 -0
- package/lib/no-refer-to-non-existent-id/index.js +9 -0
- package/lib/no-refer-to-non-existent-id/meta.d.ts +1 -0
- package/lib/no-refer-to-non-existent-id/meta.js +1 -0
- package/lib/no-use-event-handler-attr/index.d.ts +10 -0
- package/lib/no-use-event-handler-attr/index.js +8 -0
- package/lib/no-use-event-handler-attr/meta.d.ts +1 -0
- package/lib/no-use-event-handler-attr/meta.js +1 -0
- package/lib/permitted-contents/choice.d.ts +17 -0
- package/lib/permitted-contents/choice.js +30 -0
- package/lib/permitted-contents/complex-branch.d.ts +11 -7
- package/lib/permitted-contents/complex-branch.js +11 -7
- package/lib/permitted-contents/content-model.d.ts +11 -0
- package/lib/permitted-contents/content-model.js +32 -0
- package/lib/permitted-contents/count-pattern.d.ts +13 -7
- package/lib/permitted-contents/count-pattern.js +23 -7
- package/lib/permitted-contents/debug.browser.d.ts +12 -0
- package/lib/permitted-contents/debug.browser.js +12 -0
- package/lib/permitted-contents/debug.d.ts +12 -0
- package/lib/permitted-contents/debug.js +12 -0
- package/lib/permitted-contents/index.d.ts +12 -0
- package/lib/permitted-contents/index.js +21 -0
- package/lib/permitted-contents/matches-selector.d.ts +19 -0
- package/lib/permitted-contents/matches-selector.js +33 -0
- package/lib/permitted-contents/meta.d.ts +1 -0
- package/lib/permitted-contents/meta.js +1 -0
- package/lib/permitted-contents/order.d.ts +14 -7
- package/lib/permitted-contents/order.js +14 -7
- package/lib/permitted-contents/recursive-branch.d.ts +17 -0
- package/lib/permitted-contents/recursive-branch.js +17 -0
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +29 -0
- package/lib/permitted-contents/represent-transparent-nodes.js +24 -0
- package/lib/permitted-contents/start.d.ts +12 -6
- package/lib/permitted-contents/start.js +12 -6
- package/lib/permitted-contents/transparent.d.ts +12 -0
- package/lib/permitted-contents/transparent.js +12 -0
- package/lib/permitted-contents/types.d.ts +67 -0
- package/lib/permitted-contents/utils.d.ts +160 -0
- package/lib/permitted-contents/utils.js +194 -0
- package/lib/placeholder-label-option/index.d.ts +7 -0
- package/lib/placeholder-label-option/index.js +19 -15
- package/lib/placeholder-label-option/meta.d.ts +1 -0
- package/lib/placeholder-label-option/meta.js +1 -0
- package/lib/require-accessible-name/index.d.ts +11 -0
- package/lib/require-accessible-name/index.js +7 -0
- package/lib/require-accessible-name/meta.d.ts +1 -0
- package/lib/require-accessible-name/meta.js +1 -0
- package/lib/require-datetime/index.d.ts +12 -0
- package/lib/require-datetime/index.js +8 -0
- package/lib/require-datetime/meta.d.ts +1 -0
- package/lib/require-datetime/meta.js +1 -0
- package/lib/require-datetime/types.d.ts +7 -0
- package/lib/require-datetime/utils.d.ts +18 -5
- package/lib/require-datetime/utils.js +53 -9
- package/lib/required-attr/index.d.ts +13 -0
- package/lib/required-attr/index.js +7 -0
- package/lib/required-attr/meta.d.ts +1 -0
- package/lib/required-attr/meta.js +1 -0
- package/lib/required-element/index.d.ts +14 -0
- package/lib/required-element/index.js +14 -2
- package/lib/required-element/meta.d.ts +1 -0
- package/lib/required-element/meta.js +1 -0
- package/lib/required-h1/index.d.ts +12 -0
- package/lib/required-h1/index.js +7 -0
- package/lib/required-h1/meta.d.ts +1 -0
- package/lib/required-h1/meta.js +1 -0
- package/lib/table-row-column-alignment/find-children.d.ts +10 -0
- package/lib/table-row-column-alignment/find-children.js +11 -0
- package/lib/table-row-column-alignment/grid.d.ts +60 -0
- package/lib/table-row-column-alignment/grid.js +85 -0
- package/lib/table-row-column-alignment/index.d.ts +8 -0
- package/lib/table-row-column-alignment/index.js +8 -0
- package/lib/table-row-column-alignment/meta.d.ts +1 -0
- package/lib/table-row-column-alignment/meta.js +1 -0
- package/lib/table-row-column-alignment/types.d.ts +10 -0
- package/lib/use-list/index.d.ts +16 -0
- package/lib/use-list/index.js +20 -0
- package/lib/use-list/meta.d.ts +1 -0
- package/lib/use-list/meta.js +1 -0
- package/lib/wai-aria/checkings/abstract-role.d.ts +9 -0
- package/lib/wai-aria/checkings/abstract-role.js +9 -0
- package/lib/wai-aria/checkings/default-value.d.ts +11 -0
- package/lib/wai-aria/checkings/default-value.js +11 -0
- package/lib/wai-aria/checkings/deprecated-props.d.ts +11 -0
- package/lib/wai-aria/checkings/deprecated-props.js +11 -0
- package/lib/wai-aria/checkings/deprecated-role.d.ts +10 -0
- package/lib/wai-aria/checkings/deprecated-role.js +10 -0
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +14 -0
- package/lib/wai-aria/checkings/disallowed-prop.js +14 -0
- package/lib/wai-aria/checkings/implicit-props.d.ts +13 -0
- package/lib/wai-aria/checkings/implicit-props.js +13 -0
- package/lib/wai-aria/checkings/implicit-role.d.ts +9 -0
- package/lib/wai-aria/checkings/implicit-role.js +9 -0
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +9 -0
- package/lib/wai-aria/checkings/interaction-in-hidden.js +15 -4
- package/lib/wai-aria/checkings/no-global-prop.d.ts +10 -0
- package/lib/wai-aria/checkings/no-global-prop.js +10 -0
- package/lib/wai-aria/checkings/non-existent-role.d.ts +10 -0
- package/lib/wai-aria/checkings/non-existent-role.js +10 -0
- package/lib/wai-aria/checkings/permitted-roles.d.ts +10 -0
- package/lib/wai-aria/checkings/permitted-roles.js +10 -0
- package/lib/wai-aria/checkings/presentational-children.d.ts +7 -1
- package/lib/wai-aria/checkings/presentational-children.js +14 -1
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +9 -1
- package/lib/wai-aria/checkings/required-owned-elements.js +16 -1
- package/lib/wai-aria/checkings/required-prop.d.ts +13 -0
- package/lib/wai-aria/checkings/required-prop.js +13 -0
- package/lib/wai-aria/checkings/value.d.ts +23 -0
- package/lib/wai-aria/checkings/value.js +32 -0
- package/lib/wai-aria/index.d.ts +10 -0
- package/lib/wai-aria/index.js +10 -0
- package/lib/wai-aria/meta.d.ts +1 -0
- package/lib/wai-aria/meta.js +1 -0
- package/lib/wai-aria/types.d.ts +17 -0
- package/package.json +11 -11
|
@@ -2,6 +2,14 @@ import { createRule } from '@markuplint/ml-core';
|
|
|
2
2
|
import { check } from '@markuplint/types';
|
|
3
3
|
import meta from './meta.js';
|
|
4
4
|
import { getCandidateDatetimeString } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Rule that validates `<time>` elements without a `datetime` attribute have
|
|
7
|
+
* machine-readable date/time content.
|
|
8
|
+
*
|
|
9
|
+
* When a `<time>` element lacks the `datetime` attribute, this rule checks
|
|
10
|
+
* whether the text content is a valid datetime string. If not, it attempts
|
|
11
|
+
* to parse the content as natural language and suggests a `datetime` value.
|
|
12
|
+
*/
|
|
5
13
|
export default createRule({
|
|
6
14
|
meta: meta,
|
|
7
15
|
defaultOptions: {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
/** Union of possible date/time component keys used in parsed datetime data. */
|
|
1
2
|
export type DateTimeKey = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'ms';
|
|
3
|
+
/** A partial record mapping date/time component keys to their numeric values. */
|
|
2
4
|
export type DateTimeData = Partial<Record<DateTimeKey, number>>;
|
|
5
|
+
/** Represents a parsed date/time value with optional timezone offset. */
|
|
3
6
|
export type DateTime = {
|
|
7
|
+
/** The parsed date/time component values. */
|
|
4
8
|
datetime: DateTimeData;
|
|
9
|
+
/** Timezone offset in minutes from UTC (e.g., -300 for UTC-5). */
|
|
5
10
|
zone?: number;
|
|
6
11
|
};
|
|
7
12
|
/**
|
|
13
|
+
* Supported locale codes for natural language date/time parsing.
|
|
14
|
+
*
|
|
8
15
|
* @see https://github.com/wanasit/chrono#locales
|
|
9
16
|
*/
|
|
10
17
|
export type Lang = 'en' | 'ja' | 'fr' | 'nl' | 'ru' | 'de' | 'pt' | 'zh';
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import type { DateTime, Lang } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Parses natural language datetime text into structured datetime data.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
5
|
+
* Tries multiple locale parsers and returns the first successful parse result
|
|
6
|
+
* with only the certain (non-implied) date/time components included.
|
|
7
|
+
*
|
|
8
|
+
* @param content - The text content to parse as a datetime.
|
|
9
|
+
* @param langs - Locale codes to attempt parsing with.
|
|
10
|
+
* @param base - Optional reference date for relative date parsing (e.g., "tomorrow").
|
|
11
|
+
* @returns Parsed datetime data with timezone, or `null` if parsing fails.
|
|
9
12
|
*/
|
|
10
13
|
export declare function parseADatetime(content: string, langs: readonly Lang[], base?: Readonly<Date>): DateTime | null;
|
|
14
|
+
/**
|
|
15
|
+
* Generates an HTML `datetime` attribute value from natural language text.
|
|
16
|
+
*
|
|
17
|
+
* Parses the text content using chrono-node and formats the result as an
|
|
18
|
+
* ISO 8601-like datetime string suitable for the `datetime` attribute.
|
|
19
|
+
*
|
|
20
|
+
* @param content - The text content to parse.
|
|
21
|
+
* @param langs - Locale codes to use for parsing. Defaults to all supported languages.
|
|
22
|
+
* @returns A formatted datetime string, or `null` if the content cannot be parsed.
|
|
23
|
+
*/
|
|
11
24
|
export declare function getCandidateDatetimeString(content: string, langs?: Lang[]): string | null;
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import * as chrono from 'chrono-node';
|
|
2
|
+
/** Default set of languages to try when parsing natural language datetime text. */
|
|
2
3
|
const defaultLangs = ['en', 'ja', 'fr', 'nl', 'ru', 'de', 'pt', 'zh'];
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Parses natural language datetime text into structured datetime data.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
7
|
+
* Tries multiple locale parsers and returns the first successful parse result
|
|
8
|
+
* with only the certain (non-implied) date/time components included.
|
|
9
|
+
*
|
|
10
|
+
* @param content - The text content to parse as a datetime.
|
|
11
|
+
* @param langs - Locale codes to attempt parsing with.
|
|
12
|
+
* @param base - Optional reference date for relative date parsing (e.g., "tomorrow").
|
|
13
|
+
* @returns Parsed datetime data with timezone, or `null` if parsing fails.
|
|
10
14
|
*/
|
|
11
15
|
export function parseADatetime(content, langs, base) {
|
|
12
16
|
const date = parseTryMultipleLangs(content, langs, base);
|
|
@@ -43,6 +47,16 @@ export function parseADatetime(content, langs, base) {
|
|
|
43
47
|
}
|
|
44
48
|
return datetime;
|
|
45
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Generates an HTML `datetime` attribute value from natural language text.
|
|
52
|
+
*
|
|
53
|
+
* Parses the text content using chrono-node and formats the result as an
|
|
54
|
+
* ISO 8601-like datetime string suitable for the `datetime` attribute.
|
|
55
|
+
*
|
|
56
|
+
* @param content - The text content to parse.
|
|
57
|
+
* @param langs - Locale codes to use for parsing. Defaults to all supported languages.
|
|
58
|
+
* @returns A formatted datetime string, or `null` if the content cannot be parsed.
|
|
59
|
+
*/
|
|
46
60
|
export function getCandidateDatetimeString(content, langs = defaultLangs) {
|
|
47
61
|
const date = parseADatetime(content, langs);
|
|
48
62
|
if (!date) {
|
|
@@ -60,6 +74,15 @@ export function getCandidateDatetimeString(content, langs = defaultLangs) {
|
|
|
60
74
|
}
|
|
61
75
|
return datetimeStr;
|
|
62
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Converts parsed datetime data into an ISO 8601-like string.
|
|
79
|
+
*
|
|
80
|
+
* Produces different formats depending on which components are present
|
|
81
|
+
* (e.g., date-only, time-only, or combined date-time).
|
|
82
|
+
*
|
|
83
|
+
* @param date - The parsed date/time component data.
|
|
84
|
+
* @returns A formatted datetime string, or `null` if the components do not match any known format.
|
|
85
|
+
*/
|
|
63
86
|
function toDatetimeString(date) {
|
|
64
87
|
if (only(date, ['year', 'month'])) {
|
|
65
88
|
return `${f(date.year, 4)}-${f(date.month, 2)}`;
|
|
@@ -90,6 +113,17 @@ function toDatetimeString(date) {
|
|
|
90
113
|
}
|
|
91
114
|
return null;
|
|
92
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Attempts to parse datetime text using multiple locale parsers sequentially.
|
|
118
|
+
*
|
|
119
|
+
* Returns the parsed start component from the first locale that produces a
|
|
120
|
+
* valid result (non-range, single datetime).
|
|
121
|
+
*
|
|
122
|
+
* @param content - The text content to parse.
|
|
123
|
+
* @param langs - Locale codes to try in order.
|
|
124
|
+
* @param base - Optional reference date for relative date parsing.
|
|
125
|
+
* @returns The parsed start component, or `null` if no locale succeeds.
|
|
126
|
+
*/
|
|
93
127
|
function parseTryMultipleLangs(content, langs, base) {
|
|
94
128
|
for (const lang of langs) {
|
|
95
129
|
const results =
|
|
@@ -111,6 +145,16 @@ function parseTryMultipleLangs(content, langs, base) {
|
|
|
111
145
|
}
|
|
112
146
|
return null;
|
|
113
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Type guard that checks if the given datetime data contains only the specified keys.
|
|
150
|
+
*
|
|
151
|
+
* @template K - The array of datetime keys to check for.
|
|
152
|
+
* @template U - Union of the key types.
|
|
153
|
+
* @template R - The resulting narrowed type with required properties.
|
|
154
|
+
* @param date - The datetime data to check.
|
|
155
|
+
* @param keys - The keys that should be the only ones present.
|
|
156
|
+
* @returns `true` if the date contains only the specified keys (and narrows the type).
|
|
157
|
+
*/
|
|
114
158
|
function only(date, keys) {
|
|
115
159
|
const list = Object.keys(date);
|
|
116
160
|
for (const exists of list) {
|
|
@@ -121,11 +165,11 @@ function only(date, keys) {
|
|
|
121
165
|
return true;
|
|
122
166
|
}
|
|
123
167
|
/**
|
|
124
|
-
*
|
|
168
|
+
* Formats a number with zero-padding to the specified width.
|
|
125
169
|
*
|
|
126
|
-
* @param n
|
|
127
|
-
* @param pad
|
|
128
|
-
* @returns
|
|
170
|
+
* @param n - The number to format.
|
|
171
|
+
* @param pad - The minimum number of digits in the output string.
|
|
172
|
+
* @returns The zero-padded string representation.
|
|
129
173
|
*/
|
|
130
174
|
function f(n, pad) {
|
|
131
175
|
return n.toString(10).padStart(pad, '0');
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
/** The rule value: either a single attribute name or an array of required attribute descriptors. */
|
|
1
2
|
type RequiredAttributes = string | (string | Attr)[];
|
|
3
|
+
/**
|
|
4
|
+
* Descriptor for a required attribute with an optional set of allowed values.
|
|
5
|
+
*/
|
|
2
6
|
type Attr = {
|
|
7
|
+
/** The attribute name that is required. */
|
|
3
8
|
name: string;
|
|
9
|
+
/** One or more allowed values for the attribute, if constrained. */
|
|
4
10
|
value?: string | string[];
|
|
5
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Rule that validates elements have all required attributes.
|
|
14
|
+
*
|
|
15
|
+
* Checks both HTML-spec-defined required attributes and custom required attributes
|
|
16
|
+
* specified in the rule configuration. Also validates that required attribute values
|
|
17
|
+
* match expected patterns when value constraints are provided.
|
|
18
|
+
*/
|
|
6
19
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RequiredAttributes, undefined>>;
|
|
7
20
|
export default _default;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createRule, getAttrSpecs } from '@markuplint/ml-core';
|
|
2
2
|
import { attrMatches, match } from '../helpers.js';
|
|
3
3
|
import meta from './meta.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rule that validates elements have all required attributes.
|
|
6
|
+
*
|
|
7
|
+
* Checks both HTML-spec-defined required attributes and custom required attributes
|
|
8
|
+
* specified in the rule configuration. Also validates that required attribute values
|
|
9
|
+
* match expected patterns when value constraints are provided.
|
|
10
|
+
*/
|
|
4
11
|
export default createRule({
|
|
5
12
|
meta: meta,
|
|
6
13
|
defaultValue: [],
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the required-element rule.
|
|
3
|
+
*/
|
|
1
4
|
type Options = {
|
|
5
|
+
/** Whether to skip validation for elements that contain mutable (dynamic) content. */
|
|
2
6
|
ignoreHasMutableContents: boolean;
|
|
7
|
+
/** Whether to ignore omitted (ghost) elements that are implicitly created by the HTML parser. */
|
|
8
|
+
ignoreOmittedElements: boolean;
|
|
3
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Rule that validates required child elements are present.
|
|
12
|
+
*
|
|
13
|
+
* Accepts an array of CSS selectors as the rule value. For the document level,
|
|
14
|
+
* reports if any matching element is missing from the entire document. For
|
|
15
|
+
* element-level overrides, checks that the specified child elements exist
|
|
16
|
+
* among the element's direct children.
|
|
17
|
+
*/
|
|
4
18
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<string[], Options>>;
|
|
5
19
|
export default _default;
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { createRule } from '@markuplint/ml-core';
|
|
2
2
|
import meta from './meta.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rule that validates required child elements are present.
|
|
5
|
+
*
|
|
6
|
+
* Accepts an array of CSS selectors as the rule value. For the document level,
|
|
7
|
+
* reports if any matching element is missing from the entire document. For
|
|
8
|
+
* element-level overrides, checks that the specified child elements exist
|
|
9
|
+
* among the element's direct children.
|
|
10
|
+
*/
|
|
3
11
|
export default createRule({
|
|
4
12
|
meta: meta,
|
|
5
13
|
defaultValue: [],
|
|
6
14
|
defaultOptions: {
|
|
7
15
|
ignoreHasMutableContents: true,
|
|
16
|
+
ignoreOmittedElements: false,
|
|
8
17
|
},
|
|
9
18
|
async verify({ document, report, t }) {
|
|
10
19
|
const hasMutableContent = document.nodeList.some(n => n.is(n.ELEMENT_NODE) && n.hasMutableChildren());
|
|
11
20
|
if (!hasMutableContent) {
|
|
12
21
|
for (const query of document.rule.value) {
|
|
13
|
-
const
|
|
22
|
+
const matched = document.querySelectorAll(query);
|
|
23
|
+
const exists = document.rule.options.ignoreOmittedElements
|
|
24
|
+
? [...matched].filter(el => !el.isOmitted)
|
|
25
|
+
: [...matched];
|
|
14
26
|
if (exists.length === 0) {
|
|
15
27
|
const message = t('Require {0}', t('the "{0*}" {1}', query, 'element'));
|
|
16
28
|
report({
|
|
@@ -30,7 +42,7 @@ export default createRule({
|
|
|
30
42
|
return;
|
|
31
43
|
}
|
|
32
44
|
for (const query of el.rule.value) {
|
|
33
|
-
const exists = [...el.children].find(child => child.matches(query));
|
|
45
|
+
const exists = [...el.children].find(child => !(el.rule.options.ignoreOmittedElements && child.isOmitted) && child.matches(query));
|
|
34
46
|
if (!exists) {
|
|
35
47
|
const message = t('Require {0}', t('the "{0*}" {1}', query, 'element'));
|
|
36
48
|
report({
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the required-h1 rule.
|
|
3
|
+
*/
|
|
1
4
|
export interface Options {
|
|
5
|
+
/** Whether to report a violation when more than one `<h1>` element is found. */
|
|
2
6
|
'expected-once': boolean;
|
|
7
|
+
/** Whether to apply this rule in document fragments (components, partials). */
|
|
3
8
|
'in-document-fragment': boolean;
|
|
4
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Rule that requires exactly one `<h1>` element in the document.
|
|
12
|
+
*
|
|
13
|
+
* Reports a violation when no `<h1>` is present. Optionally reports when
|
|
14
|
+
* multiple `<h1>` elements exist (controlled by `expected-once`). Can be
|
|
15
|
+
* configured to skip document fragments via the `in-document-fragment` option.
|
|
16
|
+
*/
|
|
5
17
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
|
|
6
18
|
export default _default;
|
package/lib/required-h1/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { createRule } from '@markuplint/ml-core';
|
|
2
2
|
import meta from './meta.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rule that requires exactly one `<h1>` element in the document.
|
|
5
|
+
*
|
|
6
|
+
* Reports a violation when no `<h1>` is present. Optionally reports when
|
|
7
|
+
* multiple `<h1>` elements exist (controlled by `expected-once`). Can be
|
|
8
|
+
* configured to skip document fragments via the `in-document-fragment` option.
|
|
9
|
+
*/
|
|
3
10
|
export default createRule({
|
|
4
11
|
meta: meta,
|
|
5
12
|
defaultOptions: {
|
package/lib/required-h1/meta.js
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import type { Element } from '@markuplint/ml-core';
|
|
2
|
+
/**
|
|
3
|
+
* Finds direct children of an element matching the given CSS selector, with caching.
|
|
4
|
+
*
|
|
5
|
+
* Results are cached by element UUID and selector to avoid redundant DOM traversals
|
|
6
|
+
* when the same element/selector combination is queried multiple times.
|
|
7
|
+
*
|
|
8
|
+
* @param el - The parent element to search within.
|
|
9
|
+
* @param selector - The CSS selector to match children against.
|
|
10
|
+
* @returns An array of matching child elements.
|
|
11
|
+
*/
|
|
2
12
|
export declare function findChildren(el: Element<boolean>, selector: string): Element<boolean, undefined>[];
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
/** Cache for `findChildren` results, keyed by element UUID and selector. */
|
|
1
2
|
const findCache = {};
|
|
3
|
+
/**
|
|
4
|
+
* Finds direct children of an element matching the given CSS selector, with caching.
|
|
5
|
+
*
|
|
6
|
+
* Results are cached by element UUID and selector to avoid redundant DOM traversals
|
|
7
|
+
* when the same element/selector combination is queried multiple times.
|
|
8
|
+
*
|
|
9
|
+
* @param el - The parent element to search within.
|
|
10
|
+
* @param selector - The CSS selector to match children against.
|
|
11
|
+
* @returns An array of matching child elements.
|
|
12
|
+
*/
|
|
2
13
|
export function findChildren(
|
|
3
14
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
4
15
|
el, selector) {
|
|
@@ -1,22 +1,82 @@
|
|
|
1
1
|
import type { CellType } from './types.js';
|
|
2
2
|
import type { Element } from '@markuplint/ml-core';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a table as a grid model, splitting it into thead, tbody, and tfoot sections.
|
|
5
|
+
*
|
|
6
|
+
* Each section is modeled as a 2D array of `CellType` values that account for
|
|
7
|
+
* `colspan` and `rowspan` attributes. Used by the `table-row-column-alignment`
|
|
8
|
+
* rule to detect misaligned rows and overlapping cells.
|
|
9
|
+
*/
|
|
3
10
|
export declare class Grid {
|
|
4
11
|
#private;
|
|
12
|
+
/** The grid model for the `<tbody>` section. */
|
|
5
13
|
readonly tbodyGrid: ReadonlyArray<ReadonlyArray<CellType>>;
|
|
14
|
+
/** The grid model for the `<tfoot>` section. */
|
|
6
15
|
readonly tfootGrid: ReadonlyArray<ReadonlyArray<CellType>>;
|
|
16
|
+
/** The grid model for the `<thead>` section. */
|
|
7
17
|
readonly theadGrid: ReadonlyArray<ReadonlyArray<CellType>>;
|
|
18
|
+
/**
|
|
19
|
+
* Constructs a grid model from a `<table>` element.
|
|
20
|
+
*
|
|
21
|
+
* @param table - The table element to model.
|
|
22
|
+
*/
|
|
8
23
|
constructor(table: Element<boolean>);
|
|
24
|
+
/**
|
|
25
|
+
* Returns all `<tr>` elements across all table sections in document order.
|
|
26
|
+
*
|
|
27
|
+
* @returns Combined array of row elements from thead, tbody, and tfoot.
|
|
28
|
+
*/
|
|
9
29
|
getAllRowElements(): Element<boolean, undefined>[];
|
|
30
|
+
/**
|
|
31
|
+
* Returns all grid rows that contain at least one actual cell element.
|
|
32
|
+
*
|
|
33
|
+
* @returns Combined array of grid rows from thead, tbody, and tfoot, filtered to rows with real cells.
|
|
34
|
+
*/
|
|
10
35
|
getAllRows(): (readonly CellType[])[];
|
|
36
|
+
/**
|
|
37
|
+
* Determines the expected (base) column count for the table.
|
|
38
|
+
*
|
|
39
|
+
* Prefers the thead section if available, then tfoot, then tbody.
|
|
40
|
+
*
|
|
41
|
+
* @returns The base number of columns that rows should have.
|
|
42
|
+
*/
|
|
11
43
|
getBaseColLength(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the grid and row elements for each table section (thead, tbody, tfoot).
|
|
46
|
+
*
|
|
47
|
+
* @returns An array of objects, each containing the section grid and its row elements.
|
|
48
|
+
*/
|
|
12
49
|
getSections(): {
|
|
13
50
|
section: readonly (readonly CellType[])[];
|
|
14
51
|
elements: readonly Element<boolean, undefined>[];
|
|
15
52
|
}[];
|
|
53
|
+
/**
|
|
54
|
+
* Checks whether any cell in the table grid has been marked as overlapping.
|
|
55
|
+
*
|
|
56
|
+
* @returns `true` if any cell has the overlap marker `'x'`.
|
|
57
|
+
*/
|
|
16
58
|
hasOverlapped(): boolean;
|
|
59
|
+
/** Logs the grid data for all three table sections to the console (for debugging). */
|
|
17
60
|
log(): void;
|
|
18
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Detects a rowspan that extends beyond the available rows in a table section.
|
|
64
|
+
*
|
|
65
|
+
* When a grid row exists without a corresponding row element, it indicates
|
|
66
|
+
* a rowspan overflow. Returns the offending `rowspan` attribute node if found.
|
|
67
|
+
*
|
|
68
|
+
* @param rows - The grid rows for a single table section.
|
|
69
|
+
* @param rowElements - The `<tr>` elements for the section.
|
|
70
|
+
* @returns An object containing the overflowing `rowSpan` attribute, or `null`.
|
|
71
|
+
*/
|
|
19
72
|
export declare function getOverflowRowSpan(rows: ReadonlyArray<ReadonlyArray<CellType>>, rowElements: ReadonlyArray<Element<boolean>>): {
|
|
20
73
|
rowSpan: import("@markuplint/ml-core").Attr<boolean, undefined>;
|
|
21
74
|
} | null;
|
|
75
|
+
/**
|
|
76
|
+
* Maps each cell in a grid row to its source cell index (for cells that correspond
|
|
77
|
+
* to actual `<td>`/`<th>` elements), or `null` for spanned cells.
|
|
78
|
+
*
|
|
79
|
+
* @param row - A single row of cell types from the grid model.
|
|
80
|
+
* @returns An array of cell indices or `null` values, one per grid column.
|
|
81
|
+
*/
|
|
22
82
|
export declare function getIndexes(row: readonly CellType[]): (number | null)[];
|
|
@@ -11,12 +11,27 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _Grid_tbodyRowElements, _Grid_tfootRowElements, _Grid_theadRowElements;
|
|
13
13
|
import { findChildren } from './find-children.js';
|
|
14
|
+
/**
|
|
15
|
+
* Represents a table as a grid model, splitting it into thead, tbody, and tfoot sections.
|
|
16
|
+
*
|
|
17
|
+
* Each section is modeled as a 2D array of `CellType` values that account for
|
|
18
|
+
* `colspan` and `rowspan` attributes. Used by the `table-row-column-alignment`
|
|
19
|
+
* rule to detect misaligned rows and overlapping cells.
|
|
20
|
+
*/
|
|
14
21
|
export class Grid {
|
|
22
|
+
/**
|
|
23
|
+
* Constructs a grid model from a `<table>` element.
|
|
24
|
+
*
|
|
25
|
+
* @param table - The table element to model.
|
|
26
|
+
*/
|
|
15
27
|
constructor(
|
|
16
28
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
17
29
|
table) {
|
|
30
|
+
/** The `<tr>` elements within `<tbody>`. */
|
|
18
31
|
_Grid_tbodyRowElements.set(this, void 0);
|
|
32
|
+
/** The `<tr>` elements within `<tfoot>`. */
|
|
19
33
|
_Grid_tfootRowElements.set(this, void 0);
|
|
34
|
+
/** The `<tr>` elements within `<thead>`. */
|
|
20
35
|
_Grid_theadRowElements.set(this, void 0);
|
|
21
36
|
const thead = findChildren(table, 'thead')[0];
|
|
22
37
|
const tbody = findChildren(table, 'tbody')[0];
|
|
@@ -28,9 +43,19 @@ export class Grid {
|
|
|
28
43
|
this.tbodyGrid = createGrid(__classPrivateFieldGet(this, _Grid_tbodyRowElements, "f"));
|
|
29
44
|
this.tfootGrid = createGrid(__classPrivateFieldGet(this, _Grid_tfootRowElements, "f"));
|
|
30
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns all `<tr>` elements across all table sections in document order.
|
|
48
|
+
*
|
|
49
|
+
* @returns Combined array of row elements from thead, tbody, and tfoot.
|
|
50
|
+
*/
|
|
31
51
|
getAllRowElements() {
|
|
32
52
|
return [...__classPrivateFieldGet(this, _Grid_theadRowElements, "f"), ...__classPrivateFieldGet(this, _Grid_tbodyRowElements, "f"), ...__classPrivateFieldGet(this, _Grid_tfootRowElements, "f")];
|
|
33
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns all grid rows that contain at least one actual cell element.
|
|
56
|
+
*
|
|
57
|
+
* @returns Combined array of grid rows from thead, tbody, and tfoot, filtered to rows with real cells.
|
|
58
|
+
*/
|
|
34
59
|
getAllRows() {
|
|
35
60
|
return [
|
|
36
61
|
...this.theadGrid.filter(hasElementFilter),
|
|
@@ -38,6 +63,13 @@ export class Grid {
|
|
|
38
63
|
...this.tfootGrid.filter(hasElementFilter),
|
|
39
64
|
];
|
|
40
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Determines the expected (base) column count for the table.
|
|
68
|
+
*
|
|
69
|
+
* Prefers the thead section if available, then tfoot, then tbody.
|
|
70
|
+
*
|
|
71
|
+
* @returns The base number of columns that rows should have.
|
|
72
|
+
*/
|
|
41
73
|
getBaseColLength() {
|
|
42
74
|
if (this.theadGrid.length > 0) {
|
|
43
75
|
return getBaseColLength(this.theadGrid);
|
|
@@ -47,6 +79,11 @@ export class Grid {
|
|
|
47
79
|
}
|
|
48
80
|
return getBaseColLength(this.tbodyGrid);
|
|
49
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Returns the grid and row elements for each table section (thead, tbody, tfoot).
|
|
84
|
+
*
|
|
85
|
+
* @returns An array of objects, each containing the section grid and its row elements.
|
|
86
|
+
*/
|
|
50
87
|
getSections() {
|
|
51
88
|
return [
|
|
52
89
|
{
|
|
@@ -63,11 +100,17 @@ export class Grid {
|
|
|
63
100
|
},
|
|
64
101
|
];
|
|
65
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Checks whether any cell in the table grid has been marked as overlapping.
|
|
105
|
+
*
|
|
106
|
+
* @returns `true` if any cell has the overlap marker `'x'`.
|
|
107
|
+
*/
|
|
66
108
|
hasOverlapped() {
|
|
67
109
|
return (this.tbodyGrid.some(row => row.includes('x')) ||
|
|
68
110
|
this.tfootGrid.some(row => row.includes('x')) ||
|
|
69
111
|
this.theadGrid.some(row => row.includes('x')));
|
|
70
112
|
}
|
|
113
|
+
/** Logs the grid data for all three table sections to the console (for debugging). */
|
|
71
114
|
log() {
|
|
72
115
|
// eslint-disable-next-line no-console
|
|
73
116
|
console.table(this.theadGrid);
|
|
@@ -78,6 +121,16 @@ export class Grid {
|
|
|
78
121
|
}
|
|
79
122
|
}
|
|
80
123
|
_Grid_tbodyRowElements = new WeakMap(), _Grid_tfootRowElements = new WeakMap(), _Grid_theadRowElements = new WeakMap();
|
|
124
|
+
/**
|
|
125
|
+
* Detects a rowspan that extends beyond the available rows in a table section.
|
|
126
|
+
*
|
|
127
|
+
* When a grid row exists without a corresponding row element, it indicates
|
|
128
|
+
* a rowspan overflow. Returns the offending `rowspan` attribute node if found.
|
|
129
|
+
*
|
|
130
|
+
* @param rows - The grid rows for a single table section.
|
|
131
|
+
* @param rowElements - The `<tr>` elements for the section.
|
|
132
|
+
* @returns An object containing the overflowing `rowSpan` attribute, or `null`.
|
|
133
|
+
*/
|
|
81
134
|
export function getOverflowRowSpan(rows,
|
|
82
135
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
83
136
|
rowElements) {
|
|
@@ -114,10 +167,26 @@ rowElements) {
|
|
|
114
167
|
}
|
|
115
168
|
return null;
|
|
116
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Maps each cell in a grid row to its source cell index (for cells that correspond
|
|
172
|
+
* to actual `<td>`/`<th>` elements), or `null` for spanned cells.
|
|
173
|
+
*
|
|
174
|
+
* @param row - A single row of cell types from the grid model.
|
|
175
|
+
* @returns An array of cell indices or `null` values, one per grid column.
|
|
176
|
+
*/
|
|
117
177
|
export function getIndexes(row) {
|
|
118
178
|
let indexCounter = 0;
|
|
119
179
|
return row.map(col => (col === '●' || col === '◎' ? indexCounter++ : null));
|
|
120
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Calculates the base (expected) column count from a section grid.
|
|
183
|
+
*
|
|
184
|
+
* For grids with 3 or more rows, uses the row length closest to the
|
|
185
|
+
* average to handle minor variations from spans. Otherwise uses the first row.
|
|
186
|
+
*
|
|
187
|
+
* @param grid - The 2D grid for a table section.
|
|
188
|
+
* @returns The base number of columns.
|
|
189
|
+
*/
|
|
121
190
|
function getBaseColLength(grid) {
|
|
122
191
|
let baseColLength;
|
|
123
192
|
if (grid.length >= 3) {
|
|
@@ -136,6 +205,16 @@ function getBaseColLength(grid) {
|
|
|
136
205
|
}
|
|
137
206
|
return baseColLength;
|
|
138
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Builds a 2D grid model from an array of `<tr>` elements.
|
|
210
|
+
*
|
|
211
|
+
* Processes each row's `<th>` and `<td>` cells, expanding `colspan` and `rowspan`
|
|
212
|
+
* attributes into the grid. Marks cells with appropriate cell types including
|
|
213
|
+
* span origins, continuations, and overlaps.
|
|
214
|
+
*
|
|
215
|
+
* @param rows - The `<tr>` elements to convert into a grid.
|
|
216
|
+
* @returns A 2D array of cell types representing the table section layout.
|
|
217
|
+
*/
|
|
139
218
|
function createGrid(
|
|
140
219
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
141
220
|
rows) {
|
|
@@ -181,6 +260,12 @@ rows) {
|
|
|
181
260
|
}
|
|
182
261
|
return matrix;
|
|
183
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Filter predicate that returns `true` if a grid row contains at least one actual cell element.
|
|
265
|
+
*
|
|
266
|
+
* @param row - A single row of cell types.
|
|
267
|
+
* @returns `true` if the row has a regular or span-origin cell.
|
|
268
|
+
*/
|
|
184
269
|
function hasElementFilter(row) {
|
|
185
270
|
return row.some(cell => cell === '●' || cell === '◎');
|
|
186
271
|
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule that validates table row and column alignment.
|
|
3
|
+
*
|
|
4
|
+
* Builds a grid model of each `<table>` and checks that all rows have a
|
|
5
|
+
* consistent number of columns, that `colspan`/`rowspan` attributes do not
|
|
6
|
+
* cause cell overlap, and that `rowspan` values do not exceed the available
|
|
7
|
+
* rows in their table section.
|
|
8
|
+
*/
|
|
1
9
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, undefined>>;
|
|
2
10
|
export default _default;
|
|
@@ -2,6 +2,14 @@ import { createRule } from '@markuplint/ml-core';
|
|
|
2
2
|
import { findChildren } from './find-children.js';
|
|
3
3
|
import { Grid, getIndexes, getOverflowRowSpan } from './grid.js';
|
|
4
4
|
import meta from './meta.js';
|
|
5
|
+
/**
|
|
6
|
+
* Rule that validates table row and column alignment.
|
|
7
|
+
*
|
|
8
|
+
* Builds a grid model of each `<table>` and checks that all rows have a
|
|
9
|
+
* consistent number of columns, that `colspan`/`rowspan` attributes do not
|
|
10
|
+
* cause cell overlap, and that `rowspan` values do not exceed the available
|
|
11
|
+
* rows in their table section.
|
|
12
|
+
*/
|
|
5
13
|
export default createRule({
|
|
6
14
|
meta,
|
|
7
15
|
defaultSeverity: 'warning',
|