Skip to content

Commit 75cf290

Browse files
committed
refactor(markdown): unify async rendering
1 parent df6c4fe commit 75cf290

25 files changed

Lines changed: 213 additions & 227 deletions

packages/valaxy-theme-press/components/PressFuseSearchModal.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
22
import { isClient, useScrollLock } from '@vueuse/core'
3-
import MarkdownIt from 'markdown-it'
3+
import { createMarkdownExit } from 'markdown-exit'
44
import { useFuseSearch } from 'valaxy'
55
import { nextTick, ref, watch } from 'vue'
66
import { useI18n } from 'vue-i18n'
@@ -11,7 +11,7 @@ const props = defineProps<{
1111
1212
const emit = defineEmits(['close'])
1313
14-
const md = new MarkdownIt({
14+
const md = createMarkdownExit({
1515
html: true,
1616
linkify: true,
1717
})

packages/valaxy-theme-press/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@docsearch/css": "catalog:",
3434
"@docsearch/js": "catalog:",
3535
"@docsearch/sidepanel-js": "catalog:",
36+
"markdown-exit": "catalog:",
3637
"reka-ui": "catalog:frontend",
3738
"vitepress": "catalog:frontend"
3839
},

packages/valaxy/node/modules/rss/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Feed } from 'feed'
1111

1212
import fs from 'fs-extra'
1313
import matter from 'gray-matter'
14-
import MarkdownIt from 'markdown-it'
14+
import { createMarkdownExit } from 'markdown-exit'
1515

1616
import { getBorderCharacters, table } from 'table'
1717
import { tObject } from '../../../shared'
@@ -22,7 +22,7 @@ import { getCreatedTime, getUpdatedTime } from '../../utils/date'
2222
// Extend Item type to include optional updated field for Atom feed
2323
type ExtendedItem = Item & { updated?: Date }
2424

25-
const markdown = MarkdownIt({
25+
const markdown = createMarkdownExit({
2626
html: true,
2727
breaks: true,
2828
linkify: true,

packages/valaxy/node/plugins/markdown/async.ts

Lines changed: 0 additions & 127 deletions
This file was deleted.

packages/valaxy/node/plugins/markdown/env.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// ref vitepress src/node/markdown/env.ts
22
import type { MarkdownSfcBlocks } from '@mdit-vue/plugin-sfc'
33
import type { Header } from '@valaxyjs/utils'
4-
import type { Env } from 'markdown-it'
54
import type { CleanUrlsMode, Page } from '../../../types'
65

76
// Manually declaring all properties as rollup-plugin-dts
87
// is unable to merge augmented module declarations
98

10-
export interface MarkdownEnv extends Env {
9+
export interface MarkdownEnv {
1110
/**
1211
* The raw Markdown content without frontmatter
1312
*/

packages/valaxy/node/plugins/markdown/index.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import type { Header } from '@valaxyjs/utils'
22

33
import type { ResolvedValaxyOptions } from '../../types'
4-
import type { MarkdownItAsync } from './async'
54
import type { MarkdownBase } from './base'
5+
import type { MarkdownRenderer } from './renderer'
66

77
import { logger } from '../../logger'
8-
import { createMarkdownItAsync } from './async'
98
import { getSharedHighlighter } from './highlighterCache'
9+
import { createMarkdownEngine } from './renderer'
1010
import { defaultCodeTheme, setupMarkdownPageMetadata, setupMarkdownPlugins } from './setup'
1111

12-
export * from './async'
1312
export * from './env'
13+
export * from './renderer'
1414
export * from './setup'
1515
export * from './transform'
1616

@@ -27,7 +27,7 @@ export function disposePreviewMdItInstance() {
2727
_disposeHighlighter = undefined
2828
}
2929

30-
export async function createMarkdownRenderer(options?: ResolvedValaxyOptions, base?: MarkdownBase): Promise<MarkdownItAsync> {
30+
export async function createMarkdownRenderer(options?: ResolvedValaxyOptions, base?: MarkdownBase): Promise<MarkdownRenderer> {
3131
const mdOptions = options?.config.markdown || {}
3232
const theme = mdOptions.theme ?? defaultCodeTheme
3333

@@ -37,7 +37,7 @@ export async function createMarkdownRenderer(options?: ResolvedValaxyOptions, ba
3737

3838
_disposeHighlighter = dispose
3939

40-
const md = createMarkdownItAsync({
40+
const md = createMarkdownEngine({
4141
html: true,
4242
linkify: true,
4343
...mdOptions.options,
@@ -47,9 +47,8 @@ export async function createMarkdownRenderer(options?: ResolvedValaxyOptions, ba
4747
md.linkify.set({ fuzzyLink: false })
4848

4949
await setupMarkdownPlugins(md, options, base)
50-
await mdOptions.markdownItSetup?.(
51-
md as unknown as Parameters<NonNullable<typeof mdOptions.markdownItSetup>>[0],
52-
)
50+
const userMarkdownSetup = mdOptions.markdownSetup ?? mdOptions.markdownItSetup
51+
await userMarkdownSetup?.(md)
5352
setupMarkdownPageMetadata(md, options)
5453
return md
5554
}
@@ -59,7 +58,7 @@ export async function createMarkdownRenderer(options?: ResolvedValaxyOptions, ba
5958
* Used by localSearchPlugin where HTML output is stripped anyway,
6059
* saving ~20-50 MB of Shiki theme/grammar data.
6160
*/
62-
export async function createLightMarkdownRenderer(options?: ResolvedValaxyOptions, base?: MarkdownBase): Promise<MarkdownItAsync> {
61+
export async function createLightMarkdownRenderer(options?: ResolvedValaxyOptions, base?: MarkdownBase): Promise<MarkdownRenderer> {
6362
const mdOptions = options?.config.markdown || {}
6463

6564
// Define highlight separately to avoid circular type inference
@@ -73,7 +72,7 @@ export async function createLightMarkdownRenderer(options?: ResolvedValaxyOption
7372
return `<pre><code>${escaped}</code></pre>`
7473
}
7574

76-
const md = createMarkdownItAsync({
75+
const md = createMarkdownEngine({
7776
html: true,
7877
linkify: true,
7978
...mdOptions.options,
@@ -83,6 +82,8 @@ export async function createLightMarkdownRenderer(options?: ResolvedValaxyOption
8382
md.linkify.set({ fuzzyLink: false })
8483

8584
await setupMarkdownPlugins(md, options, base)
85+
const userMarkdownSetup = mdOptions.markdownSetup ?? mdOptions.markdownItSetup
86+
await userMarkdownSetup?.(md)
8687
setupMarkdownPageMetadata(md, options)
8788
return md
8889
}

packages/valaxy/node/plugins/markdown/plugins/async-utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@
1010
export function isPromiseLike(v: unknown): v is PromiseLike<unknown> {
1111
return typeof v === 'object' && v !== null && typeof (v as any).then === 'function'
1212
}
13+
14+
/** Apply a synchronous HTML transform without discarding an async render rule. */
15+
export function mapRenderResult(
16+
result: string | Promise<string>,
17+
transform: (html: string) => string,
18+
): string | Promise<string> {
19+
return isPromiseLike(result)
20+
? Promise.resolve(result).then(transform)
21+
: transform(result)
22+
}

packages/valaxy/node/plugins/markdown/plugins/image.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { MarkdownIt } from 'markdown-it'
1+
import type { MarkdownRenderer } from '../renderer'
22
import { EXTERNAL_URL_RE } from '../../../../shared'
33

44
/**
@@ -8,7 +8,7 @@ import { EXTERNAL_URL_RE } from '../../../../shared'
88
*
99
* @see https://vitepress.dev/guide/asset-handling
1010
*/
11-
export function imagePlugin(md: MarkdownIt) {
11+
export function imagePlugin(md: MarkdownRenderer) {
1212
const imageRule = md.renderer.rules.image!
1313

1414
md.renderer.rules.image = (tokens, idx, options, env, self) => {

packages/valaxy/node/plugins/markdown/plugins/link.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
// 1. adding target="_blank" to external links
55
// 2. normalize internal links to end with `.html`
66

7-
import type { MarkdownIt } from 'markdown-it'
87
import type { MarkdownBaseResolver } from '../base'
98
import type { MarkdownEnv } from '../env'
9+
import type { MarkdownRenderer } from '../renderer'
1010
import { URL } from 'node:url'
1111
import { EXTERNAL_URL_RE } from '../../../../shared'
1212
import { PATHNAME_PROTOCOL_RE } from '../../../constants'
1313

1414
const indexRE = /(^|.*\/)index.md(.*)$/i
1515

16-
export function linkPlugin(md: MarkdownIt, externalAttrs: Record<string, string>, resolveBase: MarkdownBaseResolver) {
16+
export function linkPlugin(md: MarkdownRenderer, externalAttrs: Record<string, string>, resolveBase: MarkdownBaseResolver) {
1717
md.renderer.rules.link_open = (
1818
tokens,
1919
idx,

packages/valaxy/node/plugins/markdown/plugins/markdown-it/container.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
11
// ref vitepress
22
// src/node/markdown/plugins/containers.ts
33

4-
import type { Token } from 'markdown-it'
4+
import type { Token } from 'markdown-exit'
55

6-
import type { MarkdownItAsync } from '../../async'
6+
import type { MarkdownRenderer } from '../../renderer'
77
import container from 'markdown-it-container'
88
import {
99
extractTitle,
1010
} from './preWrapper'
1111

12+
type ContainerPlugin = (
13+
md: MarkdownRenderer,
14+
name: string,
15+
options?: { render: (tokens: Token[], idx: number) => string },
16+
) => void
17+
const containerCompat = container as unknown as ContainerPlugin
18+
1219
type ContainerArgs = [
13-
typeof container,
20+
typeof containerCompat,
1421
string,
1522
{
1623
render: (tokens: Token[], idx: number) => string
1724
},
1825
]
1926

20-
function createContainer(key: string, block: BlockItem = {}, md: MarkdownItAsync): ContainerArgs {
27+
function createContainer(key: string, block: BlockItem = {}, md: MarkdownRenderer): ContainerArgs {
2128
const classes = key
2229
return [
23-
container,
30+
containerCompat,
2431
classes,
2532
{
2633
render(tokens, idx) {
@@ -87,7 +94,7 @@ const defaultBlocksOptions: Blocks = {
8794
},
8895
}
8996

90-
export function containerPlugin(md: MarkdownItAsync, containerOptions: ContainerOptions = {}) {
97+
export function containerPlugin(md: MarkdownRenderer, containerOptions: ContainerOptions = {}) {
9198
const blockKeys = new Set(Object.keys({ ...defaultBlocksOptions, ...containerOptions.blocks }))
9299
blockKeys.forEach((optionKey) => {
93100
const key = optionKey as keyof Blocks
@@ -97,18 +104,18 @@ export function containerPlugin(md: MarkdownItAsync, containerOptions: Container
97104
})
98105

99106
// explicitly escape Vue syntax
100-
md.use(container, 'v-pre', {
107+
md.use(containerCompat, 'v-pre', {
101108
render: (tokens: Token[], idx: number) =>
102109
tokens[idx].nesting === 1 ? `<div v-pre>\n` : `</div>\n`,
103110
})
104-
md.use(container, 'raw', {
111+
md.use(containerCompat, 'raw', {
105112
render: (tokens: Token[], idx: number) =>
106113
tokens[idx].nesting === 1 ? `<div class="vp-raw">\n` : `</div>\n`,
107114
})
108115

109116
const languages = containerOptions.languages || ['zh-CN', 'en']
110117
languages.forEach((lang) => {
111-
md.use(container, lang, {
118+
md.use(containerCompat, lang, {
112119
render: (tokens: Token[], idx: number) =>
113120
tokens[idx].nesting === 1 ? `<div lang="${lang}">\n` : '</div>\n',
114121
})
@@ -117,9 +124,9 @@ export function containerPlugin(md: MarkdownItAsync, containerOptions: Container
117124
md.use(...createCodeGroup(md))
118125
}
119126

120-
function createCodeGroup(md: MarkdownItAsync): ContainerArgs {
127+
function createCodeGroup(md: MarkdownRenderer): ContainerArgs {
121128
return [
122-
container,
129+
containerCompat,
123130
'code-group',
124131
{
125132
render(tokens, idx) {

0 commit comments

Comments
 (0)