From 80622356f1d648577ee47ee3a44b04bb015ee462 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sat, 25 Jan 2025 18:00:18 +0800 Subject: [PATCH] feat: use `markdown-it-async`, remove `synckit` (#4507) BREAKING CHANGES: markdown-it-async is used instead of markdown-it --------- Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> --- __tests__/e2e/.vitepress/config.ts | 4 +- docs/en/reference/default-theme-search.md | 16 +++--- docs/es/reference/default-theme-search.md | 16 +++--- docs/fa/reference/default-theme-search.md | 18 +++---- docs/ko/reference/default-theme-search.md | 16 +++--- docs/pt/reference/default-theme-search.md | 16 +++--- docs/ru/reference/default-theme-search.md | 16 +++--- docs/zh/reference/default-theme-search.md | 16 +++--- package.json | 7 +-- patches/markdown-it-anchor@9.2.0.patch | 17 ++++++ pnpm-lock.yaml | 42 +++++++-------- rollup.config.ts | 7 +-- src/node/config.ts | 9 +--- src/node/contentLoader.ts | 4 +- src/node/defaultTheme.ts | 47 ++++++++++++++++ src/node/index.ts | 8 +-- src/node/markdown/markdown.ts | 10 ++-- src/node/markdown/plugins/highlight.ts | 35 ++++-------- src/node/markdownToVue.ts | 2 +- src/node/plugins/localSearchPlugin.ts | 15 +++--- src/node/worker_shikiResolveLang.ts | 23 -------- types/default-theme.d.ts | 65 +++++++---------------- 22 files changed, 193 insertions(+), 216 deletions(-) create mode 100644 patches/markdown-it-anchor@9.2.0.patch create mode 100644 src/node/defaultTheme.ts delete mode 100644 src/node/worker_shikiResolveLang.ts diff --git a/__tests__/e2e/.vitepress/config.ts b/__tests__/e2e/.vitepress/config.ts index 65f845ab..43dbfa84 100644 --- a/__tests__/e2e/.vitepress/config.ts +++ b/__tests__/e2e/.vitepress/config.ts @@ -165,8 +165,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('local-search/excluded')) return '' return html diff --git a/docs/en/reference/default-theme-search.md b/docs/en/reference/default-theme-search.md index 16bb9289..73ef93fb 100644 --- a/docs/en/reference/default-theme-search.md +++ b/docs/en/reference/default-theme-search.md @@ -124,9 +124,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // return html string } } @@ -149,8 +149,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html @@ -162,7 +162,7 @@ export default defineConfig({ ``` ::: warning Note -In case a custom `_render` function is provided, you need to handle the `search: false` frontmatter yourself. Also, the `env` object won't be completely populated before `md.render` is called, so any checks on optional `env` properties like `frontmatter` should be done after that. +In case a custom `_render` function is provided, you need to handle the `search: false` frontmatter yourself. Also, the `env` object won't be completely populated before `md.renderAsync` is called, so any checks on optional `env` properties like `frontmatter` should be done after that. ::: #### Example: Transforming content - adding anchors @@ -175,10 +175,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/docs/es/reference/default-theme-search.md b/docs/es/reference/default-theme-search.md index 5a654705..9815721a 100644 --- a/docs/es/reference/default-theme-search.md +++ b/docs/es/reference/default-theme-search.md @@ -113,9 +113,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // retorne un string HTML } } @@ -138,8 +138,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('algum/caminho')) return '' return html @@ -151,7 +151,7 @@ export default defineConfig({ ``` ::: warning Nota -En este caso, una función `_render` se proporciona, es necesario manipular el `search: false` desde el frente por su cuenta. Además, el objeto `env` no estará completamente poblado antes que `md.render` se llama, luego verifica las propiedades opcionales `env`, como `frontmatter`, debe hacerse después de eso. +En este caso, una función `_render` se proporciona, es necesario manipular el `search: false` desde el frente por su cuenta. Además, el objeto `env` no estará completamente poblado antes que `md.renderAsync` se llama, luego verifica las propiedades opcionales `env`, como `frontmatter`, debe hacerse después de eso. ::: #### Ejemplo: Transformar contenido - agregar anclajes {#example-transforming-content-adding-anchors} @@ -164,10 +164,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/docs/fa/reference/default-theme-search.md b/docs/fa/reference/default-theme-search.md index dd325c9f..8ad599e5 100644 --- a/docs/fa/reference/default-theme-search.md +++ b/docs/fa/reference/default-theme-search.md @@ -120,9 +120,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // بازگشت رشته HTML } } @@ -145,8 +145,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html @@ -158,7 +158,7 @@ export default defineConfig({ ``` ::: warning توجه -در صورت ارائه تابع `_render` سفارشی، باید خودتان بررسی کنید که آیا frontmatter `search: false` را مدیریت می‌کند یا خیر. همچنین، شی env قبل از فراخوانی `md.render` کاملاً پر نمی‌شود، بنابراین هر بررسی‌ای روی ویژگی‌های اختیاری env مانند `frontmatter` باید بعد از آن انجام شود. +در صورت ارائه تابع `_render` سفارشی، باید خودتان بررسی کنید که آیا frontmatter `search: false` را مدیریت می‌کند یا خیر. همچنین، شی env قبل از فراخوانی `md.renderAsync` کاملاً پر نمی‌شود، بنابراین هر بررسی‌ای روی ویژگی‌های اختیاری env مانند `frontmatter` باید بعد از آن انجام شود. ::: #### مثال: تبدیل محتوا - افزودن لینک‌های صفحه {#example-transforming-content-adding-anchors} @@ -171,10 +171,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } @@ -383,4 +383,4 @@ img[src="/search.png"] { width: 100%; aspect-ratio: 1 / 1; } - \ No newline at end of file + diff --git a/docs/ko/reference/default-theme-search.md b/docs/ko/reference/default-theme-search.md index da1feb42..ea7fb682 100644 --- a/docs/ko/reference/default-theme-search.md +++ b/docs/ko/reference/default-theme-search.md @@ -120,9 +120,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // return html string } } @@ -145,8 +145,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html @@ -158,7 +158,7 @@ export default defineConfig({ ``` ::: warning 참고 -커스텀 `_render` 함수가 제공된 경우, `search: false` 전문을 직접 처리해야 합니다. 또한, `md.render`가 호출되기 전에 `env` 객체가 완전히 채워지지 않으므로, `frontmatter`와 같은 선택적 `env` 프로퍼티에 대한 검사는 그 이후에 수행해야 합니다. +커스텀 `_render` 함수가 제공된 경우, `search: false` 전문을 직접 처리해야 합니다. 또한, `md.renderAsync`가 호출되기 전에 `env` 객체가 완전히 채워지지 않으므로, `frontmatter`와 같은 선택적 `env` 프로퍼티에 대한 검사는 그 이후에 수행해야 합니다. ::: #### 예제: 콘텐츠 변환 - 앵커 추가 {#example-transforming-content-adding-anchors} @@ -171,10 +171,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/docs/pt/reference/default-theme-search.md b/docs/pt/reference/default-theme-search.md index e7347afa..086bfabc 100644 --- a/docs/pt/reference/default-theme-search.md +++ b/docs/pt/reference/default-theme-search.md @@ -113,9 +113,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // retorne a string HTML } } @@ -138,8 +138,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('algum/caminho')) return '' return html @@ -151,7 +151,7 @@ export default defineConfig({ ``` ::: warning Nota -No caso uma função `_render` personalizada ser fornecida, você precisa manipular o `search: false` do frontmatter por conta própria. Além disso, o objeto `env` não estará completamente populado antes que `md.render` seja chamado, então verificações em propriedades opcionais `env`, como `frontmatter`, devem ser feitas após isso. +No caso uma função `_render` personalizada ser fornecida, você precisa manipular o `search: false` do frontmatter por conta própria. Além disso, o objeto `env` não estará completamente populado antes que `md.renderAsync` seja chamado, então verificações em propriedades opcionais `env`, como `frontmatter`, devem ser feitas após isso. ::: #### Exemplo: Transformando conteúdo - adicionando âncoras {#example-transforming-content-adding-anchors} @@ -164,10 +164,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/docs/ru/reference/default-theme-search.md b/docs/ru/reference/default-theme-search.md index c91e4be4..3345138f 100644 --- a/docs/ru/reference/default-theme-search.md +++ b/docs/ru/reference/default-theme-search.md @@ -124,9 +124,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // возвращаем html } } @@ -149,8 +149,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html @@ -162,7 +162,7 @@ export default defineConfig({ ``` ::: warning ПРИМЕЧАНИЕ -В случае, если предоставляется пользовательская функция `_render`, вам нужно самостоятельно обработать заголовок `search: false`. Кроме того, объект `env` не будет полностью заполнен до вызова `md.render`, поэтому любые проверки необязательных свойств `env`, таких как `frontmatter`, должны быть выполнены после этого. +В случае, если предоставляется пользовательская функция `_render`, вам нужно самостоятельно обработать заголовок `search: false`. Кроме того, объект `env` не будет полностью заполнен до вызова `md.renderAsync`, поэтому любые проверки необязательных свойств `env`, таких как `frontmatter`, должны быть выполнены после этого. ::: #### Пример: Преобразование содержимого - добавление якорей {#example-transforming-content-adding-anchors} @@ -175,10 +175,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/docs/zh/reference/default-theme-search.md b/docs/zh/reference/default-theme-search.md index e19f052a..348e0a6b 100644 --- a/docs/zh/reference/default-theme-search.md +++ b/docs/zh/reference/default-theme-search.md @@ -113,9 +113,9 @@ export default defineConfig({ /** * @param {string} src * @param {import('vitepress').MarkdownEnv} env - * @param {import('markdown-it')} md + * @param {import('markdown-it-async')} md */ - _render(src, env, md) { + async _render(src, env, md) { // 返回 html 字符串 } } @@ -138,8 +138,8 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.search === false) return '' if (env.relativePath.startsWith('some/path')) return '' return html @@ -151,7 +151,7 @@ export default defineConfig({ ``` ::: warning 注意 -如果提供了自定义的 `_render` 函数,你需要自己处理 `search: false` 的 frontmatter。此外,在调用 `md.render` 之前,`env` 对象不会完全填充,因此对可选 `env` 属性 (如 `frontmatter`) 的任何检查都应该在此之后完成。 +如果提供了自定义的 `_render` 函数,你需要自己处理 `search: false` 的 frontmatter。此外,在调用 `md.renderAsync` 之前,`env` 对象不会完全填充,因此对可选 `env` 属性 (如 `frontmatter`) 的任何检查都应该在此之后完成。 ::: #### 示例:转换内容——添加锚点 {#example-transforming-content-adding-anchors} @@ -164,10 +164,10 @@ export default defineConfig({ search: { provider: 'local', options: { - _render(src, env, md) { - const html = md.render(src, env) + async _render(src, env, md) { + const html = await md.renderAsync(src, env) if (env.frontmatter?.title) - return md.render(`# ${env.frontmatter.title}`) + html + return await md.renderAsync(`# ${env.frontmatter.title}`) + html return html } } diff --git a/package.json b/package.json index 6f99adac..d43c1684 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", - "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", @@ -135,6 +134,7 @@ "@types/fs-extra": "^11.0.4", "@types/lodash.template": "^4.5.3", "@types/mark.js": "^8.11.12", + "@types/markdown-it": "^14.1.2", "@types/markdown-it-attrs": "^4.1.3", "@types/markdown-it-container": "^2.0.10", "@types/markdown-it-emoji": "^3.0.1", @@ -157,6 +157,7 @@ "lru-cache": "^11.0.2", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", + "markdown-it-async": "^2.0.0", "markdown-it-attrs": "^4.3.1", "markdown-it-container": "^4.0.0", "markdown-it-emoji": "^3.0.0", @@ -183,7 +184,6 @@ "simple-git-hooks": "^2.11.1", "sirv": "^3.0.0", "sitemap": "^8.0.0", - "synckit": "^0.9.2", "tinyglobby": "^0.2.10", "typescript": "^5.7.3", "vitest": "^3.0.4", @@ -215,7 +215,8 @@ "ora>string-width": "^5" }, "patchedDependencies": { - "@types/mdurl@2.0.0": "patches/@types__mdurl@2.0.0.patch" + "@types/mdurl@2.0.0": "patches/@types__mdurl@2.0.0.patch", + "markdown-it-anchor@9.2.0": "patches/markdown-it-anchor@9.2.0.patch" } } } diff --git a/patches/markdown-it-anchor@9.2.0.patch b/patches/markdown-it-anchor@9.2.0.patch new file mode 100644 index 00000000..ac4498e4 --- /dev/null +++ b/patches/markdown-it-anchor@9.2.0.patch @@ -0,0 +1,17 @@ +diff --git a/types/index.d.ts b/types/index.d.ts +index 40c25c0be1add8b0fc2c51489c25a423dbc49d2c..807bc1b0e434d660c6a298b1dee1c87935bfac86 100644 +--- a/types/index.d.ts ++++ b/types/index.d.ts +@@ -1,10 +1,8 @@ + import MarkdownIt from 'markdown-it'; +-import { default as MarkdownItToken } from 'markdown-it/lib/token.mjs'; +-import { default as MarkdownItState} from 'markdown-it/lib/rules_core/state_core.mjs'; ++import { default as Token } from 'markdown-it/lib/token.mjs'; ++import { default as State } from 'markdown-it/lib/rules_core/state_core.mjs'; + + declare namespace anchor { +- export type Token = MarkdownItToken +- export type State = MarkdownItState + export type RenderHref = (slug: string, state: State) => string; + export type RenderAttrs = (slug: string, state: State) => Record; + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b25eca8b..d4bdaf17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ patchedDependencies: '@types/mdurl@2.0.0': hash: ztuyknm7z4pyl4jot5hljjv5bm path: patches/@types__mdurl@2.0.0.patch + markdown-it-anchor@9.2.0: + hash: ivrlfano2jj27ilcyyknwlzzfu + path: patches/markdown-it-anchor@9.2.0.patch importers: @@ -34,9 +37,6 @@ importers: '@shikijs/types': specifier: ^2.1.0 version: 2.1.0 - '@types/markdown-it': - specifier: ^14.1.2 - version: 14.1.2 '@vitejs/plugin-vue': specifier: ^5.2.1 version: 5.2.1(vite@6.0.11(@types/node@22.10.9)(jiti@1.21.7)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) @@ -131,6 +131,9 @@ importers: '@types/mark.js': specifier: ^8.11.12 version: 8.11.12 + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 '@types/markdown-it-attrs': specifier: ^4.1.3 version: 4.1.3 @@ -196,7 +199,10 @@ importers: version: 14.1.0 markdown-it-anchor: specifier: ^9.2.0 - version: 9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0) + version: 9.2.0(patch_hash=ivrlfano2jj27ilcyyknwlzzfu)(@types/markdown-it@14.1.2)(markdown-it@14.1.0) + markdown-it-async: + specifier: ^2.0.0 + version: 2.0.0 markdown-it-attrs: specifier: ^4.3.1 version: 4.3.1(markdown-it@14.1.0) @@ -275,9 +281,6 @@ importers: sitemap: specifier: ^8.0.0 version: 8.0.0 - synckit: - specifier: ^0.9.2 - version: 0.9.2 tinyglobby: specifier: ^0.2.10 version: 0.2.10 @@ -701,10 +704,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@pkgr/core@0.1.1': - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@polka/compression@1.0.0-next.28': resolution: {integrity: sha512-aDmrBhgHJtxE+jy145WfhW9WmTAFmES/dNnn1LAs8UnnkFgBUj4T8I4ScQ9+rOkpDZStvnVP5iqhN3tvt7O1NA==} engines: {node: '>=6'} @@ -1895,6 +1894,9 @@ packages: '@types/markdown-it': '*' markdown-it: '*' + markdown-it-async@2.0.0: + resolution: {integrity: sha512-jBthmQR5MwXR9Y8Y0teRoZAenaKQMdjuTfpbNARqMBSRPvyzyXCVduHZHakyyhL3ugIacCobXJrO07t277sIjw==} + markdown-it-attrs@4.3.1: resolution: {integrity: sha512-/ko6cba+H6gdZ0DOw7BbNMZtfuJTRp9g/IrGIuz8lYc/EfnmWRpaR3CFPnNbVz0LDvF8Gf1hFGPqrQqq7De0rg==} engines: {node: '>=6'} @@ -2482,10 +2484,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.9.2: - resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} - engines: {node: ^14.18.0 || >=16.0.0} - tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} @@ -3165,8 +3163,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.18.0 - '@pkgr/core@0.1.1': {} - '@polka/compression@1.0.0-next.28': {} '@polka/url@1.0.0-next.28': {} @@ -4375,7 +4371,12 @@ snapshots: mark.js@8.11.1: {} - markdown-it-anchor@9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0): + markdown-it-anchor@9.2.0(patch_hash=ivrlfano2jj27ilcyyknwlzzfu)(@types/markdown-it@14.1.2)(markdown-it@14.1.0): + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + + markdown-it-async@2.0.0: dependencies: '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 @@ -4965,11 +4966,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.9.2: - dependencies: - '@pkgr/core': 0.1.1 - tslib: 2.8.1 - tabbable@6.2.0: {} temp-dir@3.0.0: {} diff --git a/rollup.config.ts b/rollup.config.ts index 8a996b01..6bf7ff1b 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -8,7 +8,6 @@ import { builtinModules, createRequire } from 'node:module' import { type RollupOptions, defineConfig } from 'rollup' import dts from 'rollup-plugin-dts' import esbuild from 'rollup-plugin-esbuild' -import { globSync } from 'tinyglobby' const require = createRequire(import.meta.url) const pkg = require('./package.json') @@ -39,11 +38,7 @@ const plugins = [ ] const esmBuild: RollupOptions = { - input: [ - 'src/node/index.ts', - 'src/node/cli.ts', - ...globSync('src/node/worker_*.ts') - ], + input: ['src/node/index.ts', 'src/node/cli.ts'], output: { format: 'esm', entryFileNames: `[name].js`, diff --git a/src/node/config.ts b/src/node/config.ts index ffffa5ab..5f1698fe 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -10,14 +10,9 @@ import { type ConfigEnv } from 'vite' import { DEFAULT_THEME_PATH } from './alias' +import type { DefaultTheme } from './defaultTheme' import { resolvePages } from './plugins/dynamicRoutesPlugin' -import { - APPEARANCE_KEY, - slash, - type DefaultTheme, - type HeadConfig, - type SiteData -} from './shared' +import { APPEARANCE_KEY, slash, type HeadConfig, type SiteData } from './shared' import type { RawConfigExports, SiteConfig, UserConfig } from './siteConfig' export { resolvePages } from './plugins/dynamicRoutesPlugin' diff --git a/src/node/contentLoader.ts b/src/node/contentLoader.ts index e3bee2e8..cbd12e1c 100644 --- a/src/node/contentLoader.ts +++ b/src/node/contentLoader.ts @@ -144,9 +144,9 @@ export function createContentLoader( normalizePath(path.relative(config.srcDir, file)) .replace(/(^|\/)index\.md$/, '$1') .replace(/\.md$/, config.cleanUrls ? '' : '.html') - const html = render ? md.render(src) : undefined + const html = render ? await md.renderAsync(src) : undefined const renderedExcerpt = renderExcerpt - ? excerpt && md.render(excerpt) + ? excerpt && (await md.renderAsync(excerpt)) : undefined const data: ContentData = { src: includeSrc ? src : undefined, diff --git a/src/node/defaultTheme.ts b/src/node/defaultTheme.ts new file mode 100644 index 00000000..7b50e1b6 --- /dev/null +++ b/src/node/defaultTheme.ts @@ -0,0 +1,47 @@ +// this file contains node-only types of default theme +// (functions starting with _ are node-only) +// in most of the cases these will leak to client too +// but these can import types from dev deps + +import type { MarkdownItAsync } from 'markdown-it-async' +import type { DefaultTheme } from '../../types/default-theme' +import type { PageSplitSection } from '../../types/local-search' +import type { Awaitable, MarkdownEnv } from './shared' + +declare module '../../types/default-theme.js' { + namespace DefaultTheme { + interface LocalSearchOptions { + /** + * Allows transformation of content before indexing (node only) + * Return empty string to skip indexing + */ + _render?: ( + src: string, + env: MarkdownEnv, + md: MarkdownItAsync + ) => Awaitable + } + + interface MiniSearchOptions { + /** + * Overrides the default regex based page splitter. + * Supports async generator, making it possible to run in true parallel + * (when used along with `node:child_process` or `worker_threads`) + * --- + * This should be especially useful for scalability reasons. + * --- + * @param {string} path - absolute path to the markdown source file + * @param {string} html - document page rendered as html + */ + _splitIntoSections?: ( + path: string, + html: string + ) => + | AsyncGenerator + | Generator + | Awaitable + } + } +} + +export type { DefaultTheme } diff --git a/src/node/index.ts b/src/node/index.ts index 90c0956a..63ee79f8 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -8,11 +8,7 @@ export { defineLoader, type LoaderModule } from './plugins/staticDataPlugin' export * from './postcss/isolateStyles' export * from './serve/serve' export * from './server' +export type { DefaultTheme } from './defaultTheme' // shared types -export type { - DefaultTheme, - HeadConfig, - Header, - SiteData -} from '../../types/shared' +export type { HeadConfig, Header, SiteData } from '../../types/shared' diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index c7b74d9e..a39832a4 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -20,7 +20,7 @@ import type { ThemeRegistrationAny } from '@shikijs/types' import type { Options } from 'markdown-it' -import MarkdownIt from 'markdown-it' +import { MarkdownItAsync } from 'markdown-it-async' import anchorPlugin from 'markdown-it-anchor' import attrsPlugin from 'markdown-it-attrs' import { full as emojiPlugin } from 'markdown-it-emoji' @@ -53,11 +53,11 @@ export interface MarkdownOptions extends Options { /** * Setup markdown-it instance before applying plugins */ - preConfig?: (md: MarkdownIt) => Awaited + preConfig?: (md: MarkdownItAsync) => Awaited /** * Setup markdown-it instance */ - config?: (md: MarkdownIt) => Awaited + config?: (md: MarkdownItAsync) => Awaited /** * Disable cache (experimental) */ @@ -190,7 +190,7 @@ export interface MarkdownOptions extends Options { gfmAlerts?: boolean } -export type MarkdownRenderer = MarkdownIt +export type MarkdownRenderer = MarkdownItAsync let md: MarkdownRenderer | undefined let _disposeHighlighter: (() => void) | undefined @@ -223,7 +223,7 @@ export async function createMarkdownRenderer( _disposeHighlighter = dispose - md = MarkdownIt({ html: true, linkify: true, highlight, ...options }) + md = new MarkdownItAsync({ html: true, linkify: true, highlight, ...options }) md.linkify.set({ fuzzyLink: false }) md.use(restoreEntities) diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index fb276055..f0a0170e 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -7,20 +7,12 @@ import { type TransformerCompactLineOption } from '@shikijs/transformers' import { customAlphabet } from 'nanoid' -import { createRequire } from 'node:module' -import c from 'picocolors' import type { LanguageRegistration, ShikiTransformer } from 'shiki' import { createHighlighter, isSpecialLang } from 'shiki' -import { createSyncFn } from 'synckit' import type { Logger } from 'vite' -import type { ShikiResolveLang } from 'worker_shikiResolveLang' import type { MarkdownOptions, ThemeOptions } from '../markdown' +import c from 'picocolors' -const require = createRequire(import.meta.url) - -const resolveLangSync = createSyncFn( - require.resolve('vitepress/dist/node/worker_shikiResolveLang.js') -) const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) /** @@ -59,7 +51,9 @@ export async function highlight( theme: ThemeOptions, options: MarkdownOptions, logger: Pick = console -): Promise<[(str: string, lang: string, attrs: string) => string, () => void]> { +): Promise< + [(str: string, lang: string, attrs: string) => Promise, () => void] +> { const { defaultHighlightLang: defaultLang = 'txt', codeTransformers: userTransformers = [] @@ -77,25 +71,14 @@ export async function highlight( langAlias: options.languageAlias }) - function loadLanguage(name: string | LanguageRegistration) { + async function loadLanguage(name: string | LanguageRegistration) { const lang = typeof name === 'string' ? name : name.name if ( !isSpecialLang(lang) && !highlighter.getLoadedLanguages().includes(lang) ) { - const resolvedLang = resolveLangSync(lang) - if (resolvedLang.length) highlighter.loadLanguageSync(resolvedLang) - else return false + await highlighter.loadLanguage(lang as any) } - return true - } - - // patch for twoslash - https://github.com/vuejs/vitepress/issues/4334 - const internal = highlighter.getInternalContext() - const getLanguage = internal.getLanguage - internal.getLanguage = (name) => { - loadLanguage(name) - return getLanguage.call(internal, name) } await options?.shikiSetup?.(highlighter) @@ -136,7 +119,7 @@ export async function highlight( const mustacheRE = /\{\{.*?\}\}/g return [ - (str: string, lang: string, attrs: string) => { + async (str: string, lang: string, attrs: string) => { const vPre = vueRE.test(lang) ? '' : 'v-pre' lang = lang @@ -145,7 +128,9 @@ export async function highlight( .replace(vueRE, '') .toLowerCase() || defaultLang - if (!loadLanguage(lang)) { + try { + await loadLanguage(lang) + } catch { logger.warn( c.yellow( `\nThe language '${lang}' is not loaded, falling back to '${defaultLang}' for syntax highlighting.` diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index 98f236c7..a4f8a20d 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -119,7 +119,7 @@ export async function createMarkdownToVueRenderFn( realPath: fileOrig, localeIndex } - const html = md.render(src, env) + const html = await md.renderAsync(src, env) const { frontmatter = {}, headers = [], diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index d3b37b42..bffe74e8 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -5,13 +5,9 @@ import path from 'node:path' import pMap from 'p-map' import type { Plugin, ViteDevServer } from 'vite' import type { SiteConfig } from '../config' +import type { DefaultTheme } from '../defaultTheme' import { createMarkdownRenderer } from '../markdown/markdown' -import { - getLocaleForPath, - slash, - type DefaultTheme, - type MarkdownEnv -} from '../shared' +import { getLocaleForPath, slash, type MarkdownEnv } from '../shared' import { processIncludes } from '../utils/processIncludes' const debug = _debug('vitepress:local-search') @@ -61,9 +57,10 @@ export async function localSearchPlugin( const env: MarkdownEnv = { path: file, relativePath, cleanUrls } const md_raw = await fs.promises.readFile(file, 'utf-8') const md_src = processIncludes(srcDir, md_raw, file, []) - if (options._render) return await options._render(md_src, env, md) - else { - const html = md.render(md_src, env) + if (options._render) { + return await options._render(md_src, env, md) + } else { + const html = await md.renderAsync(md_src, env) return env.frontmatter?.search === false ? '' : html } } diff --git a/src/node/worker_shikiResolveLang.ts b/src/node/worker_shikiResolveLang.ts deleted file mode 100644 index 644caf9e..00000000 --- a/src/node/worker_shikiResolveLang.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - bundledLanguages, - type DynamicImportLanguageRegistration, - type LanguageRegistration -} from 'shiki' -import { runAsWorker } from 'synckit' - -async function resolveLang(lang: string) { - return ( - ( - bundledLanguages as Record< - string, - DynamicImportLanguageRegistration | undefined - > - ) - [lang]?.() - .then((m) => m.default) || ([] as LanguageRegistration[]) - ) -} - -runAsWorker(resolveLang) - -export type ShikiResolveLang = typeof resolveLang diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts index 10681a84..5a669c49 100644 --- a/types/default-theme.d.ts +++ b/types/default-theme.d.ts @@ -1,12 +1,8 @@ -import type MarkdownIt from 'markdown-it' -import type { Options as MiniSearchOptions } from 'minisearch' +import type { Options as _MiniSearchOptions } from 'minisearch' import type { ComputedRef, Ref, ShallowRef } from 'vue' import type { DocSearchProps } from './docsearch.js' -import type { - LocalSearchTranslations, - PageSplitSection -} from './local-search.js' -import type { Awaitable, MarkdownEnv, PageData } from './shared.js' +import type { LocalSearchTranslations } from './local-search.js' +import type { PageData } from './shared.js' export namespace DefaultTheme { export interface Config { @@ -413,46 +409,21 @@ export namespace DefaultTheme { translations?: LocalSearchTranslations locales?: Record>> - miniSearch?: { - /** - * @see https://lucaong.github.io/minisearch/types/MiniSearch.Options.html - */ - options?: Pick< - MiniSearchOptions, - 'extractField' | 'tokenize' | 'processTerm' - > - /** - * @see https://lucaong.github.io/minisearch/types/MiniSearch.SearchOptions.html - */ - searchOptions?: MiniSearchOptions['searchOptions'] - - /** - * Overrides the default regex based page splitter. - * Supports async generator, making it possible to run in true parallel - * (when used along with `node:child_process` or `worker_threads`) - * --- - * This should be especially useful for scalability reasons. - * --- - * @param {string} path - absolute path to the markdown source file - * @param {string} html - document page rendered as html - */ - _splitIntoSections?: ( - path: string, - html: string - ) => - | AsyncGenerator - | Generator - | Awaitable - } - /** - * Allows transformation of content before indexing (node only) - * Return empty string to skip indexing - */ - _render?: ( - src: string, - env: MarkdownEnv, - md: MarkdownIt - ) => Awaitable + miniSearch?: MiniSearchOptions + } + + interface MiniSearchOptions { + /** + * @see https://lucaong.github.io/minisearch/types/MiniSearch.Options.html + */ + options?: Pick< + _MiniSearchOptions, + 'extractField' | 'tokenize' | 'processTerm' + > + /** + * @see https://lucaong.github.io/minisearch/types/MiniSearch.SearchOptions.html + */ + searchOptions?: _MiniSearchOptions['searchOptions'] } // algolia -------------------------------------------------------------------