feat: refine portfolio content, contact, and supporting media
This commit is contained in:
@@ -4,6 +4,7 @@ import { dirname, relative, resolve, sep } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { loadProductionInputs } from '../src/content/load-production-inputs.ts';
|
||||
import { createPageMetadata, createStaticHostArtifacts } from '../src/content/metadata.ts';
|
||||
import { contactAddress, readableContactAddress } from '../src/content/contact-email.ts';
|
||||
import { Window } from 'happy-dom';
|
||||
import { parseArtifactContract, parseBuildAuthority } from './build-contract.mjs';
|
||||
import { parseExactConfigArgument } from './config-argument.mjs';
|
||||
@@ -232,19 +233,31 @@ const assertSemanticHtml = (html, inputs, contract) => {
|
||||
...inputs.content.infrastructure.bands.flatMap((band) => [band.title, band.summary]),
|
||||
...inputs.content.projects.selected.flatMap((project) => [project.title, project.summary]),
|
||||
...inputs.content.toolkit.flatMap((group) => [group.title, ...group.technologies.map((technology) => technology.name)]),
|
||||
inputs.content.contact.email,
|
||||
];
|
||||
for (const copy of requiredVisibleCopy) {
|
||||
if (!semanticText.includes(copy)) throw new Error(`Missing visible semantic evidence: ${copy}`);
|
||||
}
|
||||
const mailLinks = [...document.querySelectorAll('a[href^="mailto:"]')];
|
||||
const startMailLinks = [...document.querySelectorAll('#start a[href^="mailto:"]')];
|
||||
const contactMailLinks = [...document.querySelectorAll('#contact a[href^="mailto:"]')];
|
||||
if (mailLinks.length !== 2 || startMailLinks.length !== 1 || contactMailLinks.length !== 1 || mailLinks.some((link) =>
|
||||
link.getAttribute('href') !== `mailto:${inputs.content.contact.email}` || link.textContent !== 'Email Luca'
|
||||
)) {
|
||||
throw new Error('Mailto links must be exactly one Email Luca action beneath each hero and contact section.');
|
||||
const contactActions = [...document.querySelectorAll('#start a[href="#contact"]')];
|
||||
if (contactActions.length !== 1 || contactActions[0].textContent !== 'Contact Me' ||
|
||||
!isVisibleSemanticNode(contactActions[0], appRoot)) {
|
||||
throw new Error('The hero must contain one visible Contact Me link to the contact section.');
|
||||
}
|
||||
if (document.querySelector('a[href^="mailto:"]') || html.includes(contactAddress(inputs.content.contact.email))) {
|
||||
throw new Error('The initial document must not expose a complete email address or mailto link.');
|
||||
}
|
||||
const disclosures = [...document.querySelectorAll('#contact details#contact-email-details')];
|
||||
if (disclosures.length !== 1 || disclosures[0].hasAttribute('open') ||
|
||||
disclosures[0].querySelector(':scope > summary')?.textContent !== 'Email Me' ||
|
||||
!isVisibleSemanticNode(disclosures[0], appRoot)) {
|
||||
throw new Error('Contact must contain a closed native Email Me disclosure.');
|
||||
}
|
||||
disclosures[0].setAttribute('open', '');
|
||||
const readableEmail = disclosures[0].querySelector('.contact-email__address');
|
||||
if (readableEmail?.textContent !== readableContactAddress(inputs.content.contact.email) ||
|
||||
!isVisibleSemanticNode(readableEmail, appRoot)) {
|
||||
throw new Error('Email disclosure must provide a readable address without JavaScript.');
|
||||
}
|
||||
disclosures[0].removeAttribute('open');
|
||||
|
||||
const moduleScript = document.querySelector(`script[type="module"][src="/${contract.entryModule}"]`);
|
||||
const snapshot = document.querySelector('script#portfolio-inputs[type="application/json"]');
|
||||
@@ -353,7 +366,7 @@ const assertMetadata = (html, expected) => {
|
||||
} catch {
|
||||
throw new Error('JSON-LD is malformed.');
|
||||
}
|
||||
if (/"(?:streetAddress|postalCode|telephone|image|interactionStatistic)"\s*:/iu.test(jsonLdMatch[1])) {
|
||||
if (/"(?:streetAddress|postalCode|telephone|image|interactionStatistic|email)"\s*:/iu.test(jsonLdMatch[1])) {
|
||||
throw new Error('JSON-LD contains unsafe private, portrait, or interaction metadata.');
|
||||
}
|
||||
if (jsonLd['@type'] !== 'ProfilePage' || jsonLd.mainEntity?.['@type'] !== 'Person') {
|
||||
|
||||
Reference in New Issue
Block a user