Skip to content

Commit b0c9ae7

Browse files
committed
chore(rehype): follow reviewer guidance - keep HAST-only behavior and document HTML postprocess approach
1 parent 5068b26 commit b0c9ae7

File tree

3 files changed

+28
-4
lines changed

3 files changed

+28
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ cache
2222
report-engine-js-compat.json
2323
scripts/compares
2424
tm-grammars-themes
25+
.pnpm-store

packages/rehype/src/core.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,27 @@ function rehypeShikiFromHighlighter(
7272
code = code.slice(0, -1)
7373

7474
try {
75+
// NOTE: per the `@shikijs/types` contract the `postprocess` hook is only
76+
// invoked when producing HTML strings (i.e. `codeToHtml`). Rehype
77+
// integrations operate on HAST. Running `postprocess` here would require
78+
// converting HAST -> HTML -> run postprocess -> parse back to HAST which
79+
// changes the semantics and surprises users expecting HAST-only
80+
// transformers.
81+
//
82+
// If you need to run HTML-based postprocessing with rehype, apply a
83+
// root transformer that converts the HAST fragment to HTML using
84+
// `hast-util-to-html`, runs your HTML transformers, then converts back
85+
// with `hast-util-from-html`.
86+
//
87+
// Example (inside a root transformer):
88+
// const html = toHtml(fragment)
89+
// const newHtml = myPostprocess(html)
90+
// fragment = fromHtml(newHtml, { fragment: true })
91+
//
92+
// We therefore keep the HAST-only path here.
93+
7594
const fragment = highlighter.codeToHast(code, codeOptions)
95+
7696
cache?.set(cacheKey, fragment)
7797
return fragment
7898
}
@@ -85,13 +105,11 @@ function rehypeShikiFromHighlighter(
85105
}
86106

87107
return (tree) => {
88-
// use this queue if lazy is enabled
89108
const queue: Promise<void>[] = []
90109

91110
visit(tree, 'element', (node, index, parent) => {
92111
let handler: RehypeShikiHandler | undefined
93112

94-
// needed for hast node replacement
95113
if (!parent || index == null)
96114
return
97115

@@ -148,7 +166,6 @@ function rehypeShikiFromHighlighter(
148166

149167
if (lazyLoad) {
150168
try {
151-
// passed language is checked in sync, promise `.catch()` wouldn't work
152169
queue.push(highlighter.loadLanguage(lang).then(() => processNode(lang)))
153170
}
154171
catch (error) {
@@ -163,7 +180,6 @@ function rehypeShikiFromHighlighter(
163180
processNode(lang)
164181
}
165182

166-
// don't visit processed nodes
167183
return 'skip'
168184
})
169185

packages/rehype/test/core.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ it('run', async () => {
3838
await expect(file.toString()).toMatchFileSnapshot('./fixtures/a.core.out.html')
3939
})
4040

41+
// The `postprocess` transformer hook is only called for HTML-producing
42+
// flows (see `@shikijs/types`), so it is not exercised here. If users want
43+
// to run HTML-based postprocessing in rehype, they should apply a root
44+
// transformer that converts the HAST fragment to HTML, runs postprocess
45+
// logic, then parses the HTML back to HAST. We intentionally do not run
46+
// `postprocess` here to preserve HAST semantics.
47+
4148
it('run with lazy', async () => {
4249
const highlighter = await createHighlighter({
4350
themes: [

0 commit comments

Comments
 (0)