${highlighted}
${text}
`
- : `${text}
`
- }),
+ html: (await render_content(exercise.file, exercise.body, { check: false })).replace(
+ /(.+?)<\/code>/g,
+ (match, filename) => {
+ // TODO wire this up
+ return filenames.size > 1 && filenames.has(filename)
+ ? `${filename}
`
+ : match;
+ }
+ ),
dir: exercise.file.split('/').slice(0, -1).join('/'),
editing_constraints: {
create: new Set(exercise.metadata.editing_constraints?.create ?? []),
diff --git a/packages/site-kit/src/lib/markdown/renderer.ts b/packages/site-kit/src/lib/markdown/renderer.ts
index 2ec91cc8d5..110901d60c 100644
--- a/packages/site-kit/src/lib/markdown/renderer.ts
+++ b/packages/site-kit/src/lib/markdown/renderer.ts
@@ -15,12 +15,7 @@ interface SnippetOptions {
copy: boolean;
}
-type TwoslashBanner = (
- filename: string,
- content: string,
- language: string,
- options: SnippetOptions
-) => string;
+type TwoslashBanner = (filename: string, content: string) => string;
// Supports js, svelte, yaml files
const METADATA_REGEX =
@@ -191,9 +186,11 @@ const snippets = await create_snippet_cache();
export async function render_content_markdown(
filename: string,
body: string,
- { twoslashBanner }: { twoslashBanner?: TwoslashBanner } = {}
+ options: { check?: boolean },
+ twoslashBanner?: TwoslashBanner
) {
const headings: string[] = [];
+ const { check = true } = options;
return await transform(body, {
async walkTokens(token) {
@@ -207,11 +204,17 @@ export async function render_content_markdown(
let { source, options } = parse_options(token.text, token.lang);
source = adjust_tab_indentation(source, token.lang);
- const match = /((?:[\s\S]+)\/\/ ---cut---\n)?([\s\S]+)/.exec(source)!;
+ let prelude = '';
+
+ if ((token.lang === 'js' || token.lang === 'ts') && check) {
+ const match = /((?:[\s\S]+)\/\/ ---cut---\n)?([\s\S]+)/.exec(source)!;
+ [, prelude = '// ---cut---\n', source] = match;
- const prelude = match[1];
+ const banner = twoslashBanner?.(filename, source);
+ if (banner) prelude = '// @filename: injected.d.ts\n' + banner + '\n' + prelude;
+ }
- source = match[2].replace(
+ source = source.replace(
/(\+\+\+|---|:::)/g,
(_, delimiter: keyof typeof delimiter_substitutes) => {
return delimiter_substitutes[delimiter];
@@ -242,24 +245,16 @@ export async function render_content_markdown(
html += '';
- html += await syntax_highlight({
- prelude,
- filename,
- language: token.lang,
- source,
- twoslashBanner,
- options
- });
+ html += await syntax_highlight({ filename, language: token.lang, prelude, source, check });
if (converted) {
- html += await syntax_highlight({
- prelude,
- filename,
- language: token.lang === 'js' ? 'ts' : token.lang,
- source: converted,
- twoslashBanner,
- options
- });
+ const language = token.lang === 'js' ? 'ts' : token.lang;
+
+ if (language === 'ts') {
+ prelude = prelude.replace(/(\/\/ @filename: .+)\.js$/gm, '$1.ts');
+ }
+
+ html += await syntax_highlight({ filename, language, prelude, source: converted, check });
}
html += '';
@@ -318,7 +313,6 @@ export async function render_content_markdown(
/**
* Pre-render step. Takes in all the code snippets, and replaces them with TS snippets if possible
- * May replace the language labels (```js) to custom labels(```generated-ts, ```original-js, ```generated-svelte,```original-svelte)
*/
async function generate_ts_from_js(
code: string,
@@ -328,32 +322,23 @@ async function generate_ts_from_js(
// No named file -> assume that the code is not meant to be shown in two versions
if (!options.file) return;
- if (language === 'js') {
- // config files have no .ts equivalent
- if (options.file === 'svelte.config.js') return;
+ // config files have no .ts equivalent
+ if (options.file === 'svelte.config.js') return;
- let [before, after] = code.split('// ---cut---\n');
+ if (language === 'svelte') {
+ // Assumption: no module blocks
+ const script = code.match(/`);
}
- // Assumption: no module blocks
- const script = code.match(/`);
+ return await convert_to_ts(code);
}
function get_jsdoc(node: ts.Node) {
@@ -372,15 +357,6 @@ async function convert_to_ts(js_code: string, indent = '', offset = '') {
// *\/ appears in some JsDoc comments in d.ts files due to the JSDoc-in-JSDoc problem
.replace(/\*\\\//g, '*/');
- // TODO temp
- if (js_code.includes('// ---cut---')) {
- throw new Error('unexpected cut directive');
- }
-
- if (js_code.includes('/// file:')) {
- throw new Error('unexpected file directive');
- }
-
const ast = ts.createSourceFile(
'filename.ts',
js_code,
@@ -653,15 +629,13 @@ async function syntax_highlight({
source,
filename,
language,
- twoslashBanner,
- options
+ check
}: {
prelude: string;
source: string;
filename: string;
language: string;
- twoslashBanner?: TwoslashBanner;
- options: SnippetOptions;
+ check: boolean;
}) {
let html = '';
@@ -673,18 +647,6 @@ async function syntax_highlight({
})
);
} else if (language === 'js' || language === 'ts') {
- let banner = twoslashBanner?.(filename, source, language, options);
-
- if (banner) {
- banner = '// @filename: injected.d.ts\n' + banner;
- }
-
- prelude = (banner ?? '') + '\n' + (prelude ?? '// ---cut---\n');
-
- if (language === 'ts') {
- prelude = prelude.replace(/(\/\/ @filename: .+)\.js$/gm, '$1.ts');
- }
-
/** We need to stash code wrapped in `---` highlights, because otherwise TS will error on e.g. bad syntax, duplicate declarations */
const redactions: string[] = [];
@@ -692,19 +654,22 @@ async function syntax_highlight({
redactions.push(content);
return ' '.repeat(content.length);
});
+
try {
html = await codeToHtml(prelude + redacted, {
lang: 'ts',
theme,
- transformers: [
- transformerTwoslash({
- twoslashOptions: {
- compilerOptions: {
- types: ['svelte', '@sveltejs/kit']
- }
- }
- })
- ]
+ transformers: check
+ ? [
+ transformerTwoslash({
+ twoslashOptions: {
+ compilerOptions: {
+ types: ['svelte', '@sveltejs/kit']
+ }
+ }
+ })
+ ]
+ : []
});
html = html.replace(/ {27,}/g, () => redactions.shift()!);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7e84fa10ba..5e1d0e548f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -128,12 +128,6 @@ importers:
port-authority:
specifier: ^2.0.1
version: 2.0.1
- prism-svelte:
- specifier: ^0.5.0
- version: 0.5.0
- prismjs:
- specifier: ^1.29.0
- version: 1.29.0
shiki:
specifier: ^1.22.0
version: 1.22.0
@@ -2592,13 +2586,6 @@ packages:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
- prism-svelte@0.5.0:
- resolution: {integrity: sha512-db91Bf3pRGKDPz1lAqLFSJXeW13mulUJxhycysFpfXV5MIK7RgWWK2E5aPAa71s8TCzQUXxF5JOV42/iOs6QkA==}
-
- prismjs@1.29.0:
- resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
- engines: {node: '>=6'}
-
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
@@ -5389,10 +5376,6 @@ snapshots:
ansi-styles: 5.2.0
react-is: 17.0.2
- prism-svelte@0.5.0: {}
-
- prismjs@1.29.0: {}
-
property-information@6.5.0: {}
pseudomap@1.0.2: {}