aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_utils/hast_util_to_swc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_utils/hast_util_to_swc.rs')
-rw-r--r--tests/test_utils/hast_util_to_swc.rs781
1 files changed, 0 insertions, 781 deletions
diff --git a/tests/test_utils/hast_util_to_swc.rs b/tests/test_utils/hast_util_to_swc.rs
deleted file mode 100644
index ea7ffd1..0000000
--- a/tests/test_utils/hast_util_to_swc.rs
+++ /dev/null
@@ -1,781 +0,0 @@
-//! Turn an HTML AST into a JavaScript AST.
-//!
-//! Port of <https://github.com/syntax-tree/hast-util-to-estree>, by the same
-//! author:
-//!
-//! (The MIT License)
-//!
-//! Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
-//!
-//! Permission is hereby granted, free of charge, to any person obtaining
-//! a copy of this software and associated documentation files (the
-//! 'Software'), to deal in the Software without restriction, including
-//! without limitation the rights to use, copy, modify, merge, publish,
-//! distribute, sublicense, and/or sell copies of the Software, and to
-//! permit persons to whom the Software is furnished to do so, subject to
-//! the following conditions:
-//!
-//! The above copyright notice and this permission notice shall be
-//! included in all copies or substantial portions of the Software.
-//!
-//! THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-//! IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-//! TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-//! SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-use crate::test_utils::{
- hast,
- swc::{parse_esm_to_tree, parse_expression_to_tree},
- swc_utils::{create_ident, position_to_span},
-};
-use core::str;
-use markdown::{Location, MdxExpressionKind};
-
-/// Result.
-#[derive(Debug, PartialEq, Eq)]
-pub struct Program {
- pub path: Option<String>,
- /// JS AST.
- pub module: swc_ecma_ast::Module,
- /// Comments relating to AST.
- pub comments: Vec<swc_common::comments::Comment>,
-}
-
-/// Whether we’re in HTML or SVG.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Space {
- Html,
- Svg,
-}
-
-#[derive(Debug)]
-struct Context<'a> {
- /// Whether we’re in HTML or SVG.
- ///
- /// Not used yet, likely useful in the future.
- space: Space,
- /// Comments we gather.
- comments: Vec<swc_common::comments::Comment>,
- /// Declarations and stuff.
- esm: Vec<swc_ecma_ast::ModuleItem>,
- /// Optional way to turn relative positions into points.
- location: Option<&'a Location>,
-}
-
-#[allow(dead_code)]
-pub fn hast_util_to_swc(
- tree: &hast::Node,
- path: Option<String>,
- location: Option<&Location>,
-) -> Result<Program, String> {
- let mut context = Context {
- space: Space::Html,
- comments: vec![],
- esm: vec![],
- location,
- };
- let expr = match one(&mut context, tree)? {
- Some(swc_ecma_ast::JSXElementChild::JSXFragment(x)) => {
- Some(swc_ecma_ast::Expr::JSXFragment(x))
- }
- Some(swc_ecma_ast::JSXElementChild::JSXElement(x)) => {
- Some(swc_ecma_ast::Expr::JSXElement(x))
- }
- Some(child) => Some(swc_ecma_ast::Expr::JSXFragment(create_fragment(
- vec![child],
- tree,
- ))),
- None => None,
- };
-
- // Add the ESM.
- let mut module = swc_ecma_ast::Module {
- shebang: None,
- body: context.esm,
- span: position_to_span(tree.position()),
- };
-
- // We have some content, wrap it.
- if let Some(expr) = expr {
- module
- .body
- .push(swc_ecma_ast::ModuleItem::Stmt(swc_ecma_ast::Stmt::Expr(
- swc_ecma_ast::ExprStmt {
- expr: Box::new(expr),
- span: swc_common::DUMMY_SP,
- },
- )));
- }
-
- Ok(Program {
- path,
- module,
- comments: context.comments,
- })
-}
-
-/// Transform one node.
-fn one(
- context: &mut Context,
- node: &hast::Node,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- let value = match node {
- hast::Node::Comment(x) => Some(transform_comment(context, node, x)),
- hast::Node::Element(x) => transform_element(context, node, x)?,
- hast::Node::MdxJsxElement(x) => transform_mdx_jsx_element(context, node, x)?,
- hast::Node::MdxExpression(x) => transform_mdx_expression(context, node, x)?,
- hast::Node::MdxjsEsm(x) => transform_mdxjs_esm(context, node, x)?,
- hast::Node::Root(x) => transform_root(context, node, x)?,
- hast::Node::Text(x) => transform_text(context, node, x),
- // Ignore:
- hast::Node::Doctype(_) => None,
- };
- Ok(value)
-}
-
-/// Transform children of `parent`.
-fn all(
- context: &mut Context,
- parent: &hast::Node,
-) -> Result<Vec<swc_ecma_ast::JSXElementChild>, String> {
- let mut result = vec![];
- if let Some(children) = parent.children() {
- let mut index = 0;
- while index < children.len() {
- let child = &children[index];
- // To do: remove line endings between table elements?
- // <https://github.com/syntax-tree/hast-util-to-estree/blob/6c45f166d106ea3a165c14ec50c35ed190055e65/lib/index.js>
- if let Some(child) = one(context, child)? {
- result.push(child);
- }
- index += 1;
- }
- }
-
- Ok(result)
-}
-
-/// [`Comment`][hast::Comment].
-fn transform_comment(
- context: &mut Context,
- node: &hast::Node,
- comment: &hast::Comment,
-) -> swc_ecma_ast::JSXElementChild {
- context.comments.push(swc_common::comments::Comment {
- kind: swc_common::comments::CommentKind::Block,
- text: comment.value.clone().into(),
- span: position_to_span(node.position()),
- });
-
- // Might be useless.
- // Might be useful when transforming to acorn/babel later.
- // This is done in the JS version too:
- // <https://github.com/syntax-tree/hast-util-to-estree/blob/6c45f166d106ea3a165c14ec50c35ed190055e65/lib/index.js#L168>
- swc_ecma_ast::JSXElementChild::JSXExprContainer(swc_ecma_ast::JSXExprContainer {
- expr: swc_ecma_ast::JSXExpr::JSXEmptyExpr(swc_ecma_ast::JSXEmptyExpr {
- span: position_to_span(node.position()),
- }),
- span: position_to_span(node.position()),
- })
-}
-
-/// [`Element`][hast::Element].
-fn transform_element(
- context: &mut Context,
- node: &hast::Node,
- element: &hast::Element,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- let space = context.space;
-
- if space == Space::Html && element.tag_name == "svg" {
- context.space = Space::Svg;
- }
-
- let children = all(context, node)?;
-
- context.space = space;
-
- let mut attrs = vec![];
-
- let mut index = 0;
- while index < element.properties.len() {
- let prop = &element.properties[index];
-
- // To do: turn style props into objects.
- let value = match &prop.1 {
- hast::PropertyValue::Boolean(x) => {
- // No value is same as `{true}` / Ignore `false`.
- if *x {
- None
- } else {
- index += 1;
- continue;
- }
- }
- hast::PropertyValue::String(x) => Some(swc_ecma_ast::Lit::Str(swc_ecma_ast::Str {
- value: x.clone().into(),
- span: swc_common::DUMMY_SP,
- raw: None,
- })),
- hast::PropertyValue::CommaSeparated(x) => {
- Some(swc_ecma_ast::Lit::Str(swc_ecma_ast::Str {
- value: x.join(", ").into(),
- span: swc_common::DUMMY_SP,
- raw: None,
- }))
- }
- hast::PropertyValue::SpaceSeparated(x) => {
- Some(swc_ecma_ast::Lit::Str(swc_ecma_ast::Str {
- value: x.join(" ").into(),
- span: swc_common::DUMMY_SP,
- raw: None,
- }))
- }
- };
-
- // Turn property case into either React-specific case, or HTML
- // attribute case.
- // To do: create a spread if this is an invalid attr name.
- let attr_name = prop_to_attr_name(&prop.0);
-
- attrs.push(swc_ecma_ast::JSXAttrOrSpread::JSXAttr(
- swc_ecma_ast::JSXAttr {
- name: create_jsx_attr_name(&attr_name),
- value: value.map(swc_ecma_ast::JSXAttrValue::Lit),
- span: swc_common::DUMMY_SP,
- },
- ));
-
- index += 1;
- }
-
- Ok(Some(swc_ecma_ast::JSXElementChild::JSXElement(
- create_element(&element.tag_name, attrs, children, node),
- )))
-}
-
-/// [`MdxJsxElement`][hast::MdxJsxElement].
-fn transform_mdx_jsx_element(
- context: &mut Context,
- node: &hast::Node,
- element: &hast::MdxJsxElement,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- let space = context.space;
-
- if let Some(name) = &element.name {
- if space == Space::Html && name == "svg" {
- context.space = Space::Svg;
- }
- }
-
- let children = all(context, node)?;
-
- context.space = space;
-
- let mut attrs = vec![];
- let mut index = 0;
-
- while index < element.attributes.len() {
- let attr = match &element.attributes[index] {
- hast::AttributeContent::Property(prop) => {
- let value = match prop.value.as_ref() {
- Some(hast::AttributeValue::Literal(x)) => {
- Some(swc_ecma_ast::JSXAttrValue::Lit(swc_ecma_ast::Lit::Str(
- swc_ecma_ast::Str {
- value: x.clone().into(),
- span: swc_common::DUMMY_SP,
- raw: None,
- },
- )))
- }
- Some(hast::AttributeValue::Expression(value, stops)) => {
- Some(swc_ecma_ast::JSXAttrValue::JSXExprContainer(
- swc_ecma_ast::JSXExprContainer {
- expr: swc_ecma_ast::JSXExpr::Expr(parse_expression_to_tree(
- value,
- &MdxExpressionKind::AttributeValueExpression,
- stops,
- context.location,
- )?),
- span: swc_common::DUMMY_SP,
- },
- ))
- }
- None => None,
- };
-
- swc_ecma_ast::JSXAttrOrSpread::JSXAttr(swc_ecma_ast::JSXAttr {
- span: swc_common::DUMMY_SP,
- name: create_jsx_attr_name(&prop.name),
- value,
- })
- }
- hast::AttributeContent::Expression(value, stops) => {
- let expr = parse_expression_to_tree(
- value,
- &MdxExpressionKind::AttributeExpression,
- stops,
- context.location,
- )?;
- swc_ecma_ast::JSXAttrOrSpread::SpreadElement(swc_ecma_ast::SpreadElement {
- dot3_token: swc_common::DUMMY_SP,
- expr,
- })
- }
- };
-
- attrs.push(attr);
- index += 1;
- }
-
- Ok(Some(if let Some(name) = &element.name {
- swc_ecma_ast::JSXElementChild::JSXElement(create_element(name, attrs, children, node))
- } else {
- swc_ecma_ast::JSXElementChild::JSXFragment(create_fragment(children, node))
- }))
-}
-
-/// [`MdxExpression`][hast::MdxExpression].
-fn transform_mdx_expression(
- context: &mut Context,
- node: &hast::Node,
- expression: &hast::MdxExpression,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- Ok(Some(swc_ecma_ast::JSXElementChild::JSXExprContainer(
- swc_ecma_ast::JSXExprContainer {
- expr: swc_ecma_ast::JSXExpr::Expr(parse_expression_to_tree(
- &expression.value,
- &MdxExpressionKind::Expression,
- &expression.stops,
- context.location,
- )?),
- span: position_to_span(node.position()),
- },
- )))
-}
-
-/// [`MdxjsEsm`][hast::MdxjsEsm].
-fn transform_mdxjs_esm(
- context: &mut Context,
- _node: &hast::Node,
- esm: &hast::MdxjsEsm,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- let mut module = parse_esm_to_tree(&esm.value, &esm.stops, context.location)?;
- let mut index = 0;
-
- // To do: check that identifiers are not duplicated across esm blocks.
- while index < module.body.len() {
- if !matches!(module.body[index], swc_ecma_ast::ModuleItem::ModuleDecl(_)) {
- return Err("Unexpected `statement` in code: only import/exports are supported".into());
- }
- index += 1;
- }
-
- context.esm.append(&mut module.body);
- Ok(None)
-}
-
-/// [`Root`][hast::Root].
-fn transform_root(
- context: &mut Context,
- node: &hast::Node,
- _root: &hast::Root,
-) -> Result<Option<swc_ecma_ast::JSXElementChild>, String> {
- let mut children = all(context, node)?;
- let mut queue = vec![];
- let mut nodes = vec![];
- let mut seen = false;
-
- children.reverse();
-
- // Remove initial/final whitespace.
- while let Some(child) = children.pop() {
- let mut stash = false;
-
- if let swc_ecma_ast::JSXElementChild::JSXExprContainer(container) = &child {
- if let swc_ecma_ast::JSXExpr::Expr(expr) = &container.expr {
- if let swc_ecma_ast::Expr::Lit(swc_ecma_ast::Lit::Str(str)) = (*expr).as_ref() {
- if inter_element_whitespace(str.value.as_ref()) {
- stash = true;
- }
- }
- }
- }
-
- if stash {
- if seen {
- queue.push(child);
- }
- } else {
- if !queue.is_empty() {
- nodes.append(&mut queue);
- }
- nodes.push(child);
- seen = true;
- }
- }
-
- Ok(Some(swc_ecma_ast::JSXElementChild::JSXFragment(
- create_fragment(nodes, node),
- )))
-}
-
-/// [`Text`][hast::Text].
-fn transform_text(
- _context: &mut Context,
- node: &hast::Node,
- text: &hast::Text,
-) -> Option<swc_ecma_ast::JSXElementChild> {
- if text.value.is_empty() {
- None
- } else {
- Some(swc_ecma_ast::JSXElementChild::JSXExprContainer(
- swc_ecma_ast::JSXExprContainer {
- expr: swc_ecma_ast::JSXExpr::Expr(Box::new(swc_ecma_ast::Expr::Lit(
- swc_ecma_ast::Lit::Str(swc_ecma_ast::Str {
- value: text.value.clone().into(),
- span: position_to_span(node.position()),
- raw: None,
- }),
- ))),
- span: position_to_span(node.position()),
- },
- ))
- }
-}
-
-/// Create an element.
-///
-/// Creates a void one if there are no children.
-fn create_element(
- name: &str,
- attrs: Vec<swc_ecma_ast::JSXAttrOrSpread>,
- children: Vec<swc_ecma_ast::JSXElementChild>,
- node: &hast::Node,
-) -> Box<swc_ecma_ast::JSXElement> {
- Box::new(swc_ecma_ast::JSXElement {
- opening: swc_ecma_ast::JSXOpeningElement {
- name: create_jsx_name(name),
- attrs,
- self_closing: children.is_empty(),
- type_args: None,
- span: swc_common::DUMMY_SP,
- },
- closing: if children.is_empty() {
- None
- } else {
- Some(swc_ecma_ast::JSXClosingElement {
- name: create_jsx_name(name),
- span: swc_common::DUMMY_SP,
- })
- },
- children,
- span: position_to_span(node.position()),
- })
-}
-
-/// Create a fragment.
-fn create_fragment(
- children: Vec<swc_ecma_ast::JSXElementChild>,
- node: &hast::Node,
-) -> swc_ecma_ast::JSXFragment {
- swc_ecma_ast::JSXFragment {
- opening: swc_ecma_ast::JSXOpeningFragment {
- span: swc_common::DUMMY_SP,
- },
- closing: swc_ecma_ast::JSXClosingFragment {
- span: swc_common::DUMMY_SP,
- },
- children,
- span: position_to_span(node.position()),
- }
-}
-
-/// Create a JSX element name.
-fn create_jsx_name(name: &str) -> swc_ecma_ast::JSXElementName {
- match parse_jsx_name(name) {
- // `<a.b.c />`
- // `<a.b />`
- JsxName::Member(parts) => {
- // Always two or more items.
- let mut member = swc_ecma_ast::JSXMemberExpr {
- obj: swc_ecma_ast::JSXObject::Ident(create_ident(parts[0])),
- prop: create_ident(parts[1]),
- };
- let mut index = 2;
- while index < parts.len() {
- member = swc_ecma_ast::JSXMemberExpr {
- obj: swc_ecma_ast::JSXObject::JSXMemberExpr(Box::new(member)),
- prop: create_ident(parts[index]),
- };
- index += 1;
- }
- swc_ecma_ast::JSXElementName::JSXMemberExpr(member)
- }
- // `<a:b />`
- JsxName::Namespace(ns, name) => {
- swc_ecma_ast::JSXElementName::JSXNamespacedName(swc_ecma_ast::JSXNamespacedName {
- ns: create_ident(ns),
- name: create_ident(name),
- })
- }
- // `<a />`
- JsxName::Normal(name) => swc_ecma_ast::JSXElementName::Ident(create_ident(name)),
- }
-}
-
-/// Create a JSX attribute name.
-fn create_jsx_attr_name(name: &str) -> swc_ecma_ast::JSXAttrName {
- match parse_jsx_name(name) {
- JsxName::Member(_) => {
- unreachable!("member expressions in attribute names are not supported")
- }
- // `<a b:c />`
- JsxName::Namespace(ns, name) => {
- swc_ecma_ast::JSXAttrName::JSXNamespacedName(swc_ecma_ast::JSXNamespacedName {
- ns: create_ident(ns),
- name: create_ident(name),
- })
- }
- // `<a b />`
- JsxName::Normal(name) => swc_ecma_ast::JSXAttrName::Ident(create_ident(name)),
- }
-}
-
-fn inter_element_whitespace(value: &str) -> bool {
- let bytes = value.as_bytes();
- let mut index = 0;
-
- while index < bytes.len() {
- match bytes[index] {
- b'\t' | 0x0C | b'\r' | b'\n' | b' ' => {}
- _ => return false,
- }
- index += 1;
- }
-
- true
-}
-
-/// Different kinds of JSX names.
-enum JsxName<'a> {
- // `a.b.c`
- Member(Vec<&'a str>),
- // `a:b`
- Namespace(&'a str, &'a str),
- // `a`
- Normal(&'a str),
-}
-
-/// Parse a JSX name from a string.
-fn parse_jsx_name(name: &str) -> JsxName {
- let bytes = name.as_bytes();
- let mut index = 0;
- let mut start = 0;
- let mut parts = vec![];
-
- while index < bytes.len() {
- if bytes[index] == b'.' {
- parts.push(&name[start..index]);
- start = index + 1;
- }
-
- index += 1;
- }
-
- // `<a.b.c />`
- if !parts.is_empty() {
- parts.push(&name[start..]);
- JsxName::Member(parts)
- }
- // `<a:b />`
- else if let Some(colon) = bytes.iter().position(|d| matches!(d, b':')) {
- JsxName::Namespace(&name[0..colon], &name[(colon + 1)..])
- }
- // `<a />`
- else {
- JsxName::Normal(name)
- }
-}
-
-/// Turn a hast property into something that particularly React understands.
-fn prop_to_attr_name(prop: &str) -> String {
- // Arbitrary data props, kebab case them.
- if prop.len() > 4 && prop.starts_with("data") {
- // Assume like two dashes maybe?
- let mut result = String::with_capacity(prop.len() + 2);
- let bytes = prop.as_bytes();
- let mut index = 4;
- let mut start = index;
- let mut valid = true;
-
- result.push_str("data");
-
- while index < bytes.len() {
- let byte = bytes[index];
- let mut dash = index == 4;
-
- match byte {
- b'A'..=b'Z' => dash = true,
- b'-' | b'.' | b':' | b'0'..=b'9' | b'a'..=b'z' => {}
- _ => {
- valid = false;
- break;
- }
- }
-
- if dash {
- if start != index {
- result.push_str(&prop[start..index]);
- }
- result.push('-');
- result.push(byte.to_ascii_lowercase().into());
- start = index + 1;
- }
-
- index += 1;
- }
-
- if valid {
- result.push_str(&prop[start..]);
- return result;
- }
- }
-
- // Look up if prop differs from attribute case.
- // Unknown things are passed through.
- PROP_TO_REACT_PROP
- .iter()
- .find(|d| d.0 == prop)
- .or_else(|| PROP_TO_ATTR_EXCEPTIONS_SHARED.iter().find(|d| d.0 == prop))
- .map(|d| d.1.into())
- .unwrap_or_else(|| prop.into())
-}
-
-// Below data is generated with:
-//
-// Note: there are currently no HTML and SVG specific exceptions.
-// If those would start appearing, the logic that uses these lists needs
-// To support spaces.
-//
-// ```js
-// import * as x from "property-information";
-//
-// /** @type {Record<string, string>} */
-// let shared = {};
-// /** @type {Record<string, string>} */
-// let html = {};
-// /** @type {Record<string, string>} */
-// let svg = {};
-//
-// Object.keys(x.html.property).forEach((prop) => {
-// let attr = x.html.property[prop].attribute;
-// if (!x.html.property[prop].space && prop !== attr) {
-// html[prop] = attr;
-// }
-// });
-//
-// Object.keys(x.svg.property).forEach((prop) => {
-// let attr = x.svg.property[prop].attribute;
-// if (!x.svg.property[prop].space && prop !== attr) {
-// // Shared.
-// if (prop in html && html[prop] === attr) {
-// shared[prop] = attr;
-// delete html[prop];
-// } else {
-// svg[prop] = attr;
-// }
-// }
-// });
-//
-// /** @type {Array<[string, Array<[string, string]>]>} */
-// const all = [
-// ["PROP_TO_REACT_PROP", Object.entries(x.hastToReact)],
-// ["PROP_TO_ATTR_EXCEPTIONS", Object.entries(shared)],
-// ["PROP_TO_ATTR_EXCEPTIONS_HTML", Object.entries(html)],
-// ["PROP_TO_ATTR_EXCEPTIONS_SVG", Object.entries(svg)],
-// ];
-//
-// console.log(
-// all
-// .map((d) => {
-// return `const ${d[0]}: [(&str, &str); ${d[1].length}] = [
-// ${d[1].map((d) => ` ("${d[0]}", "${d[1]}")`).join(",\n")}
-// ];`;
-// })
-// .join("\n\n")
-// );
-// ```
-const PROP_TO_REACT_PROP: [(&str, &str); 17] = [
- ("classId", "classID"),
- ("dataType", "datatype"),
- ("itemId", "itemID"),
- ("strokeDashArray", "strokeDasharray"),
- ("strokeDashOffset", "strokeDashoffset"),
- ("strokeLineCap", "strokeLinecap"),
- ("strokeLineJoin", "strokeLinejoin"),
- ("strokeMiterLimit", "strokeMiterlimit"),
- ("typeOf", "typeof"),
- ("xLinkActuate", "xlinkActuate"),
- ("xLinkArcRole", "xlinkArcrole"),
- ("xLinkHref", "xlinkHref"),
- ("xLinkRole", "xlinkRole"),
- ("xLinkShow", "xlinkShow"),
- ("xLinkTitle", "xlinkTitle"),
- ("xLinkType", "xlinkType"),
- ("xmlnsXLink", "xmlnsXlink"),
-];
-
-const PROP_TO_ATTR_EXCEPTIONS_SHARED: [(&str, &str); 48] = [
- ("ariaActiveDescendant", "aria-activedescendant"),
- ("ariaAtomic", "aria-atomic"),
- ("ariaAutoComplete", "aria-autocomplete"),
- ("ariaBusy", "aria-busy"),
- ("ariaChecked", "aria-checked"),
- ("ariaColCount", "aria-colcount"),
- ("ariaColIndex", "aria-colindex"),
- ("ariaColSpan", "aria-colspan"),
- ("ariaControls", "aria-controls"),
- ("ariaCurrent", "aria-current"),
- ("ariaDescribedBy", "aria-describedby"),
- ("ariaDetails", "aria-details"),
- ("ariaDisabled", "aria-disabled"),
- ("ariaDropEffect", "aria-dropeffect"),
- ("ariaErrorMessage", "aria-errormessage"),
- ("ariaExpanded", "aria-expanded"),
- ("ariaFlowTo", "aria-flowto"),
- ("ariaGrabbed", "aria-grabbed"),
- ("ariaHasPopup", "aria-haspopup"),
- ("ariaHidden", "aria-hidden"),
- ("ariaInvalid", "aria-invalid"),
- ("ariaKeyShortcuts", "aria-keyshortcuts"),
- ("ariaLabel", "aria-label"),
- ("ariaLabelledBy", "aria-labelledby"),
- ("ariaLevel", "aria-level"),
- ("ariaLive", "aria-live"),
- ("ariaModal", "aria-modal"),
- ("ariaMultiLine", "aria-multiline"),
- ("ariaMultiSelectable", "aria-multiselectable"),
- ("ariaOrientation", "aria-orientation"),
- ("ariaOwns", "aria-owns"),
- ("ariaPlaceholder", "aria-placeholder"),
- ("ariaPosInSet", "aria-posinset"),
- ("ariaPressed", "aria-pressed"),
- ("ariaReadOnly", "aria-readonly"),
- ("ariaRelevant", "aria-relevant"),
- ("ariaRequired", "aria-required"),
- ("ariaRoleDescription", "aria-roledescription"),
- ("ariaRowCount", "aria-rowcount"),
- ("ariaRowIndex", "aria-rowindex"),
- ("ariaRowSpan", "aria-rowspan"),
- ("ariaSelected", "aria-selected"),
- ("ariaSetSize", "aria-setsize"),
- ("ariaSort", "aria-sort"),
- ("ariaValueMax", "aria-valuemax"),
- ("ariaValueMin", "aria-valuemin"),
- ("ariaValueNow", "aria-valuenow"),
- ("ariaValueText", "aria-valuetext"),
-];