perf: use hook filters in vite plugins

Per-module hooks (resolveId / load / transform) now declare filters,
so rolldown skips the Rust-to-JS call for modules they cannot act on
instead of invoking every hook for every module in the graph.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5335/head
Divyansh Singh 1 month ago
parent a1f2147c0a
commit fa24c6d67b

@ -68,7 +68,7 @@ describe('node/plugins/localSearchPlugin', () => {
{ publicDir: siteConfig.publicDir }
)
const indexModule = (await (plugin.load as any)?.call(
const indexModule = (await (plugin.load as any)?.handler.call(
{},
'/@localSearchIndex'
)) as string
@ -79,10 +79,16 @@ describe('node/plugins/localSearchPlugin', () => {
expect(indexModule).toContain('"zh": () => import(\'@localSearchIndexzh\')')
const rootIndex = loadIndex(
(await (plugin.load as any)?.call({}, '/@localSearchIndexroot')) as string
(await (plugin.load as any)?.handler.call(
{},
'/@localSearchIndexroot'
)) as string
)
const zhIndex = loadIndex(
(await (plugin.load as any)?.call({}, '/@localSearchIndexzh')) as string
(await (plugin.load as any)?.handler.call(
{},
'/@localSearchIndexzh'
)) as string
)
expect(rootIndex.search('rootonlytoken')).toMatchObject([

@ -134,6 +134,7 @@
"@mdit/plugin-emoji": "^1.1.0",
"@mdit/plugin-tasklist": "^1.0.1",
"@polka/compression": "^1.0.0-next.28",
"@rolldown/pluginutils": "^1.0.1",
"@rollup/plugin-alias": "^6.0.0",
"@rollup/plugin-commonjs": "^29.0.3",
"@rollup/plugin-json": "^6.1.0",

@ -122,6 +122,9 @@ importers:
'@polka/compression':
specifier: ^1.0.0-next.28
version: 1.0.0-next.28
'@rolldown/pluginutils':
specifier: ^1.0.1
version: 1.0.1
'@rollup/plugin-alias':
specifier: ^6.0.0
version: 6.0.0(rollup@4.62.2)

@ -1,3 +1,4 @@
import { exactRegex } from '@rolldown/pluginutils'
import path from 'node:path'
import c from 'picocolors'
import {
@ -166,11 +167,12 @@ export async function createVitePressPlugin(
: baseConfig
},
resolveId(id, importer, resolveOptions) {
resolveId: {
filter: { id: [exactRegex(SITE_DATA_ID), startsWithThemeRE] },
handler(id, importer, resolveOptions) {
if (id === SITE_DATA_ID) {
return SITE_DATA_REQUEST_PATH
}
if (startsWithThemeRE.test(id)) {
return this.resolve(
siteConfig.themeDir + id.slice(6),
importer,
@ -179,8 +181,9 @@ export async function createVitePressPlugin(
}
},
load(id) {
if (id === SITE_DATA_REQUEST_PATH) {
load: {
filter: { id: exactRegex(SITE_DATA_REQUEST_PATH) },
handler() {
let data = siteData
// head info is not needed by the client in production build
if (config.command === 'build') {
@ -196,8 +199,9 @@ export async function createVitePressPlugin(
}
},
// TODO: use plugin hook filters
async transform(code, id) {
transform: {
filter: { id: [docsearchRE, /\.vue$/, /\.md$/] },
async handler(code, id) {
if (docsearchRE.test(normalizePath(id))) {
return code
.replaceAll('[data-theme=dark]', '.dark')
@ -239,6 +243,7 @@ export async function createVitePressPlugin(
}
return processClientJS(vueSrc, id)
}
}
},
renderStart() {

@ -133,8 +133,9 @@ export const dynamicRoutesPlugin = async (
name: 'vitepress:dynamic-routes',
enforce: 'pre',
resolveId(id) {
if (!id.endsWith('.md')) return
resolveId: {
filter: { id: /\.md$/ },
handler(id) {
const normalizedId = id.startsWith(config.srcDir)
? id
: normalizePath(path.resolve(config.srcDir, id.replace(/^\//, '')))
@ -142,9 +143,12 @@ export const dynamicRoutesPlugin = async (
(r) => r.fullPath === normalizedId
)
if (matched) return normalizedId
}
},
load(id) {
load: {
filter: { id: /\.md$/ },
handler(id) {
const matched = config.dynamicRoutes.find((r) => r.fullPath === id)
if (matched) {
const { route, params, content } = matched
@ -170,6 +174,7 @@ export const dynamicRoutesPlugin = async (
// __pageData in ../markdownToVue.ts
return `__VP_PARAMS_START${JSON.stringify(params)}__VP_PARAMS_END__${baseContent}`
}
}
},
async hotUpdate({ file, modules: existingMods }) {

@ -1,3 +1,4 @@
import { prefixRegex } from '@rolldown/pluginutils'
import MiniSearch from 'minisearch'
import fs from 'node:fs'
import { readFile } from 'node:fs/promises'
@ -28,13 +29,15 @@ export async function localSearchPlugin(
if (siteConfig.site.themeConfig?.search?.provider !== 'local') {
return {
name: 'vitepress:local-search',
resolveId(id) {
if (id.startsWith(LOCAL_SEARCH_INDEX_ID)) {
resolveId: {
filter: { id: prefixRegex(LOCAL_SEARCH_INDEX_ID) },
handler() {
return LOCAL_SEARCH_INDEX_REQUEST_PATH
}
},
load(id) {
if (id.startsWith(LOCAL_SEARCH_INDEX_REQUEST_PATH)) {
load: {
filter: { id: prefixRegex(LOCAL_SEARCH_INDEX_REQUEST_PATH) },
handler() {
return `export default '{}'`
}
}
@ -176,13 +179,16 @@ export async function localSearchPlugin(
pending = scanForBuild().then(onIndexUpdated)
},
resolveId(id) {
if (id.startsWith(LOCAL_SEARCH_INDEX_ID)) {
resolveId: {
filter: { id: prefixRegex(LOCAL_SEARCH_INDEX_ID) },
handler(id) {
return `/${id}`
}
},
async load(id) {
load: {
filter: { id: prefixRegex(LOCAL_SEARCH_INDEX_REQUEST_PATH) },
async handler(id) {
if (id === LOCAL_SEARCH_INDEX_REQUEST_PATH) {
await pending
if (process.env.NODE_ENV === 'production') {
@ -197,7 +203,7 @@ export async function localSearchPlugin(
)
}
return `export default {${records.join(',')}}`
} else if (id.startsWith(LOCAL_SEARCH_INDEX_REQUEST_PATH)) {
} else {
await pending
return `export default ${JSON.stringify(
JSON.stringify(
@ -207,6 +213,7 @@ export async function localSearchPlugin(
)
)}`
}
}
},
async hotUpdate({ file }) {

@ -56,8 +56,9 @@ export const staticDataPlugin: Plugin = {
server = _server
},
async load(id) {
if (loaderMatch.test(id)) {
load: {
filter: { id: loaderMatch },
async handler(id) {
let _resolve: ((res: any) => void) | undefined
if (isBuild) {
if (idToPendingPromiseMap[id]) return idToPendingPromiseMap[id]

@ -7,8 +7,9 @@ export const webFontsPlugin = (enabled = false): Plugin => ({
name: 'vitepress:webfonts',
enforce: 'pre',
transform(code, id) {
if (/[\\/]fonts\.s?css/.test(id)) {
transform: {
filter: { id: /[\\/]fonts\.s?css/ },
handler(code) {
if (enabled) {
return code.match(webfontMarkerRE)?.[1]
} else {

Loading…
Cancel
Save