Skip to content

Commit

Permalink
feat(bundlerutils): add bundlerutils package
Browse files Browse the repository at this point in the history
  • Loading branch information
meteorlxy committed Sep 12, 2024
1 parent 5709c57 commit 5488138
Show file tree
Hide file tree
Showing 18 changed files with 230 additions and 80 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
],
"cSpell.words": [
"bumpp",
"bundlerutils",
"composables",
"devtool",
"docsearch",
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Wrapper of core packages:

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

Bundler packages:
Bundler and related packages:

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

## Development Setup

Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Core Packages 的封装:

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

Bundler Packages :
Bundler 及其相关 Packages :

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

## 开发配置

Expand Down
1 change: 1 addition & 0 deletions packages/bundler-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
},
"dependencies": {
"@vitejs/plugin-vue": "^5.1.3",
"@vuepress/bundlerutils": "workspace:*",
"@vuepress/client": "workspace:*",
"@vuepress/core": "workspace:*",
"@vuepress/shared": "workspace:*",
Expand Down
23 changes: 7 additions & 16 deletions packages/bundler-vite/src/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CreateVueAppFunction } from '@vuepress/client'
import { createVueServerApp, getSsrTemplate } from '@vuepress/bundlerutils'
import type { App, Bundler } from '@vuepress/core'
import { colors, debug, fs, importFile, withSpinner } from '@vuepress/utils'
import { colors, debug, fs, withSpinner } from '@vuepress/utils'
import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'
import { build as viteBuild } from 'vite'
import { resolveViteConfig } from '../resolveViteConfig.js'
Expand Down Expand Up @@ -58,19 +58,11 @@ export const build = async (
(item) => item.type === 'chunk' && item.isEntry,
) as OutputChunk

// load the compiled server bundle
const serverEntryPath = app.dir.temp('.server', serverEntryChunk.fileName)
const { createVueApp } = await importFile<{
createVueApp: CreateVueAppFunction
}>(serverEntryPath)
// create vue ssr app
const { app: vueApp, router: vueRouter } = await createVueApp()
const { renderToString } = await import('vue/server-renderer')

// load ssr template file
const ssrTemplate = await fs.readFile(app.options.templateBuild, {
encoding: 'utf8',
})
// create vue ssr app and get ssr template
const { vueApp, vueRouter } = await createVueServerApp(
app.dir.temp('.server', serverEntryChunk.fileName),
)
const ssrTemplate = await getSsrTemplate(app)

// pre-render pages to html files
for (const page of app.pages) {
Expand All @@ -80,7 +72,6 @@ export const build = async (
page,
vueApp,
vueRouter,
renderToString,
ssrTemplate,
output: clientOutput.output,
outputEntryChunk: clientEntryChunk,
Expand Down
25 changes: 7 additions & 18 deletions packages/bundler-vite/src/build/renderPage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { renderPageToString } from '@vuepress/bundlerutils'
import type { App, Page } from '@vuepress/core'
import type { VuepressSSRContext } from '@vuepress/shared'
import { fs, renderHead } from '@vuepress/utils'
import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'
import type { App as VueApp } from 'vue'
import { ssrContextKey } from 'vue'
import type { SSRContext } from 'vue/server-renderer'
import type { Router } from 'vue-router'
import { renderPagePrefetchLinks } from './renderPagePrefetchLinks.js'
import { renderPagePreloadLinks } from './renderPagePreloadLinks.js'
Expand All @@ -17,7 +15,6 @@ export const renderPage = async ({
page,
vueApp,
vueRouter,
renderToString,
ssrTemplate,
output,
outputEntryChunk,
Expand All @@ -27,32 +24,24 @@ export const renderPage = async ({
page: Page
vueApp: VueApp
vueRouter: Router
renderToString: (input: VueApp, context: SSRContext) => Promise<string>
ssrTemplate: string
output: RollupOutput['output']
outputEntryChunk: OutputChunk
outputCssAsset: OutputAsset | undefined
}): Promise<void> => {
// switch to current page route
await vueRouter.push(page.path)
await vueRouter.isReady()

// create vue ssr context with default values
delete vueApp._context.provides[ssrContextKey]
const ssrContext: VuepressSSRContext = {
lang: 'en',
head: [],
}

// render current page to string
const pageRendered = await renderToString(vueApp, ssrContext)
const { ssrContext, ssrString } = await renderPageToString({
page,
vueApp,
vueRouter,
})

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

// generate html string
const html = await app.options.templateBuildRenderer(ssrTemplate, {
content: pageRendered,
content: ssrString,
head: ssrContext.head.map(renderHead).join(''),
lang: ssrContext.lang,
prefetch: renderPagePrefetchLinks({
Expand Down
1 change: 1 addition & 0 deletions packages/bundler-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dependencies": {
"@types/express": "^4.17.21",
"@types/webpack-env": "^1.18.5",
"@vuepress/bundlerutils": "workspace:*",
"@vuepress/client": "workspace:*",
"@vuepress/core": "workspace:*",
"@vuepress/shared": "workspace:*",
Expand Down
30 changes: 7 additions & 23 deletions packages/bundler-webpack/src/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import type { CreateVueAppFunction } from '@vuepress/client'
import { createVueServerApp, getSsrTemplate } from '@vuepress/bundlerutils'
import type { App, Bundler } from '@vuepress/core'
import {
colors,
debug,
fs,
importFileDefault,
logger,
withSpinner,
} from '@vuepress/utils'
import { colors, debug, fs, logger, withSpinner } from '@vuepress/utils'
import webpack from 'webpack'
import { resolveWebpackConfig } from '../resolveWebpackConfig.js'
import type { WebpackBundlerOptions } from '../types.js'
Expand Down Expand Up @@ -80,19 +73,11 @@ export const build = async (
const { initialFilesMeta, asyncFilesMeta, moduleFilesMetaMap } =
resolveClientManifestMeta(clientManifest)

// load the compiled server bundle
const serverEntryPath = app.dir.temp('.server/app.cjs')
const { createVueApp } = await importFileDefault<{
createVueApp: CreateVueAppFunction
}>(serverEntryPath)
// create vue ssr app
const { app: vueApp, router: vueRouter } = await createVueApp()
const { renderToString } = await import('vue/server-renderer')

// load ssr template file
const ssrTemplate = await fs.readFile(app.options.templateBuild, {
encoding: 'utf8',
})
// 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) {
Expand All @@ -104,7 +89,6 @@ export const build = async (
page,
vueApp,
vueRouter,
renderToString,
ssrTemplate,
initialFilesMeta,
asyncFilesMeta,
Expand Down
31 changes: 11 additions & 20 deletions packages/bundler-webpack/src/build/renderPage.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { PageSSRContext } from '@vuepress/bundlerutils'
import { renderPageToString } from '@vuepress/bundlerutils'
import type { App, Page } from '@vuepress/core'
import type { VuepressSSRContext } from '@vuepress/shared'
import { fs, renderHead } from '@vuepress/utils'
import type { App as VueApp } from 'vue'
import { ssrContextKey } from 'vue'
import type { SSRContext } from 'vue/server-renderer'
import type { Router } from 'vue-router'
import { renderPagePrefetchLinks } from './renderPagePrefetchLinks.js'
import { renderPagePreloadLinks } from './renderPagePreloadLinks.js'
Expand All @@ -12,7 +11,7 @@ import { renderPageStyles } from './renderPageStyles.js'
import { resolvePageClientFilesMeta } from './resolvePageClientFilesMeta.js'
import type { FileMeta, ModuleFilesMetaMap } from './types.js'

interface PageRenderContext extends SSRContext, VuepressSSRContext {
interface WebpackPageSSRContext extends PageSSRContext {
/**
* Injected by vuepress-ssr-loader
*
Expand All @@ -29,7 +28,6 @@ export const renderPage = async ({
page,
vueApp,
vueRouter,
renderToString,
ssrTemplate,
initialFilesMeta,
asyncFilesMeta,
Expand All @@ -39,26 +37,19 @@ export const renderPage = async ({
page: Page
vueApp: VueApp
vueRouter: Router
renderToString: (input: VueApp, context: SSRContext) => Promise<string>
ssrTemplate: string
initialFilesMeta: FileMeta[]
asyncFilesMeta: FileMeta[]
moduleFilesMetaMap: ModuleFilesMetaMap
}): Promise<void> => {
// switch to current page route
await vueRouter.push(page.path)
await vueRouter.isReady()

// create vue ssr context with default values
delete vueApp._context.provides[ssrContextKey]
const ssrContext: PageRenderContext = {
_registeredComponents: new Set(),
lang: 'en',
head: [],
}

// render current page to string
const pageRendered = await renderToString(vueApp, ssrContext)
const { ssrContext, ssrString } =
await renderPageToString<WebpackPageSSRContext>({
page,
vueApp,
vueRouter,
ssrContextInit: { _registeredComponents: new Set() },
})

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

// generate html string
const html = await app.options.templateBuildRenderer(ssrTemplate, {
content: pageRendered,
content: ssrString,
head: ssrContext.head.map(renderHead).join(''),
lang: ssrContext.lang,
prefetch: renderPagePrefetchLinks({
Expand Down
12 changes: 12 additions & 0 deletions packages/bundlerutils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# @vuepress/bundlerutils

[![npm](https://badgen.net/npm/v/@vuepress/bundlerutils/next)](https://www.npmjs.com/package/@vuepress/bundlerutils)
[![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)
59 changes: 59 additions & 0 deletions packages/bundlerutils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@vuepress/bundlerutils",
"version": "2.0.0-rc.15",
"description": "Utils package of VuePress bundler",
"keywords": [
"bundler",
"vuepress",
"utils"
],
"homepage": "https://github.com/vuepress",
"bugs": {
"url": "https://github.com/vuepress/core/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuepress/core.git"
},
"license": "MIT",
"author": "meteorlxy",
"type": "module",
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"clean": "rimraf dist"
},
"dependencies": {
"@vuepress/client": "workspace:*",
"@vuepress/core": "workspace:*",
"@vuepress/shared": "workspace:*",
"@vuepress/utils": "workspace:*",
"vue": "^3.5.3",
"vue-router": "^4.4.3"
},
"publishConfig": {
"access": "public"
},
"tsup": {
"clean": true,
"dts": "./src/index.ts",
"entry": [
"./src/index.ts"
],
"format": [
"esm"
],
"outDir": "./dist",
"sourcemap": false,
"target": "es2022",
"tsconfig": "../../tsconfig.dts.json"
}
}
29 changes: 29 additions & 0 deletions packages/bundlerutils/src/build/createVueServerApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { CreateVueAppFunction } from '@vuepress/client'
import { importFile, importFileDefault } from '@vuepress/utils'
import type { App } from 'vue'
import type { Router } from 'vue-router'

/**
* Create vue app and router for server side rendering
*/
export const createVueServerApp = async (
serverAppPath: string,
): Promise<{
vueApp: App
vueRouter: Router
}> => {
// use different import function for cjs and esm
const importer = serverAppPath.endsWith('.cjs')
? importFileDefault
: importFile

// import the server app entry file
const { createVueApp } = await importer<{
createVueApp: CreateVueAppFunction
}>(serverAppPath)

// create vue app
const { app, router } = await createVueApp()

return { vueApp: app, vueRouter: router }
}
8 changes: 8 additions & 0 deletions packages/bundlerutils/src/build/getSsrTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { App } from '@vuepress/core'
import { fs } from '@vuepress/utils'

/**
* Util to read the ssr template file
*/
export const getSsrTemplate = async (app: App): Promise<string> =>
fs.readFile(app.options.templateBuild, { encoding: 'utf8' })
3 changes: 3 additions & 0 deletions packages/bundlerutils/src/build/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './createVueServerApp'
export * from './getSsrTemplate'
export * from './renderPageToString'
Loading

0 comments on commit 5488138

Please sign in to comment.