From 46a6d2d6a84e41339602a3ddcdd909494e9f5397 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Mon, 4 Dec 2023 13:14:52 +0800 Subject: [PATCH 01/31] feat: introduce VPLink and virtual routes --- .../cli/src/commands/dev/handlePageAdd.ts | 8 +- .../cli/src/commands/dev/handlePageChange.ts | 20 +-- .../cli/src/commands/dev/handlePageUnlink.ts | 10 +- packages/client/package.json | 4 +- packages/client/src/components/Content.ts | 7 +- packages/client/src/components/VPLink.ts | 34 ++++ packages/client/src/components/index.ts | 1 + packages/client/src/composables/index.ts | 2 +- packages/client/src/composables/pagesData.ts | 27 ---- packages/client/src/composables/pagesMap.ts | 47 ++++++ packages/client/src/helpers/index.ts | 1 + packages/client/src/helpers/router.ts | 38 +++++ packages/client/src/resolvers.ts | 40 ++++- packages/client/src/router.ts | 36 ++++- packages/client/src/routes.ts | 44 ----- packages/client/src/setupGlobalComponents.ts | 3 +- packages/client/src/setupGlobalComputed.ts | 4 +- packages/client/src/types/index.ts | 1 - .../src/types/internal/pagesComponents.d.ts | 5 - .../client/src/types/internal/pagesData.d.ts | 5 - .../client/src/types/internal/pagesMap.d.ts | 20 +++ .../src/types/internal/pagesRoutes.d.ts | 5 - packages/client/src/types/pageRouteItem.ts | 8 - packages/client/src/utils/index.ts | 1 + packages/client/src/utils/routeGuardEvent.ts | 19 +++ packages/core/src/app/appPrepare.ts | 10 +- packages/core/src/app/prepare/index.ts | 4 +- .../core/src/app/prepare/preparePageMap.ts | 109 +++++++++++++ .../src/app/prepare/preparePagesComponents.ts | 27 ---- .../core/src/app/prepare/preparePagesData.ts | 23 --- .../src/app/prepare/preparePagesRoutes.ts | 59 ------- packages/core/src/page/createPage.ts | 17 +- packages/core/src/page/index.ts | 2 +- packages/core/src/page/resolvePageHtmlInfo.ts | 8 +- packages/core/src/page/resolvePageMeta.ts | 18 +++ packages/core/src/page/resolvePagePath.ts | 7 +- .../core/src/page/resolvePageRouteMeta.ts | 10 -- packages/core/src/types/page.ts | 4 +- .../core/tests/page/resolvePageMeta.spec.ts | 26 +++ .../tests/page/resolvePageRouteMeta.spec.ts | 26 --- .../src/plugins/linksPlugin/linksPlugin.ts | 16 +- .../tests/plugins/linksPlugin.spec.ts | 150 +++++++++--------- packages/shared/src/types/page.ts | 2 +- packages/shared/src/utils/index.ts | 1 + packages/shared/src/utils/normalizePath.ts | 13 ++ packages/shared/tests/normalizePath.spec.ts | 23 +++ 46 files changed, 547 insertions(+), 398 deletions(-) create mode 100644 packages/client/src/components/VPLink.ts delete mode 100644 packages/client/src/composables/pagesData.ts create mode 100644 packages/client/src/composables/pagesMap.ts create mode 100644 packages/client/src/helpers/router.ts delete mode 100644 packages/client/src/routes.ts delete mode 100644 packages/client/src/types/internal/pagesComponents.d.ts delete mode 100644 packages/client/src/types/internal/pagesData.d.ts create mode 100644 packages/client/src/types/internal/pagesMap.d.ts delete mode 100644 packages/client/src/types/internal/pagesRoutes.d.ts delete mode 100644 packages/client/src/types/pageRouteItem.ts create mode 100644 packages/client/src/utils/index.ts create mode 100644 packages/client/src/utils/routeGuardEvent.ts create mode 100644 packages/core/src/app/prepare/preparePageMap.ts delete mode 100644 packages/core/src/app/prepare/preparePagesComponents.ts delete mode 100644 packages/core/src/app/prepare/preparePagesData.ts delete mode 100644 packages/core/src/app/prepare/preparePagesRoutes.ts create mode 100644 packages/core/src/page/resolvePageMeta.ts delete mode 100644 packages/core/src/page/resolvePageRouteMeta.ts create mode 100644 packages/core/tests/page/resolvePageMeta.spec.ts delete mode 100644 packages/core/tests/page/resolvePageRouteMeta.spec.ts create mode 100644 packages/shared/src/utils/normalizePath.ts create mode 100644 packages/shared/tests/normalizePath.spec.ts diff --git a/packages/cli/src/commands/dev/handlePageAdd.ts b/packages/cli/src/commands/dev/handlePageAdd.ts index 2f61e0cee5..b566a22b8c 100644 --- a/packages/cli/src/commands/dev/handlePageAdd.ts +++ b/packages/cli/src/commands/dev/handlePageAdd.ts @@ -2,9 +2,7 @@ import { createPage, preparePageComponent, preparePageData, - preparePagesComponents, - preparePagesData, - preparePagesRoutes, + preparePagesMap, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -36,9 +34,7 @@ export const handlePageAdd = async ( await preparePageData(app, page) // prepare pages entry - await preparePagesComponents(app) - await preparePagesData(app) - await preparePagesRoutes(app) + await preparePagesMap(app) return page } diff --git a/packages/cli/src/commands/dev/handlePageChange.ts b/packages/cli/src/commands/dev/handlePageChange.ts index 19b1295628..5cf4d50134 100644 --- a/packages/cli/src/commands/dev/handlePageChange.ts +++ b/packages/cli/src/commands/dev/handlePageChange.ts @@ -2,9 +2,7 @@ import { createPage, preparePageComponent, preparePageData, - preparePagesComponents, - preparePagesData, - preparePagesRoutes, + preparePagesMap, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -39,18 +37,12 @@ export const handlePageChange = async ( await preparePageData(app, pageNew) const isPathChanged = pageOld.path !== pageNew.path - const isRouteMetaChanged = - JSON.stringify(pageOld.routeMeta) !== JSON.stringify(pageNew.routeMeta) + const isMetaChanged = + JSON.stringify(pageOld.meta) !== JSON.stringify(pageNew.meta) - // prepare pages entry if the path is changed - if (isPathChanged) { - await preparePagesComponents(app) - await preparePagesData(app) - } - - // prepare pages routes if the path or routeMeta is changed - if (isPathChanged || isRouteMetaChanged) { - await preparePagesRoutes(app) + // prepare pages map if the path or meta is changed + if (isPathChanged || isMetaChanged) { + await preparePagesMap(app) } return [pageOld, pageNew] diff --git a/packages/cli/src/commands/dev/handlePageUnlink.ts b/packages/cli/src/commands/dev/handlePageUnlink.ts index 792f865949..5e56b8fb9a 100644 --- a/packages/cli/src/commands/dev/handlePageUnlink.ts +++ b/packages/cli/src/commands/dev/handlePageUnlink.ts @@ -1,8 +1,4 @@ -import { - preparePagesComponents, - preparePagesData, - preparePagesRoutes, -} from '@vuepress/core' +import { preparePagesMap } from '@vuepress/core' import type { App, Page } from '@vuepress/core' /** @@ -26,9 +22,7 @@ export const handlePageUnlink = async ( app.pages.splice(pageIndex, 1) // re-prepare page files - await preparePagesComponents(app) - await preparePagesData(app) - await preparePagesRoutes(app) + await preparePagesMap(app) return page } diff --git a/packages/client/package.json b/packages/client/package.json index 692e3c54f4..a2eec4d8d6 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -56,9 +56,7 @@ "external": [ "@internal/clientConfigs", "@internal/layoutComponents", - "@internal/pagesComponents", - "@internal/pagesData", - "@internal/pagesRoutes", + "@internal/pagesMap", "@internal/siteData" ], "format": [ diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index 1923756d2a..ca3449a848 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -1,6 +1,5 @@ -import { pagesComponents } from '@internal/pagesComponents' import { computed, defineComponent, h } from 'vue' -import { usePageData } from '../composables/index.js' +import { pagesMap, usePageData } from '../composables/index.js' /** * Markdown rendered content @@ -10,7 +9,7 @@ export const Content = defineComponent({ name: 'Content', props: { - pageKey: { + url: { type: String, required: false, default: '', @@ -20,7 +19,7 @@ export const Content = defineComponent({ setup(props) { const page = usePageData() const pageComponent = computed( - () => pagesComponents[props.pageKey || page.value.key], + () => pagesMap.value.get(props.url || page.value.path)?.comp, ) return () => pageComponent.value diff --git a/packages/client/src/components/VPLink.ts b/packages/client/src/components/VPLink.ts new file mode 100644 index 0000000000..fdf94a7d6a --- /dev/null +++ b/packages/client/src/components/VPLink.ts @@ -0,0 +1,34 @@ +import { h } from 'vue' +import type { FunctionalComponent, VNode } from 'vue' +import { useRouter } from 'vue-router' +import { resolve, withBase } from '../helpers/index.js' +import { guardEvent } from '../utils/index.js' + +export interface VPLinkProps { + to: string +} + +export const VPLink: FunctionalComponent< + VPLinkProps, + Record, + { + default: () => string | VNode | (string | VNode)[] + } +> = ({ to = '' }, { slots }) => { + const router = useRouter() + const path = withBase(resolve(to).path) + + return h( + 'a', + { + class: 'vp-link', + href: path, + onClick: (event: MouseEvent = {} as MouseEvent) => { + guardEvent(event) ? router.push(to).catch() : Promise.resolve() + }, + }, + slots.default?.(), + ) +} + +VPLink.displayName = 'VPLink' diff --git a/packages/client/src/components/index.ts b/packages/client/src/components/index.ts index f11ca2bf0f..145c00508c 100644 --- a/packages/client/src/components/index.ts +++ b/packages/client/src/components/index.ts @@ -1,2 +1,3 @@ export * from './ClientOnly.js' export * from './Content.js' +export * from './VPLink.js' diff --git a/packages/client/src/composables/index.ts b/packages/client/src/composables/index.ts index 838b38b191..572a51bc03 100644 --- a/packages/client/src/composables/index.ts +++ b/packages/client/src/composables/index.ts @@ -5,7 +5,7 @@ export * from './pageHead.js' export * from './pageHeadTitle.js' export * from './pageLang.js' export * from './pageLayout.js' -export * from './pagesData.js' +export * from './pagesMap.js' export * from './routeLocale.js' export * from './siteData.js' export * from './siteLocaleData.js' diff --git a/packages/client/src/composables/pagesData.ts b/packages/client/src/composables/pagesData.ts deleted file mode 100644 index 6583ba089a..0000000000 --- a/packages/client/src/composables/pagesData.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { pagesData as pagesDataRaw } from '@internal/pagesData' -import type { PageData } from '@vuepress/shared' -import { ref } from 'vue' -import type { Ref } from 'vue' - -/** - * Data resolvers of all pages - * - * The key is page key, and the value is an async function that - * returns the page data - */ -export type PagesData = Record Promise) | undefined> - -/** - * Ref wrapper of `PagesData` - */ -export type PagesDataRef = Ref - -/** - * Global pages data ref - */ -export const pagesData: PagesDataRef = ref(pagesDataRaw) - -/** - * Returns the ref of data resolvers of all pages - */ -export const usePagesData = (): PagesDataRef => pagesData diff --git a/packages/client/src/composables/pagesMap.ts b/packages/client/src/composables/pagesMap.ts new file mode 100644 index 0000000000..76867e97b5 --- /dev/null +++ b/packages/client/src/composables/pagesMap.ts @@ -0,0 +1,47 @@ +import { + pagesMap as pagesMapRaw, + redirectsMap as redirectsMapRaw, +} from '@internal/pagesMap' +import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' +import { ref } from 'vue' +import type { Ref } from 'vue' + +/** + * Ref wrapper of `PagesMap` + */ +export type PagesMapRef = Ref + +/** + * Ref wrapper of `PageRedirectMap` + */ +export type PageRedirectsRef = Ref + +/** + * Global pages map ref + */ +export const pagesMap: PagesMapRef = ref(pagesMapRaw) + +/** + * Global pages map ref + */ +export const redirectsMap: PageRedirectsRef = ref(redirectsMapRaw) + +/** + * Returns the ref of pages map + */ +export const usePagesMap = (): PagesMapRef => pagesMap + +/** + * Returns the ref of pages map + */ +export const useRedirectsMap = (): PageRedirectsRef => redirectsMap + +if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { + // reuse vue HMR runtime + __VUE_HMR_RUNTIME__.updatePageRedirectsMap = (data: PageRedirectsMap) => { + redirectsMap.value = data + } + __VUE_HMR_RUNTIME__.updatePagesMap = (data: PagesMap) => { + pagesMap.value = data + } +} diff --git a/packages/client/src/helpers/index.ts b/packages/client/src/helpers/index.ts index e5726afa68..1be02a4358 100644 --- a/packages/client/src/helpers/index.ts +++ b/packages/client/src/helpers/index.ts @@ -1,2 +1,3 @@ export * from './defineClientConfig.js' +export * from './router.js' export * from './withBase.js' diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts new file mode 100644 index 0000000000..3e4858f4ea --- /dev/null +++ b/packages/client/src/helpers/router.ts @@ -0,0 +1,38 @@ +import type {PagesMap} from '@internal/pagesMap'; +import { pagesMap, redirectsMap } from '../composables/index.js' +import { resolvers } from '../resolvers.js' + +/** + * Get all paths of pages + * + * @returns all paths of pages + */ +export const getPagesPath = (): string[] => Object.keys(pagesMap.value) + +/** + * Check whether the page exists + * + * @param path path of the page + * @returns whether the page exists + */ +export const isPageExist = (path: string): boolean => + pagesMap.value.has( + resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path), + ) + +/** + * Resolve final path and meta with given path + * + * @param path path of the page + * @returns resolved path and meta + */ +export const resolve = ( + path: string, +): { path: string; meta: PageMeta | null } => { + path = resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) + + return { + path, + meta: (pagesMap.value as PagesMap).get(path)?.meta ?? null, + } +} diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index f8c70f3836..da195aef72 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,7 +1,9 @@ +import type {PageInfo, PageRedirectsMap, PagesMap} from '@internal/pagesMap'; import { dedupeHead, isArray, isString, + normalizePath, resolveLocalePath, } from '@vuepress/shared' import type { Component } from 'vue' @@ -16,7 +18,7 @@ import type { SiteData, SiteLocaleData, } from './composables/index.js' -import { pageDataEmpty, pagesData } from './composables/index.js' +import { pageDataEmpty } from './composables/index.js' import { LAYOUT_NAME_DEFAULT, LAYOUT_NAME_NOT_FOUND } from './constants.js' import type { ClientConfig, Layouts } from './types/index.js' @@ -39,11 +41,39 @@ export const resolvers = reactive({ ), /** - * Resolve page data according to page key + * Resolve page info according to page path */ - resolvePageData: async (pageKey: string): Promise => { - const pageDataResolver = pagesData.value[pageKey] - const pageData = await pageDataResolver?.() + resolvePagePath: ( + pagesMap: PagesMap, + redirectsMap: PageRedirectsMap, + path: string, + ): string => { + path = normalizePath(path) + + // original path + if (pagesMap.has(path)) return path + + // encoded path + const encodedPath = encodeURI(path) + if (pagesMap.has(encodedPath)) return encodedPath + + return ( + // redirects + redirectsMap.get(path) || + // if no match at this point, then we should leave the path as is + path + ) + }, + + /** + * Resolve page data according to page path and page info + */ + resolvePageData: async ( + pageInfo: PageInfo, + _path: string, + ): Promise => { + const pageData = await pageInfo?.data?.() + return pageData ?? pageDataEmpty }, diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index b9c6596d01..3def23b0d2 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -1,4 +1,3 @@ -import { pagesComponents } from '@internal/pagesComponents' import { removeEndingSlash } from '@vuepress/shared' import type { Router } from 'vue-router' import { @@ -7,9 +6,10 @@ import { createWebHistory, START_LOCATION, } from 'vue-router' +import { Vuepress } from './components/Vuepress.js' +import { pagesMap, redirectsMap } from './composables/index.js' import type { PageData } from './composables/index.js' import { resolvers } from './resolvers.js' -import { createRoutes } from './routes.js' /** * - use `createWebHistory` in dev mode and build mode client bundle @@ -24,8 +24,14 @@ export const createVueRouter = (): Router => { const router = createRouter({ // it might be an issue of vue-router that have to remove the ending slash history: historyCreator(removeEndingSlash(__VUEPRESS_BASE__)), - routes: createRoutes(), - scrollBehavior: (to, from, savedPosition) => { + routes: [ + { + name: 'vuepress-route', + path: '/:catchAll(.*)', + component: Vuepress, + }, + ], + scrollBehavior: (to, _from, savedPosition) => { if (savedPosition) return savedPosition if (to.hash) return { el: to.hash } return { top: 0 } @@ -34,11 +40,27 @@ export const createVueRouter = (): Router => { // ensure page data and page component have been loaded before resolving the route, // and save page data to route meta - router.beforeResolve(async (to, from) => { + router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { + const pagePath = resolvers.resolvePagePath( + pagesMap.value, + redirectsMap.value, + to.path, + ) + + if (pagePath !== to.path) { + return pagePath + } + + // if no match at this point, then we should provide 404 page + const pageInfo = + pagesMap.value.get(pagePath) || pagesMap.value.get('/404.html')! + + // TODO: Added for backwards compatibility, remove in stable version + to.meta = pageInfo.meta ;[to.meta._data] = await Promise.all([ - resolvers.resolvePageData(to.name as string), - pagesComponents[to.name as string]?.__asyncLoader(), + resolvers.resolvePageData(pageInfo, pagePath), + pageInfo.comp?.__asyncLoader(), ]) } }) diff --git a/packages/client/src/routes.ts b/packages/client/src/routes.ts deleted file mode 100644 index a0b6e3a816..0000000000 --- a/packages/client/src/routes.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { pagesRoutes } from '@internal/pagesRoutes' -import type { RouteRecordRaw } from 'vue-router' -import { Vuepress } from './components/Vuepress.js' - -/** - * Create routes for vue-router - */ -export const createRoutes = (): RouteRecordRaw[] => - pagesRoutes.reduce( - (result, [name, path, meta, redirects]) => { - result.push( - { - name, - path, - component: Vuepress, - meta, - }, - { - path: path.endsWith('/') - ? // redirect from `/index.html` to `/` - path + 'index.html' - : // redirect from `/foo` to `/foo.html` - path.substring(0, path.length - 5), - redirect: path, - }, - ...redirects.map((item) => ({ - path: - item === ':md' - ? // redirect from `/foo.md` to `/foo.html` - path.substring(0, path.length - 5) + '.md' - : item, - redirect: path, - })), - ) - return result - }, - [ - { - name: '404', - path: '/:catchAll(.*)', - component: Vuepress, - }, - ] as RouteRecordRaw[], - ) diff --git a/packages/client/src/setupGlobalComponents.ts b/packages/client/src/setupGlobalComponents.ts index b31443fd82..bd35c3628d 100644 --- a/packages/client/src/setupGlobalComponents.ts +++ b/packages/client/src/setupGlobalComponents.ts @@ -1,5 +1,5 @@ import type { App } from 'vue' -import { ClientOnly, Content } from './components/index.js' +import { ClientOnly, Content, VPLink } from './components/index.js' /** * Register global built-in components @@ -8,5 +8,6 @@ export const setupGlobalComponents = (app: App): void => { /* eslint-disable vue/match-component-file-name, vue/no-reserved-component-names */ app.component('ClientOnly', ClientOnly) app.component('Content', Content) + app.component('VPLink', VPLink) /* eslint-enable vue/match-component-file-name, vue/no-reserved-component-names */ } diff --git a/packages/client/src/setupGlobalComputed.ts b/packages/client/src/setupGlobalComputed.ts index 4f914337d2..66acd096cb 100644 --- a/packages/client/src/setupGlobalComputed.ts +++ b/packages/client/src/setupGlobalComputed.ts @@ -30,7 +30,7 @@ import { pageHeadTitleSymbol, pageLangSymbol, pageLayoutSymbol, - pagesData, + pagesMap, routeLocaleSymbol, siteData, siteLocaleDataSymbol, @@ -78,7 +78,7 @@ export const setupGlobalComputed = ( // handle page data HMR if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { __VUE_HMR_RUNTIME__.updatePageData = (data: PageData) => { - pagesData.value[data.key] = () => Promise.resolve(data) + pagesMap.value.get(data.path)!.data = () => Promise.resolve(data) if (data.key === router.currentRoute.value.meta._data?.key) { router.currentRoute.value.meta._data = data pageData.trigger() diff --git a/packages/client/src/types/index.ts b/packages/client/src/types/index.ts index 0988bf7ec3..ec9c7595e1 100644 --- a/packages/client/src/types/index.ts +++ b/packages/client/src/types/index.ts @@ -1,4 +1,3 @@ export * from './clientConfig.js' export * from './createVueAppFunction.js' export * from './layouts.js' -export * from './pageRouteItem.js' diff --git a/packages/client/src/types/internal/pagesComponents.d.ts b/packages/client/src/types/internal/pagesComponents.d.ts deleted file mode 100644 index e711c6fd6d..0000000000 --- a/packages/client/src/types/internal/pagesComponents.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ComponentOptions } from 'vue' - -declare module '@internal/pagesComponents' { - export const pagesComponents: Record -} diff --git a/packages/client/src/types/internal/pagesData.d.ts b/packages/client/src/types/internal/pagesData.d.ts deleted file mode 100644 index 0671aeedba..0000000000 --- a/packages/client/src/types/internal/pagesData.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { PageData } from '@vuepress/shared' - -declare module '@internal/pagesData' { - export const pagesData: Record Promise> -} diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts new file mode 100644 index 0000000000..a567fa3586 --- /dev/null +++ b/packages/client/src/types/internal/pagesMap.d.ts @@ -0,0 +1,20 @@ +import type { PageData } from '@vuepress/shared' +import type { ComponentOptions } from 'vue' + +declare module '@internal/pagesMap' { + export type PageRedirectsMap = Map + + export interface PageInfo> { + comp: ComponentOptions + data: () => Promise + meta: PageMeta + } + + export type PagesMap> = Map< + string, + PageInfo + > + + export const redirectsMap: PageRedirectsMap + export const pagesMap: PagesMap +} diff --git a/packages/client/src/types/internal/pagesRoutes.d.ts b/packages/client/src/types/internal/pagesRoutes.d.ts deleted file mode 100644 index 878d753d59..0000000000 --- a/packages/client/src/types/internal/pagesRoutes.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { PageRouteItem } from '../pageRouteItem.js' - -declare module '@internal/pagesRoutes' { - export const pagesRoutes: PageRouteItem[] -} diff --git a/packages/client/src/types/pageRouteItem.ts b/packages/client/src/types/pageRouteItem.ts deleted file mode 100644 index 0cbac2808f..0000000000 --- a/packages/client/src/types/pageRouteItem.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { RouteMeta } from 'vue-router' - -export type PageRouteItem = [ - name: string, - path: string, - meta: RouteMeta, - redirects: string[], -] diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts new file mode 100644 index 0000000000..e1ca609036 --- /dev/null +++ b/packages/client/src/utils/index.ts @@ -0,0 +1 @@ +export * from './routeGuardEvent.js' diff --git a/packages/client/src/utils/routeGuardEvent.ts b/packages/client/src/utils/routeGuardEvent.ts new file mode 100644 index 0000000000..c7b704c3e6 --- /dev/null +++ b/packages/client/src/utils/routeGuardEvent.ts @@ -0,0 +1,19 @@ +// Copied from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 +export const guardEvent = (event: MouseEvent): boolean | void => { + // don't redirect with control keys + if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return + // don't redirect when preventDefault called + if (event.defaultPrevented) return + // don't redirect on right click + if (event.button !== undefined && event.button !== 0) return + // don't redirect if `target="_blank"` + if (event.currentTarget) { + const target = (event.currentTarget as HTMLElement).getAttribute('target') + + if (target?.match(/\b_blank\b/i)) return + } + + event.preventDefault() + + return true +} diff --git a/packages/core/src/app/appPrepare.ts b/packages/core/src/app/appPrepare.ts index 85b8b7b854..8151b8b04a 100644 --- a/packages/core/src/app/appPrepare.ts +++ b/packages/core/src/app/appPrepare.ts @@ -4,9 +4,7 @@ import { prepareClientConfigs, preparePageComponent, preparePageData, - preparePagesComponents, - preparePagesData, - preparePagesRoutes, + preparePagesMap, prepareSiteData, } from './prepare/index.js' @@ -27,16 +25,14 @@ export const appPrepare = async (app: App): Promise => { for (const page of app.pages) { await preparePageComponent(app, page) } - await preparePagesComponents(app) // generate page data files for (const page of app.pages) { await preparePageData(app, page) } - await preparePagesData(app) - // generate routes file - await preparePagesRoutes(app) + // generate pages map + await preparePagesMap(app) // generate site data file await prepareSiteData(app) diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index 393e1c7801..ff75baf474 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,7 +1,5 @@ export * from './prepareClientConfigs.js' export * from './preparePageComponent.js' export * from './preparePageData.js' -export * from './preparePagesComponents.js' -export * from './preparePagesData.js' -export * from './preparePagesRoutes.js' +export * from './preparePageMap.js' export * from './prepareSiteData.js' diff --git a/packages/core/src/app/prepare/preparePageMap.ts b/packages/core/src/app/prepare/preparePageMap.ts new file mode 100644 index 0000000000..5edd06266c --- /dev/null +++ b/packages/core/src/app/prepare/preparePageMap.ts @@ -0,0 +1,109 @@ +import { ensureLeadingSlash } from '@vuepress/shared' +import type { App, Page } from '../../types/index.js' + +const HMR_CODE = ` +if (import.meta.webpackHot) { + import.meta.webpackHot.accept() + if (__VUE_HMR_RUNTIME__.updatePagesMap) { + __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) + } + if (__VUE_HMR_RUNTIME__.updatePageRedirectsMap) { + __VUE_HMR_RUNTIME__.updatePageRedirectsMap(redirectsMap) + } +} + +if (import.meta.hot) { + import.meta.hot.accept(({ pagesMap, redirectsMap }) => { + __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) + __VUE_HMR_RUNTIME__.updatePageRedirectsMap(redirectsMap) + }) +} +` + +/** + * Resolve page route item + */ +const resolvePageRedirects = ({ + path, + pathInferred, + filePathRelative, +}: Page): string[] => { + // paths that should redirect to this page, use set to dedupe + const redirectsSet = new Set() + + // redirect from decoded path + redirectsSet.add(decodeURI(path)) + + // redirect from inferred path + if (pathInferred !== null) { + redirectsSet.add(pathInferred) + } + + // redirect from none-markdown filename path + // markdown file path is omitted as it can be normalized to pathInferred + if (filePathRelative !== null && !filePathRelative.endsWith('.md')) { + redirectsSet.add(ensureLeadingSlash(filePathRelative)) + } + + // avoid redirect from the page path itself + redirectsSet.delete(path) + + return Array.from(redirectsSet) +} + +/** + * Generate page map temp file + */ +export const preparePagesMap = async (app: App): Promise => { + const pageMapEntries = app.pages + .map((page) => { + const { + meta, + path, + dataFilePath, + dataFileChunkName, + componentFilePath, + componentFileChunkName, + } = page + const redirects = resolvePageRedirects(page) + + return ` + [${JSON.stringify(path)}, defineAsyncComponent(() => import(${ + componentFileChunkName + ? `/* webpackChunkName: "${componentFileChunkName}" */` + : '' + }${JSON.stringify(componentFilePath)})), () => import(${ + dataFileChunkName ? `/* webpackChunkName: "${dataFileChunkName}" */` : '' + }${JSON.stringify(dataFilePath)}).then(({ data }) => data), ${JSON.stringify( + meta, + )}${ + redirects.length + ? `, [${redirects.map((path) => JSON.stringify(path)).join(', ')}]` + : '' + }],` + }) + .join('\n ') + + // generate page component map file + let content = `\ +import { defineAsyncComponent } from 'vue' + +const pageMapEntries = [${pageMapEntries}]; + +export const redirectsMap = new Map( + pageMapEntries + .flatMap(([path, , , , redirects]) => redirects.map((redirect) => [redirect, path])), +); + +export const pagesMap = new Map( + pageMapEntries.map(([path, comp, data, meta]) => [path, { comp, data, meta }]), +); +` + + // inject HMR code + if (app.env.isDev) { + content += HMR_CODE + } + + await app.writeTemp('internal/pagesMap.js', content) +} diff --git a/packages/core/src/app/prepare/preparePagesComponents.ts b/packages/core/src/app/prepare/preparePagesComponents.ts deleted file mode 100644 index a63b678d60..0000000000 --- a/packages/core/src/app/prepare/preparePagesComponents.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { App } from '../../types/index.js' - -/** - * Generate page key to page component map temp file - */ -export const preparePagesComponents = async (app: App): Promise => { - // generate page component map file - const content = `\ -import { defineAsyncComponent } from 'vue' - -export const pagesComponents = {\ -${app.pages - .map( - ({ key, path, componentFilePath, componentFileChunkName }) => ` - // path: ${path} - ${JSON.stringify(key)}: defineAsyncComponent(() => import(${ - componentFileChunkName - ? `/* webpackChunkName: "${componentFileChunkName}" */` - : '' - }${JSON.stringify(componentFilePath)})),`, - ) - .join('')} -} -` - - await app.writeTemp('internal/pagesComponents.js', content) -} diff --git a/packages/core/src/app/prepare/preparePagesData.ts b/packages/core/src/app/prepare/preparePagesData.ts deleted file mode 100644 index 8d2c582f6d..0000000000 --- a/packages/core/src/app/prepare/preparePagesData.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { App } from '../../types/index.js' - -/** - * Generate page path to page data map temp file - */ -export const preparePagesData = async (app: App): Promise => { - // generate page data map - const content = `\ -export const pagesData = {\ -${app.pages - .map( - ({ key, path, dataFilePath, dataFileChunkName }) => ` - // path: ${path} - ${JSON.stringify(key)}: () => import(${ - dataFileChunkName ? `/* webpackChunkName: "${dataFileChunkName}" */` : '' - }${JSON.stringify(dataFilePath)}).then(({ data }) => data),`, - ) - .join('')} -} -` - - await app.writeTemp('internal/pagesData.js', content) -} diff --git a/packages/core/src/app/prepare/preparePagesRoutes.ts b/packages/core/src/app/prepare/preparePagesRoutes.ts deleted file mode 100644 index 5c9130049e..0000000000 --- a/packages/core/src/app/prepare/preparePagesRoutes.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { PageRouteItem } from '@vuepress/client' -import { ensureLeadingSlash } from '@vuepress/shared' -import type { App, Page } from '../../types/index.js' - -/** - * Resolve page route item - */ -const resolvePageRouteItem = ({ - key, - path, - pathInferred, - filePathRelative, - routeMeta, -}: Page): PageRouteItem => { - // paths that should redirect to this page, use set to dedupe - const redirectsSet = new Set() - - // redirect from decoded path - redirectsSet.add(decodeURI(path)) - - // redirect from inferred path - if (pathInferred !== null) { - redirectsSet.add(pathInferred) - redirectsSet.add(encodeURI(pathInferred)) - } - - // redirect from filename path - if (filePathRelative !== null) { - const filenamePath = ensureLeadingSlash(filePathRelative) - redirectsSet.add(filenamePath) - redirectsSet.add(encodeURI(filenamePath)) - } - - // avoid redirect from the page path itself - redirectsSet.delete(path) - - return [ - key, - path, - routeMeta, - [...redirectsSet].map((item) => - item.replace(/\.md$/, '.html') === path ? ':md' : item, - ), - ] -} - -/** - * Generate routes temp file - */ -export const preparePagesRoutes = async (app: App): Promise => { - const routeItems = app.pages.map(resolvePageRouteItem) - const content = `\ -export const pagesRoutes = [\ -${routeItems.map((routeItem) => `\n ${JSON.stringify(routeItem)},`).join('')} -] -` - - await app.writeTemp('internal/pagesRoutes.js', content) -} diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index f1edd0d963..42f8ea20de 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -9,9 +9,9 @@ import { resolvePageFilePath } from './resolvePageFilePath.js' import { resolvePageHtmlInfo } from './resolvePageHtmlInfo.js' import { resolvePageKey } from './resolvePageKey.js' import { resolvePageLang } from './resolvePageLang.js' +import { resolvePageMeta } from './resolvePageMeta.js' import { resolvePagePath } from './resolvePagePath.js' import { resolvePagePermalink } from './resolvePagePermalink.js' -import { resolvePageRouteMeta } from './resolvePageRouteMeta.js' import { resolvePageSlug } from './resolvePageSlug.js' export const createPage = async ( @@ -49,7 +49,7 @@ export const createPage = async ( }) // resolve route meta from frontmatter - const routeMeta = resolvePageRouteMeta({ frontmatter }) + const meta = resolvePageMeta({ frontmatter }) // resolve slug from file path const slug = resolvePageSlug({ filePathRelative }) @@ -128,7 +128,7 @@ export const createPage = async ( pathInferred, pathLocale, permalink, - routeMeta, + meta, sfcBlocks, slug, @@ -143,6 +143,17 @@ export const createPage = async ( dataFileChunkName, htmlFilePath, htmlFilePathRelative, + + // TODO: Added for backwards compatibility, remove in next major version + // @ts-expect-error use meta instead + routeMeta: new Proxy(meta, { + set: (obj, prop, value) => { + console.warn('routeMeta is deprecated, please use meta instead') + + obj[prop as string] = value + return true + }, + }), } // plugin hook: extendsPage diff --git a/packages/core/src/page/index.ts b/packages/core/src/page/index.ts index 1d41ae5ae1..62bdf070c5 100644 --- a/packages/core/src/page/index.ts +++ b/packages/core/src/page/index.ts @@ -11,5 +11,5 @@ export * from './resolvePageKey.js' export * from './resolvePageLang.js' export * from './resolvePagePath.js' export * from './resolvePagePermalink.js' -export * from './resolvePageRouteMeta.js' +export * from './resolvePageMeta.js' export * from './resolvePageSlug.js' diff --git a/packages/core/src/page/resolvePageHtmlInfo.ts b/packages/core/src/page/resolvePageHtmlInfo.ts index 5cecab0323..58d1c63b4b 100644 --- a/packages/core/src/page/resolvePageHtmlInfo.ts +++ b/packages/core/src/page/resolvePageHtmlInfo.ts @@ -14,10 +14,16 @@ export const resolvePageHtmlInfo = ({ htmlFilePath: string htmlFilePathRelative: string } => { + const path = decodeURI(pagePath) + // /foo.html -> foo.html // /foo/ -> foo/index.html const htmlFilePathRelative = removeLeadingSlash( - decodeURI(pagePath.replace(/\/$/, '/index.html')), + path.endsWith('/') + ? path + 'index.html' + : path.endsWith('.html') + ? path + : path + '.html', ) const htmlFilePath = app.dir.dest(htmlFilePathRelative) diff --git a/packages/core/src/page/resolvePageMeta.ts b/packages/core/src/page/resolvePageMeta.ts new file mode 100644 index 0000000000..e532cabb09 --- /dev/null +++ b/packages/core/src/page/resolvePageMeta.ts @@ -0,0 +1,18 @@ +import type { PageFrontmatter } from '../types/index.js' + +/** + * Resolve page route meta + */ +export const resolvePageMeta = ({ + frontmatter, +}: { + frontmatter: PageFrontmatter +}): Record => { + // TODO: Added for backwards compatibility, should be removed in stable + if (frontmatter.routeMeta) { + console.warn('routeMeta is deprecated, please use meta instead') + frontmatter.meta = { ...frontmatter.routeMeta, ...frontmatter.meta } + } + + return frontmatter.meta ?? {} +} diff --git a/packages/core/src/page/resolvePagePath.ts b/packages/core/src/page/resolvePagePath.ts index 3929d6955c..c40e366455 100644 --- a/packages/core/src/page/resolvePagePath.ts +++ b/packages/core/src/page/resolvePagePath.ts @@ -1,4 +1,3 @@ -import { ensureEndingSlash } from '@vuepress/shared' import { logger, sanitizeFileName } from '@vuepress/utils' import type { PageOptions } from '../types/index.js' @@ -14,7 +13,7 @@ export const resolvePagePath = ({ pathInferred: string | null options: PageOptions }): string => { - let pagePath = options.path || permalink || pathInferred + const pagePath = options.path || permalink || pathInferred if (!pagePath) { throw logger.createError( @@ -22,9 +21,5 @@ export const resolvePagePath = ({ ) } - if (!pagePath.endsWith('.html')) { - pagePath = ensureEndingSlash(pagePath) - } - return encodeURI(pagePath.split('/').map(sanitizeFileName).join('/')) } diff --git a/packages/core/src/page/resolvePageRouteMeta.ts b/packages/core/src/page/resolvePageRouteMeta.ts deleted file mode 100644 index 76e5030a91..0000000000 --- a/packages/core/src/page/resolvePageRouteMeta.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageFrontmatter } from '../types/index.js' - -/** - * Resolve page route meta - */ -export const resolvePageRouteMeta = ({ - frontmatter, -}: { - frontmatter: PageFrontmatter -}): Record => frontmatter.routeMeta ?? {} diff --git a/packages/core/src/types/page.ts b/packages/core/src/types/page.ts index a274bd442f..6495a973b2 100644 --- a/packages/core/src/types/page.ts +++ b/packages/core/src/types/page.ts @@ -73,11 +73,11 @@ export type Page< permalink: string | null /** - * Custom data to be attached to the page route record of vue-router + * Custom data to be attached to page record * * @see https://router.vuejs.org/api/#meta */ - routeMeta: Record + meta: Record /** * Extracted sfc blocks of the page diff --git a/packages/core/tests/page/resolvePageMeta.spec.ts b/packages/core/tests/page/resolvePageMeta.spec.ts new file mode 100644 index 0000000000..3ee0728c0d --- /dev/null +++ b/packages/core/tests/page/resolvePageMeta.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import { resolvePageMeta } from '../../src/index.js' + +describe('core > page > resolvePageMeta', () => { + it('should use frontmatter meta', () => { + const meta = resolvePageMeta({ + frontmatter: { + meta: { + foo: 'foo', + }, + }, + }) + + expect(meta).toEqual({ + foo: 'foo', + }) + }) + + it('should return default value', () => { + const meta = resolvePageMeta({ + frontmatter: {}, + }) + + expect(meta).toEqual({}) + }) +}) diff --git a/packages/core/tests/page/resolvePageRouteMeta.spec.ts b/packages/core/tests/page/resolvePageRouteMeta.spec.ts deleted file mode 100644 index 033ebebac3..0000000000 --- a/packages/core/tests/page/resolvePageRouteMeta.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolvePageRouteMeta } from '../../src/index.js' - -describe('core > page > resolvePageRouteMeta', () => { - it('should use frontmatter routeMeta', () => { - const routeMeta = resolvePageRouteMeta({ - frontmatter: { - routeMeta: { - foo: 'foo', - }, - }, - }) - - expect(routeMeta).toEqual({ - foo: 'foo', - }) - }) - - it('should return default value', () => { - const routeMeta = resolvePageRouteMeta({ - frontmatter: {}, - }) - - expect(routeMeta).toEqual({}) - }) -}) diff --git a/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts b/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts index ecd65ae3a8..f2702f663b 100644 --- a/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts +++ b/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts @@ -8,9 +8,9 @@ export interface LinksPluginOptions { /** * Tag for internal links * - * @default 'RouterLink' + * @default 'VPLink' */ - internalTag?: 'a' | 'RouterLink' + internalTag?: 'a' | 'VPLink' | 'RouterLink' /** * Additional attributes for external links @@ -29,7 +29,7 @@ export interface LinksPluginOptions { /** * Process links in markdown file * - * - internal links: convert them into `` + * - internal links: convert them into `` * - external links: add extra attrs and external icon */ export const linksPlugin: PluginWithOptions = ( @@ -37,7 +37,7 @@ export const linksPlugin: PluginWithOptions = ( options: LinksPluginOptions = {}, ): void => { // tag of internal links - const internalTag = options.internalTag || 'RouterLink' + const internalTag = options.internalTag || 'VPLink' // attrs that going to be added to external links const externalAttrs = { @@ -93,7 +93,7 @@ export const linksPlugin: PluginWithOptions = ( // convert // // to - // + // // notice that the path and hash are encoded by markdown-it const rawPath = internalLinkMatch[1] @@ -109,7 +109,7 @@ export const linksPlugin: PluginWithOptions = ( // normalize markdown file path to route path // // we are removing the `base` from absolute path because it should not be - // passed to `` + // passed to `` // // '/foo/index.md' => '/foo/' // '/foo/bar.md' => '/foo/bar.html' @@ -118,8 +118,8 @@ export const linksPlugin: PluginWithOptions = ( .replace(/(^|\/)(README|index).md$/i, '$1') .replace(/\.md$/, '.html') - if (internalTag === 'RouterLink') { - // convert starting tag of internal link to `` + if (['RouterLink', 'VPLink'].includes(internalTag)) { + // convert starting tag of internal link to `internalTag` token.tag = internalTag // replace the original `href` attr with `to` attr hrefAttr[0] = 'to' diff --git a/packages/markdown/tests/plugins/linksPlugin.spec.ts b/packages/markdown/tests/plugins/linksPlugin.spec.ts index e9986f954c..75760afa3f 100644 --- a/packages/markdown/tests/plugins/linksPlugin.spec.ts +++ b/packages/markdown/tests/plugins/linksPlugin.spec.ts @@ -293,24 +293,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => { expect(rendered).toEqual( [ - 'foo1', - 'foo2', - 'foo3', - 'bar1', - 'bar2', - 'bar3', - 'foobar1', - 'foobar2', - 'foobar3', - 'foobar4', - 'index1', - 'index2', - 'index3', - 'index4', - 'index5', - 'readme1', - 'readme2', - 'readme3', + 'foo1', + 'foo2', + 'foo3', + 'bar1', + 'bar2', + 'bar3', + 'foobar1', + 'foobar2', + 'foobar3', + 'foobar4', + 'index1', + 'index2', + 'index3', + 'index4', + 'index5', + 'readme1', + 'readme2', + 'readme3', ] .map((a) => `

${a}

`) .join('\n') + '\n', @@ -420,24 +420,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => { expect(rendered).toEqual( [ - 'foo1', - 'foo2', - 'foo3', - 'bar1', - 'bar2', - 'bar3', - 'foobar1', - 'foobar2', - 'foobar3', - 'foobar4', - 'index1', - 'index2', - 'index3', - 'index4', - 'index5', - 'readme1', - 'readme2', - 'readme3', + 'foo1', + 'foo2', + 'foo3', + 'bar1', + 'bar2', + 'bar3', + 'foobar1', + 'foobar2', + 'foobar3', + 'foobar4', + 'index1', + 'index2', + 'index3', + 'index4', + 'index5', + 'readme1', + 'readme2', + 'readme3', ] .map((a) => `

${a}

`) .join('\n') + '\n', @@ -549,24 +549,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => { expect(rendered).toEqual( [ - `foo1`, - `foo2`, - `foo3`, - `bar1`, - `bar2`, - `bar3`, - `foobar1`, - `foobar2`, - `foobar3`, - `foobar4`, - `index1`, - `index2`, - `index3`, - `index4`, - `index5`, - `readme1`, - `readme2`, - `readme3`, + `foo1`, + `foo2`, + `foo3`, + `bar1`, + `bar2`, + `bar3`, + `foobar1`, + `foobar2`, + `foobar3`, + `foobar4`, + `index1`, + `index2`, + `index3`, + `index4`, + `index5`, + `readme1`, + `readme2`, + `readme3`, ] .map((a) => `

${a}

`) .join('\n') + '\n', @@ -677,24 +677,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => { expect(rendered).toEqual( [ - 'foo1', - 'foo2', - 'foo3', - 'bar1', - 'bar2', - 'bar3', - 'foobar1', - 'foobar2', - 'foobar3', - 'foobar4', - 'index1', - 'index2', - 'index3', - 'index4', - 'index5', - 'readme1', - 'readme2', - 'readme3', + 'foo1', + 'foo2', + 'foo3', + 'bar1', + 'bar2', + 'bar3', + 'foobar1', + 'foobar2', + 'foobar3', + 'foobar4', + 'index1', + 'index2', + 'index3', + 'index4', + 'index5', + 'readme1', + 'readme2', + 'readme3', ] .map((a) => `

${a}

`) .join('\n') + '\n', @@ -812,9 +812,9 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => { expect(rendered).toEqual( [ - 'md', - 'md-with-redundant-base', - 'html', + 'md', + 'md-with-redundant-base', + 'html', ] .map((a) => `

${a}

`) .join('\n') + '\n', diff --git a/packages/shared/src/types/page.ts b/packages/shared/src/types/page.ts index 4ffecb23e8..41cffab7d8 100644 --- a/packages/shared/src/types/page.ts +++ b/packages/shared/src/types/page.ts @@ -73,7 +73,7 @@ export type PageFrontmatter< layout?: string permalink?: string permalinkPattern?: string | null - routeMeta?: Record + meta?: Record title?: string } diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index 5cbea86d0e..f8af25b775 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -9,6 +9,7 @@ export * from './isLinkExternal.js' export * from './isLinkHttp.js' export * from './isLinkWithProtocol.js' export * from './isPlainObject.js' +export * from './normalizePath.js' export * from './omit.js' export * from './removeEndingSlash.js' export * from './removeLeadingSlash.js' diff --git a/packages/shared/src/utils/normalizePath.ts b/packages/shared/src/utils/normalizePath.ts new file mode 100644 index 0000000000..f3ef770903 --- /dev/null +++ b/packages/shared/src/utils/normalizePath.ts @@ -0,0 +1,13 @@ +export const normalizePath = (path: string): string => { + const convertedMdPath = path.endsWith('README.md') + ? path.substring(0, path.length - 9) + : path.endsWith('.md') + ? path.substring(0, path.length - 3) + '.html' + : path + + return convertedMdPath.endsWith('/index.html') + ? convertedMdPath.substring(0, path.length - 10) + : convertedMdPath.endsWith('.html') || convertedMdPath.endsWith('/') + ? convertedMdPath + : convertedMdPath + '.html' +} diff --git a/packages/shared/tests/normalizePath.spec.ts b/packages/shared/tests/normalizePath.spec.ts new file mode 100644 index 0000000000..351448bbc7 --- /dev/null +++ b/packages/shared/tests/normalizePath.spec.ts @@ -0,0 +1,23 @@ +import { expect, it } from 'vitest' +import { normalizePath } from '../src/index.js' + +it('normalizePath', () => { + const tests = [ + ['/', '/'], + ['/index.html', '/'], + ['/foo', '/foo.html'], + ['/foo.md', '/foo.html'], + ['/foo/', '/foo/'], + ['/foo/README.md', '/foo/'], + ['/foo/index.html', '/foo/'], + ['/foo/bar', '/foo/bar.html'], + ['/foo/bar/', '/foo/bar/'], + ['/foo/bar/README.md', '/foo/bar/'], + ['/foo/bar.md', '/foo/bar.html'], + ['/foo/bar.html', '/foo/bar.html'], + ] + + tests.forEach(([path, expected]) => { + expect(normalizePath(path)).toBe(expected) + }) +}) From aedc63ab17dc8d5c3b273d90b23da2f0709d38c6 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Mon, 4 Dec 2023 13:33:32 +0800 Subject: [PATCH 02/31] style: fix linter --- packages/client/src/helpers/router.ts | 2 +- packages/client/src/resolvers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts index 3e4858f4ea..75b56065e5 100644 --- a/packages/client/src/helpers/router.ts +++ b/packages/client/src/helpers/router.ts @@ -1,4 +1,4 @@ -import type {PagesMap} from '@internal/pagesMap'; +import type { PagesMap } from '@internal/pagesMap' import { pagesMap, redirectsMap } from '../composables/index.js' import { resolvers } from '../resolvers.js' diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index da195aef72..802b0ff454 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,4 +1,4 @@ -import type {PageInfo, PageRedirectsMap, PagesMap} from '@internal/pagesMap'; +import type { PageInfo, PageRedirectsMap, PagesMap } from '@internal/pagesMap' import { dedupeHead, isArray, From 6920338628f50bea3cad1fce20b032a983e5dc31 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Mon, 4 Dec 2023 13:42:45 +0800 Subject: [PATCH 03/31] test: update tests --- packages/core/tests/page/resolvePageHtmlInfo.spec.ts | 2 ++ packages/core/tests/page/resolvePagePath.spec.ts | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/tests/page/resolvePageHtmlInfo.spec.ts b/packages/core/tests/page/resolvePageHtmlInfo.spec.ts index 52bed79e04..58042587be 100644 --- a/packages/core/tests/page/resolvePageHtmlInfo.spec.ts +++ b/packages/core/tests/page/resolvePageHtmlInfo.spec.ts @@ -9,8 +9,10 @@ const app = createBaseApp({ }) const testCases: [string, string][] = [ + ['/foo', 'foo.html'], ['/foo.html', 'foo.html'], ['/foo/bar.html', 'foo/bar.html'], + ['/foo/bar', 'foo/bar.html'], ['/foo/index.html', 'foo/index.html'], ['/foo/bar/index.html', 'foo/bar/index.html'], ['/foo/', 'foo/index.html'], diff --git a/packages/core/tests/page/resolvePagePath.spec.ts b/packages/core/tests/page/resolvePagePath.spec.ts index 56f44a0ec3..1ca68d2d65 100644 --- a/packages/core/tests/page/resolvePagePath.spec.ts +++ b/packages/core/tests/page/resolvePagePath.spec.ts @@ -16,7 +16,7 @@ const testCases: [ }, }, ], - '/options/', + '/options', ], [ [ @@ -51,7 +51,7 @@ const testCases: [ options: {}, }, ], - '/permalink/', + '/permalink', ], [ [ @@ -72,7 +72,7 @@ const testCases: [ options: {}, }, ], - '/inferred/', + '/inferred', ], [ [ From ca7eb8c8e248448de1c13ff8728598dac1cf2190 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Mon, 11 Dec 2023 11:58:17 +0800 Subject: [PATCH 04/31] fix: fix redirect issue --- packages/core/src/app/prepare/preparePageMap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/app/prepare/preparePageMap.ts b/packages/core/src/app/prepare/preparePageMap.ts index 5edd06266c..7e6354bcf7 100644 --- a/packages/core/src/app/prepare/preparePageMap.ts +++ b/packages/core/src/app/prepare/preparePageMap.ts @@ -92,7 +92,7 @@ const pageMapEntries = [${pageMapEntries}]; export const redirectsMap = new Map( pageMapEntries - .flatMap(([path, , , , redirects]) => redirects.map((redirect) => [redirect, path])), + .flatMap(([path, , , , redirects = []]) => redirects.map((redirect) => [redirect, path])), ); export const pagesMap = new Map( From 34bb3a1939f639c1521613e427164fc674ce811a Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Mon, 11 Dec 2023 12:41:36 +0800 Subject: [PATCH 05/31] feat: add e2e test --- e2e/docs/.vuepress/config.ts | 9 ++++ e2e/docs/page-data/meta.md | 5 ++ e2e/docs/router.md | 38 +++++++++++++ e2e/tests/router.cy.ts | 60 +++++++++++++++++++++ packages/client/src/composables/pagesMap.ts | 6 +-- packages/client/src/helpers/router.ts | 2 +- 6 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 e2e/docs/page-data/meta.md create mode 100644 e2e/docs/router.md create mode 100644 e2e/tests/router.cy.ts diff --git a/e2e/docs/.vuepress/config.ts b/e2e/docs/.vuepress/config.ts index 8460c8ddff..037c3dc0c1 100644 --- a/e2e/docs/.vuepress/config.ts +++ b/e2e/docs/.vuepress/config.ts @@ -28,4 +28,13 @@ export default defineUserConfig({ process.env.E2E_BUNDLER === 'webpack' ? webpackBundler() : viteBundler(), theme: e2eTheme(), + + extendsPage: (page) => { + if (page.path === '/page-data/meta.html') + page.meta = { + a: 1, + b: 2, + ...page.meta, + } + }, }) diff --git a/e2e/docs/page-data/meta.md b/e2e/docs/page-data/meta.md new file mode 100644 index 0000000000..3e822a6928 --- /dev/null +++ b/e2e/docs/page-data/meta.md @@ -0,0 +1,5 @@ +--- +meta: + a: 0 + c: 3 +--- diff --git a/e2e/docs/router.md b/e2e/docs/router.md new file mode 100644 index 0000000000..7e3914d04c --- /dev/null +++ b/e2e/docs/router.md @@ -0,0 +1,38 @@ +## GetPagesPath + +
    +
  • + {{path}} +
  • +
+ +## isPageExist + +
+ +- /: {{isPageExist('/')}} +- /README.md: {{isPageExist('/README.md')}} +- /index.html: {{isPageExist('/index.html')}} +- /not-exist: {{isPageExist('/not-exist')}} +- /not-exist.html: {{isPageExist('/not-exist.html')}} +- /not-exist.md: {{isPageExist('/not-exist.md')}} +- /zh/: {{isPageExist('/zh/')}} +- /zh: {{isPageExist('/zh')}} + +
+ +## resolve + +
+ +- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} +- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} +- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} + +
+ + diff --git a/e2e/tests/router.cy.ts b/e2e/tests/router.cy.ts new file mode 100644 index 0000000000..a1288360ad --- /dev/null +++ b/e2e/tests/router.cy.ts @@ -0,0 +1,60 @@ +context('router', () => { + it('getPagesPath', () => { + cy.visit('/router.html') + + const paths = [ + '/', + '/layouts/custom-layout.html', + '/layouts/layout.html', + '/markdown/code-blocks.html', + '/markdown/emoji.html', + '/markdown/import-code-blocks.html', + '/markdown/toc.html', + '/markdown/vue-components.html', + '/markdown/links/bar.html', + '/markdown/links/baz.html', + '/markdown/links/foo.html', + '/page-data/frontmatter.html', + '/page-data/headers.html', + '/page-data/lang.html', + '/page-data/meta.html', + '/page-data/permalink.html', + '/page-data/title-from-frontmatter.html', + '/page-data/title-from-h1.html', + '/router.html', + '/zh/', + '/404.html', + ] + + cy.get('.e2e-theme-content ul.get-pages-path li').each((el) => { + expect(paths.includes(el.text())).to.equal(true) + }) + }) + + it('isPageExist', () => { + cy.visit('/router.html') + const results = [ + '/: true', + '/README.md: true', + '/index.html: true', + '/not-exist: false', + '/not-exist.html: false', + '/not-exist.md: false', + '/zh/: true', + '/zh: false', + ] + + cy.get('.e2e-theme-content .is-page-exist ul li').each((el) => { + expect(results.includes(el.text())).to.equal(true) + }) + }) + + it('resolve', () => { + cy.visit('/router.html') + const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } + + cy.get('.e2e-theme-content .resolve ul li').each((el) => { + expect(el.text()).to.contain(JSON.stringify(result)) + }) + }) +}) diff --git a/packages/client/src/composables/pagesMap.ts b/packages/client/src/composables/pagesMap.ts index 76867e97b5..ebd30796d6 100644 --- a/packages/client/src/composables/pagesMap.ts +++ b/packages/client/src/composables/pagesMap.ts @@ -3,7 +3,7 @@ import { redirectsMap as redirectsMapRaw, } from '@internal/pagesMap' import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' -import { ref } from 'vue' +import { shallowRef } from 'vue' import type { Ref } from 'vue' /** @@ -19,12 +19,12 @@ export type PageRedirectsRef = Ref /** * Global pages map ref */ -export const pagesMap: PagesMapRef = ref(pagesMapRaw) +export const pagesMap: PagesMapRef = shallowRef(pagesMapRaw) /** * Global pages map ref */ -export const redirectsMap: PageRedirectsRef = ref(redirectsMapRaw) +export const redirectsMap: PageRedirectsRef = shallowRef(redirectsMapRaw) /** * Returns the ref of pages map diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts index 75b56065e5..25d86244e7 100644 --- a/packages/client/src/helpers/router.ts +++ b/packages/client/src/helpers/router.ts @@ -7,7 +7,7 @@ import { resolvers } from '../resolvers.js' * * @returns all paths of pages */ -export const getPagesPath = (): string[] => Object.keys(pagesMap.value) +export const getPagesPath = (): string[] => Array.from(pagesMap.value.keys()) /** * Check whether the page exists From 9684cf5008dc5b1c314c262e8f53dcc675d048e7 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Thu, 14 Dec 2023 15:38:49 +0800 Subject: [PATCH 06/31] test: update e2e test --- e2e/tests/router.cy.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/tests/router.cy.ts b/e2e/tests/router.cy.ts index a1288360ad..c6046ef4cf 100644 --- a/e2e/tests/router.cy.ts +++ b/e2e/tests/router.cy.ts @@ -6,14 +6,15 @@ context('router', () => { '/', '/layouts/custom-layout.html', '/layouts/layout.html', + '/markdown/anchors.html', '/markdown/code-blocks.html', '/markdown/emoji.html', '/markdown/import-code-blocks.html', - '/markdown/toc.html', - '/markdown/vue-components.html', '/markdown/links/bar.html', '/markdown/links/baz.html', '/markdown/links/foo.html', + '/markdown/toc.html', + '/markdown/vue-components.html', '/page-data/frontmatter.html', '/page-data/headers.html', '/page-data/lang.html', From 2d5bac1133e4c46d010faf1226c7d4e57954937c Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Fri, 29 Dec 2023 15:31:24 +0800 Subject: [PATCH 07/31] chore: update e2e test --- e2e/docs/router.md | 38 ----------------- e2e/docs/routes/get-pages-path.md | 13 ++++++ e2e/docs/routes/is-page-exist.md | 18 ++++++++ e2e/docs/routes/resolve.md | 9 ++++ e2e/tests/router.cy.ts | 61 --------------------------- e2e/tests/routes/get-pages-path.cy.ts | 45 ++++++++++++++++++++ e2e/tests/routes/is-page-exisit.cy.ts | 22 ++++++++++ e2e/tests/routes/resolve.cy.ts | 8 ++++ 8 files changed, 115 insertions(+), 99 deletions(-) delete mode 100644 e2e/docs/router.md create mode 100644 e2e/docs/routes/get-pages-path.md create mode 100644 e2e/docs/routes/is-page-exist.md create mode 100644 e2e/docs/routes/resolve.md delete mode 100644 e2e/tests/router.cy.ts create mode 100644 e2e/tests/routes/get-pages-path.cy.ts create mode 100644 e2e/tests/routes/is-page-exisit.cy.ts create mode 100644 e2e/tests/routes/resolve.cy.ts diff --git a/e2e/docs/router.md b/e2e/docs/router.md deleted file mode 100644 index 7e3914d04c..0000000000 --- a/e2e/docs/router.md +++ /dev/null @@ -1,38 +0,0 @@ -## GetPagesPath - -
    -
  • - {{path}} -
  • -
- -## isPageExist - -
- -- /: {{isPageExist('/')}} -- /README.md: {{isPageExist('/README.md')}} -- /index.html: {{isPageExist('/index.html')}} -- /not-exist: {{isPageExist('/not-exist')}} -- /not-exist.html: {{isPageExist('/not-exist.html')}} -- /not-exist.md: {{isPageExist('/not-exist.md')}} -- /zh/: {{isPageExist('/zh/')}} -- /zh: {{isPageExist('/zh')}} - -
- -## resolve - -
- -- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} -- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} -- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} - -
- - diff --git a/e2e/docs/routes/get-pages-path.md b/e2e/docs/routes/get-pages-path.md new file mode 100644 index 0000000000..80cba289f2 --- /dev/null +++ b/e2e/docs/routes/get-pages-path.md @@ -0,0 +1,13 @@ +## GetPagesPath + +
    +
  • + {{path}} +
  • +
+ + diff --git a/e2e/docs/routes/is-page-exist.md b/e2e/docs/routes/is-page-exist.md new file mode 100644 index 0000000000..ea263787fa --- /dev/null +++ b/e2e/docs/routes/is-page-exist.md @@ -0,0 +1,18 @@ +# isPageExist + +- /: {{isPageExist('/')}} +- /README.md: {{isPageExist('/README.md')}} +- /index.html: {{isPageExist('/index.html')}} +- /not-exist: {{isPageExist('/not-exist')}} +- /not-exist.html: {{isPageExist('/not-exist.html')}} +- /not-exist.md: {{isPageExist('/not-exist.md')}} +- /routes/non-ascii-paths/中文目录名/中文文件名.md: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.md')}} +- /routes/non-ascii-paths/中文目录名/中文文件名.html: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.html')}} +- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))}} +- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))}} +- /zh/: {{isPageExist('/zh/')}} +- /zh: {{isPageExist('/zh')}} + + diff --git a/e2e/docs/routes/resolve.md b/e2e/docs/routes/resolve.md new file mode 100644 index 0000000000..0b49d02d5f --- /dev/null +++ b/e2e/docs/routes/resolve.md @@ -0,0 +1,9 @@ +## resolve + +- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} +- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} +- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} + + diff --git a/e2e/tests/router.cy.ts b/e2e/tests/router.cy.ts deleted file mode 100644 index c6046ef4cf..0000000000 --- a/e2e/tests/router.cy.ts +++ /dev/null @@ -1,61 +0,0 @@ -context('router', () => { - it('getPagesPath', () => { - cy.visit('/router.html') - - const paths = [ - '/', - '/layouts/custom-layout.html', - '/layouts/layout.html', - '/markdown/anchors.html', - '/markdown/code-blocks.html', - '/markdown/emoji.html', - '/markdown/import-code-blocks.html', - '/markdown/links/bar.html', - '/markdown/links/baz.html', - '/markdown/links/foo.html', - '/markdown/toc.html', - '/markdown/vue-components.html', - '/page-data/frontmatter.html', - '/page-data/headers.html', - '/page-data/lang.html', - '/page-data/meta.html', - '/page-data/permalink.html', - '/page-data/title-from-frontmatter.html', - '/page-data/title-from-h1.html', - '/router.html', - '/zh/', - '/404.html', - ] - - cy.get('.e2e-theme-content ul.get-pages-path li').each((el) => { - expect(paths.includes(el.text())).to.equal(true) - }) - }) - - it('isPageExist', () => { - cy.visit('/router.html') - const results = [ - '/: true', - '/README.md: true', - '/index.html: true', - '/not-exist: false', - '/not-exist.html: false', - '/not-exist.md: false', - '/zh/: true', - '/zh: false', - ] - - cy.get('.e2e-theme-content .is-page-exist ul li').each((el) => { - expect(results.includes(el.text())).to.equal(true) - }) - }) - - it('resolve', () => { - cy.visit('/router.html') - const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } - - cy.get('.e2e-theme-content .resolve ul li').each((el) => { - expect(el.text()).to.contain(JSON.stringify(result)) - }) - }) -}) diff --git a/e2e/tests/routes/get-pages-path.cy.ts b/e2e/tests/routes/get-pages-path.cy.ts new file mode 100644 index 0000000000..1021e1bbe2 --- /dev/null +++ b/e2e/tests/routes/get-pages-path.cy.ts @@ -0,0 +1,45 @@ +it('getPagesPath', () => { + cy.visit('/routes/get-pages-path.html') + + const paths = [ + '/', + // '/layouts/custom-layout.html', + // '/layouts/layout.html', + // '/markdown/anchors.html', + // '/markdown/code-blocks.html', + // '/markdown/emoji.html', + // '/markdown/import-code-blocks.html', + // '/markdown/links/bar.html', + // '/markdown/links/baz.html', + // '/markdown/links/foo.html', + // '/markdown/toc.html', + // '/markdown/vue-components.html', + // '/page-data/frontmatter.html', + // '/page-data/headers.html', + // '/page-data/lang.html', + // '/page-data/meta.html', + // '/page-data/permalink.html', + // '/page-data/title-from-frontmatter.html', + // '/page-data/title-from-h1.html', + // '/routes/non-ascii-paths/', + // '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', + // '/routes/get-pages-path.html', + // '/routes/resolve.html', + // '/routes/is-page-exist.html', + // '/zh/', + // '/404.html', + ] + + const pagePaths: string[] = [] + + // eslint-disable-next-line cypress/unsafe-to-chain-command + cy.get('.e2e-theme-content ul li') + .each((el) => { + pagePaths.push(el.text()) + }) + .then(() => { + paths.forEach((path) => { + expect(pagePaths.includes(path)).to.equal(true) + }) + }) +}) diff --git a/e2e/tests/routes/is-page-exisit.cy.ts b/e2e/tests/routes/is-page-exisit.cy.ts new file mode 100644 index 0000000000..42c5e5052d --- /dev/null +++ b/e2e/tests/routes/is-page-exisit.cy.ts @@ -0,0 +1,22 @@ +it('isPageExist', () => { + cy.visit('/routes/is-page-exist.html') + + const results = [ + '/: true', + '/README.md: true', + '/index.html: true', + '/not-exist: false', + '/not-exist.html: false', + '/not-exist.md: false', + '/routes/non-ascii-paths/中文目录名/中文文件名.md: true', + '/routes/non-ascii-paths/中文目录名/中文文件名.html: true', + `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}: true`, + `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}: true`, + '/zh/: true', + '/zh: false', + ] + + cy.get('.e2e-theme-content ul li').each((el) => { + expect(results.includes(el.text())).to.equal(true) + }) +}) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts new file mode 100644 index 0000000000..1f23ed8665 --- /dev/null +++ b/e2e/tests/routes/resolve.cy.ts @@ -0,0 +1,8 @@ +it('resolve', () => { + cy.visit('/routes/resolve.html') + const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } + + cy.get('.e2e-theme-content ul li').each((el) => { + expect(el.text()).to.contain(JSON.stringify(result)) + }) +}) From ca01f9ce2b6f3223cb3161b7a9973baa15ad0f73 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Fri, 29 Dec 2023 16:34:31 +0800 Subject: [PATCH 08/31] test: fix tests --- e2e/tests/routes/resolve.cy.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts index 1f23ed8665..9f5e6b1812 100644 --- a/e2e/tests/routes/resolve.cy.ts +++ b/e2e/tests/routes/resolve.cy.ts @@ -3,6 +3,8 @@ it('resolve', () => { const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } cy.get('.e2e-theme-content ul li').each((el) => { - expect(el.text()).to.contain(JSON.stringify(result)) + expect(JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1])).to.deep.include( + result, + ) }) }) From a6fe43dfac3c3fa7e54fb99261d055fdb8e51f41 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Mon, 8 Jan 2024 00:31:23 +0800 Subject: [PATCH 09/31] refactor: remove extra methods --- e2e/docs/routes/get-pages-path.md | 13 ------ e2e/docs/routes/is-page-exist.md | 18 -------- e2e/docs/routes/resolve.md | 9 ---- e2e/tests/routes/get-pages-path.cy.ts | 45 -------------------- e2e/tests/routes/is-page-exisit.cy.ts | 22 ---------- e2e/tests/routes/resolve.cy.ts | 10 ----- packages/client/src/components/Content.ts | 4 +- packages/client/src/components/VPLink.ts | 30 +++++++++++-- packages/client/src/helpers/index.ts | 1 - packages/client/src/helpers/router.ts | 38 ----------------- packages/client/src/utils/index.ts | 1 - packages/client/src/utils/routeGuardEvent.ts | 19 --------- 12 files changed, 28 insertions(+), 182 deletions(-) delete mode 100644 e2e/docs/routes/get-pages-path.md delete mode 100644 e2e/docs/routes/is-page-exist.md delete mode 100644 e2e/docs/routes/resolve.md delete mode 100644 e2e/tests/routes/get-pages-path.cy.ts delete mode 100644 e2e/tests/routes/is-page-exisit.cy.ts delete mode 100644 e2e/tests/routes/resolve.cy.ts delete mode 100644 packages/client/src/helpers/router.ts delete mode 100644 packages/client/src/utils/index.ts delete mode 100644 packages/client/src/utils/routeGuardEvent.ts diff --git a/e2e/docs/routes/get-pages-path.md b/e2e/docs/routes/get-pages-path.md deleted file mode 100644 index 80cba289f2..0000000000 --- a/e2e/docs/routes/get-pages-path.md +++ /dev/null @@ -1,13 +0,0 @@ -## GetPagesPath - -
    -
  • - {{path}} -
  • -
- - diff --git a/e2e/docs/routes/is-page-exist.md b/e2e/docs/routes/is-page-exist.md deleted file mode 100644 index ea263787fa..0000000000 --- a/e2e/docs/routes/is-page-exist.md +++ /dev/null @@ -1,18 +0,0 @@ -# isPageExist - -- /: {{isPageExist('/')}} -- /README.md: {{isPageExist('/README.md')}} -- /index.html: {{isPageExist('/index.html')}} -- /not-exist: {{isPageExist('/not-exist')}} -- /not-exist.html: {{isPageExist('/not-exist.html')}} -- /not-exist.md: {{isPageExist('/not-exist.md')}} -- /routes/non-ascii-paths/中文目录名/中文文件名.md: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.md')}} -- /routes/non-ascii-paths/中文目录名/中文文件名.html: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.html')}} -- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))}} -- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))}} -- /zh/: {{isPageExist('/zh/')}} -- /zh: {{isPageExist('/zh')}} - - diff --git a/e2e/docs/routes/resolve.md b/e2e/docs/routes/resolve.md deleted file mode 100644 index 0b49d02d5f..0000000000 --- a/e2e/docs/routes/resolve.md +++ /dev/null @@ -1,9 +0,0 @@ -## resolve - -- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} -- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} -- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} - - diff --git a/e2e/tests/routes/get-pages-path.cy.ts b/e2e/tests/routes/get-pages-path.cy.ts deleted file mode 100644 index 1021e1bbe2..0000000000 --- a/e2e/tests/routes/get-pages-path.cy.ts +++ /dev/null @@ -1,45 +0,0 @@ -it('getPagesPath', () => { - cy.visit('/routes/get-pages-path.html') - - const paths = [ - '/', - // '/layouts/custom-layout.html', - // '/layouts/layout.html', - // '/markdown/anchors.html', - // '/markdown/code-blocks.html', - // '/markdown/emoji.html', - // '/markdown/import-code-blocks.html', - // '/markdown/links/bar.html', - // '/markdown/links/baz.html', - // '/markdown/links/foo.html', - // '/markdown/toc.html', - // '/markdown/vue-components.html', - // '/page-data/frontmatter.html', - // '/page-data/headers.html', - // '/page-data/lang.html', - // '/page-data/meta.html', - // '/page-data/permalink.html', - // '/page-data/title-from-frontmatter.html', - // '/page-data/title-from-h1.html', - // '/routes/non-ascii-paths/', - // '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', - // '/routes/get-pages-path.html', - // '/routes/resolve.html', - // '/routes/is-page-exist.html', - // '/zh/', - // '/404.html', - ] - - const pagePaths: string[] = [] - - // eslint-disable-next-line cypress/unsafe-to-chain-command - cy.get('.e2e-theme-content ul li') - .each((el) => { - pagePaths.push(el.text()) - }) - .then(() => { - paths.forEach((path) => { - expect(pagePaths.includes(path)).to.equal(true) - }) - }) -}) diff --git a/e2e/tests/routes/is-page-exisit.cy.ts b/e2e/tests/routes/is-page-exisit.cy.ts deleted file mode 100644 index 42c5e5052d..0000000000 --- a/e2e/tests/routes/is-page-exisit.cy.ts +++ /dev/null @@ -1,22 +0,0 @@ -it('isPageExist', () => { - cy.visit('/routes/is-page-exist.html') - - const results = [ - '/: true', - '/README.md: true', - '/index.html: true', - '/not-exist: false', - '/not-exist.html: false', - '/not-exist.md: false', - '/routes/non-ascii-paths/中文目录名/中文文件名.md: true', - '/routes/non-ascii-paths/中文目录名/中文文件名.html: true', - `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}: true`, - `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}: true`, - '/zh/: true', - '/zh: false', - ] - - cy.get('.e2e-theme-content ul li').each((el) => { - expect(results.includes(el.text())).to.equal(true) - }) -}) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts deleted file mode 100644 index 9f5e6b1812..0000000000 --- a/e2e/tests/routes/resolve.cy.ts +++ /dev/null @@ -1,10 +0,0 @@ -it('resolve', () => { - cy.visit('/routes/resolve.html') - const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } - - cy.get('.e2e-theme-content ul li').each((el) => { - expect(JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1])).to.deep.include( - result, - ) - }) -}) diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index ca3449a848..7056198558 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -9,7 +9,7 @@ export const Content = defineComponent({ name: 'Content', props: { - url: { + path: { type: String, required: false, default: '', @@ -19,7 +19,7 @@ export const Content = defineComponent({ setup(props) { const page = usePageData() const pageComponent = computed( - () => pagesMap.value.get(props.url || page.value.path)?.comp, + () => pagesMap.value.get(props.path || page.value.path)?.comp, ) return () => pageComponent.value diff --git a/packages/client/src/components/VPLink.ts b/packages/client/src/components/VPLink.ts index fdf94a7d6a..83857f950b 100644 --- a/packages/client/src/components/VPLink.ts +++ b/packages/client/src/components/VPLink.ts @@ -1,10 +1,30 @@ import { h } from 'vue' import type { FunctionalComponent, VNode } from 'vue' import { useRouter } from 'vue-router' -import { resolve, withBase } from '../helpers/index.js' -import { guardEvent } from '../utils/index.js' +import { pagesMap, redirectsMap } from '../composables/index.js' +import { withBase } from '../helpers/index.js' +import { resolvers } from '../resolvers.js' -export interface VPLinkProps { +/** + * Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 + */ +const guardEvent = (event: MouseEvent): boolean | void => { + // don't redirect with control keys + if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return + // don't redirect when preventDefault called + if (event.defaultPrevented) return + // don't redirect on right click + if (event.button !== undefined && event.button !== 0) return + // don't redirect if `target="_blank"` + if (event.currentTarget) { + const target = (event.currentTarget as HTMLElement).getAttribute('target') + if (target?.match(/\b_blank\b/i)) return + } + event.preventDefault() + return true +} + +interface VPLinkProps { to: string } @@ -16,7 +36,9 @@ export const VPLink: FunctionalComponent< } > = ({ to = '' }, { slots }) => { const router = useRouter() - const path = withBase(resolve(to).path) + const path = withBase( + resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, to), + ) return h( 'a', diff --git a/packages/client/src/helpers/index.ts b/packages/client/src/helpers/index.ts index 1be02a4358..e5726afa68 100644 --- a/packages/client/src/helpers/index.ts +++ b/packages/client/src/helpers/index.ts @@ -1,3 +1,2 @@ export * from './defineClientConfig.js' -export * from './router.js' export * from './withBase.js' diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts deleted file mode 100644 index 25d86244e7..0000000000 --- a/packages/client/src/helpers/router.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { PagesMap } from '@internal/pagesMap' -import { pagesMap, redirectsMap } from '../composables/index.js' -import { resolvers } from '../resolvers.js' - -/** - * Get all paths of pages - * - * @returns all paths of pages - */ -export const getPagesPath = (): string[] => Array.from(pagesMap.value.keys()) - -/** - * Check whether the page exists - * - * @param path path of the page - * @returns whether the page exists - */ -export const isPageExist = (path: string): boolean => - pagesMap.value.has( - resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path), - ) - -/** - * Resolve final path and meta with given path - * - * @param path path of the page - * @returns resolved path and meta - */ -export const resolve = ( - path: string, -): { path: string; meta: PageMeta | null } => { - path = resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) - - return { - path, - meta: (pagesMap.value as PagesMap).get(path)?.meta ?? null, - } -} diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts deleted file mode 100644 index e1ca609036..0000000000 --- a/packages/client/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './routeGuardEvent.js' diff --git a/packages/client/src/utils/routeGuardEvent.ts b/packages/client/src/utils/routeGuardEvent.ts deleted file mode 100644 index c7b704c3e6..0000000000 --- a/packages/client/src/utils/routeGuardEvent.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copied from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 -export const guardEvent = (event: MouseEvent): boolean | void => { - // don't redirect with control keys - if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return - // don't redirect when preventDefault called - if (event.defaultPrevented) return - // don't redirect on right click - if (event.button !== undefined && event.button !== 0) return - // don't redirect if `target="_blank"` - if (event.currentTarget) { - const target = (event.currentTarget as HTMLElement).getAttribute('target') - - if (target?.match(/\b_blank\b/i)) return - } - - event.preventDefault() - - return true -} From 46bba074e811aaa39f3f771030e2cea7150a82be Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Mon, 8 Jan 2024 00:47:26 +0800 Subject: [PATCH 10/31] chore: tweaks --- packages/core/src/app/prepare/index.ts | 2 +- .../{preparePageMap.ts => preparePagesMap.ts} | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) rename packages/core/src/app/prepare/{preparePageMap.ts => preparePagesMap.ts} (92%) diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index ff75baf474..a008edef46 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,5 +1,5 @@ export * from './prepareClientConfigs.js' export * from './preparePageComponent.js' export * from './preparePageData.js' -export * from './preparePageMap.js' +export * from './preparePagesMap.js' export * from './prepareSiteData.js' diff --git a/packages/core/src/app/prepare/preparePageMap.ts b/packages/core/src/app/prepare/preparePagesMap.ts similarity index 92% rename from packages/core/src/app/prepare/preparePageMap.ts rename to packages/core/src/app/prepare/preparePagesMap.ts index 7e6354bcf7..5d4ba02657 100644 --- a/packages/core/src/app/prepare/preparePageMap.ts +++ b/packages/core/src/app/prepare/preparePagesMap.ts @@ -55,7 +55,7 @@ const resolvePageRedirects = ({ * Generate page map temp file */ export const preparePagesMap = async (app: App): Promise => { - const pageMapEntries = app.pages + const pagesMapEntries = app.pages .map((page) => { const { meta, @@ -67,7 +67,7 @@ export const preparePagesMap = async (app: App): Promise => { } = page const redirects = resolvePageRedirects(page) - return ` + return `\ [${JSON.stringify(path)}, defineAsyncComponent(() => import(${ componentFileChunkName ? `/* webpackChunkName: "${componentFileChunkName}" */` @@ -82,21 +82,23 @@ export const preparePagesMap = async (app: App): Promise => { : '' }],` }) - .join('\n ') + .join('\n') // generate page component map file let content = `\ import { defineAsyncComponent } from 'vue' -const pageMapEntries = [${pageMapEntries}]; +const pagesMapEntries = [ +${pagesMapEntries} +]; export const redirectsMap = new Map( - pageMapEntries + pagesMapEntries .flatMap(([path, , , , redirects = []]) => redirects.map((redirect) => [redirect, path])), ); export const pagesMap = new Map( - pageMapEntries.map(([path, comp, data, meta]) => [path, { comp, data, meta }]), + pagesMapEntries.map(([path, comp, data, meta]) => [path, { comp, data, meta }]), ); ` From 880ccba87400e298560041da6fa0b8a5f28e4370 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Wed, 24 Jan 2024 13:08:46 +0800 Subject: [PATCH 11/31] revert: "refactor: remove extra methods" This reverts commit a6fe43dfac3c3fa7e54fb99261d055fdb8e51f41. --- e2e/docs/routes/get-pages-path.md | 13 +++++++ e2e/docs/routes/is-page-exist.md | 18 ++++++++++ e2e/docs/routes/resolve.md | 9 +++++ e2e/tests/routes/get-pages-path.cy.ts | 45 ++++++++++++++++++++++++ e2e/tests/routes/is-page-exisit.cy.ts | 22 ++++++++++++ e2e/tests/routes/resolve.cy.ts | 10 ++++++ packages/client/src/components/VPLink.ts | 10 ++---- packages/client/src/helpers/index.ts | 1 + packages/client/src/helpers/router.ts | 38 ++++++++++++++++++++ 9 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 e2e/docs/routes/get-pages-path.md create mode 100644 e2e/docs/routes/is-page-exist.md create mode 100644 e2e/docs/routes/resolve.md create mode 100644 e2e/tests/routes/get-pages-path.cy.ts create mode 100644 e2e/tests/routes/is-page-exisit.cy.ts create mode 100644 e2e/tests/routes/resolve.cy.ts create mode 100644 packages/client/src/helpers/router.ts diff --git a/e2e/docs/routes/get-pages-path.md b/e2e/docs/routes/get-pages-path.md new file mode 100644 index 0000000000..80cba289f2 --- /dev/null +++ b/e2e/docs/routes/get-pages-path.md @@ -0,0 +1,13 @@ +## GetPagesPath + +
    +
  • + {{path}} +
  • +
+ + diff --git a/e2e/docs/routes/is-page-exist.md b/e2e/docs/routes/is-page-exist.md new file mode 100644 index 0000000000..ea263787fa --- /dev/null +++ b/e2e/docs/routes/is-page-exist.md @@ -0,0 +1,18 @@ +# isPageExist + +- /: {{isPageExist('/')}} +- /README.md: {{isPageExist('/README.md')}} +- /index.html: {{isPageExist('/index.html')}} +- /not-exist: {{isPageExist('/not-exist')}} +- /not-exist.html: {{isPageExist('/not-exist.html')}} +- /not-exist.md: {{isPageExist('/not-exist.md')}} +- /routes/non-ascii-paths/中文目录名/中文文件名.md: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.md')}} +- /routes/non-ascii-paths/中文目录名/中文文件名.html: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.html')}} +- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))}} +- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))}} +- /zh/: {{isPageExist('/zh/')}} +- /zh: {{isPageExist('/zh')}} + + diff --git a/e2e/docs/routes/resolve.md b/e2e/docs/routes/resolve.md new file mode 100644 index 0000000000..0b49d02d5f --- /dev/null +++ b/e2e/docs/routes/resolve.md @@ -0,0 +1,9 @@ +## resolve + +- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} +- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} +- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} + + diff --git a/e2e/tests/routes/get-pages-path.cy.ts b/e2e/tests/routes/get-pages-path.cy.ts new file mode 100644 index 0000000000..1021e1bbe2 --- /dev/null +++ b/e2e/tests/routes/get-pages-path.cy.ts @@ -0,0 +1,45 @@ +it('getPagesPath', () => { + cy.visit('/routes/get-pages-path.html') + + const paths = [ + '/', + // '/layouts/custom-layout.html', + // '/layouts/layout.html', + // '/markdown/anchors.html', + // '/markdown/code-blocks.html', + // '/markdown/emoji.html', + // '/markdown/import-code-blocks.html', + // '/markdown/links/bar.html', + // '/markdown/links/baz.html', + // '/markdown/links/foo.html', + // '/markdown/toc.html', + // '/markdown/vue-components.html', + // '/page-data/frontmatter.html', + // '/page-data/headers.html', + // '/page-data/lang.html', + // '/page-data/meta.html', + // '/page-data/permalink.html', + // '/page-data/title-from-frontmatter.html', + // '/page-data/title-from-h1.html', + // '/routes/non-ascii-paths/', + // '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', + // '/routes/get-pages-path.html', + // '/routes/resolve.html', + // '/routes/is-page-exist.html', + // '/zh/', + // '/404.html', + ] + + const pagePaths: string[] = [] + + // eslint-disable-next-line cypress/unsafe-to-chain-command + cy.get('.e2e-theme-content ul li') + .each((el) => { + pagePaths.push(el.text()) + }) + .then(() => { + paths.forEach((path) => { + expect(pagePaths.includes(path)).to.equal(true) + }) + }) +}) diff --git a/e2e/tests/routes/is-page-exisit.cy.ts b/e2e/tests/routes/is-page-exisit.cy.ts new file mode 100644 index 0000000000..42c5e5052d --- /dev/null +++ b/e2e/tests/routes/is-page-exisit.cy.ts @@ -0,0 +1,22 @@ +it('isPageExist', () => { + cy.visit('/routes/is-page-exist.html') + + const results = [ + '/: true', + '/README.md: true', + '/index.html: true', + '/not-exist: false', + '/not-exist.html: false', + '/not-exist.md: false', + '/routes/non-ascii-paths/中文目录名/中文文件名.md: true', + '/routes/non-ascii-paths/中文目录名/中文文件名.html: true', + `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}: true`, + `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}: true`, + '/zh/: true', + '/zh: false', + ] + + cy.get('.e2e-theme-content ul li').each((el) => { + expect(results.includes(el.text())).to.equal(true) + }) +}) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts new file mode 100644 index 0000000000..9f5e6b1812 --- /dev/null +++ b/e2e/tests/routes/resolve.cy.ts @@ -0,0 +1,10 @@ +it('resolve', () => { + cy.visit('/routes/resolve.html') + const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } + + cy.get('.e2e-theme-content ul li').each((el) => { + expect(JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1])).to.deep.include( + result, + ) + }) +}) diff --git a/packages/client/src/components/VPLink.ts b/packages/client/src/components/VPLink.ts index 83857f950b..9b83516062 100644 --- a/packages/client/src/components/VPLink.ts +++ b/packages/client/src/components/VPLink.ts @@ -1,9 +1,7 @@ import { h } from 'vue' import type { FunctionalComponent, VNode } from 'vue' import { useRouter } from 'vue-router' -import { pagesMap, redirectsMap } from '../composables/index.js' -import { withBase } from '../helpers/index.js' -import { resolvers } from '../resolvers.js' +import { resolve, withBase } from '../helpers/index.js' /** * Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 @@ -24,7 +22,7 @@ const guardEvent = (event: MouseEvent): boolean | void => { return true } -interface VPLinkProps { +export interface VPLinkProps { to: string } @@ -36,9 +34,7 @@ export const VPLink: FunctionalComponent< } > = ({ to = '' }, { slots }) => { const router = useRouter() - const path = withBase( - resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, to), - ) + const path = withBase(resolve(to).path) return h( 'a', diff --git a/packages/client/src/helpers/index.ts b/packages/client/src/helpers/index.ts index e5726afa68..1be02a4358 100644 --- a/packages/client/src/helpers/index.ts +++ b/packages/client/src/helpers/index.ts @@ -1,2 +1,3 @@ export * from './defineClientConfig.js' +export * from './router.js' export * from './withBase.js' diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts new file mode 100644 index 0000000000..25d86244e7 --- /dev/null +++ b/packages/client/src/helpers/router.ts @@ -0,0 +1,38 @@ +import type { PagesMap } from '@internal/pagesMap' +import { pagesMap, redirectsMap } from '../composables/index.js' +import { resolvers } from '../resolvers.js' + +/** + * Get all paths of pages + * + * @returns all paths of pages + */ +export const getPagesPath = (): string[] => Array.from(pagesMap.value.keys()) + +/** + * Check whether the page exists + * + * @param path path of the page + * @returns whether the page exists + */ +export const isPageExist = (path: string): boolean => + pagesMap.value.has( + resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path), + ) + +/** + * Resolve final path and meta with given path + * + * @param path path of the page + * @returns resolved path and meta + */ +export const resolve = ( + path: string, +): { path: string; meta: PageMeta | null } => { + path = resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) + + return { + path, + meta: (pagesMap.value as PagesMap).get(path)?.meta ?? null, + } +} From 1f83964265f0693fcd21e8e17109c9ff28e98660 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Wed, 24 Jan 2024 13:11:47 +0800 Subject: [PATCH 12/31] test: fix e2e test --- e2e/tests/routes/get-pages-path.cy.ts | 50 +++++++++++++-------------- e2e/tests/routes/resolve.cy.ts | 6 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/e2e/tests/routes/get-pages-path.cy.ts b/e2e/tests/routes/get-pages-path.cy.ts index 1021e1bbe2..8359a0354d 100644 --- a/e2e/tests/routes/get-pages-path.cy.ts +++ b/e2e/tests/routes/get-pages-path.cy.ts @@ -3,31 +3,31 @@ it('getPagesPath', () => { const paths = [ '/', - // '/layouts/custom-layout.html', - // '/layouts/layout.html', - // '/markdown/anchors.html', - // '/markdown/code-blocks.html', - // '/markdown/emoji.html', - // '/markdown/import-code-blocks.html', - // '/markdown/links/bar.html', - // '/markdown/links/baz.html', - // '/markdown/links/foo.html', - // '/markdown/toc.html', - // '/markdown/vue-components.html', - // '/page-data/frontmatter.html', - // '/page-data/headers.html', - // '/page-data/lang.html', - // '/page-data/meta.html', - // '/page-data/permalink.html', - // '/page-data/title-from-frontmatter.html', - // '/page-data/title-from-h1.html', - // '/routes/non-ascii-paths/', - // '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', - // '/routes/get-pages-path.html', - // '/routes/resolve.html', - // '/routes/is-page-exist.html', - // '/zh/', - // '/404.html', + '/layouts/custom-layout.html', + '/layouts/layout.html', + '/markdown/anchors.html', + '/markdown/code-blocks.html', + '/markdown/emoji.html', + '/markdown/import-code-blocks.html', + '/markdown/links/bar.html', + '/markdown/links/baz.html', + '/markdown/links/foo.html', + '/markdown/toc.html', + '/markdown/vue-components.html', + '/page-data/frontmatter.html', + '/page-data/headers.html', + '/page-data/lang.html', + '/page-data/meta.html', + '/page-data/permalink.html', + '/page-data/title-from-frontmatter.html', + '/page-data/title-from-h1.html', + '/routes/non-ascii-paths/', + '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', + '/routes/get-pages-path.html', + '/routes/resolve.html', + '/routes/is-page-exist.html', + '/zh/', + '/404.html', ] const pagePaths: string[] = [] diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts index 9f5e6b1812..38d15f0fbd 100644 --- a/e2e/tests/routes/resolve.cy.ts +++ b/e2e/tests/routes/resolve.cy.ts @@ -3,8 +3,8 @@ it('resolve', () => { const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } cy.get('.e2e-theme-content ul li').each((el) => { - expect(JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1])).to.deep.include( - result, - ) + const data = JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) + + expect(data).to.deep.contains(result) }) }) From 36e10bf2023679997c193ddc2d41857e9d59e35d Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Wed, 24 Jan 2024 14:08:38 +0800 Subject: [PATCH 13/31] feat: merge comp and data into 1 chunk --- e2e/tests/routes/resolve.cy.ts | 2 +- .../src/build/resolvePageChunkFiles.ts | 4 +-- .../cli/src/commands/dev/handlePageAdd.ts | 4 +-- .../cli/src/commands/dev/handlePageChange.ts | 4 +-- packages/client/src/components/Content.ts | 13 ++++++--- packages/client/src/resolvers.ts | 11 ++----- packages/client/src/router.ts | 8 ++--- .../client/src/types/internal/pagesMap.d.ts | 6 ++-- packages/core/src/app/appPrepare.ts | 4 +-- packages/core/src/app/prepare/index.ts | 2 +- .../{preparePageData.ts => preparePage.ts} | 14 +++++---- .../core/src/app/prepare/preparePagesMap.ts | 25 ++++------------ packages/core/src/page/createPage.ts | 12 ++++---- packages/core/src/page/index.ts | 2 +- .../core/src/page/resolvePageChunkInfo.ts | 29 +++++++++++++++++++ packages/core/src/page/resolvePageDataInfo.ts | 29 ------------------- packages/core/src/types/page.ts | 12 ++++---- packages/core/tests/page/createPage.spec.ts | 6 ++-- .../tests/page/resolvePageDataInfo.spec.ts | 10 +++---- 19 files changed, 93 insertions(+), 104 deletions(-) rename packages/core/src/app/prepare/{preparePageData.ts => preparePage.ts} (57%) create mode 100644 packages/core/src/page/resolvePageChunkInfo.ts delete mode 100644 packages/core/src/page/resolvePageDataInfo.ts diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts index 38d15f0fbd..a159508a85 100644 --- a/e2e/tests/routes/resolve.cy.ts +++ b/e2e/tests/routes/resolve.cy.ts @@ -5,6 +5,6 @@ it('resolve', () => { cy.get('.e2e-theme-content ul li').each((el) => { const data = JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) - expect(data).to.deep.contains(result) + expect(data).to.deep.include(result) }) }) diff --git a/packages/bundler-vite/src/build/resolvePageChunkFiles.ts b/packages/bundler-vite/src/build/resolvePageChunkFiles.ts index 96590994fc..a22408517c 100644 --- a/packages/bundler-vite/src/build/resolvePageChunkFiles.ts +++ b/packages/bundler-vite/src/build/resolvePageChunkFiles.ts @@ -11,9 +11,7 @@ export const resolvePageChunkFiles = ({ output .filter( (item): item is OutputChunk => - item.type === 'chunk' && - (item.facadeModuleId === page.componentFilePath || - item.facadeModuleId === page.dataFilePath), + item.type === 'chunk' && item.facadeModuleId === page.chunkFilePath, ) .flatMap(({ fileName, imports, dynamicImports }) => [ fileName, diff --git a/packages/cli/src/commands/dev/handlePageAdd.ts b/packages/cli/src/commands/dev/handlePageAdd.ts index b566a22b8c..7c4e730660 100644 --- a/packages/cli/src/commands/dev/handlePageAdd.ts +++ b/packages/cli/src/commands/dev/handlePageAdd.ts @@ -1,7 +1,7 @@ import { createPage, + preparePage, preparePageComponent, - preparePageData, preparePagesMap, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -31,7 +31,7 @@ export const handlePageAdd = async ( // prepare page files await preparePageComponent(app, page) - await preparePageData(app, page) + await preparePage(app, page) // prepare pages entry await preparePagesMap(app) diff --git a/packages/cli/src/commands/dev/handlePageChange.ts b/packages/cli/src/commands/dev/handlePageChange.ts index 5cf4d50134..10330ed423 100644 --- a/packages/cli/src/commands/dev/handlePageChange.ts +++ b/packages/cli/src/commands/dev/handlePageChange.ts @@ -1,7 +1,7 @@ import { createPage, + preparePage, preparePageComponent, - preparePageData, preparePagesMap, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -34,7 +34,7 @@ export const handlePageChange = async ( // prepare page files await preparePageComponent(app, pageNew) - await preparePageData(app, pageNew) + await preparePage(app, pageNew) const isPathChanged = pageOld.path !== pageNew.path const isMetaChanged = diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index 7056198558..0cf3150dca 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -1,4 +1,4 @@ -import { computed, defineComponent, h } from 'vue' +import { computed, defineAsyncComponent, defineComponent, h } from 'vue' import { pagesMap, usePageData } from '../composables/index.js' /** @@ -18,9 +18,14 @@ export const Content = defineComponent({ setup(props) { const page = usePageData() - const pageComponent = computed( - () => pagesMap.value.get(props.path || page.value.path)?.comp, - ) + const pageComponent = computed(() => { + const pageInfo = pagesMap.value.get(props.path || page.value.path) + + return pageInfo + ? defineAsyncComponent(() => pageInfo.v().then(({ comp }) => comp)) + : null + }) + return () => pageComponent.value ? // use page component diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index 723b6883c6..b0c3db93a2 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,4 +1,4 @@ -import type { PageInfo, PageRedirectsMap, PagesMap } from '@internal/pagesMap' +import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' import { dedupeHead, isArray, @@ -69,14 +69,9 @@ export const resolvers = reactive({ * Resolve page data according to page path and page info */ resolvePageData: async ( - pageInfo: PageInfo, + pageData: PageData, _path: string, - ): Promise => { - const pageData = await pageInfo?.data?.() - - return pageData ?? pageDataEmpty - }, - + ): Promise => Promise.resolve(pageData ?? pageDataEmpty), /** * Resolve page frontmatter from page data */ diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 3def23b0d2..0ef8800e05 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -58,10 +58,10 @@ export const createVueRouter = (): Router => { // TODO: Added for backwards compatibility, remove in stable version to.meta = pageInfo.meta - ;[to.meta._data] = await Promise.all([ - resolvers.resolvePageData(pageInfo, pagePath), - pageInfo.comp?.__asyncLoader(), - ]) + to.meta._data = await resolvers.resolvePageData( + (await pageInfo.v()).data, + pagePath, + ) } }) diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts index a567fa3586..4a5d1f2605 100644 --- a/packages/client/src/types/internal/pagesMap.d.ts +++ b/packages/client/src/types/internal/pagesMap.d.ts @@ -5,8 +5,10 @@ declare module '@internal/pagesMap' { export type PageRedirectsMap = Map export interface PageInfo> { - comp: ComponentOptions - data: () => Promise + v: () => Promise<{ + comp: ComponentOptions + data: PageData + }> meta: PageMeta } diff --git a/packages/core/src/app/appPrepare.ts b/packages/core/src/app/appPrepare.ts index 8151b8b04a..0d01ca366d 100644 --- a/packages/core/src/app/appPrepare.ts +++ b/packages/core/src/app/appPrepare.ts @@ -2,8 +2,8 @@ import { debug } from '@vuepress/utils' import type { App } from '../types/index.js' import { prepareClientConfigs, + preparePage, preparePageComponent, - preparePageData, preparePagesMap, prepareSiteData, } from './prepare/index.js' @@ -28,7 +28,7 @@ export const appPrepare = async (app: App): Promise => { // generate page data files for (const page of app.pages) { - await preparePageData(app, page) + await preparePage(app, page) } // generate pages map diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index a008edef46..22b147004f 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,5 +1,5 @@ export * from './prepareClientConfigs.js' +export * from './preparePage.js' export * from './preparePageComponent.js' -export * from './preparePageData.js' export * from './preparePagesMap.js' export * from './prepareSiteData.js' diff --git a/packages/core/src/app/prepare/preparePageData.ts b/packages/core/src/app/prepare/preparePage.ts similarity index 57% rename from packages/core/src/app/prepare/preparePageData.ts rename to packages/core/src/app/prepare/preparePage.ts index 7bf96372b4..e05a17f7df 100644 --- a/packages/core/src/app/prepare/preparePageData.ts +++ b/packages/core/src/app/prepare/preparePage.ts @@ -16,13 +16,15 @@ if (import.meta.hot) { ` /** - * Generate page data temp file of a single page + * Generate page temp file of a single page */ -export const preparePageData = async (app: App, page: Page): Promise => { +export const preparePage = async (app: App, page: Page): Promise => { // page data file content - let content = `export const data = JSON.parse(${JSON.stringify( - JSON.stringify(page.data), - )}) + 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 @@ -30,5 +32,5 @@ export const preparePageData = async (app: App, page: Page): Promise => { content += HMR_CODE } - await app.writeTemp(page.dataFilePathRelative, content) + await app.writeTemp(page.chunkFilePathRelative, content) } diff --git a/packages/core/src/app/prepare/preparePagesMap.ts b/packages/core/src/app/prepare/preparePagesMap.ts index 5d4ba02657..b82f6d1eb5 100644 --- a/packages/core/src/app/prepare/preparePagesMap.ts +++ b/packages/core/src/app/prepare/preparePagesMap.ts @@ -57,26 +57,13 @@ const resolvePageRedirects = ({ export const preparePagesMap = async (app: App): Promise => { const pagesMapEntries = app.pages .map((page) => { - const { - meta, - path, - dataFilePath, - dataFileChunkName, - componentFilePath, - componentFileChunkName, - } = page + const { meta, path, chunkFilePath, chunkName } = page const redirects = resolvePageRedirects(page) return `\ - [${JSON.stringify(path)}, defineAsyncComponent(() => import(${ - componentFileChunkName - ? `/* webpackChunkName: "${componentFileChunkName}" */` - : '' - }${JSON.stringify(componentFilePath)})), () => import(${ - dataFileChunkName ? `/* webpackChunkName: "${dataFileChunkName}" */` : '' - }${JSON.stringify(dataFilePath)}).then(({ data }) => data), ${JSON.stringify( - meta, - )}${ + [${JSON.stringify(path)}, () => import(${ + chunkName ? `/* webpackChunkName: "${chunkName}" */` : '' + }${JSON.stringify(chunkFilePath)}), ${JSON.stringify(meta)}${ redirects.length ? `, [${redirects.map((path) => JSON.stringify(path)).join(', ')}]` : '' @@ -94,11 +81,11 @@ ${pagesMapEntries} export const redirectsMap = new Map( pagesMapEntries - .flatMap(([path, , , , redirects = []]) => redirects.map((redirect) => [redirect, path])), + .flatMap(([path, , , redirects = []]) => redirects.map((redirect) => [redirect, path])), ); export const pagesMap = new Map( - pagesMapEntries.map(([path, comp, data, meta]) => [path, { comp, data, meta }]), + pagesMapEntries.map(([path, v, meta]) => [path, { v, meta }]), ); ` diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index 42f8ea20de..56f7f60601 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -1,8 +1,8 @@ import type { App, Page, PageOptions } from '../types/index.js' import { inferPagePath } from './inferPagePath.js' import { renderPageContent } from './renderPageContent.js' +import { resolvePageChunkInfo } from './resolvePageChunkInfo.js' import { resolvePageComponentInfo } from './resolvePageComponentInfo.js' -import { resolvePageDataInfo } from './resolvePageDataInfo.js' import { resolvePageDate } from './resolvePageDate.js' import { resolvePageFileContent } from './resolvePageFileContent.js' import { resolvePageFilePath } from './resolvePageFilePath.js' @@ -96,8 +96,8 @@ export const createPage = async ( key, }) - const { dataFilePath, dataFilePathRelative, dataFileChunkName } = - resolvePageDataInfo({ app, htmlFilePathRelative, key }) + const { chunkFilePath, chunkFilePathRelative, chunkName } = + resolvePageChunkInfo({ app, htmlFilePathRelative, key }) const page: Page = { // page data @@ -138,9 +138,9 @@ export const createPage = async ( componentFilePath, componentFilePathRelative, componentFileChunkName, - dataFilePath, - dataFilePathRelative, - dataFileChunkName, + chunkFilePath, + chunkFilePathRelative, + chunkName, htmlFilePath, htmlFilePathRelative, diff --git a/packages/core/src/page/index.ts b/packages/core/src/page/index.ts index 62bdf070c5..3a0d13bffe 100644 --- a/packages/core/src/page/index.ts +++ b/packages/core/src/page/index.ts @@ -1,8 +1,8 @@ export * from './createPage.js' export * from './inferPagePath.js' export * from './renderPageContent.js' +export * from './resolvePageChunkInfo.js' export * from './resolvePageComponentInfo.js' -export * from './resolvePageDataInfo.js' export * from './resolvePageDate.js' export * from './resolvePageFileContent.js' export * from './resolvePageFilePath.js' diff --git a/packages/core/src/page/resolvePageChunkInfo.ts b/packages/core/src/page/resolvePageChunkInfo.ts new file mode 100644 index 0000000000..ee0fcc18c2 --- /dev/null +++ b/packages/core/src/page/resolvePageChunkInfo.ts @@ -0,0 +1,29 @@ +import { path } from '@vuepress/utils' +import type { App } from '../types/index.js' + +/** + * Resolve page data file path + */ +export const resolvePageChunkInfo = ({ + app, + htmlFilePathRelative, + key, +}: { + app: App + htmlFilePathRelative: string + key: string +}): { + chunkFilePath: string + chunkFilePathRelative: string + chunkName: string +} => { + const chunkFilePathRelative = path.join('pages', `${htmlFilePathRelative}.js`) + const chunkFilePath = app.dir.temp(chunkFilePathRelative) + const chunkName = key + + return { + chunkFilePath, + chunkFilePathRelative, + chunkName, + } +} diff --git a/packages/core/src/page/resolvePageDataInfo.ts b/packages/core/src/page/resolvePageDataInfo.ts deleted file mode 100644 index 60a826c97f..0000000000 --- a/packages/core/src/page/resolvePageDataInfo.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { path } from '@vuepress/utils' -import type { App } from '../types/index.js' - -/** - * Resolve page data file path - */ -export const resolvePageDataInfo = ({ - app, - htmlFilePathRelative, - key, -}: { - app: App - htmlFilePathRelative: string - key: string -}): { - dataFilePath: string - dataFilePathRelative: string - dataFileChunkName: string -} => { - const dataFilePathRelative = path.join('pages', `${htmlFilePathRelative}.js`) - const dataFilePath = app.dir.temp(dataFilePathRelative) - const dataFileChunkName = key - - return { - dataFilePath, - dataFilePathRelative, - dataFileChunkName, - } -} diff --git a/packages/core/src/types/page.ts b/packages/core/src/types/page.ts index 6495a973b2..3d9565f3a5 100644 --- a/packages/core/src/types/page.ts +++ b/packages/core/src/types/page.ts @@ -121,21 +121,21 @@ export type Page< componentFileChunkName: string /** - * Page data file path + * Page file path */ - dataFilePath: string + chunkFilePath: string /** - * Page data file path relative to temp directory + * Page file path relative to temp directory */ - dataFilePathRelative: string + chunkFilePathRelative: string /** - * Page data file chunk name + * Page file chunk name * * Only take effect in webpack */ - dataFileChunkName: string + chunkName: string /** * Rendered html file path diff --git a/packages/core/tests/page/createPage.spec.ts b/packages/core/tests/page/createPage.spec.ts index 67d37ef3ee..018a88e5be 100644 --- a/packages/core/tests/page/createPage.spec.ts +++ b/packages/core/tests/page/createPage.spec.ts @@ -81,13 +81,13 @@ describe('core > page > createPage', () => { `pages/${page.htmlFilePathRelative}.vue`, ) expect(page.componentFileChunkName).toBe(page.key) - expect(page.dataFilePath).toBe( + expect(page.chunkFilePath).toBe( app.dir.temp(`pages/${page.htmlFilePathRelative}.js`), ) - expect(page.dataFilePathRelative).toBe( + expect(page.chunkFilePathRelative).toBe( `pages/${page.htmlFilePathRelative}.js`, ) - expect(page.dataFileChunkName).toBe(page.key) + expect(page.chunkName).toBe(page.key) }) it('should be extended by plugin correctly', async () => { diff --git a/packages/core/tests/page/resolvePageDataInfo.spec.ts b/packages/core/tests/page/resolvePageDataInfo.spec.ts index 6ee7a164ce..6680d6a15f 100644 --- a/packages/core/tests/page/resolvePageDataInfo.spec.ts +++ b/packages/core/tests/page/resolvePageDataInfo.spec.ts @@ -1,6 +1,6 @@ import { path } from '@vuepress/utils' import { describe, expect, it } from 'vitest' -import { createBaseApp, resolvePageDataInfo } from '../../src/index.js' +import { createBaseApp, resolvePageChunkInfo } from '../../src/index.js' const app = createBaseApp({ source: path.resolve(__dirname, 'fake-source'), @@ -14,15 +14,15 @@ describe('core > page > resolvePageDataInfo', () => { const htmlFilePathRelative = 'foobar.html' const expectedFilePath = app.dir.temp(`pages/${htmlFilePathRelative}.js`) expect( - resolvePageDataInfo({ + resolvePageChunkInfo({ app, key, htmlFilePathRelative, }), ).toEqual({ - dataFilePath: expectedFilePath, - dataFilePathRelative: path.relative(app.dir.temp(), expectedFilePath), - dataFileChunkName: key, + chunkFilePath: expectedFilePath, + chunkFilePathRelative: path.relative(app.dir.temp(), expectedFilePath), + chunkName: key, }) }) }) From 986e3296031be708b2e826b4c191caf407c395f4 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Wed, 24 Jan 2024 15:11:19 +0800 Subject: [PATCH 14/31] fix: fix e2e --- e2e/tests/routes/resolve.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts index a159508a85..fb53daca1a 100644 --- a/e2e/tests/routes/resolve.cy.ts +++ b/e2e/tests/routes/resolve.cy.ts @@ -1,10 +1,10 @@ it('resolve', () => { cy.visit('/routes/resolve.html') - const result = { path: '/page-data/meta.html', meta: { a: 0, b: 2, c: 3 } } cy.get('.e2e-theme-content ul li').each((el) => { const data = JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) - expect(data).to.deep.include(result) + expect(data.path).to.equal('/page-data/meta.html') + expect(data.meta).to.include({ a: 0, b: 2, c: 3 }) }) }) From c9fd53d3ceb168a0a49374b9f5e780304ce22771 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Wed, 24 Jan 2024 15:18:37 +0800 Subject: [PATCH 15/31] fix: add missing imports --- packages/client/src/resolvers.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index 394aa2246c..35b1104412 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,5 +1,10 @@ import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' -import { dedupeHead, isString, resolveLocalePath } from '@vuepress/shared' +import { + dedupeHead, + isString, + normalizePath, + resolveLocalePath, +} from '@vuepress/shared' import type { Component } from 'vue' import { reactive } from 'vue' import type { From 69fb0155e83c86118b57425a98c16f4b3d46daaa Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 27 Jan 2024 00:47:57 +0800 Subject: [PATCH 16/31] chore: tweak --- e2e/docs/routes/resolve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docs/routes/resolve.md b/e2e/docs/routes/resolve.md index 0b49d02d5f..ca831bcb9f 100644 --- a/e2e/docs/routes/resolve.md +++ b/e2e/docs/routes/resolve.md @@ -5,5 +5,5 @@ - Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} From 96decf07bd3fcd093dcdb41761f3fa12f90a2711 Mon Sep 17 00:00:00 2001 From: "Mr.Hope" Date: Sun, 28 Jan 2024 10:57:39 +0800 Subject: [PATCH 17/31] chore: remove componentChunkName --- packages/core/src/page/createPage.ts | 16 ++++++---------- .../core/src/page/resolvePageComponentInfo.ts | 3 --- packages/core/src/types/page.ts | 7 ------- packages/core/tests/page/createPage.spec.ts | 1 - .../tests/page/resolvePageComponentInfo.spec.ts | 1 - 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index 56f7f60601..d45d9dfbba 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -86,15 +86,12 @@ export const createPage = async ( }) // resolve page component and extract headers & links - const { - componentFilePath, - componentFilePathRelative, - componentFileChunkName, - } = await resolvePageComponentInfo({ - app, - htmlFilePathRelative, - key, - }) + const { componentFilePath, componentFilePathRelative } = + await resolvePageComponentInfo({ + app, + htmlFilePathRelative, + key, + }) const { chunkFilePath, chunkFilePathRelative, chunkName } = resolvePageChunkInfo({ app, htmlFilePathRelative, key }) @@ -137,7 +134,6 @@ export const createPage = async ( filePathRelative, componentFilePath, componentFilePathRelative, - componentFileChunkName, chunkFilePath, chunkFilePathRelative, chunkName, diff --git a/packages/core/src/page/resolvePageComponentInfo.ts b/packages/core/src/page/resolvePageComponentInfo.ts index 1fed489ad7..ff1a6b7baa 100644 --- a/packages/core/src/page/resolvePageComponentInfo.ts +++ b/packages/core/src/page/resolvePageComponentInfo.ts @@ -15,7 +15,6 @@ export const resolvePageComponentInfo = async ({ }): Promise<{ componentFilePath: string componentFilePathRelative: string - componentFileChunkName: string }> => { // resolve component file path const componentFilePathRelative = path.join( @@ -23,11 +22,9 @@ export const resolvePageComponentInfo = async ({ `${htmlFilePathRelative}.vue`, ) const componentFilePath = app.dir.temp(componentFilePathRelative) - const componentFileChunkName = key return { componentFilePath, componentFilePathRelative, - componentFileChunkName, } } diff --git a/packages/core/src/types/page.ts b/packages/core/src/types/page.ts index 3d9565f3a5..a26ef8c865 100644 --- a/packages/core/src/types/page.ts +++ b/packages/core/src/types/page.ts @@ -113,13 +113,6 @@ export type Page< */ componentFilePathRelative: string - /** - * Component file chunk name - * - * Only take effect in webpack - */ - componentFileChunkName: string - /** * Page file path */ diff --git a/packages/core/tests/page/createPage.spec.ts b/packages/core/tests/page/createPage.spec.ts index 018a88e5be..07be0b7474 100644 --- a/packages/core/tests/page/createPage.spec.ts +++ b/packages/core/tests/page/createPage.spec.ts @@ -80,7 +80,6 @@ describe('core > page > createPage', () => { expect(page.componentFilePathRelative).toBe( `pages/${page.htmlFilePathRelative}.vue`, ) - expect(page.componentFileChunkName).toBe(page.key) expect(page.chunkFilePath).toBe( app.dir.temp(`pages/${page.htmlFilePathRelative}.js`), ) diff --git a/packages/core/tests/page/resolvePageComponentInfo.spec.ts b/packages/core/tests/page/resolvePageComponentInfo.spec.ts index 5689940cbe..e1eef146eb 100644 --- a/packages/core/tests/page/resolvePageComponentInfo.spec.ts +++ b/packages/core/tests/page/resolvePageComponentInfo.spec.ts @@ -19,7 +19,6 @@ describe('core > page > resolvePageComponentInfo', () => { expect(resolved).toEqual({ componentFilePath: app.dir.temp('pages/foo.html.vue'), componentFilePathRelative: 'pages/foo.html.vue', - componentFileChunkName: 'key', }) }) }) From 92307b7e4d02e26c0cb44842567282ebb3418ce0 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Wed, 31 Jan 2024 19:54:58 +0800 Subject: [PATCH 18/31] chore: tweaks --- packages/client/src/components/Content.ts | 2 +- packages/client/src/router.ts | 2 +- packages/client/src/types/internal/pagesMap.d.ts | 2 +- packages/core/src/app/prepare/preparePagesMap.ts | 14 ++------------ 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index 0cf3150dca..7743849f33 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -22,7 +22,7 @@ export const Content = defineComponent({ const pageInfo = pagesMap.value.get(props.path || page.value.path) return pageInfo - ? defineAsyncComponent(() => pageInfo.v().then(({ comp }) => comp)) + ? defineAsyncComponent(() => pageInfo.loader().then(({ comp }) => comp)) : null }) diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 0ef8800e05..0fb305e5be 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -59,7 +59,7 @@ export const createVueRouter = (): Router => { // TODO: Added for backwards compatibility, remove in stable version to.meta = pageInfo.meta to.meta._data = await resolvers.resolvePageData( - (await pageInfo.v()).data, + (await pageInfo.loader()).data, pagePath, ) } diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts index 4a5d1f2605..4bb9699d22 100644 --- a/packages/client/src/types/internal/pagesMap.d.ts +++ b/packages/client/src/types/internal/pagesMap.d.ts @@ -5,7 +5,7 @@ declare module '@internal/pagesMap' { export type PageRedirectsMap = Map export interface PageInfo> { - v: () => Promise<{ + loader: () => Promise<{ comp: ComponentOptions data: PageData }> diff --git a/packages/core/src/app/prepare/preparePagesMap.ts b/packages/core/src/app/prepare/preparePagesMap.ts index b82f6d1eb5..9b5e8aacbe 100644 --- a/packages/core/src/app/prepare/preparePagesMap.ts +++ b/packages/core/src/app/prepare/preparePagesMap.ts @@ -59,22 +59,12 @@ export const preparePagesMap = async (app: App): Promise => { .map((page) => { const { meta, path, chunkFilePath, chunkName } = page const redirects = resolvePageRedirects(page) - - return `\ - [${JSON.stringify(path)}, () => import(${ - chunkName ? `/* webpackChunkName: "${chunkName}" */` : '' - }${JSON.stringify(chunkFilePath)}), ${JSON.stringify(meta)}${ - redirects.length - ? `, [${redirects.map((path) => JSON.stringify(path)).join(', ')}]` - : '' - }],` + return ` [${JSON.stringify(path)}, () => import(${chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''}${JSON.stringify(chunkFilePath)}), ${JSON.stringify(meta)}${redirects.length ? `, [${redirects.map((path) => JSON.stringify(path)).join(', ')}]` : ''}],` }) .join('\n') // generate page component map file let content = `\ -import { defineAsyncComponent } from 'vue' - const pagesMapEntries = [ ${pagesMapEntries} ]; @@ -85,7 +75,7 @@ export const redirectsMap = new Map( ); export const pagesMap = new Map( - pagesMapEntries.map(([path, v, meta]) => [path, { v, meta }]), + pagesMapEntries.map(([path, loader, meta]) => [path, { loader, meta }]), ); ` From c3ba0cb7b4c7a1d78f2dd525e7d110c1400c3099 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Wed, 31 Jan 2024 19:59:22 +0800 Subject: [PATCH 19/31] chore: remove page key --- packages/core/src/page/createPage.ts | 2 -- packages/shared/src/types/page.ts | 9 --------- 2 files changed, 11 deletions(-) diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index d45d9dfbba..37a76521d8 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -99,7 +99,6 @@ export const createPage = async ( const page: Page = { // page data data: { - key, path, title, lang, @@ -108,7 +107,6 @@ export const createPage = async ( }, // base fields - key, path, title, lang, diff --git a/packages/shared/src/types/page.ts b/packages/shared/src/types/page.ts index 41cffab7d8..4c3d315a8f 100644 --- a/packages/shared/src/types/page.ts +++ b/packages/shared/src/types/page.ts @@ -7,15 +7,6 @@ import type { HeadConfig } from './head.js' export interface PageBase< ExtraPageFrontmatter extends Record = Record, > { - /** - * Identifier of the page - * - * Will also be used as the component name - * - * @example 'v-foobar' - */ - key: string - /** * Route path of the page * From 45c85deb3ea2b294dd381a045109d37c565da1f6 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Wed, 31 Jan 2024 20:07:29 +0800 Subject: [PATCH 20/31] chore: remove page key related code --- packages/core/src/page/createPage.ts | 7 +------ packages/core/src/page/index.ts | 1 - packages/core/src/page/resolvePageChunkInfo.ts | 6 ++---- .../core/src/page/resolvePageComponentInfo.ts | 2 -- packages/core/src/page/resolvePageKey.ts | 7 ------- packages/core/tests/page/createPage.spec.ts | 4 +--- .../tests/page/resolvePageComponentInfo.spec.ts | 1 - .../core/tests/page/resolvePageDataInfo.spec.ts | 6 ++---- packages/core/tests/page/resolvePageKey.spec.ts | 17 ----------------- 9 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 packages/core/src/page/resolvePageKey.ts delete mode 100644 packages/core/tests/page/resolvePageKey.spec.ts diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index 37a76521d8..32b6717eed 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -7,7 +7,6 @@ import { resolvePageDate } from './resolvePageDate.js' import { resolvePageFileContent } from './resolvePageFileContent.js' import { resolvePageFilePath } from './resolvePageFilePath.js' import { resolvePageHtmlInfo } from './resolvePageHtmlInfo.js' -import { resolvePageKey } from './resolvePageKey.js' import { resolvePageLang } from './resolvePageLang.js' import { resolvePageMeta } from './resolvePageMeta.js' import { resolvePagePath } from './resolvePagePath.js' @@ -76,9 +75,6 @@ export const createPage = async ( // resolve page path const path = resolvePagePath({ permalink, pathInferred, options }) - // resolve page key - const key = resolvePageKey({ path }) - // resolve page rendered html file path const { htmlFilePath, htmlFilePathRelative } = resolvePageHtmlInfo({ app, @@ -90,11 +86,10 @@ export const createPage = async ( await resolvePageComponentInfo({ app, htmlFilePathRelative, - key, }) const { chunkFilePath, chunkFilePathRelative, chunkName } = - resolvePageChunkInfo({ app, htmlFilePathRelative, key }) + resolvePageChunkInfo({ app, htmlFilePathRelative }) const page: Page = { // page data diff --git a/packages/core/src/page/index.ts b/packages/core/src/page/index.ts index 3a0d13bffe..d7e4a9e522 100644 --- a/packages/core/src/page/index.ts +++ b/packages/core/src/page/index.ts @@ -7,7 +7,6 @@ export * from './resolvePageDate.js' export * from './resolvePageFileContent.js' export * from './resolvePageFilePath.js' export * from './resolvePageHtmlInfo.js' -export * from './resolvePageKey.js' export * from './resolvePageLang.js' export * from './resolvePagePath.js' export * from './resolvePagePermalink.js' diff --git a/packages/core/src/page/resolvePageChunkInfo.ts b/packages/core/src/page/resolvePageChunkInfo.ts index ee0fcc18c2..79cb154ce4 100644 --- a/packages/core/src/page/resolvePageChunkInfo.ts +++ b/packages/core/src/page/resolvePageChunkInfo.ts @@ -1,4 +1,4 @@ -import { path } from '@vuepress/utils' +import { hash, path } from '@vuepress/utils' import type { App } from '../types/index.js' /** @@ -7,11 +7,9 @@ import type { App } from '../types/index.js' export const resolvePageChunkInfo = ({ app, htmlFilePathRelative, - key, }: { app: App htmlFilePathRelative: string - key: string }): { chunkFilePath: string chunkFilePathRelative: string @@ -19,7 +17,7 @@ export const resolvePageChunkInfo = ({ } => { const chunkFilePathRelative = path.join('pages', `${htmlFilePathRelative}.js`) const chunkFilePath = app.dir.temp(chunkFilePathRelative) - const chunkName = key + const chunkName = `v-${hash(htmlFilePathRelative)}` return { chunkFilePath, diff --git a/packages/core/src/page/resolvePageComponentInfo.ts b/packages/core/src/page/resolvePageComponentInfo.ts index ff1a6b7baa..d27aa1933c 100644 --- a/packages/core/src/page/resolvePageComponentInfo.ts +++ b/packages/core/src/page/resolvePageComponentInfo.ts @@ -7,11 +7,9 @@ import type { App } from '../types/index.js' export const resolvePageComponentInfo = async ({ app, htmlFilePathRelative, - key, }: { app: App htmlFilePathRelative: string - key: string }): Promise<{ componentFilePath: string componentFilePathRelative: string diff --git a/packages/core/src/page/resolvePageKey.ts b/packages/core/src/page/resolvePageKey.ts deleted file mode 100644 index 859fcec450..0000000000 --- a/packages/core/src/page/resolvePageKey.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { hash } from '@vuepress/utils' - -/** - * Resolve page key to identify the page - */ -export const resolvePageKey = ({ path }: { path: string }): string => - `v-${hash(path)}` diff --git a/packages/core/tests/page/createPage.spec.ts b/packages/core/tests/page/createPage.spec.ts index 07be0b7474..e2f832b2ab 100644 --- a/packages/core/tests/page/createPage.spec.ts +++ b/packages/core/tests/page/createPage.spec.ts @@ -29,7 +29,6 @@ describe('core > page > createPage', () => { }) // page data - expect(page.data.key).toBeTruthy() expect(page.data.path).toBe('/') expect(page.data.lang).toBe('en-US') expect(page.data.title).toBe('') @@ -37,7 +36,6 @@ describe('core > page > createPage', () => { expect(page.data.headers).toEqual([]) // base fields - expect(page.key).toBeTruthy() expect(page.path).toBe('/') expect(page.lang).toBe('en-US') expect(page.title).toBe('') @@ -86,7 +84,7 @@ describe('core > page > createPage', () => { expect(page.chunkFilePathRelative).toBe( `pages/${page.htmlFilePathRelative}.js`, ) - expect(page.chunkName).toBe(page.key) + expect(page.chunkName).toBeTruthy() }) it('should be extended by plugin correctly', async () => { diff --git a/packages/core/tests/page/resolvePageComponentInfo.spec.ts b/packages/core/tests/page/resolvePageComponentInfo.spec.ts index e1eef146eb..15c9b009b9 100644 --- a/packages/core/tests/page/resolvePageComponentInfo.spec.ts +++ b/packages/core/tests/page/resolvePageComponentInfo.spec.ts @@ -13,7 +13,6 @@ describe('core > page > resolvePageComponentInfo', () => { const resolved = await resolvePageComponentInfo({ app, htmlFilePathRelative: 'foo.html', - key: 'key', }) expect(resolved).toEqual({ diff --git a/packages/core/tests/page/resolvePageDataInfo.spec.ts b/packages/core/tests/page/resolvePageDataInfo.spec.ts index 6680d6a15f..1e9cc8d662 100644 --- a/packages/core/tests/page/resolvePageDataInfo.spec.ts +++ b/packages/core/tests/page/resolvePageDataInfo.spec.ts @@ -1,4 +1,4 @@ -import { path } from '@vuepress/utils' +import { hash, path } from '@vuepress/utils' import { describe, expect, it } from 'vitest' import { createBaseApp, resolvePageChunkInfo } from '../../src/index.js' @@ -10,19 +10,17 @@ const app = createBaseApp({ describe('core > page > resolvePageDataInfo', () => { it('should resolve page data file path correctly', () => { - const key = 'foobar' const htmlFilePathRelative = 'foobar.html' const expectedFilePath = app.dir.temp(`pages/${htmlFilePathRelative}.js`) expect( resolvePageChunkInfo({ app, - key, htmlFilePathRelative, }), ).toEqual({ chunkFilePath: expectedFilePath, chunkFilePathRelative: path.relative(app.dir.temp(), expectedFilePath), - chunkName: key, + chunkName: `v-${hash(htmlFilePathRelative)}`, }) }) }) diff --git a/packages/core/tests/page/resolvePageKey.spec.ts b/packages/core/tests/page/resolvePageKey.spec.ts deleted file mode 100644 index b574908edf..0000000000 --- a/packages/core/tests/page/resolvePageKey.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolvePageKey } from '../../src/index.js' - -describe('core > page > resolvePageKey', () => { - it('should begin with "v-"', () => { - const key = resolvePageKey({ path: 'foobar' }) - - expect(key.startsWith('v-')).toBe(true) - }) - - it('should return different page key with different identifier', () => { - const keyFoo = resolvePageKey({ path: 'foo' }) - const keyBar = resolvePageKey({ path: 'bar' }) - - expect(keyFoo).not.toEqual(keyBar) - }) -}) From 10df16ca0ab15432f0d321e0806839f99567586b Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Fri, 2 Feb 2024 00:44:04 +0800 Subject: [PATCH 21/31] chore: updates --- packages/client/src/components/Content.ts | 9 ++-- packages/client/src/composables/pagesMap.ts | 14 +++---- packages/client/src/helpers/router.ts | 24 ++++++----- packages/client/src/resolvers.ts | 23 +++++----- packages/client/src/router.ts | 18 ++++---- packages/client/src/setupGlobalComputed.ts | 8 ++-- .../client/src/types/internal/pagesMap.d.ts | 21 +++++----- packages/core/src/app/prepare/preparePage.ts | 1 - .../core/src/app/prepare/preparePagesMap.ts | 42 +++++++++---------- 9 files changed, 80 insertions(+), 80 deletions(-) diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index 7743849f33..2fced932d8 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -19,10 +19,11 @@ export const Content = defineComponent({ setup(props) { const page = usePageData() const pageComponent = computed(() => { - const pageInfo = pagesMap.value.get(props.path || page.value.path) - - return pageInfo - ? defineAsyncComponent(() => pageInfo.loader().then(({ comp }) => comp)) + const pageMapItem = pagesMap.value[props.path || page.value.path] + return pageMapItem + ? defineAsyncComponent(() => + pageMapItem.loader().then(({ comp }) => comp), + ) : null }) diff --git a/packages/client/src/composables/pagesMap.ts b/packages/client/src/composables/pagesMap.ts index ebd30796d6..1991365d52 100644 --- a/packages/client/src/composables/pagesMap.ts +++ b/packages/client/src/composables/pagesMap.ts @@ -2,7 +2,7 @@ import { pagesMap as pagesMapRaw, redirectsMap as redirectsMapRaw, } from '@internal/pagesMap' -import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' +import type { PagesMap, RedirectsMap } from '@internal/pagesMap' import { shallowRef } from 'vue' import type { Ref } from 'vue' @@ -14,7 +14,7 @@ export type PagesMapRef = Ref /** * Ref wrapper of `PageRedirectMap` */ -export type PageRedirectsRef = Ref +export type RedirectsRef = Ref /** * Global pages map ref @@ -24,7 +24,7 @@ export const pagesMap: PagesMapRef = shallowRef(pagesMapRaw) /** * Global pages map ref */ -export const redirectsMap: PageRedirectsRef = shallowRef(redirectsMapRaw) +export const redirectsMap: RedirectsRef = shallowRef(redirectsMapRaw) /** * Returns the ref of pages map @@ -34,14 +34,14 @@ export const usePagesMap = (): PagesMapRef => pagesMap /** * Returns the ref of pages map */ -export const useRedirectsMap = (): PageRedirectsRef => redirectsMap +export const useRedirectsMap = (): RedirectsRef => redirectsMap if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { // reuse vue HMR runtime - __VUE_HMR_RUNTIME__.updatePageRedirectsMap = (data: PageRedirectsMap) => { - redirectsMap.value = data - } __VUE_HMR_RUNTIME__.updatePagesMap = (data: PagesMap) => { pagesMap.value = data } + __VUE_HMR_RUNTIME__.updateRedirectsMap = (data: RedirectsMap) => { + redirectsMap.value = data + } } diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts index 25d86244e7..854a15333c 100644 --- a/packages/client/src/helpers/router.ts +++ b/packages/client/src/helpers/router.ts @@ -1,4 +1,3 @@ -import type { PagesMap } from '@internal/pagesMap' import { pagesMap, redirectsMap } from '../composables/index.js' import { resolvers } from '../resolvers.js' @@ -7,7 +6,7 @@ import { resolvers } from '../resolvers.js' * * @returns all paths of pages */ -export const getPagesPath = (): string[] => Array.from(pagesMap.value.keys()) +export const getPagesPath = (): string[] => Object.keys(pagesMap.value) /** * Check whether the page exists @@ -15,10 +14,10 @@ export const getPagesPath = (): string[] => Array.from(pagesMap.value.keys()) * @param path path of the page * @returns whether the page exists */ -export const isPageExist = (path: string): boolean => - pagesMap.value.has( - resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path), - ) +export const hasPage = (path: string): boolean => + !!pagesMap.value[ + resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) + ] /** * Resolve final path and meta with given path @@ -26,13 +25,16 @@ export const isPageExist = (path: string): boolean => * @param path path of the page * @returns resolved path and meta */ -export const resolve = ( +export const resolve = | null>( path: string, ): { path: string; meta: PageMeta | null } => { - path = resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) - - return { + const resolvedPath = resolvers.resolvePagePath( + pagesMap.value, + redirectsMap.value, path, - meta: (pagesMap.value as PagesMap).get(path)?.meta ?? null, + ) + return { + path: resolvedPath, + meta: (pagesMap.value[resolvedPath]?.meta ?? null) as PageMeta, } } diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index 35b1104412..a7c83b37e4 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,4 +1,4 @@ -import type { PageRedirectsMap, PagesMap } from '@internal/pagesMap' +import type { PagesMap, RedirectsMap } from '@internal/pagesMap' import { dedupeHead, isString, @@ -42,35 +42,34 @@ export const resolvers = reactive({ /** * Resolve page info according to page path */ - resolvePagePath: ( - pagesMap: PagesMap, - redirectsMap: PageRedirectsMap, + resolvePagePath: ( + pagesMap: PagesMap, + redirectsMap: RedirectsMap, path: string, ): string => { path = normalizePath(path) // original path - if (pagesMap.has(path)) return path + if (pagesMap[path]) return path // encoded path const encodedPath = encodeURI(path) - if (pagesMap.has(encodedPath)) return encodedPath + if (pagesMap[encodedPath]) return encodedPath return ( // redirects - redirectsMap.get(path) || + redirectsMap[path] || // if no match at this point, then we should leave the path as is path ) }, /** - * Resolve page data according to page path and page info + * Resolve page data */ - resolvePageData: async ( - pageData: PageData, - _path: string, - ): Promise => Promise.resolve(pageData ?? pageDataEmpty), + resolvePageData: (pageData: PageData): Promise => + Promise.resolve(pageData ?? pageDataEmpty), + /** * Resolve page frontmatter from page data */ diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 0fb305e5be..cb6431cccb 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -52,16 +52,16 @@ export const createVueRouter = (): Router => { return pagePath } - // if no match at this point, then we should provide 404 page - const pageInfo = - pagesMap.value.get(pagePath) || pagesMap.value.get('/404.html')! + // get the target page map item or fallback to 404 page + const pageMapItem = + pagesMap.value[pagePath] || pagesMap.value['/404.html']! - // TODO: Added for backwards compatibility, remove in stable version - to.meta = pageInfo.meta - to.meta._data = await resolvers.resolvePageData( - (await pageInfo.loader()).data, - pagePath, - ) + // attach page meta to route meta + to.meta = pageMapItem.meta + + // attach page data to route meta to trigger page data computed when route changes + const pageChunk = await pageMapItem.loader() + to.meta._data = await resolvers.resolvePageData(pageChunk.data) } }) diff --git a/packages/client/src/setupGlobalComputed.ts b/packages/client/src/setupGlobalComputed.ts index cd81cba24a..5064bf2cf4 100644 --- a/packages/client/src/setupGlobalComputed.ts +++ b/packages/client/src/setupGlobalComputed.ts @@ -73,9 +73,11 @@ export const setupGlobalComputed = ( ) // handle page data HMR if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { - __VUE_HMR_RUNTIME__.updatePageData = (data: PageData) => { - pagesMap.value.get(data.path)!.data = () => Promise.resolve(data) - if (data.key === router.currentRoute.value.meta._data?.key) { + __VUE_HMR_RUNTIME__.updatePageData = async (data: PageData) => { + const pageChunk = await pagesMap.value[data.path].loader() + pagesMap.value[data.path].loader = () => + Promise.resolve({ comp: pageChunk.comp, data }) + if (data.path === router.currentRoute.value.meta._data?.path) { router.currentRoute.value.meta._data = data pageData.trigger() } diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts index 4bb9699d22..9b7eb2f23d 100644 --- a/packages/client/src/types/internal/pagesMap.d.ts +++ b/packages/client/src/types/internal/pagesMap.d.ts @@ -2,21 +2,20 @@ import type { PageData } from '@vuepress/shared' import type { ComponentOptions } from 'vue' declare module '@internal/pagesMap' { - export type PageRedirectsMap = Map + export type RedirectsMap = Record - export interface PageInfo> { - loader: () => Promise<{ - comp: ComponentOptions - data: PageData - }> + export interface PageChunk { + comp: ComponentOptions + data: PageData + } + + export interface PageMapItem> { + loader: () => Promise meta: PageMeta } - export type PagesMap> = Map< - string, - PageInfo - > + export type PagesMap = Record - export const redirectsMap: PageRedirectsMap + export const redirectsMap: RedirectsMap export const pagesMap: PagesMap } diff --git a/packages/core/src/app/prepare/preparePage.ts b/packages/core/src/app/prepare/preparePage.ts index e05a17f7df..8877e70b2f 100644 --- a/packages/core/src/app/prepare/preparePage.ts +++ b/packages/core/src/app/prepare/preparePage.ts @@ -22,7 +22,6 @@ export const preparePage = async (app: App, page: Page): Promise => { // page data file content let content = `\ import comp from ${JSON.stringify(page.componentFilePath)} - const data = JSON.parse(${JSON.stringify(JSON.stringify(page.data))}) export { comp, data } ` diff --git a/packages/core/src/app/prepare/preparePagesMap.ts b/packages/core/src/app/prepare/preparePagesMap.ts index 9b5e8aacbe..541ca3a1f6 100644 --- a/packages/core/src/app/prepare/preparePagesMap.ts +++ b/packages/core/src/app/prepare/preparePagesMap.ts @@ -7,15 +7,15 @@ if (import.meta.webpackHot) { if (__VUE_HMR_RUNTIME__.updatePagesMap) { __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) } - if (__VUE_HMR_RUNTIME__.updatePageRedirectsMap) { - __VUE_HMR_RUNTIME__.updatePageRedirectsMap(redirectsMap) + if (__VUE_HMR_RUNTIME__.updateRedirectsMap) { + __VUE_HMR_RUNTIME__.updateRedirectsMap(redirectsMap) } } if (import.meta.hot) { import.meta.hot.accept(({ pagesMap, redirectsMap }) => { __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) - __VUE_HMR_RUNTIME__.updatePageRedirectsMap(redirectsMap) + __VUE_HMR_RUNTIME__.updateRedirectsMap(redirectsMap) }) } ` @@ -55,28 +55,26 @@ const resolvePageRedirects = ({ * Generate page map temp file */ export const preparePagesMap = async (app: App): Promise => { - const pagesMapEntries = app.pages - .map((page) => { - const { meta, path, chunkFilePath, chunkName } = page - const redirects = resolvePageRedirects(page) - return ` [${JSON.stringify(path)}, () => import(${chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''}${JSON.stringify(chunkFilePath)}), ${JSON.stringify(meta)}${redirects.length ? `, [${redirects.map((path) => JSON.stringify(path)).join(', ')}]` : ''}],` - }) - .join('\n') - // generate page component map file let content = `\ -const pagesMapEntries = [ -${pagesMapEntries} -]; - -export const redirectsMap = new Map( - pagesMapEntries - .flatMap(([path, , , redirects = []]) => redirects.map((redirect) => [redirect, path])), -); +export const redirectsMap = JSON.parse(${JSON.stringify( + JSON.stringify( + Object.fromEntries( + app.pages.flatMap((page) => + resolvePageRedirects(page).map((redirect) => [redirect, page.path]), + ), + ), + ), + )}) -export const pagesMap = new Map( - pagesMapEntries.map(([path, loader, meta]) => [path, { loader, meta }]), -); +export const pagesMap = Object.fromEntries([ +${app.pages + .map( + ({ meta, path, chunkFilePath, chunkName }) => + ` [${JSON.stringify(path)}, { loader: () => import(${chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''}${JSON.stringify(chunkFilePath)}), meta: ${JSON.stringify(meta)} }],`, + ) + .join('\n')} +]); ` // inject HMR code From f3f6e9ed3f2eb734789dde58238e872612e8ea0e Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Fri, 2 Feb 2024 19:38:02 +0800 Subject: [PATCH 22/31] chore: updates --- e2e/docs/router/resolve-page.md | 43 +++++++++++++++++ e2e/docs/routes/get-pages-path.md | 13 ----- e2e/docs/routes/is-page-exist.md | 18 ------- e2e/docs/routes/resolve.md | 9 ---- e2e/tests/router/resolve-page.cy.ts | 47 +++++++++++++++++++ e2e/tests/routes/get-pages-path.cy.ts | 45 ------------------ e2e/tests/routes/is-page-exisit.cy.ts | 22 --------- e2e/tests/routes/resolve.cy.ts | 10 ---- package.json | 1 + packages/client/src/app.ts | 2 +- packages/client/src/components/Content.ts | 25 +++------- packages/client/src/components/VPLink.ts | 5 +- packages/client/src/composables/pageData.ts | 14 +----- packages/client/src/composables/pagesMap.ts | 42 ++--------------- packages/client/src/composables/siteData.ts | 4 +- .../src/{router.ts => createVueRouter.ts} | 25 ++++------ packages/client/src/helpers/index.ts | 1 - packages/client/src/helpers/router.ts | 40 ---------------- packages/client/src/index.ts | 1 + packages/client/src/resolvers.ts | 42 ++--------------- packages/client/src/router/index.ts | 3 ++ packages/client/src/router/pagesMap.ts | 35 ++++++++++++++ packages/client/src/router/resolvePage.ts | 24 ++++++++++ packages/client/src/router/resolvePagePath.ts | 18 +++++++ packages/client/src/setupGlobalComputed.ts | 2 +- .../client/src/types/internal/pagesMap.d.ts | 11 +++-- packages/core/src/app/resolveAppPages.ts | 1 + pnpm-lock.yaml | 3 ++ tsconfig.json | 2 +- 29 files changed, 213 insertions(+), 295 deletions(-) create mode 100644 e2e/docs/router/resolve-page.md delete mode 100644 e2e/docs/routes/get-pages-path.md delete mode 100644 e2e/docs/routes/is-page-exist.md delete mode 100644 e2e/docs/routes/resolve.md create mode 100644 e2e/tests/router/resolve-page.cy.ts delete mode 100644 e2e/tests/routes/get-pages-path.cy.ts delete mode 100644 e2e/tests/routes/is-page-exisit.cy.ts delete mode 100644 e2e/tests/routes/resolve.cy.ts rename packages/client/src/{router.ts => createVueRouter.ts} (72%) delete mode 100644 packages/client/src/helpers/router.ts create mode 100644 packages/client/src/router/index.ts create mode 100644 packages/client/src/router/pagesMap.ts create mode 100644 packages/client/src/router/resolvePage.ts create mode 100644 packages/client/src/router/resolvePagePath.ts diff --git a/e2e/docs/router/resolve-page.md b/e2e/docs/router/resolve-page.md new file mode 100644 index 0000000000..00b4a1b4fe --- /dev/null +++ b/e2e/docs/router/resolve-page.md @@ -0,0 +1,43 @@ +## resolve + +### Path + +#### Index + +- Clean URL: {{ JSON.stringify(resolvePage('/')) }} +- HTML: {{ JSON.stringify(resolvePage('/index.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/README.md')) }} + +#### Non-Index + +- Clean URL: {{ JSON.stringify(resolvePage('/helpers/resolve-page')) }} +- HTML: {{ JSON.stringify(resolvePage('/helpers/resolve-page.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/helpers/resolve-page.md')) }} + +#### Non-ASCII + +- Clean URL: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名')) }} +- HTML: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名.md')) }} + +#### Non-ASCII Encoded + +- Clean URL: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名'))) }} +- HTML: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))) }} +- Markdown: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))) }} + +#### Non-Existent + +- Clean URL: {{ JSON.stringify(resolvePage('/non-existent')) }} +- HTML: {{ JSON.stringify(resolvePage('/non-existent.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/non-existent.md')) }} + +### Meta + +- Clean URL: {{ JSON.stringify(resolvePage('/page-data/meta')) }} +- HTML: {{ JSON.stringify(resolvePage('/page-data/meta.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/page-data/meta.md')) }} + + diff --git a/e2e/docs/routes/get-pages-path.md b/e2e/docs/routes/get-pages-path.md deleted file mode 100644 index 80cba289f2..0000000000 --- a/e2e/docs/routes/get-pages-path.md +++ /dev/null @@ -1,13 +0,0 @@ -## GetPagesPath - -
    -
  • - {{path}} -
  • -
- - diff --git a/e2e/docs/routes/is-page-exist.md b/e2e/docs/routes/is-page-exist.md deleted file mode 100644 index ea263787fa..0000000000 --- a/e2e/docs/routes/is-page-exist.md +++ /dev/null @@ -1,18 +0,0 @@ -# isPageExist - -- /: {{isPageExist('/')}} -- /README.md: {{isPageExist('/README.md')}} -- /index.html: {{isPageExist('/index.html')}} -- /not-exist: {{isPageExist('/not-exist')}} -- /not-exist.html: {{isPageExist('/not-exist.html')}} -- /not-exist.md: {{isPageExist('/not-exist.md')}} -- /routes/non-ascii-paths/中文目录名/中文文件名.md: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.md')}} -- /routes/non-ascii-paths/中文目录名/中文文件名.html: {{isPageExist('/routes/non-ascii-paths/中文目录名/中文文件名.html')}} -- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))}} -- {{encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}}: {{isPageExist(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))}} -- /zh/: {{isPageExist('/zh/')}} -- /zh: {{isPageExist('/zh')}} - - diff --git a/e2e/docs/routes/resolve.md b/e2e/docs/routes/resolve.md deleted file mode 100644 index ca831bcb9f..0000000000 --- a/e2e/docs/routes/resolve.md +++ /dev/null @@ -1,9 +0,0 @@ -## resolve - -- Clean url: {{JSON.stringify(resolve('/page-data/meta'))}} -- HTML: {{JSON.stringify(resolve('/page-data/meta.html'))}} -- Markdown: {{JSON.stringify(resolve('/page-data/meta.md'))}} - - diff --git a/e2e/tests/router/resolve-page.cy.ts b/e2e/tests/router/resolve-page.cy.ts new file mode 100644 index 0000000000..f1dea0d66d --- /dev/null +++ b/e2e/tests/router/resolve-page.cy.ts @@ -0,0 +1,47 @@ +const parseResolvedPageFromElement = (el: Cypress.JQueryWithSelector) => + JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) + +it('should resolve paths correctly', () => { + cy.visit('/router/resolve-page.html') + + const testCases = [ + { + selector: '#index', + expectedPath: '/', + }, + { + selector: '#non-index', + expectedPath: '/helpers/resolve-page.html', + }, + { + selector: '#non-ascii', + expectedPath: '/routes/non-ascii-paths/中文目录名/中文文件名.html', + }, + { + selector: '#non-ascii-encoded', + expectedPath: '/routes/non-ascii-paths/中文目录名/中文文件名.html', + }, + ] + + testCases.forEach(({ selector, expectedPath }) => { + cy.get(`.e2e-theme-content ${selector}`).each((el) => { + const resolvedPage = parseResolvedPageFromElement(el) + expect(resolvedPage.path).to.equal(expectedPath) + }) + }) + + cy.get(`.e2e-theme-content #non-existent`).each((el) => { + const resolvedPage = parseResolvedPageFromElement(el) + expect(resolvedPage).to.equal(null) + }) +}) + +it('should resolve meta correctly', () => { + cy.visit('/router/resolve-page.html') + + cy.get('.e2e-theme-content #meta ul li').each((el) => { + const resolvedPage = parseResolvedPageFromElement(el) + expect(resolvedPage.path).to.equal('/page-data/meta.html') + expect(resolvedPage.meta).to.include({ a: 0, b: 2, c: 3 }) + }) +}) diff --git a/e2e/tests/routes/get-pages-path.cy.ts b/e2e/tests/routes/get-pages-path.cy.ts deleted file mode 100644 index 8359a0354d..0000000000 --- a/e2e/tests/routes/get-pages-path.cy.ts +++ /dev/null @@ -1,45 +0,0 @@ -it('getPagesPath', () => { - cy.visit('/routes/get-pages-path.html') - - const paths = [ - '/', - '/layouts/custom-layout.html', - '/layouts/layout.html', - '/markdown/anchors.html', - '/markdown/code-blocks.html', - '/markdown/emoji.html', - '/markdown/import-code-blocks.html', - '/markdown/links/bar.html', - '/markdown/links/baz.html', - '/markdown/links/foo.html', - '/markdown/toc.html', - '/markdown/vue-components.html', - '/page-data/frontmatter.html', - '/page-data/headers.html', - '/page-data/lang.html', - '/page-data/meta.html', - '/page-data/permalink.html', - '/page-data/title-from-frontmatter.html', - '/page-data/title-from-h1.html', - '/routes/non-ascii-paths/', - '/routes/non-ascii-paths/%E4%B8%AD%E6%96%87%E7%9B%AE%E5%BD%95%E5%90%8D/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6%E5%90%8D.html', - '/routes/get-pages-path.html', - '/routes/resolve.html', - '/routes/is-page-exist.html', - '/zh/', - '/404.html', - ] - - const pagePaths: string[] = [] - - // eslint-disable-next-line cypress/unsafe-to-chain-command - cy.get('.e2e-theme-content ul li') - .each((el) => { - pagePaths.push(el.text()) - }) - .then(() => { - paths.forEach((path) => { - expect(pagePaths.includes(path)).to.equal(true) - }) - }) -}) diff --git a/e2e/tests/routes/is-page-exisit.cy.ts b/e2e/tests/routes/is-page-exisit.cy.ts deleted file mode 100644 index 42c5e5052d..0000000000 --- a/e2e/tests/routes/is-page-exisit.cy.ts +++ /dev/null @@ -1,22 +0,0 @@ -it('isPageExist', () => { - cy.visit('/routes/is-page-exist.html') - - const results = [ - '/: true', - '/README.md: true', - '/index.html: true', - '/not-exist: false', - '/not-exist.html: false', - '/not-exist.md: false', - '/routes/non-ascii-paths/中文目录名/中文文件名.md: true', - '/routes/non-ascii-paths/中文目录名/中文文件名.html: true', - `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md')}: true`, - `${encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html')}: true`, - '/zh/: true', - '/zh: false', - ] - - cy.get('.e2e-theme-content ul li').each((el) => { - expect(results.includes(el.text())).to.equal(true) - }) -}) diff --git a/e2e/tests/routes/resolve.cy.ts b/e2e/tests/routes/resolve.cy.ts deleted file mode 100644 index fb53daca1a..0000000000 --- a/e2e/tests/routes/resolve.cy.ts +++ /dev/null @@ -1,10 +0,0 @@ -it('resolve', () => { - cy.visit('/routes/resolve.html') - - cy.get('.e2e-theme-content ul li').each((el) => { - const data = JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) - - expect(data.path).to.equal('/page-data/meta.html') - expect(data.meta).to.include({ a: 0, b: 2, c: 3 }) - }) -}) diff --git a/package.json b/package.json index 7dd5c24dde..d25291a8ee 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@vitest/coverage-istanbul": "^1.2.1", "bumpp": "^9.3.0", "conventional-changelog-cli": "^4.1.0", + "cypress": "^13.6.3", "eslint": "^8.56.0", "eslint-config-vuepress": "^4.10.0", "eslint-config-vuepress-typescript": "^4.10.0", diff --git a/packages/client/src/app.ts b/packages/client/src/app.ts index 73f53e01aa..5098bcc218 100644 --- a/packages/client/src/app.ts +++ b/packages/client/src/app.ts @@ -2,7 +2,7 @@ import { clientConfigs } from '@internal/clientConfigs' import { createApp, createSSRApp, h } from 'vue' import { RouterView } from 'vue-router' import { siteData } from './composables/index.js' -import { createVueRouter } from './router.js' +import { createVueRouter } from './createVueRouter.js' import { setupGlobalComponents } from './setupGlobalComponents.js' import { setupGlobalComputed } from './setupGlobalComputed.js' import { setupUpdateHead } from './setupUpdateHead.js' diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index 2fced932d8..e0541aa326 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -1,5 +1,6 @@ import { computed, defineAsyncComponent, defineComponent, h } from 'vue' -import { pagesMap, usePageData } from '../composables/index.js' +import { usePageData } from '../composables/index.js' +import { resolvePage } from '../router/index.js' /** * Markdown rendered content @@ -19,24 +20,12 @@ export const Content = defineComponent({ setup(props) { const page = usePageData() const pageComponent = computed(() => { - const pageMapItem = pagesMap.value[props.path || page.value.path] - return pageMapItem - ? defineAsyncComponent(() => - pageMapItem.loader().then(({ comp }) => comp), - ) - : null + const resolvedPage = resolvePage(props.path || page.value.path) + return defineAsyncComponent(() => + resolvedPage.loader().then(({ comp }) => comp), + ) }) - return () => - pageComponent.value - ? // use page component - h(pageComponent.value) - : // fallback content - h( - 'div', - __VUEPRESS_DEV__ - ? 'Page does not exist. This is a fallback content.' - : '404 Not Found', - ) + return () => h(pageComponent.value) }, }) diff --git a/packages/client/src/components/VPLink.ts b/packages/client/src/components/VPLink.ts index 9b83516062..801e23b894 100644 --- a/packages/client/src/components/VPLink.ts +++ b/packages/client/src/components/VPLink.ts @@ -1,7 +1,8 @@ import { h } from 'vue' import type { FunctionalComponent, VNode } from 'vue' import { useRouter } from 'vue-router' -import { resolve, withBase } from '../helpers/index.js' +import { withBase } from '../helpers/index.js' +import { resolvePagePath } from '../router/index.js' /** * Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 @@ -34,7 +35,7 @@ export const VPLink: FunctionalComponent< } > = ({ to = '' }, { slots }) => { const router = useRouter() - const path = withBase(resolve(to).path) + const path = withBase(resolvePagePath(to)) return h( 'a', diff --git a/packages/client/src/composables/pageData.ts b/packages/client/src/composables/pageData.ts index 3f96a00659..e9147272b2 100644 --- a/packages/client/src/composables/pageData.ts +++ b/packages/client/src/composables/pageData.ts @@ -1,6 +1,6 @@ import type { PageData } from '@vuepress/shared' import type { InjectionKey, Ref } from 'vue' -import { inject, readonly } from 'vue' +import { inject } from 'vue' export type { PageData } @@ -17,18 +17,6 @@ export const pageDataSymbol: InjectionKey = Symbol( __VUEPRESS_DEV__ ? 'pageData' : '', ) -/** - * Empty page data to be used as the fallback value - */ -export const pageDataEmpty = readonly({ - key: '', - path: '', - title: '', - lang: '', - frontmatter: {}, - headers: [], -} as PageData) as PageData - /** * Returns the ref of the data of current page */ diff --git a/packages/client/src/composables/pagesMap.ts b/packages/client/src/composables/pagesMap.ts index 1991365d52..d474ab0dce 100644 --- a/packages/client/src/composables/pagesMap.ts +++ b/packages/client/src/composables/pagesMap.ts @@ -1,47 +1,11 @@ -import { - pagesMap as pagesMapRaw, - redirectsMap as redirectsMapRaw, -} from '@internal/pagesMap' -import type { PagesMap, RedirectsMap } from '@internal/pagesMap' -import { shallowRef } from 'vue' -import type { Ref } from 'vue' - -/** - * Ref wrapper of `PagesMap` - */ -export type PagesMapRef = Ref - -/** - * Ref wrapper of `PageRedirectMap` - */ -export type RedirectsRef = Ref - -/** - * Global pages map ref - */ -export const pagesMap: PagesMapRef = shallowRef(pagesMapRaw) - -/** - * Global pages map ref - */ -export const redirectsMap: RedirectsRef = shallowRef(redirectsMapRaw) +import { pagesMap, redirectsMap } from '../router' /** * Returns the ref of pages map */ -export const usePagesMap = (): PagesMapRef => pagesMap +export const usePagesMap = (): typeof pagesMap => pagesMap /** * Returns the ref of pages map */ -export const useRedirectsMap = (): RedirectsRef => redirectsMap - -if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { - // reuse vue HMR runtime - __VUE_HMR_RUNTIME__.updatePagesMap = (data: PagesMap) => { - pagesMap.value = data - } - __VUE_HMR_RUNTIME__.updateRedirectsMap = (data: RedirectsMap) => { - redirectsMap.value = data - } -} +export const useRedirectsMap = (): typeof redirectsMap => redirectsMap diff --git a/packages/client/src/composables/siteData.ts b/packages/client/src/composables/siteData.ts index 89a16b47b8..f070e48713 100644 --- a/packages/client/src/composables/siteData.ts +++ b/packages/client/src/composables/siteData.ts @@ -1,6 +1,6 @@ import { siteData as siteDataRaw } from '@internal/siteData' import type { SiteData } from '@vuepress/shared' -import { ref } from 'vue' +import { shallowRef } from 'vue' import type { Ref } from 'vue' export type { SiteData } @@ -13,7 +13,7 @@ export type SiteDataRef = Ref /** * Global site data ref */ -export const siteData: SiteDataRef = ref(siteDataRaw) +export const siteData: SiteDataRef = shallowRef(siteDataRaw) /** * Returns the ref of the site data diff --git a/packages/client/src/router.ts b/packages/client/src/createVueRouter.ts similarity index 72% rename from packages/client/src/router.ts rename to packages/client/src/createVueRouter.ts index cb6431cccb..3957723573 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/createVueRouter.ts @@ -7,9 +7,8 @@ import { START_LOCATION, } from 'vue-router' import { Vuepress } from './components/Vuepress.js' -import { pagesMap, redirectsMap } from './composables/index.js' import type { PageData } from './composables/index.js' -import { resolvers } from './resolvers.js' +import { resolvePage } from './router/index.js' /** * - use `createWebHistory` in dev mode and build mode client bundle @@ -42,26 +41,18 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const pagePath = resolvers.resolvePagePath( - pagesMap.value, - redirectsMap.value, - to.path, - ) + const resolvedPage = resolvePage(to.path) - if (pagePath !== to.path) { - return pagePath + if (resolvedPage.path !== to.path) { + return resolvedPage.path } - // get the target page map item or fallback to 404 page - const pageMapItem = - pagesMap.value[pagePath] || pagesMap.value['/404.html']! - // attach page meta to route meta - to.meta = pageMapItem.meta + to.meta = resolvedPage.meta // attach page data to route meta to trigger page data computed when route changes - const pageChunk = await pageMapItem.loader() - to.meta._data = await resolvers.resolvePageData(pageChunk.data) + const pageChunk = await resolvedPage.loader() + to.meta._data = pageChunk.data } }) @@ -71,7 +62,7 @@ export const createVueRouter = (): Router => { declare module 'vue-router' { interface RouteMeta { /** - * Store page data to route meta + * Store page data in route meta * * @internal only for internal use */ diff --git a/packages/client/src/helpers/index.ts b/packages/client/src/helpers/index.ts index 1be02a4358..e5726afa68 100644 --- a/packages/client/src/helpers/index.ts +++ b/packages/client/src/helpers/index.ts @@ -1,3 +1,2 @@ export * from './defineClientConfig.js' -export * from './router.js' export * from './withBase.js' diff --git a/packages/client/src/helpers/router.ts b/packages/client/src/helpers/router.ts deleted file mode 100644 index 854a15333c..0000000000 --- a/packages/client/src/helpers/router.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { pagesMap, redirectsMap } from '../composables/index.js' -import { resolvers } from '../resolvers.js' - -/** - * Get all paths of pages - * - * @returns all paths of pages - */ -export const getPagesPath = (): string[] => Object.keys(pagesMap.value) - -/** - * Check whether the page exists - * - * @param path path of the page - * @returns whether the page exists - */ -export const hasPage = (path: string): boolean => - !!pagesMap.value[ - resolvers.resolvePagePath(pagesMap.value, redirectsMap.value, path) - ] - -/** - * Resolve final path and meta with given path - * - * @param path path of the page - * @returns resolved path and meta - */ -export const resolve = | null>( - path: string, -): { path: string; meta: PageMeta | null } => { - const resolvedPath = resolvers.resolvePagePath( - pagesMap.value, - redirectsMap.value, - path, - ) - return { - path: resolvedPath, - meta: (pagesMap.value[resolvedPath]?.meta ?? null) as PageMeta, - } -} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 88174b4ff8..ee9dd3a3df 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -3,5 +3,6 @@ export type { PageHeader } from '@vuepress/shared' export * from './components/index.js' export * from './composables/index.js' export * from './helpers/index.js' +export * from './router/index.js' export * from './resolvers.js' export * from './types/index.js' diff --git a/packages/client/src/resolvers.ts b/packages/client/src/resolvers.ts index a7c83b37e4..fe1c30e12c 100644 --- a/packages/client/src/resolvers.ts +++ b/packages/client/src/resolvers.ts @@ -1,10 +1,4 @@ -import type { PagesMap, RedirectsMap } from '@internal/pagesMap' -import { - dedupeHead, - isString, - normalizePath, - resolveLocalePath, -} from '@vuepress/shared' +import { dedupeHead, isString, resolveLocalePath } from '@vuepress/shared' import type { Component } from 'vue' import { reactive } from 'vue' import type { @@ -17,7 +11,6 @@ import type { SiteData, SiteLocaleData, } from './composables/index.js' -import { pageDataEmpty } from './composables/index.js' import { LAYOUT_NAME_DEFAULT, LAYOUT_NAME_NOT_FOUND } from './constants.js' import type { ClientConfig, Layouts } from './types/index.js' @@ -25,6 +18,8 @@ import type { ClientConfig, Layouts } from './types/index.js' * Resolver methods to get global computed * * Users can override corresponding method for advanced customization + * + * @experimental - This is an experimental API and may be changed in minor versions */ export const resolvers = reactive({ /** @@ -39,37 +34,6 @@ export const resolvers = reactive({ {} as Layouts, ), - /** - * Resolve page info according to page path - */ - resolvePagePath: ( - pagesMap: PagesMap, - redirectsMap: RedirectsMap, - path: string, - ): string => { - path = normalizePath(path) - - // original path - if (pagesMap[path]) return path - - // encoded path - const encodedPath = encodeURI(path) - if (pagesMap[encodedPath]) return encodedPath - - return ( - // redirects - redirectsMap[path] || - // if no match at this point, then we should leave the path as is - path - ) - }, - - /** - * Resolve page data - */ - resolvePageData: (pageData: PageData): Promise => - Promise.resolve(pageData ?? pageDataEmpty), - /** * Resolve page frontmatter from page data */ diff --git a/packages/client/src/router/index.ts b/packages/client/src/router/index.ts new file mode 100644 index 0000000000..8b86265e30 --- /dev/null +++ b/packages/client/src/router/index.ts @@ -0,0 +1,3 @@ +export * from './pagesMap.js' +export * from './resolvePage.js' +export * from './resolvePagePath.js' diff --git a/packages/client/src/router/pagesMap.ts b/packages/client/src/router/pagesMap.ts new file mode 100644 index 0000000000..014d36ee1a --- /dev/null +++ b/packages/client/src/router/pagesMap.ts @@ -0,0 +1,35 @@ +import { + pagesMap as pagesMapRaw, + redirectsMap as redirectsMapRaw, +} from '@internal/pagesMap' +import type { + PageChunk, + PageMapItem, + PageMetaDefault, + PagesMap, + RedirectsMap, +} from '@internal/pagesMap' +import { shallowRef } from 'vue' +import type { Ref } from 'vue' + +export type { PagesMap, PageMapItem, PageMetaDefault, PageChunk, RedirectsMap } + +/** + * Global pages map ref + */ +export const pagesMap: Ref = shallowRef(pagesMapRaw) + +/** + * Global pages map ref + */ +export const redirectsMap: Ref = shallowRef(redirectsMapRaw) + +if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { + // reuse vue HMR runtime + __VUE_HMR_RUNTIME__.updatePagesMap = (data: PagesMap) => { + pagesMap.value = data + } + __VUE_HMR_RUNTIME__.updateRedirectsMap = (data: RedirectsMap) => { + redirectsMap.value = data + } +} diff --git a/packages/client/src/router/resolvePage.ts b/packages/client/src/router/resolvePage.ts new file mode 100644 index 0000000000..bdf03dca9e --- /dev/null +++ b/packages/client/src/router/resolvePage.ts @@ -0,0 +1,24 @@ +import type { PageMapItem, PageMetaDefault } from '@internal/pagesMap' +import { pagesMap } from './pagesMap.js' +import { resolvePagePath } from './resolvePagePath.js' + +interface ResolvedPage + extends PageMapItem { + path: string +} + +/** + * Resolve page with given raw path + */ +export const resolvePage = ( + path: string, +): ResolvedPage => { + const resolvedPath = resolvePagePath(path) + const pageMapItem = + pagesMap.value[resolvedPath] || pagesMap.value['/404.html'] + + return { + ...(pageMapItem as ResolvedPage), + path: resolvedPath, + } +} diff --git a/packages/client/src/router/resolvePagePath.ts b/packages/client/src/router/resolvePagePath.ts new file mode 100644 index 0000000000..f09087d957 --- /dev/null +++ b/packages/client/src/router/resolvePagePath.ts @@ -0,0 +1,18 @@ +import { normalizePath } from '@vuepress/shared' +import { pagesMap, redirectsMap } from './pagesMap.js' + +/** + * Resolve page path with given raw path + */ +export const resolvePagePath = (path: string): string => { + // normalized path + const normalizedPath = normalizePath(path) + if (pagesMap.value[normalizedPath]) return normalizedPath + + // encoded path + const encodedPath = encodeURI(normalizedPath) + if (pagesMap.value[encodedPath]) return encodedPath + + // redirected path or fallback to the normalized path + return redirectsMap.value[normalizedPath] || normalizedPath +} diff --git a/packages/client/src/setupGlobalComputed.ts b/packages/client/src/setupGlobalComputed.ts index 5064bf2cf4..67b77947c1 100644 --- a/packages/client/src/setupGlobalComputed.ts +++ b/packages/client/src/setupGlobalComputed.ts @@ -30,13 +30,13 @@ import { pageHeadTitleSymbol, pageLangSymbol, pageLayoutSymbol, - pagesMap, routeLocaleSymbol, siteData, siteLocaleDataSymbol, } from './composables/index.js' import { withBase } from './helpers/index.js' import { resolvers } from './resolvers.js' +import { pagesMap } from './router/index.js' import type { ClientConfig } from './types/index.js' /** diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts index 9b7eb2f23d..79b3277f61 100644 --- a/packages/client/src/types/internal/pagesMap.d.ts +++ b/packages/client/src/types/internal/pagesMap.d.ts @@ -2,20 +2,23 @@ import type { PageData } from '@vuepress/shared' import type { ComponentOptions } from 'vue' declare module '@internal/pagesMap' { - export type RedirectsMap = Record - export interface PageChunk { comp: ComponentOptions data: PageData } - export interface PageMapItem> { + export type PageMetaDefault = Record + + export interface PageMapItem< + PageMeta extends PageMetaDefault = PageMetaDefault, + > { loader: () => Promise meta: PageMeta } export type PagesMap = Record + export type RedirectsMap = Record - export const redirectsMap: RedirectsMap export const pagesMap: PagesMap + export const redirectsMap: RedirectsMap } diff --git a/packages/core/src/app/resolveAppPages.ts b/packages/core/src/app/resolveAppPages.ts index d253ec547f..d015759611 100644 --- a/packages/core/src/app/resolveAppPages.ts +++ b/packages/core/src/app/resolveAppPages.ts @@ -29,6 +29,7 @@ export const resolveAppPages = async (app: App): Promise => { frontmatter: { layout: 'NotFound', }, + content: '404 Not Found', }), ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60150649a9..0c467457c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: conventional-changelog-cli: specifier: ^4.1.0 version: 4.1.0 + cypress: + specifier: ^13.6.3 + version: 13.6.3 eslint: specifier: ^8.56.0 version: 8.56.0 diff --git a/tsconfig.json b/tsconfig.json index 6f623a8249..2350bd1aac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "@vuepress/*": ["./packages/*/src"], "vuepress": ["./packages/vuepress/src"], }, - "types": ["webpack-env", "vite/client"], + "types": ["cypress", "webpack-env", "vite/client"], }, "include": [ "**/.vuepress/**/*", From 1d7ab6c0f340b5080d94463dc23f4c6957ad2552 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Fri, 2 Feb 2024 19:45:48 +0800 Subject: [PATCH 23/31] chore: tweaks --- packages/client/src/components/Content.ts | 8 +++----- packages/client/src/createVueRouter.ts | 10 +++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index e0541aa326..b1d405025e 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -18,12 +18,10 @@ export const Content = defineComponent({ }, setup(props) { - const page = usePageData() + const pageData = usePageData() const pageComponent = computed(() => { - const resolvedPage = resolvePage(props.path || page.value.path) - return defineAsyncComponent(() => - resolvedPage.loader().then(({ comp }) => comp), - ) + const page = resolvePage(props.path || pageData.value.path) + return defineAsyncComponent(() => page.loader().then(({ comp }) => comp)) }) return () => h(pageComponent.value) diff --git a/packages/client/src/createVueRouter.ts b/packages/client/src/createVueRouter.ts index 3957723573..ea3f29407f 100644 --- a/packages/client/src/createVueRouter.ts +++ b/packages/client/src/createVueRouter.ts @@ -41,17 +41,17 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const resolvedPage = resolvePage(to.path) + const page = resolvePage(to.path) - if (resolvedPage.path !== to.path) { - return resolvedPage.path + if (page.path !== to.path) { + return page.path } // attach page meta to route meta - to.meta = resolvedPage.meta + to.meta = page.meta // attach page data to route meta to trigger page data computed when route changes - const pageChunk = await resolvedPage.loader() + const pageChunk = await page.loader() to.meta._data = pageChunk.data } }) From db2a90f75568d07def9b48f9ceb2c5dafe673463 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Fri, 2 Feb 2024 23:21:25 +0800 Subject: [PATCH 24/31] chore: updates --- e2e/docs/.vuepress/config.ts | 3 +- e2e/docs/404.md | 4 ++ e2e/docs/router/resolve-page.md | 8 ++-- e2e/tests/router/resolve-page.cy.ts | 56 ++++++++++++++---------- packages/client/src/createVueRouter.ts | 13 +++--- packages/core/src/app/resolveAppPages.ts | 13 ++++-- 6 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 e2e/docs/404.md diff --git a/e2e/docs/.vuepress/config.ts b/e2e/docs/.vuepress/config.ts index e4edee8063..abb0237258 100644 --- a/e2e/docs/.vuepress/config.ts +++ b/e2e/docs/.vuepress/config.ts @@ -54,11 +54,12 @@ export default defineUserConfig({ theme: e2eTheme(), extendsPage: (page) => { - if (page.path === '/page-data/meta.html') + if (page.path === '/page-data/meta.html') { page.meta = { a: 1, b: 2, ...page.meta, } + } }, }) diff --git a/e2e/docs/404.md b/e2e/docs/404.md new file mode 100644 index 0000000000..cf0aab5676 --- /dev/null +++ b/e2e/docs/404.md @@ -0,0 +1,4 @@ +--- +meta: + notFound: true +--- diff --git a/e2e/docs/router/resolve-page.md b/e2e/docs/router/resolve-page.md index 00b4a1b4fe..c5095c88cc 100644 --- a/e2e/docs/router/resolve-page.md +++ b/e2e/docs/router/resolve-page.md @@ -10,9 +10,9 @@ #### Non-Index -- Clean URL: {{ JSON.stringify(resolvePage('/helpers/resolve-page')) }} -- HTML: {{ JSON.stringify(resolvePage('/helpers/resolve-page.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/helpers/resolve-page.md')) }} +- Clean URL: {{ JSON.stringify(resolvePage('/router/resolve-page')) }} +- HTML: {{ JSON.stringify(resolvePage('/router/resolve-page.html')) }} +- Markdown: {{ JSON.stringify(resolvePage('/router/resolve-page.md')) }} #### Non-ASCII @@ -32,7 +32,7 @@ - HTML: {{ JSON.stringify(resolvePage('/non-existent.html')) }} - Markdown: {{ JSON.stringify(resolvePage('/non-existent.md')) }} -### Meta +#### Meta - Clean URL: {{ JSON.stringify(resolvePage('/page-data/meta')) }} - HTML: {{ JSON.stringify(resolvePage('/page-data/meta.html')) }} diff --git a/e2e/tests/router/resolve-page.cy.ts b/e2e/tests/router/resolve-page.cy.ts index f1dea0d66d..facda51bdc 100644 --- a/e2e/tests/router/resolve-page.cy.ts +++ b/e2e/tests/router/resolve-page.cy.ts @@ -7,41 +7,53 @@ it('should resolve paths correctly', () => { const testCases = [ { selector: '#index', - expectedPath: '/', + expected: { + path: '/', + meta: {}, + }, }, { selector: '#non-index', - expectedPath: '/helpers/resolve-page.html', + expected: { + path: '/router/resolve-page.html', + meta: {}, + }, }, { selector: '#non-ascii', - expectedPath: '/routes/non-ascii-paths/中文目录名/中文文件名.html', + expected: { + path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), + meta: {}, + }, }, { selector: '#non-ascii-encoded', - expectedPath: '/routes/non-ascii-paths/中文目录名/中文文件名.html', + expected: { + path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), + meta: {}, + }, + }, + { + selector: '#non-existent', + expected: { + path: '/non-existent.html', + meta: { notFound: true }, + }, + }, + { + selector: '#meta', + expected: { + path: '/page-data/meta.html', + meta: { a: 0, b: 2, c: 3 }, + }, }, ] - testCases.forEach(({ selector, expectedPath }) => { - cy.get(`.e2e-theme-content ${selector}`).each((el) => { + testCases.forEach(({ selector, expected }) => { + cy.get(`.e2e-theme-content ${selector} + ul > li`).each((el) => { const resolvedPage = parseResolvedPageFromElement(el) - expect(resolvedPage.path).to.equal(expectedPath) + expect(resolvedPage.path).to.equal(expected.path) + expect(resolvedPage.meta).to.deep.equal(expected.meta) }) }) - - cy.get(`.e2e-theme-content #non-existent`).each((el) => { - const resolvedPage = parseResolvedPageFromElement(el) - expect(resolvedPage).to.equal(null) - }) -}) - -it('should resolve meta correctly', () => { - cy.visit('/router/resolve-page.html') - - cy.get('.e2e-theme-content #meta ul li').each((el) => { - const resolvedPage = parseResolvedPageFromElement(el) - expect(resolvedPage.path).to.equal('/page-data/meta.html') - expect(resolvedPage.meta).to.include({ a: 0, b: 2, c: 3 }) - }) }) diff --git a/packages/client/src/createVueRouter.ts b/packages/client/src/createVueRouter.ts index ea3f29407f..b0b95a3044 100644 --- a/packages/client/src/createVueRouter.ts +++ b/packages/client/src/createVueRouter.ts @@ -46,13 +46,14 @@ export const createVueRouter = (): Router => { if (page.path !== to.path) { return page.path } - - // attach page meta to route meta - to.meta = page.meta - - // attach page data to route meta to trigger page data computed when route changes const pageChunk = await page.loader() - to.meta._data = pageChunk.data + + to.meta = { + // attach page meta to route meta + ...page.meta, + // attach page data to route meta to trigger page data computed when route changes + _data: pageChunk.data, + } } }) diff --git a/packages/core/src/app/resolveAppPages.ts b/packages/core/src/app/resolveAppPages.ts index d015759611..2f721fa7fd 100644 --- a/packages/core/src/app/resolveAppPages.ts +++ b/packages/core/src/app/resolveAppPages.ts @@ -21,14 +21,19 @@ export const resolveAppPages = async (app: App): Promise => { pageFilePaths.map((filePath) => createPage(app, { filePath })), ) + // find the 404 page + const notFoundPage = pages.find((page) => page.path === '/404.html') + + // if there is a 404 page, set the default layout to NotFound + if (notFoundPage) { + notFoundPage.frontmatter.layout ??= 'NotFound' + } // if there is no 404 page, add one - if (!pages.some((page) => page.path === '/404.html')) { + else { pages.push( await createPage(app, { path: '/404.html', - frontmatter: { - layout: 'NotFound', - }, + frontmatter: { layout: 'NotFound' }, content: '404 Not Found', }), ) From c5a61deb76cd1c6beec4deab81e444946209ca8b Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Fri, 2 Feb 2024 23:29:12 +0800 Subject: [PATCH 25/31] chore: tweaks --- package.json | 1 - pnpm-lock.yaml | 3 --- tsconfig.json | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index d25291a8ee..7dd5c24dde 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "@vitest/coverage-istanbul": "^1.2.1", "bumpp": "^9.3.0", "conventional-changelog-cli": "^4.1.0", - "cypress": "^13.6.3", "eslint": "^8.56.0", "eslint-config-vuepress": "^4.10.0", "eslint-config-vuepress-typescript": "^4.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c467457c6..60150649a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,9 +29,6 @@ importers: conventional-changelog-cli: specifier: ^4.1.0 version: 4.1.0 - cypress: - specifier: ^13.6.3 - version: 13.6.3 eslint: specifier: ^8.56.0 version: 8.56.0 diff --git a/tsconfig.json b/tsconfig.json index 2350bd1aac..6f623a8249 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "@vuepress/*": ["./packages/*/src"], "vuepress": ["./packages/vuepress/src"], }, - "types": ["cypress", "webpack-env", "vite/client"], + "types": ["webpack-env", "vite/client"], }, "include": [ "**/.vuepress/**/*", From 521139e7e2a8cd8cf4378624f6955ef591c1b1f2 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 00:45:12 +0800 Subject: [PATCH 26/31] fix: fix normalizePath and resolvePageRedirects --- .../core/src/app/prepare/preparePagesMap.ts | 26 +++++++----- packages/shared/src/utils/normalizePath.ts | 2 +- packages/shared/tests/normalizePath.spec.ts | 41 +++++++++++-------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/core/src/app/prepare/preparePagesMap.ts b/packages/core/src/app/prepare/preparePagesMap.ts index 541ca3a1f6..9aaa6fcadf 100644 --- a/packages/core/src/app/prepare/preparePagesMap.ts +++ b/packages/core/src/app/prepare/preparePagesMap.ts @@ -1,4 +1,4 @@ -import { ensureLeadingSlash } from '@vuepress/shared' +import { ensureLeadingSlash, normalizePath } from '@vuepress/shared' import type { App, Page } from '../../types/index.js' const HMR_CODE = ` @@ -31,23 +31,27 @@ const resolvePageRedirects = ({ // paths that should redirect to this page, use set to dedupe const redirectsSet = new Set() - // redirect from decoded path - redirectsSet.add(decodeURI(path)) + // add redirect to the set when the redirect could not be normalized & encoded to the page path + const addRedirect = (redirect: string): void => { + const normalizedPath = normalizePath(redirect) + if (normalizedPath === path) return + + const encodedPath = encodeURI(normalizedPath) + if (encodedPath === path) return + + redirectsSet.add(redirect) + } // redirect from inferred path if (pathInferred !== null) { - redirectsSet.add(pathInferred) + addRedirect(pathInferred) } - // redirect from none-markdown filename path - // markdown file path is omitted as it can be normalized to pathInferred - if (filePathRelative !== null && !filePathRelative.endsWith('.md')) { - redirectsSet.add(ensureLeadingSlash(filePathRelative)) + // redirect from filename path + if (filePathRelative !== null) { + addRedirect(ensureLeadingSlash(filePathRelative)) } - // avoid redirect from the page path itself - redirectsSet.delete(path) - return Array.from(redirectsSet) } diff --git a/packages/shared/src/utils/normalizePath.ts b/packages/shared/src/utils/normalizePath.ts index f3ef770903..e7ec96ccbd 100644 --- a/packages/shared/src/utils/normalizePath.ts +++ b/packages/shared/src/utils/normalizePath.ts @@ -6,7 +6,7 @@ export const normalizePath = (path: string): string => { : path return convertedMdPath.endsWith('/index.html') - ? convertedMdPath.substring(0, path.length - 10) + ? convertedMdPath.substring(0, convertedMdPath.length - 10) : convertedMdPath.endsWith('.html') || convertedMdPath.endsWith('/') ? convertedMdPath : convertedMdPath + '.html' diff --git a/packages/shared/tests/normalizePath.spec.ts b/packages/shared/tests/normalizePath.spec.ts index 351448bbc7..8c4c6f8f79 100644 --- a/packages/shared/tests/normalizePath.spec.ts +++ b/packages/shared/tests/normalizePath.spec.ts @@ -1,23 +1,28 @@ import { expect, it } from 'vitest' import { normalizePath } from '../src/index.js' -it('normalizePath', () => { - const tests = [ - ['/', '/'], - ['/index.html', '/'], - ['/foo', '/foo.html'], - ['/foo.md', '/foo.html'], - ['/foo/', '/foo/'], - ['/foo/README.md', '/foo/'], - ['/foo/index.html', '/foo/'], - ['/foo/bar', '/foo/bar.html'], - ['/foo/bar/', '/foo/bar/'], - ['/foo/bar/README.md', '/foo/bar/'], - ['/foo/bar.md', '/foo/bar.html'], - ['/foo/bar.html', '/foo/bar.html'], - ] +const testCases = [ + ['/', '/'], + ['/README.md', '/'], + ['/index.md', '/'], + ['/index.html', '/'], + ['/foo', '/foo.html'], + ['/foo.md', '/foo.html'], + ['/foo/', '/foo/'], + ['/foo/README.md', '/foo/'], + ['/foo/index.md', '/foo/'], + ['/foo/index.html', '/foo/'], + ['/foo/bar', '/foo/bar.html'], + ['/foo/bar/', '/foo/bar/'], + ['/foo/bar/README.md', '/foo/bar/'], + ['/foo/bar/index.md', '/foo/bar/'], + ['/foo/bar/index.html', '/foo/bar/'], + ['/foo/bar.md', '/foo/bar.html'], + ['/foo/bar.html', '/foo/bar.html'], +] - tests.forEach(([path, expected]) => { +testCases.forEach(([path, expected]) => + it(`should normalize "${path}" to "${expected}"`, () => { expect(normalizePath(path)).toBe(expected) - }) -}) + }), +) From 9cdca77aa608502d92075723b37403834b6412d0 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 03:16:47 +0800 Subject: [PATCH 27/31] feat: rename pagesMap to routes, revert page meta back to route meta --- e2e/docs/.vuepress/config.ts | 6 +- e2e/docs/404.md | 2 +- e2e/docs/page-data/{meta.md => route-meta.md} | 2 +- e2e/docs/router/resolve-page.md | 43 -------------- e2e/docs/router/resolve-route.md | 43 ++++++++++++++ e2e/tests/router/resolve-page.cy.ts | 59 ------------------- e2e/tests/router/resolve-route.cy.ts | 59 +++++++++++++++++++ .../cli/src/commands/dev/handlePageAdd.ts | 6 +- .../cli/src/commands/dev/handlePageChange.ts | 12 ++-- .../cli/src/commands/dev/handlePageUnlink.ts | 6 +- packages/client/package.json | 2 +- packages/client/src/components/Content.ts | 6 +- packages/client/src/components/VPLink.ts | 4 +- packages/client/src/composables/index.ts | 2 +- packages/client/src/composables/pagesMap.ts | 11 ---- packages/client/src/composables/routes.ts | 11 ++++ packages/client/src/createVueRouter.ts | 14 ++--- packages/client/src/router/index.ts | 6 +- packages/client/src/router/pagesMap.ts | 35 ----------- packages/client/src/router/resolvePage.ts | 24 -------- packages/client/src/router/resolvePagePath.ts | 18 ------ packages/client/src/router/resolveRoute.ts | 25 ++++++++ .../client/src/router/resolveRoutePath.ts | 18 ++++++ packages/client/src/router/routes.ts | 35 +++++++++++ packages/client/src/setupGlobalComputed.ts | 6 +- .../client/src/types/internal/pagesMap.d.ts | 24 -------- .../client/src/types/internal/routes.d.ts | 22 +++++++ packages/core/src/app/appPrepare.ts | 6 +- packages/core/src/app/prepare/index.ts | 2 +- .../{preparePagesMap.ts => prepareRoutes.ts} | 34 +++++------ packages/core/src/page/createPage.ts | 17 +----- packages/core/src/page/index.ts | 2 +- packages/core/src/page/resolvePageMeta.ts | 18 ------ .../core/src/page/resolvePageRouteMeta.ts | 10 ++++ packages/core/src/types/page.ts | 6 +- .../core/tests/page/resolvePageMeta.spec.ts | 26 -------- .../tests/page/resolvePageRouteMeta.spec.ts | 26 ++++++++ packages/shared/src/types/page.ts | 2 +- packages/shared/src/utils/index.ts | 2 +- ...normalizePath.ts => normalizeRoutePath.ts} | 5 +- ...ath.spec.ts => normalizeRoutePath.spec.ts} | 4 +- 41 files changed, 321 insertions(+), 340 deletions(-) rename e2e/docs/page-data/{meta.md => route-meta.md} (66%) delete mode 100644 e2e/docs/router/resolve-page.md create mode 100644 e2e/docs/router/resolve-route.md delete mode 100644 e2e/tests/router/resolve-page.cy.ts create mode 100644 e2e/tests/router/resolve-route.cy.ts delete mode 100644 packages/client/src/composables/pagesMap.ts create mode 100644 packages/client/src/composables/routes.ts delete mode 100644 packages/client/src/router/pagesMap.ts delete mode 100644 packages/client/src/router/resolvePage.ts delete mode 100644 packages/client/src/router/resolvePagePath.ts create mode 100644 packages/client/src/router/resolveRoute.ts create mode 100644 packages/client/src/router/resolveRoutePath.ts create mode 100644 packages/client/src/router/routes.ts delete mode 100644 packages/client/src/types/internal/pagesMap.d.ts create mode 100644 packages/client/src/types/internal/routes.d.ts rename packages/core/src/app/prepare/{preparePagesMap.ts => prepareRoutes.ts} (62%) delete mode 100644 packages/core/src/page/resolvePageMeta.ts create mode 100644 packages/core/src/page/resolvePageRouteMeta.ts delete mode 100644 packages/core/tests/page/resolvePageMeta.spec.ts create mode 100644 packages/core/tests/page/resolvePageRouteMeta.spec.ts rename packages/shared/src/utils/{normalizePath.ts => normalizeRoutePath.ts} (78%) rename packages/shared/tests/{normalizePath.spec.ts => normalizeRoutePath.spec.ts} (86%) diff --git a/e2e/docs/.vuepress/config.ts b/e2e/docs/.vuepress/config.ts index abb0237258..8419bae8f2 100644 --- a/e2e/docs/.vuepress/config.ts +++ b/e2e/docs/.vuepress/config.ts @@ -54,11 +54,11 @@ export default defineUserConfig({ theme: e2eTheme(), extendsPage: (page) => { - if (page.path === '/page-data/meta.html') { - page.meta = { + if (page.path === '/page-data/route-meta.html') { + page.routeMeta = { a: 1, b: 2, - ...page.meta, + ...page.routeMeta, } } }, diff --git a/e2e/docs/404.md b/e2e/docs/404.md index cf0aab5676..951515198d 100644 --- a/e2e/docs/404.md +++ b/e2e/docs/404.md @@ -1,4 +1,4 @@ --- -meta: +routeMeta: notFound: true --- diff --git a/e2e/docs/page-data/meta.md b/e2e/docs/page-data/route-meta.md similarity index 66% rename from e2e/docs/page-data/meta.md rename to e2e/docs/page-data/route-meta.md index 3e822a6928..b319753d1c 100644 --- a/e2e/docs/page-data/meta.md +++ b/e2e/docs/page-data/route-meta.md @@ -1,5 +1,5 @@ --- -meta: +routeMeta: a: 0 c: 3 --- diff --git a/e2e/docs/router/resolve-page.md b/e2e/docs/router/resolve-page.md deleted file mode 100644 index c5095c88cc..0000000000 --- a/e2e/docs/router/resolve-page.md +++ /dev/null @@ -1,43 +0,0 @@ -## resolve - -### Path - -#### Index - -- Clean URL: {{ JSON.stringify(resolvePage('/')) }} -- HTML: {{ JSON.stringify(resolvePage('/index.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/README.md')) }} - -#### Non-Index - -- Clean URL: {{ JSON.stringify(resolvePage('/router/resolve-page')) }} -- HTML: {{ JSON.stringify(resolvePage('/router/resolve-page.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/router/resolve-page.md')) }} - -#### Non-ASCII - -- Clean URL: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名')) }} -- HTML: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/routes/non-ascii-paths/中文目录名/中文文件名.md')) }} - -#### Non-ASCII Encoded - -- Clean URL: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名'))) }} -- HTML: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))) }} -- Markdown: {{ JSON.stringify(resolvePage(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))) }} - -#### Non-Existent - -- Clean URL: {{ JSON.stringify(resolvePage('/non-existent')) }} -- HTML: {{ JSON.stringify(resolvePage('/non-existent.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/non-existent.md')) }} - -#### Meta - -- Clean URL: {{ JSON.stringify(resolvePage('/page-data/meta')) }} -- HTML: {{ JSON.stringify(resolvePage('/page-data/meta.html')) }} -- Markdown: {{ JSON.stringify(resolvePage('/page-data/meta.md')) }} - - diff --git a/e2e/docs/router/resolve-route.md b/e2e/docs/router/resolve-route.md new file mode 100644 index 0000000000..c556b6c66b --- /dev/null +++ b/e2e/docs/router/resolve-route.md @@ -0,0 +1,43 @@ +## resolve + +### Path + +#### Index + +- Clean URL: {{ JSON.stringify(resolveRoute('/')) }} +- HTML: {{ JSON.stringify(resolveRoute('/index.html')) }} +- Markdown: {{ JSON.stringify(resolveRoute('/README.md')) }} + +#### Non-Index + +- Clean URL: {{ JSON.stringify(resolveRoute('/router/resolve-route')) }} +- HTML: {{ JSON.stringify(resolveRoute('/router/resolve-route.html')) }} +- Markdown: {{ JSON.stringify(resolveRoute('/router/resolve-route.md')) }} + +#### Non-ASCII + +- Clean URL: {{ JSON.stringify(resolveRoute('/routes/non-ascii-paths/中文目录名/中文文件名')) }} +- HTML: {{ JSON.stringify(resolveRoute('/routes/non-ascii-paths/中文目录名/中文文件名.html')) }} +- Markdown: {{ JSON.stringify(resolveRoute('/routes/non-ascii-paths/中文目录名/中文文件名.md')) }} + +#### Non-ASCII Encoded + +- Clean URL: {{ JSON.stringify(resolveRoute(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名'))) }} +- HTML: {{ JSON.stringify(resolveRoute(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'))) }} +- Markdown: {{ JSON.stringify(resolveRoute(encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.md'))) }} + +#### Non-Existent + +- Clean URL: {{ JSON.stringify(resolveRoute('/non-existent')) }} +- HTML: {{ JSON.stringify(resolveRoute('/non-existent.html')) }} +- Markdown: {{ JSON.stringify(resolveRoute('/non-existent.md')) }} + +#### Route Meta + +- Clean URL: {{ JSON.stringify(resolveRoute('/page-data/route-meta')) }} +- HTML: {{ JSON.stringify(resolveRoute('/page-data/route-meta.html')) }} +- Markdown: {{ JSON.stringify(resolveRoute('/page-data/route-meta.md')) }} + + diff --git a/e2e/tests/router/resolve-page.cy.ts b/e2e/tests/router/resolve-page.cy.ts deleted file mode 100644 index facda51bdc..0000000000 --- a/e2e/tests/router/resolve-page.cy.ts +++ /dev/null @@ -1,59 +0,0 @@ -const parseResolvedPageFromElement = (el: Cypress.JQueryWithSelector) => - JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) - -it('should resolve paths correctly', () => { - cy.visit('/router/resolve-page.html') - - const testCases = [ - { - selector: '#index', - expected: { - path: '/', - meta: {}, - }, - }, - { - selector: '#non-index', - expected: { - path: '/router/resolve-page.html', - meta: {}, - }, - }, - { - selector: '#non-ascii', - expected: { - path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), - meta: {}, - }, - }, - { - selector: '#non-ascii-encoded', - expected: { - path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), - meta: {}, - }, - }, - { - selector: '#non-existent', - expected: { - path: '/non-existent.html', - meta: { notFound: true }, - }, - }, - { - selector: '#meta', - expected: { - path: '/page-data/meta.html', - meta: { a: 0, b: 2, c: 3 }, - }, - }, - ] - - testCases.forEach(({ selector, expected }) => { - cy.get(`.e2e-theme-content ${selector} + ul > li`).each((el) => { - const resolvedPage = parseResolvedPageFromElement(el) - expect(resolvedPage.path).to.equal(expected.path) - expect(resolvedPage.meta).to.deep.equal(expected.meta) - }) - }) -}) diff --git a/e2e/tests/router/resolve-route.cy.ts b/e2e/tests/router/resolve-route.cy.ts new file mode 100644 index 0000000000..2f85ef0cf2 --- /dev/null +++ b/e2e/tests/router/resolve-route.cy.ts @@ -0,0 +1,59 @@ +const testCases = [ + { + selector: '#index', + expected: { + path: '/', + meta: {}, + }, + }, + { + selector: '#non-index', + expected: { + path: '/router/resolve-route.html', + meta: {}, + }, + }, + { + selector: '#non-ascii', + expected: { + path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), + meta: {}, + }, + }, + { + selector: '#non-ascii-encoded', + expected: { + path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), + meta: {}, + }, + }, + { + selector: '#non-existent', + expected: { + path: '/non-existent.html', + meta: { notFound: true }, + }, + }, + { + selector: '#route-meta', + expected: { + path: '/page-data/route-meta.html', + meta: { a: 0, b: 2, c: 3 }, + }, + }, +] + +const parseResolvedRouteFromElement = (el: Cypress.JQueryWithSelector) => + JSON.parse(/: (\{.*\})\s*$/.exec(el.text())![1]) + +it('should resolve routes correctly', () => { + cy.visit('/router/resolve-route.html') + + testCases.forEach(({ selector, expected }) => { + cy.get(`.e2e-theme-content ${selector} + ul > li`).each((el) => { + const resolvedRoute = parseResolvedRouteFromElement(el) + expect(resolvedRoute.path).to.equal(expected.path) + expect(resolvedRoute.meta).to.deep.equal(expected.meta) + }) + }) +}) diff --git a/packages/cli/src/commands/dev/handlePageAdd.ts b/packages/cli/src/commands/dev/handlePageAdd.ts index 7c4e730660..9390e1a96c 100644 --- a/packages/cli/src/commands/dev/handlePageAdd.ts +++ b/packages/cli/src/commands/dev/handlePageAdd.ts @@ -2,7 +2,7 @@ import { createPage, preparePage, preparePageComponent, - preparePagesMap, + prepareRoutes, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -33,8 +33,8 @@ export const handlePageAdd = async ( await preparePageComponent(app, page) await preparePage(app, page) - // prepare pages entry - await preparePagesMap(app) + // 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 10330ed423..2a79b4f639 100644 --- a/packages/cli/src/commands/dev/handlePageChange.ts +++ b/packages/cli/src/commands/dev/handlePageChange.ts @@ -2,7 +2,7 @@ import { createPage, preparePage, preparePageComponent, - preparePagesMap, + prepareRoutes, } from '@vuepress/core' import type { App, Page } from '@vuepress/core' @@ -37,12 +37,12 @@ export const handlePageChange = async ( await preparePage(app, pageNew) const isPathChanged = pageOld.path !== pageNew.path - const isMetaChanged = - JSON.stringify(pageOld.meta) !== JSON.stringify(pageNew.meta) + const isRouteMetaChanged = + JSON.stringify(pageOld.routeMeta) !== JSON.stringify(pageNew.routeMeta) - // prepare pages map if the path or meta is changed - if (isPathChanged || isMetaChanged) { - await preparePagesMap(app) + // prepare routes file if the path or route meta is changed + if (isPathChanged || isRouteMetaChanged) { + await prepareRoutes(app) } return [pageOld, pageNew] diff --git a/packages/cli/src/commands/dev/handlePageUnlink.ts b/packages/cli/src/commands/dev/handlePageUnlink.ts index 5e56b8fb9a..1260480292 100644 --- a/packages/cli/src/commands/dev/handlePageUnlink.ts +++ b/packages/cli/src/commands/dev/handlePageUnlink.ts @@ -1,4 +1,4 @@ -import { preparePagesMap } from '@vuepress/core' +import { prepareRoutes } from '@vuepress/core' import type { App, Page } from '@vuepress/core' /** @@ -21,8 +21,8 @@ export const handlePageUnlink = async ( // remove the old page app.pages.splice(pageIndex, 1) - // re-prepare page files - await preparePagesMap(app) + // re-prepare routes file + await prepareRoutes(app) return page } diff --git a/packages/client/package.json b/packages/client/package.json index 0b5c0d94f2..9bbe205b20 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -59,7 +59,7 @@ "external": [ "@internal/clientConfigs", "@internal/layoutComponents", - "@internal/pagesMap", + "@internal/routes", "@internal/siteData" ], "format": [ diff --git a/packages/client/src/components/Content.ts b/packages/client/src/components/Content.ts index b1d405025e..ea35bf9793 100644 --- a/packages/client/src/components/Content.ts +++ b/packages/client/src/components/Content.ts @@ -1,6 +1,6 @@ import { computed, defineAsyncComponent, defineComponent, h } from 'vue' import { usePageData } from '../composables/index.js' -import { resolvePage } from '../router/index.js' +import { resolveRoute } from '../router/index.js' /** * Markdown rendered content @@ -20,8 +20,8 @@ export const Content = defineComponent({ setup(props) { const pageData = usePageData() const pageComponent = computed(() => { - const page = resolvePage(props.path || pageData.value.path) - return defineAsyncComponent(() => page.loader().then(({ comp }) => comp)) + const route = resolveRoute(props.path || pageData.value.path) + return defineAsyncComponent(() => route.loader().then(({ comp }) => comp)) }) return () => h(pageComponent.value) diff --git a/packages/client/src/components/VPLink.ts b/packages/client/src/components/VPLink.ts index 801e23b894..72e6677c3e 100644 --- a/packages/client/src/components/VPLink.ts +++ b/packages/client/src/components/VPLink.ts @@ -2,7 +2,7 @@ import { h } from 'vue' import type { FunctionalComponent, VNode } from 'vue' import { useRouter } from 'vue-router' import { withBase } from '../helpers/index.js' -import { resolvePagePath } from '../router/index.js' +import { resolveRoutePath } from '../router/index.js' /** * Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 @@ -35,7 +35,7 @@ export const VPLink: FunctionalComponent< } > = ({ to = '' }, { slots }) => { const router = useRouter() - const path = withBase(resolvePagePath(to)) + const path = withBase(resolveRoutePath(to)) return h( 'a', diff --git a/packages/client/src/composables/index.ts b/packages/client/src/composables/index.ts index 572a51bc03..f6c1751901 100644 --- a/packages/client/src/composables/index.ts +++ b/packages/client/src/composables/index.ts @@ -5,8 +5,8 @@ export * from './pageHead.js' export * from './pageHeadTitle.js' export * from './pageLang.js' export * from './pageLayout.js' -export * from './pagesMap.js' export * from './routeLocale.js' +export * from './routes.js' export * from './siteData.js' export * from './siteLocaleData.js' export * from './updateHead.js' diff --git a/packages/client/src/composables/pagesMap.ts b/packages/client/src/composables/pagesMap.ts deleted file mode 100644 index d474ab0dce..0000000000 --- a/packages/client/src/composables/pagesMap.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { pagesMap, redirectsMap } from '../router' - -/** - * Returns the ref of pages map - */ -export const usePagesMap = (): typeof pagesMap => pagesMap - -/** - * Returns the ref of pages map - */ -export const useRedirectsMap = (): typeof redirectsMap => redirectsMap diff --git a/packages/client/src/composables/routes.ts b/packages/client/src/composables/routes.ts new file mode 100644 index 0000000000..65a736eab2 --- /dev/null +++ b/packages/client/src/composables/routes.ts @@ -0,0 +1,11 @@ +import { redirects, routes } from '../router/index.js' + +/** + * Returns the ref of pages map + */ +export const useRedirects = (): typeof redirects => redirects + +/** + * Returns the ref of routes map + */ +export const useRoutes = (): typeof routes => routes diff --git a/packages/client/src/createVueRouter.ts b/packages/client/src/createVueRouter.ts index b0b95a3044..50f2078b53 100644 --- a/packages/client/src/createVueRouter.ts +++ b/packages/client/src/createVueRouter.ts @@ -8,7 +8,7 @@ import { } from 'vue-router' import { Vuepress } from './components/Vuepress.js' import type { PageData } from './composables/index.js' -import { resolvePage } from './router/index.js' +import { resolveRoute } from './router/index.js' /** * - use `createWebHistory` in dev mode and build mode client bundle @@ -41,16 +41,16 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const page = resolvePage(to.path) + const route = resolveRoute(to.path) - if (page.path !== to.path) { - return page.path + if (route.path !== to.path) { + return route.path } - const pageChunk = await page.loader() + const pageChunk = await route.loader() to.meta = { - // attach page meta to route meta - ...page.meta, + // attach route meta + ...route.meta, // attach page data to route meta to trigger page data computed when route changes _data: pageChunk.data, } diff --git a/packages/client/src/router/index.ts b/packages/client/src/router/index.ts index 8b86265e30..84600902f1 100644 --- a/packages/client/src/router/index.ts +++ b/packages/client/src/router/index.ts @@ -1,3 +1,3 @@ -export * from './pagesMap.js' -export * from './resolvePage.js' -export * from './resolvePagePath.js' +export * from './resolveRoute.js' +export * from './resolveRoutePath.js' +export * from './routes.js' diff --git a/packages/client/src/router/pagesMap.ts b/packages/client/src/router/pagesMap.ts deleted file mode 100644 index 014d36ee1a..0000000000 --- a/packages/client/src/router/pagesMap.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - pagesMap as pagesMapRaw, - redirectsMap as redirectsMapRaw, -} from '@internal/pagesMap' -import type { - PageChunk, - PageMapItem, - PageMetaDefault, - PagesMap, - RedirectsMap, -} from '@internal/pagesMap' -import { shallowRef } from 'vue' -import type { Ref } from 'vue' - -export type { PagesMap, PageMapItem, PageMetaDefault, PageChunk, RedirectsMap } - -/** - * Global pages map ref - */ -export const pagesMap: Ref = shallowRef(pagesMapRaw) - -/** - * Global pages map ref - */ -export const redirectsMap: Ref = shallowRef(redirectsMapRaw) - -if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { - // reuse vue HMR runtime - __VUE_HMR_RUNTIME__.updatePagesMap = (data: PagesMap) => { - pagesMap.value = data - } - __VUE_HMR_RUNTIME__.updateRedirectsMap = (data: RedirectsMap) => { - redirectsMap.value = data - } -} diff --git a/packages/client/src/router/resolvePage.ts b/packages/client/src/router/resolvePage.ts deleted file mode 100644 index bdf03dca9e..0000000000 --- a/packages/client/src/router/resolvePage.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { PageMapItem, PageMetaDefault } from '@internal/pagesMap' -import { pagesMap } from './pagesMap.js' -import { resolvePagePath } from './resolvePagePath.js' - -interface ResolvedPage - extends PageMapItem { - path: string -} - -/** - * Resolve page with given raw path - */ -export const resolvePage = ( - path: string, -): ResolvedPage => { - const resolvedPath = resolvePagePath(path) - const pageMapItem = - pagesMap.value[resolvedPath] || pagesMap.value['/404.html'] - - return { - ...(pageMapItem as ResolvedPage), - path: resolvedPath, - } -} diff --git a/packages/client/src/router/resolvePagePath.ts b/packages/client/src/router/resolvePagePath.ts deleted file mode 100644 index f09087d957..0000000000 --- a/packages/client/src/router/resolvePagePath.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { normalizePath } from '@vuepress/shared' -import { pagesMap, redirectsMap } from './pagesMap.js' - -/** - * Resolve page path with given raw path - */ -export const resolvePagePath = (path: string): string => { - // normalized path - const normalizedPath = normalizePath(path) - if (pagesMap.value[normalizedPath]) return normalizedPath - - // encoded path - const encodedPath = encodeURI(normalizedPath) - if (pagesMap.value[encodedPath]) return encodedPath - - // redirected path or fallback to the normalized path - return redirectsMap.value[normalizedPath] || normalizedPath -} diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts new file mode 100644 index 0000000000..f1891cc979 --- /dev/null +++ b/packages/client/src/router/resolveRoute.ts @@ -0,0 +1,25 @@ +import { resolveRoutePath } from './resolveRoutePath.js' +import type { PageMetaDefault, Route } from './routes.js' +import { routes } from './routes.js' + +interface ResolvedRoute + extends Route { + path: string +} + +/** + * Resolve route with given path + */ +export const resolveRoute = < + PageMeta extends PageMetaDefault = PageMetaDefault, +>( + path: string, +): ResolvedRoute => { + const routePath = resolveRoutePath(path) + const pageMapItem = routes.value[routePath] || routes.value['/404.html'] + + return { + ...(pageMapItem as ResolvedRoute), + path: routePath, + } +} diff --git a/packages/client/src/router/resolveRoutePath.ts b/packages/client/src/router/resolveRoutePath.ts new file mode 100644 index 0000000000..ca3ae57349 --- /dev/null +++ b/packages/client/src/router/resolveRoutePath.ts @@ -0,0 +1,18 @@ +import { normalizeRoutePath } from '@vuepress/shared' +import { redirects, routes } from './routes.js' + +/** + * Resolve route path with given raw path + */ +export const resolveRoutePath = (path: string): string => { + // normalized path + const normalizedPath = normalizeRoutePath(path) + if (routes.value[normalizedPath]) return normalizedPath + + // encoded path + const encodedPath = encodeURI(normalizedPath) + if (routes.value[encodedPath]) return encodedPath + + // redirected path or fallback to the normalized path + return redirects.value[normalizedPath] || normalizedPath +} diff --git a/packages/client/src/router/routes.ts b/packages/client/src/router/routes.ts new file mode 100644 index 0000000000..b523b9b5be --- /dev/null +++ b/packages/client/src/router/routes.ts @@ -0,0 +1,35 @@ +import { + redirects as redirectsRaw, + routes as routesRaw, +} from '@internal/routes' +import type { + PageChunk, + PageMetaDefault, + Redirects, + Route, + Routes, +} from '@internal/routes' +import { shallowRef } from 'vue' +import type { Ref } from 'vue' + +export type { PageMetaDefault, PageChunk, Redirects, Route, Routes } + +/** + * Global redirects ref + */ +export const redirects: Ref = shallowRef(redirectsRaw) + +/** + * Global routes ref + */ +export const routes: Ref = shallowRef(routesRaw) + +if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { + // reuse vue HMR runtime + __VUE_HMR_RUNTIME__.updateRoutes = (data: Routes) => { + routes.value = data + } + __VUE_HMR_RUNTIME__.updateRedirects = (data: Redirects) => { + redirects.value = data + } +} diff --git a/packages/client/src/setupGlobalComputed.ts b/packages/client/src/setupGlobalComputed.ts index 67b77947c1..7554a76cd9 100644 --- a/packages/client/src/setupGlobalComputed.ts +++ b/packages/client/src/setupGlobalComputed.ts @@ -36,7 +36,7 @@ import { } from './composables/index.js' import { withBase } from './helpers/index.js' import { resolvers } from './resolvers.js' -import { pagesMap } from './router/index.js' +import { routes } from './router/index.js' import type { ClientConfig } from './types/index.js' /** @@ -74,8 +74,8 @@ export const setupGlobalComputed = ( // handle page data HMR if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { __VUE_HMR_RUNTIME__.updatePageData = async (data: PageData) => { - const pageChunk = await pagesMap.value[data.path].loader() - pagesMap.value[data.path].loader = () => + const pageChunk = await routes.value[data.path].loader() + routes.value[data.path].loader = () => Promise.resolve({ comp: pageChunk.comp, data }) if (data.path === router.currentRoute.value.meta._data?.path) { router.currentRoute.value.meta._data = data diff --git a/packages/client/src/types/internal/pagesMap.d.ts b/packages/client/src/types/internal/pagesMap.d.ts deleted file mode 100644 index 79b3277f61..0000000000 --- a/packages/client/src/types/internal/pagesMap.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { PageData } from '@vuepress/shared' -import type { ComponentOptions } from 'vue' - -declare module '@internal/pagesMap' { - export interface PageChunk { - comp: ComponentOptions - data: PageData - } - - export type PageMetaDefault = Record - - export interface PageMapItem< - PageMeta extends PageMetaDefault = PageMetaDefault, - > { - loader: () => Promise - meta: PageMeta - } - - export type PagesMap = Record - export type RedirectsMap = Record - - export const pagesMap: PagesMap - export const redirectsMap: RedirectsMap -} diff --git a/packages/client/src/types/internal/routes.d.ts b/packages/client/src/types/internal/routes.d.ts new file mode 100644 index 0000000000..96d8109469 --- /dev/null +++ b/packages/client/src/types/internal/routes.d.ts @@ -0,0 +1,22 @@ +import type { PageData } from '@vuepress/shared' +import type { ComponentOptions } from 'vue' + +declare module '@internal/routes' { + export interface PageChunk { + comp: ComponentOptions + data: PageData + } + + export type PageMetaDefault = Record + + export interface Route { + loader: () => Promise + meta: PageMeta + } + + export type Redirects = Record + export type Routes = Record + + export const redirects: Redirects + export const routes: Routes +} diff --git a/packages/core/src/app/appPrepare.ts b/packages/core/src/app/appPrepare.ts index 0d01ca366d..13195ffed7 100644 --- a/packages/core/src/app/appPrepare.ts +++ b/packages/core/src/app/appPrepare.ts @@ -4,7 +4,7 @@ import { prepareClientConfigs, preparePage, preparePageComponent, - preparePagesMap, + prepareRoutes, prepareSiteData, } from './prepare/index.js' @@ -31,8 +31,8 @@ export const appPrepare = async (app: App): Promise => { await preparePage(app, page) } - // generate pages map - await preparePagesMap(app) + // generate routes file + await prepareRoutes(app) // generate site data file await prepareSiteData(app) diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index 22b147004f..c4e8d36228 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,5 +1,5 @@ export * from './prepareClientConfigs.js' export * from './preparePage.js' export * from './preparePageComponent.js' -export * from './preparePagesMap.js' +export * from './prepareRoutes.js' export * from './prepareSiteData.js' diff --git a/packages/core/src/app/prepare/preparePagesMap.ts b/packages/core/src/app/prepare/prepareRoutes.ts similarity index 62% rename from packages/core/src/app/prepare/preparePagesMap.ts rename to packages/core/src/app/prepare/prepareRoutes.ts index 9aaa6fcadf..c12f2d7dfe 100644 --- a/packages/core/src/app/prepare/preparePagesMap.ts +++ b/packages/core/src/app/prepare/prepareRoutes.ts @@ -1,27 +1,27 @@ -import { ensureLeadingSlash, normalizePath } from '@vuepress/shared' +import { ensureLeadingSlash, normalizeRoutePath } from '@vuepress/shared' import type { App, Page } from '../../types/index.js' const HMR_CODE = ` if (import.meta.webpackHot) { import.meta.webpackHot.accept() - if (__VUE_HMR_RUNTIME__.updatePagesMap) { - __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) + if (__VUE_HMR_RUNTIME__.updateRoutes) { + __VUE_HMR_RUNTIME__.updateRoutes(routes) } - if (__VUE_HMR_RUNTIME__.updateRedirectsMap) { - __VUE_HMR_RUNTIME__.updateRedirectsMap(redirectsMap) + if (__VUE_HMR_RUNTIME__.updateRedirects) { + __VUE_HMR_RUNTIME__.updateRedirects(redirects) } } if (import.meta.hot) { - import.meta.hot.accept(({ pagesMap, redirectsMap }) => { - __VUE_HMR_RUNTIME__.updatePagesMap(pagesMap) - __VUE_HMR_RUNTIME__.updateRedirectsMap(redirectsMap) + import.meta.hot.accept(({ routes, redirects }) => { + __VUE_HMR_RUNTIME__.updateRoutes(routes) + __VUE_HMR_RUNTIME__.updateRedirects(redirects) }) } ` /** - * Resolve page route item + * Resolve page redirects */ const resolvePageRedirects = ({ path, @@ -33,7 +33,7 @@ const resolvePageRedirects = ({ // add redirect to the set when the redirect could not be normalized & encoded to the page path const addRedirect = (redirect: string): void => { - const normalizedPath = normalizePath(redirect) + const normalizedPath = normalizeRoutePath(redirect) if (normalizedPath === path) return const encodedPath = encodeURI(normalizedPath) @@ -56,12 +56,12 @@ const resolvePageRedirects = ({ } /** - * Generate page map temp file + * Generate routes temp file */ -export const preparePagesMap = async (app: App): Promise => { +export const prepareRoutes = async (app: App): Promise => { // generate page component map file let content = `\ -export const redirectsMap = JSON.parse(${JSON.stringify( +export const redirects = JSON.parse(${JSON.stringify( JSON.stringify( Object.fromEntries( app.pages.flatMap((page) => @@ -71,11 +71,11 @@ export const redirectsMap = JSON.parse(${JSON.stringify( ), )}) -export const pagesMap = Object.fromEntries([ +export const routes = Object.fromEntries([ ${app.pages .map( - ({ meta, path, chunkFilePath, chunkName }) => - ` [${JSON.stringify(path)}, { loader: () => import(${chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''}${JSON.stringify(chunkFilePath)}), meta: ${JSON.stringify(meta)} }],`, + ({ chunkFilePath, chunkName, path, routeMeta }) => + ` [${JSON.stringify(path)}, { loader: () => import(${chunkName ? `/* webpackChunkName: "${chunkName}" */` : ''}${JSON.stringify(chunkFilePath)}), meta: ${JSON.stringify(routeMeta)} }],`, ) .join('\n')} ]); @@ -86,5 +86,5 @@ ${app.pages content += HMR_CODE } - await app.writeTemp('internal/pagesMap.js', content) + await app.writeTemp('internal/routes.js', content) } diff --git a/packages/core/src/page/createPage.ts b/packages/core/src/page/createPage.ts index 32b6717eed..ab9b05deb6 100644 --- a/packages/core/src/page/createPage.ts +++ b/packages/core/src/page/createPage.ts @@ -8,9 +8,9 @@ import { resolvePageFileContent } from './resolvePageFileContent.js' import { resolvePageFilePath } from './resolvePageFilePath.js' import { resolvePageHtmlInfo } from './resolvePageHtmlInfo.js' import { resolvePageLang } from './resolvePageLang.js' -import { resolvePageMeta } from './resolvePageMeta.js' import { resolvePagePath } from './resolvePagePath.js' import { resolvePagePermalink } from './resolvePagePermalink.js' +import { resolvePageRouteMeta } from './resolvePageRouteMeta.js' import { resolvePageSlug } from './resolvePageSlug.js' export const createPage = async ( @@ -48,7 +48,7 @@ export const createPage = async ( }) // resolve route meta from frontmatter - const meta = resolvePageMeta({ frontmatter }) + const routeMeta = resolvePageRouteMeta({ frontmatter }) // resolve slug from file path const slug = resolvePageSlug({ filePathRelative }) @@ -118,7 +118,7 @@ export const createPage = async ( pathInferred, pathLocale, permalink, - meta, + routeMeta, sfcBlocks, slug, @@ -132,17 +132,6 @@ export const createPage = async ( chunkName, htmlFilePath, htmlFilePathRelative, - - // TODO: Added for backwards compatibility, remove in next major version - // @ts-expect-error use meta instead - routeMeta: new Proxy(meta, { - set: (obj, prop, value) => { - console.warn('routeMeta is deprecated, please use meta instead') - - obj[prop as string] = value - return true - }, - }), } // plugin hook: extendsPage diff --git a/packages/core/src/page/index.ts b/packages/core/src/page/index.ts index d7e4a9e522..179d9f4cc6 100644 --- a/packages/core/src/page/index.ts +++ b/packages/core/src/page/index.ts @@ -10,5 +10,5 @@ export * from './resolvePageHtmlInfo.js' export * from './resolvePageLang.js' export * from './resolvePagePath.js' export * from './resolvePagePermalink.js' -export * from './resolvePageMeta.js' +export * from './resolvePageRouteMeta.js' export * from './resolvePageSlug.js' diff --git a/packages/core/src/page/resolvePageMeta.ts b/packages/core/src/page/resolvePageMeta.ts deleted file mode 100644 index e532cabb09..0000000000 --- a/packages/core/src/page/resolvePageMeta.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { PageFrontmatter } from '../types/index.js' - -/** - * Resolve page route meta - */ -export const resolvePageMeta = ({ - frontmatter, -}: { - frontmatter: PageFrontmatter -}): Record => { - // TODO: Added for backwards compatibility, should be removed in stable - if (frontmatter.routeMeta) { - console.warn('routeMeta is deprecated, please use meta instead') - frontmatter.meta = { ...frontmatter.routeMeta, ...frontmatter.meta } - } - - return frontmatter.meta ?? {} -} diff --git a/packages/core/src/page/resolvePageRouteMeta.ts b/packages/core/src/page/resolvePageRouteMeta.ts new file mode 100644 index 0000000000..76e5030a91 --- /dev/null +++ b/packages/core/src/page/resolvePageRouteMeta.ts @@ -0,0 +1,10 @@ +import type { PageFrontmatter } from '../types/index.js' + +/** + * Resolve page route meta + */ +export const resolvePageRouteMeta = ({ + frontmatter, +}: { + frontmatter: PageFrontmatter +}): Record => frontmatter.routeMeta ?? {} diff --git a/packages/core/src/types/page.ts b/packages/core/src/types/page.ts index a26ef8c865..303a9d41cd 100644 --- a/packages/core/src/types/page.ts +++ b/packages/core/src/types/page.ts @@ -73,11 +73,9 @@ export type Page< permalink: string | null /** - * Custom data to be attached to page record - * - * @see https://router.vuejs.org/api/#meta + * Custom data to be attached to route record */ - meta: Record + routeMeta: Record /** * Extracted sfc blocks of the page diff --git a/packages/core/tests/page/resolvePageMeta.spec.ts b/packages/core/tests/page/resolvePageMeta.spec.ts deleted file mode 100644 index 3ee0728c0d..0000000000 --- a/packages/core/tests/page/resolvePageMeta.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolvePageMeta } from '../../src/index.js' - -describe('core > page > resolvePageMeta', () => { - it('should use frontmatter meta', () => { - const meta = resolvePageMeta({ - frontmatter: { - meta: { - foo: 'foo', - }, - }, - }) - - expect(meta).toEqual({ - foo: 'foo', - }) - }) - - it('should return default value', () => { - const meta = resolvePageMeta({ - frontmatter: {}, - }) - - expect(meta).toEqual({}) - }) -}) diff --git a/packages/core/tests/page/resolvePageRouteMeta.spec.ts b/packages/core/tests/page/resolvePageRouteMeta.spec.ts new file mode 100644 index 0000000000..033ebebac3 --- /dev/null +++ b/packages/core/tests/page/resolvePageRouteMeta.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import { resolvePageRouteMeta } from '../../src/index.js' + +describe('core > page > resolvePageRouteMeta', () => { + it('should use frontmatter routeMeta', () => { + const routeMeta = resolvePageRouteMeta({ + frontmatter: { + routeMeta: { + foo: 'foo', + }, + }, + }) + + expect(routeMeta).toEqual({ + foo: 'foo', + }) + }) + + it('should return default value', () => { + const routeMeta = resolvePageRouteMeta({ + frontmatter: {}, + }) + + expect(routeMeta).toEqual({}) + }) +}) diff --git a/packages/shared/src/types/page.ts b/packages/shared/src/types/page.ts index 4c3d315a8f..f05a83ad2c 100644 --- a/packages/shared/src/types/page.ts +++ b/packages/shared/src/types/page.ts @@ -64,7 +64,7 @@ export type PageFrontmatter< layout?: string permalink?: string permalinkPattern?: string | null - meta?: Record + routeMeta?: Record title?: string } diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index 8427fc44c2..a14fe7c3d9 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -6,7 +6,7 @@ export * from './isLinkExternal.js' export * from './isLinkHttp.js' export * from './isLinkWithProtocol.js' export * from './isPlainObject.js' -export * from './normalizePath.js' +export * from './normalizeRoutePath.js' export * from './omit.js' export * from './removeEndingSlash.js' export * from './removeLeadingSlash.js' diff --git a/packages/shared/src/utils/normalizePath.ts b/packages/shared/src/utils/normalizeRoutePath.ts similarity index 78% rename from packages/shared/src/utils/normalizePath.ts rename to packages/shared/src/utils/normalizeRoutePath.ts index e7ec96ccbd..c3924ac90b 100644 --- a/packages/shared/src/utils/normalizePath.ts +++ b/packages/shared/src/utils/normalizeRoutePath.ts @@ -1,4 +1,7 @@ -export const normalizePath = (path: string): string => { +/** + * Normalize the given path to the final route path + */ +export const normalizeRoutePath = (path: string): string => { const convertedMdPath = path.endsWith('README.md') ? path.substring(0, path.length - 9) : path.endsWith('.md') diff --git a/packages/shared/tests/normalizePath.spec.ts b/packages/shared/tests/normalizeRoutePath.spec.ts similarity index 86% rename from packages/shared/tests/normalizePath.spec.ts rename to packages/shared/tests/normalizeRoutePath.spec.ts index 8c4c6f8f79..b448ffceeb 100644 --- a/packages/shared/tests/normalizePath.spec.ts +++ b/packages/shared/tests/normalizeRoutePath.spec.ts @@ -1,5 +1,5 @@ import { expect, it } from 'vitest' -import { normalizePath } from '../src/index.js' +import { normalizeRoutePath } from '../src/index.js' const testCases = [ ['/', '/'], @@ -23,6 +23,6 @@ const testCases = [ testCases.forEach(([path, expected]) => it(`should normalize "${path}" to "${expected}"`, () => { - expect(normalizePath(path)).toBe(expected) + expect(normalizeRoutePath(path)).toBe(expected) }), ) From 41448c4191c896c971b8ca81f1e8e24467a21027 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 19:11:17 +0800 Subject: [PATCH 28/31] chore: tweaks --- packages/client/src/router/resolveRoute.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index f1891cc979..37b5b373fd 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -16,10 +16,10 @@ export const resolveRoute = < path: string, ): ResolvedRoute => { const routePath = resolveRoutePath(path) - const pageMapItem = routes.value[routePath] || routes.value['/404.html'] + const route = routes.value[routePath] || routes.value['/404.html'] return { - ...(pageMapItem as ResolvedRoute), + ...route, path: routePath, - } + } as ResolvedRoute } From 362ba4b5e9885fd2f9df3f7e1622f555f55719f1 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 19:22:36 +0800 Subject: [PATCH 29/31] feat: add notFound property to resolved route --- e2e/docs/404.md | 2 +- e2e/tests/router/resolve-route.cy.ts | 9 ++++++++- packages/client/src/router/resolveRoute.ts | 9 +++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/e2e/docs/404.md b/e2e/docs/404.md index 951515198d..fac3cec274 100644 --- a/e2e/docs/404.md +++ b/e2e/docs/404.md @@ -1,4 +1,4 @@ --- routeMeta: - notFound: true + foo: bar --- diff --git a/e2e/tests/router/resolve-route.cy.ts b/e2e/tests/router/resolve-route.cy.ts index 2f85ef0cf2..360df88089 100644 --- a/e2e/tests/router/resolve-route.cy.ts +++ b/e2e/tests/router/resolve-route.cy.ts @@ -4,6 +4,7 @@ const testCases = [ expected: { path: '/', meta: {}, + notFound: false, }, }, { @@ -11,6 +12,7 @@ const testCases = [ expected: { path: '/router/resolve-route.html', meta: {}, + notFound: false, }, }, { @@ -18,6 +20,7 @@ const testCases = [ expected: { path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), meta: {}, + notFound: false, }, }, { @@ -25,13 +28,15 @@ const testCases = [ expected: { path: encodeURI('/routes/non-ascii-paths/中文目录名/中文文件名.html'), meta: {}, + notFound: false, }, }, { selector: '#non-existent', expected: { path: '/non-existent.html', - meta: { notFound: true }, + meta: { foo: 'bar' }, + notFound: true, }, }, { @@ -39,6 +44,7 @@ const testCases = [ expected: { path: '/page-data/route-meta.html', meta: { a: 0, b: 2, c: 3 }, + notFound: false, }, }, ] @@ -54,6 +60,7 @@ it('should resolve routes correctly', () => { const resolvedRoute = parseResolvedRouteFromElement(el) expect(resolvedRoute.path).to.equal(expected.path) expect(resolvedRoute.meta).to.deep.equal(expected.meta) + expect(resolvedRoute.notFound).to.equal(expected.notFound) }) }) }) diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index 37b5b373fd..e15109ebfd 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -5,6 +5,7 @@ import { routes } from './routes.js' interface ResolvedRoute extends Route { path: string + notFound: boolean } /** @@ -16,10 +17,14 @@ export const resolveRoute = < path: string, ): ResolvedRoute => { const routePath = resolveRoutePath(path) - const route = routes.value[routePath] || routes.value['/404.html'] + const route = routes.value[routePath] ?? { + ...routes.value['/404.html'], + notFound: true, + } return { - ...route, path: routePath, + notFound: false, + ...route, } as ResolvedRoute } From 85371551f48ea52acb86660bb615da560cdc8e14 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 19:35:28 +0800 Subject: [PATCH 30/31] chore: tweaks --- .../cli/src/commands/dev/handlePageAdd.ts | 4 ++-- .../cli/src/commands/dev/handlePageChange.ts | 4 ++-- packages/core/src/app/appPrepare.ts | 6 ++--- packages/core/src/app/prepare/index.ts | 2 +- .../{preparePage.ts => preparePageChunk.ts} | 6 ++--- .../core/src/app/prepare/prepareRoutes.ts | 2 +- .../tests/page/resolvePageChunkInfo.spec.ts | 24 +++++++++++++++++++ 7 files changed, 36 insertions(+), 12 deletions(-) rename packages/core/src/app/prepare/{preparePage.ts => preparePageChunk.ts} (80%) create mode 100644 packages/core/tests/page/resolvePageChunkInfo.spec.ts diff --git a/packages/cli/src/commands/dev/handlePageAdd.ts b/packages/cli/src/commands/dev/handlePageAdd.ts index 9390e1a96c..8f4b367975 100644 --- a/packages/cli/src/commands/dev/handlePageAdd.ts +++ b/packages/cli/src/commands/dev/handlePageAdd.ts @@ -1,6 +1,6 @@ import { createPage, - preparePage, + preparePageChunk, preparePageComponent, prepareRoutes, } from '@vuepress/core' @@ -31,7 +31,7 @@ export const handlePageAdd = async ( // prepare page files await preparePageComponent(app, page) - await preparePage(app, page) + await preparePageChunk(app, page) // prepare routes file await prepareRoutes(app) diff --git a/packages/cli/src/commands/dev/handlePageChange.ts b/packages/cli/src/commands/dev/handlePageChange.ts index 2a79b4f639..6eeff179aa 100644 --- a/packages/cli/src/commands/dev/handlePageChange.ts +++ b/packages/cli/src/commands/dev/handlePageChange.ts @@ -1,6 +1,6 @@ import { createPage, - preparePage, + preparePageChunk, preparePageComponent, prepareRoutes, } from '@vuepress/core' @@ -34,7 +34,7 @@ export const handlePageChange = async ( // prepare page files await preparePageComponent(app, pageNew) - await preparePage(app, pageNew) + await preparePageChunk(app, pageNew) const isPathChanged = pageOld.path !== pageNew.path const isRouteMetaChanged = diff --git a/packages/core/src/app/appPrepare.ts b/packages/core/src/app/appPrepare.ts index 13195ffed7..e2552022ca 100644 --- a/packages/core/src/app/appPrepare.ts +++ b/packages/core/src/app/appPrepare.ts @@ -2,7 +2,7 @@ import { debug } from '@vuepress/utils' import type { App } from '../types/index.js' import { prepareClientConfigs, - preparePage, + preparePageChunk, preparePageComponent, prepareRoutes, prepareSiteData, @@ -26,9 +26,9 @@ export const appPrepare = async (app: App): Promise => { await preparePageComponent(app, page) } - // generate page data files + // generate page files for (const page of app.pages) { - await preparePage(app, page) + await preparePageChunk(app, page) } // generate routes file diff --git a/packages/core/src/app/prepare/index.ts b/packages/core/src/app/prepare/index.ts index c4e8d36228..ea49aa7600 100644 --- a/packages/core/src/app/prepare/index.ts +++ b/packages/core/src/app/prepare/index.ts @@ -1,5 +1,5 @@ export * from './prepareClientConfigs.js' -export * from './preparePage.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/preparePage.ts b/packages/core/src/app/prepare/preparePageChunk.ts similarity index 80% rename from packages/core/src/app/prepare/preparePage.ts rename to packages/core/src/app/prepare/preparePageChunk.ts index 8877e70b2f..353a98eee1 100644 --- a/packages/core/src/app/prepare/preparePage.ts +++ b/packages/core/src/app/prepare/preparePageChunk.ts @@ -16,10 +16,10 @@ if (import.meta.hot) { ` /** - * Generate page temp file of a single page + * Generate page chunk temp file of a single page */ -export const preparePage = async (app: App, page: Page): Promise => { - // page data file content +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))}) diff --git a/packages/core/src/app/prepare/prepareRoutes.ts b/packages/core/src/app/prepare/prepareRoutes.ts index c12f2d7dfe..969ed879c1 100644 --- a/packages/core/src/app/prepare/prepareRoutes.ts +++ b/packages/core/src/app/prepare/prepareRoutes.ts @@ -59,7 +59,7 @@ const resolvePageRedirects = ({ * Generate routes temp file */ export const prepareRoutes = async (app: App): Promise => { - // generate page component map file + // routes file content let content = `\ export const redirects = JSON.parse(${JSON.stringify( JSON.stringify( diff --git a/packages/core/tests/page/resolvePageChunkInfo.spec.ts b/packages/core/tests/page/resolvePageChunkInfo.spec.ts new file mode 100644 index 0000000000..46e4ce6cce --- /dev/null +++ b/packages/core/tests/page/resolvePageChunkInfo.spec.ts @@ -0,0 +1,24 @@ +import { hash, path } from '@vuepress/utils' +import { describe, expect, it } from 'vitest' +import { createBaseApp, resolvePageChunkInfo } from '../../src/index.js' + +const app = createBaseApp({ + source: path.resolve(__dirname, 'fake-source'), + theme: { name: 'test' }, + bundler: {} as any, +}) + +describe('core > page > resolvePageChunkInfo', () => { + it('should resolve page chunk info correctly', () => { + const resolved = resolvePageChunkInfo({ + app, + htmlFilePathRelative: 'foo.html', + }) + + expect(resolved).toEqual({ + chunkFilePath: app.dir.temp('pages/foo.html.js'), + chunkFilePathRelative: 'pages/foo.html.js', + chunkName: `v-${hash('foo.html')}`, + }) + }) +}) From 769859c00ea9e658d755ed2b8659f9957b24a1ef Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Sat, 3 Feb 2024 19:40:59 +0800 Subject: [PATCH 31/31] chore: tweak comments --- packages/core/src/types/page.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/types/page.ts b/packages/core/src/types/page.ts index 303a9d41cd..5aadd8d7ae 100644 --- a/packages/core/src/types/page.ts +++ b/packages/core/src/types/page.ts @@ -112,19 +112,19 @@ export type Page< componentFilePathRelative: string /** - * Page file path + * Chunk file path */ chunkFilePath: string /** - * Page file path relative to temp directory + * Chunk file path relative to temp directory */ chunkFilePathRelative: string /** - * Page file chunk name + * Chunk name * - * Only take effect in webpack + * This will only take effect in webpack */ chunkName: string