@@ -3,80 +3,12 @@ import { defineConfig } from 'mdsx';
33import rehypeSlug from 'rehype-slug' ;
44import remarkGfm from 'remark-gfm' ;
55import rehypePrettyCode from 'rehype-pretty-code' ;
6- import { transformerMetaHighlight } from '@shikijs/transformers' ;
76
8- import { readFileSync } from 'node:fs' ;
9- import path , { resolve } from 'node:path' ;
10- import process from 'node:process' ;
11- // import { fileURLToPath } from "node:url";
12- // import prettier from "@prettier/sync";
13- // import { getHighlighter } from 'shiki';
14- // import { createHighlighter } from 'shiki';
15- import { u } from 'unist-builder' ;
16- import { visit } from 'unist-util-visit' ;
17- // import { codeBlockPrettierConfig } from "./other/code-block-prettier.js";
18- // import { rehypeCustomHighlight } from '@mdsx/rehype-custom-highlighter';
19-
20- /**
21- * Custom transformer for diff highlighting
22- * Processes code blocks with 'diff' in the meta string to add/remove line styling
23- */
24- function shikiDiffTransformer ( ) {
25- return {
26- name : 'diff-transformer' ,
27- code ( node ) {
28- // Check if this code block has 'diff' in the meta string
29- const metaString = this . options . meta ?. __raw || '' ;
30- if ( ! metaString . includes ( 'diff' ) ) return ;
31-
32- // Add class to the pre element
33- this . addClassToHast ( this . pre , 'has-diff' ) ;
34-
35- // Get all line elements
36- const lines = node . children . filter ( ( child ) => child . type === 'element' ) ;
37-
38- for ( const line of lines ) {
39- // Get all text tokens in this line
40- const tokens = line . children . filter ( ( child ) => child . type === 'element' ) ;
41-
42- if ( tokens . length === 0 ) continue ;
43-
44- // Check the first token's text content
45- const firstToken = tokens [ 0 ] ;
46- const textNodes = firstToken . children ?. filter ( ( child ) => child . type === 'text' ) ;
47-
48- if ( ! textNodes || textNodes . length === 0 ) continue ;
49-
50- const firstText = textNodes [ 0 ] ;
51- const text = firstText . value ;
52-
53- if ( text . startsWith ( '+' ) ) {
54- this . addClassToHast ( line , 'diff-add' ) ;
55- firstText . value = text . slice ( 1 ) ;
56- } else if ( text . startsWith ( '-' ) ) {
57- this . addClassToHast ( line , 'diff-remove' ) ;
58- firstText . value = text . slice ( 1 ) ;
59- }
60- }
61- }
62- } ;
63- }
64-
65- /**
66- * @type {import('rehype-pretty-code').Options }
67- */
68- const prettyCodeOptions = {
69- theme : {
70- light : 'github-light' ,
71- dark : 'github-dark'
72- } ,
73- keepBackground : false ,
74- defaultLang : {
75- block : 'plaintext'
76- // inline: "plaintext",
77- } ,
78- transformers : [ shikiDiffTransformer ( ) , transformerMetaHighlight ( ) ]
79- } ;
7+ import {
8+ prettyCodeOptions ,
9+ rehypeCodeBlockTitle ,
10+ rehypeHandleCodeBlocks
11+ } from './src/lib/markdown/config/index.js' ;
8012
8113export const mdsxConfig = defineConfig ( {
8214 extensions : [ '.md' ] ,
@@ -94,175 +26,3 @@ export const mdsxConfig = defineConfig({
9426 }
9527 }
9628} ) ;
97-
98- /**
99- * Adds the source code to component examples.
100- *
101- * @returns {HastTransformer } - unified transformer
102- */
103- function rehypeComponentExample ( ) {
104- return async ( tree ) => {
105- const componentRegex = / c o m p o n e n t = " ( [ ^ " ] + ) " / ;
106- const nameRegex = / n a m e = " ( [ ^ " ] + ) " / ;
107-
108- visit ( tree , ( node , index , parent ) => {
109- if ( node ?. type === 'raw' && node ?. value ?. startsWith ( '<SourceExample' ) ) {
110- const currNode = node ;
111-
112- const componentMatch = currNode . value . match ( componentRegex ) ;
113- const component = componentMatch ? componentMatch [ 1 ] : null ;
114- if ( ! component ) return null ;
115-
116- const nameMatch = currNode . value . match ( nameRegex ) ;
117- const name = nameMatch ? nameMatch [ 1 ] : null ;
118- if ( ! name ) return null ;
119-
120- try {
121- const sourceCode = getComponentSourceFileContent ( component , name ) ;
122- if ( ! sourceCode )
123- throw new Error (
124- `Could not find source code for component: ${ component } example: ${ name } `
125- ) ;
126-
127- const sourceCodeNode = u ( 'element' , {
128- tagName : 'pre' ,
129- properties : {
130- className : [ 'code' ]
131- } ,
132- children : [
133- u ( 'element' , {
134- tagName : 'code' ,
135- properties : {
136- className : [ `language-svelte` ]
137- } ,
138- attributes : { } ,
139- children : [
140- {
141- type : 'text' ,
142- value : sourceCode
143- }
144- ]
145- } )
146- ]
147- } ) ;
148- if ( ! index ) return ;
149- parent . children . splice ( index + 1 , 0 , sourceCodeNode ) ;
150- } catch ( e ) {
151- console . error ( e ) ;
152- }
153- }
154- } ) ;
155- } ;
156- }
157-
158- function getComponentSourceFileContent ( component , name ) {
159- if ( ! component || ! name ) return null ;
160-
161- const filePath = path . join (
162- process . cwd ( ) ,
163- `./src/examples/components/${ component } /${ name } .svelte`
164- ) ;
165- try {
166- const fileContents = readFileSync ( filePath , 'utf-8' ) ;
167-
168- // return prettier.format(
169- // transformComponentSourceContent(fileContents),
170- // codeBlockPrettierConfig
171- // );
172- return fileContents ;
173- } catch ( e ) {
174- console . error ( `Error reading file at ${ filePath } :` , e ) ;
175- return null ;
176- }
177- }
178-
179- /**
180- * Handles metadata attributes for code blocks with titles
181- * Adds data-metadata attribute when a figcaption (title) is present
182- */
183- function rehypeCodeBlockTitle ( ) {
184- return ( tree ) => {
185- visit ( tree , 'element' , ( node ) => {
186- if (
187- node . tagName === 'figure' &&
188- node . properties ?. [ 'data-rehype-pretty-code-figure' ] !== undefined
189- ) {
190- const preElement = node . children ?. at ( - 1 ) ;
191- const firstChild = node . children ?. at ( 0 ) ;
192-
193- if (
194- preElement &&
195- preElement . type === 'element' &&
196- preElement . tagName === 'pre' &&
197- firstChild &&
198- firstChild . type === 'element' &&
199- firstChild . tagName === 'figcaption'
200- ) {
201- node . properties [ 'data-metadata' ] = '' ;
202- preElement . properties [ 'data-metadata' ] = '' ;
203- }
204- }
205- } ) ;
206- } ;
207- }
208-
209- /**
210- * Adds custom classes and data attributes to code blocks based on meta string
211- * Supports syntax like ```js frame title="My Code" showLineNumbers
212- */
213- function rehypeHandleCodeBlocks ( ) {
214- return ( tree ) => {
215- visit ( tree , 'element' , ( node ) => {
216- if ( node . tagName === 'pre' ) {
217- node . properties . className = node . properties . className || [ ] ;
218-
219- // Check if the code element has a data-meta attribute from rehype-pretty-code
220- const codeNode = node . children ?. find ( ( child ) => child . tagName === 'code' ) ;
221- const meta = codeNode ?. data ?. meta || codeNode ?. properties ?. metastring ;
222-
223- if ( meta ) {
224- // Check for 'frame' keyword
225- if ( meta . includes ( 'frame' ) ) {
226- node . properties [ 'data-frame' ] = '' ;
227- }
228-
229- // Extract title="..." if present
230- const titleMatch = meta . match ( / t i t l e = " ( [ ^ " ] + ) " / ) ;
231- if ( titleMatch ) {
232- node . properties [ 'data-title' ] = titleMatch [ 1 ] ;
233- }
234-
235- // Check for line numbers flag in meta
236- if ( meta . includes ( 'showLineNumbers' ) || meta . includes ( 'ln' ) ) {
237- node . properties [ 'data-line-numbers' ] = '' ;
238- }
239- }
240-
241- // Check recursively for [data-line] attribute in nested children
242- function hasDataLine ( element ) {
243- if ( element . properties ?. [ 'data-line' ] !== undefined ) {
244- return true ;
245- }
246- if ( element . children ) {
247- return element . children . some ( ( child ) => hasDataLine ( child ) ) ;
248- }
249- return false ;
250- }
251-
252- if ( codeNode && hasDataLine ( codeNode ) ) {
253- node . properties [ 'data-line-numbers' ] = '' ;
254- }
255-
256- // Extract language from code element
257- const codeClassName = codeNode ?. properties ?. className ;
258- if ( Array . isArray ( codeClassName ) ) {
259- const langClass = codeClassName . find ( ( cls ) => cls . startsWith ( 'language-' ) ) ;
260- if ( langClass ) {
261- const language = langClass . replace ( 'language-' , '' ) ;
262- node . properties [ 'data-language' ] = language ;
263- }
264- }
265- }
266- } ) ;
267- } ;
268- }
0 commit comments