121 lines
4.7 KiB
JavaScript
121 lines
4.7 KiB
JavaScript
import { marked } from 'marked';
|
|
import { escapeAttribute, escapeHtml, highlightCode } from './highlight.js';
|
|
|
|
const modules = import.meta.glob('../../../../doc/manual/souce/**/*.md', {
|
|
query: '?raw',
|
|
import: 'default',
|
|
eager: true,
|
|
});
|
|
|
|
export const docs = Object.fromEntries(
|
|
Object.entries(modules).map(([path, content]) => {
|
|
const slug = path
|
|
.replace(/^\.\.\/\.\.\/\.\.\/\.\.\/doc\/manual\/souce\//, '')
|
|
.replace(/\.md$/, '')
|
|
.replace(/\/index$/, '');
|
|
return [slug || 'index', content];
|
|
}),
|
|
);
|
|
|
|
export const nav = [
|
|
{ title: 'Introduction', slug: 'introduction' },
|
|
{
|
|
title: 'Language Specification',
|
|
slug: 'language',
|
|
children: [
|
|
{ title: 'Syntax', slug: 'language/syntax' },
|
|
{
|
|
title: 'Value',
|
|
slug: 'language/value',
|
|
children: [
|
|
{ title: 'String', slug: 'language/value/string' },
|
|
{ title: 'Int', slug: 'language/value/int' },
|
|
{ title: 'Float', slug: 'language/value/float' },
|
|
{ title: 'Bool', slug: 'language/value/bool' },
|
|
],
|
|
},
|
|
{
|
|
title: 'Expression',
|
|
slug: 'language/expression',
|
|
children: [
|
|
{ title: 'Literal', slug: 'language/expression/literal' },
|
|
{ title: 'Identifier', slug: 'language/expression/identifier' },
|
|
{ title: 'Path Reference', slug: 'language/expression/path-reference' },
|
|
{ title: 'Object', slug: 'language/expression/object' },
|
|
{ title: 'Array', slug: 'language/expression/array' },
|
|
{ title: 'Function', slug: 'language/expression/function' },
|
|
{ title: 'Function Call', slug: 'language/expression/function-call' },
|
|
{ title: 'Let', slug: 'language/expression/let' },
|
|
{ title: 'Match', slug: 'language/expression/match' },
|
|
{ title: 'Import', slug: 'language/expression/import' },
|
|
{ title: 'Composition', slug: 'language/expression/composition' },
|
|
{ title: 'Default', slug: 'language/expression/default' },
|
|
{ title: 'Arithmetic', slug: 'language/expression/arithmetic' },
|
|
{ title: 'String Interpolation', slug: 'language/expression/string-interpolation' },
|
|
],
|
|
},
|
|
{ title: 'Constraints and Defaults', slug: 'language/constraints-and-defaults' },
|
|
{ title: 'Composition Operators', slug: 'language/operators' },
|
|
{ title: 'Functions', slug: 'language/functions' },
|
|
{ title: 'Modules and Imports', slug: 'language/modules-and-imports' },
|
|
{ title: 'Evaluation Semantics', slug: 'language/evaluation' },
|
|
{ title: 'Materialization and Errors', slug: 'language/materialization-and-errors' },
|
|
{ title: 'Naming', slug: 'language/naming' },
|
|
{ title: 'Examples', slug: 'language/examples' },
|
|
],
|
|
},
|
|
{
|
|
title: 'Implementation Design',
|
|
slug: 'design',
|
|
children: [
|
|
{ title: 'Execution Pipeline', slug: 'design/execution-pipeline' },
|
|
{ title: 'Runtime Model', slug: 'design/runtime-model' },
|
|
{ title: 'Thunk and Lazy Evaluation', slug: 'design/thunk-and-lazy-evaluation' },
|
|
{ title: 'Composition and Materialization', slug: 'design/composition-and-materialization' },
|
|
{ title: 'Diagnostics and Fallback', slug: 'design/diagnostics-and-fallback' },
|
|
{ title: 'Embedding API', slug: 'design/embedding-api' },
|
|
{ title: 'Features', slug: 'design/features' },
|
|
],
|
|
},
|
|
{ title: 'Development', slug: 'development' },
|
|
{ title: 'Open Issues', slug: 'open-issues' },
|
|
];
|
|
|
|
const renderer = new marked.Renderer();
|
|
|
|
renderer.code = (code, language = '') => {
|
|
const normalizedLanguage = language.split(/\s+/)[0] ?? '';
|
|
const className = normalizedLanguage ? ` class="language-${escapeAttribute(normalizedLanguage)}"` : '';
|
|
return `<pre class="code-block"><code${className}>${highlightCode(code, normalizedLanguage)}</code></pre>`;
|
|
};
|
|
|
|
renderer.codespan = (code) => `<code>${escapeHtml(code)}</code>`;
|
|
|
|
marked.setOptions({ gfm: true, renderer });
|
|
|
|
export function allDocSlugs() {
|
|
return Object.keys(docs).filter((slug) => slug !== 'index');
|
|
}
|
|
|
|
export function renderMarkdown(slug) {
|
|
const source = docs[slug] ?? docs.index;
|
|
const html = marked.parse(source ?? '# Not found\n');
|
|
return html.replace(/href="([^"#][^"]*)\.md(#[^"]*)?"/g, (_all, href, hash = '') => {
|
|
const target = normalizeDocLink(slug, href);
|
|
return `href="/docs/${target}/${hash}"`;
|
|
});
|
|
}
|
|
|
|
function normalizeDocLink(currentSlug, href) {
|
|
const base = currentSlug.includes('/') ? currentSlug.split('/').slice(0, -1) : [];
|
|
const parts = [...base, ...href.split('/')];
|
|
const out = [];
|
|
for (const part of parts) {
|
|
if (!part || part === '.') continue;
|
|
if (part === '..') out.pop();
|
|
else out.push(part);
|
|
}
|
|
if (out[out.length - 1] === 'index') out.pop();
|
|
return out.join('/') || 'index';
|
|
}
|