diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index a1e793fa9b9d..f221096773ee 100644 --- a/docs/.vitepress/config.ts +++ b/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', @@ -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' @@ -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 { diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index d5a548f280a4..b629d9c72d52 100644 --- a/docs/.vitepress/theme/index.ts +++ b/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 diff --git a/docs/.vitepress/theme/pwa.ts b/docs/.vitepress/theme/pwa.ts new file mode 100644 index 000000000000..01b098a7ec60 --- /dev/null +++ b/docs/.vitepress/theme/pwa.ts @@ -0,0 +1,3 @@ +import { registerSW } from 'virtual:pwa-register' + +registerSW({ immediate: true }) diff --git a/docs/contributors.json b/docs/contributors.json index e6dd60dc5cc4..076c11eab548 100644 --- a/docs/contributors.json +++ b/docs/contributors.json @@ -29,4 +29,4 @@ "aleclarson", "CyriacBr", "christianhg" -] \ No newline at end of file +] diff --git a/docs/docs-data.ts b/docs/docs-data.ts new file mode 100644 index 000000000000..ed7c8fb30cd1 --- /dev/null +++ b/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 = { + // 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') diff --git a/docs/index.md b/docs/index.md index e17559c44516..9742b22b1dc0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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' --- diff --git a/docs/main.css b/docs/main.css index 5b6dae453401..36ebc6c5a224 100644 --- a/docs/main.css +++ b/docs/main.css @@ -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; + } +} + diff --git a/docs/package.json b/docs/package.json index 89a98962041a..2a541836e4ef 100644 --- a/docs/package.json +++ b/docs/package.json @@ -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": { @@ -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" } } diff --git a/docs/public/_headers b/docs/public/_headers new file mode 100644 index 000000000000..c40df339f3c4 --- /dev/null +++ b/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 diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png new file mode 100644 index 000000000000..363af09f3abc Binary files /dev/null and b/docs/public/apple-touch-icon.png differ diff --git a/docs/public/bg-original.png b/docs/public/bg-original.png new file mode 100644 index 000000000000..718b0677a886 Binary files /dev/null and b/docs/public/bg-original.png differ diff --git a/docs/public/bg.png b/docs/public/bg.png index 718b0677a886..a7d17a86cc6a 100644 Binary files a/docs/public/bg.png and b/docs/public/bg.png differ diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 000000000000..6198fc750fd9 Binary files /dev/null and b/docs/public/favicon.ico differ diff --git a/docs/public/og-original.png b/docs/public/og-original.png new file mode 100644 index 000000000000..3202ba63b00f Binary files /dev/null and b/docs/public/og-original.png differ diff --git a/docs/public/og.png b/docs/public/og.png index 3202ba63b00f..0afbc1e11d20 100644 Binary files a/docs/public/og.png and b/docs/public/og.png differ diff --git a/docs/public/pwa-192x192.png b/docs/public/pwa-192x192.png new file mode 100644 index 000000000000..150d695a6267 Binary files /dev/null and b/docs/public/pwa-192x192.png differ diff --git a/docs/public/pwa-512x512.png b/docs/public/pwa-512x512.png new file mode 100644 index 000000000000..55a0437e0071 Binary files /dev/null and b/docs/public/pwa-512x512.png differ diff --git a/docs/scripts/assets.ts b/docs/scripts/assets.ts new file mode 100644 index 000000000000..b7748738c17e --- /dev/null +++ b/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 => ``).join('\n')} + ${preconnectLinks.map(l => ``).join('\n')} +` + +const preconnectHome = ` + ${preconnectHomeLinks.map(l => ``).join('\n')} + ${preconnectHomeLinks.map(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' + + let usePreconnect = preconnect + + if (i.endsWith('/dist/index.html')) { + usePreconnect = preconnectHome + prefetchImg = ` +${prefetchImg} +\t +\t +` + } + + // we need the font on development, so the font entry is added in vitepress head + html = html.replace(``, '') + + html = html.replace( + //g, + ` + ${usePreconnect} + + + + `).trim() + + if (pwa) { + html = html.replace( + '', + ` +\t${prefetchImg} +\t\n`, + ) + } + + // TODO: dark/light theme, don't remove yet + // html = html.replace( + // '', + // '\t\n', + // ) + + html = html.replace( + /aria-hidden="true"/gi, + 'tabindex="-1" aria-hidden="true"', + ).replace( + / { + 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() diff --git a/docs/src/components/Avatar.vue b/docs/src/components/Avatar.vue index 18c10073c14b..d55952e41ab8 100644 --- a/docs/src/components/Avatar.vue +++ b/docs/src/components/Avatar.vue @@ -1,33 +1,39 @@ diff --git a/docs/src/components/Home.vue b/docs/src/components/Home.vue index 7e2c35aeb407..b861538b26d1 100644 --- a/docs/src/components/Home.vue +++ b/docs/src/components/Home.vue @@ -1,13 +1,17 @@ +