Skip to content

Commit

Permalink
feat: markdown documentation PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
ppvg committed Jan 27, 2025
1 parent 125b750 commit a7fe3d7
Show file tree
Hide file tree
Showing 17 changed files with 521 additions and 581 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ updates:
groups:
build:
patterns:
- mdsvex
- sass
- "svelte"
- "svelte-*"
Expand Down
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"mdsvex": "^0.12.3",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"sass": "^1.83.4",
Expand Down
41 changes: 34 additions & 7 deletions docs/src/lib/breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Breadcrumb, BreadcrumbNames } from "./types";

/**
* Get the breadcrumbs for the given route.
*/
export function getBreadcrumbs(
routeId: string | null,
breadcrumbNames: BreadcrumbNames,
Expand All @@ -17,15 +20,22 @@ export function getBreadcrumbs(
return breadcrumbs;
}

/**
* Import breadcrumb names from page modules.
*
* Supports both `export const breadcrumb` from +page.svelte files and the
* `title` and `breadcrumb` frontmatter properties from +page.md files.
*
* Resolves to an object mapping routes to breadcrumb names.
*/
export async function loadBreadcrumbNames(): Promise<BreadcrumbNames> {
const modules = import.meta.glob("../routes/**/+page.svelte");
const modules = import.meta.glob("../routes/**/+page.*");
const names: BreadcrumbNames = {};
await Promise.all(
Object.entries(modules).map(async ([key, value]) => {
key = routeFromModulePath(key);
if (!key) return;
const name = (await nameFromModule(value)) || nameFromKey(key);
names[key] = name;
names[key] = nameFromModule(await value()) || nameFromKey(key);
}),
);
return names;
Expand All @@ -34,7 +44,7 @@ export async function loadBreadcrumbNames(): Promise<BreadcrumbNames> {
const routeSegmentPattern = /\/[^+.(\/]+/g;

/**
* Strip the "../routes/" prefix, "/+page.svelte" suffix, and any route
* Strip the "../routes/" prefix, "/+page.*" suffix, and any route
* "(group)/"s from the module path.
*/
function routeFromModulePath(path: string): string {
Expand All @@ -48,9 +58,26 @@ function routeFromModulePath(path: string): string {
return route;
}

async function nameFromModule(mod: () => Promise<any>): Promise<string> {
const name = (await mod()).breadcrumb;
return typeof name === "string" ? name : "";
function nameFromModule(mod: unknown): string | undefined {
if (typeof mod !== "object") {
return undefined;
}

// Try getting `breadcrumb` export from svelte page component
if (typeof (mod as any).breadcrumb === "string") {
return (mod as any).breadcrumb;
}

// Try getting frontmatter `breadcrumb` or `title` from markdown page
if (typeof (mod as any).metadata !== "object") {
return undefined;
}
if (typeof ((mod as any).metadata as any).breadcrumb === "string") {
return ((mod as any).metadata as any).breadcrumb;
}
if (typeof ((mod as any).metadata as any).title === "string") {
return ((mod as any).metadata as any).title;
}
}

function nameFromKey(key: string): string {
Expand Down
29 changes: 29 additions & 0 deletions docs/src/lib/markdown-nav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { base } from "$app/paths";

export type MarkdownNav = Array<MarkdownNavItem>;
export type MarkdownNavItem = Record<string, string | MarkdownNav>;
export type ParsedNav = Array<ParsedNavItem>;
export type ParsedNavItem = { link: Link; children?: ParsedNav };
export type Link = { name: string; href: string };

export function parseNav(list?: MarkdownNav): ParsedNav | undefined {
if (!list || !list.length) return;
const result: ParsedNav = [];
for (const item of list) {
let link, children;
for (let [key, value] of Object.entries(item)) {
if (typeof value === "string") {
const href = value.charAt(0) === "#" ? value : `${base}${value}`;
link = { name: key, href };
} else if (Array.isArray(value)) {
children = parseNav(value);
}
}
if (link) {
result.push({ link, children });
} else {
console.error(`Invalid nav list: ${JSON.stringify(item)}`);
}
}
if (result.length) return result;
}
11 changes: 11 additions & 0 deletions docs/src/markdown/components/A.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import { base } from "$app/paths";
import type { HTMLAnchorAttributes } from "svelte/elements";
let { href, children, ...rest }: HTMLAnchorAttributes = $props();
let rebasedHref = $derived(href && href.startsWith("/") ? `${base}${href}` : href);
</script>

<a href={rebasedHref} {...rest}>
{@render children?.()}
</a>
41 changes: 41 additions & 0 deletions docs/src/markdown/layouts/default.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script module lang="ts">
export { default as Code } from "$lib/Code.svelte";
export { default as a } from "../components/A.svelte";
</script>

<script lang="ts">
import type { Snippet } from "svelte";
import { parseNav, type MarkdownNav, type ParsedNav } from "$lib/markdown-nav";
import SideMenu from "$lib/SideMenu.svelte";
interface Props {
title?: string;
nav?: MarkdownNav;
children?: Snippet;
}
let { title, nav, children }: Props = $props();
let parsedNav = $derived(parseNav(nav));
</script>

<svelte:head>
<title>{title || "Manon"}</title>
</svelte:head>

{#snippet navlist(items: ParsedNav)}
<ul>
{#each items as { link, children }}
<li><a href={link.href}>{link.name}</a></li>
{#if children}{@render navlist(children)}{/if}
{/each}
</ul>
{/snippet}

<main class:sidemenu={!!parsedNav} id="main-content" tabindex="-1">
{#if parsedNav}
<SideMenu>{@render navlist(parsedNav)}</SideMenu>
{/if}
<section class="visually-grouped">
{@render children?.()}
</section>
</main>
7 changes: 7 additions & 0 deletions docs/src/md.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module '*.md' {
import type { SvelteComponent } from 'svelte'

export default class Comp extends SvelteComponent{}

export const metadata: Record<string, unknown>
}
131 changes: 0 additions & 131 deletions docs/src/routes/components/button-base/+page.svelte

This file was deleted.

Loading

0 comments on commit a7fe3d7

Please sign in to comment.