Skip to content

Commit 5488138

Browse files
committedSep 12, 2024·
feat(bundlerutils): add bundlerutils package
1 parent 5709c57 commit 5488138

18 files changed

+230
-80
lines changed
 

‎.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
],
2323
"cSpell.words": [
2424
"bumpp",
25+
"bundlerutils",
2526
"composables",
2627
"devtool",
2728
"docsearch",

‎CONTRIBUTING.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ Wrapper of core packages:
1717

1818
- `vuepress`: A wrapper of the above packages, and provides `vuepress` command line tool. Users need to choose and install bundler and theme by themselves.
1919

20-
Bundler packages:
20+
Bundler and related packages:
2121

2222
- `bundler-vite`: The VuePress bundler package with vite. Use vite to `dev` and `build` VuePress app that generated by `@vuepress/core`.
2323
- `bundler-webpack`: The VuePress bundler package with webpack. Use webpack to `dev` and `build` VuePress app that generated by `@vuepress/core`.
24+
- `bundlerutils`: Utilities for bundler packages.
2425

2526
## Development Setup
2627

‎CONTRIBUTING_zh.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ Core Packages 的封装:
1717

1818
- `vuepress`: 是上述 Core Packages 的封装,提供了 `vuepress` 命令行工具。用户需要在此包的基础上自行选择并安装打包工具和主题。
1919

20-
Bundler Packages :
20+
Bundler 及其相关 Packages :
2121

2222
- `bundler-vite`: 基于 Vite 的 Bundler 模块。使用 Vite 对 VuePress App 执行 `dev``build` 操作。
2323
- `bundler-webpack`: 基于 Webpack 的 Bundler 模块。使用 Webpack 对 VuePress App 执行 `dev``build` 操作。
24+
- `bundlerutils`: 供 Bundler 模块使用的工具函数模块。
2425

2526
## 开发配置
2627

‎packages/bundler-vite/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
},
3737
"dependencies": {
3838
"@vitejs/plugin-vue": "^5.1.3",
39+
"@vuepress/bundlerutils": "workspace:*",
3940
"@vuepress/client": "workspace:*",
4041
"@vuepress/core": "workspace:*",
4142
"@vuepress/shared": "workspace:*",

‎packages/bundler-vite/src/build/build.ts

+7-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { CreateVueAppFunction } from '@vuepress/client'
1+
import { createVueServerApp, getSsrTemplate } from '@vuepress/bundlerutils'
22
import type { App, Bundler } from '@vuepress/core'
3-
import { colors, debug, fs, importFile, withSpinner } from '@vuepress/utils'
3+
import { colors, debug, fs, withSpinner } from '@vuepress/utils'
44
import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'
55
import { build as viteBuild } from 'vite'
66
import { resolveViteConfig } from '../resolveViteConfig.js'
@@ -58,19 +58,11 @@ export const build = async (
5858
(item) => item.type === 'chunk' && item.isEntry,
5959
) as OutputChunk
6060

61-
// load the compiled server bundle
62-
const serverEntryPath = app.dir.temp('.server', serverEntryChunk.fileName)
63-
const { createVueApp } = await importFile<{
64-
createVueApp: CreateVueAppFunction
65-
}>(serverEntryPath)
66-
// create vue ssr app
67-
const { app: vueApp, router: vueRouter } = await createVueApp()
68-
const { renderToString } = await import('vue/server-renderer')
69-
70-
// load ssr template file
71-
const ssrTemplate = await fs.readFile(app.options.templateBuild, {
72-
encoding: 'utf8',
73-
})
61+
// create vue ssr app and get ssr template
62+
const { vueApp, vueRouter } = await createVueServerApp(
63+
app.dir.temp('.server', serverEntryChunk.fileName),
64+
)
65+
const ssrTemplate = await getSsrTemplate(app)
7466

7567
// pre-render pages to html files
7668
for (const page of app.pages) {
@@ -80,7 +72,6 @@ export const build = async (
8072
page,
8173
vueApp,
8274
vueRouter,
83-
renderToString,
8475
ssrTemplate,
8576
output: clientOutput.output,
8677
outputEntryChunk: clientEntryChunk,

‎packages/bundler-vite/src/build/renderPage.ts

+7-18
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1+
import { renderPageToString } from '@vuepress/bundlerutils'
12
import type { App, Page } from '@vuepress/core'
2-
import type { VuepressSSRContext } from '@vuepress/shared'
33
import { fs, renderHead } from '@vuepress/utils'
44
import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'
55
import type { App as VueApp } from 'vue'
6-
import { ssrContextKey } from 'vue'
7-
import type { SSRContext } from 'vue/server-renderer'
86
import type { Router } from 'vue-router'
97
import { renderPagePrefetchLinks } from './renderPagePrefetchLinks.js'
108
import { renderPagePreloadLinks } from './renderPagePreloadLinks.js'
@@ -17,7 +15,6 @@ export const renderPage = async ({
1715
page,
1816
vueApp,
1917
vueRouter,
20-
renderToString,
2118
ssrTemplate,
2219
output,
2320
outputEntryChunk,
@@ -27,32 +24,24 @@ export const renderPage = async ({
2724
page: Page
2825
vueApp: VueApp
2926
vueRouter: Router
30-
renderToString: (input: VueApp, context: SSRContext) => Promise<string>
3127
ssrTemplate: string
3228
output: RollupOutput['output']
3329
outputEntryChunk: OutputChunk
3430
outputCssAsset: OutputAsset | undefined
3531
}): Promise<void> => {
36-
// switch to current page route
37-
await vueRouter.push(page.path)
38-
await vueRouter.isReady()
39-
40-
// create vue ssr context with default values
41-
delete vueApp._context.provides[ssrContextKey]
42-
const ssrContext: VuepressSSRContext = {
43-
lang: 'en',
44-
head: [],
45-
}
46-
4732
// render current page to string
48-
const pageRendered = await renderToString(vueApp, ssrContext)
33+
const { ssrContext, ssrString } = await renderPageToString({
34+
page,
35+
vueApp,
36+
vueRouter,
37+
})
4938

5039
// resolve page chunks
5140
const pageChunkFiles = resolvePageChunkFiles({ page, output })
5241

5342
// generate html string
5443
const html = await app.options.templateBuildRenderer(ssrTemplate, {
55-
content: pageRendered,
44+
content: ssrString,
5645
head: ssrContext.head.map(renderHead).join(''),
5746
lang: ssrContext.lang,
5847
prefetch: renderPagePrefetchLinks({

‎packages/bundler-webpack/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"dependencies": {
3939
"@types/express": "^4.17.21",
4040
"@types/webpack-env": "^1.18.5",
41+
"@vuepress/bundlerutils": "workspace:*",
4142
"@vuepress/client": "workspace:*",
4243
"@vuepress/core": "workspace:*",
4344
"@vuepress/shared": "workspace:*",

‎packages/bundler-webpack/src/build/build.ts

+7-23
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
import type { CreateVueAppFunction } from '@vuepress/client'
1+
import { createVueServerApp, getSsrTemplate } from '@vuepress/bundlerutils'
22
import type { App, Bundler } from '@vuepress/core'
3-
import {
4-
colors,
5-
debug,
6-
fs,
7-
importFileDefault,
8-
logger,
9-
withSpinner,
10-
} from '@vuepress/utils'
3+
import { colors, debug, fs, logger, withSpinner } from '@vuepress/utils'
114
import webpack from 'webpack'
125
import { resolveWebpackConfig } from '../resolveWebpackConfig.js'
136
import type { WebpackBundlerOptions } from '../types.js'
@@ -80,19 +73,11 @@ export const build = async (
8073
const { initialFilesMeta, asyncFilesMeta, moduleFilesMetaMap } =
8174
resolveClientManifestMeta(clientManifest)
8275

83-
// load the compiled server bundle
84-
const serverEntryPath = app.dir.temp('.server/app.cjs')
85-
const { createVueApp } = await importFileDefault<{
86-
createVueApp: CreateVueAppFunction
87-
}>(serverEntryPath)
88-
// create vue ssr app
89-
const { app: vueApp, router: vueRouter } = await createVueApp()
90-
const { renderToString } = await import('vue/server-renderer')
91-
92-
// load ssr template file
93-
const ssrTemplate = await fs.readFile(app.options.templateBuild, {
94-
encoding: 'utf8',
95-
})
76+
// create vue ssr app and get ssr template
77+
const { vueApp, vueRouter } = await createVueServerApp(
78+
app.dir.temp('.server/app.cjs'),
79+
)
80+
const ssrTemplate = await getSsrTemplate(app)
9681

9782
// pre-render pages to html files
9883
for (const page of app.pages) {
@@ -104,7 +89,6 @@ export const build = async (
10489
page,
10590
vueApp,
10691
vueRouter,
107-
renderToString,
10892
ssrTemplate,
10993
initialFilesMeta,
11094
asyncFilesMeta,

‎packages/bundler-webpack/src/build/renderPage.ts

+11-20
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
import type { PageSSRContext } from '@vuepress/bundlerutils'
2+
import { renderPageToString } from '@vuepress/bundlerutils'
13
import type { App, Page } from '@vuepress/core'
2-
import type { VuepressSSRContext } from '@vuepress/shared'
34
import { fs, renderHead } from '@vuepress/utils'
45
import type { App as VueApp } from 'vue'
5-
import { ssrContextKey } from 'vue'
6-
import type { SSRContext } from 'vue/server-renderer'
76
import type { Router } from 'vue-router'
87
import { renderPagePrefetchLinks } from './renderPagePrefetchLinks.js'
98
import { renderPagePreloadLinks } from './renderPagePreloadLinks.js'
@@ -12,7 +11,7 @@ import { renderPageStyles } from './renderPageStyles.js'
1211
import { resolvePageClientFilesMeta } from './resolvePageClientFilesMeta.js'
1312
import type { FileMeta, ModuleFilesMetaMap } from './types.js'
1413

15-
interface PageRenderContext extends SSRContext, VuepressSSRContext {
14+
interface WebpackPageSSRContext extends PageSSRContext {
1615
/**
1716
* Injected by vuepress-ssr-loader
1817
*
@@ -29,7 +28,6 @@ export const renderPage = async ({
2928
page,
3029
vueApp,
3130
vueRouter,
32-
renderToString,
3331
ssrTemplate,
3432
initialFilesMeta,
3533
asyncFilesMeta,
@@ -39,26 +37,19 @@ export const renderPage = async ({
3937
page: Page
4038
vueApp: VueApp
4139
vueRouter: Router
42-
renderToString: (input: VueApp, context: SSRContext) => Promise<string>
4340
ssrTemplate: string
4441
initialFilesMeta: FileMeta[]
4542
asyncFilesMeta: FileMeta[]
4643
moduleFilesMetaMap: ModuleFilesMetaMap
4744
}): Promise<void> => {
48-
// switch to current page route
49-
await vueRouter.push(page.path)
50-
await vueRouter.isReady()
51-
52-
// create vue ssr context with default values
53-
delete vueApp._context.provides[ssrContextKey]
54-
const ssrContext: PageRenderContext = {
55-
_registeredComponents: new Set(),
56-
lang: 'en',
57-
head: [],
58-
}
59-
6045
// render current page to string
61-
const pageRendered = await renderToString(vueApp, ssrContext)
46+
const { ssrContext, ssrString } =
47+
await renderPageToString<WebpackPageSSRContext>({
48+
page,
49+
vueApp,
50+
vueRouter,
51+
ssrContextInit: { _registeredComponents: new Set() },
52+
})
6253

6354
// resolve client files that used by this page
6455
const pageClientFilesMeta = resolvePageClientFilesMeta({
@@ -68,7 +59,7 @@ export const renderPage = async ({
6859

6960
// generate html string
7061
const html = await app.options.templateBuildRenderer(ssrTemplate, {
71-
content: pageRendered,
62+
content: ssrString,
7263
head: ssrContext.head.map(renderHead).join(''),
7364
lang: ssrContext.lang,
7465
prefetch: renderPagePrefetchLinks({

‎packages/bundlerutils/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# @vuepress/bundlerutils
2+
3+
[![npm](https://badgen.net/npm/v/@vuepress/bundlerutils/next)](https://www.npmjs.com/package/@vuepress/bundlerutils)
4+
[![license](https://badgen.net/github/license/vuepress/core)](https://github.com/vuepress/core/blob/main/LICENSE)
5+
6+
## Documentation
7+
8+
https://vuepress.vuejs.org
9+
10+
## License
11+
12+
[MIT](https://github.com/vuepress/core/blob/main/LICENSE)

‎packages/bundlerutils/package.json

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"name": "@vuepress/bundlerutils",
3+
"version": "2.0.0-rc.15",
4+
"description": "Utils package of VuePress bundler",
5+
"keywords": [
6+
"bundler",
7+
"vuepress",
8+
"utils"
9+
],
10+
"homepage": "https://github.com/vuepress",
11+
"bugs": {
12+
"url": "https://github.com/vuepress/core/issues"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "git+https://github.com/vuepress/core.git"
17+
},
18+
"license": "MIT",
19+
"author": "meteorlxy",
20+
"type": "module",
21+
"exports": {
22+
".": "./dist/index.js",
23+
"./package.json": "./package.json"
24+
},
25+
"main": "./dist/index.js",
26+
"types": "./dist/index.d.ts",
27+
"files": [
28+
"dist"
29+
],
30+
"scripts": {
31+
"build": "tsup",
32+
"clean": "rimraf dist"
33+
},
34+
"dependencies": {
35+
"@vuepress/client": "workspace:*",
36+
"@vuepress/core": "workspace:*",
37+
"@vuepress/shared": "workspace:*",
38+
"@vuepress/utils": "workspace:*",
39+
"vue": "^3.5.3",
40+
"vue-router": "^4.4.3"
41+
},
42+
"publishConfig": {
43+
"access": "public"
44+
},
45+
"tsup": {
46+
"clean": true,
47+
"dts": "./src/index.ts",
48+
"entry": [
49+
"./src/index.ts"
50+
],
51+
"format": [
52+
"esm"
53+
],
54+
"outDir": "./dist",
55+
"sourcemap": false,
56+
"target": "es2022",
57+
"tsconfig": "../../tsconfig.dts.json"
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { CreateVueAppFunction } from '@vuepress/client'
2+
import { importFile, importFileDefault } from '@vuepress/utils'
3+
import type { App } from 'vue'
4+
import type { Router } from 'vue-router'
5+
6+
/**
7+
* Create vue app and router for server side rendering
8+
*/
9+
export const createVueServerApp = async (
10+
serverAppPath: string,
11+
): Promise<{
12+
vueApp: App
13+
vueRouter: Router
14+
}> => {
15+
// use different import function for cjs and esm
16+
const importer = serverAppPath.endsWith('.cjs')
17+
? importFileDefault
18+
: importFile
19+
20+
// import the server app entry file
21+
const { createVueApp } = await importer<{
22+
createVueApp: CreateVueAppFunction
23+
}>(serverAppPath)
24+
25+
// create vue app
26+
const { app, router } = await createVueApp()
27+
28+
return { vueApp: app, vueRouter: router }
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { App } from '@vuepress/core'
2+
import { fs } from '@vuepress/utils'
3+
4+
/**
5+
* Util to read the ssr template file
6+
*/
7+
export const getSsrTemplate = async (app: App): Promise<string> =>
8+
fs.readFile(app.options.templateBuild, { encoding: 'utf8' })
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './createVueServerApp'
2+
export * from './getSsrTemplate'
3+
export * from './renderPageToString'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Page } from '@vuepress/core'
2+
import type { VuepressSSRContext } from '@vuepress/shared'
3+
import type { App as VueApp } from 'vue'
4+
import { ssrContextKey } from 'vue'
5+
import type { SSRContext } from 'vue/server-renderer'
6+
import type { Router } from 'vue-router'
7+
8+
export type PageSSRContext = SSRContext & VuepressSSRContext
9+
10+
/**
11+
* Render a vuepress page to string
12+
*/
13+
export const renderPageToString = async <
14+
T extends PageSSRContext = PageSSRContext,
15+
>({
16+
page,
17+
vueApp,
18+
vueRouter,
19+
ssrContextInit,
20+
}: {
21+
page: Page
22+
vueApp: VueApp
23+
vueRouter: Router
24+
ssrContextInit?: Partial<T>
25+
}): Promise<{
26+
ssrContext: T
27+
ssrString: string
28+
}> => {
29+
// switch to current page route
30+
await vueRouter.push(page.path)
31+
await vueRouter.isReady()
32+
33+
// create vue ssr context with default values
34+
delete vueApp._context.provides[ssrContextKey]
35+
const ssrContext = {
36+
lang: 'en',
37+
head: [],
38+
...ssrContextInit,
39+
} satisfies PageSSRContext as T
40+
41+
// lazy load renderToString function
42+
const { renderToString } = await import('vue/server-renderer')
43+
44+
// render current page to string
45+
const ssrString = await renderToString(vueApp, ssrContext)
46+
47+
return {
48+
ssrContext,
49+
ssrString,
50+
}
51+
}

‎packages/bundlerutils/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './build/index.js'

‎pnpm-lock.yaml

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎vitest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default defineConfig({
1515
coverage: {
1616
all: true,
1717
exclude: [
18-
'packages/bundler-*/**',
18+
'packages/bundler*/**',
1919
'packages/client/**',
2020
'packages/vuepress/**',
2121
],

0 commit comments

Comments
 (0)
Please sign in to comment.