fmt and ci
Deploy website / deploy (push) Failing after 28s

This commit is contained in:
2026-08-25 17:59:45 -04:00
parent 7e9e744f0d
commit aacfd33c62
11 changed files with 1005 additions and 835 deletions
+52 -40
View File
@@ -1,67 +1,79 @@
import { readdir, readFile, stat } from 'node:fs/promises';
import { join } from 'node:path';
import { readdir, readFile, stat } from 'node:fs/promises'
import { join } from 'node:path'
const generatedRoot = '.generated';
const generatedRoot = '.generated'
const walk = async (directory) => {
const files = [];
const files = []
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (entry.isDirectory()) files.push(...await walk(path));
else files.push(path);
const path = join(directory, entry.name)
if (entry.isDirectory()) files.push(...(await walk(path)))
else files.push(path)
}
return files;
};
return files
}
const exists = async (path) => {
try {
await stat(path);
return true;
await stat(path)
return true
} catch {
return false;
return false
}
};
}
const errors = [];
const htmlFiles = (await walk(generatedRoot)).filter((file) => file.endsWith('.html'));
const clientSource = await readFile('src/main.js', 'utf8');
const builtInPrismLanguages = new Set(['markup', 'html', 'xml', 'svg', 'css', 'clike', 'javascript', 'js', 'plain', 'plaintext', 'text', 'txt']);
const prismLanguageAliases = { sh: 'bash', shell: 'bash' };
const errors = []
const htmlFiles = (await walk(generatedRoot)).filter((file) => file.endsWith('.html'))
const clientSource = await readFile('src/main.js', 'utf8')
const builtInPrismLanguages = new Set([
'markup',
'html',
'xml',
'svg',
'css',
'clike',
'javascript',
'js',
'plain',
'plaintext',
'text',
'txt',
])
const prismLanguageAliases = { sh: 'bash', shell: 'bash' }
for (const file of htmlFiles) {
const html = await readFile(file, 'utf8');
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index);
if (duplicateIds.length > 0) errors.push(`${file}: duplicate IDs: ${[...new Set(duplicateIds)].join(', ')}`);
if (!html.includes('<meta name="viewport"')) errors.push(`${file}: missing viewport metadata`);
if (html.includes('<heading')) errors.push(`${file}: contains a nonstandard <heading> element`);
const html = await readFile(file, 'utf8')
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1])
const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index)
if (duplicateIds.length > 0) errors.push(`${file}: duplicate IDs: ${[...new Set(duplicateIds)].join(', ')}`)
if (!html.includes('<meta name="viewport"')) errors.push(`${file}: missing viewport metadata`)
if (html.includes('<heading')) errors.push(`${file}: contains a nonstandard <heading> element`)
for (const [, fragment] of html.matchAll(/href="#([^"]+)"/g)) {
if (!ids.includes(fragment)) errors.push(`${file}: missing fragment target #${fragment}`);
if (!ids.includes(fragment)) errors.push(`${file}: missing fragment target #${fragment}`)
}
for (const [, href] of html.matchAll(/href="(\/[^"]*)"/g)) {
if (href === '/') continue;
const pathname = href.split(/[?#]/, 1)[0];
let target;
if (pathname.startsWith('/src/')) target = `.${pathname}`;
else if (pathname.startsWith('/assets/') || pathname === '/favicon.ico') target = `public${pathname}`;
else target = pathname.endsWith('/')
? join(generatedRoot, pathname, 'index.html')
: join(generatedRoot, pathname);
if (!await exists(target)) errors.push(`${file}: broken internal link ${href}`);
if (href === '/') continue
const pathname = href.split(/[?#]/, 1)[0]
let target
if (pathname.startsWith('/src/')) target = `.${pathname}`
else if (pathname.startsWith('/assets/') || pathname === '/favicon.ico') target = `public${pathname}`
else target = pathname.endsWith('/') ? join(generatedRoot, pathname, 'index.html') : join(generatedRoot, pathname)
if (!(await exists(target))) errors.push(`${file}: broken internal link ${href}`)
}
for (const [, language] of html.matchAll(/class="language-([\w-]+)"/g)) {
if (builtInPrismLanguages.has(language)) continue;
const component = prismLanguageAliases[language] ?? language;
if (!clientSource.includes(`prism-${component}`)) errors.push(`${file}: Prism grammar is not imported for ${language}`);
if (builtInPrismLanguages.has(language)) continue
const component = prismLanguageAliases[language] ?? language
if (!clientSource.includes(`prism-${component}`))
errors.push(`${file}: Prism grammar is not imported for ${language}`)
}
}
if (errors.length > 0) {
console.error(errors.join('\n'));
process.exitCode = 1;
console.error(errors.join('\n'))
process.exitCode = 1
} else {
console.log(`Checked ${htmlFiles.length} generated pages.`);
console.log(`Checked ${htmlFiles.length} generated pages.`)
}