From e0b730aa8ee9bec1fe16245c4c1a1a91f62bed42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez?= Date: Sat, 9 Jul 2022 17:00:25 +0200 Subject: [PATCH 01/10] feat: provide build end hook (#709) Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> --- src/node/build/build.ts | 2 ++ src/node/config.ts | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/node/build/build.ts b/src/node/build/build.ts index d8c34f3f..5936a61f 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -87,5 +87,7 @@ export async function build( await fs.remove(siteConfig.tempDir) } + await siteConfig.buildEnd?.(siteConfig) + console.log(`build complete in ${((Date.now() - start) / 1000).toFixed(2)}s.`) } diff --git a/src/node/config.ts b/src/node/config.ts index 7560020e..223238c7 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -71,6 +71,12 @@ export interface UserConfig { * @default false */ ignoreDeadLinks?: boolean + + /** + * Build end hook: called when SSG finish. + * @param siteConfig The resolved configuration. + */ + buildEnd?: (siteConfig: SiteConfig) => Promise } export type RawConfigExports = @@ -88,6 +94,7 @@ export interface SiteConfig | 'mpa' | 'lastUpdated' | 'ignoreDeadLinks' + | 'buildEnd' > { root: string srcDir: string @@ -166,7 +173,8 @@ export async function resolveConfig( vite: userConfig.vite, shouldPreload: userConfig.shouldPreload, mpa: !!userConfig.mpa, - ignoreDeadLinks: userConfig.ignoreDeadLinks + ignoreDeadLinks: userConfig.ignoreDeadLinks, + buildEnd: userConfig.buildEnd } return config From 30249dc2c3d933dadf6e22e64728a6ffd3647f8e Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:25:49 +0530 Subject: [PATCH 02/10] fix: regression caused by #887 --- src/client/theme-default/composables/sidebar.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/theme-default/composables/sidebar.ts b/src/client/theme-default/composables/sidebar.ts index 7baedde4..123fe9cc 100644 --- a/src/client/theme-default/composables/sidebar.ts +++ b/src/client/theme-default/composables/sidebar.ts @@ -10,7 +10,8 @@ export function useSidebar() { const sidebar = computed(() => { const sidebarConfig = theme.value.sidebar - return sidebarConfig ? getSidebar(sidebarConfig, route.path) : [] + const relativePath = route.data.relativePath + return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [] }) const hasSidebar = computed(() => { From d91f3b1b7d46db8009bb459079794b0626488033 Mon Sep 17 00:00:00 2001 From: Kaz <54239670+object-kaz@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:42:08 +0800 Subject: [PATCH 03/10] fix(theme): tweak styles of nav title (#962) (#968) Co-authored-by: ObjectKaz --- src/client/theme-default/components/VPSidebar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/components/VPSidebar.vue b/src/client/theme-default/components/VPSidebar.vue index 7602d68b..f39a7d99 100644 --- a/src/client/theme-default/components/VPSidebar.vue +++ b/src/client/theme-default/components/VPSidebar.vue @@ -94,7 +94,7 @@ watchPostEffect(async () => { @media (min-width: 1440px) { .VPSidebar { - padding-left: calc((100% - (var(--vp-layout-max-width) - 64px)) / 2); + padding-left: max(32px, calc((100% - (var(--vp-layout-max-width) - 64px)) / 2)); width: calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px); } } From 0257ea88dca09ced9c1dc6e53ba5f133c468df19 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:00:57 +0530 Subject: [PATCH 04/10] fix: layout inconsistencies and remove sidebar from 404 page (#964) --- src/node/serve/serve.ts | 17 +++++++++++++---- src/shared/shared.ts | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/node/serve/serve.ts b/src/node/serve/serve.ts index 6b81e306..c099b3d8 100644 --- a/src/node/serve/serve.ts +++ b/src/node/serve/serve.ts @@ -1,3 +1,5 @@ +import fs from 'fs' +import path from 'path' import sirv from 'sirv' import compression from 'compression' import polka from 'polka' @@ -26,14 +28,21 @@ export async function serve(options: ServeOptions = {}) { const site = await resolveConfig(options.root, 'serve', 'production') const base = trimChar(options?.base ?? site?.site?.base ?? '', '/') + const notAnAsset = (pathname: string) => !pathname.includes('/assets/') + const notFound = fs.readFileSync(path.resolve(site.outDir, './404.html')) + const onNoMatch: polka.Options['onNoMatch'] = (req, res) => { + res.statusCode = 404 + if (notAnAsset(req.path)) res.write(notFound.toString()) + res.end() + } + const compress = compression() const serve = sirv(site.outDir, { etag: true, - single: true, maxAge: 31536000, immutable: true, setHeaders(res, pathname) { - if (!pathname.includes('/assets/')) { + if (notAnAsset(pathname)) { // force server validation for non-asset files since they // are not fingerprinted res.setHeader('cache-control', 'no-cache') @@ -42,14 +51,14 @@ export async function serve(options: ServeOptions = {}) { }) if (base) { - polka() + polka({ onNoMatch }) .use(base, compress, serve) .listen(port, (err: any) => { if (err) throw err console.log(`Built site served at http://localhost:${port}/${base}/\n`) }) } else { - polka() + polka({ onNoMatch }) .use(compress, serve) .listen(port, (err: any) => { if (err) throw err diff --git a/src/shared/shared.ts b/src/shared/shared.ts index 959d470c..67cb0b94 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -21,7 +21,7 @@ export const notFoundPageData: PageData = { title: '404', description: 'Not Found', headers: [], - frontmatter: {}, + frontmatter: { sidebar: false, layout: 'page' }, lastUpdated: 0 } From 7a9e4d9ee02cc677f1b84cc5f2c1f8f385c3a65c Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:07:05 +0530 Subject: [PATCH 05/10] fix: line highlighting in custom code block (#959) (#969) --- src/client/theme-default/styles/components/vp-doc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/styles/components/vp-doc.css b/src/client/theme-default/styles/components/vp-doc.css index 29128d3a..c8c64a94 100644 --- a/src/client/theme-default/styles/components/vp-doc.css +++ b/src/client/theme-default/styles/components/vp-doc.css @@ -222,7 +222,7 @@ .vp-doc .custom-block div[class*='language-'] code { font-weight: 400; - background-color: var(--vp-code-block-bg); + background-color: transparent; } /** From 98e45af127a11bfff3577fe5675788e5479f9d79 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Tue, 12 Jul 2022 16:58:02 +0800 Subject: [PATCH 06/10] fix: can't detect that the page has scrolled to the bottom (#956) (#970) --- src/client/theme-default/composables/outline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/composables/outline.ts b/src/client/theme-default/composables/outline.ts index 698aeda6..355e9bc4 100644 --- a/src/client/theme-default/composables/outline.ts +++ b/src/client/theme-default/composables/outline.ts @@ -105,7 +105,7 @@ export function useActiveAnchor( const scrollY = window.scrollY const innerHeight = window.innerHeight const offsetHeight = document.body.offsetHeight - const isBottom = scrollY + innerHeight === offsetHeight + const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1 // page bottom - highlight last one if (anchors.length && isBottom) { From 65ec9d740b00684530750695ce7e9ab09e72a5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E6=B8=B8=E5=90=9B?= Date: Wed, 13 Jul 2022 14:42:14 +0800 Subject: [PATCH 07/10] chore: remove repeated css property (#974) --- src/client/theme-default/components/VPSidebar.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/theme-default/components/VPSidebar.vue b/src/client/theme-default/components/VPSidebar.vue index f39a7d99..ffa3047c 100644 --- a/src/client/theme-default/components/VPSidebar.vue +++ b/src/client/theme-default/components/VPSidebar.vue @@ -83,7 +83,6 @@ watchPostEffect(async () => { padding-bottom: 128px; width: var(--vp-sidebar-width); max-width: 100%; - width: var(--vp-sidebar-width); background-color: var(--vp-c-bg-alt); opacity: 1; visibility: visible; From f7e9cfeb3a06ec93726870dd17116a019959d980 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 13 Jul 2022 22:49:06 +0530 Subject: [PATCH 08/10] fix: de-duplicate head tags while merging (#975) (#976) --- src/client/app/composables/head.ts | 50 ++++++------------------------ src/node/build/render.ts | 16 +++++++--- src/node/config.ts | 2 +- src/shared/shared.ts | 20 +++++++++++- 4 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/client/app/composables/head.ts b/src/client/app/composables/head.ts index 91a46d21..d74d6b5c 100644 --- a/src/client/app/composables/head.ts +++ b/src/client/app/composables/head.ts @@ -1,5 +1,5 @@ import { watchEffect, Ref } from 'vue' -import { HeadConfig, SiteData, createTitle } from '../../shared' +import { HeadConfig, SiteData, createTitle, mergeHead } from '../../shared' import { Route } from '../router' export function useUpdateHead(route: Route, siteDataByRouteRef: Ref) { @@ -14,50 +14,20 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref) { return } - const newEls: HTMLElement[] = [] - const commonLength = Math.min(managedHeadTags.length, newTags.length) - for (let i = 0; i < commonLength; i++) { - let el = managedHeadTags[i] - const [tag, attrs, innerHTML = ''] = newTags[i] - if (el.tagName.toLocaleLowerCase() === tag) { - for (const key in attrs) { - if (el.getAttribute(key) !== attrs[key]) { - el.setAttribute(key, attrs[key]) - } - } - for (let i = 0; i < el.attributes.length; i++) { - const name = el.attributes[i].name - if (!(name in attrs)) { - el.removeAttribute(name) - } - } - if (el.innerHTML !== innerHTML) { - el.innerHTML = innerHTML - } - } else { - document.head.removeChild(el) - el = createHeadElement(newTags[i]) - document.head.append(el) - } - newEls.push(el) - } - - managedHeadTags - .slice(commonLength) - .forEach((el) => document.head.removeChild(el)) - newTags.slice(commonLength).forEach((headConfig) => { + managedHeadTags.forEach((el) => document.head.removeChild(el)) + managedHeadTags = [] + newTags.forEach((headConfig) => { const el = createHeadElement(headConfig) document.head.appendChild(el) - newEls.push(el) + managedHeadTags.push(el) }) - managedHeadTags = newEls } watchEffect(() => { const pageData = route.data const siteData = siteDataByRouteRef.value const pageDescription = pageData && pageData.description - const frontmatterHead = pageData && pageData.frontmatter.head + const frontmatterHead = (pageData && pageData.frontmatter.head) || [] // update title and description document.title = createTitle(siteData, pageData) @@ -66,11 +36,9 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref) { .querySelector(`meta[name=description]`)! .setAttribute('content', pageDescription || siteData.description) - updateHeadTags([ - // site head can only change during dev - ...(import.meta.env.DEV ? siteData.head : []), - ...(frontmatterHead ? filterOutHeadDescription(frontmatterHead) : []) - ]) + updateHeadTags( + mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)) + ) }) } diff --git a/src/node/build/render.ts b/src/node/build/render.ts index c4330e3e..a03729e5 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -5,7 +5,13 @@ import { pathToFileURL } from 'url' import escape from 'escape-html' import { normalizePath, transformWithEsbuild } from 'vite' import { RollupOutput, OutputChunk, OutputAsset } from 'rollup' -import { HeadConfig, PageData, createTitle, notFoundPageData } from '../shared' +import { + HeadConfig, + PageData, + createTitle, + notFoundPageData, + mergeHead +} from '../shared' import { slash } from '../utils/slash' import { SiteConfig, resolveSiteDataByRoute } from '../config' @@ -115,10 +121,10 @@ export async function renderPage( const title: string = createTitle(siteData, pageData) const description: string = pageData.description || siteData.description - const head = [ - ...siteData.head, - ...filterOutHeadDescription(pageData.frontmatter.head) - ] + const head = mergeHead( + siteData.head, + filterOutHeadDescription(pageData.frontmatter.head) + ) let inlinedScript = '' if (config.mpa && result) { diff --git a/src/node/config.ts b/src/node/config.ts index 223238c7..1fac1d1b 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -285,7 +285,7 @@ function resolveSiteDataHead(userConfig?: UserConfig): HeadConfig[] { if (userConfig?.appearance ?? true) { head.push([ 'script', - {}, + { id: 'check-dark-light' }, ` ;(() => { const saved = localStorage.getItem('${APPEARANCE_KEY}') diff --git a/src/shared/shared.ts b/src/shared/shared.ts index 67cb0b94..e5041629 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -1,4 +1,9 @@ -import { SiteData, PageData, LocaleConfig } from '../../types/shared' +import { + SiteData, + PageData, + LocaleConfig, + HeadConfig +} from '../../types/shared' export type { SiteData, @@ -136,3 +141,16 @@ function cleanRoute(siteData: SiteData, route: string): string { return route.slice(baseWithoutSuffix.length) } + +function hasTag(head: HeadConfig[], tag: HeadConfig) { + const [tagType, tagAttrs] = tag + const keyAttr = Object.entries(tagAttrs)[0] // First key + if (keyAttr == null) return false + return head.some( + ([type, attrs]) => type === tagType && attrs[keyAttr[0]] === keyAttr[1] + ) +} + +export function mergeHead(prev: HeadConfig[], curr: HeadConfig[]) { + return [...prev.filter((tagAttrs) => !hasTag(curr, tagAttrs)), ...curr] +} From 1ef7a1857c2a8e2abc7c1859cd54504c144eab3b Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 13 Jul 2022 23:31:15 +0530 Subject: [PATCH 09/10] fix: only check for duplicate meta tags (#977) https://github.com/vuejs/vitepress/issues/975#issuecomment-1183507200 --- src/shared/shared.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/shared.ts b/src/shared/shared.ts index e5041629..98be7691 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -144,6 +144,7 @@ function cleanRoute(siteData: SiteData, route: string): string { function hasTag(head: HeadConfig[], tag: HeadConfig) { const [tagType, tagAttrs] = tag + if (tagType !== 'meta') return false const keyAttr = Object.entries(tagAttrs)[0] // First key if (keyAttr == null) return false return head.some( From e4c60ab3c834fe7f730cd7b0d64dd23c6d04dbed Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 13 Jul 2022 23:47:27 +0530 Subject: [PATCH 10/10] fix: remove explicit noopener from external links (#871) --- docs/guide/markdown.md | 2 +- src/client/theme-default/components/VPButton.vue | 2 +- src/client/theme-default/components/VPLink.vue | 2 +- src/client/theme-default/components/VPSocialLink.vue | 2 +- src/node/markdown/markdown.ts | 2 +- src/node/markdownToVue.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md index 342904e5..11c60ff7 100644 --- a/docs/guide/markdown.md +++ b/docs/guide/markdown.md @@ -46,7 +46,7 @@ Pages and internal links get generated with the `.html` suffix by default. ### External Links -Outbound links automatically get `target="_blank" rel="noopener noreferrer"`: +Outbound links automatically get `target="_blank" rel="noreferrer"`: - [vuejs.org](https://vuejs.org) - [VitePress on GitHub](https://github.com/vuejs/vitepress) diff --git a/src/client/theme-default/components/VPButton.vue b/src/client/theme-default/components/VPButton.vue index 714f3e0c..12d0a44a 100644 --- a/src/client/theme-default/components/VPButton.vue +++ b/src/client/theme-default/components/VPButton.vue @@ -34,7 +34,7 @@ const component = computed(() => { :class="classes" :href="href ? normalizeLink(href) : undefined" :target="isExternal ? '_blank' : undefined" - :rel="isExternal ? 'noopener noreferrer' : undefined" + :rel="isExternal ? 'noreferrer' : undefined" > {{ text }} diff --git a/src/client/theme-default/components/VPLink.vue b/src/client/theme-default/components/VPLink.vue index 3984f811..1a28c273 100644 --- a/src/client/theme-default/components/VPLink.vue +++ b/src/client/theme-default/components/VPLink.vue @@ -19,7 +19,7 @@ const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href) :class="{ link: href }" :href="href ? normalizeLink(href) : undefined" :target="isExternal ? '_blank' : undefined" - :rel="isExternal ? 'noopener noreferrer' : undefined" + :rel="isExternal ? 'noreferrer' : undefined" > diff --git a/src/client/theme-default/components/VPSocialLink.vue b/src/client/theme-default/components/VPSocialLink.vue index 92d3fb12..53a6153c 100644 --- a/src/client/theme-default/components/VPSocialLink.vue +++ b/src/client/theme-default/components/VPSocialLink.vue @@ -32,7 +32,7 @@ const icons = { :href="link" :title="icon" target="_blank" - rel="noopener noreferrer" + rel="noreferrer" > {{ icon }} diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 77276c62..7582f47f 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -78,7 +78,7 @@ export const createMarkdownRenderer = async ( linkPlugin, { target: '_blank', - rel: 'noopener noreferrer', + rel: 'noreferrer', ...options.externalLinks }, base diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index bc82adb5..9f050cd9 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -83,7 +83,7 @@ export async function createMarkdownToVueRenderFn( `\n(!) Found dead link ${c.cyan(url)} in file ${c.white( c.dim(file) )}\nIf it is intended, you can use:\n ${c.cyan( - `${url}` + `${url}` )}` ) )