app/lib/pages/page-json-ld.ts
77 lines and 2 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| pageJsonLd | 16 to 70 | 19 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| contentPageJsonLd | 73 to 80 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports app/lib/content-pages.mjs, app/lib/dictionary-entries.mjs, app/lib/pages/compile.mjs, app/lib/seo.ts. Imported by app/routes/content-page.tsx, app/routes/phages.tsx.
Source
// The structured data a content page carries: its trail, its own node typed from the page, and its dictionary entry.
// Shared by the research/* page route and the routes that draw a page's own piece themselves (the phage table).
import type { PageRecord } from "~/lib/pages/compile.mjs";
import { contentPageMarkdownPath } from "~/lib/content-pages.mjs";
import { definedTermJsonLd, type DictionaryEntry as Entry } from "~/lib/dictionary-entries.mjs";
import { LICENSE_URL, SITE, SITE_ORIGIN, breadcrumbJsonLd, personId } from "~/lib/seo";
type ContentPage = PageRecord;
/**
* The page's own node, typed from the page, so a WebSite is not emitted as an application. Each property
* is one the page's frontmatter or its markdown states; a fact the repo does not hold is left out, never
* filled in. A code repository belongs to SoftwareSourceCode in schema.org, so an application that has
* one gets a second node for its source, joined to it by targetProduct.
*/
function pageJsonLd(page: ContentPage): object[] {
if (!page.schemaType) return [];
const pageUrl = `${SITE_ORIGIN}${page.path}`;
const person = { "@type": "Person", "@id": personId(SITE_ORIGIN), name: SITE.name, url: SITE_ORIGIN };
const isDataset = page.schemaType === "Dataset";
const node = {
"@context": "https://schema.org",
"@type": page.schemaType,
"@id": `${pageUrl}#${page.schemaType.toLowerCase()}`,
// A dataset is named by what it holds; the page's short title ("Our phages") names the page.
name: isDataset ? page.seoTitle : page.title,
description: page.description,
url: page.productUrl ?? pageUrl,
...(page.productUrl ? { mainEntityOfPage: pageUrl } : {}),
// A legal page's last-updated day, the one the page shows under its title.
...(page.lastUpdated ? { dateModified: page.lastUpdated } : {}),
...(isDataset ? { creator: person } : { author: person }),
...(page.schemaType === "SoftwareSourceCode" && page.codeRepository
? { codeRepository: page.codeRepository }
: {}),
...(page.applicationCategory ? { applicationCategory: page.applicationCategory } : {}),
...(page.programmingLanguage ? { programmingLanguage: page.programmingLanguage } : {}),
...(page.runtimePlatform ? { runtimePlatform: page.runtimePlatform } : {}),
// A Dataset's license is the site's terms, whatever the page's frontmatter says, so none can be left out.
...(isDataset ? { license: LICENSE_URL } : page.license ? { license: page.license } : {}),
...(page.dataset
? {
// Public on this page with no sign-in, which is all the property claims.
isAccessibleForFree: true,
variableMeasured: page.dataset.variableMeasured,
...(page.dataset.temporalCoverage ? { temporalCoverage: page.dataset.temporalCoverage } : {}),
...(page.spatialCoverage ? { spatialCoverage: { "@type": "Place", name: page.spatialCoverage } } : {}),
// The markdown twin carries the same table, so it is the download.
distribution: {
"@type": "DataDownload",
encodingFormat: "text/markdown",
contentUrl: `${SITE_ORIGIN}${contentPageMarkdownPath(page.path)}`,
},
}
: {}),
};
if (page.schemaType !== "SoftwareApplication" || !page.codeRepository) return [node];
return [
node,
{
"@context": "https://schema.org",
"@type": "SoftwareSourceCode",
"@id": `${pageUrl}#softwaresourcecode`,
name: page.title,
codeRepository: page.codeRepository,
author: person,
targetProduct: { "@id": node["@id"] },
},
];
}
/** The page's structured data: the trail it shows, its own node or nodes, and its dictionary entry. */
export function contentPageJsonLd(page: ContentPage, trail: Array<[string, string]>, entry: Entry | null) {
return [
// Only a trail the page shows: the visible one renders from two steps, and Google reads no fewer.
...(trail.length >= 2 ? [breadcrumbJsonLd(SITE_ORIGIN, trail)] : []),
...pageJsonLd(page),
...(entry ? [definedTermJsonLd(entry, SITE_ORIGIN)] : []),
];
}