"astro": "^6.4.4"
},
"devDependencies": {
+ "@types/js-yaml": "^4.0.9",
+ "js-yaml": "^4.2.0",
"sass": "^1.100.0"
},
"engines": {
"@types/unist": "*"
}
},
+ "node_modules/@types/js-yaml": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
"astro": "^6.4.4"
},
"devDependencies": {
+ "@types/js-yaml": "^4.0.9",
+ "js-yaml": "^4.2.0",
"sass": "^1.100.0"
}
}
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
+import { load as yamlLoad } from 'js-yaml';
+import { readFileSync, readdirSync } from 'node:fs';
+import { resolve, relative, dirname, basename } from 'node:path';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
}),
});
-export const collections = { blog };
+const projectPageSchema = z.object({
+ title: z.string(),
+ weight: z.number().default(999),
+ url: z.string(),
+ layout: z.string().optional(),
+ outputs: z.array(z.string()).optional(),
+ params: z.object({
+ current: z.boolean(),
+ canonical: z.string().optional(),
+ }),
+});
+
+function findHtmlFiles(dir: string, baseDir: string): string[] {
+ const results: string[] = [];
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
+ const fullPath = resolve(dir, entry.name);
+ if (entry.isDirectory()) {
+ results.push(...findHtmlFiles(fullPath, baseDir));
+ } else if (entry.name.endsWith('.html')) {
+ results.push(relative(baseDir, fullPath));
+ }
+ }
+ return results;
+}
+
+function parseHtmlWithFrontmatter(content: string): { data: Record<string, unknown>; body: string } {
+ if (!content.startsWith('---')) return { data: {}, body: content };
+ const end = content.indexOf('\n---', 3);
+ if (end === -1) return { data: {}, body: content };
+ const frontmatter = content.slice(3, end).trim();
+ const body = content.slice(end + 4).trim();
+ return { data: yamlLoad(frontmatter) as Record<string, unknown>, body };
+}
+
+const projects = defineCollection({
+ loader: {
+ name: 'project-pages-loader',
+ load: async ({ store, parseData, logger }) => {
+ const baseDir = resolve('./src/content/projects');
+ let files: string[];
+ try {
+ files = findHtmlFiles(baseDir, baseDir);
+ } catch {
+ logger.warn('No src/content/projects/ directory found — skipping projects loader');
+ return;
+ }
+
+ for (const relPath of files) {
+ const fullPath = resolve(baseDir, relPath);
+ const raw = readFileSync(fullPath, 'utf-8');
+ const { data, body } = parseHtmlWithFrontmatter(raw);
+
+ // Normalise id: use forward slashes, strip leading ./
+ const id = relPath.replace(/\\/g, '/');
+
+ try {
+ const parsed = await parseData({ id, data });
+ store.set({ id, data: parsed, body });
+ } catch (e) {
+ logger.warn(`Skipping ${id}: ${e}`);
+ }
+ }
+ },
+ },
+ schema: projectPageSchema,
+});
+
+export const collections = { blog, projects };
--- /dev/null
+---
+title: Hibernate Maven Plugin
+weight: 0
+outputs:
+ - html
+url: /hibernate-maven-plugin/
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<h1>Hibernate Maven Plugin</h1><section>
+<h2>A simple plugin for generating a database-schema from Hibernate-Mappings</h2>
+<p>The <b>hibernate-maven-plugin</b> is a plugin for generating a database-schema
+from your Hibernate-Mappings and create or update your database accordingly.
+Its main usage is to automatically create and populate a test-database for
+unit-tests in cooperation with the
+<a class="externalLink" href="http://mojo.codehaus.org/dbunit-maven-plugin">dbunit-maven-plugin</a>.</p>
+<p>The plugin was designed with three main goals in mind:</p>
+<ul>
+<li>It should be easy to use.</li>
+<li>It should be maximal unlikely, to erase a producation-database by accident.</li>
+<li>It should not slow down the development cycle.</li>
+</ul>
+<p>To achieve the first goal, the convention-over-configuration paradigma was applied
+and the plugin was stuffed with usefull logging-messages. So, if in doubt, just turn
+on the <a href="./debugging.html">debugging output</a> with the <code>mvn -X ...</code>.</p>
+<p>To achieve the second goal, the precedence in which the configuration locations are
+consulted was layouted in a way that makes it possible, to prevent overwrites of the
+wrong database by accident.</p>
+<p>Last but not least, in order to not slow down the development cycle, the
+hibernate-maven-plugin only executes the generated SQL, if the mapping or the
+configuration has changed (or if you force it to do so).</p>
+</section><section>
+<h2>Documentation</h2>
+<ul>
+<li>See <a href="./configuration.html">Configuration Examples</a> for Usage-Explanations
+and simple examples of how to use this plugin.</li>
+<li>See <a href="./create-mojo.html">hibernate:create</a>,
+<a href="./update-mojo.html">hibernate:update</a>,
+<a href="./drop-mojo.html">hibernate:drop</a> and
+<a href="./help-mojo.html">hibernate:help</a> and
+<a href="./plugin-info.html">Plugin Documentation</a> for the full autogenerated documentation.</li>
+</ul>
+</section><section>
+<h2>Releases</h2>
+<ul>
+<li><a href="/hibernate-maven-plugin/">current version</a></li>
+<li>2.1.1 (this version)</li>
+<li><a href="/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/">2.1.2-SNAPSHOT</a></li>
+<li><a href="/projects/hibernate-maven-plugin/2.1.0/">2.1.0</a></li>
+</ul>
+</section>
+</div>
--- /dev/null
+---
+title: Configuration Examples
+weight: 20
+outputs:
+ - html
+url: /hibernate-maven-plugin/configuration.html
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<h1>Configuration Examples</h1><section>
+<h2>Configuration Through A Configuration-File</h2>
+<p>The most simple way to configure the plugin is, to put all the hibernate-configuration
+in a <b>hibernate.properties</b>- or a <b>hibernate.cfg.xml</b>-file on your classpath
+or in the <b>persistence.xml</b>-file of your JPA-configuration, just like you would
+do, if you are not using the hibernate-maven-plugin at all.</p>
+<p>In that case, the only configuration needed in the pom.xml is:</p>
+<pre><plugin>
+ <groupId>de.juplo</groupId>
+ <artifactId>hibernate-maven-plugin</artifactId>
+ <version>2.1.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>create</goal>
+ </goals>
+ </execution>
+ </executions>
+</plugin></pre>
+</section>
+</div>
--- /dev/null
+---
+title: Project Information
+weight: 1
+outputs:
+ - html
+url: /hibernate-maven-plugin/project-info.html
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<section>
+<h2>Project Information</h2>
+<p>This document provides an overview of the various documents and links that are part
+of this project's general information. All of this content is automatically generated
+by <a class="externalLink" href="http://maven.apache.org">Maven</a> on behalf of the project.</p>
+<section>
+<h3>Overview</h3>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Document</th><th>Description</th></tr>
+<tr class="b"><td><a href="ci-management.html">CI Management</a></td>
+<td>This document lists the continuous integration management system of this project.</td></tr>
+<tr class="a"><td><a href="dependencies.html">Dependencies</a></td>
+<td>This document lists the project's dependencies and provides information on each dependency.</td></tr>
+<tr class="b"><td><a href="licenses.html">Licenses</a></td>
+<td>This document lists the project license(s).</td></tr>
+<tr class="a"><td><a href="team.html">Team</a></td>
+<td>This document provides information on the members of this project.</td></tr>
+</table>
+</section>
+</section>
+</div>
--- /dev/null
+---
+title: Project Reports
+weight: 12
+outputs:
+ - html
+url: /hibernate-maven-plugin/project-reports.html
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<section>
+<h2>Generated Reports</h2>
+<p>This document provides an overview of the various reports that are automatically
+generated by <a class="externalLink" href="http://maven.apache.org">Maven</a>.</p>
+<section>
+<h3>Overview</h3>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Document</th><th>Description</th></tr>
+<tr class="b"><td><a href="/projects/hibernate-maven-plugin/2.1.1/apidocs/">JavaDocs</a></td>
+<td>This document contains the JavaDoc API documentation.</td></tr>
+<tr class="a"><td><a href="/projects/hibernate-maven-plugin/2.1.1/testapidocs/">Test JavaDocs</a></td>
+<td>This document contains the test JavaDoc API documentation.</td></tr>
+<tr class="b"><td><a href="plugin-info.html">Plugin Documentation</a></td>
+<td>This document lists the plugins goals and their parameters.</td></tr>
+</table>
+</section>
+</section>
+</div>
--- /dev/null
+---
+title: Plugin Documentation
+weight: 19
+outputs:
+ - html
+url: /hibernate-maven-plugin/plugin-info.html
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<section>
+<h2>Plugin Documentation</h2>
+<p>Goals available for this plugin:</p>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Goal</th><th>Description</th></tr>
+<tr class="b"><td><a href="create-mojo.html">hibernate:create</a></td>
+<td>Creates the database schema using Hibernate.</td></tr>
+<tr class="a"><td><a href="drop-mojo.html">hibernate:drop</a></td>
+<td>Drops the database schema using Hibernate.</td></tr>
+<tr class="b"><td><a href="update-mojo.html">hibernate:update</a></td>
+<td>Updates the database schema using Hibernate.</td></tr>
+<tr class="a"><td><a href="help-mojo.html">hibernate:help</a></td>
+<td>Display help information on hibernate-maven-plugin.</td></tr>
+</table>
+</section>
+</div>
--- /dev/null
+---
+title: "Goal — hibernate:create"
+weight: 21
+outputs:
+ - html
+url: /hibernate-maven-plugin/create-mojo.html
+layout: article
+params:
+ current: true
+---
+<div id="sili-body">
+<section>
+<h2>hibernate:create</h2>
+<p><b>Full name:</b> de.juplo:hibernate-maven-plugin:2.1.1:create</p>
+<p><b>Description:</b> Creates the database-schema from Hibernate-Mappings.
+This goal executes the generated DDL-SQL, if the mapping or the configuration
+has changed since the last run (or if forced).</p>
+<p><b>Attributes:</b></p>
+<ul>
+<li>Requires a Maven project to be executed.</li>
+<li>Executes as an aggregator goal.</li>
+<li>Binds by default to the lifecycle phase: <code>process-test-resources</code>.</li>
+</ul>
+</section>
+</div>
--- /dev/null
+---
+title: Hibernate Maven Plugin
+weight: 0
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/
+---
+<div id="sili-body">
+<h1>Hibernate Maven Plugin</h1>
+<p><b>This is a development snapshot (2.1.2-SNAPSHOT).</b>
+The current stable release is <a href="/hibernate-maven-plugin/">2.1.1</a>.</p>
+<section>
+<h2>A simple plugin for generating a database-schema from Hibernate-Mappings</h2>
+<p>The <b>hibernate-maven-plugin</b> is a plugin for generating a database-schema
+from your Hibernate-Mappings and create or update your database accordingly.
+Its main usage is to automatically create and populate a test-database for
+unit-tests in cooperation with the
+<a class="externalLink" href="http://mojo.codehaus.org/dbunit-maven-plugin">dbunit-maven-plugin</a>.</p>
+</section><section>
+<h2>Releases</h2>
+<ul>
+<li><a href="/hibernate-maven-plugin/">current version (2.1.1)</a></li>
+<li>2.1.2-SNAPSHOT (this version)</li>
+</ul>
+</section>
+</div>
--- /dev/null
+---
+title: Configuration Examples
+weight: 20
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/configuration.html
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/configuration.html
+---
+<div id="sili-body">
+<h1>Configuration Examples</h1><section>
+<h2>Configuration Through A Configuration-File</h2>
+<p>The most simple way to configure the plugin is, to put all the hibernate-configuration
+in a <b>hibernate.properties</b>- or a <b>hibernate.cfg.xml</b>-file on your classpath
+or in the <b>persistence.xml</b>-file of your JPA-configuration, just like you would
+do, if you are not using the hibernate-maven-plugin at all.</p>
+<p>In that case, the only configuration needed in the pom.xml is:</p>
+<pre><plugin>
+ <groupId>de.juplo</groupId>
+ <artifactId>hibernate-maven-plugin</artifactId>
+ <version>2.1.2-SNAPSHOT</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>create</goal>
+ </goals>
+ </execution>
+ </executions>
+</plugin></pre>
+</section>
+</div>
--- /dev/null
+---
+title: Project Information
+weight: 1
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-info.html
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/project-info.html
+---
+<div id="sili-body">
+<section>
+<h2>Project Information</h2>
+<p>This document provides an overview of the various documents and links that are part
+of this project's general information. All of this content is automatically generated
+by <a class="externalLink" href="http://maven.apache.org">Maven</a> on behalf of the project.</p>
+<section>
+<h3>Overview</h3>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Document</th><th>Description</th></tr>
+<tr class="b"><td><a href="ci-management.html">CI Management</a></td>
+<td>This document lists the continuous integration management system of this project.</td></tr>
+<tr class="a"><td><a href="dependencies.html">Dependencies</a></td>
+<td>This document lists the project's dependencies and provides information on each dependency.</td></tr>
+<tr class="b"><td><a href="licenses.html">Licenses</a></td>
+<td>This document lists the project license(s).</td></tr>
+<tr class="a"><td><a href="team.html">Team</a></td>
+<td>This document provides information on the members of this project.</td></tr>
+</table>
+</section>
+</section>
+</div>
--- /dev/null
+---
+title: Project Reports
+weight: 12
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/project-reports.html
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/project-reports.html
+---
+<div id="sili-body">
+<section>
+<h2>Generated Reports</h2>
+<p>This document provides an overview of the various reports that are automatically
+generated by <a class="externalLink" href="http://maven.apache.org">Maven</a>.</p>
+<section>
+<h3>Overview</h3>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Document</th><th>Description</th></tr>
+<tr class="b"><td><a href="/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/apidocs/">JavaDocs</a></td>
+<td>This document contains the JavaDoc API documentation.</td></tr>
+<tr class="a"><td><a href="/projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/testapidocs/">Test JavaDocs</a></td>
+<td>This document contains the test JavaDoc API documentation.</td></tr>
+<tr class="b"><td><a href="plugin-info.html">Plugin Documentation</a></td>
+<td>This document lists the plugins goals and their parameters.</td></tr>
+</table>
+</section>
+</section>
+</div>
--- /dev/null
+---
+title: Plugin Documentation
+weight: 19
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/plugin-info.html
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/plugin-info.html
+---
+<div id="sili-body">
+<section>
+<h2>Plugin Documentation</h2>
+<p>Goals available for this plugin:</p>
+<table border="0" class="bodyTable">
+<tr class="a"><th>Goal</th><th>Description</th></tr>
+<tr class="b"><td><a href="create-mojo.html">hibernate:create</a></td>
+<td>Creates the database schema using Hibernate.</td></tr>
+<tr class="a"><td><a href="drop-mojo.html">hibernate:drop</a></td>
+<td>Drops the database schema using Hibernate.</td></tr>
+<tr class="b"><td><a href="update-mojo.html">hibernate:update</a></td>
+<td>Updates the database schema using Hibernate.</td></tr>
+<tr class="a"><td><a href="help-mojo.html">hibernate:help</a></td>
+<td>Display help information on hibernate-maven-plugin.</td></tr>
+</table>
+</section>
+</div>
--- /dev/null
+---
+title: "Goal — hibernate:create"
+weight: 21
+outputs:
+ - html
+url: /projects/hibernate-maven-plugin/2.1.2-SNAPSHOT/create-mojo.html
+layout: article
+params:
+ current: false
+ canonical: /hibernate-maven-plugin/create-mojo.html
+---
+<div id="sili-body">
+<section>
+<h2>hibernate:create</h2>
+<p><b>Full name:</b> de.juplo:hibernate-maven-plugin:2.1.2-SNAPSHOT:create</p>
+<p><b>Description:</b> Creates the database-schema from Hibernate-Mappings.</p>
+</section>
+</div>
--- /dev/null
+import { getCollection, type CollectionEntry } from 'astro:content';
+import { readdirSync } from 'node:fs';
+import { resolve } from 'node:path';
+
+export type ProjectPage = CollectionEntry<'projects'>;
+
+export interface ProjectNavNode {
+ title: string;
+ url: string;
+ weight: number;
+ isNode: boolean; // true when this entry is a section (_index.html)
+ children: ProjectNavNode[];
+}
+
+export interface ProjectInfo {
+ slug: string; // e.g. 'hibernate-maven-plugin'
+ version: string; // e.g. '2.1.1' or '2.1.2-SNAPSHOT'
+ isCurrent: boolean;
+ navRoot: ProjectNavNode;
+}
+
+// Detect generated-content dirs (apidocs, xref, etc.) in public/projects/X/Y/
+const GENERATED_DOC_NAMES: Record<string, string> = {
+ apidocs: 'JavaDocs',
+ testapidocs: 'Test JavaDocs',
+ xref: 'Source Xref',
+ 'xref-test': 'Test Source Xref',
+};
+
+function generatedDocNodes(projectSlug: string, version: string, isCurrent: boolean): ProjectNavNode[] {
+ const baseUrl = `/projects/${projectSlug}/${version}/`;
+ const nodes: ProjectNavNode[] = [];
+ const pubDir = resolve(`./public/projects/${projectSlug}/${version}`);
+ let entries: string[] = [];
+ try { entries = readdirSync(pubDir); } catch { return []; }
+ for (const [dirName, title] of Object.entries(GENERATED_DOC_NAMES)) {
+ if (entries.includes(dirName)) {
+ nodes.push({ title, url: `${baseUrl}${dirName}/`, weight: -1, isNode: false, children: [] });
+ }
+ }
+ return nodes;
+}
+
+// Build a nav tree for a single version from the collection entries.
+// The hierarchy is derived from the filesystem path (id):
+// X/Y/_index.html → root node
+// X/Y/page.html → direct child of root
+// X/Y/sub/_index.html → direct child of root (section node)
+// X/Y/sub/page.html → child of sub
+function buildNavTree(
+ pages: ProjectPage[],
+ projectSlug: string,
+ version: string,
+): ProjectNavNode | null {
+ // Find the root entry (_index.html at version level)
+ const rootEntry = pages.find(p => p.id === `${projectSlug}/${version}/_index.html`);
+ if (!rootEntry) return null;
+
+ function makeNode(entry: ProjectPage): ProjectNavNode {
+ const idPath = entry.id; // e.g. "hibernate-maven-plugin/2.1.1/project-reports/plugin-info/_index.html"
+ const versionPrefix = `${projectSlug}/${version}/`;
+ const rel = idPath.slice(versionPrefix.length); // e.g. "project-reports/plugin-info/_index.html"
+ const isNode = rel.endsWith('/_index.html') || rel === '_index.html';
+
+ // Find direct children: entries whose path is one level deeper inside the same dir
+ const relDir = isNode
+ ? (rel === '_index.html' ? '' : rel.replace('/_index.html', '/'))
+ : null;
+
+ const children: ProjectNavNode[] = [];
+ if (relDir !== null) {
+ for (const child of pages) {
+ if (child.id === entry.id) continue;
+ const childRel = child.id.slice(versionPrefix.length);
+ if (!childRel.startsWith(relDir)) continue;
+ const remainder = childRel.slice(relDir.length); // e.g. "sub/_index.html" or "page.html"
+ // Direct child: either "page.html" (no slash) or "sub/_index.html" (one slash, ends with _index.html)
+ const isDirectChild =
+ (!remainder.includes('/')) ||
+ (remainder.indexOf('/') === remainder.lastIndexOf('/') && remainder.endsWith('/_index.html'));
+ if (isDirectChild) {
+ children.push(makeNode(child));
+ }
+ }
+ // Add generated-content nodes under root or under project-reports
+ const isReports = rel === 'project-reports/_index.html' || rel === '_index.html';
+ if (rel === 'project-reports/_index.html') {
+ children.push(...generatedDocNodes(projectSlug, version, entry.data.params.current));
+ }
+ }
+
+ children.sort((a, b) => a.weight - b.weight);
+
+ return {
+ title: entry.data.title,
+ url: entry.data.url,
+ weight: entry.data.weight,
+ isNode,
+ children,
+ };
+ }
+
+ return makeNode(rootEntry);
+}
+
+let _cachedPages: ProjectPage[] | null = null;
+
+export async function getAllProjectPages(): Promise<ProjectPage[]> {
+ if (!_cachedPages) _cachedPages = await getCollection('projects');
+ return _cachedPages;
+}
+
+// Returns ProjectInfo for the version the user is currently browsing,
+// or null if currentUrl is not a project page.
+export async function getActiveProject(currentUrl: string): Promise<ProjectInfo | null> {
+ const pages = await getAllProjectPages();
+
+ // Non-visible: URL matches /projects/X/Y/...
+ const nonVisibleMatch = currentUrl.match(/^\/projects\/([^/]+)\/([^/]+)\//);
+ if (nonVisibleMatch) {
+ const [, projectSlug, version] = nonVisibleMatch;
+ const versionPages = pages.filter(p => p.id.startsWith(`${projectSlug}/${version}/`));
+ if (versionPages.length === 0) return null;
+ const navRoot = buildNavTree(versionPages, projectSlug, version);
+ if (!navRoot) return null;
+ return { slug: projectSlug, version, isCurrent: false, navRoot };
+ }
+
+ // Visible: currentUrl matches the url of a current: true page
+ const visibleRoot = pages.find(
+ p => p.data.params.current && p.id.endsWith('/_index.html') && p.data.url === currentUrl
+ );
+ if (visibleRoot) {
+ const [projectSlug, version] = visibleRoot.id.split('/');
+ const versionPages = pages.filter(p => p.id.startsWith(`${projectSlug}/${version}/`));
+ const navRoot = buildNavTree(versionPages, projectSlug, version);
+ if (!navRoot) return null;
+ return { slug: projectSlug, version, isCurrent: true, navRoot };
+ }
+
+ // Also handle sub-pages of a visible project
+ const visiblePage = pages.find(p => p.data.params.current && p.data.url === currentUrl);
+ if (visiblePage) {
+ const [projectSlug, version] = visiblePage.id.split('/');
+ const versionPages = pages.filter(p => p.id.startsWith(`${projectSlug}/${version}/`));
+ const navRoot = buildNavTree(versionPages, projectSlug, version);
+ if (!navRoot) return null;
+ return { slug: projectSlug, version, isCurrent: true, navRoot };
+ }
+
+ return null;
+}
+
+// Returns the nav tree for ALL visible projects (for the normal-case nav).
+export async function getVisibleProjectNavTrees(): Promise<ProjectInfo[]> {
+ const pages = await getAllProjectPages();
+ const seen = new Set<string>();
+ const result: ProjectInfo[] = [];
+
+ for (const page of pages) {
+ if (!page.data.params.current) continue;
+ if (!page.id.endsWith('/_index.html')) continue;
+ const parts = page.id.split('/');
+ if (parts.length < 3) continue; // must be X/Y/_index.html
+ const [projectSlug, version] = parts;
+ const key = `${projectSlug}/${version}`;
+ if (seen.has(key)) continue;
+ seen.add(key);
+ const versionPages = pages.filter(p => p.id.startsWith(`${key}/`));
+ const navRoot = buildNavTree(versionPages, projectSlug, version);
+ if (navRoot) result.push({ slug: projectSlug, version, isCurrent: true, navRoot });
+ }
+
+ return result;
+}