Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove temp page files and load page component via bundler (close #1584) #1606

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions e2e/docs/.vuepress/components/ComponentForMarkdownImport.vue

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="component-for-markdown-import-bar">
<p>component for markdown import bar</p>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="component-for-markdown-import-foo">
<p>component for markdown import foo</p>
</div>
</template>
5 changes: 5 additions & 0 deletions e2e/docs/.vuepress/markdowns/dangling-markdown-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="dangling-markdown-file">

dangling markdown file

</div>
2 changes: 2 additions & 0 deletions e2e/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
foo

## Home H2

demo
11 changes: 8 additions & 3 deletions e2e/docs/markdown/vue-components.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<ComponentForMarkdownGlobal />

<ComponentForMarkdownImport />
<ComponentForMarkdownImportFoo />

<ComponentForMarkdownImportBar />

<script setup>
// TODO: relative path import?
import ComponentForMarkdownImport from '@source/.vuepress/components/ComponentForMarkdownImport.vue';
// import via alias
import ComponentForMarkdownImportFoo from '@source/.vuepress/components/ComponentForMarkdownImportFoo.vue';

// import via relative path
import ComponentForMarkdownImportBar from '../.vuepress/components/ComponentForMarkdownImportBar.vue';
</script>
7 changes: 5 additions & 2 deletions e2e/tests/markdown/vue-components.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ test('should render vue components correctly', async ({ page }) => {
await expect(page.locator('.component-for-markdown-global p')).toHaveText(
'component for markdown global',
)
await expect(page.locator('.component-for-markdown-import p')).toHaveText(
'component for markdown import',
await expect(page.locator('.component-for-markdown-import-foo p')).toHaveText(
'component for markdown import foo',
)
await expect(page.locator('.component-for-markdown-import-bar p')).toHaveText(
'component for markdown import bar',
)
})
1 change: 1 addition & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default vuepress(
'__dirname',
'_context',
'_pageChunk',
'_pageData',
'_registeredComponents',
],
},
Expand Down
1 change: 1 addition & 0 deletions packages/bundler-vite/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './vuepressBuildPlugin.js'
export * from './vuepressConfigPlugin.js'
export * from './vuepressDevPlugin.js'
export * from './vuepressMarkdownPlugin.js'
export * from './vuepressUserConfigPlugin.js'
export * from './vuepressVuePlugin.js'
46 changes: 46 additions & 0 deletions packages/bundler-vite/src/plugins/vuepressMarkdownPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { App } from '@vuepress/core'
import { createPage, renderPageToVue } from '@vuepress/core'
import type { Plugin } from 'vite'

/**
* Handle markdown transformation
*/
export const vuepressMarkdownPlugin = ({ app }: { app: App }): Plugin => ({
name: 'vuepress:markdown',

enforce: 'pre',

async transform(code, id) {
if (!id.endsWith('.md')) return

// get the matched page by file path (id)
const page = app.pagesMap[id]

// if the page content is not changed, render it to vue component directly
if (page?.content === code) {
return renderPageToVue(app, page)
}

// create a new page with the new content
const newPage = await createPage(app, {
content: code,
filePath: id,
})
return renderPageToVue(app, newPage)
},

async handleHotUpdate(ctx) {
if (!ctx.file.endsWith('.md')) return

// read the source code
const code = await ctx.read()

// create a new page with the new content
const newPage = await createPage(app, {
content: code,
filePath: ctx.file,
})

ctx.read = () => renderPageToVue(app, newPage)
},
})
1 change: 1 addition & 0 deletions packages/bundler-vite/src/plugins/vuepressVuePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const vuepressVuePlugin = ({
options: ViteBundlerOptions
}): Plugin =>
vuePlugin({
include: [/\.vue$/, /\.md$/],
...options.vuePluginOptions,
})
2 changes: 2 additions & 0 deletions packages/bundler-vite/src/resolveViteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
vuepressBuildPlugin,
vuepressConfigPlugin,
vuepressDevPlugin,
vuepressMarkdownPlugin,
vuepressUserConfigPlugin,
vuepressVuePlugin,
} from './plugins/index.js'
Expand All @@ -31,6 +32,7 @@ export const resolveViteConfig = ({
},
plugins: [
vuepressConfigPlugin({ app, isBuild, isServer }),
vuepressMarkdownPlugin({ app }),
vuepressDevPlugin({ app }),
vuepressBuildPlugin({ isServer }),
vuepressVuePlugin({ options }),
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 @@ -20,6 +20,7 @@
"author": "meteorlxy",
"type": "module",
"imports": {
"#vuepress-markdown-loader": "./dist/vuepress-markdown-loader.cjs",
"#vuepress-ssr-loader": "./dist/vuepress-ssr-loader.cjs"
},
"exports": {
Expand Down
12 changes: 0 additions & 12 deletions packages/bundler-webpack/src/build/createClientConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createRequire } from 'node:module'
import type { App } from '@vuepress/core'
import { fs } from '@vuepress/utils'
import CopyWebpackPlugin from 'copy-webpack-plugin'
Expand All @@ -10,8 +9,6 @@ import { createClientBaseConfig } from '../config/index.js'
import type { WebpackBundlerOptions } from '../types.js'
import { createClientPlugin } from './createClientPlugin.js'

const require = createRequire(import.meta.url)

/**
* Filename of the client manifest file that generated by client plugin
*/
Expand All @@ -27,15 +24,6 @@ export const createClientConfig = async (
isBuild: true,
})

// use internal vuepress-ssr-loader to handle SSR dependencies
config.module
.rule('vue')
.test(/\.vue$/)
.use('vuepress-ssr-loader')
.before('vue-loader')
.loader(require.resolve('#vuepress-ssr-loader'))
.end()

// vuepress client plugin, handle client assets info for ssr
config
.plugin('vuepress-client')
Expand Down
15 changes: 0 additions & 15 deletions packages/bundler-webpack/src/build/createServerConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { createRequire } from 'node:module'
import type { App } from '@vuepress/core'
import type Config from 'webpack-5-chain'
import { createBaseConfig } from '../config/index.js'
import type { WebpackBundlerOptions } from '../types.js'

const require = createRequire(import.meta.url)

export const createServerConfig = async (
app: App,
options: WebpackBundlerOptions,
Expand Down Expand Up @@ -43,17 +40,5 @@ export const createServerConfig = async (
// do not need to minimize server bundle
config.optimization.minimize(false)

// use internal vuepress-ssr-loader to handle SSR dependencies
config.module
.rule('vue')
.test(/\.vue$/)
.use('vuepress-ssr-loader')
.before('vue-loader')
.loader(require.resolve('#vuepress-ssr-loader'))
.options({
app,
})
.end()

return config
}
2 changes: 1 addition & 1 deletion packages/bundler-webpack/src/config/createBaseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const createBaseConfig = async ({
/**
* module
*/
handleModule({ options, config, isBuild, isServer })
handleModule({ app, options, config, isBuild, isServer })

/**
* plugins
Expand Down
5 changes: 4 additions & 1 deletion packages/bundler-webpack/src/config/handleModule.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { App } from '@vuepress/core'
import type Config from 'webpack-5-chain'
import type { WebpackBundlerOptions } from '../types.js'
import { handleModuleAssets } from './handleModuleAssets.js'
Expand All @@ -11,11 +12,13 @@ import { handleModuleVue } from './handleModuleVue.js'
* Set webpack module
*/
export const handleModule = ({
app,
options,
config,
isBuild,
isServer,
}: {
app: App
options: WebpackBundlerOptions
config: Config
isBuild: boolean
Expand All @@ -27,7 +30,7 @@ export const handleModule = ({
)

// vue files
handleModuleVue({ options, config, isServer })
handleModuleVue({ app, options, config, isBuild, isServer })

// pug files, for templates
handleModulePug({ config })
Expand Down
57 changes: 45 additions & 12 deletions packages/bundler-webpack/src/config/handleModuleVue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createRequire } from 'node:module'
import type { App } from '@vuepress/core'
import type { VueLoaderOptions } from 'vue-loader'
import { VueLoaderPlugin } from 'vue-loader'
import type Config from 'webpack-5-chain'
import type { VuepressMarkdownLoaderOptions } from '../loaders/vuepressMarkdownLoader'
import type { WebpackBundlerOptions } from '../types.js'

const require = createRequire(import.meta.url)
Expand All @@ -10,26 +12,57 @@ const require = createRequire(import.meta.url)
* Set webpack module to handle vue files
*/
export const handleModuleVue = ({
app,
options,
config,
isBuild,
isServer,
}: {
app: App
options: WebpackBundlerOptions
config: Config
isBuild: boolean
isServer: boolean
}): void => {
// .vue files
config.module
.rule('vue')
.test(/\.vue$/)
// use vue-loader
.use('vue-loader')
.loader(require.resolve('vue-loader'))
.options({
...options.vue,
isServerBuild: isServer,
} as VueLoaderOptions)
.end()
const handleVue = ({
lang,
test,
}: {
lang: 'md' | 'vue'
test: RegExp
}): void => {
const rule = config.module.rule(lang).test(test)

// use internal vuepress-ssr-loader to handle SSR dependencies
if (isBuild) {
rule
.use('vuepress-ssr-loader')
.loader(require.resolve('#vuepress-ssr-loader'))
.end()
}

// use official vue-loader
rule
.use('vue-loader')
.loader(require.resolve('vue-loader'))
.options({
...options.vue,
isServerBuild: isServer,
} satisfies VueLoaderOptions)
.end()

// use internal vuepress-markdown-loader to handle markdown files
if (lang === 'md') {
rule
.use('vuepress-markdown-loader')
.loader(require.resolve('#vuepress-markdown-loader'))
.options({ app } satisfies VuepressMarkdownLoaderOptions)
.end()
}
}

handleVue({ lang: 'md', test: /\.md$/ })
handleVue({ lang: 'vue', test: /\.vue$/ })

// use vue-loader plugin
config.plugin('vue-loader').use(VueLoaderPlugin)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const loader = require('./vuepressMarkdownLoader.js')

module.exports = loader.vuepressMarkdownLoader
33 changes: 33 additions & 0 deletions packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { App } from '@vuepress/core'
import type { LoaderDefinitionFunction } from 'webpack'

export interface VuepressMarkdownLoaderOptions {
app: App
}

/**
* A webpack loader to transform markdown content to vue component
*/
export const vuepressMarkdownLoader: LoaderDefinitionFunction<VuepressMarkdownLoaderOptions> =
async function vuepressMarkdownLoader(source) {
// import esm dependencies
const { createPage, renderPageToVue } = await import('@vuepress/core')

// get app instance from loader options
const { app } = this.getOptions()

// get the matched page by file path
const page = app.pagesMap[this.resourcePath]

// if the page content is not changed, render it to vue component directly
if (page?.content === source) {
return renderPageToVue(app, page)
}

// create a new page with the new content
const newPage = await createPage(app, {
content: source,
filePath: this.resourcePath,
})
return renderPageToVue(app, newPage)
}
1 change: 1 addition & 0 deletions packages/bundler-webpack/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default defineConfig([
{
...shared,
entry: {
'vuepress-markdown-loader': './src/loaders/vuepressMarkdownLoader.cts',
'vuepress-ssr-loader': './src/loaders/vuepressSsrLoader.cts',
},
format: ['cjs'],
Expand Down
Loading
Loading