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
12 changes: 9 additions & 3 deletions apps/svelte.dev/src/routes/content.json/+server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Tokens } from 'marked';
import { index, docs as _docs, examples } from '$lib/server/content';
import { json } from '@sveltejs/kit';
import { transform, slugify, clean } from '@sveltejs/site-kit/markdown';
import { transform, slugify, clean, create_slug_deduper } from '@sveltejs/site-kit/markdown';
import type { Block } from '@sveltejs/site-kit/search';
import { get_slug } from '../tutorial/[...slug]/content.server';

Expand Down Expand Up @@ -38,6 +38,8 @@ async function content() {
const intro = sections?.shift()?.trim()!;
const rank = +metadata.rank;

const dedupe = create_slug_deduper();

blocks.push({
breadcrumbs: [...breadcrumbs, clean(metadata.title ?? '')],
href: get_href([slug]),
Expand All @@ -53,13 +55,15 @@ async function content() {
continue;
}

const h2_slug = dedupe(slugify(h2));

const content = lines.join('\n');
const subsections = content.trim().split('## ');
const intro = subsections?.shift()?.trim();
if (intro) {
blocks.push({
breadcrumbs: [...breadcrumbs, clean(metadata.title), clean(h2)],
href: get_href([slug, slugify(h2)]),
href: get_href([slug, h2_slug]),
content: await plaintext(intro),
rank
});
Expand All @@ -73,9 +77,11 @@ async function content() {
continue;
}

const h3_slug = dedupe(`${h2_slug}-${slugify(h3)}`);

blocks.push({
breadcrumbs: [...breadcrumbs, clean(metadata.title), clean(h2), clean(h3)],
href: get_href([slug, slugify(h2) + '-' + slugify(h3)]),
href: get_href([slug, h3_slug]),
content: await plaintext(lines.join('\n').trim()),
rank
});
Expand Down
2 changes: 1 addition & 1 deletion packages/site-kit/src/lib/markdown/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { render_content_markdown } from './renderer.ts';

export { transform, slugify, clean, strip_origin } from './utils.ts';
export { transform, slugify, clean, strip_origin, create_slug_deduper } from './utils.ts';

// TODO none of these really belong here
export type Modules = Array<{
Expand Down
13 changes: 11 additions & 2 deletions packages/site-kit/src/lib/markdown/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { transformerTwoslash, rendererRich } from '@shikijs/twoslash';
import { createFileSystemTypesCache } from '@shikijs/vitepress-twoslash/cache-fs';
import { compress_and_encode_text } from 'gzip';
import {
create_slug_deduper,
decode_html_entities,
SHIKI_LANGUAGE_MAP,
slugify,
Expand Down Expand Up @@ -329,6 +330,7 @@ export async function render_content_markdown(
twoslashBanner?: TwoslashBanner
) {
const headings: string[] = [];
const dedupe_slug = create_slug_deduper();
const { check = true, references } = options ?? {};

interface CodeBlockFile {
Expand Down Expand Up @@ -598,9 +600,16 @@ export async function render_content_markdown(
const text = this.parser!.parseInline(tokens);
const html = text.replace(/<\/?code>/g, '');

headings[depth - 1] = slugify(text);
const parent = headings
.slice(0, depth - 1)
.filter(Boolean)
.join('-');
const raw_segment = slugify(text);
const slug = dedupe_slug(parent ? `${parent}-${raw_segment}` : raw_segment);
const segment = parent ? slug.slice(parent.length + 1) : slug;

headings[depth - 1] = segment;
headings.length = depth;
const slug = headings.filter(Boolean).join('-');

return `<h${depth} id="${slug}"><span>${html}</span><a href="#${slug}" class="permalink" aria-label="permalink"></a></h${depth}>`;
},
Expand Down
9 changes: 9 additions & 0 deletions packages/site-kit/src/lib/markdown/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ export const slugify = (str: string) => {
);
};

export function create_slug_deduper() {
const seen = new Map<string, number>();
return (slug: string) => {
const count = seen.get(slug) ?? 0;
seen.set(slug, count + 1);
return count > 0 ? `${slug}-${count}` : slug;
};
}

export function smart_quotes(
str: string,
{ first = true, html = false }: { first?: boolean; html?: boolean } = {}
Expand Down
17 changes: 14 additions & 3 deletions packages/site-kit/src/lib/server/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { extract_frontmatter, is_in_code_block, slugify, smart_quotes } from '../../markdown/utils';
import {
create_slug_deduper,
extract_frontmatter,
is_in_code_block,
slugify,
smart_quotes
} from '../../markdown/utils';
import type { Document, Section } from '../../types';

export async function create_index(
Expand Down Expand Up @@ -30,6 +36,8 @@ export async function create_index(
'<code>$1</code>'
);

const dedupe = create_slug_deduper();

const sections = Array.from(body.matchAll(/^#{2,3}\s+(.*)$/gm)).reduce((arr, match) => {
if (is_in_code_block(body, match.index || 0)) return arr;
const title = match[1];
Expand All @@ -43,10 +51,13 @@ export async function create_index(
if (match[0].startsWith('###')) {
const section = arr.at(-1);
if (section) {
section.subsections.push({ slug: `${section.slug}-${slug}`, title: displayed_title });
section.subsections.push({
slug: dedupe(`${section.slug}-${slug}`),
title: displayed_title
});
}
} else {
arr.push({ slug, title: displayed_title, subsections: [] });
arr.push({ slug: dedupe(slug), title: displayed_title, subsections: [] });
}

return arr;
Expand Down