Skip to content

tidy up some stuff in site-kit #314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 10, 2024
Merged
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
14 changes: 2 additions & 12 deletions apps/svelte.dev/src/lib/server/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import { renderContentMarkdown } from '@sveltejs/site-kit/markdown';
import { render_content_markdown } from '@sveltejs/site-kit/markdown';

export const render_content = (filename: string, body: string) =>
renderContentMarkdown(filename, body, {
cacheCodeSnippets: true,

// TODO these didn't work for a while in the old sites, too, investigate bringing back this functionality at some point
// resolveTypeLinks: (module_name, type_name) => {
// return {
// page: `/docs/${slugify(module_name)}`,
// slug: `types-${slugify(type_name)}`
// };
// },

render_content_markdown(filename, body, {
twoslashBanner: (filename, source) => {
// TODO these are copied from Svelte and SvelteKit - adjust for new filenames
const injected = [];
Expand Down
21 changes: 8 additions & 13 deletions apps/svelte.dev/src/routes/content.json/+server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { index, docs as _docs, examples } from '$lib/server/content';
import { json } from '@sveltejs/kit';
import { markedTransform, normalizeSlugify, removeMarkdown } from '@sveltejs/site-kit/markdown';
import { transform, slugify, clean } 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 @@ -31,14 +31,14 @@ async function content() {

for (const document of docs) {
const { slug, body, metadata } = document;
const breadcrumbs = document.breadcrumbs.map((x) => removeMarkdown(x.title));
const breadcrumbs = document.breadcrumbs.map((x) => clean(x.title));

const sections = body.trim().split(/^## /m);
const intro = sections?.shift()?.trim()!;
const rank = +metadata.rank;

blocks.push({
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title ?? '')],
breadcrumbs: [...breadcrumbs, clean(metadata.title ?? '')],
href: get_href([slug]),
content: await plaintext(intro),
rank
Expand All @@ -57,8 +57,8 @@ async function content() {
const intro = subsections?.shift()?.trim();
if (intro) {
blocks.push({
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title), removeMarkdown(h2)],
href: get_href([slug, normalizeSlugify(h2)]),
breadcrumbs: [...breadcrumbs, clean(metadata.title), clean(h2)],
href: get_href([slug, slugify(h2)]),
content: await plaintext(intro),
rank
});
Expand All @@ -73,13 +73,8 @@ async function content() {
}

blocks.push({
breadcrumbs: [
...breadcrumbs,
removeMarkdown(metadata.title),
removeMarkdown(h2),
removeMarkdown(h3)
],
href: get_href([slug, normalizeSlugify(h2) + '-' + normalizeSlugify(h3)]),
breadcrumbs: [...breadcrumbs, clean(metadata.title), clean(h2), clean(h3)],
href: get_href([slug, slugify(h2) + '-' + slugify(h3)]),
content: await plaintext(lines.join('\n').trim()),
rank
});
Expand Down Expand Up @@ -107,7 +102,7 @@ async function plaintext(markdown: string) {
const inline = ({ text }: any) => text;

return (
await markedTransform(markdown, {
await transform(markdown, {
code: ({ text }) => {
const raw = text.split('// ---cut---\n').pop() ?? '';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'prismjs/components/prism-typescript.js';
import 'prism-svelte';
import { read } from '$app/server';
import { index } from '$lib/server/content';
import { markedTransform } from '@sveltejs/site-kit/markdown';
import { transform } from '@sveltejs/site-kit/markdown';
import type { Exercise, ExerciseStub, PartStub, Scope } from '$lib/tutorial';
import { error } from '@sveltejs/kit';
import { text_files } from './shared';
Expand Down Expand Up @@ -263,7 +263,7 @@ export async function load_exercise(slug: string): Promise<Exercise> {
prev: prev && { slug: prev.slug },
next,
markdown: exercise.body,
html: await markedTransform(exercise.body, {
html: await transform(exercise.body, {
...default_renderer,
codespan: ({ text }) =>
filenames.size > 1 && filenames.has(text)
Expand Down
12 changes: 3 additions & 9 deletions packages/site-kit/src/lib/markdown/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
export { render_content_markdown as renderContentMarkdown } from './renderer';
export { render_content_markdown } from './renderer';

export {
extract_frontmatter as extractFrontmatter,
transform as markedTransform,
normalizeSlugify,
slugify,
removeMarkdown,
escape
} from './utils';
export { transform, slugify, clean } from './utils';

// TODO none of these really belong here
export type Modules = Array<{
name?: string;
comment?: string;
Expand Down
67 changes: 5 additions & 62 deletions packages/site-kit/src/lib/markdown/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ts from 'typescript';
import * as prettier from 'prettier';
import { codeToHtml, createCssVariablesTheme } from 'shiki';
import { transformerTwoslash } from '@shikijs/twoslash';
import { SHIKI_LANGUAGE_MAP, escape, normalizeSlugify, smart_quotes, transform } from './utils';
import { SHIKI_LANGUAGE_MAP, slugify, smart_quotes, transform } from './utils';
import type { Modules } from './index';
import { fileURLToPath } from 'node:url';

Expand All @@ -23,13 +23,6 @@ type TwoslashBanner = (
options: SnippetOptions
) => string;

interface RenderContentOptions {
twoslashBanner?: TwoslashBanner;
modules?: Modules;
cacheCodeSnippets?: boolean;
resolveTypeLinks?: Parameters<typeof create_type_links>['1'];
}

// Supports js, svelte, yaml files
const METADATA_REGEX =
/(?:<!---\s*|\/\/\/\s*|###\s*)(?<key>file|link|copy):\s*(?<value>.*?)(?:\s*--->|$)\n/gm;
Expand Down Expand Up @@ -120,35 +113,18 @@ const theme = createCssVariablesTheme({
* @param {string} body
* @param {object} options
* @param {TwoslashBanner} [options.twoslashBanner] - A function that returns a string to be prepended to the code snippet before running the code with twoslash. Helps in adding imports from svelte or sveltekit or whichever modules are being globally referenced in all or most code snippets.
* @param {import('.').Modules} [options.modules] Module info generated from type-gen script. Used to create type links and type information blocks
* @param {boolean} [options.cacheCodeSnippets] Whether to cache code snippets or not. Defaults to true.
* @param {Parameters<typeof create_type_links>['1']} [options.resolveTypeLinks] Resolve types into its slugs(used on the page itself).
*/
export async function render_content_markdown(
filename: string,
body: string,
{
twoslashBanner,
modules = [],
cacheCodeSnippets = false,
resolveTypeLinks
}: RenderContentOptions = {}
{ twoslashBanner }: { twoslashBanner?: TwoslashBanner } = {}
) {
const { type_links, type_regex } = create_type_links(modules, resolveTypeLinks);
const snippets = await create_snippet_cache(cacheCodeSnippets);
const snippets = await create_snippet_cache(true);

const headings: string[] = [];

// this is a bit hacky, but it allows us to prevent type declarations
// from linking to themselves
let current = '';

return await transform(body, {
async walkTokens(token) {
if (token.type === 'heading') {
current = token.text;
}

if (token.type === 'code') {
if (snippets.get(token.text)) return;

Expand Down Expand Up @@ -199,25 +175,6 @@ export async function render_content_markdown(

html += '</div>';

// TODO this is currently disabled, we don't have access to `modules`
if (type_regex) {
type_regex.lastIndex = 0;

html = html.replace(type_regex, (match, prefix, name, pos, str) => {
const char_after = str.slice(pos + match.length, pos + match.length + 1);

if (!options.link || name === current || /(\$|\d|\w)/.test(char_after)) {
// we don't want e.g. RequestHandler to link to RequestHandler
return match;
}

const link = type_links?.get(name)
? `<a href="${type_links.get(name)?.relativeURL}">${name}</a>`
: '';
return `${prefix || ''}${link}`;
});
}

// Save everything locally now
snippets.save(token.text, html);
}
Expand All @@ -236,7 +193,7 @@ export async function render_content_markdown(
heading({ tokens, depth, raw }) {
const text = this.parser!.parseInline(tokens);

headings[depth - 1] = normalizeSlugify(raw);
headings[depth - 1] = slugify(raw);
headings.length = depth;
const slug = headings.filter(Boolean).join('-');
return `<h${depth} id="${slug}">${text.replace(
Expand All @@ -247,20 +204,6 @@ export async function render_content_markdown(
code({ text }) {
return snippets.get(text);
},
codespan({ text }) {
return (
'<code>' +
(type_regex
? text.replace(type_regex, (_, prefix, name) => {
const link = type_links?.get(name)
? `<a href="${type_links.get(name)?.relativeURL}">${name}</a>`
: '';
return `${prefix || ''}${link}`;
})
: text) +
'</code>'
);
},
blockquote(token) {
let content = this.parser?.parse(token.tokens) ?? '';
if (content.includes('[!LEGACY]')) {
Expand Down Expand Up @@ -756,7 +699,7 @@ async function syntax_highlight({

return {
type,
content: escape(content)
content: content.replace(/</g, '&lt;')
};
});

Expand Down
60 changes: 12 additions & 48 deletions packages/site-kit/src/lib/markdown/utils.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
import { Marked, Renderer, type TokenizerObject, type MarkedExtension } from 'marked';
import json5 from 'json5';

const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
const escapeReplacements = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};

/**
* @param {string} ch
*/
const getEscapeReplacement = (ch: string) =>
escapeReplacements[ch as keyof typeof escapeReplacements];

export const SHIKI_LANGUAGE_MAP = {
bash: 'bash',
env: 'bash',
Expand All @@ -33,30 +15,10 @@ export const SHIKI_LANGUAGE_MAP = {
'': ''
};

export function escape(html: string, encode = false) {
if (encode) {
if (escapeTest.test(html)) {
return html.replace(escapeReplace, getEscapeReplacement);
}
} else {
if (escapeTestNoEncode.test(html)) {
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
}
}

return html;
}

export function slugify(title: string) {
return title
.replace(/&.+;/g, '')
.replace(/[^a-zA-Z0-9-$(.):]/g, '-')
.replace(/-{2,}/g, '-')
.replace(/^-/, '')
.replace(/-$/, '');
}

export function removeMarkdown(markdown: string) {
/**
* Strip styling/links etc from markdown
*/
export function clean(markdown: string) {
return markdown
.replace(/\*\*(.+?)\*\*/g, '$1') // bold
.replace(/_(.+?)_/g, '$1') // Italics
Expand All @@ -69,12 +31,14 @@ export function removeMarkdown(markdown: string) {
.trim();
}

export function removeHTMLEntities(html: string) {
return html.replace(/&.+?;/g, '');
}

export const normalizeSlugify = (str: string) => {
return slugify(removeHTMLEntities(removeMarkdown(str))).replace(/(<([^>]+)>)/gi, '');
export const slugify = (str: string) => {
return clean(str)
.replace(/&.+;/g, '')
.replace(/[^a-zA-Z0-9-$(.):]/g, '-')
.replace(/-{2,}/g, '-')
.replace(/^-/, '')
.replace(/-$/, '')
.replace(/(<([^>]+)>)/gi, '');
};

export function smart_quotes(str: string, html: boolean = false) {
Expand Down
Loading