OCaml HTML5 parser/serialiser based on Python's JustHTML
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at de4d9bbbe3a439c9cd0e723fae4e5fb14a8c48c5 739 lines 40 kB view raw
1(** Typed error codes for HTML5 validation messages. 2 3 This module defines a comprehensive variant type for all validation errors, 4 ensuring exact message matching with the Nu HTML Validator test suite. *) 5 6(** Severity level of a validation message *) 7type severity = Error | Warning | Info 8 9(** Typed error codes with associated data *) 10type t = 11 (* ===== Attribute Errors ===== *) 12 | Attr_not_allowed_on_element of { attr: string; element: string } 13 (** Attribute "X" not allowed on element "Y" at this point. *) 14 | Attr_not_allowed_here of { attr: string } 15 (** Attribute "X" not allowed here. *) 16 | Attr_not_allowed_when of { attr: string; element: string; condition: string } 17 (** Attribute "X" is only allowed when ... *) 18 | Missing_required_attr of { element: string; attr: string } 19 (** Element "X" is missing required attribute "Y". *) 20 | Missing_required_attr_one_of of { element: string; attrs: string list } 21 (** Element "X" is missing one or more of the following attributes: [A, B]. *) 22 | Bad_attr_value of { element: string; attr: string; value: string; reason: string } 23 (** Bad value "X" for attribute "Y" on element "Z". *) 24 | Bad_attr_value_generic of { message: string } 25 (** Generic bad attribute value message *) 26 | Duplicate_id of { id: string } 27 (** Duplicate ID "X". *) 28 | Data_attr_invalid_name of { reason: string } 29 (** "data-*" attribute names must be XML 1.0 4th ed. plus Namespaces NCNames. *) 30 | Data_attr_uppercase 31 (** "data-*" attributes must not have characters from the range "A"…"Z" in the name. *) 32 33 (* ===== Element Errors ===== *) 34 | Obsolete_element of { element: string; suggestion: string } 35 (** The "X" element is obsolete. Y *) 36 | Obsolete_attr of { element: string; attr: string; suggestion: string option } 37 (** The "X" attribute on the "Y" element is obsolete. *) 38 | Obsolete_global_attr of { attr: string; suggestion: string } 39 (** The "X" attribute is obsolete. Y *) 40 | Element_not_allowed_as_child of { child: string; parent: string } 41 (** Element "X" not allowed as child of element "Y" in this context. *) 42 | Unknown_element of { name: string } 43 (** Unknown element "X". *) 44 | Element_must_not_be_descendant of { element: string; attr: string option; ancestor: string } 45 (** The element "X" [with attribute "A"] must not appear as a descendant of the "Y" element. *) 46 | Missing_required_child of { parent: string; child: string } 47 (** Element "X" is missing required child element "Y". *) 48 | Missing_required_child_one_of of { parent: string; children: string list } 49 (** Element "X" is missing one or more of the following child elements: [A, B]. *) 50 | Missing_required_child_generic of { parent: string } 51 (** Element "X" is missing a required child element. *) 52 | Element_must_not_be_empty of { element: string } 53 (** Element "X" must not be empty. *) 54 | Stray_start_tag of { tag: string } 55 (** Stray start tag "X". *) 56 | Stray_end_tag of { tag: string } 57 (** Stray end tag "X". *) 58 | End_tag_for_void_element of { tag: string } 59 (** End tag "X". (for void elements like br) *) 60 | Self_closing_non_void 61 (** Self-closing syntax used on a non-void HTML element. *) 62 | Text_not_allowed of { parent: string } 63 (** Text not allowed in element "X" in this context. *) 64 65 (* ===== Child Restrictions ===== *) 66 | Div_child_of_dl_bad_role 67 (** A "div" child of a "dl" element must not have any "role" value other than "presentation" or "none". *) 68 | Li_bad_role_in_menu 69 (** An "li" element descendant of role=menu/menubar must have specific roles. *) 70 | Li_bad_role_in_tablist 71 (** An "li" element descendant of role=tablist must have role=tab. *) 72 | Li_bad_role_in_list 73 (** An "li" element descendant of ul/ol/menu or role=list must have role=listitem. *) 74 75 (* ===== ARIA Errors ===== *) 76 | Unnecessary_role of { role: string; element: string; reason: string } 77 (** The "X" role is unnecessary for Y. *) 78 | Bad_role of { element: string; role: string } 79 (** Bad value "X" for attribute "role" on element "Y". *) 80 | Aria_must_not_be_specified of { attr: string; element: string; condition: string } 81 (** The "X" attribute must not be specified on any "Y" element unless... *) 82 | Aria_must_not_be_used of { attr: string; element: string; condition: string } 83 (** The "X" attribute must not be used on an "Y" element which has... *) 84 | Aria_should_not_be_used of { attr: string; role: string } 85 (** The "X" attribute should not be used on any element which has "role=Y". *) 86 | Aria_hidden_on_body 87 (** "aria-hidden=true" must not be used on the "body" element. *) 88 | Img_empty_alt_with_role 89 (** An "img" element with empty alt must not have a role attribute. *) 90 | Checkbox_button_needs_aria_pressed 91 (** An "input" type="checkbox" with role="button" must have aria-pressed. *) 92 | Tab_without_tabpanel 93 (** Every active "role=tab" element must have a corresponding "role=tabpanel" element. *) 94 | Multiple_main_visible 95 (** A document should not include more than one visible element with "role=main". *) 96 | Discarding_unrecognized_role of { token: string } 97 (** Discarding unrecognized token "X" from value of attribute "role". *) 98 99 (* ===== Required Attribute/Element Conditions ===== *) 100 | Img_missing_alt 101 (** An "img" element must have an "alt" attribute. *) 102 | Img_missing_src_or_srcset 103 (** Element "img" is missing one or more of the following attributes: [src, srcset]. *) 104 | Option_empty_without_label 105 (** Element "option" without attribute "label" must not be empty. *) 106 | Bdo_missing_dir 107 (** Element "bdo" must have attribute "dir". *) 108 | Bdo_dir_auto 109 (** The value of "dir" attribute for the "bdo" element must not be "auto". *) 110 | Base_missing_href_or_target 111 (** Element "base" is missing one or more of the following attributes: [href, target]. *) 112 | Base_after_link_script 113 (** The "base" element must come before any "link" or "script" elements. *) 114 | Link_missing_href 115 (** A "link" element must have an "href" or "imagesrcset" attribute. *) 116 | Link_as_requires_preload 117 (** A "link" element with an "as" attribute must have rel="preload" or "modulepreload". *) 118 | Link_imagesrcset_requires_as_image 119 (** A "link" element with "imagesrcset" must have as="image". *) 120 | Img_ismap_needs_a_href 121 (** The "img" element with "ismap" must have an "a" ancestor with "href". *) 122 | Sizes_without_srcset 123 (** The "sizes" attribute must only be specified if "srcset" is also specified. *) 124 | Imagesizes_without_imagesrcset 125 (** The "imagesizes" attribute must only be specified if "imagesrcset" is also specified. *) 126 | Srcset_w_without_sizes 127 (** When the "srcset" attribute has width descriptors, "sizes" must also be specified. *) 128 | Source_missing_srcset 129 (** Element "source" is missing required attribute "srcset". *) 130 | Source_needs_media_or_type 131 (** A "source" element with following source/img[srcset] must have media/type. *) 132 | Picture_missing_img 133 (** Element "picture" is missing required child element "img". *) 134 | Map_id_name_mismatch 135 (** The "id" attribute on a "map" element must have the same value as the "name" attribute. *) 136 | List_attr_requires_datalist 137 (** The "list" attribute of "input" must refer to a "datalist" element. *) 138 | Input_list_not_allowed 139 (** Attribute "list" is only allowed on certain input types. *) 140 | Label_too_many_labelable 141 (** The "label" element may contain at most one labelable descendant. *) 142 | Label_for_id_mismatch 143 (** Any "input" descendant of a "label" with "for" must have matching ID. *) 144 | Role_on_label_ancestor 145 (** The "role" attribute must not be on label ancestor of labelable element. *) 146 | Role_on_label_for 147 (** The "role" attribute must not be on label associated via for. *) 148 | Aria_label_on_label_for 149 (** The "aria-label" attribute must not be on label associated via for. *) 150 | Input_value_constraint of { constraint_type: string } 151 (** The value of the "value" attribute must be... *) 152 | Summary_missing_role 153 (** Element "summary" is missing required attribute "role". *) 154 | Summary_missing_attrs 155 (** Element "summary" is missing one or more of [aria-checked, aria-level, role]. *) 156 | Summary_role_not_allowed 157 (** The "role" attribute must not be used on any "summary" for its parent "details". *) 158 | Autocomplete_webauthn_on_select 159 (** The value of "autocomplete" for "select" must not contain "webauthn". *) 160 | Commandfor_invalid_target 161 (** The value of "commandfor" must be the ID of an element in the same tree. *) 162 163 (* ===== Parse Errors ===== *) 164 | Forbidden_codepoint of { codepoint: int } 165 (** Forbidden code point U+XXXX. *) 166 | Char_ref_control of { codepoint: int } 167 (** Character reference expands to a control character (U+XXXX). *) 168 | Char_ref_non_char of { codepoint: int; astral: bool } 169 (** Character reference expands to a [astral] non-character (U+XXXX). *) 170 | Char_ref_unassigned 171 (** Character reference expands to a permanently unassigned code point. *) 172 | Char_ref_zero 173 (** Character reference expands to zero. *) 174 | Char_ref_out_of_range 175 (** Character reference outside the permissible Unicode range. *) 176 | Numeric_char_ref_carriage_return 177 (** A numeric character reference expanded to carriage return. *) 178 | End_of_file_with_open_elements 179 (** End of file seen and there were open elements. *) 180 | No_element_in_scope of { tag: string } 181 (** No "X" element in scope but a "X" end tag seen. *) 182 | End_tag_implied_open_elements of { tag: string } 183 (** End tag "X" implied, but there were open elements. *) 184 | Start_tag_in_table of { tag: string } 185 (** Start tag "X" seen in "table". *) 186 | Bad_start_tag_in of { tag: string; context: string } 187 (** Bad start tag in "X" in "noscript" in "head". *) 188 189 (* ===== Table Errors ===== *) 190 | Table_row_no_cells of { row: int } 191 (** Row N of an implicit row group has no cells beginning on it. *) 192 | Table_cell_overlap 193 (** Table cell is overlapped by later table cell. *) 194 | Table_cell_spans_rowgroup 195 (** Table cell spans past the end of its row group. *) 196 | Table_column_no_cells of { column: int; element: string } 197 (** Table column N established by element "X" has no cells beginning in it. *) 198 199 (* ===== Language/Internationalization ===== *) 200 | Missing_lang_attr 201 (** Consider adding a "lang" attribute to the "html" start tag. *) 202 | Wrong_lang of { detected: string; declared: string; suggested: string } 203 (** This document appears to be written in X but has lang="Y". Consider using "Z". *) 204 | Missing_dir_rtl of { language: string } 205 (** This document appears to be written in X. Consider adding dir="rtl". *) 206 | Wrong_dir of { language: string; declared: string } 207 (** This document appears to be written in X but has dir="Y". Consider dir="rtl". *) 208 | Xml_lang_without_lang 209 (** When xml:lang is specified, lang must also be present with the same value. *) 210 | Xml_lang_lang_mismatch 211 (** xml:lang and lang must have the same value. *) 212 213 (* ===== Unicode Normalization ===== *) 214 | Not_nfc of { replacement: string } 215 (** Text run is not in Unicode Normalization Form C. *) 216 217 (* ===== Multiple h1 ===== *) 218 | Multiple_h1 219 (** Consider using only one "h1" element per document. *) 220 | Multiple_autofocus 221 (** There must not be two elements with autofocus in the same scoping root. *) 222 223 (* ===== Import Maps ===== *) 224 | Importmap_invalid_json 225 (** A "script" type="importmap" must have valid JSON content. *) 226 | Importmap_invalid_root 227 (** A "script" type="importmap" must contain a JSON object with only imports/scopes/integrity. *) 228 | Importmap_imports_not_object 229 (** The value of "imports" property must be a JSON object. *) 230 | Importmap_empty_key 231 (** Specifier map must only contain non-empty keys. *) 232 | Importmap_non_string_value 233 (** Specifier map must only contain string values. *) 234 | Importmap_key_trailing_slash 235 (** Specifier map values must end with "/" when key ends with "/". *) 236 | Importmap_scopes_not_object 237 (** The value of "scopes" property must be a JSON object with valid URL keys. *) 238 | Importmap_scopes_values_not_object 239 (** The value of "scopes" property values must also be JSON objects. *) 240 | Importmap_scopes_invalid_url 241 (** The "scopes" property keys must be valid URL strings. *) 242 | Importmap_scopes_value_invalid_url 243 (** The specifier map within "scopes" must only contain valid URL values. *) 244 245 (* ===== Style Element ===== *) 246 | Style_type_invalid 247 (** The only allowed value for "type" on "style" is "text/css". *) 248 249 (* ===== Headingoffset ===== *) 250 | Headingoffset_invalid 251 (** The value of "headingoffset" must be a number between "0" and "8". *) 252 253 (* ===== Media Attribute ===== *) 254 | Media_empty 255 (** Value of "media" attribute here must not be empty. *) 256 | Media_all 257 (** Value of "media" attribute here must not be "all". *) 258 259 (* ===== SVG/MathML specific ===== *) 260 | Svg_deprecated_attr of { attr: string; element: string } 261 (** SVG deprecated attribute *) 262 | Missing_required_svg_attr of { element: string; attr: string } 263 (** Element "X" is missing required attribute "Y". (SVG) *) 264 265 (* ===== Generic/Fallback ===== *) 266 | Generic of { message: string } 267 (** For messages that don't fit any specific pattern *) 268 269(** Get the severity level for an error code *) 270let severity = function 271 | Missing_lang_attr -> Info 272 | Multiple_h1 -> Info 273 | Wrong_lang _ -> Warning 274 | Missing_dir_rtl _ -> Warning 275 | Wrong_dir _ -> Warning 276 | Unnecessary_role _ -> Warning 277 | Aria_should_not_be_used _ -> Warning 278 | Unknown_element _ -> Warning 279 | Not_nfc _ -> Warning 280 | _ -> Error 281 282(** Get a short code string for categorization *) 283let code_string = function 284 | Attr_not_allowed_on_element _ -> "disallowed-attribute" 285 | Attr_not_allowed_here _ -> "disallowed-attribute" 286 | Attr_not_allowed_when _ -> "disallowed-attribute" 287 | Missing_required_attr _ -> "missing-required-attribute" 288 | Missing_required_attr_one_of _ -> "missing-required-attribute" 289 | Bad_attr_value _ -> "bad-attribute-value" 290 | Bad_attr_value_generic _ -> "bad-attribute-value" 291 | Duplicate_id _ -> "duplicate-id" 292 | Data_attr_invalid_name _ -> "bad-attribute-name" 293 | Data_attr_uppercase -> "bad-attribute-name" 294 | Obsolete_element _ -> "obsolete-element" 295 | Obsolete_attr _ -> "obsolete-attribute" 296 | Obsolete_global_attr _ -> "obsolete-attribute" 297 | Element_not_allowed_as_child _ -> "disallowed-child" 298 | Unknown_element _ -> "unknown-element" 299 | Element_must_not_be_descendant _ -> "prohibited-ancestor" 300 | Missing_required_child _ -> "missing-required-child" 301 | Missing_required_child_one_of _ -> "missing-required-child" 302 | Missing_required_child_generic _ -> "missing-required-child" 303 | Element_must_not_be_empty _ -> "empty-element" 304 | Stray_start_tag _ -> "stray-tag" 305 | Stray_end_tag _ -> "stray-tag" 306 | End_tag_for_void_element _ -> "end-tag-void" 307 | Self_closing_non_void -> "self-closing-non-void" 308 | Text_not_allowed _ -> "text-not-allowed" 309 | Div_child_of_dl_bad_role -> "invalid-role" 310 | Li_bad_role_in_menu -> "invalid-role" 311 | Li_bad_role_in_tablist -> "invalid-role" 312 | Li_bad_role_in_list -> "invalid-role" 313 | Unnecessary_role _ -> "unnecessary-role" 314 | Bad_role _ -> "bad-role" 315 | Aria_must_not_be_specified _ -> "aria-not-allowed" 316 | Aria_must_not_be_used _ -> "aria-not-allowed" 317 | Aria_should_not_be_used _ -> "aria-not-allowed" 318 | Aria_hidden_on_body -> "aria-not-allowed" 319 | Img_empty_alt_with_role -> "img-alt-role" 320 | Checkbox_button_needs_aria_pressed -> "missing-aria-pressed" 321 | Tab_without_tabpanel -> "tab-without-tabpanel" 322 | Multiple_main_visible -> "multiple-main" 323 | Discarding_unrecognized_role _ -> "unrecognized-role" 324 | Img_missing_alt -> "missing-alt" 325 | Img_missing_src_or_srcset -> "missing-src" 326 | Option_empty_without_label -> "empty-option" 327 | Bdo_missing_dir -> "missing-dir" 328 | Bdo_dir_auto -> "bdo-dir-auto" 329 | Base_missing_href_or_target -> "missing-required-attribute" 330 | Base_after_link_script -> "base-position" 331 | Link_missing_href -> "missing-href" 332 | Link_as_requires_preload -> "link-as-preload" 333 | Link_imagesrcset_requires_as_image -> "link-imagesrcset" 334 | Img_ismap_needs_a_href -> "ismap-needs-href" 335 | Sizes_without_srcset -> "sizes-without-srcset" 336 | Imagesizes_without_imagesrcset -> "imagesizes-without-srcset" 337 | Srcset_w_without_sizes -> "srcset-needs-sizes" 338 | Source_missing_srcset -> "missing-srcset" 339 | Source_needs_media_or_type -> "source-needs-media" 340 | Picture_missing_img -> "picture-missing-img" 341 | Map_id_name_mismatch -> "map-id-name" 342 | List_attr_requires_datalist -> "list-datalist" 343 | Input_list_not_allowed -> "list-not-allowed" 344 | Label_too_many_labelable -> "label-multiple" 345 | Label_for_id_mismatch -> "label-for-mismatch" 346 | Role_on_label_ancestor -> "role-on-label" 347 | Role_on_label_for -> "role-on-label" 348 | Aria_label_on_label_for -> "aria-label-on-label" 349 | Input_value_constraint _ -> "input-value" 350 | Summary_missing_role -> "summary-role" 351 | Summary_missing_attrs -> "summary-attrs" 352 | Summary_role_not_allowed -> "summary-role" 353 | Autocomplete_webauthn_on_select -> "autocomplete" 354 | Commandfor_invalid_target -> "commandfor" 355 | Forbidden_codepoint _ -> "forbidden-codepoint" 356 | Char_ref_control _ -> "char-ref-control" 357 | Char_ref_non_char _ -> "char-ref-non-char" 358 | Char_ref_unassigned -> "char-ref-unassigned" 359 | Char_ref_zero -> "char-ref-zero" 360 | Char_ref_out_of_range -> "char-ref-range" 361 | Numeric_char_ref_carriage_return -> "numeric-char-ref" 362 | End_of_file_with_open_elements -> "eof-open-elements" 363 | No_element_in_scope _ -> "no-element-in-scope" 364 | End_tag_implied_open_elements _ -> "end-tag-implied" 365 | Start_tag_in_table _ -> "start-tag-in-table" 366 | Bad_start_tag_in _ -> "bad-start-tag" 367 | Table_row_no_cells _ -> "table-row" 368 | Table_cell_overlap -> "table-overlap" 369 | Table_cell_spans_rowgroup -> "table-span" 370 | Table_column_no_cells _ -> "table-column" 371 | Missing_lang_attr -> "missing-lang" 372 | Wrong_lang _ -> "wrong-lang" 373 | Missing_dir_rtl _ -> "missing-dir" 374 | Wrong_dir _ -> "wrong-dir" 375 | Xml_lang_without_lang -> "xml-lang" 376 | Xml_lang_lang_mismatch -> "xml-lang-mismatch" 377 | Not_nfc _ -> "unicode-normalization" 378 | Multiple_h1 -> "multiple-h1" 379 | Multiple_autofocus -> "multiple-autofocus" 380 | Importmap_invalid_json -> "importmap" 381 | Importmap_invalid_root -> "importmap" 382 | Importmap_imports_not_object -> "importmap" 383 | Importmap_empty_key -> "importmap" 384 | Importmap_non_string_value -> "importmap" 385 | Importmap_key_trailing_slash -> "importmap" 386 | Importmap_scopes_not_object -> "importmap" 387 | Importmap_scopes_values_not_object -> "importmap" 388 | Importmap_scopes_invalid_url -> "importmap" 389 | Importmap_scopes_value_invalid_url -> "importmap" 390 | Style_type_invalid -> "style-type" 391 | Headingoffset_invalid -> "headingoffset" 392 | Media_empty -> "media-empty" 393 | Media_all -> "media-all" 394 | Svg_deprecated_attr _ -> "svg-deprecated" 395 | Missing_required_svg_attr _ -> "missing-required-attribute" 396 | Generic _ -> "generic" 397 398(** Format using curly quotes (Unicode) *) 399let q s = "\xe2\x80\x9c" ^ s ^ "\xe2\x80\x9d" 400 401(** Convert error code to exact Nu validator message string *) 402let to_message = function 403 | Attr_not_allowed_on_element { attr; element } -> 404 Printf.sprintf "Attribute %s not allowed on element %s at this point." 405 (q attr) (q element) 406 | Attr_not_allowed_here { attr } -> 407 Printf.sprintf "Attribute %s not allowed here." (q attr) 408 | Attr_not_allowed_when { attr; element = _; condition } -> 409 Printf.sprintf "The %s attribute must not be used on any element which has %s." (q attr) condition 410 | Missing_required_attr { element; attr } -> 411 Printf.sprintf "Element %s is missing required attribute %s." 412 (q element) (q attr) 413 | Missing_required_attr_one_of { element; attrs } -> 414 let attrs_str = String.concat ", " attrs in 415 Printf.sprintf "Element %s is missing one or more of the following attributes: [%s]." 416 (q element) attrs_str 417 | Bad_attr_value { element; attr; value; reason } -> 418 Printf.sprintf "Bad value %s for attribute %s on element %s: %s" 419 (q value) (q attr) (q element) reason 420 | Bad_attr_value_generic { message } -> message 421 | Duplicate_id { id } -> 422 Printf.sprintf "Duplicate ID %s." (q id) 423 | Data_attr_invalid_name { reason } -> 424 Printf.sprintf "%s attribute names %s." (q "data-*") reason 425 | Data_attr_uppercase -> 426 Printf.sprintf "%s attributes must not have characters from the range %s\xe2\x80\xa6%s in the name." 427 (q "data-*") (q "A") (q "Z") 428 429 | Obsolete_element { element; suggestion } -> 430 if suggestion = "" then 431 Printf.sprintf "The %s element is obsolete." (q element) 432 else 433 Printf.sprintf "The %s element is obsolete. %s" (q element) suggestion 434 | Obsolete_attr { element; attr; suggestion } -> 435 let base = Printf.sprintf "The %s attribute on the %s element is obsolete." 436 (q attr) (q element) in 437 (match suggestion with Some s -> base ^ " " ^ s | None -> base) 438 | Obsolete_global_attr { attr; suggestion } -> 439 Printf.sprintf "The %s attribute is obsolete. %s" (q attr) suggestion 440 | Element_not_allowed_as_child { child; parent } -> 441 Printf.sprintf "Element %s not allowed as child of element %s in this context. (Suppressing further errors from this subtree.)" 442 (q child) (q parent) 443 | Unknown_element { name } -> 444 Printf.sprintf "Unknown element %s." (q name) 445 | Element_must_not_be_descendant { element; attr; ancestor } -> 446 (match attr with 447 | Some a -> 448 Printf.sprintf "The element %s with the attribute %s must not appear as a descendant of the %s element." 449 (q element) (q a) (q ancestor) 450 | None -> 451 Printf.sprintf "The element %s must not appear as a descendant of the %s element." 452 (q element) (q ancestor)) 453 | Missing_required_child { parent; child } -> 454 Printf.sprintf "Element %s is missing required child element %s." 455 (q parent) (q child) 456 | Missing_required_child_one_of { parent; children } -> 457 let children_str = String.concat ", " children in 458 Printf.sprintf "Element %s is missing one or more of the following child elements: [%s]." 459 (q parent) children_str 460 | Missing_required_child_generic { parent } -> 461 Printf.sprintf "Element %s is missing a required child element." (q parent) 462 | Element_must_not_be_empty { element } -> 463 Printf.sprintf "Element %s must not be empty." (q element) 464 | Stray_start_tag { tag } -> 465 Printf.sprintf "Stray start tag %s." (q tag) 466 | Stray_end_tag { tag } -> 467 Printf.sprintf "Stray end tag %s." (q tag) 468 | End_tag_for_void_element { tag } -> 469 Printf.sprintf "End tag %s." (q tag) 470 | Self_closing_non_void -> 471 Printf.sprintf "Self-closing syntax (%s) used on a non-void HTML element. Ignoring the slash and treating as a start tag." 472 (q "/>") 473 | Text_not_allowed { parent } -> 474 Printf.sprintf "Text not allowed in element %s in this context." (q parent) 475 476 | Div_child_of_dl_bad_role -> 477 Printf.sprintf "A %s child of a %s element must not have any %s value other than %s or %s." 478 (q "div") (q "dl") (q "role") (q "presentation") (q "none") 479 | Li_bad_role_in_menu -> 480 Printf.sprintf "An %s element that is a descendant of a %s element or %s element must not have any %s value other than %s, %s, %s, %s, or %s." 481 (q "li") (q "role=menu") (q "role=menubar") (q "role") 482 (q "group") (q "menuitem") (q "menuitemcheckbox") (q "menuitemradio") (q "separator") 483 | Li_bad_role_in_tablist -> 484 Printf.sprintf "An %s element that is a descendant of a %s element must not have any %s value other than %s." 485 (q "li") (q "role=tablist") (q "role") (q "tab") 486 | Li_bad_role_in_list -> 487 Printf.sprintf "An %s element that is a descendant of a %s, %s, or %s element with no explicit %s value, or a descendant of a %s element, must not have any %s value other than %s." 488 (q "li") (q "ul") (q "ol") (q "menu") (q "role") (q "role=list") (q "role") (q "listitem") 489 490 | Unnecessary_role { role; element = _; reason } -> 491 Printf.sprintf "The %s role is unnecessary %s." 492 (q role) reason 493 | Bad_role { element; role } -> 494 Printf.sprintf "Bad value %s for attribute %s on element %s." 495 (q role) (q "role") (q element) 496 | Aria_must_not_be_specified { attr; element; condition } -> 497 Printf.sprintf "The %s attribute must not be specified on any %s element unless %s." 498 (q attr) (q element) condition 499 | Aria_must_not_be_used { attr; element; condition } -> 500 Printf.sprintf "The %s attribute must not be used on an %s element which has %s." 501 (q attr) (q element) condition 502 | Aria_should_not_be_used { attr; role } -> 503 Printf.sprintf "The %s attribute should not be used on any element which has %s." 504 (q attr) (q ("role=" ^ role)) 505 | Aria_hidden_on_body -> 506 Printf.sprintf "%s must not be used on the %s element." 507 (q "aria-hidden=true") (q "body") 508 | Img_empty_alt_with_role -> 509 Printf.sprintf "An %s element which has an %s attribute whose value is the empty string must not have a %s attribute." 510 (q "img") (q "alt") (q "role") 511 | Checkbox_button_needs_aria_pressed -> 512 Printf.sprintf "An %s element with a %s attribute whose value is %s and with a %s attribute whose value is %s must have an %s attribute." 513 (q "input") (q "type") (q "checkbox") (q "role") (q "button") (q "aria-pressed") 514 | Tab_without_tabpanel -> 515 Printf.sprintf "Every active %s element must have a corresponding %s element." 516 (q "role=tab") (q "role=tabpanel") 517 | Multiple_main_visible -> 518 Printf.sprintf "A document should not include more than one visible element with %s." 519 (q "role=main") 520 | Discarding_unrecognized_role { token } -> 521 Printf.sprintf "Discarding unrecognized token %s from value of attribute %s. Browsers ignore any token that is not a defined ARIA non-abstract role." 522 (q token) (q "role") 523 524 | Img_missing_alt -> 525 Printf.sprintf "An %s element must have an %s attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images." 526 (q "img") (q "alt") 527 | Img_missing_src_or_srcset -> 528 Printf.sprintf "Element %s is missing one or more of the following attributes: [src, srcset]." 529 (q "img") 530 | Option_empty_without_label -> 531 Printf.sprintf "Element %s without attribute %s must not be empty." 532 (q "option") (q "label") 533 | Bdo_missing_dir -> 534 Printf.sprintf "Element %s must have attribute %s." (q "bdo") (q "dir") 535 | Bdo_dir_auto -> 536 Printf.sprintf "The value of %s attribute for the %s element must not be %s." 537 (q "dir") (q "bdo") (q "auto") 538 | Base_missing_href_or_target -> 539 Printf.sprintf "Element %s is missing one or more of the following attributes: [href, target]." 540 (q "base") 541 | Base_after_link_script -> 542 Printf.sprintf "The %s element must come before any %s or %s elements in the document." 543 (q "base") (q "link") (q "script") 544 | Link_missing_href -> 545 Printf.sprintf "A %s element must have an %s or %s attribute, or both." 546 (q "link") (q "href") (q "imagesrcset") 547 | Link_as_requires_preload -> 548 Printf.sprintf "A %s element with an %s attribute must have a %s attribute that contains the value %s or the value %s." 549 (q "link") (q "as") (q "rel") (q "preload") (q "modulepreload") 550 | Link_imagesrcset_requires_as_image -> 551 Printf.sprintf "A %s element with an %s attribute must have an %s attribute with value %s." 552 (q "link") (q "imagesrcset") (q "as") (q "image") 553 | Img_ismap_needs_a_href -> 554 Printf.sprintf "The %s element with the %s attribute set must have an %s ancestor with the %s attribute." 555 (q "img") (q "ismap") (q "a") (q "href") 556 | Sizes_without_srcset -> 557 Printf.sprintf "The %s attribute must only be specified if the %s attribute is also specified." 558 (q "sizes") (q "srcset") 559 | Imagesizes_without_imagesrcset -> 560 Printf.sprintf "The %s attribute must only be specified if the %s attribute is also specified." 561 (q "imagesizes") (q "imagesrcset") 562 | Srcset_w_without_sizes -> 563 Printf.sprintf "When the %s attribute has any image candidate string with a width descriptor, the %s attribute must also be specified." 564 (q "srcset") (q "sizes") 565 | Source_missing_srcset -> 566 Printf.sprintf "Element %s is missing required attribute %s." 567 (q "source") (q "srcset") 568 | Source_needs_media_or_type -> 569 Printf.sprintf "A %s element that has a following sibling %s element or %s element with a %s attribute must have a %s attribute and/or %s attribute." 570 (q "source") (q "source") (q "img") (q "srcset") (q "media") (q "type") 571 | Picture_missing_img -> 572 Printf.sprintf "Element %s is missing required child element %s." 573 (q "picture") (q "img") 574 | Map_id_name_mismatch -> 575 Printf.sprintf "The %s attribute on a %s element must have an the same value as the %s attribute." 576 (q "id") (q "map") (q "name") 577 | List_attr_requires_datalist -> 578 Printf.sprintf "The %s attribute of the %s element must refer to a %s element." 579 (q "list") (q "input") (q "datalist") 580 | Input_list_not_allowed -> 581 Printf.sprintf "Attribute %s is only allowed when the input type is %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, or %s." 582 (q "list") (q "color") (q "date") (q "datetime-local") (q "email") (q "month") 583 (q "number") (q "range") (q "search") (q "tel") (q "text") (q "time") (q "url") (q "week") 584 | Label_too_many_labelable -> 585 Printf.sprintf "The %s element may contain at most one %s, %s, %s, %s, %s, %s, or %s descendant." 586 (q "label") (q "button") (q "input") (q "meter") (q "output") (q "progress") (q "select") (q "textarea") 587 | Label_for_id_mismatch -> 588 Printf.sprintf "Any %s descendant of a %s element with a %s attribute must have an ID value that matches that %s attribute." 589 (q "input") (q "label") (q "for") (q "for") 590 | Role_on_label_ancestor -> 591 Printf.sprintf "The %s attribute must not be used on any %s element that is an ancestor of a labelable element." 592 (q "role") (q "label") 593 | Role_on_label_for -> 594 Printf.sprintf "The %s attribute must not be used on any %s element that is associated with a labelable element." 595 (q "role") (q "label") 596 | Aria_label_on_label_for -> 597 Printf.sprintf "The %s attribute must not be used on any %s element that is associated with a labelable element." 598 (q "aria-label") (q "label") 599 | Input_value_constraint { constraint_type } -> constraint_type 600 | Summary_missing_role -> 601 Printf.sprintf "Element %s is missing required attribute %s." 602 (q "summary") (q "role") 603 | Summary_missing_attrs -> 604 Printf.sprintf "Element %s is missing one or more of the following attributes: [aria-checked, aria-level, role]." 605 (q "summary") 606 | Summary_role_not_allowed -> 607 Printf.sprintf "The %s attribute must not be used on any %s element that is a summary for its parent %s element." 608 (q "role") (q "summary") (q "details") 609 | Autocomplete_webauthn_on_select -> 610 Printf.sprintf "The value of the %s attribute for the %s element must not contain %s." 611 (q "autocomplete") (q "select") (q "webauthn") 612 | Commandfor_invalid_target -> 613 Printf.sprintf "The value of the %s attribute of the %s element must be the ID of an element in the same tree as the %s with the %s attribute." 614 (q "commandfor") (q "button") (q "button") (q "commandfor") 615 616 | Forbidden_codepoint { codepoint } -> 617 Printf.sprintf "Forbidden code point U+%04x." codepoint 618 | Char_ref_control { codepoint } -> 619 Printf.sprintf "Character reference expands to a control character (U+%04x)." codepoint 620 | Char_ref_non_char { codepoint; astral } -> 621 if astral then 622 Printf.sprintf "Character reference expands to an astral non-character (U+%05x)." codepoint 623 else 624 Printf.sprintf "Character reference expands to a non-character (U+%04x)." codepoint 625 | Char_ref_unassigned -> 626 "Character reference expands to a permanently unassigned code point." 627 | Char_ref_zero -> 628 "Character reference expands to zero." 629 | Char_ref_out_of_range -> 630 "Character reference outside the permissible Unicode range." 631 | Numeric_char_ref_carriage_return -> 632 "A numeric character reference expanded to carriage return." 633 | End_of_file_with_open_elements -> 634 "End of file seen and there were open elements." 635 | No_element_in_scope { tag } -> 636 Printf.sprintf "No %s element in scope but a %s end tag seen." 637 (q tag) (q tag) 638 | End_tag_implied_open_elements { tag } -> 639 Printf.sprintf "End tag %s implied, but there were open elements." 640 (q tag) 641 | Start_tag_in_table { tag } -> 642 Printf.sprintf "Start tag %s seen in %s." (q tag) (q "table") 643 | Bad_start_tag_in { tag; context = _ } -> 644 Printf.sprintf "Bad start tag in %s in %s in %s." 645 (q tag) (q "noscript") (q "head") 646 647 | Table_row_no_cells { row } -> 648 Printf.sprintf "Row %d of an implicit row group has no cells beginning on it." row 649 | Table_cell_overlap -> 650 "Table cell is overlapped by later table cell." 651 | Table_cell_spans_rowgroup -> 652 Printf.sprintf "Table cell spans past the end of its row group established by a %s element; clipped to the end of the row group." 653 (q "tbody") 654 | Table_column_no_cells { column; element } -> 655 Printf.sprintf "Table column %d established by element %s has no cells beginning in it." 656 column (q element) 657 658 | Missing_lang_attr -> 659 Printf.sprintf "Consider adding a %s attribute to the %s start tag to declare the language of this document." 660 (q "lang") (q "html") 661 | Wrong_lang { detected; declared; suggested } -> 662 Printf.sprintf "This document appears to be written in %s but the %s start tag has %s. Consider using %s (or variant) instead." 663 detected (q "html") (q ("lang=\"" ^ declared ^ "\"")) (q ("lang=\"" ^ suggested ^ "\"")) 664 | Missing_dir_rtl { language } -> 665 Printf.sprintf "This document appears to be written in %s. Consider adding %s to the %s start tag." 666 language (q "dir=\"rtl\"") (q "html") 667 | Wrong_dir { language; declared } -> 668 Printf.sprintf "This document appears to be written in %s but the %s start tag has %s. Consider using %s instead." 669 language (q "html") (q ("dir=\"" ^ declared ^ "\"")) (q "dir=\"rtl\"") 670 | Xml_lang_without_lang -> 671 Printf.sprintf "When the attribute %s in no namespace is specified, the element must also have the attribute %s present with the same value." 672 (q "xml:lang") (q "lang") 673 | Xml_lang_lang_mismatch -> 674 Printf.sprintf "The %s and %s attributes must have the same value." 675 (q "xml:lang") (q "lang") 676 677 | Not_nfc { replacement } -> 678 Printf.sprintf "Text run is not in Unicode Normalization Form C. Should instead be %s. (Copy and paste that into your source document to replace the un-normalized text.)" 679 (q replacement) 680 681 | Multiple_h1 -> 682 Printf.sprintf "Consider using only one %s element per document (or, if using %s elements multiple times is required, consider using the %s attribute to indicate that these %s elements are not all top-level headings)." 683 (q "h1") (q "h1") (q "headingoffset") (q "h1") 684 | Multiple_autofocus -> 685 Printf.sprintf "There must not be two elements with the same %s that both have the %s attribute specified." 686 (q "nearest ancestor autofocus scoping root element") (q "autofocus") 687 688 | Importmap_invalid_json -> 689 Printf.sprintf "A script %s with a %s attribute whose value is %s must have valid JSON content." 690 (q "script") (q "type") (q "importmap") 691 | Importmap_invalid_root -> 692 Printf.sprintf "A %s element with a %s attribute whose value is %s must contain a JSON object with no properties other than %s, %s, and %s." 693 (q "script") (q "type") (q "importmap") (q "imports") (q "scopes") (q "integrity") 694 | Importmap_imports_not_object -> 695 Printf.sprintf "The value of the %s property within the content of a %s element with a %s attribute whose value is %s must be a JSON object." 696 (q "imports") (q "script") (q "type") (q "importmap") 697 | Importmap_empty_key -> 698 Printf.sprintf "A specifier map defined in a %s property within the content of a %s element with a %s attribute whose value is %s must only contain non-empty keys." 699 (q "imports") (q "script") (q "type") (q "importmap") 700 | Importmap_non_string_value -> 701 Printf.sprintf "A specifier map defined in a %s property within the content of a %s element with a %s attribute whose value is %s must only contain string values." 702 (q "imports") (q "script") (q "type") (q "importmap") 703 | Importmap_key_trailing_slash -> 704 Printf.sprintf "A specifier map defined in a %s property within the content of a %s element with a %s attribute whose value is %s must have values that end with %s when its corresponding key ends with %s." 705 (q "imports") (q "script") (q "type") (q "importmap") (q "/") (q "/") 706 | Importmap_scopes_not_object -> 707 Printf.sprintf "The value of the %s property within the content of a %s element with a %s attribute whose value is %s must be a JSON object whose keys are valid URL strings." 708 (q "scopes") (q "script") (q "type") (q "importmap") 709 | Importmap_scopes_values_not_object -> 710 Printf.sprintf "The value of the %s property within the content of a %s element with a %s attribute whose value is %s must be a JSON object whose values are also JSON objects." 711 (q "scopes") (q "script") (q "type") (q "importmap") 712 | Importmap_scopes_invalid_url -> 713 Printf.sprintf "The value of the %s property within the content of a %s element with a %s attribute whose value is %s must be a JSON object whose keys are valid URL strings." 714 (q "scopes") (q "script") (q "type") (q "importmap") 715 | Importmap_scopes_value_invalid_url -> 716 Printf.sprintf "A specifier map defined in a %s property within the content of a %s element with a %s attribute whose value is %s must only contain valid URL values." 717 (q "scopes") (q "script") (q "type") (q "importmap") 718 719 | Style_type_invalid -> 720 Printf.sprintf "The only allowed value for the %s attribute for the %s element is %s (with no parameters). (But the attribute is not needed and should be omitted altogether.)" 721 (q "type") (q "style") (q "text/css") 722 723 | Headingoffset_invalid -> 724 Printf.sprintf "The value of the %s attribute must be a number between %s and %s." 725 (q "headingoffset") (q "0") (q "8") 726 727 | Media_empty -> 728 Printf.sprintf "Value of %s attribute here must not be empty." (q "media") 729 | Media_all -> 730 Printf.sprintf "Value of %s attribute here must not be %s." (q "media") (q "all") 731 732 | Svg_deprecated_attr { attr; element } -> 733 Printf.sprintf "Attribute %s not allowed on element %s at this point." 734 (q attr) (q element) 735 | Missing_required_svg_attr { element; attr } -> 736 Printf.sprintf "Element %s is missing required attribute %s." 737 (q element) (q attr) 738 739 | Generic { message } -> message