Skip to content
Open
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"mdit",
"prefetch",
"preload",
"rspack",
"slugify",
"tinyglobby",
"unmount",
Expand Down
19 changes: 11 additions & 8 deletions e2e/docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import process from 'node:process'

import { rspackBundler } from '@vuepress/bundler-rspack'
import { viteBundler } from '@vuepress/bundler-vite'
import { webpackBundler } from '@vuepress/bundler-webpack'
import { defineUserConfig } from 'vuepress'
Expand Down Expand Up @@ -62,15 +63,17 @@ export default defineUserConfig({
},

bundler:
E2E_BUNDLER === 'webpack'
? webpackBundler()
: viteBundler({
viteOptions: {
optimizeDeps: {
include: ['@vuepress-e2e/conditional-exports'],
E2E_BUNDLER === 'rspack'
? rspackBundler()
: E2E_BUNDLER === 'webpack'
? webpackBundler()
: viteBundler({
viteOptions: {
optimizeDeps: {
include: ['@vuepress-e2e/conditional-exports'],
},
},
},
}),
}),

theme: e2eTheme(),

Expand Down
5 changes: 5 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@
"type": "module",
"scripts": {
"docs:build": "vuepress build docs --clean-cache --clean-temp",
"docs:build:rspack": "cross-env E2E_BUNDLER=rspack pnpm docs:build",
"docs:build:webpack": "cross-env E2E_BUNDLER=webpack pnpm docs:build",
"docs:clean": "rimraf docs/.vuepress/.temp docs/.vuepress/.cache docs/.vuepress/dist",
"docs:dev": "vuepress dev docs --clean-cache --clean-temp",
"docs:dev:rspack": "cross-env E2E_BUNDLER=rspack pnpm docs:dev",
"docs:dev:webpack": "cross-env E2E_BUNDLER=webpack pnpm docs:dev",
"docs:serve": "serve -l 9080 docs/.vuepress/dist",
"e2e:build": "cross-env E2E_COMMAND=build playwright test",
"e2e:build:rspack": "cross-env E2E_COMMAND=build E2E_BUNDLER=rspack playwright test",
"e2e:build:webpack": "cross-env E2E_COMMAND=build E2E_BUNDLER=webpack playwright test",
"e2e:dev": "cross-env E2E_COMMAND=dev playwright test",
"e2e:dev:rspack": "cross-env E2E_COMMAND=dev E2E_BUNDLER=rspack playwright test",
"e2e:dev:webpack": "cross-env E2E_COMMAND=dev E2E_BUNDLER=webpack playwright test"
},
"dependencies": {
"@vuepress-e2e/conditional-exports": "file:./modules/conditional-exports",
"@vuepress-e2e/style-exports": "file:./modules/style-exports",
"@vuepress/bundler-rspack": "workspace:*",
"@vuepress/bundler-vite": "workspace:*",
"@vuepress/bundler-webpack": "workspace:*",
"sass": "^1.99.0",
Expand Down
12 changes: 12 additions & 0 deletions packages/bundler-rspack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# @vuepress/bundler-rspack

[![npm](https://badgen.net/npm/v/@vuepress/bundler-rspack/next)](https://www.npmjs.com/package/@vuepress/bundler-rspack)
[![license](https://badgen.net/github/license/vuepress/core)](https://github.com/vuepress/core/blob/main/LICENSE)

## Documentation

https://vuepress.vuejs.org

## License

[MIT](https://github.com/vuepress/core/blob/main/LICENSE)
67 changes: 67 additions & 0 deletions packages/bundler-rspack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "@vuepress/bundler-rspack",
"version": "2.0.0-rc.29",
"description": "Bundler rspack package of VuePress",
"keywords": [
"bundler",
"rspack",
"vuepress",
"vuepress-bundler"
],
"homepage": "https://github.com/vuepress",
"bugs": {
"url": "https://github.com/vuepress/core/issues"
},
"license": "MIT",
"author": "meteorlxy",
"repository": {
"type": "git",
"url": "git+https://github.com/vuepress/core.git"
},
"files": [
"dist"
],
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"imports": {
"#vuepress-markdown-loader": "./dist/vuepress-markdown-loader.cjs",
"#vuepress-ssr-loader": "./dist/vuepress-ssr-loader.cjs"
},
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsdown",
"clean": "rimraf dist"
},
"dependencies": {
"@rsbuild/plugin-pug": "^1.3.3",
"@rspack/core": "^2.0.3",
"@rspack/dev-server": "^2.0.1",
"@vuepress/bundlerutils": "workspace:*",
"@vuepress/client": "workspace:*",
"@vuepress/core": "workspace:*",
"@vuepress/shared": "workspace:*",
"@vuepress/utils": "workspace:*",
"autoprefixer": "^10.5.0",
"css-loader": "^7.1.4",
"esbuild-loader": "~4.4.3",
"postcss": "^8.5.14",
"postcss-loader": "^8.2.1",
"rspack-chain": "^2.0.1",
"rspack-merge": "^0.1.1",
"style-loader": "^4.0.0",
"vue": "catalog:vue",
"vue-loader": "^17.4.2",
"vue-router": "catalog:vue"
},
"devDependencies": {
"connect-next": "^4.0.1"
}
}
114 changes: 114 additions & 0 deletions packages/bundler-rspack/src/build/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { rspack } from '@rspack/core'
import type { MultiRspackOptions } from '@rspack/core'
import { createVueServerApp, getSsrTemplate } from '@vuepress/bundlerutils'
import type { App, Bundler } from '@vuepress/core'
import { colors, debug, fs, logger, withSpinner } from '@vuepress/utils'

import { resolveRspackConfig } from '../resolveRspackConfig.js'
import type { RspackBundlerOptions } from '../types.js'
import {
CLIENT_MANIFEST_FILENAME,
createClientConfig,
} from './createClientConfig.js'
import { createServerConfig } from './createServerConfig.js'
import { renderPage } from './renderPage.js'
import { resolveClientManifestMeta } from './resolveClientManifestMeta.js'
import type { ClientManifest } from './types.js'

const log = debug('vuepress:bundler-rspack/build')

export const build = async (
options: RspackBundlerOptions,
app: App,
): ReturnType<Bundler['build']> => {
// plugin hook: extendsBundlerOptions
await app.pluginApi.hooks.extendsBundlerOptions.process(options, app)

// rspack compile
log('compiling start')
await withSpinner('Compiling with rspack')(async () => {
// create rspack config
const clientConfig = resolveRspackConfig({
config: await createClientConfig(app, options),
options,
isServer: false,
isBuild: true,
})
const serverConfig = resolveRspackConfig({
config: await createServerConfig(app, options),
options,
isServer: true,
isBuild: true,
})

await new Promise<void>((resolve, reject) => {
rspack(
[clientConfig, serverConfig] as MultiRspackOptions,
(err, stats) => {
if (err) {
reject(err)
} else {
if (stats) {
const { warnings, errors } = stats.toJson('errors-warnings')

errors?.forEach((item) => {
logger.error(item)
})
warnings?.forEach((warning) => {
logger.warn(warning)
})

if (stats.hasErrors())
reject(new Error('Failed to compile with errors'))
}

resolve()
}
},
)
})
})
log('compiling finish')

// render pages
await withSpinner(`Rendering ${app.pages.length} pages`)(async (spinner) => {
// load the client manifest file
const clientManifestPath = app.dir.temp(CLIENT_MANIFEST_FILENAME)
const clientManifest = (await fs.readJson(
clientManifestPath,
)) as ClientManifest

// resolve client files meta
const { initialFilesMeta, asyncFilesMeta, moduleFilesMetaMap } =
resolveClientManifestMeta(clientManifest)

// create vue ssr app and get ssr template
const { vueApp, vueRouter } = await createVueServerApp(
app.dir.temp('.server/app.cjs'),
)
const ssrTemplate = await getSsrTemplate(app)

// pre-render pages to html files
for (const page of app.pages) {
if (spinner) {
spinner.text = `Rendering pages ${colors.magenta(page.path)}`
}
await renderPage({
app,
page,
vueApp,
vueRouter,
ssrTemplate,
initialFilesMeta,
asyncFilesMeta,
moduleFilesMetaMap,
})
}
})

// keep the server bundle files in debug mode
if (!app.env.isDebug) {
// remove server temp directory after pages rendered
await fs.remove(app.dir.temp('.server'))
}
}
96 changes: 96 additions & 0 deletions packages/bundler-rspack/src/build/createClientConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
CopyRspackPlugin,
CssExtractRspackPlugin,
LightningCssMinimizerRspackPlugin,
} from '@rspack/core'
import type { Module } from '@rspack/core'
import type { App } from '@vuepress/core'
import { fs } from '@vuepress/utils'
import type { RspackChain } from 'rspack-chain'

import { createClientBaseConfig } from '../config/index.js'
import type { RspackBundlerOptions } from '../types.js'
import { createClientPlugin } from './createClientPlugin.js'

/**
* Filename of the client manifest file that generated by client plugin
*/
export const CLIENT_MANIFEST_FILENAME = '.server/client-manifest.json'

export const createClientConfig = async (
app: App,
options: RspackBundlerOptions,
): Promise<RspackChain> => {
const config = await createClientBaseConfig({
app,
options,
isBuild: true,
})

// vuepress client plugin, handle client assets info for ssr
config
.plugin('vuepress-client')
.use(createClientPlugin(app.dir.temp(CLIENT_MANIFEST_FILENAME)))

// copy files from public dir to dest dir
if (fs.pathExistsSync(app.dir.public())) {
config.plugin('copy').use(CopyRspackPlugin, [
{
patterns: [{ from: app.dir.public(), to: app.dir.dest() }],
},
])
}

// optimizations for production mode
// css-extract
config.plugin('css-extract').use(CssExtractRspackPlugin, [
{
filename: 'assets/css/styles.[chunkhash:8].css',
},
])

config.optimization.splitChunks({
cacheGroups: {
// ensure all css are extracted together.
// since most of the CSS will be from the theme and very little
// CSS will be from async chunks
styles: {
idHint: 'styles',
// necessary to ensure async chunks are also extracted
test: (m: Module) => m.type.includes('css/mini-extract'),
chunks: 'all',
enforce: true,
reuseExistingChunk: true,
},
// extract external library to a standalone chunk
vendor: {
idHint: 'vendor',
test: /node_modules/,
chunks: 'all',
priority: -10,
reuseExistingChunk: true,
},
},
})

// enable runtimeChunk
config.optimization.runtimeChunk(true)

// minimize
config.optimization.minimize(true)

// minimizer
config.optimization.set('minimizer', [
// keep the default minimizer
'...',
// add css minimizer
new LightningCssMinimizerRspackPlugin(),
])

// disable performance hints
if (!app.env.isDebug) {
config.performance.hints(false)
}

return config
}
Loading
Loading