(** Typed error codes for HTML5 validation messages. *) type severity = Error | Warning | Info type attr_error = [ | `Not_allowed of [`Attr of string] * [`Elem of string] | `Not_allowed_here of [`Attr of string] | `Not_allowed_when of [`Attr of string] * [`Elem of string] * [`Condition of string] | `Missing of [`Elem of string] * [`Attr of string] | `Missing_one_of of [`Elem of string] * [`Attrs of string list] | `Bad_value of [`Elem of string] * [`Attr of string] * [`Value of string] * [`Reason of string] | `Bad_value_generic of [`Message of string] | `Duplicate_id of [`Id of string] | `Data_invalid_name of [`Reason of string] | `Data_uppercase ] type element_error = [ | `Obsolete of [`Elem of string] * [`Suggestion of string] | `Obsolete_attr of [`Elem of string] * [`Attr of string] * [`Suggestion of string option] | `Obsolete_global_attr of [`Attr of string] * [`Suggestion of string] | `Not_allowed_as_child of [`Child of string] * [`Parent of string] | `Unknown of [`Elem of string] | `Must_not_descend of [`Elem of string] * [`Attr of string option] * [`Ancestor of string] | `Missing_child of [`Parent of string] * [`Child of string] | `Missing_child_one_of of [`Parent of string] * [`Children of string list] | `Missing_child_generic of [`Parent of string] | `Must_not_be_empty of [`Elem of string] | `Text_not_allowed of [`Parent of string] ] type tag_error = [ | `Stray_start of [`Tag of string] | `Stray_end of [`Tag of string] | `End_for_void of [`Tag of string] | `Self_closing_non_void | `Not_in_scope of [`Tag of string] | `End_implied_open of [`Tag of string] | `Start_in_table of [`Tag of string] | `Bad_start_in of [`Tag of string] * [`Context of string] | `Eof_with_open ] type char_ref_error = [ | `Forbidden_codepoint of [`Codepoint of int] | `Control_char of [`Codepoint of int] | `Non_char of [`Codepoint of int] * [`Astral of bool] | `Unassigned | `Zero | `Out_of_range | `Carriage_return ] type aria_error = [ | `Unnecessary_role of [`Role of string] * [`Elem of string] * [`Reason of string] | `Bad_role of [`Elem of string] * [`Role of string] | `Must_not_specify of [`Attr of string] * [`Elem of string] * [`Condition of string] | `Must_not_use of [`Attr of string] * [`Elem of string] * [`Condition of string] | `Should_not_use of [`Attr of string] * [`Role of string] | `Hidden_on_body | `Unrecognized_role of [`Token of string] | `Tab_without_tabpanel | `Multiple_main | `Accessible_name_prohibited of [`Attr of string] * [`Elem of string] ] type li_role_error = [ | `Div_in_dl_bad_role | `Li_bad_role_in_menu | `Li_bad_role_in_tablist | `Li_bad_role_in_list ] type table_error = [ | `Row_no_cells of [`Row of int] | `Cell_overlap | `Cell_spans_rowgroup | `Column_no_cells of [`Column of int] * [`Elem of string] ] type i18n_error = [ | `Missing_lang | `Wrong_lang of [`Detected of string] * [`Declared of string] * [`Suggested of string] | `Missing_dir_rtl of [`Language of string] | `Wrong_dir of [`Language of string] * [`Declared of string] | `Xml_lang_without_lang | `Xml_lang_mismatch | `Not_nfc of [`Replacement of string] ] type importmap_error = [ | `Invalid_json | `Invalid_root | `Imports_not_object | `Empty_key | `Non_string_value | `Key_trailing_slash | `Scopes_not_object | `Scopes_values_not_object | `Scopes_invalid_url | `Scopes_value_invalid_url ] type img_error = [ | `Missing_alt | `Missing_src_or_srcset | `Empty_alt_with_role | `Ismap_needs_href ] type link_error = [ | `Missing_href | `As_requires_preload | `Imagesrcset_requires_as_image ] type label_error = [ | `Too_many_labelable | `For_id_mismatch | `Role_on_ancestor | `Role_on_for | `Aria_label_on_ancestor | `Aria_label_on_for ] type input_error = [ | `Checkbox_needs_aria_pressed | `Value_constraint of [`Constraint of string] | `List_not_allowed | `List_requires_datalist ] type srcset_error = [ | `Sizes_without_srcset | `Imagesizes_without_imagesrcset | `W_without_sizes | `Source_missing_srcset | `Source_needs_media_or_type | `Picture_missing_img ] type svg_error = [ | `Deprecated_attr of [`Attr of string] * [`Elem of string] | `Missing_attr of [`Elem of string] * [`Attr of string] ] type misc_error = [ | `Option_empty_without_label | `Bdo_missing_dir | `Bdo_dir_auto | `Base_missing_href_or_target | `Base_after_link_script | `Map_id_name_mismatch | `Summary_missing_role | `Summary_missing_attrs | `Summary_role_not_allowed | `Autocomplete_webauthn_on_select | `Commandfor_invalid_target | `Style_type_invalid | `Headingoffset_invalid | `Media_empty | `Media_all | `Multiple_h1 | `Multiple_autofocus ] type t = [ | `Attr of attr_error | `Element of element_error | `Tag of tag_error | `Char_ref of char_ref_error | `Aria of aria_error | `Li_role of li_role_error | `Table of table_error | `I18n of i18n_error | `Importmap of importmap_error | `Img of img_error | `Link of link_error | `Label of label_error | `Input of input_error | `Srcset of srcset_error | `Svg of svg_error | `Misc of misc_error | `Generic of string ] (** Get the severity level for an error code *) let severity : t -> severity = function (* Info level *) | `I18n `Missing_lang -> Info | `Misc `Multiple_h1 -> Info (* Warning level *) | `I18n (`Wrong_lang _) -> Warning | `I18n (`Missing_dir_rtl _) -> Warning | `I18n (`Wrong_dir _) -> Warning | `I18n (`Not_nfc _) -> Warning | `Aria (`Unnecessary_role _) -> Warning | `Aria (`Should_not_use _) -> Warning | `Element (`Unknown _) -> Warning (* Everything else is Error *) | _ -> Error (** Get a short code string for categorization *) let code_string : t -> string = function (* Attribute errors *) | `Attr (`Not_allowed _) -> "disallowed-attribute" | `Attr (`Not_allowed_here _) -> "disallowed-attribute" | `Attr (`Not_allowed_when _) -> "disallowed-attribute" | `Attr (`Missing _) -> "missing-required-attribute" | `Attr (`Missing_one_of _) -> "missing-required-attribute" | `Attr (`Bad_value _) -> "bad-attribute-value" | `Attr (`Bad_value_generic _) -> "bad-attribute-value" | `Attr (`Duplicate_id _) -> "duplicate-id" | `Attr (`Data_invalid_name _) -> "bad-attribute-name" | `Attr `Data_uppercase -> "bad-attribute-name" (* Element errors *) | `Element (`Obsolete _) -> "obsolete-element" | `Element (`Obsolete_attr _) -> "obsolete-attribute" | `Element (`Obsolete_global_attr _) -> "obsolete-attribute" | `Element (`Not_allowed_as_child _) -> "disallowed-child" | `Element (`Unknown _) -> "unknown-element" | `Element (`Must_not_descend _) -> "prohibited-ancestor" | `Element (`Missing_child _) -> "missing-required-child" | `Element (`Missing_child_one_of _) -> "missing-required-child" | `Element (`Missing_child_generic _) -> "missing-required-child" | `Element (`Must_not_be_empty _) -> "empty-element" | `Element (`Text_not_allowed _) -> "text-not-allowed" (* Tag errors *) | `Tag (`Stray_start _) -> "stray-tag" | `Tag (`Stray_end _) -> "stray-tag" | `Tag (`End_for_void _) -> "end-tag-void" | `Tag `Self_closing_non_void -> "self-closing-non-void" | `Tag (`Not_in_scope _) -> "no-element-in-scope" | `Tag (`End_implied_open _) -> "end-tag-implied" | `Tag (`Start_in_table _) -> "start-tag-in-table" | `Tag (`Bad_start_in _) -> "bad-start-tag" | `Tag `Eof_with_open -> "eof-open-elements" (* Character reference errors *) | `Char_ref (`Forbidden_codepoint _) -> "forbidden-codepoint" | `Char_ref (`Control_char _) -> "char-ref-control" | `Char_ref (`Non_char _) -> "char-ref-non-char" | `Char_ref `Unassigned -> "char-ref-unassigned" | `Char_ref `Zero -> "char-ref-zero" | `Char_ref `Out_of_range -> "char-ref-range" | `Char_ref `Carriage_return -> "numeric-char-ref" (* ARIA errors *) | `Aria (`Unnecessary_role _) -> "unnecessary-role" | `Aria (`Bad_role _) -> "bad-role" | `Aria (`Must_not_specify _) -> "aria-not-allowed" | `Aria (`Must_not_use _) -> "aria-not-allowed" | `Aria (`Should_not_use _) -> "aria-not-allowed" | `Aria `Hidden_on_body -> "aria-not-allowed" | `Aria (`Unrecognized_role _) -> "unrecognized-role" | `Aria `Tab_without_tabpanel -> "tab-without-tabpanel" | `Aria `Multiple_main -> "multiple-main" | `Aria (`Accessible_name_prohibited _) -> "aria-not-allowed" (* List item role errors *) | `Li_role `Div_in_dl_bad_role -> "invalid-role" | `Li_role `Li_bad_role_in_menu -> "invalid-role" | `Li_role `Li_bad_role_in_tablist -> "invalid-role" | `Li_role `Li_bad_role_in_list -> "invalid-role" (* Table errors *) | `Table (`Row_no_cells _) -> "table-row" | `Table `Cell_overlap -> "table-overlap" | `Table `Cell_spans_rowgroup -> "table-span" | `Table (`Column_no_cells _) -> "table-column" (* I18n errors *) | `I18n `Missing_lang -> "missing-lang" | `I18n (`Wrong_lang _) -> "wrong-lang" | `I18n (`Missing_dir_rtl _) -> "missing-dir" | `I18n (`Wrong_dir _) -> "wrong-dir" | `I18n `Xml_lang_without_lang -> "xml-lang" | `I18n `Xml_lang_mismatch -> "xml-lang-mismatch" | `I18n (`Not_nfc _) -> "unicode-normalization" (* Import map errors *) | `Importmap `Invalid_json -> "importmap" | `Importmap `Invalid_root -> "importmap" | `Importmap `Imports_not_object -> "importmap" | `Importmap `Empty_key -> "importmap" | `Importmap `Non_string_value -> "importmap" | `Importmap `Key_trailing_slash -> "importmap" | `Importmap `Scopes_not_object -> "importmap" | `Importmap `Scopes_values_not_object -> "importmap" | `Importmap `Scopes_invalid_url -> "importmap" | `Importmap `Scopes_value_invalid_url -> "importmap" (* Image errors *) | `Img `Missing_alt -> "missing-alt" | `Img `Missing_src_or_srcset -> "missing-src" | `Img `Empty_alt_with_role -> "img-alt-role" | `Img `Ismap_needs_href -> "ismap-needs-href" (* Link errors *) | `Link `Missing_href -> "missing-href" | `Link `As_requires_preload -> "link-as-preload" | `Link `Imagesrcset_requires_as_image -> "link-imagesrcset" (* Label errors *) | `Label `Too_many_labelable -> "label-multiple" | `Label `For_id_mismatch -> "label-for-mismatch" | `Label `Role_on_ancestor -> "role-on-label" | `Label `Role_on_for -> "role-on-label" | `Label `Aria_label_on_ancestor -> "aria-label-on-label" | `Label `Aria_label_on_for -> "aria-label-on-label" (* Input errors *) | `Input `Checkbox_needs_aria_pressed -> "missing-aria-pressed" | `Input (`Value_constraint _) -> "input-value" | `Input `List_not_allowed -> "list-not-allowed" | `Input `List_requires_datalist -> "list-datalist" (* Srcset errors *) | `Srcset `Sizes_without_srcset -> "sizes-without-srcset" | `Srcset `Imagesizes_without_imagesrcset -> "imagesizes-without-srcset" | `Srcset `W_without_sizes -> "srcset-needs-sizes" | `Srcset `Source_missing_srcset -> "missing-srcset" | `Srcset `Source_needs_media_or_type -> "source-needs-media" | `Srcset `Picture_missing_img -> "picture-missing-img" (* SVG errors *) | `Svg (`Deprecated_attr _) -> "svg-deprecated" | `Svg (`Missing_attr _) -> "missing-required-attribute" (* Misc errors *) | `Misc `Option_empty_without_label -> "empty-option" | `Misc `Bdo_missing_dir -> "missing-dir" | `Misc `Bdo_dir_auto -> "bdo-dir-auto" | `Misc `Base_missing_href_or_target -> "missing-required-attribute" | `Misc `Base_after_link_script -> "base-position" | `Misc `Map_id_name_mismatch -> "map-id-name" | `Misc `Summary_missing_role -> "summary-role" | `Misc `Summary_missing_attrs -> "summary-attrs" | `Misc `Summary_role_not_allowed -> "summary-role" | `Misc `Autocomplete_webauthn_on_select -> "autocomplete" | `Misc `Commandfor_invalid_target -> "commandfor" | `Misc `Style_type_invalid -> "style-type" | `Misc `Headingoffset_invalid -> "headingoffset" | `Misc `Media_empty -> "media-empty" | `Misc `Media_all -> "media-all" | `Misc `Multiple_h1 -> "multiple-h1" | `Misc `Multiple_autofocus -> "multiple-autofocus" (* Generic *) | `Generic _ -> "generic" (** Format using curly quotes (Unicode) *) let q s = "\xe2\x80\x9c" ^ s ^ "\xe2\x80\x9d" (** Convert error code to exact Nu validator message string *) let to_message : t -> string = function (* Attribute errors *) | `Attr (`Not_allowed (`Attr attr, `Elem element)) -> Printf.sprintf "Attribute %s not allowed on element %s at this point." (q attr) (q element) | `Attr (`Not_allowed_here (`Attr attr)) -> Printf.sprintf "Attribute %s not allowed here." (q attr) | `Attr (`Not_allowed_when (`Attr attr, `Elem _, `Condition condition)) -> Printf.sprintf "The %s attribute must not be used on any element which has %s." (q attr) condition | `Attr (`Missing (`Elem element, `Attr attr)) -> Printf.sprintf "Element %s is missing required attribute %s." (q element) (q attr) | `Attr (`Missing_one_of (`Elem element, `Attrs attrs)) -> let attrs_str = String.concat ", " attrs in Printf.sprintf "Element %s is missing one or more of the following attributes: [%s]." (q element) attrs_str | `Attr (`Bad_value (`Elem element, `Attr attr, `Value value, `Reason reason)) -> if reason = "" then Printf.sprintf "Bad value %s for attribute %s on element %s." (q value) (q attr) (q element) else Printf.sprintf "Bad value %s for attribute %s on element %s: %s" (q value) (q attr) (q element) reason | `Attr (`Bad_value_generic (`Message message)) -> message | `Attr (`Duplicate_id (`Id id)) -> Printf.sprintf "Duplicate ID %s." (q id) | `Attr (`Data_invalid_name (`Reason reason)) -> Printf.sprintf "%s attribute names %s." (q "data-*") reason | `Attr `Data_uppercase -> Printf.sprintf "%s attributes must not have characters from the range %s\xe2\x80\xa6%s in the name." (q "data-*") (q "A") (q "Z") (* Element errors *) | `Element (`Obsolete (`Elem element, `Suggestion suggestion)) -> if suggestion = "" then Printf.sprintf "The %s element is obsolete." (q element) else Printf.sprintf "The %s element is obsolete. %s" (q element) suggestion | `Element (`Obsolete_attr (`Elem element, `Attr attr, `Suggestion suggestion)) -> let base = Printf.sprintf "The %s attribute on the %s element is obsolete." (q attr) (q element) in (match suggestion with Some s -> base ^ " " ^ s | None -> base) | `Element (`Obsolete_global_attr (`Attr attr, `Suggestion suggestion)) -> Printf.sprintf "The %s attribute is obsolete. %s" (q attr) suggestion | `Element (`Not_allowed_as_child (`Child child, `Parent parent)) -> Printf.sprintf "Element %s not allowed as child of element %s in this context. (Suppressing further errors from this subtree.)" (q child) (q parent) | `Element (`Unknown (`Elem name)) -> Printf.sprintf "Unknown element %s." (q name) | `Element (`Must_not_descend (`Elem element, `Attr attr, `Ancestor ancestor)) -> (match attr with | Some a -> Printf.sprintf "The element %s with the attribute %s must not appear as a descendant of the %s element." (q element) (q a) (q ancestor) | None -> Printf.sprintf "The element %s must not appear as a descendant of the %s element." (q element) (q ancestor)) | `Element (`Missing_child (`Parent parent, `Child child)) -> Printf.sprintf "Element %s is missing required child element %s." (q parent) (q child) | `Element (`Missing_child_one_of (`Parent parent, `Children children)) -> let children_str = String.concat ", " children in Printf.sprintf "Element %s is missing one or more of the following child elements: [%s]." (q parent) children_str | `Element (`Missing_child_generic (`Parent parent)) -> Printf.sprintf "Element %s is missing a required child element." (q parent) | `Element (`Must_not_be_empty (`Elem element)) -> Printf.sprintf "Element %s must not be empty." (q element) | `Element (`Text_not_allowed (`Parent parent)) -> Printf.sprintf "Text not allowed in element %s in this context." (q parent) (* Tag errors *) | `Tag (`Stray_start (`Tag tag)) -> Printf.sprintf "Stray start tag %s." (q tag) | `Tag (`Stray_end (`Tag tag)) -> Printf.sprintf "Stray end tag %s." (q tag) | `Tag (`End_for_void (`Tag tag)) -> Printf.sprintf "End tag %s." (q tag) | `Tag `Self_closing_non_void -> Printf.sprintf "Self-closing syntax (%s) used on a non-void HTML element. Ignoring the slash and treating as a start tag." (q "/>") | `Tag (`Not_in_scope (`Tag tag)) -> Printf.sprintf "No %s element in scope but a %s end tag seen." (q tag) (q tag) | `Tag (`End_implied_open (`Tag tag)) -> Printf.sprintf "End tag %s implied, but there were open elements." (q tag) | `Tag (`Start_in_table (`Tag tag)) -> Printf.sprintf "Start tag %s seen in %s." (q tag) (q "table") | `Tag (`Bad_start_in (`Tag tag, `Context _)) -> Printf.sprintf "Bad start tag in %s in %s in %s." (q tag) (q "noscript") (q "head") | `Tag `Eof_with_open -> "End of file seen and there were open elements." (* Character reference errors *) | `Char_ref (`Forbidden_codepoint (`Codepoint codepoint)) -> Printf.sprintf "Forbidden code point U+%04x." codepoint | `Char_ref (`Control_char (`Codepoint codepoint)) -> Printf.sprintf "Character reference expands to a control character (U+%04x)." codepoint | `Char_ref (`Non_char (`Codepoint codepoint, `Astral astral)) -> if astral then Printf.sprintf "Character reference expands to an astral non-character (U+%05x)." codepoint else Printf.sprintf "Character reference expands to a non-character (U+%04x)." codepoint | `Char_ref `Unassigned -> "Character reference expands to a permanently unassigned code point." | `Char_ref `Zero -> "Character reference expands to zero." | `Char_ref `Out_of_range -> "Character reference outside the permissible Unicode range." | `Char_ref `Carriage_return -> "A numeric character reference expanded to carriage return." (* ARIA errors *) | `Aria (`Unnecessary_role (`Role role, `Elem _, `Reason reason)) -> Printf.sprintf "The %s role is unnecessary %s." (q role) reason | `Aria (`Bad_role (`Elem element, `Role role)) -> Printf.sprintf "Bad value %s for attribute %s on element %s." (q role) (q "role") (q element) | `Aria (`Must_not_specify (`Attr attr, `Elem element, `Condition condition)) -> Printf.sprintf "The %s attribute must not be specified on any %s element unless %s." (q attr) (q element) condition | `Aria (`Must_not_use (`Attr attr, `Elem element, `Condition condition)) -> Printf.sprintf "The %s attribute must not be used on an %s element which has %s." (q attr) (q element) condition | `Aria (`Should_not_use (`Attr attr, `Role role)) -> Printf.sprintf "The %s attribute should not be used on any element which has %s." (q attr) (q ("role=" ^ role)) | `Aria `Hidden_on_body -> Printf.sprintf "%s must not be used on the %s element." (q "aria-hidden=true") (q "body") | `Aria (`Unrecognized_role (`Token token)) -> Printf.sprintf "Discarding unrecognized token %s from value of attribute %s. Browsers ignore any token that is not a defined ARIA non-abstract role." (q token) (q "role") | `Aria `Tab_without_tabpanel -> Printf.sprintf "Every active %s element must have a corresponding %s element." (q "role=tab") (q "role=tabpanel") | `Aria `Multiple_main -> Printf.sprintf "A document should not include more than one visible element with %s." (q "role=main") | `Aria (`Accessible_name_prohibited (`Attr attr, `Elem element)) -> (* Roles that prohibit accessible names - defined by ARIA spec *) let prohibited_roles = [ "caption"; "code"; "deletion"; "emphasis"; "generic"; "insertion"; "paragraph"; "presentation"; "strong"; "subscript"; "superscript" ] in let roles_str = String.concat ", " (List.map q (List.rev (List.tl (List.rev prohibited_roles)))) ^ ", or " ^ q (List.hd (List.rev prohibited_roles)) in Printf.sprintf "The %s attribute must not be specified on any %s element unless the element has a %s value other than %s." (q attr) (q element) (q "role") roles_str (* List item role errors *) | `Li_role `Div_in_dl_bad_role -> Printf.sprintf "A %s child of a %s element must not have any %s value other than %s or %s." (q "div") (q "dl") (q "role") (q "presentation") (q "none") | `Li_role `Li_bad_role_in_menu -> 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." (q "li") (q "role=menu") (q "role=menubar") (q "role") (q "group") (q "menuitem") (q "menuitemcheckbox") (q "menuitemradio") (q "separator") | `Li_role `Li_bad_role_in_tablist -> Printf.sprintf "An %s element that is a descendant of a %s element must not have any %s value other than %s." (q "li") (q "role=tablist") (q "role") (q "tab") | `Li_role `Li_bad_role_in_list -> 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." (q "li") (q "ul") (q "ol") (q "menu") (q "role") (q "role=list") (q "role") (q "listitem") (* Table errors *) | `Table (`Row_no_cells (`Row row)) -> Printf.sprintf "Row %d of an implicit row group has no cells beginning on it." row | `Table `Cell_overlap -> "Table cell is overlapped by later table cell." | `Table `Cell_spans_rowgroup -> 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." (q "tbody") | `Table (`Column_no_cells (`Column column, `Elem element)) -> Printf.sprintf "Table column %d established by element %s has no cells beginning in it." column (q element) (* I18n errors *) | `I18n `Missing_lang -> Printf.sprintf "Consider adding a %s attribute to the %s start tag to declare the language of this document." (q "lang") (q "html") | `I18n (`Wrong_lang (`Detected detected, `Declared declared, `Suggested suggested)) -> Printf.sprintf "This document appears to be written in %s but the %s start tag has %s. Consider using %s (or variant) instead." detected (q "html") (q ("lang=\"" ^ declared ^ "\"")) (q ("lang=\"" ^ suggested ^ "\"")) | `I18n (`Missing_dir_rtl (`Language language)) -> Printf.sprintf "This document appears to be written in %s. Consider adding %s to the %s start tag." language (q "dir=\"rtl\"") (q "html") | `I18n (`Wrong_dir (`Language language, `Declared declared)) -> Printf.sprintf "This document appears to be written in %s but the %s start tag has %s. Consider using %s instead." language (q "html") (q ("dir=\"" ^ declared ^ "\"")) (q "dir=\"rtl\"") | `I18n `Xml_lang_without_lang -> Printf.sprintf "When the attribute %s in no namespace is specified, the element must also have the attribute %s present with the same value." (q "xml:lang") (q "lang") | `I18n `Xml_lang_mismatch -> Printf.sprintf "The %s and %s attributes must have the same value." (q "xml:lang") (q "lang") | `I18n (`Not_nfc (`Replacement replacement)) -> 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.)" (q replacement) (* Import map errors *) | `Importmap `Invalid_json -> Printf.sprintf "A script %s with a %s attribute whose value is %s must have valid JSON content." (q "script") (q "type") (q "importmap") | `Importmap `Invalid_root -> 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." (q "script") (q "type") (q "importmap") (q "imports") (q "scopes") (q "integrity") | `Importmap `Imports_not_object -> 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." (q "imports") (q "script") (q "type") (q "importmap") | `Importmap `Empty_key -> 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." (q "imports") (q "script") (q "type") (q "importmap") | `Importmap `Non_string_value -> 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." (q "imports") (q "script") (q "type") (q "importmap") | `Importmap `Key_trailing_slash -> 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." (q "imports") (q "script") (q "type") (q "importmap") (q "/") (q "/") | `Importmap `Scopes_not_object -> 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." (q "scopes") (q "script") (q "type") (q "importmap") | `Importmap `Scopes_values_not_object -> 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." (q "scopes") (q "script") (q "type") (q "importmap") | `Importmap `Scopes_invalid_url -> 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." (q "scopes") (q "script") (q "type") (q "importmap") | `Importmap `Scopes_value_invalid_url -> 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." (q "scopes") (q "script") (q "type") (q "importmap") (* Image errors *) | `Img `Missing_alt -> Printf.sprintf "An %s element must have an %s attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images." (q "img") (q "alt") | `Img `Missing_src_or_srcset -> Printf.sprintf "Element %s is missing one or more of the following attributes: [src, srcset]." (q "img") | `Img `Empty_alt_with_role -> Printf.sprintf "An %s element which has an %s attribute whose value is the empty string must not have a %s attribute." (q "img") (q "alt") (q "role") | `Img `Ismap_needs_href -> Printf.sprintf "The %s element with the %s attribute set must have an %s ancestor with the %s attribute." (q "img") (q "ismap") (q "a") (q "href") (* Link errors *) | `Link `Missing_href -> Printf.sprintf "A %s element must have an %s or %s attribute, or both." (q "link") (q "href") (q "imagesrcset") | `Link `As_requires_preload -> Printf.sprintf "A %s element with an %s attribute must have a %s attribute that contains the value %s or the value %s." (q "link") (q "as") (q "rel") (q "preload") (q "modulepreload") | `Link `Imagesrcset_requires_as_image -> Printf.sprintf "A %s element with an %s attribute must have an %s attribute with value %s." (q "link") (q "imagesrcset") (q "as") (q "image") (* Label errors *) | `Label `Too_many_labelable -> Printf.sprintf "The %s element may contain at most one %s, %s, %s, %s, %s, %s, or %s descendant." (q "label") (q "button") (q "input") (q "meter") (q "output") (q "progress") (q "select") (q "textarea") | `Label `For_id_mismatch -> Printf.sprintf "Any %s descendant of a %s element with a %s attribute must have an ID value that matches that %s attribute." (q "input") (q "label") (q "for") (q "for") | `Label `Role_on_ancestor -> Printf.sprintf "The %s attribute must not be used on any %s element that is an ancestor of a labelable element." (q "role") (q "label") | `Label `Role_on_for -> Printf.sprintf "The %s attribute must not be used on any %s element that is associated with a labelable element." (q "role") (q "label") | `Label `Aria_label_on_ancestor -> Printf.sprintf "The %s attribute must not be used on any %s element that is an ancestor of a labelable element." (q "aria-label") (q "label") | `Label `Aria_label_on_for -> Printf.sprintf "The %s attribute must not be used on any %s element that is associated with a labelable element." (q "aria-label") (q "label") (* Input errors *) | `Input `Checkbox_needs_aria_pressed -> 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." (q "input") (q "type") (q "checkbox") (q "role") (q "button") (q "aria-pressed") | `Input (`Value_constraint (`Constraint constraint_type)) -> constraint_type | `Input `List_not_allowed -> 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." (q "list") (q "color") (q "date") (q "datetime-local") (q "email") (q "month") (q "number") (q "range") (q "search") (q "tel") (q "text") (q "time") (q "url") (q "week") | `Input `List_requires_datalist -> Printf.sprintf "The %s attribute of the %s element must refer to a %s element." (q "list") (q "input") (q "datalist") (* Srcset errors *) | `Srcset `Sizes_without_srcset -> Printf.sprintf "The %s attribute must only be specified if the %s attribute is also specified." (q "sizes") (q "srcset") | `Srcset `Imagesizes_without_imagesrcset -> Printf.sprintf "The %s attribute must only be specified if the %s attribute is also specified." (q "imagesizes") (q "imagesrcset") | `Srcset `W_without_sizes -> Printf.sprintf "When the %s attribute has any image candidate string with a width descriptor, the %s attribute must also be specified." (q "srcset") (q "sizes") | `Srcset `Source_missing_srcset -> Printf.sprintf "Element %s is missing required attribute %s." (q "source") (q "srcset") | `Srcset `Source_needs_media_or_type -> 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." (q "source") (q "source") (q "img") (q "srcset") (q "media") (q "type") | `Srcset `Picture_missing_img -> Printf.sprintf "Element %s is missing required child element %s." (q "picture") (q "img") (* SVG errors *) | `Svg (`Deprecated_attr (`Attr attr, `Elem element)) -> Printf.sprintf "Attribute %s not allowed on element %s at this point." (q attr) (q element) | `Svg (`Missing_attr (`Elem element, `Attr attr)) -> Printf.sprintf "Element %s is missing required attribute %s." (q element) (q attr) (* Misc errors *) | `Misc `Option_empty_without_label -> Printf.sprintf "Element %s without attribute %s must not be empty." (q "option") (q "label") | `Misc `Bdo_missing_dir -> Printf.sprintf "Element %s must have attribute %s." (q "bdo") (q "dir") | `Misc `Bdo_dir_auto -> Printf.sprintf "The value of %s attribute for the %s element must not be %s." (q "dir") (q "bdo") (q "auto") | `Misc `Base_missing_href_or_target -> Printf.sprintf "Element %s is missing one or more of the following attributes: [href, target]." (q "base") | `Misc `Base_after_link_script -> Printf.sprintf "The %s element must come before any %s or %s elements in the document." (q "base") (q "link") (q "script") | `Misc `Map_id_name_mismatch -> Printf.sprintf "The %s attribute on a %s element must have an the same value as the %s attribute." (q "id") (q "map") (q "name") | `Misc `Summary_missing_role -> Printf.sprintf "Element %s is missing required attribute %s." (q "summary") (q "role") | `Misc `Summary_missing_attrs -> Printf.sprintf "Element %s is missing one or more of the following attributes: [aria-checked, aria-level, role]." (q "summary") | `Misc `Summary_role_not_allowed -> Printf.sprintf "The %s attribute must not be used on any %s element that is a summary for its parent %s element." (q "role") (q "summary") (q "details") | `Misc `Autocomplete_webauthn_on_select -> Printf.sprintf "The value of the %s attribute for the %s element must not contain %s." (q "autocomplete") (q "select") (q "webauthn") | `Misc `Commandfor_invalid_target -> 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." (q "commandfor") (q "button") (q "button") (q "commandfor") | `Misc `Style_type_invalid -> 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.)" (q "type") (q "style") (q "text/css") | `Misc `Headingoffset_invalid -> Printf.sprintf "The value of the %s attribute must be a number between %s and %s." (q "headingoffset") (q "0") (q "8") | `Misc `Media_empty -> Printf.sprintf "Value of %s attribute here must not be empty." (q "media") | `Misc `Media_all -> Printf.sprintf "Value of %s attribute here must not be %s." (q "media") (q "all") | `Misc `Multiple_h1 -> 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)." (q "h1") (q "h1") (q "headingoffset") (q "h1") | `Misc `Multiple_autofocus -> Printf.sprintf "There must not be two elements with the same %s that both have the %s attribute specified." (q "nearest ancestor autofocus scoping root element") (q "autofocus") (* Generic *) | `Generic message -> message (** {2 Error Construction Helpers} *) (** Create a bad attribute value error with element, attribute, value, and reason. *) let bad_value ~element ~attr ~value ~reason : t = `Attr (`Bad_value (`Elem element, `Attr attr, `Value value, `Reason reason)) (** Create a bad attribute value error with just a message. *) let bad_value_msg msg : t = `Attr (`Bad_value_generic (`Message msg)) (** Create a missing required attribute error. *) let missing_attr ~element ~attr : t = `Attr (`Missing (`Elem element, `Attr attr)) (** Create an attribute not allowed error. *) let attr_not_allowed ~element ~attr : t = `Attr (`Not_allowed (`Attr attr, `Elem element)) (** Create an element not allowed as child error. *) let not_allowed_as_child ~child ~parent : t = `Element (`Not_allowed_as_child (`Child child, `Parent parent)) (** Create a must not be empty error. *) let must_not_be_empty ~element : t = `Element (`Must_not_be_empty (`Elem element))