diff --git a/lib/render-content/create-processor.js b/lib/render-content/create-processor.js index c89eb3395b..e083f8da10 100644 --- a/lib/render-content/create-processor.js +++ b/lib/render-content/create-processor.js @@ -23,6 +23,7 @@ import useEnglishHeadings from './plugins/use-english-headings.js' import wrapInElement from './plugins/wrap-in-element.js' import headingLinks from './plugins/heading-links.js' import rewriteTheadThScope from './plugins/rewrite-thead-th-scope.js' +import rewriteForRowheaders from './plugins/rewrite-for-rowheaders.js' // plugins aren't designed to be used more than once, // this workaround lets us do that @@ -30,17 +31,6 @@ import rewriteTheadThScope from './plugins/rewrite-thead-th-scope.js' const wrapperForImages = () => wrapInElement({ selector: 'ol > li img', wrapper: 'span.procedural-image-wrapper' }) -const wrapperForRowheadersTbody = () => - wrapInElement({ - selector: '.rowheaders tbody tr td:first-child', - wrapper: 'th', - // This moves what it finds in the selector inside the wrapper. - rewrite: true, - wrapperAdditionalAttributes: { - scope: 'row', - }, - }) - export default function createProcessor(context) { return unified() .use(process.env.COMMONMARK ? markdownNext : markdown) @@ -58,7 +48,7 @@ export default function createProcessor(context) { .use(raw) .use(wrapperForImages) .use(rewriteTheadThScope) - .use(wrapperForRowheadersTbody) + .use(rewriteForRowheaders) .use(rewriteImgSources) .use(rewriteAssetImgTags) .use(rewriteLocalLinks, context) diff --git a/lib/render-content/plugins/rewrite-for-rowheaders.js b/lib/render-content/plugins/rewrite-for-rowheaders.js new file mode 100644 index 0000000000..a000d5b6dc --- /dev/null +++ b/lib/render-content/plugins/rewrite-for-rowheaders.js @@ -0,0 +1,62 @@ +import { visitParents } from 'unist-util-visit-parents' + +/** + * Where it can mutate the AST to swap from: + * + *
+ * + * ... + * + * + * ... + * ... + * ... + * + *
+ * + * to: + * + * + *
+ * + * ... + * + * + * ... + * ... + * ... + * + *
+ * + * In other words, if contained in a `.rowheaders`, the *first* `` + * in a `` should have `scope="row"`. + * + * */ + +function matcher(node) { + return node.type === 'element' && node.tagName === 'td' && !('scope' in node.properties) +} + +function insideRowheaders(ancestors) { + return ancestors.some( + (node) => + node.properties && + node.properties.className && + node.properties.className.includes('rowheaders') + ) +} + +function visitor(node, ancestors) { + if (insideRowheaders(ancestors)) { + const tr = ancestors.at(-1) + if (!tr._scoped) { + tr._scoped = true + node.properties.scope = 'row' + node.tagName = 'th' + } + } +} + +export default function rewriteForRowheaders() { + return (tree) => visitParents(tree, matcher, visitor) +}