diff --git a/11ty/CustomLiquid.ts b/11ty/CustomLiquid.ts index 948a4847f3..54fcc35fe1 100644 --- a/11ty/CustomLiquid.ts +++ b/11ty/CustomLiquid.ts @@ -7,7 +7,7 @@ import { basename } from "path"; import type { GlobalData } from "eleventy.config"; -import { biblioPattern, getBiblio } from "./biblio"; +import { biblioPattern, getBiblio, getXmlBiblio } from "./biblio"; import { flattenDom, load, type CheerioAnyNode } from "./cheerio"; import { generateId } from "./common"; import { getAcknowledgementsForVersion, type TermsMap } from "./guidelines"; @@ -22,12 +22,22 @@ const techniquesPattern = /\btechniques\//; const understandingPattern = /\bunderstanding\//; const biblio = await getBiblio(); +const xmlBiblio = await getXmlBiblio(); const termLinkSelector = "a:not([href])"; /** Generates {% include "foo.html" %} directives from 1 or more basenames */ const generateIncludes = (...basenames: string[]) => `\n${basenames.map((basename) => `{% include "${basename}.html" %}`).join("\n")}\n`; +/** Version of generateIncludes for a single include with parameters */ +const generateIncludeWithParams = (basename: string, params: Record) => { + const strParams = Object.entries(params).reduce( + (str, [key, value]) => `${str}, ${key}: ${JSON.stringify(value)}`, + "" + ); + return `\n{% include "${basename}.html"${strParams} %}\n`; +}; + /** * Determines whether a given string is actually HTML, * not e.g. a data value Eleventy sent to the templating engine. @@ -278,8 +288,9 @@ export class CustomLiquid extends Liquid { // Expand techniques links to always include title $(understandingToTechniqueLinkSelector).each((_, el) => expandTechniqueLink($(el))); - // Add key terms by default, to be removed in #parse if there are no terms - $("body").append(generateIncludes("understanding/key-terms")); + // Add key terms and references by default, to be removed in #parse if not needed + $("body").append(generateIncludeWithParams("dl-section", { title: "Key Terms" })); + $("body").append(generateIncludeWithParams("dl-section", { title: "References" })); } // Remove h2-level sections with no content other than heading @@ -469,7 +480,7 @@ export class CustomLiquid extends Liquid { for (const name of termNames) { const term = this.termsMap[name]; // Already verified existence in the earlier loop $termsList.append( - `
${term.name}
` + + `\n
${term.name}
` + `
${term.definition}
` ); } @@ -567,17 +578,34 @@ export class CustomLiquid extends Liquid { // Link biblio references if (scope.isUnderstanding) { + const xmlBiblioReferences: string[] = []; $("p").each((_, el) => { const $el = $(el); const html = $el.html(); if (html && biblioPattern.test(html)) { $el.html( - html.replace(biblioPattern, (substring, code) => - biblio[code]?.href ? `[${code}]` : substring - ) + html.replace(biblioPattern, (substring, code) => { + if (biblio[code]?.href) return `[${code}]`; + if (code in xmlBiblio) { + xmlBiblioReferences.push(code); + return `[${code}]`; + } + console.warn(`${scope.page.inputPath}: Unresolved biblio ref: ${code}`); + return substring; + }) ); } }); + + // Populate references section, or remove if unused + if (xmlBiblioReferences.length) { + for (const ref of uniq(xmlBiblioReferences).sort()) { + $("section#references dl").append( + `\n
${ref}
` + + `
${xmlBiblio[ref]}
` + ); + } + } else $("section#references").remove(); } // Allow autogenerating missing top-level section IDs in understanding docs, diff --git a/11ty/biblio.ts b/11ty/biblio.ts index fcf5faca80..5b23b14f1e 100644 --- a/11ty/biblio.ts +++ b/11ty/biblio.ts @@ -1,12 +1,20 @@ import axios from "axios"; import { readFile } from "fs/promises"; import { glob } from "glob"; +import invert from "lodash-es/invert"; import uniq from "lodash-es/uniq"; +import { join } from "path"; +import { loadFromFile } from "./cheerio"; import { wrapAxiosRequest } from "./common"; export const biblioPattern = /\[\[\??([\w-]+)\]\]/g; +// Resolve necessary aliases locally rather than requiring an extra round trip to specref +const aliases = { + "css3-values": "css-values-3", +}; + /** Compiles URLs from local biblio + specref for linking in Understanding documents. */ export async function getBiblio() { const localBiblio = eval( @@ -21,19 +29,32 @@ export async function getBiblio() { let match; while ((match = biblioPattern.exec(content))) if (!localBiblio[match[1]]) refs.push(match[1]); } - const uniqueRefs = uniq(refs); + const uniqueRefs = uniq(refs).map((ref) => + ref in aliases ? aliases[ref as keyof typeof aliases] : ref + ); const response = await wrapAxiosRequest( axios.get(`https://api.specref.org/bibrefs?refs=${uniqueRefs.join(",")}`) ); - const fullBiblio = { + for (const [from, to] of Object.entries(invert(aliases))) + response.data[to] = response.data[from]; + + return { ...response.data, ...localBiblio, }; +} - const resolvedRefs = Object.keys(fullBiblio); - const unresolvedRefs = uniqueRefs.filter((ref) => !resolvedRefs.includes(ref)); - if (unresolvedRefs.length) console.warn(`Unresolved biblio refs: ${unresolvedRefs.join(", ")}`); - - return fullBiblio; +/** Returns mapping of references included in WCAG 2.0, which largely lack URLs */ +export async function getXmlBiblio() { + const xmlRefs: Record = {}; + const $ = await loadFromFile(join("wcag20", "sources", "guide-to-wcag2-src.xml")); + $("bibl[id]").each((_, el) => { + const $ref = $(`#${el.attribs.id}`); + if (!$ref.length) return; + // Note: Using text() drops links ( tags in the XML), + // but the only link within refs that are actually used seems to be broken + xmlRefs[el.attribs.id] = $ref.text(); + }); + return xmlRefs; } diff --git a/_includes/dl-section.html b/_includes/dl-section.html new file mode 100644 index 0000000000..aff5144833 --- /dev/null +++ b/_includes/dl-section.html @@ -0,0 +1,6 @@ +
+
+

{{ title }}

+
+
+
diff --git a/_includes/understanding/key-terms.html b/_includes/understanding/key-terms.html deleted file mode 100644 index bea2906e38..0000000000 --- a/_includes/understanding/key-terms.html +++ /dev/null @@ -1,6 +0,0 @@ -
-
-

Key Terms

-
-
-