From 23276bae050190b6c1d57347424360fe2c3a57be Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Tue, 14 Jun 2022 13:31:21 +0530 Subject: [PATCH] feat: emit 404.html on build (#729) (#740) close #729 Co-authored-by: Kia Ishii --- src/client/app/router.ts | 13 ++-------- src/node/build/build.ts | 4 ++- src/node/build/render.ts | 55 +++++++++++++++++++++++++--------------- src/shared/shared.ts | 9 +++++++ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 2eca5b7b41c6..57398144cc72 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -1,6 +1,6 @@ import { reactive, inject, markRaw, nextTick, readonly } from 'vue' import type { Component, InjectionKey } from 'vue' -import { PageData } from '../shared' +import { PageData, notFoundPageData } from '../shared' import { inBrowser, withBase } from './utils' import { siteDataRef } from './data' @@ -21,15 +21,6 @@ export const RouterSymbol: InjectionKey = Symbol() // matter and is only passed to support same-host hrefs. const fakeHost = `http://a.com` -const notFoundPageData: PageData = { - relativePath: '', - title: '404', - description: 'Not Found', - headers: [], - frontmatter: {}, - lastUpdated: 0 -} - const getDefaultRoute = (): Route => ({ path: '/', component: null, @@ -109,7 +100,7 @@ export function createRouter( } } } catch (err: any) { - if (!err.message.match(/fetch/)) { + if (!err.message.match(/fetch/) && !href.match(/^[\\/]404\.html$/)) { console.error(err) } diff --git a/src/node/build/build.ts b/src/node/build/build.ts index 91c08742316e..f35ad51059ee 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -49,7 +49,9 @@ export async function build( // as JS object literal. const hashMapString = JSON.stringify(JSON.stringify(pageToHashMap)) - for (const page of siteConfig.pages) { + const pages = ['404.md', ...siteConfig.pages] + + for (const page of pages) { await renderPage( siteConfig, page, diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 3345342a10d5..7be6ee2584eb 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -5,7 +5,7 @@ import { pathToFileURL } from 'url' import escape from 'escape-html' import { normalizePath, transformWithEsbuild } from 'vite' import { RollupOutput, OutputChunk, OutputAsset } from 'rollup' -import { HeadConfig, createTitle } from '../shared' +import { HeadConfig, PageData, createTitle, notFoundPageData } from '../shared' import { slash } from '../utils/slash' import { SiteConfig, resolveSiteDataByRoute } from '../config' @@ -52,28 +52,41 @@ export async function renderPage( const pageHash = pageToHashMap[pageName.toLowerCase()] const pageClientJsFileName = `assets/${pageName}.${pageHash}.lean.js` - // resolve page data so we can render head tags - const { __pageData } = await import( - pathToFileURL(path.join(config.tempDir, pageServerJsFileName)).toString() - ) - const pageData = JSON.parse(__pageData) + let pageData: PageData + let hasCustom404 = true + + try { + // resolve page data so we can render head tags + const { __pageData } = await import( + pathToFileURL(path.join(config.tempDir, pageServerJsFileName)).toString() + ) + pageData = JSON.parse(__pageData) + } catch (e) { + if (page === '404.md') { + hasCustom404 = false + pageData = notFoundPageData + } else { + throw e + } + } - let preloadLinks = config.mpa - ? appChunk - ? [appChunk.fileName] + let preloadLinks = + config.mpa || (!hasCustom404 && page === '404.md') + ? appChunk + ? [appChunk.fileName] + : [] + : result && appChunk + ? [ + ...new Set([ + // resolve imports for index.js + page.md.js and inject script tags + // for them as well so we fetch everything as early as possible + // without having to wait for entry chunks to parse + ...resolvePageImports(config, page, result, appChunk), + pageClientJsFileName, + appChunk.fileName + ]) + ] : [] - : result && appChunk - ? [ - ...new Set([ - // resolve imports for index.js + page.md.js and inject script tags for - // them as well so we fetch everything as early as possible without having - // to wait for entry chunks to parse - ...resolvePageImports(config, page, result, appChunk), - pageClientJsFileName, - appChunk.fileName - ]) - ] - : [] let prefetchLinks: string[] = [] diff --git a/src/shared/shared.ts b/src/shared/shared.ts index 895e47d20086..7db6114e07a8 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -16,6 +16,15 @@ export const APPEARANCE_KEY = 'vitepress-theme-appearance' // @ts-ignore export const inBrowser = typeof window !== 'undefined' +export const notFoundPageData: PageData = { + relativePath: '', + title: '404', + description: 'Not Found', + headers: [], + frontmatter: {}, + lastUpdated: 0 +} + function findMatchRoot(route: string, roots: string[]): string | undefined { // first match to the routes with the most deep level. roots.sort((a, b) => {