Merge branch 'main' into feat/custom-social-icons

pull/953/head
Divyansh Singh 3 years ago committed by GitHub
commit 489ba7151a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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)

@ -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<SiteData>) {
@ -14,50 +14,20 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
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<SiteData>) {
.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))
)
})
}

@ -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 }}
</component>

@ -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"
>
<slot />
<VPIconExternalLink v-if="isExternal && !noIcon" class="icon" />

@ -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;
@ -94,7 +93,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);
}
}

@ -19,7 +19,7 @@ const svg = computed(() => {
class="VPSocialLink"
:href="link"
target="_blank"
rel="noopener noreferrer"
rel="noopener"
v-html="svg"
>
</a>

@ -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) {

@ -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(() => {

@ -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;
}
/**

@ -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.`)
}

@ -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) {

@ -71,6 +71,12 @@ export interface UserConfig<ThemeConfig = any> {
* @default false
*/
ignoreDeadLinks?: boolean
/**
* Build end hook: called when SSG finish.
* @param siteConfig The resolved configuration.
*/
buildEnd?: (siteConfig: SiteConfig) => Promise<void>
}
export type RawConfigExports<ThemeConfig = any> =
@ -88,6 +94,7 @@ export interface SiteConfig<ThemeConfig = any>
| '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
@ -277,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}')

@ -78,7 +78,7 @@ export const createMarkdownRenderer = async (
linkPlugin,
{
target: '_blank',
rel: 'noopener noreferrer',
rel: 'noreferrer',
...options.externalLinks
},
base

@ -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(
`<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`
`<a href="${url}" target="_blank" rel="noreferrer">${url}</a>`
)}`
)
)

@ -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

@ -1,4 +1,9 @@
import { SiteData, PageData, LocaleConfig } from '../../types/shared'
import {
SiteData,
PageData,
LocaleConfig,
HeadConfig
} from '../../types/shared'
export type {
SiteData,
@ -21,7 +26,7 @@ export const notFoundPageData: PageData = {
title: '404',
description: 'Not Found',
headers: [],
frontmatter: {},
frontmatter: { sidebar: false, layout: 'page' },
lastUpdated: 0
}
@ -136,3 +141,17 @@ function cleanRoute(siteData: SiteData, route: string): string {
return route.slice(baseWithoutSuffix.length)
}
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(
([type, attrs]) => type === tagType && attrs[keyAttr[0]] === keyAttr[1]
)
}
export function mergeHead(prev: HeadConfig[], curr: HeadConfig[]) {
return [...prev.filter((tagAttrs) => !hasTag(curr, tagAttrs)), ...curr]
}

Loading…
Cancel
Save