From e5ab5b65ce25f4eab262591b1cce1316d68667f4 Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Fri, 3 Feb 2023 15:53:43 +0530 Subject: [PATCH 01/12] Rename markdown to blog --- .../src/lib/server/{markdown => blog}/index.js | 0 .../src/lib/server/{markdown => blog}/marked.js | 0 .../lib/server/{markdown => blog}/types.d.ts | 0 .../svelte.dev/src/routes/blog/+page.server.js | 4 ++-- .../src/routes/blog/[slug]/+page.server.js | 4 ++-- .../src/routes/blog/[slug]/card.png/+server.js | 17 +++++++++-------- .../src/routes/blog/rss.xml/+server.js | 10 +++++----- sites/svelte.dev/tsconfig.json | 5 +++++ 8 files changed, 23 insertions(+), 17 deletions(-) rename sites/svelte.dev/src/lib/server/{markdown => blog}/index.js (100%) rename sites/svelte.dev/src/lib/server/{markdown => blog}/marked.js (100%) rename sites/svelte.dev/src/lib/server/{markdown => blog}/types.d.ts (100%) diff --git a/sites/svelte.dev/src/lib/server/markdown/index.js b/sites/svelte.dev/src/lib/server/blog/index.js similarity index 100% rename from sites/svelte.dev/src/lib/server/markdown/index.js rename to sites/svelte.dev/src/lib/server/blog/index.js diff --git a/sites/svelte.dev/src/lib/server/markdown/marked.js b/sites/svelte.dev/src/lib/server/blog/marked.js similarity index 100% rename from sites/svelte.dev/src/lib/server/markdown/marked.js rename to sites/svelte.dev/src/lib/server/blog/marked.js diff --git a/sites/svelte.dev/src/lib/server/markdown/types.d.ts b/sites/svelte.dev/src/lib/server/blog/types.d.ts similarity index 100% rename from sites/svelte.dev/src/lib/server/markdown/types.d.ts rename to sites/svelte.dev/src/lib/server/blog/types.d.ts diff --git a/sites/svelte.dev/src/routes/blog/+page.server.js b/sites/svelte.dev/src/routes/blog/+page.server.js index 4a6091a9f570..0a1ca27d9f51 100644 --- a/sites/svelte.dev/src/routes/blog/+page.server.js +++ b/sites/svelte.dev/src/routes/blog/+page.server.js @@ -1,9 +1,9 @@ -import { get_index } from '$lib/server/markdown'; +import { get_index } from '$lib/server/blog'; export const prerender = true; export async function load() { return { - posts: get_index() + posts: get_index(), }; } diff --git a/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js b/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js index 3f102cbf9533..57005879b9ee 100644 --- a/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js +++ b/sites/svelte.dev/src/routes/blog/[slug]/+page.server.js @@ -1,4 +1,4 @@ -import { get_post } from '$lib/server/markdown/index.js'; +import { get_post } from '$lib/server/blog/index.js'; import { error } from '@sveltejs/kit'; export const prerender = true; @@ -11,6 +11,6 @@ export async function load({ params }) { } return { - post + post, }; } diff --git a/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js b/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js index f199d240462d..a63103765c62 100644 --- a/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js +++ b/sites/svelte.dev/src/routes/blog/[slug]/card.png/+server.js @@ -2,7 +2,7 @@ import satori from 'satori'; import { Resvg } from '@resvg/resvg-js'; import OverpassRegular from './Overpass-Regular.ttf'; import { html as toReactNode } from 'satori-html'; -import { get_post } from '$lib/server/markdown/index.js'; +import { get_post } from '$lib/server/blog/index.js'; import { error } from '@sveltejs/kit'; import Card from './Card.svelte'; @@ -19,6 +19,7 @@ export const GET = async ({ params, url }) => { throw error(404); } + // @ts-ignore const result = Card.render({ post }); const element = toReactNode(`${result.html}`); @@ -28,18 +29,18 @@ export const GET = async ({ params, url }) => { name: 'Overpass', data: Buffer.from(OverpassRegular), style: 'normal', - weight: 400 - } + weight: 400, + }, ], height, - width + width, }); const resvg = new Resvg(svg, { fitTo: { mode: 'width', - value: width - } + value: width, + }, }); const image = resvg.render(); @@ -47,7 +48,7 @@ export const GET = async ({ params, url }) => { return new Response(image.asPng(), { headers: { 'content-type': 'image/png', - 'cache-control': 'public, max-age=600' // cache for 10 minutes - } + 'cache-control': 'public, max-age=600', // cache for 10 minutes + }, }); }; diff --git a/sites/svelte.dev/src/routes/blog/rss.xml/+server.js b/sites/svelte.dev/src/routes/blog/rss.xml/+server.js index 2c9af085e85f..353c634d7553 100644 --- a/sites/svelte.dev/src/routes/blog/rss.xml/+server.js +++ b/sites/svelte.dev/src/routes/blog/rss.xml/+server.js @@ -1,4 +1,4 @@ -import { get_index } from '$lib/server/markdown'; +import { get_index } from '$lib/server/blog'; export const prerender = true; @@ -15,13 +15,13 @@ function escapeHTML(html) { "'": '#39', '&': 'amp', '<': 'lt', - '>': 'gt' + '>': 'gt', }; return html.replace(/["'&<>]/g, (c) => `&${chars[c]};`); } -/** @type {import('$lib/server/markdown/types').BlogPostSummary[]} */ +/** @param {import('$lib/server/blog/types').BlogPostSummary[]} posts */ const get_rss = (posts) => ` @@ -63,7 +63,7 @@ export async function GET() { return new Response(get_rss(posts), { headers: { 'Cache-Control': `max-age=${30 * 60 * 1e3}`, - 'Content-Type': 'application/rss+xml' - } + 'Content-Type': 'application/rss+xml', + }, }); } diff --git a/sites/svelte.dev/tsconfig.json b/sites/svelte.dev/tsconfig.json index 73f0a46d9784..6484f32c5198 100644 --- a/sites/svelte.dev/tsconfig.json +++ b/sites/svelte.dev/tsconfig.json @@ -1,3 +1,8 @@ { "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "allowSyntheticDefaultImports": true + } } From 3c2af2aff54c410da21807bd9bf0e3b24097c2cd Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Sun, 5 Feb 2023 17:43:12 +0530 Subject: [PATCH 02/12] Multi-page docs --- sites/svelte.dev/package-lock.json | 235 ++++++++ sites/svelte.dev/package.json | 4 + sites/svelte.dev/src/lib/server/blog/index.js | 28 +- sites/svelte.dev/src/lib/server/docs/index.js | 564 ++++++++++++++++++ sites/svelte.dev/src/lib/server/docs/types.ts | 13 + .../src/lib/server/markdown/index.js | 191 ++++++ .../src/routes/docs/+layout.server.js | 23 + .../svelte.dev/src/routes/docs/+layout.svelte | 334 +++++++++++ .../src/routes/docs/Contents.svelte | 137 +++++ .../src/routes/docs/[slug]/+page.server.js | 24 + .../src/routes/docs/[slug]/+page.svelte | 8 + 11 files changed, 1537 insertions(+), 24 deletions(-) create mode 100644 sites/svelte.dev/src/lib/server/docs/index.js create mode 100644 sites/svelte.dev/src/lib/server/docs/types.ts create mode 100644 sites/svelte.dev/src/lib/server/markdown/index.js create mode 100644 sites/svelte.dev/src/routes/docs/+layout.server.js create mode 100644 sites/svelte.dev/src/routes/docs/+layout.svelte create mode 100644 sites/svelte.dev/src/routes/docs/Contents.svelte create mode 100644 sites/svelte.dev/src/routes/docs/[slug]/+page.server.js create mode 100644 sites/svelte.dev/src/routes/docs/[slug]/+page.svelte diff --git a/sites/svelte.dev/package-lock.json b/sites/svelte.dev/package-lock.json index 12ff957e2d1c..4b49329d6bbb 100644 --- a/sites/svelte.dev/package-lock.json +++ b/sites/svelte.dev/package-lock.json @@ -22,6 +22,8 @@ "@sveltejs/kit": "^1.3.10", "@sveltejs/site-kit": "^3.2.1", "@sveltejs/vite-plugin-svelte": "^2.0.2", + "@types/marked": "^4.0.8", + "@types/prismjs": "^1.26.0", "degit": "^2.8.4", "dotenv": "^16.0.3", "jimp": "^0.16.2", @@ -34,6 +36,8 @@ "satori": "^0.1.2", "satori-html": "^0.3.2", "shelljs": "^0.8.5", + "shiki": "^0.14.0", + "shiki-twoslash": "^3.1.0", "svelte": "^3.55.1", "svelte-check": "^3.0.3", "typescript": "^4.9.5", @@ -1340,6 +1344,12 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" }, + "node_modules/@types/marked": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz", + "integrity": "sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==", + "dev": true + }, "node_modules/@types/node": { "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", @@ -1351,6 +1361,12 @@ "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz", "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==" }, + "node_modules/@types/prismjs": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz", + "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==", + "dev": true + }, "node_modules/@types/pug": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", @@ -1366,6 +1382,35 @@ "@types/node": "*" } }, + "node_modules/@typescript/twoslash": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript/twoslash/-/twoslash-3.1.0.tgz", + "integrity": "sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==", + "dev": true, + "dependencies": { + "@typescript/vfs": "1.3.5", + "debug": "^4.1.1", + "lz-string": "^1.4.4" + } + }, + "node_modules/@typescript/twoslash/node_modules/@typescript/vfs": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz", + "integrity": "sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.4.tgz", + "integrity": "sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -1377,6 +1422,12 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, "node_modules/any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", @@ -2369,6 +2420,12 @@ "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", "dev": true }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -2418,6 +2475,15 @@ "node": ">=10" } }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", @@ -3204,6 +3270,47 @@ "node": ">=4" } }, + "node_modules/shiki": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.0.tgz", + "integrity": "sha512-fb9Fg1Yx/ElVJcTqPQIEOSfn7mSZlrT1W3CkymY08lL2Jsi+t7jPcZzKO1lCsQwlSDuyNhHvolnyA2OI4EgJNg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/shiki-twoslash": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shiki-twoslash/-/shiki-twoslash-3.1.0.tgz", + "integrity": "sha512-uDqrTutOIZzyHbo103GsK7Vvc10saK1XCCivnOQ1NHJzgp3FBilEpftGeVzVSMOJs+JyhI7whkvhXV7kXQ5zCg==", + "dev": true, + "dependencies": { + "@typescript/twoslash": "3.1.0", + "@typescript/vfs": "1.3.4", + "shiki": "0.10.1", + "typescript": ">3" + } + }, + "node_modules/shiki-twoslash/node_modules/shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/shiki-twoslash/node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3721,6 +3828,18 @@ } } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/web-streams-polyfill": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", @@ -4696,6 +4815,12 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" }, + "@types/marked": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz", + "integrity": "sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==", + "dev": true + }, "@types/node": { "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", @@ -4707,6 +4832,12 @@ "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz", "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==" }, + "@types/prismjs": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz", + "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==", + "dev": true + }, "@types/pug": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", @@ -4722,11 +4853,48 @@ "@types/node": "*" } }, + "@typescript/twoslash": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript/twoslash/-/twoslash-3.1.0.tgz", + "integrity": "sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==", + "dev": true, + "requires": { + "@typescript/vfs": "1.3.5", + "debug": "^4.1.1", + "lz-string": "^1.4.4" + }, + "dependencies": { + "@typescript/vfs": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz", + "integrity": "sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + } + } + }, + "@typescript/vfs": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.4.tgz", + "integrity": "sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, + "ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, "any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", @@ -5489,6 +5657,12 @@ "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", "dev": true }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -5528,6 +5702,12 @@ "yallist": "^4.0.0" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true + }, "magic-string": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", @@ -6076,6 +6256,49 @@ "rechoir": "^0.6.2" } }, + "shiki": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.0.tgz", + "integrity": "sha512-fb9Fg1Yx/ElVJcTqPQIEOSfn7mSZlrT1W3CkymY08lL2Jsi+t7jPcZzKO1lCsQwlSDuyNhHvolnyA2OI4EgJNg==", + "dev": true, + "requires": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "shiki-twoslash": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shiki-twoslash/-/shiki-twoslash-3.1.0.tgz", + "integrity": "sha512-uDqrTutOIZzyHbo103GsK7Vvc10saK1XCCivnOQ1NHJzgp3FBilEpftGeVzVSMOJs+JyhI7whkvhXV7kXQ5zCg==", + "dev": true, + "requires": { + "@typescript/twoslash": "3.1.0", + "@typescript/vfs": "1.3.4", + "shiki": "0.10.1", + "typescript": ">3" + }, + "dependencies": { + "shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "requires": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + } + } + }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -6400,6 +6623,18 @@ "dev": true, "requires": {} }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "web-streams-polyfill": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json index 9eb9420162ed..2cd8e1c00d66 100644 --- a/sites/svelte.dev/package.json +++ b/sites/svelte.dev/package.json @@ -30,6 +30,8 @@ "@sveltejs/kit": "^1.3.10", "@sveltejs/site-kit": "^3.2.1", "@sveltejs/vite-plugin-svelte": "^2.0.2", + "@types/marked": "^4.0.8", + "@types/prismjs": "^1.26.0", "degit": "^2.8.4", "dotenv": "^16.0.3", "jimp": "^0.16.2", @@ -42,6 +44,8 @@ "satori": "^0.1.2", "satori-html": "^0.3.2", "shelljs": "^0.8.5", + "shiki": "^0.14.0", + "shiki-twoslash": "^3.1.0", "svelte": "^3.55.1", "svelte-check": "^3.0.3", "typescript": "^4.9.5", diff --git a/sites/svelte.dev/src/lib/server/blog/index.js b/sites/svelte.dev/src/lib/server/blog/index.js index d708622c9f26..6e39380b6f28 100644 --- a/sites/svelte.dev/src/lib/server/blog/index.js +++ b/sites/svelte.dev/src/lib/server/blog/index.js @@ -1,4 +1,5 @@ import fs from 'fs'; +import { extract_frontmatter } from '../markdown'; import { transform } from './marked'; /** @@ -21,7 +22,7 @@ export function get_index() { date, title: metadata.title, description: metadata.description, - draft: !!metadata.draft + draft: !!metadata.draft, }; }); } @@ -47,10 +48,10 @@ export function get_post(slug) { description: metadata.description, author: { name: metadata.author, - url: metadata.authorURL + url: metadata.authorURL, }, draft: !!metadata.draft, - content: transform(body) + content: transform(body), }; } } @@ -67,27 +68,6 @@ function get_date_and_slug(filename) { return { date, date_formatted, slug }; } -/** @param {string} markdown */ -function extract_frontmatter(markdown) { - const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown); - const frontmatter = match[1]; - const body = markdown.slice(match[0].length); - - /** @type {Record} */ - const metadata = {}; - frontmatter.split('\n').forEach((pair) => { - const i = pair.indexOf(':'); - metadata[pair.slice(0, i).trim()] = strip_quotes(pair.slice(i + 1).trim()); - }); - - return { metadata, body }; -} - -function strip_quotes(str) { - if (str[0] === '"' && str[str.length - 1] === '"') return str.slice(1, -1); - return str; -} - const months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '); function format_date(date) {} diff --git a/sites/svelte.dev/src/lib/server/docs/index.js b/sites/svelte.dev/src/lib/server/docs/index.js new file mode 100644 index 000000000000..5760a2128e1e --- /dev/null +++ b/sites/svelte.dev/src/lib/server/docs/index.js @@ -0,0 +1,564 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { renderCodeToHTML, runTwoSlash, createShikiHighlighter } from 'shiki-twoslash'; +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 { escape, extract_frontmatter, transform } from '../markdown'; +// import { render, replace_placeholders } from './render.js'; +// import { parse_route_id } from '../../../../../../packages/kit/src/utils/routing.js'; +import ts, { ScriptTarget } from 'typescript'; +import MagicString from 'magic-string'; +import { fileURLToPath } from 'url'; +import { createHash } from 'crypto'; + +const base = '../../site/content/docs/'; + +const languages = { + bash: 'bash', + env: 'bash', + html: 'markup', + svelte: 'svelte', + js: 'javascript', + css: 'css', + diff: 'diff', + ts: 'typescript', + '': '', +}; + +/** + * @param {string} file + */ +export async function read_file(file) { + const match = /\d{2}-(.+)\.md/.exec(path.basename(file)); + if (!match) return null; + + // const markdown = replace_placeholders(fs.readFileSync(`${base}/${file}`, 'utf-8')); + const markdown = fs.readFileSync(`${base}/${file}`, 'utf-8'); + + const highlighter = await createShikiHighlighter({ theme: 'css-variables' }); + + const { metadata, body } = extract_frontmatter(markdown); + + const { content, sections } = parse({ + file, + body: generate_ts_from_js(body), + code: (source, language, current) => { + const hash = createHash('sha256'); + hash.update(source + language + current); + const digest = hash.digest().toString('base64').replace(/\//g, '-'); + + // TODO: cache + // if (fs.existsSync(`${snippet_cache}/${digest}.html`)) { + // return fs.readFileSync(`${snippet_cache}/${digest}.html`, 'utf-8'); + // } + + /** @type {Record} */ + const options = {}; + + let html = ''; + + source = source + .replace(/^\/\/\/ (.+?): (.+)\n/gm, (_, key, value) => { + options[key] = value; + return ''; + }) + .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, '*/'); + + let version_class = ''; + if (language === 'generated-ts' || language === 'generated-svelte') { + language = language.replace('generated-', ''); + version_class = 'ts-version'; + } else if (language === 'original-js' || language === 'original-svelte') { + language = language.replace('original-', ''); + version_class = 'js-version'; + } + + // if (language === 'dts') { + // // @ts-ignore + // html = renderCodeToHTML(source, 'ts', { twoslash: false }, {}, highlighter); + // } else if (language === 'js' || language === 'ts') { + // try { + // const injected = []; + // if ( + // source.includes('$app/') || + // source.includes('$service-worker') || + // source.includes('@sveltejs/kit/') + // ) { + // injected.push( + // `// @filename: ambient-kit.d.ts`, + // `/// ` + // ); + // } + + // 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 }`, + // `declare module '$env/dynamic/public' { export const env: Record }`, + // `declare module '$env/static/private' { export const API_KEY: string }`, + // `declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }` + // ); + // } + + // if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) { + // const params = parse_route_id(options.file || `+page.${language}`) + // .params.map((param) => `${param.name}: string`) + // .join(', '); + + // injected.push( + // `// @filename: $types.d.ts`, + // `import type * as Kit from '@sveltejs/kit';`, + // `export type PageLoad = Kit.Load<{${params}}>;`, + // `export type PageServerLoad = Kit.ServerLoad<{${params}}>;`, + // `export type LayoutLoad = Kit.Load<{${params}}>;`, + // `export type LayoutServerLoad = Kit.ServerLoad<{${params}}>;`, + // `export type RequestHandler = Kit.RequestHandler<{${params}}>;`, + // `export type Action = Kit.Action<{${params}}>;`, + // `export type Actions = Kit.Actions<{${params}}>;` + // ); + // } + + // // special case — we need to make allowances for code snippets coming + // // from e.g. ambient.d.ts + // if (file.endsWith('30-modules.md')) { + // injected.push('// @errors: 7006 7031'); + // } + + // // another special case + // if (source.includes('$lib/types')) { + // injected.push(`declare module '$lib/types' { export interface User {} }`); + // } + + // if (injected.length) { + // const injected_str = injected.join('\n'); + // if (source.includes('// @filename:')) { + // source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`); + // } else { + // source = source.replace( + // /^(?!\/\/ @)/m, + // `${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n` + // ); + // } + // } + + // const twoslash = runTwoSlash(source, language, { + // defaultCompilerOptions: { + // allowJs: true, + // checkJs: true, + // target: 'es2021', + // }, + // }); + + // html = renderCodeToHTML( + // twoslash.code, + // 'ts', + // { twoslash: true }, + // {}, + // highlighter, + // twoslash + // ); + // } catch (e) { + // console.error(`Error compiling snippet in ${file}`); + // console.error(e.code); + // throw e; + // } + + // // we need to be able to inject the LSP attributes as HTML, not text, so we + // // turn < into &lt; + // html = html.replace( + // /]*)>(\w+)<\/data-lsp>/g, + // (match, lsp, attrs, name) => { + // if (!lsp) return name; + // return `${name}`; + // } + // ); + + // // preserve blank lines in output (maybe there's a more correct way to do this?) + // html = html.replace(/
<\/div>/g, '
'); + // } else if (language === 'diff') { + // const lines = source.split('\n').map((content) => { + // let type = null; + // if (/^[\+\-]/.test(content)) { + // type = content[0] === '+' ? 'inserted' : 'deleted'; + // content = content.slice(1); + // } + + // return { + // type, + // content: escape(content), + // }; + // }); + + // html = `
${lines
+			// 		.map((line) => {
+			// 			if (line.type) return `${line.content}\n`;
+			// 			return line.content + '\n';
+			// 		})
+			// 		.join('')}
`; + // } else { + // const plang = languages[language]; + // const highlighted = plang + // ? PrismJS.highlight(source, PrismJS.languages[plang], language) + // : source.replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c])); + + // html = `
${highlighted}
`; + // } + + if (options.file) { + html = `
${options.file}${html}
`; + } + + if (version_class) { + html = html.replace(/class=('|")/, `class=$1${version_class} `); + } + + // type_regex.lastIndex = 0; + + html = html + // .replace(type_regex, (match, prefix, name) => { + // if (options.link === 'false' || name === current) { + // // we don't want e.g. RequestHandler to link to RequestHandler + // return match; + // } + + // const link = `${name}`; + // return `${prefix || ''}${link}`; + // }) + .replace( + /^(\s+)([\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 `${ + line ?? '' + }`; + }) + .join(''); + } + ) + .replace(/\/\*…\*\//g, '…'); + + // fs.writeFileSync(`${snippet_cache}/${digest}.html`, html); + return html; + }, + codespan: (text) => { + return ( + '' + + text + + // text.replace(type_regex, (match, prefix, name) => { + // const link = `${name}`; + // return `${prefix || ''}${link}`; + // }) + + '' + ); + }, + }); + + return { + file, + slug: match[1], + title: metadata.title, + content, + sections, + }; +} + +/** @param {string} title */ +export function slugify(title) { + return title + .toLowerCase() + .replace(/</g, '') + .replace(/>/g, '') + .replace(/[^a-z0-9-$]/g, '-') + .replace(/-{2,}/g, '-') + .replace(/^-/, '') + .replace(/-$/, ''); +} + +/** + * @param {{ + * file: string; + * body: string; + * code: (source: string, language: string, current: string) => string; + * codespan: (source: string) => string; + * }} opts + */ +function parse({ file, body, code, codespan }) { + const headings = []; + + /** @type {import('./types').Section[]} */ + const sections = []; + + /** @type {import('./types').Section} */ + let section; + + // this is a bit hacky, but it allows us to prevent type declarations + // from linking to themselves + let current = ''; + + /** @type {string} */ + const content = transform(body, { + /** + * @param {string} html + * @param {number} level + */ + heading(html, level) { + const title = html + .replace(/<\/?code>/g, '') + .replace(/"/g, '"') + .replace(/</g, '<') + .replace(/>/g, '>'); + + current = title; + + const normalized = slugify(title); + + headings[level - 1] = normalized; + headings.length = level; + + const slug = headings.filter(Boolean).join('-'); + + // TODO: All this will need to change when headings in documentation are restructured to be h2+ instead of h3+ as they are right now + if (level === 3) { + section = { + title, + slug, + sections: [], + }; + + sections.push(section); + } else if (level === 4 || level === 5) { + (section?.sections ?? sections).push({ + title, + slug, + }); + } else { + throw new Error(`Unexpected in ${file}`); + } + + return `${html}`; + }, + code: (source, language) => code(source, language, current), + codespan, + }); + + return { + sections, + content, + }; +} + +export function generate_ts_from_js(markdown) { + return markdown + .replaceAll(/```js\n([\s\S]+?)\n```/g, (match, code) => { + if (!code.includes('/// file:')) { + // No named file -> assume that the code is not meant to be shown in two versions + return match; + } + if (code.includes('/// file: svelte.config.js')) { + // svelte.config.js has no TS equivalent + return match; + } + + const ts = convert_to_ts(code); + + if (!ts) { + // No changes -> don't show TS version + return match; + } + + return match.replace('js', 'original-js') + '\n```generated-ts\n' + ts + '\n```'; + }) + .replaceAll(/```svelte\n([\s\S]+?)\n```/g, (match, code) => { + if (!code.includes('/// file:')) { + // No named file -> assume that the code is not meant to be shown in two versions + return match; + } + + // Assumption: no context="module" blocks + const script = code.match(/`) + + '\n```' + ); + }); +} + +/** + * Transforms a JS code block into a TS code block by turning JSDoc into type annotations. + * Due to pragmatism only the cases currently used in the docs are implemented. + * @param {string} js_code + * @param {string} [indent] + * @param {string} [offset] + * */ +function convert_to_ts(js_code, indent = '', offset = '') { + js_code = js_code + .replaceAll('// @filename: index.js', '// @filename: index.ts') + .replace(/(\/\/\/ .+?\.)js/, '$1ts') + // *\/ appears in some JsDoc comments in d.ts files due to the JSDoc-in-JSDoc problem + .replace(/\*\\\//g, '*/'); + + const ast = ts.createSourceFile( + 'filename.ts', + js_code, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TS + ); + const code = new MagicString(js_code); + const imports = new Map(); + + function walk(node) { + // @ts-ignore + if (node.jsDoc) { + // @ts-ignore + for (const comment of node.jsDoc) { + let modified = false; + + for (const tag of comment.tags ?? []) { + if (ts.isJSDocTypeTag(tag)) { + const [name, generics] = get_type_info(tag); + + if (ts.isFunctionDeclaration(node)) { + const is_export = node.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword + ) + ? 'export ' + : ''; + const is_async = node.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword + ); + code.overwrite( + node.getStart(), + node.name.getEnd(), + `${is_export ? 'export ' : ''}const ${node.name.getText()} = (${ + is_async ? 'async ' : '' + }` + ); + code.appendLeft(node.body.getStart(), '=> '); + const type = generics !== undefined ? `${name}${generics}` : name; + code.appendLeft(node.body.getEnd(), `) satisfies ${type};`); + + modified = true; + } else if ( + ts.isVariableStatement(node) && + node.declarationList.declarations.length === 1 + ) { + const variable_statement = node.declarationList.declarations[0]; + + if (variable_statement.name.getText() === 'actions') { + code.appendLeft(variable_statement.getEnd(), ` satisfies ${name}`); + } else { + code.appendLeft(variable_statement.name.getEnd(), `: ${name}`); + } + + modified = true; + } else { + throw new Error('Unhandled @type JsDoc->TS conversion: ' + js_code); + } + } else if (ts.isJSDocParameterTag(tag) && ts.isFunctionDeclaration(node)) { + if (node.parameters.length !== 1) { + throw new Error( + 'Unhandled @type JsDoc->TS conversion; needs more params logic: ' + node.getText() + ); + } + const [name] = get_type_info(tag); + code.appendLeft(node.parameters[0].getEnd(), `: ${name}`); + + modified = true; + } + } + + if (modified) { + code.overwrite(comment.getStart(), comment.getEnd(), ''); + } + } + } + + ts.forEachChild(node, walk); + } + + walk(ast); + + if (imports.size) { + const import_statements = Array.from(imports.entries()) + .map(([from, names]) => { + return `${indent}import type { ${Array.from(names).join(', ')} } from '${from}';`; + }) + .join('\n'); + const idxOfLastImport = [...ast.statements] + .reverse() + .find((statement) => ts.isImportDeclaration(statement)) + ?.getEnd(); + const insertion_point = Math.max( + idxOfLastImport ? idxOfLastImport + 1 : 0, + js_code.includes('---cut---') + ? js_code.indexOf('\n', js_code.indexOf('---cut---')) + 1 + : js_code.includes('/// file:') + ? js_code.indexOf('\n', js_code.indexOf('/// file:')) + 1 + : 0 + ); + code.appendLeft(insertion_point, offset + import_statements + '\n'); + } + + const transformed = code.toString(); + return transformed === js_code ? undefined : transformed.replace(/\n\s*\n\s*\n/g, '\n\n'); + + /** @param {ts.JSDocTypeTag | ts.JSDocParameterTag} tag */ + function get_type_info(tag) { + const type_text = tag.typeExpression.getText(); + let name = type_text.slice(1, -1); // remove { } + + const import_match = /import\('(.+?)'\)\.(\w+)(<{?[\n\* \w:;,]+}?>)?/.exec(type_text); + if (import_match) { + const [, from, _name, generics] = import_match; + name = _name; + const existing = imports.get(from); + if (existing) { + existing.add(name); + } else { + imports.set(from, new Set([name])); + } + if (generics !== undefined) { + return [ + name, + generics + .replaceAll('*', '') // get rid of JSDoc asterisks + .replace(' }>', '}>'), // unindent closing brace + ]; + } + } + return [name]; + } +} diff --git a/sites/svelte.dev/src/lib/server/docs/types.ts b/sites/svelte.dev/src/lib/server/docs/types.ts new file mode 100644 index 000000000000..c627e1b68ba7 --- /dev/null +++ b/sites/svelte.dev/src/lib/server/docs/types.ts @@ -0,0 +1,13 @@ +export interface Section { + title: string; + slug: string; + sections?: Section[]; +} + +export interface Type { + name: string; + comment: string; + snippet: string; + bullets: string[]; + children: Type[]; +} diff --git a/sites/svelte.dev/src/lib/server/markdown/index.js b/sites/svelte.dev/src/lib/server/markdown/index.js new file mode 100644 index 000000000000..a5e9aa0b795b --- /dev/null +++ b/sites/svelte.dev/src/lib/server/markdown/index.js @@ -0,0 +1,191 @@ +import { marked } from 'marked'; + +const escapeTest = /[&<>"']/; +const escapeReplace = /[&<>"']/g; +const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; +const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; +const escapeReplacements = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', +}; + +/** + * @param {keyof typeof escapeReplacements} ch + */ +const getEscapeReplacement = (ch) => escapeReplacements[ch]; + +/** + * @param {string} html + * @param {boolean} encode + */ +export function escape(html, 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; +} + +/** @type {Partial} */ +const default_renderer = { + code(code, infostring, escaped) { + const lang = (infostring || '').match(/\S*/)[0]; + + code = code.replace(/\n$/, '') + '\n'; + + if (!lang) { + return '
' + (escaped ? code : escape(code, true)) + '
\n'; + } + + return ( + '
' +
+			(escaped ? code : escape(code, true)) +
+			'
\n' + ); + }, + + blockquote(quote) { + return '
\n' + quote + '
\n'; + }, + + html(html) { + return html; + }, + + heading(text, level) { + return '' + text + '\n'; + }, + + hr() { + return '
\n'; + }, + + list(body, ordered, start) { + const type = ordered ? 'ol' : 'ul', + startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; + return '<' + type + startatt + '>\n' + body + '\n'; + }, + + listitem(text) { + return '
  • ' + text + '
  • \n'; + }, + + checkbox(checked) { + return ' '; + }, + + paragraph(text) { + return '

    ' + text + '

    \n'; + }, + + table(header, body) { + if (body) body = '' + body + ''; + + return '\n' + '\n' + header + '\n' + body + '
    \n'; + }, + + tablerow(content) { + return '\n' + content + '\n'; + }, + + tablecell(content, flags) { + const type = flags.header ? 'th' : 'td'; + const tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>'; + return tag + content + '\n'; + }, + + // span level renderer + strong(text) { + return '' + text + ''; + }, + + em(text) { + return '' + text + ''; + }, + + codespan(text) { + return '' + text + ''; + }, + + br() { + return '
    '; + }, + + del(text) { + return '' + text + ''; + }, + + link(href, title, text) { + if (href === null) { + return text; + } + let out = ''; + return out; + }, + + image(href, title, text) { + if (href === null) { + return text; + } + + let out = '' + text + '} renderer + */ +export function transform(markdown, renderer = {}) { + marked.use({ + renderer: { + // we have to jump through these hoops because of marked's API design choices — + // options are global, and merged in confusing ways. You can't do e.g. + // `new Marked(options).parse(markdown)` + ...default_renderer, + ...renderer, + }, + }); + + return marked(markdown); +} + +/** @param {string} markdown */ +export function extract_frontmatter(markdown) { + const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown); + const frontmatter = match[1]; + const body = markdown.slice(match[0].length); + + /** @type {Record} */ + const metadata = {}; + frontmatter.split('\n').forEach((pair) => { + const i = pair.indexOf(':'); + metadata[pair.slice(0, i).trim()] = pair.slice(i + 1).trim(); + }); + + return { metadata, body }; +} diff --git a/sites/svelte.dev/src/routes/docs/+layout.server.js b/sites/svelte.dev/src/routes/docs/+layout.server.js new file mode 100644 index 000000000000..222f0b30650b --- /dev/null +++ b/sites/svelte.dev/src/routes/docs/+layout.server.js @@ -0,0 +1,23 @@ +import { extract_frontmatter } from '$lib/server/markdown'; +import fs from 'fs'; +import { base } from '$app/paths'; + +export const prerender = true; + +const base_dir = '../../site/content/docs/'; + +/** @type {import('./$types').LayoutServerLoad} */ +export function load() { + const sections = fs.readdirSync(base_dir).map((file) => { + const { title } = extract_frontmatter(fs.readFileSync(`${base_dir}/${file}`, 'utf-8')).metadata; + + return { + title, + path: `${base}/docs/${file.slice(3, -3)}`, + }; + }); + + return { + sections, + }; +} diff --git a/sites/svelte.dev/src/routes/docs/+layout.svelte b/sites/svelte.dev/src/routes/docs/+layout.svelte new file mode 100644 index 000000000000..6fa5030f9b6c --- /dev/null +++ b/sites/svelte.dev/src/routes/docs/+layout.svelte @@ -0,0 +1,334 @@ + + +
    +
    + +
    + +
    + +
    +
    + + diff --git a/sites/svelte.dev/src/routes/docs/Contents.svelte b/sites/svelte.dev/src/routes/docs/Contents.svelte new file mode 100644 index 000000000000..373fda400692 --- /dev/null +++ b/sites/svelte.dev/src/routes/docs/Contents.svelte @@ -0,0 +1,137 @@ + + +
    + + diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js b/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js new file mode 100644 index 000000000000..a78422af8534 --- /dev/null +++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.server.js @@ -0,0 +1,24 @@ +import fs from 'fs'; +// import { read_file } from '$lib/server/docs'; +import { error } from '@sveltejs/kit'; +import { read_file } from '$lib/server/docs'; + +export const prerender = true; + +const base = '../../site/content/docs/'; + +/** + * ASSUMPTION FOR FUTURE: This assumes the directory structure of docs is flat. AKA, no nested folders + */ +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + for (const file of fs.readdirSync(`${base}`)) { + if (file.slice(3, -3) === params.slug) { + return { + page: await read_file(file), + }; + } + } + + throw error(404); +} diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte new file mode 100644 index 000000000000..0343a37c1670 --- /dev/null +++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte @@ -0,0 +1,8 @@ + + +{@html data.page.content} From f73f07ac855c493af3a0a3c6c5ab43abd969e142 Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Sun, 5 Feb 2023 17:47:37 +0530 Subject: [PATCH 03/12] Ignore missing ID for now --- sites/svelte.dev/svelte.config.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sites/svelte.dev/svelte.config.js b/sites/svelte.dev/svelte.config.js index c814fd98a738..68268902e0d8 100644 --- a/sites/svelte.dev/svelte.config.js +++ b/sites/svelte.dev/svelte.config.js @@ -3,6 +3,10 @@ import adapter from '@sveltejs/adapter-auto'; /** @type {import('@sveltejs/kit').Config} */ export default { kit: { - adapter: adapter() - } + adapter: adapter(), + prerender: { + // TODO: REMOVE + handleMissingId: 'ignore', + }, + }, }; From 7ec748291b70b8179009358ddeb8112222b67f11 Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Sun, 5 Feb 2023 17:55:42 +0530 Subject: [PATCH 04/12] heading --- sites/svelte.dev/src/routes/docs/+layout.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sites/svelte.dev/src/routes/docs/+layout.svelte b/sites/svelte.dev/src/routes/docs/+layout.svelte index 6fa5030f9b6c..e3e23d8c62e1 100644 --- a/sites/svelte.dev/src/routes/docs/+layout.svelte +++ b/sites/svelte.dev/src/routes/docs/+layout.svelte @@ -1,4 +1,5 @@ - - - Docs • Svelte - - - - - - - diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte index 0343a37c1670..4fcb062739e6 100644 --- a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte +++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte @@ -1,8 +1,10 @@ + + {data.page.title} - Svelte + + {@html data.page.content} From d012369b0c59a1009b14034d52be6aabeaf8522a Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Mon, 6 Feb 2023 14:36:09 +0530 Subject: [PATCH 07/12] Remove ukraine petition --- sites/svelte.dev/src/routes/+layout.svelte | 68 ++++------------------ 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/sites/svelte.dev/src/routes/+layout.svelte b/sites/svelte.dev/src/routes/+layout.svelte index 70849596bb74..6d5c9da35915 100644 --- a/sites/svelte.dev/src/routes/+layout.svelte +++ b/sites/svelte.dev/src/routes/+layout.svelte @@ -1,9 +1,8 @@ @@ -12,9 +11,9 @@ {/if} -{#if $page.url.pathname !== "/repl/embed"} +{#if $page.url.pathname !== '/repl/embed'} -