Skip to content

Commit c244cc5

Browse files
committed
WIP
1 parent cf5fb77 commit c244cc5

File tree

2 files changed

+114
-111
lines changed

2 files changed

+114
-111
lines changed

packages/site-kit/src/lib/components/Text.svelte

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@
4949
5050
.code-block {
5151
position: relative;
52+
box-shadow: 1px 2px 1rem hsla(0 0 0 / 0.08);
53+
margin: 2rem 0;
54+
55+
.controls {
56+
position: relative;
57+
58+
.ts-toggle {
59+
position: absolute;
60+
}
61+
}
62+
63+
&:has(.ts-toggle:checked) [data-language='js'] {
64+
display: none;
65+
}
66+
67+
&:has(.ts-toggle:not(:checked)) [data-language='ts'] {
68+
display: none;
69+
}
5270
5371
.filename {
5472
content: attr(data-file);
@@ -65,50 +83,41 @@
6583
}
6684
6785
pre {
68-
margin-top: 0;
69-
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
70-
}
71-
}
72-
73-
pre {
74-
position: relative;
75-
margin: 1em 0;
76-
width: 100%;
77-
padding: 1rem;
78-
box-sizing: border-box;
79-
color: var(--sk-code-base);
80-
border-radius: var(--sk-border-radius);
81-
font-size: var(--sk-text-s);
82-
overflow-x: auto;
83-
84-
code {
85-
display: block;
86-
padding: 0;
86+
position: relative;
8787
margin: 0;
88-
top: 0;
8988
width: 100%;
90-
background: transparent;
91-
}
89+
padding: 1rem;
90+
box-sizing: border-box;
91+
color: var(--sk-code-base);
92+
border-radius: var(--sk-border-radius);
93+
font-size: var(--sk-text-s);
94+
overflow-x: auto;
95+
96+
code {
97+
display: block;
98+
padding: 0;
99+
margin: 0;
100+
top: 0;
101+
width: 100%;
102+
background: transparent;
103+
}
92104
93-
a:hover {
94-
border-bottom: 1px solid var(--sk-theme-1);
95-
text-decoration: none;
96-
}
105+
a:hover {
106+
border-bottom: 1px solid var(--sk-theme-1);
107+
text-decoration: none;
108+
}
97109
98-
/* TODO what is this for? */
99-
&.border {
100-
border-left: 5px solid var(--sk-theme-2);
101-
}
110+
/* TODO what is this for? */
111+
&.border {
112+
border-left: 5px solid var(--sk-theme-2);
113+
}
102114
103-
&.language-diff code {
104-
color: var(--sk-code-diff-base);
115+
&.language-diff code {
116+
color: var(--sk-code-diff-base);
117+
}
105118
}
106119
}
107120
108-
.ts-block pre {
109-
margin: 0;
110-
}
111-
112121
p code {
113122
max-width: 100%;
114123
display: inline-flex;
@@ -117,11 +126,6 @@
117126
padding-bottom: 0;
118127
}
119128
120-
/* TODO what is this for? */
121-
.copy-code-block {
122-
box-shadow: 1px 2px 1rem hsla(0 0 0 / 0.08);
123-
}
124-
125129
a:not(.permalink) {
126130
color: inherit;
127131
text-decoration: underline;

packages/site-kit/src/lib/markdown/renderer.ts

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,23 @@ export async function render_content_markdown(
129129
const highlighter = await twoslash_module.createShikiHighlighter({ theme: 'css-variables' });
130130

131131
const { type_links, type_regex } = create_type_links(modules, resolveTypeLinks);
132-
const SNIPPET_CACHE = await create_snippet_cache(cacheCodeSnippets);
132+
const SNIPPET_CACHE = await create_snippet_cache(false);
133+
134+
body = await replace_export_type_placeholders(body, modules);
135+
136+
const conversions = new Map<string, string>();
137+
138+
for (const [_, language, code] of body.matchAll(/```(js|svelte)\n([\s\S]+?)\n```/g)) {
139+
let { source, options } = parse_options(code);
140+
141+
const converted = await generate_ts_from_js(source, language as 'js' | 'svelte', options);
142+
if (converted) {
143+
conversions.set(source, converted);
144+
}
145+
}
133146

134147
return parse({
135-
body: await generate_ts_from_js(await replace_export_type_placeholders(body, modules)),
148+
body,
136149
type_links,
137150
code: (raw, language, current) => {
138151
const cached_snippet = SNIPPET_CACHE.get(raw + language + current);
@@ -141,16 +154,25 @@ export async function render_content_markdown(
141154
let { source, options } = parse_options(raw);
142155
source = adjust_tab_indentation(source, language);
143156

144-
let version_class: 'ts-version' | 'js-version' | '' = '';
145-
if (/^generated-(ts|svelte)$/.test(language)) {
146-
language = language.replace('generated-', '');
147-
version_class = 'ts-version';
148-
} else if (/^original-(js|svelte)$/.test(language)) {
149-
language = language.replace('original-', '');
150-
version_class = 'js-version';
157+
const converted = conversions.get(source);
158+
159+
let html = '<div class="code-block"><div class="controls">';
160+
161+
if (options.file) {
162+
html += `<span class="filename">${options.file}</span>`;
163+
}
164+
165+
if (converted) {
166+
html += `<input class="ts-toggle" type="checkbox">`;
151167
}
152168

153-
let html = syntax_highlight({
169+
if (options.copy) {
170+
html += `<button class="copy-to-clipboard" aria-label="Copy to clipboard"></button>`;
171+
}
172+
173+
html += '</div>';
174+
175+
html += syntax_highlight({
154176
filename,
155177
highlighter,
156178
language,
@@ -159,18 +181,20 @@ export async function render_content_markdown(
159181
options
160182
});
161183

162-
if (options.file) {
163-
html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`;
164-
}
165-
166-
if (options.copy) {
167-
html = html.replace(/class=('|")/, `class=$1copy-code-block `);
184+
if (converted) {
185+
html += syntax_highlight({
186+
filename,
187+
highlighter,
188+
language: language === 'js' ? 'ts' : language,
189+
source: converted,
190+
twoslashBanner,
191+
options
192+
});
168193
}
169194

170-
if (version_class) {
171-
html = html.replace(/class=('|")/, `class=$1${version_class} `);
172-
}
195+
html += '</div>';
173196

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

@@ -189,10 +213,6 @@ export async function render_content_markdown(
189213
});
190214
}
191215

192-
html = indent_multiline_comments(html);
193-
194-
html = html.replace(/\/\*\*\//g, '…');
195-
196216
// Save everything locally now
197217
SNIPPET_CACHE.save(cached_snippet?.uid, html);
198218

@@ -273,57 +293,31 @@ async function parse({
273293
* Pre-render step. Takes in all the code snippets, and replaces them with TS snippets if possible
274294
* May replace the language labels (```js) to custom labels(```generated-ts, ```original-js, ```generated-svelte,```original-svelte)
275295
*/
276-
async function generate_ts_from_js(markdown: string) {
277-
markdown = await async_replace(markdown, /```js\n([\s\S]+?)\n```/g, async ([match, code]) => {
278-
if (!code.includes('/// file:')) {
279-
// No named file -> assume that the code is not meant to be shown in two versions
280-
return match;
281-
}
282-
283-
if (code.includes('/// file: svelte.config.js')) {
284-
// svelte.config.js has no TS equivalent
285-
return match;
286-
}
287-
288-
const ts = await convert_to_ts(code);
289-
290-
if (!ts) {
291-
// No changes -> don't show TS version
292-
return match;
293-
}
294-
295-
return match.replace('js', 'original-js') + '\n```generated-ts\n' + ts + '```';
296-
});
296+
async function generate_ts_from_js(
297+
code: string,
298+
language: 'js' | 'svelte',
299+
options: SnippetOptions
300+
) {
301+
// No named file -> assume that the code is not meant to be shown in two versions
302+
if (!options.file) return;
297303

298-
markdown = await async_replace(markdown, /```svelte\n([\s\S]+?)\n```/g, async ([match, code]) => {
299-
METADATA_REGEX.lastIndex = 0;
304+
if (language === 'js') {
305+
// config files have no .ts equivalent
306+
if (options.file === 'svelte.config.js') return;
300307

301-
if (!METADATA_REGEX.test(code)) {
302-
// No named file -> assume that the code is not meant to be shown in two versions
303-
return match;
304-
}
305-
306-
// Assumption: no module blocks
307-
const script = code.match(/<script>([\s\S]+?)<\/script>/);
308-
if (!script) return match;
308+
return await convert_to_ts(code.replace(/\/\/\/ file: .+?\n/, ''));
309+
}
309310

310-
const [outer, inner] = script;
311-
const ts = await convert_to_ts(inner, '\t', '\n');
311+
// Assumption: no module blocks
312+
const script = code.match(/<script>([\s\S]+?)<\/script>/);
313+
if (!script) return;
312314

313-
if (!ts) {
314-
// No changes -> don't show TS version
315-
return match;
316-
}
315+
const [outer, inner] = script;
316+
const ts = await convert_to_ts(inner, '\t', '\n');
317317

318-
return (
319-
match.replace('svelte', 'original-svelte') +
320-
'\n```generated-svelte\n' +
321-
code.replace(outer, `<script lang="ts">\n\t${ts.trim()}\n</script>`) +
322-
'\n```'
323-
);
324-
});
318+
if (!ts) return;
325319

326-
return markdown;
320+
return code.replace(outer, `<script lang="ts">\n\t${ts.trim()}\n</script>`);
327321
}
328322

329323
function get_jsdoc(node: ts.Node) {
@@ -477,8 +471,9 @@ export async function convert_to_ts(js_code: string, indent = '', offset = '') {
477471

478472
// Indent transformed's each line by 2
479473
transformed = transformed
474+
.replace(/\n$/, '')
480475
.split('\n')
481-
.map((line) => indent.repeat(1) + line)
476+
.map((line) => indent + line)
482477
.join('\n');
483478

484479
return transformed === js_code ? undefined : transformed.replace(/\n\s*\n\s*\n/g, '\n\n');
@@ -1001,6 +996,8 @@ function syntax_highlight({
1001996
}) {
1002997
let html = '';
1003998

999+
console.log({ source });
1000+
10041001
if (/^(dts|yaml|yml)/.test(language)) {
10051002
html = replace_blank_lines(
10061003
twoslash_module.renderCodeToHTML(
@@ -1090,7 +1087,9 @@ function syntax_highlight({
10901087
html = replace_blank_lines(highlighted);
10911088
}
10921089

1093-
return html;
1090+
return indent_multiline_comments(html)
1091+
.replace(/\/\*\*\//g, '…')
1092+
.replace('<pre', `<pre data-language="${language}"`);
10941093
}
10951094

10961095
function indent_multiline_comments(str: string) {

0 commit comments

Comments
 (0)