@@ -12,6 +12,7 @@ import mime from "mime";
1212import { join } from "path" ;
1313import { canReadSync } from "./files.js" ;
1414import { transpileJavaScript , type FileReference , type ImportReference , type Transpile } from "./javascript.js" ;
15+ import { computeHash } from "./hash.js" ;
1516
1617export interface HtmlPiece {
1718 type : "html" ;
@@ -42,8 +43,6 @@ interface RenderPiece {
4243}
4344
4445interface ParseContext {
45- id : number ;
46- js : string ;
4746 pieces : RenderPiece [ ] ;
4847 files : { name : string ; mimeType : string | null } [ ] ;
4948 imports : ImportReference [ ] ;
@@ -69,14 +68,24 @@ function makeHtmlRenderer(root: string, baseRenderer: RenderRule): RenderRule {
6968 } ;
7069}
7170
71+ function uniqueCodeId ( context : ParseContext , content : string ) : string {
72+ const hash = String ( computeHash ( content ) ) ;
73+ let id = hash ;
74+ let count = 1 ;
75+ while ( context . pieces . some ( ( piece ) => piece . code . some ( ( code ) => code . id === id ) ) ) {
76+ id = `${ hash } -${ count ++ } ` ;
77+ }
78+ return id ;
79+ }
80+
7281function makeFenceRenderer ( root : string , baseRenderer : RenderRule ) : RenderRule {
7382 return ( tokens , idx , options , context : ParseContext , self ) => {
7483 const token = tokens [ idx ] ;
7584 const [ language , option ] = token . info . split ( " " ) ;
7685 let result = "" ;
7786 let count = 0 ;
7887 if ( language === "js" && option !== "no-run" ) {
79- const id = ++ context . id ;
88+ const id = uniqueCodeId ( context , token . content ) ;
8089 const transpile = transpileJavaScript ( token . content , {
8190 id,
8291 root,
@@ -163,12 +172,12 @@ function transformPlaceholderBlock(token) {
163172 const output : any [ ] = [ ] ;
164173 let i = 0 ;
165174 parsePlaceholder ( input , ( j , k ) => {
166- output . push ( { ...token , content : input . slice ( i , j ) } ) ;
167- output . push ( { type : "placeholder" , content : input . slice ( j + 2 , k - 1 ) } ) ;
175+ output . push ( { ...token , level : i > 0 ? token . level + 1 : token . level , content : input . slice ( i , j ) } ) ;
176+ output . push ( { type : "placeholder" , level : token . level + 1 , content : input . slice ( j + 2 , k - 1 ) } ) ;
168177 i = k ;
169178 } ) ;
170179 if ( i === 0 ) return [ token ] ;
171- else if ( i < input . length ) output . push ( { ...token , content : input . slice ( i ) } ) ;
180+ else if ( i < input . length ) output . push ( { ...token , content : input . slice ( i ) , nesting : - 1 } ) ;
172181 return output ;
173182}
174183
@@ -229,7 +238,7 @@ const transformPlaceholderCore: RuleCore = (state) => {
229238
230239function makePlaceholderRenderer ( root : string ) : RenderRule {
231240 return ( tokens , idx , options , context : ParseContext ) => {
232- const id = ++ context . id ;
241+ const id = uniqueCodeId ( context , tokens [ idx ] . content ) ;
233242 const token = tokens [ idx ] ;
234243 const transpile = transpileJavaScript ( token . content , {
235244 id,
@@ -335,7 +344,7 @@ export function parseMarkdown(source: string, root: string): ParseResult {
335344 md . renderer . rules . fence = makeFenceRenderer ( root , md . renderer . rules . fence ! ) ;
336345 md . renderer . rules . softbreak = makeSoftbreakRenderer ( md . renderer . rules . softbreak ! ) ;
337346 md . renderer . render = renderIntoPieces ( md . renderer ) ;
338- const context : ParseContext = { id : 0 , js : "" , files : [ ] , imports : [ ] , pieces : [ ] , startLine : 0 , currentLine : 0 } ;
347+ const context : ParseContext = { files : [ ] , imports : [ ] , pieces : [ ] , startLine : 0 , currentLine : 0 } ;
339348 const tokens = md . parse ( parts . content , context ) ;
340349 const html = md . renderer . render ( tokens , md . options , context ) ;
341350 return {
0 commit comments