Skip to content

Commit 767af5c

Browse files
committed
update node creation and refactor style injection
Signed-off-by: rishichawda <rishichawda@users.noreply.github.com>
1 parent 8d9e04e commit 767af5c

File tree

2 files changed

+99
-74
lines changed

2 files changed

+99
-74
lines changed

index.ts

Lines changed: 82 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,109 @@
11
import { visit } from 'unist-util-visit'
2-
import type { Node } from 'unist'
2+
import type { Node, Parent } from 'unist'
33
import 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'
95
import { 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

188
export 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+
}

lib/styles.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* CSS styles for remark-notes
3+
* Loaded once at module initialization
4+
*/
5+
6+
import { readFileSync } from 'fs'
7+
import { fileURLToPath } from 'url'
8+
import { dirname, join } from 'path'
9+
10+
const __filename = fileURLToPath(import.meta.url)
11+
const __dirname = dirname(__filename)
12+
13+
// Read CSS file once at module load time
14+
const cssPath = join(__dirname, '..', 'styles.css')
15+
export const styles = readFileSync(cssPath, 'utf-8')
16+
17+
export default styles

0 commit comments

Comments
 (0)