@@ -129,10 +129,23 @@ export async function render_content_markdown(
129
129
const highlighter = await twoslash_module . createShikiHighlighter ( { theme : 'css-variables' } ) ;
130
130
131
131
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 ( / ` ` ` ( j s | s v e l t e ) \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
+ }
133
146
134
147
return parse ( {
135
- body : await generate_ts_from_js ( await replace_export_type_placeholders ( body , modules ) ) ,
148
+ body,
136
149
type_links,
137
150
code : ( raw , language , current ) => {
138
151
const cached_snippet = SNIPPET_CACHE . get ( raw + language + current ) ;
@@ -141,16 +154,25 @@ export async function render_content_markdown(
141
154
let { source, options } = parse_options ( raw ) ;
142
155
source = adjust_tab_indentation ( source , language ) ;
143
156
144
- let version_class : 'ts-version' | 'js-version' | '' = '' ;
145
- if ( / ^ g e n e r a t e d - ( t s | s v e l t e ) $ / . test ( language ) ) {
146
- language = language . replace ( 'generated-' , '' ) ;
147
- version_class = 'ts-version' ;
148
- } else if ( / ^ o r i g i n a l - ( j s | s v e l t e ) $ / . 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">` ;
151
167
}
152
168
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 ( {
154
176
filename,
155
177
highlighter,
156
178
language,
@@ -159,18 +181,20 @@ export async function render_content_markdown(
159
181
options
160
182
} ) ;
161
183
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 ( / c l a s s = ( ' | " ) / , `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
+ } ) ;
168
193
}
169
194
170
- if ( version_class ) {
171
- html = html . replace ( / c l a s s = ( ' | " ) / , `class=$1${ version_class } ` ) ;
172
- }
195
+ html += '</div>' ;
173
196
197
+ // TODO this is currently disabled, we don't have access to `modules`
174
198
if ( type_regex ) {
175
199
type_regex . lastIndex = 0 ;
176
200
@@ -189,10 +213,6 @@ export async function render_content_markdown(
189
213
} ) ;
190
214
}
191
215
192
- html = indent_multiline_comments ( html ) ;
193
-
194
- html = html . replace ( / \/ \* … \* \/ / g, '…' ) ;
195
-
196
216
// Save everything locally now
197
217
SNIPPET_CACHE . save ( cached_snippet ?. uid , html ) ;
198
218
@@ -273,57 +293,31 @@ async function parse({
273
293
* Pre-render step. Takes in all the code snippets, and replaces them with TS snippets if possible
274
294
* May replace the language labels (```js) to custom labels(```generated-ts, ```original-js, ```generated-svelte,```original-svelte)
275
295
*/
276
- async function generate_ts_from_js ( markdown : string ) {
277
- markdown = await async_replace ( markdown , / ` ` ` j s \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 ;
297
303
298
- markdown = await async_replace ( markdown , / ` ` ` s v e l t e \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 ;
300
307
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 ( / < s c r i p t > ( [ \s \S ] + ?) < \/ s c r i p t > / ) ;
308
- if ( ! script ) return match ;
308
+ return await convert_to_ts ( code . replace ( / \/ \/ \/ f i l e : .+ ?\n / , '' ) ) ;
309
+ }
309
310
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 ( / < s c r i p t > ( [ \s \S ] + ?) < \/ s c r i p t > / ) ;
313
+ if ( ! script ) return ;
312
314
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' ) ;
317
317
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 ;
325
319
326
- return markdown ;
320
+ return code . replace ( outer , `<script lang="ts">\n\t ${ ts . trim ( ) } \n</script>` ) ;
327
321
}
328
322
329
323
function get_jsdoc ( node : ts . Node ) {
@@ -477,8 +471,9 @@ export async function convert_to_ts(js_code: string, indent = '', offset = '') {
477
471
478
472
// Indent transformed's each line by 2
479
473
transformed = transformed
474
+ . replace ( / \n $ / , '' )
480
475
. split ( '\n' )
481
- . map ( ( line ) => indent . repeat ( 1 ) + line )
476
+ . map ( ( line ) => indent + line )
482
477
. join ( '\n' ) ;
483
478
484
479
return transformed === js_code ? undefined : transformed . replace ( / \n \s * \n \s * \n / g, '\n\n' ) ;
@@ -1001,6 +996,8 @@ function syntax_highlight({
1001
996
} ) {
1002
997
let html = '' ;
1003
998
999
+ console . log ( { source } ) ;
1000
+
1004
1001
if ( / ^ ( d t s | y a m l | y m l ) / . test ( language ) ) {
1005
1002
html = replace_blank_lines (
1006
1003
twoslash_module . renderCodeToHTML (
@@ -1090,7 +1087,9 @@ function syntax_highlight({
1090
1087
html = replace_blank_lines ( highlighted ) ;
1091
1088
}
1092
1089
1093
- return html ;
1090
+ return indent_multiline_comments ( html )
1091
+ . replace ( / \/ \* … \* \/ / g, '…' )
1092
+ . replace ( '<pre' , `<pre data-language="${ language } "` ) ;
1094
1093
}
1095
1094
1096
1095
function indent_multiline_comments ( str : string ) {
0 commit comments