diff --git a/e2e/docs/.vuepress/components/ComponentForMarkdownImport.vue b/e2e/docs/.vuepress/components/ComponentForMarkdownImport.vue deleted file mode 100644 index 7108c4a426..0000000000 --- a/e2e/docs/.vuepress/components/ComponentForMarkdownImport.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/e2e/docs/.vuepress/components/ComponentForMarkdownImportBar.vue b/e2e/docs/.vuepress/components/ComponentForMarkdownImportBar.vue new file mode 100644 index 0000000000..0d9fa2f2f6 --- /dev/null +++ b/e2e/docs/.vuepress/components/ComponentForMarkdownImportBar.vue @@ -0,0 +1,5 @@ + diff --git a/e2e/docs/.vuepress/components/ComponentForMarkdownImportFoo.vue b/e2e/docs/.vuepress/components/ComponentForMarkdownImportFoo.vue new file mode 100644 index 0000000000..3905e34a83 --- /dev/null +++ b/e2e/docs/.vuepress/components/ComponentForMarkdownImportFoo.vue @@ -0,0 +1,5 @@ + diff --git a/e2e/docs/.vuepress/markdowns/dangling-markdown-file.md b/e2e/docs/.vuepress/markdowns/dangling-markdown-file.md new file mode 100644 index 0000000000..5bf006ada9 --- /dev/null +++ b/e2e/docs/.vuepress/markdowns/dangling-markdown-file.md @@ -0,0 +1,5 @@ +
+ +dangling markdown file + +
diff --git a/e2e/docs/README.md b/e2e/docs/README.md index eb63f0ccbf..b3586d97fe 100644 --- a/e2e/docs/README.md +++ b/e2e/docs/README.md @@ -1,3 +1,5 @@ foo ## Home H2 + +demo diff --git a/e2e/docs/markdown/vue-components.md b/e2e/docs/markdown/vue-components.md index c038c08795..f38b703d0b 100644 --- a/e2e/docs/markdown/vue-components.md +++ b/e2e/docs/markdown/vue-components.md @@ -1,8 +1,13 @@ - + + + diff --git a/e2e/tests/markdown/vue-components.spec.ts b/e2e/tests/markdown/vue-components.spec.ts index f33b1dbc68..1f3f1818d6 100644 --- a/e2e/tests/markdown/vue-components.spec.ts +++ b/e2e/tests/markdown/vue-components.spec.ts @@ -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', ) }) diff --git a/eslint.config.ts b/eslint.config.ts index fa0eed31d7..77ca52bc6c 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -20,6 +20,7 @@ export default vuepress( '__dirname', '_context', '_pageChunk', + '_pageData', '_registeredComponents', ], }, diff --git a/packages/bundler-vite/src/plugins/index.ts b/packages/bundler-vite/src/plugins/index.ts index 0268dc0dba..9ff20c8972 100644 --- a/packages/bundler-vite/src/plugins/index.ts +++ b/packages/bundler-vite/src/plugins/index.ts @@ -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' diff --git a/packages/bundler-vite/src/plugins/vuepressMarkdownPlugin.ts b/packages/bundler-vite/src/plugins/vuepressMarkdownPlugin.ts new file mode 100644 index 0000000000..9dc2ec29c9 --- /dev/null +++ b/packages/bundler-vite/src/plugins/vuepressMarkdownPlugin.ts @@ -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) + }, +}) diff --git a/packages/bundler-vite/src/plugins/vuepressVuePlugin.ts b/packages/bundler-vite/src/plugins/vuepressVuePlugin.ts index d85656c731..5688e4c2fb 100644 --- a/packages/bundler-vite/src/plugins/vuepressVuePlugin.ts +++ b/packages/bundler-vite/src/plugins/vuepressVuePlugin.ts @@ -11,5 +11,6 @@ export const vuepressVuePlugin = ({ options: ViteBundlerOptions }): Plugin => vuePlugin({ + include: [/\.vue$/, /\.md$/], ...options.vuePluginOptions, }) diff --git a/packages/bundler-vite/src/resolveViteConfig.ts b/packages/bundler-vite/src/resolveViteConfig.ts index 6fafcec449..f950836f92 100644 --- a/packages/bundler-vite/src/resolveViteConfig.ts +++ b/packages/bundler-vite/src/resolveViteConfig.ts @@ -5,6 +5,7 @@ import { vuepressBuildPlugin, vuepressConfigPlugin, vuepressDevPlugin, + vuepressMarkdownPlugin, vuepressUserConfigPlugin, vuepressVuePlugin, } from './plugins/index.js' @@ -31,6 +32,7 @@ export const resolveViteConfig = ({ }, plugins: [ vuepressConfigPlugin({ app, isBuild, isServer }), + vuepressMarkdownPlugin({ app }), vuepressDevPlugin({ app }), vuepressBuildPlugin({ isServer }), vuepressVuePlugin({ options }), diff --git a/packages/bundler-webpack/package.json b/packages/bundler-webpack/package.json index c7caed7369..28ee050c6e 100644 --- a/packages/bundler-webpack/package.json +++ b/packages/bundler-webpack/package.json @@ -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": { diff --git a/packages/bundler-webpack/src/build/createClientConfig.ts b/packages/bundler-webpack/src/build/createClientConfig.ts index 6266b9bda1..7f0ae415de 100644 --- a/packages/bundler-webpack/src/build/createClientConfig.ts +++ b/packages/bundler-webpack/src/build/createClientConfig.ts @@ -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' @@ -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 */ @@ -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') diff --git a/packages/bundler-webpack/src/build/createServerConfig.ts b/packages/bundler-webpack/src/build/createServerConfig.ts index d21b75b034..600141751d 100644 --- a/packages/bundler-webpack/src/build/createServerConfig.ts +++ b/packages/bundler-webpack/src/build/createServerConfig.ts @@ -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, @@ -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 } diff --git a/packages/bundler-webpack/src/config/createBaseConfig.ts b/packages/bundler-webpack/src/config/createBaseConfig.ts index 0b6e0accc3..b6d8bec3b0 100644 --- a/packages/bundler-webpack/src/config/createBaseConfig.ts +++ b/packages/bundler-webpack/src/config/createBaseConfig.ts @@ -52,7 +52,7 @@ export const createBaseConfig = async ({ /** * module */ - handleModule({ options, config, isBuild, isServer }) + handleModule({ app, options, config, isBuild, isServer }) /** * plugins diff --git a/packages/bundler-webpack/src/config/handleModule.ts b/packages/bundler-webpack/src/config/handleModule.ts index be33ca6a73..14d8a57b0c 100644 --- a/packages/bundler-webpack/src/config/handleModule.ts +++ b/packages/bundler-webpack/src/config/handleModule.ts @@ -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' @@ -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 @@ -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 }) diff --git a/packages/bundler-webpack/src/config/handleModuleVue.ts b/packages/bundler-webpack/src/config/handleModuleVue.ts index 335159cc1b..cca19e8fab 100644 --- a/packages/bundler-webpack/src/config/handleModuleVue.ts +++ b/packages/bundler-webpack/src/config/handleModuleVue.ts @@ -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) @@ -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) diff --git a/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.cts b/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.cts new file mode 100644 index 0000000000..f4b3001431 --- /dev/null +++ b/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.cts @@ -0,0 +1,3 @@ +const loader = require('./vuepressMarkdownLoader.js') + +module.exports = loader.vuepressMarkdownLoader diff --git a/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.ts b/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.ts new file mode 100644 index 0000000000..df071dfc90 --- /dev/null +++ b/packages/bundler-webpack/src/loaders/vuepressMarkdownLoader.ts @@ -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 = + 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) + } diff --git a/packages/bundler-webpack/tsup.config.ts b/packages/bundler-webpack/tsup.config.ts index 4899bc89aa..9f8379c060 100644 --- a/packages/bundler-webpack/tsup.config.ts +++ b/packages/bundler-webpack/tsup.config.ts @@ -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'], diff --git a/packages/cli/src/commands/dev/handlePageAdd.ts b/packages/cli/src/commands/dev/handlePageAdd.ts index bcf5415a97..0d91181927 100644 --- a/packages/cli/src/commands/dev/handlePageAdd.ts +++ b/packages/cli/src/commands/dev/handlePageAdd.ts @@ -1,10 +1,5 @@ import type { App, Page } from '@vuepress/core' -import { - createPage, - preparePageChunk, - preparePageComponent, - prepareRoutes, -} from '@vuepress/core' +import { createPage, preparePageChunk, prepareRoutes } from '@vuepress/core' /** * Event handler for page add event @@ -22,18 +17,16 @@ export const handlePageAdd = async ( } // create page - const page = await createPage(app, { - filePath, - }) + const page = await createPage(app, { filePath }) // add the new page app.pages.push(page) + app.pagesMap[filePath] = page - // prepare page files - await preparePageComponent(app, page) + // prepare page file await preparePageChunk(app, page) - // prepare routes file + // re-prepare routes file await prepareRoutes(app) return page diff --git a/packages/cli/src/commands/dev/handlePageChange.ts b/packages/cli/src/commands/dev/handlePageChange.ts index c2255096f0..84df9d5d4c 100644 --- a/packages/cli/src/commands/dev/handlePageChange.ts +++ b/packages/cli/src/commands/dev/handlePageChange.ts @@ -1,10 +1,5 @@ import type { App, Page } from '@vuepress/core' -import { - createPage, - preparePageChunk, - preparePageComponent, - prepareRoutes, -} from '@vuepress/core' +import { createPage, preparePageChunk, prepareRoutes } from '@vuepress/core' /** * Event handler for page change event @@ -25,15 +20,13 @@ export const handlePageChange = async ( const pageOld = app.pages[pageIndex] // create a new page from the changed file - const pageNew = await createPage(app, { - filePath, - }) + const pageNew = await createPage(app, { filePath }) // replace the old page with the new page app.pages.splice(pageIndex, 1, pageNew) + app.pagesMap[filePath] = pageNew // prepare page files - await preparePageComponent(app, pageNew) await preparePageChunk(app, pageNew) const isPathChanged = pageOld.path !== pageNew.path diff --git a/packages/cli/src/commands/dev/handlePageUnlink.ts b/packages/cli/src/commands/dev/handlePageUnlink.ts index ad4d01a2c4..40dca36d4e 100644 --- a/packages/cli/src/commands/dev/handlePageUnlink.ts +++ b/packages/cli/src/commands/dev/handlePageUnlink.ts @@ -20,6 +20,7 @@ export const handlePageUnlink = async ( // remove the old page app.pages.splice(pageIndex, 1) + delete app.pagesMap[filePath] // re-prepare routes file await prepareRoutes(app) diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index e58b46214d..c17bc44a3a 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -22,7 +22,7 @@ export const Content = defineComponent({ if (!props.path) return pageComponent.value const route = resolveRoute(props.path) return defineAsyncComponent(async () => - route.loader().then(({ comp }) => comp), + route.loader().then((m) => m.default), ) }) diff --git a/packages/client/src/setupGlobalComputed.ts b/packages/client/src/setupGlobalComputed.ts index e258cb7673..79c8fe459a 100644 --- a/packages/client/src/setupGlobalComputed.ts +++ b/packages/client/src/setupGlobalComputed.ts @@ -47,12 +47,15 @@ export const setupGlobalComputed = ( if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { __VUE_HMR_RUNTIME__.updatePageData = async (newPageData: PageData) => { const oldPageChunk = await routes.value[newPageData.path].loader() - const newPageChunk = { comp: oldPageChunk.comp, data: newPageData } + const newPageChunk: PageChunk = { + default: oldPageChunk.default, + _pageData: newPageData, + } routes.value[newPageData.path].loader = async () => Promise.resolve(newPageChunk) if ( newPageData.path === - router.currentRoute.value.meta._pageChunk?.data.path + router.currentRoute.value.meta._pageChunk?._pageData.path ) { pageChunk.value = newPageChunk } @@ -67,8 +70,8 @@ export const setupGlobalComputed = ( const siteLocaleData = computed(() => resolvers.resolveSiteLocaleData(siteData.value, routeLocale.value), ) - const pageComponent = computed(() => pageChunk.value.comp) - const pageData = computed(() => pageChunk.value.data) + const pageComponent = computed(() => pageChunk.value.default) + const pageData = computed(() => pageChunk.value._pageData) const pageFrontmatter = computed(() => pageData.value.frontmatter) const pageHeadTitle = computed(() => resolvers.resolvePageHeadTitle(pageData.value, siteLocaleData.value), diff --git a/packages/client/src/types/routes.ts b/packages/client/src/types/routes.ts index 6aa5d10871..c102dee700 100644 --- a/packages/client/src/types/routes.ts +++ b/packages/client/src/types/routes.ts @@ -2,8 +2,8 @@ import type { Component } from 'vue' import type { PageData } from '../types/index.js' export interface PageChunk { - comp: Component - data: PageData + default: Component + _pageData: PageData } export type RouteMeta = Record diff --git a/packages/core/src/app/appInit.ts b/packages/core/src/app/appInit.ts index 95ce110178..1e9f65b48c 100644 --- a/packages/core/src/app/appInit.ts +++ b/packages/core/src/app/appInit.ts @@ -24,7 +24,9 @@ export const appInit = async (app: App): Promise => { app.markdown = await resolveAppMarkdown(app) // create pages - app.pages = await resolveAppPages(app) + const { pages, pagesMap } = await resolveAppPages(app) + app.pages = pages + app.pagesMap = pagesMap // plugin hook: onInitialized await app.pluginApi.hooks.onInitialized.process(app) diff --git a/packages/core/src/app/appPrepare.ts b/packages/core/src/app/appPrepare.ts index 15dd6986b7..4490332677 100644 --- a/packages/core/src/app/appPrepare.ts +++ b/packages/core/src/app/appPrepare.ts @@ -3,7 +3,6 @@ import type { App } from '../types/index.js' import { prepareClientConfigs, preparePageChunk, - preparePageComponent, prepareRoutes, prepareSiteData, } from './prepare/index.js' @@ -13,7 +12,6 @@ const log = debug('vuepress:core/app') /** * Prepare files for development or build * - * - page components * - page chunks * - routes * - site data @@ -25,10 +23,7 @@ export const appPrepare = async (app: App): Promise => { log('prepare start') await Promise.all([ - ...app.pages.flatMap((page) => [ - preparePageComponent(app, page), - preparePageChunk(app, page), - ]), + ...app.pages.map(async (page) => preparePageChunk(app, page)), prepareRoutes(app), prepareSiteData(app), prepareClientConfigs(app), diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index ea49aa7600..460f3b497f 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,5 +1,4 @@ export * from './prepareClientConfigs.js' export * from './preparePageChunk.js' -export * from './preparePageComponent.js' export * from './prepareRoutes.js' export * from './prepareSiteData.js' diff --git a/packages/core/src/app/prepare/preparePageChunk.ts b/packages/core/src/app/prepare/preparePageChunk.ts index 353a98eee1..0dd2475008 100644 --- a/packages/core/src/app/prepare/preparePageChunk.ts +++ b/packages/core/src/app/prepare/preparePageChunk.ts @@ -1,35 +1,11 @@ +import { renderPageToVue } from '../../page/index.js' import type { App, Page } from '../../types/index.js' -const HMR_CODE = ` -if (import.meta.webpackHot) { - import.meta.webpackHot.accept() - if (__VUE_HMR_RUNTIME__.updatePageData) { - __VUE_HMR_RUNTIME__.updatePageData(data) - } -} - -if (import.meta.hot) { - import.meta.hot.accept(({ data }) => { - __VUE_HMR_RUNTIME__.updatePageData(data) - }) -} -` - /** - * Generate page chunk temp file of a single page + * Generate temp file the page does not have a source file */ export const preparePageChunk = async (app: App, page: Page): Promise => { - // page chunk file content - let content = `\ -import comp from ${JSON.stringify(page.componentFilePath)} -const data = JSON.parse(${JSON.stringify(JSON.stringify(page.data))}) -export { comp, data } -` - - // inject HMR code - if (app.env.isDev) { - content += HMR_CODE + if (page.filePath === null) { + await app.writeTemp(page.chunkFilePathRelative, renderPageToVue(app, page)) } - - await app.writeTemp(page.chunkFilePathRelative, content) } diff --git a/packages/core/src/app/prepare/preparePageComponent.ts b/packages/core/src/app/prepare/preparePageComponent.ts deleted file mode 100644 index 569d63697a..0000000000 --- a/packages/core/src/app/prepare/preparePageComponent.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { renderPageSfcBlocksToVue } from '../../page/index.js' -import type { App, Page } from '../../types/index.js' - -/** - * Generate page component temp file of a single page - */ -export const preparePageComponent = async ( - app: App, - page: Page, -): Promise => { - await app.writeTemp( - page.componentFilePathRelative, - renderPageSfcBlocksToVue(page.sfcBlocks), - ) -} diff --git a/packages/core/src/app/prepare/prepareRoutes.ts b/packages/core/src/app/prepare/prepareRoutes.ts index b3556e8041..e8bf45a7bb 100644 --- a/packages/core/src/app/prepare/prepareRoutes.ts +++ b/packages/core/src/app/prepare/prepareRoutes.ts @@ -1,27 +1,28 @@ import { normalizeRoutePath } from '@vuepress/shared' import type { App, Page } from '../../types/index.js' +const ROUTES_VAR_NAME = 'routes' +const REDIRECTS_VAR_NAME = 'redirects' + const HMR_CODE = ` if (import.meta.webpackHot) { import.meta.webpackHot.accept() - if (__VUE_HMR_RUNTIME__.updateRoutes) { - __VUE_HMR_RUNTIME__.updateRoutes(routes) - } - if (__VUE_HMR_RUNTIME__.updateRedirects) { - __VUE_HMR_RUNTIME__.updateRedirects(redirects) - } + __VUE_HMR_RUNTIME__.updateRoutes?.(${ROUTES_VAR_NAME}) + __VUE_HMR_RUNTIME__.updateRedirects?.(${REDIRECTS_VAR_NAME}) } if (import.meta.hot) { - import.meta.hot.accept(({ routes, redirects }) => { - __VUE_HMR_RUNTIME__.updateRoutes(routes) - __VUE_HMR_RUNTIME__.updateRedirects(redirects) + import.meta.hot.accept((m) => { + __VUE_HMR_RUNTIME__.updateRoutes?.(m.${ROUTES_VAR_NAME}) + __VUE_HMR_RUNTIME__.updateRedirects?.(m.${REDIRECTS_VAR_NAME}) }) } ` /** * Resolve page redirects + * + * @internal */ const resolvePageRedirects = ({ path, pathInferred }: Page): string[] => { // paths that should redirect to this page, use set to dedupe @@ -47,7 +48,7 @@ const resolvePageRedirects = ({ path, pathInferred }: Page): string[] => { export const prepareRoutes = async (app: App): Promise => { // routes file content let content = `\ -export const redirects = JSON.parse(${JSON.stringify( +export const ${REDIRECTS_VAR_NAME} = JSON.parse(${JSON.stringify( JSON.stringify( Object.fromEntries( app.pages.flatMap((page) => @@ -57,7 +58,7 @@ export const redirects = JSON.parse(${JSON.stringify( ), )}) -export const routes = Object.fromEntries([ +export const ${ROUTES_VAR_NAME} = Object.fromEntries([ ${app.pages .map( ({ chunkFilePath, chunkName, path, routeMeta }) => diff --git a/packages/core/src/app/prepare/prepareSiteData.ts b/packages/core/src/app/prepare/prepareSiteData.ts index 7121e72845..68976cceb3 100644 --- a/packages/core/src/app/prepare/prepareSiteData.ts +++ b/packages/core/src/app/prepare/prepareSiteData.ts @@ -1,16 +1,16 @@ import type { App } from '../../types/index.js' +const SITE_DATA_VAR_NAME = 'siteData' + const HMR_CODE = ` if (import.meta.webpackHot) { import.meta.webpackHot.accept() - if (__VUE_HMR_RUNTIME__.updateSiteData) { - __VUE_HMR_RUNTIME__.updateSiteData(siteData) - } + __VUE_HMR_RUNTIME__.updateSiteData?.(${SITE_DATA_VAR_NAME}) } if (import.meta.hot) { - import.meta.hot.accept(({ siteData }) => { - __VUE_HMR_RUNTIME__.updateSiteData(siteData) + import.meta.hot.accept((m) => { + __VUE_HMR_RUNTIME__.updateSiteData?.(m.${SITE_DATA_VAR_NAME}) }) } ` @@ -22,7 +22,7 @@ if (import.meta.hot) { */ export const prepareSiteData = async (app: App): Promise => { let content = `\ -export const siteData = JSON.parse(${JSON.stringify( +export const ${SITE_DATA_VAR_NAME} = JSON.parse(${JSON.stringify( JSON.stringify(app.siteData), )}) ` diff --git a/packages/core/src/app/resolveAppPages.ts b/packages/core/src/app/resolveAppPages.ts index 51ed100308..0380d2e45b 100644 --- a/packages/core/src/app/resolveAppPages.ts +++ b/packages/core/src/app/resolveAppPages.ts @@ -9,7 +9,9 @@ const log = debug('vuepress:core/app') * * @internal */ -export const resolveAppPages = async (app: App): Promise => { +export const resolveAppPages = async ( + app: App, +): Promise> => { log('resolveAppPages start') // resolve page absolute file paths according to the page patterns @@ -21,9 +23,14 @@ export const resolveAppPages = async (app: App): Promise => { let hasNotFoundPage = false as boolean // create pages from files + const pagesMap: Record = {} const pages = await Promise.all( pageFilePaths.map(async (filePath) => { const page = await createPage(app, { filePath }) + // if there is a source file for the page, set it to the pages map + if (page.filePath) { + pagesMap[page.filePath] = page + } // if there is a 404 page, set the default layout to NotFound if (page.path === '/404.html') { page.frontmatter.layout ??= 'NotFound' @@ -46,5 +53,5 @@ export const resolveAppPages = async (app: App): Promise => { log('resolveAppPages finish') - return pages + return { pages, pagesMap } } diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index 2e9946dec2..1200cace41 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -2,7 +2,6 @@ import type { App, Page, PageOptions } from '../types/index.js' import { inferPagePath } from './inferPagePath.js' import { parsePageContent } from './parsePageContent.js' import { resolvePageChunkInfo } from './resolvePageChunkInfo.js' -import { resolvePageComponentInfo } from './resolvePageComponentInfo.js' import { resolvePageContent } from './resolvePageContent.js' import { resolvePageDate } from './resolvePageDate.js' import { resolvePageFilePath } from './resolvePageFilePath.js' @@ -84,16 +83,14 @@ export const createPage = async ( path, }) - // resolve page component and extract headers & links - const { componentFilePath, componentFilePathRelative } = - resolvePageComponentInfo({ + const { chunkFilePath, chunkFilePathRelative, chunkName } = + resolvePageChunkInfo({ app, + filePath, + filePathRelative, htmlFilePathRelative, }) - const { chunkFilePath, chunkFilePathRelative, chunkName } = - resolvePageChunkInfo({ app, htmlFilePathRelative }) - const page: Page = { // page data data: { @@ -128,8 +125,6 @@ export const createPage = async ( // file info filePath, filePathRelative, - componentFilePath, - componentFilePathRelative, chunkFilePath, chunkFilePathRelative, chunkName, diff --git a/packages/core/src/page/index.ts b/packages/core/src/page/index.ts index f37c622171..e6bc9edd07 100644 --- a/packages/core/src/page/index.ts +++ b/packages/core/src/page/index.ts @@ -1,9 +1,8 @@ export * from './createPage.js' export * from './inferPagePath.js' export * from './parsePageContent.js' -export * from './renderPageSfcBlocksToVue.js' +export * from './renderPageToVue.js' export * from './resolvePageChunkInfo.js' -export * from './resolvePageComponentInfo.js' export * from './resolvePageDate.js' export * from './resolvePageContent.js' export * from './resolvePageFilePath.js' diff --git a/packages/core/src/page/renderPageSfcBlocksToVue.ts b/packages/core/src/page/renderPageSfcBlocksToVue.ts deleted file mode 100644 index 79f15e92ca..0000000000 --- a/packages/core/src/page/renderPageSfcBlocksToVue.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MarkdownSfcBlocks } from '@vuepress/markdown' - -/** - * Render page sfc blocks to vue component - * - * @internal - */ -export const renderPageSfcBlocksToVue = ( - sfcBlocks: MarkdownSfcBlocks, -): string => - [ - // #688: wrap the content of `