Skip to content

Commit

Permalink
feat(docs): add pwa with auto update strategy (#573)
Browse files Browse the repository at this point in the history
  • Loading branch information
userquin committed Feb 21, 2022
1 parent 12b6851 commit f5189fd
Show file tree
Hide file tree
Showing 31 changed files with 1,045 additions and 121 deletions.
57 changes: 39 additions & 18 deletions docs/.vitepress/config.ts
@@ -1,20 +1,41 @@

import { defineConfig } from 'vitepress'
import { version } from '../../package.json'
import {
contributing,
discord,
font,
ogImage,
ogUrl,
releases,
twitter,
vitestDescription,
vitestName
} from "../docs-data";
// noinspection ES6PreferShortImport: IntelliJ IDE hint to avoid warning to use `~/contributors`, will fail on build if changed
import { coreTeamMembers } from '../src/contributors'

export default defineConfig({
title: 'Vitest',
description: 'A blazing fast unit test framework powered by Vite',
title: vitestName,
description: vitestDescription,
head: [
['meta', { property: 'og:title', content: 'Vitest' }],
['meta', { property: 'og:description', content: 'A blazing fast unit test framework powered by Vite' }],
['meta', { property: 'og:url', content: 'https://vitest.dev/' }],
['meta', { property: 'og:image', content: 'https://vitest.dev/og.png' }],
['meta', { name: 'twitter:title', content: 'Vitest' }],
['meta', { name: 'twitter:description', content: 'A blazing fast unit test framework powered by Vite' }],
['meta', { name: 'twitter:image', content: 'https://vitest.dev/og.png' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'theme-color', content: '#ffffff' }],
['link', { rel: 'icon', href: '/logo.svg', type: 'image/svg+xml' }],
['link', { href: 'https://fonts.googleapis.com/css2?family=Readex+Pro:wght@200;400;600&display=swap', rel: 'stylesheet' }],
['link', { rel: 'alternate icon', href: '/favicon.ico', type: 'image/png', sizes: '16x16' }],
['meta', { name: 'author', content: `${coreTeamMembers.map(c => c.name).join(', ')} and ${vitestName} contributors` }],
// TODO: review this
['meta', { name: 'keywords', content: 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, tinypool, tinyspy, c8, node' }],
['meta', { property: 'og:title', content: vitestName }],
['meta', { property: 'og:description', content: vitestDescription }],
['meta', { property: 'og:url', content: ogUrl }],
['meta', { property: 'og:image', content: ogImage }],
['meta', { name: 'twitter:title', content: vitestName }],
['meta', { name: 'twitter:description', content: vitestDescription }],
['meta', { name: 'twitter:image', content: ogImage }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['link', { href: font, rel: 'stylesheet' }],
['link', { rel: 'mask-icon', href: '/logo.svg', color: '#ffffff' }],
['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: "180x180" }],
],
themeConfig: {
repo: 'vitest-dev/vitest',
Expand All @@ -24,16 +45,16 @@ export default defineConfig({
editLinks: true,
editLinkText: 'Suggest changes to this page',

/* TODO
algolia: {
apiKey: '...',
apiKey: '9c3ced6fed60d2670bb36ab7e8bed8bc',
indexName: 'vitest',
searchParameters: {
facetFilters: ['tags:en']
}
},

/* TODO
carbonAds: {
carbon: '...',
placement: 'vitest'
Expand All @@ -50,22 +71,22 @@ export default defineConfig({
items: [
{
text: 'Release Notes ',
link: 'https://github.com/vitest-dev/vitest/releases',
link: releases,
},
{
text: 'Contributing ',
link: 'https://github.com/vitest-dev/vitest/blob/main/CONTRIBUTING.md',
link: contributing,
},
],

},
{
text: 'Discord',
link: 'https://chat.vitest.dev'
link: discord,
},
{
text: 'Twitter',
link: 'https://twitter.com/vitest_dev'
link: twitter,
},
/* TODO
{
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/index.ts
@@ -1,5 +1,9 @@
import 'uno.css'
import { inBrowser } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import '../../main.css'

if (inBrowser)
import('./pwa')

export default DefaultTheme
3 changes: 3 additions & 0 deletions docs/.vitepress/theme/pwa.ts
@@ -0,0 +1,3 @@
import { registerSW } from 'virtual:pwa-register'

registerSW({ immediate: true })
2 changes: 1 addition & 1 deletion docs/contributors.json
Expand Up @@ -29,4 +29,4 @@
"aleclarson",
"CyriacBr",
"christianhg"
]
]
43 changes: 43 additions & 0 deletions docs/docs-data.ts
@@ -0,0 +1,43 @@
// noinspection ES6PreferShortImport: IntelliJ IDE hint to avoid warning to use `~/contributors`, will fail on build if changed
import { antfuSponsors, jsdelivr, patak, patakSponsors } from './src/contributors'
/* PWA DISABLED */
export const pwaDisabled = true

/* Texts */
export const vitestName = 'Vitest'
export const vitestShortName = 'Vitest'
export const vitestDescription = 'A blazing fast unit test framework powered by Vite'

/* CDN fonts and styles */
export const googleapis = 'https://fonts.googleapis.com'
export const gstatic = 'https://fonts.gstatic.com'
export const font = `${googleapis}/css2?family=Readex+Pro:wght@200;400;600&display=swap`

/* vitepress head */
export const ogUrl = 'https://vitest.dev/'
export const ogImage = `${ogUrl}og.png`

/* GitHub and social links */
export const releases = 'https://github.com/vitest-dev/vitest/releases'
export const contributing = 'https://github.com/vitest-dev/vitest/blob/main/CONTRIBUTING.md'
export const discord = 'https://chat.vitest.dev'
export const twitter = 'https://twitter.com/vitest_dev'

/* Avatar/Image/Sponsors servers */
export const imageServers: Record<string, string> = {
// for antfu sponsors
jsdelivr,
// for patak sponsors
patak,
// GitHub contributor avatars
github: 'github.com',
avatars: 'avatars.githubusercontent.com',
}
export const sponsors = [antfuSponsors, patakSponsors]
export const preconnectLinks = [googleapis, gstatic]
export const preconnectHomeLinks = [googleapis, gstatic, ...Object.values(imageServers).map(s => `https://${s}`)]

/* PWA runtime caching urlPattern regular expressions */
export const pwaImagesRegex = new RegExp(`^https://(${Object.values(imageServers).join('|')})/.*`, 'i')
export const pwaFontsRegex = new RegExp(`^${googleapis}/.*`, 'i')
export const pwaFontStylesRegex = new RegExp(`^${gstatic}/.*`, 'i')
2 changes: 2 additions & 0 deletions docs/index.md
Expand Up @@ -2,6 +2,8 @@
home: true
sidebar: false
title: 'Vitest - A blazing fast unit test framework powered by Vite'

footer: 'MIT Licensed | Copyright © 2021-PRESENT Anthony Fu, Matías Capeletto and Vitest contributors'
---

<Home />
51 changes: 51 additions & 0 deletions docs/main.css
Expand Up @@ -93,3 +93,54 @@ main.home {
th, td {
border: 1px solid #8885;
}

/* h3 breaks SEO => replaced with h2 with the same size */
.home-content h2 {
margin-top: 2rem;
font-size: 1.35rem;
border-bottom: none;
margin-bottom: 0;
}

.DocSearch-Modal {
--docsearch-logo-color: #ca931c;
}

@media (prefers-color-scheme: dark) {
.algolia-search-box > button {
--docsearch-searchbox-background: transparent;
--docsearch-searchbox-focus-background: var(--docsearch-searchbox-background);
--docsearch-text-color: #90a4b7;
--docsearch-key-gradient: linear-gradient(-225deg,--docsearch-searchbox-background,--docsearch-searchbox-background);
}
.algolia-search-box > button:hover {
--c-brand: #ca931c;
--docsearch-text-color: #ca931c;
--docsearch-muted-color: #ca931c;
}
.algolia-search-box > button:hover > .DocSearch-Button-Keys > .DocSearch-Button-Key {
color: #ca931c;
box-shadow: inset 0 -1px 0 0 var(--docsearch-muted-color),inset 0 0 1px 1px var(--docsearch-muted-color),0 1px 1px 1px var(--docsearch-muted-color);
}
.DocSearch-Modal {
--docsearch-modal-background: rgb(20,20,20);
--docsearch-primary-color: #ca931c;
--docsearch-highlight-color: var(--docsearch-primary-color);
--docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
--docsearch-text-color: #90a4b7;
--docsearch-muted-color: var(--c-text-lighter);
--docsearch-searchbox-background: rgb(20,20,20);
--docsearch-searchbox-focus-background: rgb(20,20,20);
--docsearch-footer-background: rgb(20,20,20);
}
.DocSearch-Commands-Key {
color: #ca931c;
--docsearch-text-color: #ca931c;
--docsearch-highlight-color: var(--docsearch-text-color);
}
.DocSearch-Footer {
--docsearch-key-gradient: linear-gradient(-225deg,--docsearch-searchbox-background,--docsearch-searchbox-background);
--docsearch-key-shadow: inset 0 -1px 0 0 #ca931c,inset 0 0 1px 1px #ca931c,0 1px 1px 1px #ca931c;
}
}

11 changes: 9 additions & 2 deletions docs/package.json
Expand Up @@ -3,8 +3,9 @@
"private": true,
"scripts": {
"dev": "vitepress --port 3333 --open",
"build": "vitepress build",
"build": "vitepress build && esno scripts/build-pwa.ts",
"serve": "vitepress serve",
"preview-https": "pnpm run build && serve .vitepress/dist",
"lint": "eslint . --ext=.ts,.vue"
},
"dependencies": {
Expand All @@ -13,10 +14,16 @@
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.0",
"@types/node": "^17.0.8",
"@unocss/reset": "^0.24.4",
"@vitejs/plugin-vue": "^2.2.0",
"esno": "^0.14.0",
"fast-glob": "^3.2.10",
"https-localhost": "^4.7.0",
"unocss": "^0.24.4",
"unplugin-vue-components": "^0.17.18",
"vitepress": "^0.22.2"
"vite-plugin-pwa": "^0.11.13",
"vitepress": "^0.22.2",
"workbox-window": "^6.4.2"
}
}
28 changes: 28 additions & 0 deletions docs/public/_headers
@@ -0,0 +1,28 @@
/
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

/api/
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

/config/
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

/guide/
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

/*.html
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

/*
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains

/assets/*
cache-control: max-age=31536000
cache-control: immutable
Binary file added docs/public/apple-touch-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/bg-original.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/public/bg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/favicon.ico
Binary file not shown.
Binary file added docs/public/og-original.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/public/og.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/pwa-192x192.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/pwa-512x512.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions docs/scripts/assets.ts
@@ -0,0 +1,85 @@
import { promises as fs } from 'fs'
import fg from 'fast-glob'
import { font, preconnectHomeLinks, preconnectLinks } from '../docs-data'

const preconnect = `
${preconnectLinks.map(l => `<link rel="dns-prefetch" href="${l}">`).join('\n')}
${preconnectLinks.map(l => `<link rel="preconnect" crossorigin="anonymous" href="${l}">`).join('\n')}
`

const preconnectHome = `
${preconnectHomeLinks.map(l => `<link rel="dns-prefetch" href="${l}">`).join('\n')}
${preconnectHomeLinks.map(l => `<link rel="preconnect" crossorigin="anonymous" href="${l}">`).join('\n')}
`

export const optimizePages = async(pwa: boolean) => {
const names = await fg('./.vitepress/dist/**/*.html', { onlyFiles: true })

await Promise.all(names.map(async(i) => {
let html = await fs.readFile(i, 'utf-8')

let prefetchImg = '\n\t<link rel="prefetch" href="/logo.svg">'

let usePreconnect = preconnect

if (i.endsWith('/dist/index.html')) {
usePreconnect = preconnectHome
prefetchImg = `
${prefetchImg}
\t<link rel="prefetch" href="/netlify.svg">
\t<link rel="prefetch" href="/bg.png">
`
}

// we need the font on development, so the font entry is added in vitepress head
html = html.replace(`<link href="${font.replace('&', '&amp;')}" rel="stylesheet">`, '')

html = html.replace(
/<link rel="stylesheet" href="(.*)">/g,
`
${usePreconnect}
<link rel="preload" as="style" href="$1" />
<link rel="stylesheet" href="$1" />
<link
rel="preload"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
href="${font}"
/>
<noscript>
<link rel="stylesheet" crossorigin="anonymous" href="${font}" />
</noscript>`).trim()

if (pwa) {
html = html.replace(
'</head>',
`
\t<link rel="prefetch" href="/manifest.webmanifest">${prefetchImg}
\t<link rel="manifest" href="/manifest.webmanifest">\n</head>`,
)
}

// TODO: dark/light theme, don't remove yet
// html = html.replace(
// '</head>',
// '\t<link rel="manifest" href="/manifest.webmanifest">\n<script>\n'
// + ' (function() {\n'
// + ' const prefersDark = window.matchMedia && window.matchMedia(\'(prefers-color-scheme: dark)\').matches\n'
// + ' const setting = localStorage.getItem(\'color-schema\') || \'auto\'\n'
// + ' if (setting === \'dark\' || (prefersDark && setting !== \'light\'))\n'
// + ' document.documentElement.classList.toggle(\'dark\', true)\n'
// + ' })()\n'
// + ' </script></head>',
// )

html = html.replace(
/aria-hidden="true"/gi,
'tabindex="-1" aria-hidden="true"',
).replace(
/<img class="logo"/gi,
'<img class="logo" width="31" height="31"',
)

await fs.writeFile(i, html, 'utf-8')
}))
}
15 changes: 15 additions & 0 deletions docs/scripts/build-pwa.ts
@@ -0,0 +1,15 @@
import { resolveConfig } from 'vite'
import type { VitePluginPWAAPI } from 'vite-plugin-pwa'
import { optimizePages } from './assets'

const rebuildPwa = async() => {
const config = await resolveConfig({}, 'build', 'production')
// when `vite-plugin-pwa` is presented, use it to regenerate SW after rendering
const pwaPlugin: VitePluginPWAAPI = config.plugins.find(i => i.name === 'vite-plugin-pwa')?.api
const pwa = pwaPlugin && !pwaPlugin.disabled
await optimizePages(pwa)
if (pwa)
await pwaPlugin.generateSW()
}

rebuildPwa()

0 comments on commit f5189fd

Please sign in to comment.