11'use strict' ;
22
3+ import { createTypeAnnotationHandler } from '@doc-kit/core/plugins/type-annotations/highlighter.mjs' ;
34import { toJsxRuntime } from 'hast-util-to-jsx-runtime' ;
45import { renderToString } from 'preact-render-to-string' ;
56import { Fragment , jsx , jsxs } from 'preact/jsx-runtime' ;
@@ -51,29 +52,39 @@ const render = children =>
5152 ) ;
5253
5354/**
54- * Turns the `<code>` of highlighted code into a JSX `<code>` holding its
55- * markup as it is.
55+ * The attributes and the markup of the `<code>` of highlighted code.
5656 *
5757 * @param {import('hast').Element } code - The `<code>`
58- * @param { boolean } [inline] - Whether it's a type's, rather than a block's
58+ * @returns { { attributes: Record<string, unknown>, html: string } }
5959 */
60- const embedCode = ( code , inline = false ) => {
60+ const toMarkup = code => {
6161 const classNames = getClassNames ( code ) ;
6262
6363 // JSX takes the class names as one `className`, which comes first, as
6464 // `class` did
6565 const attributes = {
66- inline,
6766 className : classNames . length > 0 ? classNames . join ( ' ' ) : undefined ,
6867 ...code . properties ,
69- dangerouslySetInnerHTML : { __html : render ( code . children ) } ,
7068 } ;
7169
7270 delete attributes . class ;
7371
74- return createJSXElement ( 'code' , attributes ) ;
72+ return { attributes, html : render ( code . children ) } ;
7573} ;
7674
75+ /**
76+ * A JSX `<code>` holding highlighted markup as it is.
77+ *
78+ * @param {ReturnType<typeof toMarkup> } markup
79+ * @param {boolean } [inline] - Whether it's a type's, rather than a block's
80+ */
81+ const createCodeElement = ( { attributes, html } , inline = false ) =>
82+ createJSXElement ( 'code' , {
83+ inline,
84+ ...attributes ,
85+ dangerouslySetInnerHTML : { __html : html } ,
86+ } ) ;
87+
7788/**
7889 * Embeds the code blocks of a tree that Shiki highlighted.
7990 *
@@ -87,7 +98,7 @@ export const embedHighlightedBlocks = tree => {
8798
8899 if ( node . tagName === 'pre' && code ?. tagName === 'code' ) {
89100 if ( isHighlighted ( node ) ) {
90- node . children [ 0 ] = embedCode ( code ) ;
101+ node . children [ 0 ] = createCodeElement ( toMarkup ( code ) ) ;
91102 }
92103
93104 return SKIP ;
@@ -98,25 +109,46 @@ export const embedHighlightedBlocks = tree => {
98109} ;
99110
100111/**
101- * Wraps a `typeAnnotation` handler of `remark-rehype`, embedding the types it
102- * highlights.
112+ * Creates the `typeAnnotation` handler of `remark-rehype` highlighting types
113+ * and embedding them.
114+ *
115+ * Every highlighted type is kept, by highlighter: the same few hundred types
116+ * are highlighted thousands of times, `{string}` alone on most pages.
103117 *
104- * @param {(state: import('mdast-util-to-hast ').State, node: import('mdast').Node) => import('hast').Element } handler
105- * @returns {typeof handler }
118+ * @param {() => import('@doc-kit/core/plugins/shiki/highlighter.mjs ').SyntaxHighlighter } getHighlighter - Gives the highlighter, once a type is highlighted
119+ * @returns {(state: import('mdast-util-to-hast').State, node: import('mdast').Node) => import('hast').ElementContent }
106120 */
107- export const embedHighlightedTypes = handler => ( state , node ) => {
108- const result = handler ( state , node ) ;
121+ export const embedHighlightedTypes = getHighlighter => {
122+ const highlight = createTypeAnnotationHandler ( getHighlighter ) ;
123+ const highlighted = new WeakMap ( ) ;
109124
110- // A type that didn't parse, or links nowhere, isn't highlighted
111- if ( ! isHighlighted ( result ) ) {
112- return result ;
113- }
125+ return ( state , node ) => {
126+ const highlighter = getHighlighter ( ) ;
127+
128+ if ( ! highlighted . has ( highlighter ) ) {
129+ highlighted . set ( highlighter , new Map ( ) ) ;
130+ }
131+
132+ const types = highlighted . get ( highlighter ) ;
133+ const key = JSON . stringify ( [ node . value , node . data ] ) ;
134+
135+ if ( ! types . has ( key ) ) {
136+ const result = highlight ( state , node ) ;
114137
115- const code = embedCode ( result , true ) ;
138+ // A type that didn't parse, or links nowhere, isn't highlighted
139+ if ( ! isHighlighted ( result ) ) {
140+ return result ;
141+ }
142+
143+ types . set ( key , toMarkup ( result ) ) ;
144+ }
116145
117- state . patch ( node , code ) ;
146+ const code = createCodeElement ( types . get ( key ) , true ) ;
118147
119- return code ;
148+ state . patch ( node , code ) ;
149+
150+ return code ;
151+ } ;
120152} ;
121153
122154/**
0 commit comments