feat!: migrate to vite 8

triage
Divyansh Singh 2 weeks ago
parent 7e2273a3e4
commit 228eef187a

@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node_version: [20, 22, 24, latest]
node_version: [22, 24, 26]
include:
- os: windows-latest
node_version: 24

@ -12,7 +12,7 @@ You can try VitePress directly in your browser on [StackBlitz](https://vitepress
### Prerequisites
- [Node.js](https://nodejs.org/) version 20 or higher.
- [Node.js](https://nodejs.org/) version 22 or higher.
- Terminal for accessing VitePress via its command line interface (CLI).
- Text Editor with [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax support.
- [VSCode](https://code.visualstudio.com/) is recommended, along with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

@ -12,7 +12,7 @@ Puede experimentar VitePress directamente en su navegador en [StackBlitz](https:
### Prerrequisitos {#prerequisites}
- [Node.js](https://nodejs.org/) versión 20 o superior.
- [Node.js](https://nodejs.org/) versión 22 o superior.
- Terminal para acessar VitePress a través de su interfaz de linea de comando (CLI).
- Editor de texto con soporte a sintaxis [Markdown](https://en.wikipedia.org/wiki/Markdown).
- [VSCode](https://code.visualstudio.com/) es recomendado, junto con la [extensión oficial Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

@ -12,7 +12,7 @@ description: نصب و راه‌اندازی ویت‌پرس. یاد بگیری
### پیش‌نیازها {#prerequisites}
- [Node.js](https://nodejs.org/) نسخه 20 یا بالاتر.
- [Node.js](https://nodejs.org/) نسخه 22 یا بالاتر.
- ترمینال برای دسترسی به ویت‌پرس از طریق رابط خط فرمان (CLI).
- ویرایشگر متنی با پشتیبانی از [Markdown](https://en.wikipedia.org/wiki/Markdown).
- [VSCode](https://code.visualstudio.com/) به همراه [افزونه رسمی Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

@ -12,7 +12,7 @@ description: VitePressのインストール、セットアップ、開発サー
### 前提条件 {#prerequisites}
- [Node.js](https://nodejs.org/) バージョン 20 以上
- [Node.js](https://nodejs.org/) バージョン 22 以上
- VitePress をコマンドラインインターフェース (CLI) で操作するためのターミナル
- [Markdown](https://en.wikipedia.org/wiki/Markdown) 構文に対応したテキストエディタ
- 推奨: [VSCode](https://code.visualstudio.com/) と [公式 Vue 拡張](https://marketplace.visualstudio.com/items?itemName=Vue.volar)

@ -12,7 +12,7 @@ description: VitePress를 시작하세요. 문서 사이트를 설치하고, 스
### 사전 준비 사항 {#prerequisites}
- [Node.js](https://nodejs.org/) 버전 20 이상.
- [Node.js](https://nodejs.org/) 버전 22 이상.
- VitePress를 명령줄 인터페이스(CLI)를 통해 접근하기 위한 터미널.
- [마크다운](https://en.wikipedia.org/wiki/Markdown) 문법 지원이 있는 텍스트 에디터.
- [VSCode](https://code.visualstudio.com/)와 [공식 Vue 확장 프로그램](https://marketplace.visualstudio.com/items?itemName=Vue.volar) 사용을 권장합니다.

@ -12,7 +12,7 @@ Você pode experimentar VitePress diretamente no seu navegador em [StackBlitz](h
### Pré-requisitos {#prerequisites}
- [Node.js](https://nodejs.org/) na versão 20 ou superior.
- [Node.js](https://nodejs.org/) na versão 22 ou superior.
- Terminal para acessar VitePress através da sua interface de linha de comando (CLI).
- Editor de texto com suporte a sintaxe [Markdown](https://en.wikipedia.org/wiki/Markdown).
- [VSCode](https://code.visualstudio.com/) é recomendado, junto com a [extensão oficial Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

@ -12,7 +12,7 @@ description: Начните работу с VitePress. Узнайте, как у
### Требования {#prerequisites}
- [Node.js](https://nodejs.org/) версии 20 или выше.
- [Node.js](https://nodejs.org/) версии 22 или выше.
- Терминал для доступа к VitePress через интерфейс командной строки (CLI).
- Текстовый редактор с поддержкой синтаксиса [Markdown](https://ru.wikipedia.org/wiki/Markdown).
- Рекомендуется использовать [VSCode](https://code.visualstudio.com/), а также [официальное расширение Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

@ -12,7 +12,7 @@ description: 快速上手 VitePress。了解如何安装、创建项目结构并
### 前置准备 {#prerequisites}
- [Node.js](https://nodejs.org/) 20 及以上版本。
- [Node.js](https://nodejs.org/) 22 及以上版本。
- 通过命令行界面 (CLI) 访问 VitePress 的终端。
- 支持 [Markdown](https://en.wikipedia.org/wiki/Markdown) 语法的编辑器。
- 推荐 [VSCode](https://code.visualstudio.com/) 及其[官方 Vue 扩展](https://marketplace.visualstudio.com/items?itemName=Vue.volar)。

@ -113,7 +113,7 @@
"mark.js": "8.11.1",
"minisearch": "^7.2.0",
"shiki": "^4.3.0",
"vite": "^7.3.6",
"vite": "^8.1.3",
"vue": "^3.5.39"
},
"devDependencies": {
@ -168,7 +168,6 @@
"nanoid": "^5.1.16",
"obug": "^2.1.3",
"ora": "^9.4.1",
"oxc-minify": "^0.98.0",
"p-map": "^7.0.4",
"package-directory": "^8.2.0",
"path-to-regexp": "^6.3.0",
@ -190,13 +189,12 @@
"sitemap": "^9.0.1",
"tinyglobby": "^0.2.17",
"typescript": "^5.9.3",
"vitest": "4.0.0-beta.4",
"vitest": "^4.1.9",
"vue-tsc": "^3.3.5",
"wait-on": "^9.0.10"
},
"peerDependencies": {
"markdown-it-mathjax3": "^4",
"oxc-minify": "*",
"postcss": "^8"
},
"peerDependenciesMeta": {
@ -205,9 +203,6 @@
},
"postcss": {
"optional": true
},
"oxc-minify": {
"optional": true
}
},
"packageManager": "pnpm@10.33.0"

File diff suppressed because it is too large Load Diff

@ -13,7 +13,6 @@ onlyBuiltDependencies:
overrides:
ora>string-width: ^5
vite: npm:rolldown-vite@latest
patchedDependencies:
'@types/markdown-it-attrs': patches/@types__markdown-it-attrs@4.1.3.patch

@ -33,7 +33,7 @@ const plugins = [
}),
commonjs(),
nodeResolve({ preferBuiltins: false }),
esbuild({ target: 'node20' }),
esbuild({ target: 'node22' }),
json()
]

@ -3,15 +3,14 @@ import fs from 'fs-extra'
import { createHash } from 'node:crypto'
import { createRequire } from 'node:module'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import pMap from 'p-map'
import { packageDirectorySync } from 'package-directory'
import * as vite from 'vite'
import type { BuildOptions, Rollup } from 'vite'
import type { BuildOptions, Rolldown } from 'vite'
import { resolveConfig, type SiteConfig } from '../config'
import { clearCache } from '../markdownToVue'
import { slash, type Awaitable, type HeadConfig } from '../shared'
import { deserializeFunctions, serializeFunctions } from '../utils/fnSerialize'
import { nativeImport } from '../utils/nativeImport'
import { task } from '../utils/task'
import { bundle } from './bundle'
import { generateSitemap } from './generateSitemap'
@ -29,19 +28,6 @@ export async function build(
) {
const start = Date.now()
// @ts-ignore only exists for rolldown-vite
if (vite.rolldownVersion) {
try {
await import('oxc-minify')
} catch {
throw new Error(
'`oxc-minify` is not installed.' +
' vitepress requires `oxc-minify` to be installed when rolldown-vite is used.' +
' Please run `npm install oxc-minify`.'
)
}
}
process.env.NODE_ENV = 'production'
const siteConfig = await resolveConfig(root, 'build', 'production')
@ -76,7 +62,7 @@ export async function build(
}
const entryPath = path.join(siteConfig.tempDir, 'app.js')
const { render } = await import(pathToFileURL(entryPath).href)
const { render } = await nativeImport(entryPath)
await task('rendering pages', async () => {
const appChunk =
@ -86,13 +72,13 @@ export async function build(
chunk.type === 'chunk' &&
chunk.isEntry &&
chunk.facadeModuleId?.endsWith('.js')
) as Rollup.OutputChunk)
) as Rolldown.OutputChunk)
const cssChunk = (
siteConfig.mpa ? serverResult : clientResult!
).output.find(
(chunk) => chunk.type === 'asset' && chunk.fileName.endsWith('.css')
) as Rollup.OutputAsset
) as Rolldown.OutputAsset
const assets = (siteConfig.mpa ? serverResult : clientResult!).output
.filter(
@ -108,8 +94,7 @@ export async function build(
clientResult.output.some(
(chunk) =>
chunk.type === 'chunk' &&
// @ts-ignore only exists for rolldown-vite
(vite.rolldownVersion || chunk.name === 'theme') && // FIXME: remove when rolldown-vite supports manualChunks
chunk.name === 'theme' &&
chunk.moduleIds.some((id) => id.includes('client/theme-default'))
)

@ -1,4 +1,4 @@
import { build, type Rollup } from 'vite'
import { build, type Rolldown } from 'vite'
import type { SiteConfig } from '..'
const virtualEntry = 'client.js'
@ -6,7 +6,7 @@ const virtualEntry = 'client.js'
export async function buildMPAClient(
js: Record<string, string>,
config: SiteConfig
): Promise<Rollup.RollupOutput> {
): Promise<Rolldown.RolldownOutput> {
const files = Object.keys(js)
const themeFiles = files.filter((f) => !f.endsWith('.md'))
const pages = files.filter((f) => f.endsWith('.md'))
@ -19,7 +19,7 @@ export async function buildMPAClient(
build: {
emptyOutDir: false,
outDir: config.outDir,
rollupOptions: {
rolldownOptions: {
input: [virtualEntry, ...pages]
}
},
@ -42,5 +42,5 @@ export async function buildMPAClient(
}
}
]
}) as Promise<Rollup.RollupOutput>
}) as Promise<Rolldown.RolldownOutput>
}

@ -1,12 +1,11 @@
import fs from 'fs-extra'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import * as vite from 'vite'
import {
build,
normalizePath,
type BuildOptions,
type Rollup,
type Rolldown,
type InlineConfig as ViteInlineConfig
} from 'vite'
import { APP_PATH } from '../alias'
@ -39,14 +38,14 @@ export async function bundle(
config: SiteConfig,
options: BuildOptions
): Promise<{
clientResult: Rollup.RollupOutput | null
serverResult: Rollup.RollupOutput
clientResult: Rolldown.RolldownOutput | null
serverResult: Rolldown.RolldownOutput
pageToHashMap: Record<string, string>
}> {
const pageToHashMap = Object.create(null) as Record<string, string>
const clientJSMap = Object.create(null) as Record<string, string>
// define custom rollup input
// define custom rolldown input
// this is a multi-entry build - every page is considered an entry chunk
// the loading is done via filename conversion rules so that the
// metadata doesn't need to be included in the main chunk.
@ -65,7 +64,11 @@ export async function bundle(
)
// resolve options to pass to vite
const { rollupOptions } = options
const {
rollupOptions,
rolldownOptions = rollupOptions,
...restOptions
} = options
const resolveViteConfig = async (
ssr: boolean
@ -84,15 +87,15 @@ export async function bundle(
noExternal: ['vitepress', '@docsearch/css']
},
build: {
...options,
...restOptions,
emptyOutDir: true,
ssr,
ssrEmitAssets: config.mpa,
minify: ssr ? !!config.mpa : (options.minify ?? !process.env.DEBUG),
outDir: ssr ? config.tempDir : config.outDir,
cssCodeSplit: false,
rollupOptions: {
...rollupOptions,
rolldownOptions: {
...rolldownOptions,
input: {
// use different entry based on ssr or not
app: path.resolve(APP_PATH, ssr ? 'ssr.js' : 'index.js'),
@ -103,7 +106,7 @@ export async function bundle(
preserveEntrySignatures: 'allow-extension',
output: {
sanitizeFileName,
...rollupOptions?.output,
...rolldownOptions?.output,
assetFileNames: `${config.assetsDir}/[name].[hash].[ext]`,
...(ssr
? {
@ -118,19 +121,23 @@ export async function bundle(
? `${config.assetsDir}/chunks/ui-custom.[hash].js`
: `${config.assetsDir}/chunks/[name].[hash].js`
},
// @ts-ignore skip setting it for rolldown-vite since it doesn't support `manualChunks`
...(vite.rolldownVersion
? undefined
: {
manualChunks(
codeSplitting: {
groups: [
{
name(
id: string,
ctx: Pick<Rollup.PluginContext, 'getModuleInfo'>
ctx: Pick<Rolldown.PluginContext, 'getModuleInfo'>
) {
// ctx.getModuleInfo must not be called detached from ctx
const getModuleInfo: Rolldown.GetModuleInfo = (
moduleId
) => ctx.getModuleInfo(moduleId)
// move known framework code into a stable chunk so that
// custom theme changes do not invalidate hash for all pages
if (
id.startsWith('\0vite') ||
ctx.getModuleInfo(id)?.meta['vite:asset']
getModuleInfo(id)?.meta['vite:asset']
) {
return 'framework'
}
@ -144,7 +151,7 @@ export async function bundle(
return 'framework'
}
if (
isEagerChunk(id, ctx.getModuleInfo) &&
isEagerChunk(id, getModuleInfo) &&
/@vue\/(runtime|shared|reactivity)/.test(id)
) {
return 'framework'
@ -155,7 +162,7 @@ export async function bundle(
!excludedModules.some((i) => id.includes(i))) &&
staticImportedByEntry(
id,
ctx.getModuleInfo,
getModuleInfo,
cacheTheme,
themeEntryRE
)
@ -163,7 +170,9 @@ export async function bundle(
return 'theme'
}
}
})
}
]
}
})
}
}
@ -171,16 +180,18 @@ export async function bundle(
configFile: config.vite?.configFile
})
let clientResult!: Rollup.RollupOutput | null
let serverResult!: Rollup.RollupOutput
let clientResult!: Rolldown.RolldownOutput | null
let serverResult!: Rolldown.RolldownOutput
await task('building client + server bundles', async () => {
clientResult = config.mpa
? null
: ((await build(await resolveViteConfig(false))) as Rollup.RollupOutput)
: ((await build(
await resolveViteConfig(false)
)) as Rolldown.RolldownOutput)
serverResult = (await build(
await resolveViteConfig(true)
)) as Rollup.RollupOutput
)) as Rolldown.RolldownOutput
})
if (config.mpa) {
@ -223,7 +234,7 @@ const cacheTheme = new Map<string, boolean>()
/**
* Check if a module is statically imported by at least one entry.
*/
function isEagerChunk(id: string, getModuleInfo: Rollup.GetModuleInfo) {
function isEagerChunk(id: string, getModuleInfo: Rolldown.GetModuleInfo) {
if (
id.includes('node_modules') &&
!CSS_LANGS_RE.test(id) &&
@ -235,7 +246,7 @@ function isEagerChunk(id: string, getModuleInfo: Rollup.GetModuleInfo) {
function staticImportedByEntry(
id: string,
getModuleInfo: Rollup.GetModuleInfo,
getModuleInfo: Rolldown.GetModuleInfo,
cache: Map<string, boolean>,
entryRE: RegExp | null = null,
importStack: string[] = []

@ -1,9 +1,7 @@
import { isBooleanAttr } from '@vue/shared'
import fs from 'fs-extra'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import * as vite from 'vite'
import { normalizePath, transformWithEsbuild, type Rollup } from 'vite'
import { minify, normalizePath, type Rolldown } from 'vite'
import { version } from '../../../package.json'
import type { SiteConfig } from '../config'
import {
@ -19,14 +17,15 @@ import {
type PageData,
type SSGContext
} from '../shared'
import { nativeImport } from '../utils/nativeImport'
export async function renderPage(
render: (path: string) => Promise<SSGContext>,
config: SiteConfig,
page: string, // foo.md
result: Rollup.RollupOutput | null,
appChunk: Rollup.OutputChunk | null,
cssChunk: Rollup.OutputAsset | null,
result: Rolldown.RolldownOutput | null,
appChunk: Rolldown.OutputChunk | null,
cssChunk: Rolldown.OutputAsset | null,
assets: string[],
pageToHashMap: Record<string, string>,
metadataScript: { html: string; inHead: boolean },
@ -57,8 +56,8 @@ export async function renderPage(
try {
// resolve page data so we can render head tags
const { __pageData } = await import(
pathToFileURL(path.join(config.tempDir, pageServerJsFileName)).href
const { __pageData } = await nativeImport(
path.join(config.tempDir, pageServerJsFileName)
)
pageData = __pageData
} catch (e) {
@ -143,7 +142,7 @@ export async function renderPage(
(chunk) =>
chunk.type === 'chunk' &&
chunk.facadeModuleId === slash(path.join(config.srcDir, page))
) as Rollup.OutputChunk
) as Rolldown.OutputChunk
if (matchingChunk) {
if (!matchingChunk.code.includes('import')) {
inlinedScript = `<script type="module">${matchingChunk.code}</script>`
@ -208,8 +207,8 @@ export async function renderPage(
function resolvePageImports(
config: SiteConfig,
page: string,
result: Rollup.RollupOutput,
appChunk: Rollup.OutputChunk
result: Rolldown.RolldownOutput,
appChunk: Rolldown.OutputChunk
) {
page = config.rewrites.inv[page] || page
// find the page's js chunk and inject script tags for its imports so that
@ -226,7 +225,7 @@ function resolvePageImports(
srcPath = normalizePath(srcPath)
const pageChunk = result.output.find(
(chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === srcPath
) as Rollup.OutputChunk
) as Rolldown.OutputChunk
return [
...appChunk.imports,
// ...appChunk.dynamicImports,
@ -265,14 +264,7 @@ function renderAttrs(attrs: Record<string, string>): string {
}
async function minifyScript(code: string, filename: string): Promise<string> {
// @ts-ignore use oxc-minify when rolldown-vite is used
if (vite.rolldownVersion) {
const oxcMinify = await import('oxc-minify')
return (await oxcMinify.minify(filename, code)).code.trim()
}
return (
await transformWithEsbuild(code, filename, { minify: true })
).code.trim()
return (await minify(filename, code)).code.trim()
}
function filterOutHeadDescription(head: HeadConfig[] = []) {

@ -7,7 +7,7 @@ import {
type EnvironmentModuleNode,
type Plugin,
type ResolvedConfig,
type Rollup,
type Rolldown,
type UserConfig
} from 'vite'
import {
@ -54,8 +54,8 @@ const staticRestoreRE = /__VP_STATIC_(START|END)__/g
const scriptClientRE = /<script\b[^>]*client\b[^>]*>([^]*?)<\/script>/
const isPageChunk = (
chunk: Rollup.OutputAsset | Rollup.OutputChunk
): chunk is Rollup.OutputChunk & { facadeModuleId: string } =>
chunk: Rolldown.OutputAsset | Rolldown.OutputChunk
): chunk is Rolldown.OutputChunk & { facadeModuleId: string } =>
!!(
chunk.type === 'chunk' &&
chunk.isEntry &&
@ -285,7 +285,7 @@ export async function createVitePressPlugin(
},
renderChunk(code, chunk) {
if (!ssr && isPageChunk(chunk as Rollup.OutputChunk)) {
if (!ssr && isPageChunk(chunk as Rolldown.OutputChunk)) {
// For each page chunk, inject marker for start/end of static strings.
// we do this here because in generateBundle the chunks would have been
// minified and we won't be able to safely locate the strings.

@ -0,0 +1,24 @@
import { createRequire } from 'node:module'
import { pathToFileURL } from 'node:url'
const require = createRequire(import.meta.url)
// vitepress may itself be executed inside a vite module runner, e.g. when
// `build()` is called from a vitest global setup file with vitepress not
// externalized. the runner rewrites every dynamic import to be resolved
// through vite, which cannot load the temp SSR bundle and its relative page
// imports, and runtime-constructed `import()` wrappers fail there with
// ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. require(esm) is supported by all
// node versions vitepress runs on and always uses node's own loader.
export async function nativeImport(file: string): Promise<any> {
try {
return require(file)
} catch (e: any) {
// require() cannot load modules that use top-level await - fall back to
// a plain dynamic import, which only misbehaves inside a module runner
if (e.code === 'ERR_REQUIRE_ASYNC_MODULE') {
return import(pathToFileURL(file).href)
}
throw e
}
}
Loading…
Cancel
Save