-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(site-2): Local tutorial (#8427)
* feat(docs): Local tutorial * Refactor some stuff * Better error handling * Fix search imports * Prerender tutorial * try prerendering hack * fix super stupid display hidden bug * Shorten the rendered URL * Shorten URL code even more
- Loading branch information
Showing
16 changed files
with
283 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
{ | ||
"singleQuote": true, | ||
"printWidth": 100, | ||
"useTabs": true | ||
"useTabs": true, | ||
"trailingComma": "es5" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
sites/svelte.dev/src/lib/server/tutorial/get-tutorial-data.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// @ts-check | ||
import fs from 'node:fs'; | ||
import { extract_frontmatter } from '../markdown/index.js'; | ||
|
||
const base = '../../site/content/tutorial/'; | ||
|
||
/** | ||
* @returns {import('./types').TutorialData} | ||
*/ | ||
export function get_tutorial_data() { | ||
const tutorials = []; | ||
|
||
for (const subdir of fs.readdirSync(base)) { | ||
const section = { | ||
title: '', // Initialise with empty | ||
slug: subdir.split('-').slice(1).join('-'), | ||
tutorials: [], | ||
}; | ||
|
||
if (!(fs.statSync(`${base}/${subdir}`).isDirectory() || subdir.endsWith('meta.json'))) continue; | ||
|
||
if (!subdir.endsWith('meta.json')) | ||
section.title = JSON.parse(fs.readFileSync(`${base}/${subdir}/meta.json`, 'utf-8')).title; | ||
|
||
for (const section_dir of fs.readdirSync(`${base}/${subdir}`)) { | ||
const match = /\d{2}-(.+)/.exec(section_dir); | ||
if (!match) continue; | ||
|
||
const slug = match[1]; | ||
|
||
const tutorial_base_dir = `${base}/${subdir}/${section_dir}`; | ||
|
||
// Read the file, get frontmatter | ||
const contents = fs.readFileSync(`${tutorial_base_dir}/text.md`, 'utf-8'); | ||
const { metadata, body } = extract_frontmatter(contents); | ||
|
||
// Get the contents of the apps. | ||
const completion_states_data = { initial: [], complete: [] }; | ||
for (const app_dir of fs.readdirSync(tutorial_base_dir)) { | ||
if (!app_dir.startsWith('app-')) continue; | ||
|
||
const app_dir_path = `${tutorial_base_dir}/${app_dir}`; | ||
const app_contents = fs.readdirSync(app_dir_path, 'utf-8'); | ||
|
||
for (const file of app_contents) { | ||
completion_states_data[app_dir === 'app-a' ? 'initial' : 'complete'].push({ | ||
name: file, | ||
type: file.split('.').at(-1), | ||
content: fs.readFileSync(`${app_dir_path}/${file}`, 'utf-8'), | ||
}); | ||
} | ||
} | ||
|
||
section.tutorials.push({ | ||
title: metadata.title, | ||
slug, | ||
content: body, | ||
dir: `${subdir}/${section_dir}`, | ||
...completion_states_data, | ||
}); | ||
} | ||
|
||
tutorials.push(section); | ||
} | ||
|
||
return tutorials; | ||
} | ||
|
||
/** | ||
* @param {import('./types').TutorialData} tutorial_data | ||
* @returns {import('./types').TutorialsList} | ||
*/ | ||
export function get_tutorial_list(tutorial_data) { | ||
return tutorial_data.map((section) => ({ | ||
title: section.title, | ||
tutorials: section.tutorials.map((tutorial) => ({ | ||
title: tutorial.title, | ||
slug: tutorial.slug, | ||
})), | ||
})); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { createShikiHighlighter } from 'shiki-twoslash'; | ||
import { transform } from '../markdown'; | ||
|
||
const languages = { | ||
bash: 'bash', | ||
env: 'bash', | ||
html: 'svelte', | ||
svelte: 'svelte', | ||
sv: 'svelte', | ||
js: 'javascript', | ||
css: 'css', | ||
diff: 'diff', | ||
ts: 'typescript', | ||
'': '', | ||
}; | ||
|
||
/** | ||
* @param {import('./types').TutorialData} tutorial_data | ||
* @param {string} slug | ||
*/ | ||
export async function get_parsed_tutorial(tutorial_data, slug) { | ||
const tutorial = tutorial_data | ||
.find(({ tutorials }) => tutorials.find((t) => t.slug === slug)) | ||
?.tutorials?.find((t) => t.slug === slug); | ||
|
||
if (!tutorial) return null; | ||
|
||
const body = tutorial.content; | ||
|
||
const highlighter = await createShikiHighlighter({ theme: 'css-variables' }); | ||
|
||
const content = transform(body, { | ||
/** | ||
* @param {string} html | ||
*/ | ||
heading(html) { | ||
const title = html | ||
.replace(/<\/?code>/g, '') | ||
.replace(/"/g, '"') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>'); | ||
|
||
return title; | ||
}, | ||
code: (source, language) => { | ||
let html = ''; | ||
|
||
source = source | ||
.replace(/^([\-\+])?((?: )+)/gm, (match, prefix = '', spaces) => { | ||
if (prefix && language !== 'diff') return match; | ||
|
||
// for no good reason at all, marked replaces tabs with spaces | ||
let tabs = ''; | ||
for (let i = 0; i < spaces.length; i += 4) { | ||
tabs += ' '; | ||
} | ||
return prefix + tabs; | ||
}) | ||
.replace(/\*\\\//g, '*/'); | ||
|
||
html = highlighter.codeToHtml(source, { lang: languages[language] }); | ||
|
||
html = html | ||
.replace( | ||
/^(\s+)<span class="token comment">([\s\S]+?)<\/span>\n/gm, | ||
(match, intro_whitespace, content) => { | ||
// we use some CSS trickery to make comments break onto multiple lines while preserving indentation | ||
const lines = (intro_whitespace + content).split('\n'); | ||
return lines | ||
.map((line) => { | ||
const match = /^(\s*)(.*)/.exec(line); | ||
const indent = (match[1] ?? '').replace(/\t/g, ' ').length; | ||
|
||
return `<span class="token comment wrapped" style="--indent: ${indent}ch">${ | ||
line ?? '' | ||
}</span>`; | ||
}) | ||
.join(''); | ||
} | ||
) | ||
.replace(/\/\*…\*\//g, '…'); | ||
|
||
return html; | ||
}, | ||
codespan: (text) => '<code>' + text + '</code>', | ||
}); | ||
|
||
return { ...tutorial, content }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
export type TutorialData = { | ||
title: string; | ||
slug: string; | ||
tutorials: { | ||
title: string; | ||
slug: string; | ||
dir: string; | ||
content: string; | ||
initial: { name: string; type: string; content: string }[]; | ||
complete: { name: string; type: string; content: string }[]; | ||
}[]; | ||
}[]; | ||
|
||
export interface Tutorial { | ||
title: string; | ||
slug: string; | ||
} | ||
|
||
export interface TutorialSection { | ||
title: string; | ||
tutorials: Tutorial[]; | ||
} | ||
|
||
export type TutorialsList = TutorialSection[]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
2 changes: 2 additions & 0 deletions
2
...s/svelte.dev/src/routes/tutorial/+page.js → ...e.dev/src/routes/tutorial/+page.server.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
import { redirect } from '@sveltejs/kit'; | ||
|
||
export const prerender = true; | ||
|
||
export function load() { | ||
throw redirect(301, '/tutorial/basics'); | ||
} |
This file was deleted.
Oops, something went wrong.
20 changes: 20 additions & 0 deletions
20
sites/svelte.dev/src/routes/tutorial/[slug]/+page.server.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { get_parsed_tutorial } from '$lib/server/tutorial'; | ||
import { get_tutorial_data, get_tutorial_list } from '$lib/server/tutorial/get-tutorial-data'; | ||
import { error } from '@sveltejs/kit'; | ||
|
||
export const prerender = true; | ||
|
||
export async function load({ params }) { | ||
const tutorial_data = get_tutorial_data(); | ||
const tutorials_list = get_tutorial_list(tutorial_data); | ||
|
||
const tutorial = await get_parsed_tutorial(tutorial_data, params.slug); | ||
|
||
if (!tutorial) throw error(404); | ||
|
||
return { | ||
tutorials_list, | ||
tutorial, | ||
slug: params.slug, | ||
}; | ||
} |
Oops, something went wrong.
35e7a85
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
svelte-dev-2 – ./
svelte-dev-2-svelte.vercel.app
svelte-dev-2.vercel.app
svelte-dev-2-git-sites-svelte.vercel.app