Skip to content
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
2 changes: 0 additions & 2 deletions apps/svelte.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@
"flexsearch": "^0.7.43",
"flru": "^1.0.2",
"port-authority": "^2.0.1",
"prism-svelte": "^0.5.0",
"prismjs": "^1.29.0",
"shiki": "^1.22.0",
"topojson-client": "^3.1.0",
"vitest": "^2.1.2",
Expand Down
137 changes: 70 additions & 67 deletions apps/svelte.dev/src/lib/server/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,85 @@
import { render_content_markdown } from '@sveltejs/site-kit/markdown';

export const render_content = (filename: string, body: string) =>
render_content_markdown(filename, body, {
twoslashBanner: (filename, source) => {
// TODO these are copied from Svelte and SvelteKit - adjust for new filenames
const injected = [];
export const render_content = (
filename: string,
body: string,
options: { check?: boolean } = {}
) => {
return render_content_markdown(filename, body, options, (filename, source) => {
// TODO these are copied from Svelte and SvelteKit - adjust for new filenames
const injected = [];

if (/(svelte)/.test(source) || filename.includes('typescript')) {
injected.push(`// @filename: ambient.d.ts`, `/// <reference types="svelte" />`);
}

if (filename.includes('svelte-compiler')) {
injected.push('// @esModuleInterop');
}
if (/(svelte)/.test(source) || filename.includes('typescript')) {
injected.push(`// @filename: ambient.d.ts`, `/// <reference types="svelte" />`);
}

if (filename.includes('svelte.md')) {
injected.push('// @errors: 2304');
}
if (filename.includes('svelte-compiler')) {
injected.push('// @esModuleInterop');
}

// Actions JSDoc examples are invalid. Too many errors, edge cases
// d.ts files are not properly supported right now, fix this later
if (filename.includes('svelte-action') || source.includes(' declare const ')) {
injected.push('// @noErrors');
}
if (filename.includes('svelte.md')) {
injected.push('// @errors: 2304');
}

if (filename.includes('typescript')) {
injected.push('// @errors: 2304');
}
// Actions JSDoc examples are invalid. Too many errors, edge cases
// d.ts files are not properly supported right now, fix this later
if (filename.includes('svelte-action') || source.includes(' declare const ')) {
injected.push('// @noErrors');
}

if (
source.includes('$app/') ||
source.includes('$service-worker') ||
source.includes('@sveltejs/kit/')
) {
injected.push(`// @filename: ambient-kit.d.ts`, `/// <reference types="@sveltejs/kit" />`);
}
if (filename.includes('typescript')) {
injected.push('// @errors: 2304');
}

if (source.includes('$env/')) {
// TODO we're hardcoding static env vars that are used in code examples
// in the types, which isn't... totally ideal, but will do for now
injected.push(
`declare module '$env/dynamic/private' { export const env: Record<string, string> }`,
`declare module '$env/dynamic/public' { export const env: Record<string, string> }`,
`declare module '$env/static/private' { export const API_KEY: string }`,
`declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }`
);
}
if (
source.includes('$app/') ||
source.includes('$service-worker') ||
source.includes('@sveltejs/kit/')
) {
injected.push(`// @filename: ambient-kit.d.ts`, `/// <reference types="@sveltejs/kit" />`);
}

if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
injected.push(
`// @filename: $types.d.ts`,
`import type * as Kit from '@sveltejs/kit';`,
`export type PageLoad = Kit.Load<Record<string, any>>;`,
`export type PageServerLoad = Kit.ServerLoad<Record<string, any>>;`,
`export type LayoutLoad = Kit.Load<Record<string, any>>;`,
`export type LayoutServerLoad = Kit.ServerLoad<Record<string, any>>;`,
`export type RequestHandler = Kit.RequestHandler<Record<string, any>>;`,
`export type Action = Kit.Action<Record<string, any>>;`,
`export type Actions = Kit.Actions<Record<string, any>>;`,
`export type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>;`
);
}
if (source.includes('$env/')) {
// TODO we're hardcoding static env vars that are used in code examples
// in the types, which isn't... totally ideal, but will do for now
injected.push(
`declare module '$env/dynamic/private' { export const env: Record<string, string> }`,
`declare module '$env/dynamic/public' { export const env: Record<string, string> }`,
`declare module '$env/static/private' { export const API_KEY: string }`,
`declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }`
);
}

// special case — we need to make allowances for code snippets coming
// from e.g. ambient.d.ts
if (filename.endsWith('$env-all.md') || filename.endsWith('$app-forms.md')) {
injected.push('// @errors: 7006 7031');
}
if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
injected.push(
`// @filename: $types.d.ts`,
`import type * as Kit from '@sveltejs/kit';`,
`export type PageLoad = Kit.Load<Record<string, any>>;`,
`export type PageServerLoad = Kit.ServerLoad<Record<string, any>>;`,
`export type LayoutLoad = Kit.Load<Record<string, any>>;`,
`export type LayoutServerLoad = Kit.ServerLoad<Record<string, any>>;`,
`export type RequestHandler = Kit.RequestHandler<Record<string, any>>;`,
`export type Action = Kit.Action<Record<string, any>>;`,
`export type Actions = Kit.Actions<Record<string, any>>;`,
`export type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>;`
);
}

if (filename.endsWith('10-configuration.md')) {
injected.push('// @errors: 2307');
}
// special case — we need to make allowances for code snippets coming
// from e.g. ambient.d.ts
if (filename.endsWith('$env-all.md') || filename.endsWith('$app-forms.md')) {
injected.push('// @errors: 7006 7031');
}

// another special case
if (source.includes('$lib/types')) {
injected.push(`declare module '$lib/types' { export interface User {} }`);
}
if (filename.endsWith('10-configuration.md')) {
injected.push('// @errors: 2307');
}

return injected.join('\n');
// another special case
if (source.includes('$lib/types')) {
injected.push(`declare module '$lib/types' { export interface User {} }`);
}

return injected.join('\n');
});
};
94 changes: 10 additions & 84 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/content.server.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
// @ts-expect-error has no types
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash.js';
import 'prismjs/components/prism-diff.js';
import 'prismjs/components/prism-typescript.js';
import 'prism-svelte';
import { read } from '$app/server';
import { index } from '$lib/server/content';
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';
import type { Document } from '@sveltejs/site-kit';
import { escape_html } from '$lib/utils/escape';
import type { Renderer } from 'marked';
import { render_content } from '$lib/server/renderer';

const lookup: Record<
string,
Expand Down Expand Up @@ -100,74 +92,6 @@ async function get(assets: Record<string, string>, key: string) {
: Buffer.from(await response.arrayBuffer()).toString('base64');
}

const languages = {
bash: 'bash',
env: 'bash',
html: 'markup',
svelte: 'svelte',
js: 'javascript',
css: 'css',
ts: 'typescript',
'': ''
};

const delimiter_substitutes = {
'+++': ' ',
'---': ' ',
':::': ' '
};

function highlight_spans(content: string, classname: string) {
return `<span class="${classname}">${content}</span>`;
// return content.replace(/<span class="([^"]+)"/g, (_, classnames) => {
// return `<span class="${classname} ${classnames}"`;
// });
}

const default_renderer: Partial<Renderer> = {
code: ({ text, lang = '' }) => {
/** @type {Record<string, string>} */
const options: Record<string, string> = {};

let source = text
.replace(/\/\/\/ (.+?)(?:: (.+))?\n/gm, (_, key, value) => {
options[key] = value;
return '';
})
.replace(/(\+\+\+|---|:::)/g, (_, delimiter: keyof typeof delimiter_substitutes) => {
return delimiter_substitutes[delimiter];
})
.replace(/\*\\\//g, '*/');

let html = '<div class="code-block"><div class="controls">';

if (options.file) {
html += `<span class="filename">${options.file}</span>`;
}

html += '</div>';

const plang = languages[lang as keyof typeof languages];
const highlighted = plang
? // TODO use shiki here rather than Prism?
PrismJS.highlight(source, PrismJS.languages[plang], lang)
: escape_html(source);

html += `<pre class='language-${plang}'><code>${highlighted}</code></pre></div>`;

return html
.replace(/ {13}([^ ][^]+?) {13}/g, (_, content) => {
return highlight_spans(content, 'highlight add');
})
.replace(/ {11}([^ ][^]+?) {11}/g, (_, content) => {
return highlight_spans(content, 'highlight remove');
})
.replace(/ {9}([^ ][^]+?) {9}/g, (_, content) => {
return highlight_spans(content, 'highlight');
});
}
};

export async function load_exercise(slug: string): Promise<Exercise> {
if (!(slug in lookup)) {
error(404, 'No such tutorial found');
Expand Down Expand Up @@ -228,13 +152,15 @@ export async function load_exercise(slug: string): Promise<Exercise> {
prev: prev && { slug: prev.slug },
next,
markdown: exercise.body,
html: await transform(exercise.body, {
...default_renderer,
codespan: ({ text }) =>
filenames.size > 1 && filenames.has(text)
? `<code data-file="${scope.prefix + text}">${text}</code>`
: `<code>${text}</code>`
}),
html: (await render_content(exercise.file, exercise.body, { check: false })).replace(
/<code>(.+?)<\/code>/g,
(match, filename) => {
// TODO wire this up
return filenames.size > 1 && filenames.has(filename)
? `<code data-file="${scope.prefix + filename}">${filename}</code>`
: match;
}
),
dir: exercise.file.split('/').slice(0, -1).join('/'),
editing_constraints: {
create: new Set(exercise.metadata.editing_constraints?.create ?? []),
Expand Down
Loading
Loading