11import { visit } from 'unist-util-visit'
2- import type { Node } from 'unist'
2+ import type { Node , Parent } from 'unist'
33import type { Blockquote , Paragraph , Text } from 'mdast'
4- import { readFileSync } from 'fs'
5- import { fileURLToPath } from 'url'
6- import { dirname , join } from 'path'
7- import { toHtml } from 'hast-util-to-html'
8- import { toHast } from 'mdast-util-to-hast'
4+ import type { NoteNode } from './lib/types/index.js'
95import { NOTE_TYPES } from './lib/types/index.js'
10-
11- // Get the styles content
12- const __filename = fileURLToPath ( import . meta. url )
13- const __dirname = dirname ( __filename )
14- // Look for styles.css in the package root (one level up from dist)
15- const stylesPath = join ( __dirname , 'styles.css' )
16- const styles = readFileSync ( stylesPath , 'utf-8' )
6+ import { styles } from './lib/styles.js'
177
188export default function remarkNotes ( ) {
19- return ( tree : Node ) => {
20- let hasInjectedStyles = false ;
9+ let hasInjectedStyles = false
2110
22- visit ( tree , 'root' , ( node : any ) => {
23- if ( ! hasInjectedStyles ) {
24- // Inject styles at the beginning of the document
25- node . children . unshift ( {
11+ return ( tree : Node ) => {
12+ // Inject styles at the beginning of the document (only once)
13+ if ( ! hasInjectedStyles ) {
14+ const root = tree as Parent
15+ if ( root . children ) {
16+ root . children . unshift ( {
2617 type : 'html' ,
2718 value : `<style>${ styles } </style>`
28- } ) ;
29- hasInjectedStyles = true ;
19+ } as any )
20+ hasInjectedStyles = true
3021 }
31- } ) ;
22+ }
3223
33- visit ( tree , 'blockquote' , ( node : Blockquote ) => {
34- const firstParagraph = node . children [ 0 ] as Paragraph
35- if ( ! firstParagraph ) return
24+ visit ( tree , 'blockquote' , ( node : Blockquote , index , parent ) => {
25+ // Validate we have parent and index for proper node replacement
26+ if ( ! parent || index === null || index === undefined ) return
27+
28+ const firstParagraph = node . children [ 0 ]
29+ if ( ! firstParagraph || firstParagraph . type !== 'paragraph' ) return
3630
3731 const firstChild = firstParagraph . children [ 0 ]
3832 if ( ! firstChild || firstChild . type !== 'text' ) return
3933
4034 const textNode = firstChild as Text
41- if ( ! textNode ) return
4235
43- // Updated pattern to match [!type]
36+ // Match [!type] pattern
4437 const match = textNode . value . match ( / ^ \[ ! ( \w + ) \] / )
4538 if ( ! match ) return
4639
47- const noteType = match [ 1 ] . toLowerCase ( ) // Convert to lowercase to match our types
40+ const noteType = match [ 1 ] . toLowerCase ( )
4841
49- if ( noteType in NOTE_TYPES ) {
50- const noteConfig = NOTE_TYPES [ noteType ]
51-
52- // Create a clone of the blockquote children to preserve markdown structure
53- const children = [ ...node . children ]
42+ // Only transform if it's a recognized note type
43+ if ( ! ( noteType in NOTE_TYPES ) ) return
44+
45+ // Clone children to preserve original markdown structure
46+ const children = [ ...node . children ]
47+
48+ // Process the first paragraph to remove the note marker
49+ if ( children . length > 0 && children [ 0 ] . type === 'paragraph' ) {
50+ const firstPara = children [ 0 ] as Paragraph
51+ const firstTextNode = firstPara . children [ 0 ] as Text
5452
55- // Process the first paragraph to remove the note marker
56- if ( children . length > 0 && children [ 0 ] . type === 'paragraph' ) {
57- const firstPara = children [ 0 ] as Paragraph
58- const firstTextNode = firstPara . children [ 0 ] as Text
53+ if ( firstTextNode && firstTextNode . type === 'text' ) {
54+ // Remove the [!type] marker and any trailing whitespace
55+ firstTextNode . value = firstTextNode . value . replace ( / ^ \[ ! \w + \] \s * / , '' )
5956
60- if ( firstTextNode && firstTextNode . type === 'text' ) {
61- // Only remove the marker from the text content
62- firstTextNode . value = firstTextNode . value . replace ( / ^ \[ ! \w + \] \s * / , '' )
63-
64- // If the text node is now empty and it's the only child, remove it
65- if ( firstTextNode . value === '' && firstPara . children . length === 1 ) {
66- children . shift ( )
67- }
57+ // If the text node is now empty and it's the only child, remove the paragraph
58+ if ( firstTextNode . value === '' && firstPara . children . length === 1 ) {
59+ children . shift ( )
6860 }
6961 }
70-
71- // Convert the modified markdown structure to HTML
72- const contentHast = toHast ( {
73- type : 'root' ,
74- children : children
75- } ) as any
76-
77- // Convert hast to HTML string
78- const contentHtml = contentHast ? toHtml ( contentHast ) : ''
79-
80- // Create HTML structure using classes
81- const html = {
82- type : 'html' ,
83- value : `
84- <div class="remark-note ${ noteConfig . type } ">
85- <div class="remark-note-header">
86- <span class="remark-note-icon" data-type="${ noteConfig . type } ">${ noteConfig . icon } </span>
87- <span class="remark-note-title">${ noteType } </span>
88- </div>
89- <div class="remark-note-content">
90- ${ contentHtml }
91- </div>
92- </div>
93- `
94- }
95-
96- // Replace the blockquote with our HTML
97- Object . assign ( node , html )
9862 }
63+
64+ // Get the icon for this note type
65+ const noteConfig = NOTE_TYPES [ noteType ]
66+ const icon = noteConfig . icon
67+
68+ // Create header with icon and title
69+ const headerNode = {
70+ type : 'html' ,
71+ value : `<div class="remark-note-header"><span class="remark-note-icon">${ icon } </span><span class="remark-note-title">${ noteType } </span></div>`
72+ }
73+
74+ const contentWrapperStart = {
75+ type : 'html' ,
76+ value : '<div class="remark-note-content">'
77+ }
78+
79+ const contentWrapperEnd = {
80+ type : 'html' ,
81+ value : '</div>'
82+ }
83+
84+ // Wrap content with header and content wrapper
85+ const wrappedChildren = [
86+ headerNode ,
87+ contentWrapperStart ,
88+ ...children ,
89+ contentWrapperEnd
90+ ]
91+
92+ // Create the custom NoteNode
93+ const noteNode = {
94+ type : 'note' ,
95+ noteType : noteType ,
96+ data : {
97+ hName : 'div' ,
98+ hProperties : {
99+ className : [ 'remark-note' , noteType ]
100+ }
101+ } ,
102+ children : wrappedChildren
103+ } as NoteNode
104+
105+ // Replace the blockquote with the note node in the parent
106+ ( parent as Parent ) . children [ index ] = noteNode as any
99107 } )
100108 }
101- }
109+ }
0 commit comments