diff --git a/ssr/meta-description.ts b/ssr/meta-description.ts new file mode 100644 index 000000000000..56437d5f5a7b --- /dev/null +++ b/ssr/meta-description.ts @@ -0,0 +1,77 @@ +import { DEFAULT_LOCALE } from "../libs/constants/index.js"; +import { Doc } from "../libs/types/document.js"; + +export function getMetaDescription(doc: Doc): string { + const { pageType } = doc; + if (doc.locale === DEFAULT_LOCALE) { + const sections = doc.toc?.map(({ text }) => text) ?? []; + + const syntaxItems = Object.entries({ + Value: "type", + "Event type": "type", + Syntax: "syntax", + Parameters: "parameters", + Constructor: "constructor", + "Instance properties": "properties", + "Event properties": "properties", + "Instance methods": "methods", + "Return value": "return value", + }) + .filter(([section]) => sections.includes(section)) + .map(([, text]) => text); + + const otherItems = Object.entries({ + Exceptions: "exceptions", + Examples: "code examples", + Specifications: "specifications", + "Browser compatibility": "browser compatibility", + }) + .filter(([section]) => sections.includes(section)) + .map(([, text]) => text); + + const listFormatter = new Intl.ListFormat("en", { + style: "long", + type: "conjunction", + }); + const syntaxContents = listFormatter.format(syntaxItems); + const otherContents = listFormatter.format(otherItems); + const contents = [ + syntaxContents ? `its ${syntaxContents}` : "", + otherContents, + ] + .filter(Boolean) + .join(", "); + + switch (pageType) { + case "web-api-instance-property": + case "web-api-static-property": + // "Learn about the Window.localStorage property, ..." + // "Learn about the static Notification.permission property, ..." + return `Learn about the ${doc.title.replace(/^(.*?): (.*?) (static )?property$/, "$3$1.$2 property")}, including ${contents}.`; + + case "web-api-instance-method": + case "web-api-static-method": + // "Learn about the EventTarget.addEventListener() method, ..." + // "Learn about the static URL.createObjectURL() method, ..." + return `Learn about the ${doc.title.replace(/^(.*?): (.*?) (static )?method$/, "$3$1.$2 method")}, including ${contents}.`; + + case "web-api-interface": + // "Learn about the URLSearchParams interface, ..." + return `Learn about the ${doc.title} interface, including ${contents}.`; + + case "web-api-event": + // "Learn about the DOMContentLoaded event, ..." + return `Learn about the ${doc.title.replace(/^.*?: /, "")}, including ${contents}.`; + + case "web-api-constructor": + // "Learn about the URL() constructor, ..." + return `Learn about the ${doc.title.replace(/^.*?: /, "")}, including ${contents}.`; + + case "web-api-global-function": + // "Learn about the global setTimeout() function, ..." + return `Learn about the ${doc.title.replace(/^(.*) global function$/, "global $1 function")}, including ${contents}.`; + } + } + + return doc.summary; +} diff --git a/ssr/render.ts b/ssr/render.ts index 7cbdba439454..f3fcb41ff11c 100644 --- a/ssr/render.ts +++ b/ssr/render.ts @@ -12,6 +12,7 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import HTML from "../client/build/index.html?raw"; +import { getMetaDescription } from "./meta-description"; // When there are multiple options for a given language, this gives the // preferred locale for that language (language => preferred locale). @@ -120,6 +121,7 @@ export default function render( const canonicalURL = `${BASE_URL}${url}`; let escapedPageTitle = htmlEscape(pageTitle); + let metaDescription = pageDescription; const hydrationData: HydrationData = { url }; const translations: string[] = []; @@ -137,6 +139,7 @@ export default function render( // Use the doc's title instead escapedPageTitle = htmlEscape(doc.pageTitle); + metaDescription = htmlEscape(getMetaDescription(doc)); if (doc.summary) { pageDescription = htmlEscape(doc.summary); } @@ -231,9 +234,9 @@ export default function render( return ``; } ); - if (pageDescription) { + if (metaDescription) { html = html.replace(//g, () => { - return ``; + return ``; }); } html = html.replace("