Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 60 additions & 12 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { docShell, esc, EXT_ATTRS, GITHUB_URL, highlightTokens, SITE_URL } from
import { renderLanding } from './src/landing.mjs';
import { renderImpressum } from './src/impressum.mjs';
import { renderLicense } from './src/license.mjs';
import { renderSchemaDoc } from './src/schema-doc.mjs';
import { collectSchemas, locateSchemas } from './src/schemas.mjs';

const root = path.dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -62,7 +63,21 @@ if (schemasDir) {
process.exit(1);
}
} else {
console.warn('warn: no schemas/ in the flashtrace checkout - skipping /schemas/.');
console.warn('warn: no schemas/ in the flashtrace checkout - skipping /schemas/ and /docs/schemas/.');
}

// Serving is format-agnostic - every schema file is copied verbatim whatever
// its extension. Rendering is not: src/schema-doc.mjs reads JSON Schema. A
// format we cannot render is still published, just without a docs page, and
// says so loudly rather than vanishing.
const documented = schemas.filter((s) => s.format === 'json');
for (const s of schemas) {
if (s.format !== 'json') {
console.warn(
`warn: ${s.title} (schemas/${s.name}/v*.${s.format}) is served but not documented - ` +
'src/schema-doc.mjs renders JSON Schema only.',
);
}
}

// --- version: release tag from env, else the tool repo's package.json ------
Expand Down Expand Up @@ -164,34 +179,51 @@ const marked = new Marked({
},
});

function renderDoc(page) {
state.toc = [];
state.slugCounts = new Map();
const md = readFileSync(path.join(docsDir, page.file), 'utf8');
const content = marked.parse(md);
const navGroups = [
// Shared by the markdown docs and the generated schema pages, so both render
// the same sidebar. `current` is { slug } for a doc page, { schema } for a
// schema page - a schema page has no slug, so no doc entry matches.
function navGroups(current) {
const groups = [
{
label: 'Getting started',
items: [{ title: 'Usage Guide', href: '/docs/usage/', current: page.slug === 'usage' }],
items: [{ title: 'Usage Guide', href: '/docs/usage/', current: current.slug === 'usage' }],
},
{
label: 'Specification',
items: [
{ title: 'Overview', href: '/docs/', current: page.slug === '' },
{ title: 'Overview', href: '/docs/', current: current.slug === '' },
...specPages.map((p) => ({
title: p.title,
href: `/docs/${p.slug}/`,
current: p.slug === page.slug,
current: p.slug === current.slug,
})),
],
},
];
if (documented.length > 0) {
groups.push({
label: 'Schemas',
items: documented.map((s) => ({
title: s.title,
href: `/docs/schemas/${s.slug}/`,
current: s.slug === current.schema,
})),
});
}
return groups;
}

function renderDoc(page) {
state.toc = [];
state.slugCounts = new Map();
const md = readFileSync(path.join(docsDir, page.file), 'utf8');
const content = marked.parse(md);
return docShell({
title: `${page.title} · flashtrace`,
description: `flashtrace documentation - ${page.title}.`,
path: page.slug ? `/docs/${page.slug}/` : '/docs/',
version,
navGroups,
navGroups: navGroups({ slug: page.slug }),
toc: state.toc,
content,
});
Expand Down Expand Up @@ -232,11 +264,25 @@ if (schemasDir) {
schema.latest.bytes,
);
}
for (const schema of documented) {
const dir = path.join(dist, 'docs', 'schemas', schema.slug);
mkdirSync(dir, { recursive: true });
try {
writeFileSync(
path.join(dir, 'index.html'),
renderSchemaDoc({ schema, version, navGroups: navGroups({ schema: schema.slug }) }),
);
} catch (err) {
console.error(`error: rendering /docs/schemas/${schema.slug}/ failed.\n${err.message}`);
process.exit(1);
}
}
}

const sitePaths = [
'/',
...pages.map((p) => (p.slug ? `/docs/${p.slug}/` : '/docs/')),
...documented.map((s) => `/docs/schemas/${s.slug}/`),
'/license/',
'/impressum/',
];
Expand All @@ -253,4 +299,6 @@ cpSync(path.join(root, 'public'), dist, { recursive: true });
cpSync(path.join(root, 'src', 'styles', 'site.css'), path.join(dist, 'site.css'));
cpSync(path.join(root, 'src', 'scripts', 'site.js'), path.join(dist, 'site.js'));

console.log(`built ${pages.length + 3} pages into dist/ (flashtrace ${version || 'unknown version'})`);
console.log(
`built ${pages.length + documented.length + 3} pages into dist/ (flashtrace ${version || 'unknown version'})`,
);
15 changes: 12 additions & 3 deletions src/layout.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export function highlightTokens(escaped) {
);
}

const themeInit = `(function(){try{var t=localStorage.getItem('ft-theme');if(t!=='light'&&t!=='dark'){t=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}document.documentElement.dataset.theme=t;}catch(e){document.documentElement.dataset.theme='light';}})();`;
// Also marks the document as scripted before first paint, so a JS-only control
// (the schema version picker) can be styled away when scripting is off, and
// the version filtering it drives never flashes in.
const themeInit = `(function(){document.documentElement.classList.add('js');try{var t=localStorage.getItem('ft-theme');if(t!=='light'&&t!=='dark'){t=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}document.documentElement.dataset.theme=t;}catch(e){document.documentElement.dataset.theme='light';}})();`;

const sunIcon = `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>`;
const moonIcon = `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/></svg>`;
Expand Down Expand Up @@ -135,15 +138,21 @@ ${groupHtml}
<div class="sidebar-backdrop" hidden></div>`;
}

// Right-rail "On this page" TOC from [{ id, text, level }] (h2/h3).
// Right-rail "On this page" TOC from [{ id, text, level }] (h2/h3). An entry
// may carry { version, current } to tie it to one section of a versioned page,
// so the rail lists only what is on screen.
function tocRail(toc) {
if (!toc.length) return '<aside class="toc" aria-hidden="true"></aside>';
return `<aside class="toc">
<nav aria-label="On this page">
<p class="toc-label">On this page</p>
<ul>
${toc
.map((h) => `<li class="toc-l${h.level}"><a href="#${h.id}">${h.text}</a></li>`)
.map((h) => {
const v = h.version ? ` data-version="${esc(h.version)}"` : '';
const cur = h.current ? ' is-current' : '';
return `<li class="toc-l${h.level}${cur}"${v}><a href="#${h.id}">${h.text}</a></li>`;
})
.join('\n ')}
</ul>
</nav>
Expand Down
Loading