diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e357735..c32e32a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [0.20.10](https://github.com/vuejs/vitepress/compare/v0.20.9...v0.20.10) (2021-12-25) + +### Features + +- minify head inline scripts ([e61db62](https://github.com/vuejs/vitepress/commit/e61db62a1c49cb5f368a152221bfa60737dbbc6a)) + ## [0.20.9](https://github.com/vuejs/vitepress/compare/v0.20.8...v0.20.9) (2021-12-15) ### Features diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8dff7daf..3b4f5aa2 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,8 +1,18 @@ -export default { +import { defineConfig } from '../../src/node' + +export default defineConfig({ lang: 'en-US', title: 'VitePress', description: 'Vite & Vue powered static site generator.', + head: [ + [ + 'script', + {}, + '(() => { const afsefe = window.foo;\n console.log(afsefe);})()' + ] + ], + themeConfig: { repo: 'vuejs/vitepress', docsDir: 'docs', @@ -42,7 +52,7 @@ export default { '/': getGuideSidebar() } } -} +}) function getGuideSidebar() { return [ diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 7dc8d1e0..8ffaad52 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -1,5 +1,7 @@ # Configuration +## Overview + Without any configuration, the page is pretty minimal, and the user has no way to navigate around the site. To customize your site, let’s first create a `.vitepress` directory inside your docs directory. This is where all VitePress-specific files will be placed. Your project structure is probably like this: ```bash @@ -21,3 +23,57 @@ module.exports = { ``` Check out the [Config Reference](/config/basics) for a full list of options. + +## Config Intellisense + +Since VitePress ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints: + +```js +/** + * @type {import('vitepress').UserConfig} + */ +const config = { + // ... +} + +export default config +``` + +Alternatively, you can use the `defineConfig` helper at which should provide intellisense without the need for jsdoc annotations: + +```js +import { defineConfig } from 'vitepress' + +export default defineConfig({ + // ... +}) +``` + +VitePress also directly supports TS config files. You can use `.vitepress/config.ts` with the `defineConfig` helper as well. + +## Typed Theme Config + +By default, `defineConfig` helper leverages the theme config type from default theme: + +```ts +import { defineConfig } from 'vitepress' + +export default defineConfig({ + themeConfig: { + // Type is `DefaultTheme.Config` + } +}) +``` + +If you use a custom theme and want type checks for the theme config, you'll need to use `defineConfigWithTheme` instead, and pass the config type for your custom theme via a generic argument: + +```ts +import { defineConfigWithTheme } from 'vitepress' +import { ThemeConfig } from 'your-theme' + +export default defineConfigWithTheme({ + themeConfig: { + // Type is `ThemeConfig` + } +}) +``` diff --git a/package.json b/package.json index ed5f104e..cc38bfd1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitepress", - "version": "0.20.9", + "version": "0.20.10", "description": "Vite & Vue powered static site generator", "main": "dist/node/index.js", "typings": "types/index.d.ts", diff --git a/src/client/app/router.ts b/src/client/app/router.ts index e7f1c59a..14c91206 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -25,8 +25,8 @@ const getDefaultRoute = (): Route => ({ component: null, // this will be set upon initial page load, which is before // the app is mounted, so it's guaranteed to be available in - // components - data: null as any + // components. We just need enough data for 404 pages to render. + data: { frontmatter: {} } as any }) interface PageModule { diff --git a/src/client/theme-default/components/NavBarTitle.vue b/src/client/theme-default/components/NavBarTitle.vue index 4bfcbb1d..8b9d3709 100644 --- a/src/client/theme-default/components/NavBarTitle.vue +++ b/src/client/theme-default/components/NavBarTitle.vue @@ -24,6 +24,9 @@ const { site, theme, localePath } = useData() font-size: 1.3rem; font-weight: 600; color: var(--c-text); + display: flex; + justify-content: center; + align-items: center; } .nav-bar-title:hover { diff --git a/src/client/theme-default/config.ts b/src/client/theme-default/config.ts index d7284871..92869ac4 100644 --- a/src/client/theme-default/config.ts +++ b/src/client/theme-default/config.ts @@ -1,146 +1 @@ -export namespace DefaultTheme { - export interface Config { - logo?: string - nav?: NavItem[] | false - sidebar?: SideBarConfig | MultiSideBarConfig - - /** - * GitHub repository following the format /. - * - * @example `"vuejs/vue-next"` - */ - repo?: string - - /** - * Customize the header label. Defaults to GitHub/Gitlab/Bitbucket - * depending on the provided repo. - * - * @example `"Contribute!"` - */ - repoLabel?: string - - /** - * If your docs are in a different repository from your main project. - * - * @example `"vuejs/docs-next"` - */ - docsRepo?: string - - /** - * If your docs are not at the root of the repo. - * - * @example `"docs"` - */ - docsDir?: string - - /** - * If your docs are in a different branch. Defaults to `master`. - * - * @example `"next"` - */ - docsBranch?: string - - /** - * Enable links to edit pages at the bottom of the page. - */ - editLinks?: boolean - - /** - * Custom text for edit link. Defaults to "Edit this page". - */ - editLinkText?: string - - /** - * Show last updated time at the bottom of the page. Defaults to `false`. - * If given a string, it will be displayed as a prefix (default value: - * "Last Updated"). - */ - lastUpdated?: string | boolean - - prevLinks?: boolean - nextLinks?: boolean - - locales?: Record> - - algolia?: AlgoliaSearchOptions - - carbonAds?: { - carbon: string - custom?: string - placement: string - } - } - - // navbar -------------------------------------------------------------------- - - export type NavItem = NavItemWithLink | NavItemWithChildren - - export interface NavItemBase { - text: string - target?: string - rel?: string - ariaLabel?: string - activeMatch?: string - } - - export interface NavItemWithLink extends NavItemBase { - link: string - } - - export interface NavItemWithChildren extends NavItemBase { - items: NavItemWithLink[] - } - - // sidebar ------------------------------------------------------------------- - - export type SideBarConfig = SideBarItem[] | 'auto' | false - - export interface MultiSideBarConfig { - [path: string]: SideBarConfig - } - - export type SideBarItem = SideBarLink | SideBarGroup - - export interface SideBarLink { - text: string - link: string - } - - export interface SideBarGroup { - text: string - link?: string - - /** - * @default false - */ - collapsable?: boolean - - children: SideBarItem[] - } - - // algolia ------------------------------------------------------------------ - // partially copied from @docsearch/react/dist/esm/DocSearch.d.ts - export interface AlgoliaSearchOptions { - appId?: string - apiKey: string - indexName: string - placeholder?: string - searchParameters?: any - disableUserPersonalization?: boolean - initialQuery?: string - } - - // locales ------------------------------------------------------------------- - - export interface LocaleConfig { - /** - * Text for the language dropdown. - */ - selectText?: string - - /** - * Label for this locale in the language dropdown. - */ - label?: string - } -} +export { DefaultTheme } from '../shared' diff --git a/src/client/theme-default/index.ts b/src/client/theme-default/index.ts index b9c753dd..bcd79051 100644 --- a/src/client/theme-default/index.ts +++ b/src/client/theme-default/index.ts @@ -8,6 +8,7 @@ import { Theme } from 'vitepress' import Layout from './Layout.vue' import NotFound from './NotFound.vue' +export { DefaultTheme } from './config' const theme: Theme = { Layout, NotFound diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json index f7f086aa..92a8af1c 100644 --- a/src/client/tsconfig.json +++ b/src/client/tsconfig.json @@ -12,5 +12,5 @@ "vitepress": ["index.ts"] } }, - "include": [".", "../../types/shared.d.ts"] + "include": ["."] } diff --git a/src/node/build/render.ts b/src/node/build/render.ts index ae851343..8bfe54d5 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -2,7 +2,7 @@ import path from 'path' import fs from 'fs-extra' import { SiteConfig, resolveSiteDataByRoute } from '../config' import { HeadConfig } from '../shared' -import { normalizePath } from 'vite' +import { normalizePath, transformWithEsbuild } from 'vite' import { RollupOutput, OutputChunk, OutputAsset } from 'rollup' import { slash } from '../utils/slash' import escape from 'escape-html' @@ -121,7 +121,7 @@ export async function renderPage( ${stylesheetLink} ${preloadLinksString} ${prefetchLinkString} - ${renderHead(head)} + ${await renderHead(head)}
${content}
@@ -165,17 +165,24 @@ function resolvePageImports( ] } -function renderHead(head: HeadConfig[]) { - return head - .map(([tag, attrs = {}, innerHTML = '']) => { +function renderHead(head: HeadConfig[]): Promise { + return Promise.all( + head.map(async ([tag, attrs = {}, innerHTML = '']) => { const openTag = `<${tag}${renderAttrs(attrs)}>` if (tag !== 'link' && tag !== 'meta') { + if (tag === 'script') { + innerHTML = ( + await transformWithEsbuild(innerHTML, 'inline-script.js', { + minify: true + }) + ).code.trim() + } return `${openTag}${innerHTML}` } else { return openTag } }) - .join('\n ') + ).then((tags) => tags.join('\n ')) } function renderAttrs(attrs: Record): string { diff --git a/src/node/config.ts b/src/node/config.ts index 5b7e7622..3080395d 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -14,9 +14,10 @@ import { SiteData, HeadConfig, LocaleConfig, - createLangDictionary + createLangDictionary, + DefaultTheme } from './shared' -import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias' +import { resolveAliases, DEFAULT_THEME_PATH } from './alias' import { MarkdownOptions } from './markdown/markdown' import _debug from 'debug' @@ -27,7 +28,7 @@ const debug = _debug('vitepress:config') export type { MarkdownOptions } export interface UserConfig { - extends?: RawConfigExports + extends?: RawConfigExports lang?: string base?: string title?: string @@ -57,10 +58,10 @@ export interface UserConfig { mpa?: boolean } -export type RawConfigExports = - | UserConfig - | Promise - | (() => UserConfig | Promise) +export type RawConfigExports = + | UserConfig + | Promise> + | (() => UserConfig | Promise>) export interface SiteConfig extends Pick< @@ -84,7 +85,16 @@ const resolve = (root: string, file: string) => /** * Type config helper */ -export function defineConfig(config: RawConfigExports) { +export function defineConfig(config: UserConfig) { + return config +} + +/** + * Type config helper for custom theme config + */ +export function defineConfigWithTheme( + config: UserConfig +) { return config } @@ -125,7 +135,7 @@ export async function resolveConfig( pages, configPath, outDir, - tempDir: path.resolve(APP_PATH, 'temp'), + tempDir: resolve(root, '.tmp'), markdown: userConfig.markdown, alias: resolveAliases(themeDir), vue: userConfig.vue, @@ -137,7 +147,7 @@ export async function resolveConfig( return config } -const supportedConfigExtensions = ['js', 'ts', '.mjs', 'mts'] +const supportedConfigExtensions = ['js', 'ts', 'mjs', 'mts'] async function resolveUserConfig( root: string, diff --git a/src/node/index.ts b/src/node/index.ts index f671e921..dd6b048e 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -4,4 +4,4 @@ export * from './serve/serve' export * from './config' export * from './markdown/markdown' -export type { SiteData, HeadConfig, LocaleConfig } from '../../types/shared' +export type { SiteData, HeadConfig, LocaleConfig, DefaultTheme } from '../../types/shared' diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index bc01c15d..083f60da 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -89,10 +89,12 @@ export function createMarkdownToVueRenderFn( for (let url of data.links) { url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '') if (url.endsWith('/')) url += `index` - const resolved = slash( - url.startsWith('/') - ? url.slice(1) - : path.relative(srcDir, path.resolve(dir, url)) + const resolved = decodeURIComponent( + slash( + url.startsWith('/') + ? url.slice(1) + : path.relative(srcDir, path.resolve(dir, url)) + ) ) if ( !pages.includes(resolved) && diff --git a/src/shared/shared.ts b/src/shared/shared.ts index d3d34eb8..1e1e0a5a 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -5,7 +5,8 @@ export type { PageData, HeadConfig, LocaleConfig, - Header + Header, + DefaultTheme, } from '../../types/shared' export const EXTERNAL_URL_RE = /^https?:/i diff --git a/src/shared/tsconfig.json b/src/shared/tsconfig.json index 5dd8a6db..a8ed344e 100644 --- a/src/shared/tsconfig.json +++ b/src/shared/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "baseUrl": "." }, - "include": [".", "../../types/shared.d.ts"] + "include": ["."] } diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts new file mode 100644 index 00000000..d7284871 --- /dev/null +++ b/types/default-theme.d.ts @@ -0,0 +1,146 @@ +export namespace DefaultTheme { + export interface Config { + logo?: string + nav?: NavItem[] | false + sidebar?: SideBarConfig | MultiSideBarConfig + + /** + * GitHub repository following the format /. + * + * @example `"vuejs/vue-next"` + */ + repo?: string + + /** + * Customize the header label. Defaults to GitHub/Gitlab/Bitbucket + * depending on the provided repo. + * + * @example `"Contribute!"` + */ + repoLabel?: string + + /** + * If your docs are in a different repository from your main project. + * + * @example `"vuejs/docs-next"` + */ + docsRepo?: string + + /** + * If your docs are not at the root of the repo. + * + * @example `"docs"` + */ + docsDir?: string + + /** + * If your docs are in a different branch. Defaults to `master`. + * + * @example `"next"` + */ + docsBranch?: string + + /** + * Enable links to edit pages at the bottom of the page. + */ + editLinks?: boolean + + /** + * Custom text for edit link. Defaults to "Edit this page". + */ + editLinkText?: string + + /** + * Show last updated time at the bottom of the page. Defaults to `false`. + * If given a string, it will be displayed as a prefix (default value: + * "Last Updated"). + */ + lastUpdated?: string | boolean + + prevLinks?: boolean + nextLinks?: boolean + + locales?: Record> + + algolia?: AlgoliaSearchOptions + + carbonAds?: { + carbon: string + custom?: string + placement: string + } + } + + // navbar -------------------------------------------------------------------- + + export type NavItem = NavItemWithLink | NavItemWithChildren + + export interface NavItemBase { + text: string + target?: string + rel?: string + ariaLabel?: string + activeMatch?: string + } + + export interface NavItemWithLink extends NavItemBase { + link: string + } + + export interface NavItemWithChildren extends NavItemBase { + items: NavItemWithLink[] + } + + // sidebar ------------------------------------------------------------------- + + export type SideBarConfig = SideBarItem[] | 'auto' | false + + export interface MultiSideBarConfig { + [path: string]: SideBarConfig + } + + export type SideBarItem = SideBarLink | SideBarGroup + + export interface SideBarLink { + text: string + link: string + } + + export interface SideBarGroup { + text: string + link?: string + + /** + * @default false + */ + collapsable?: boolean + + children: SideBarItem[] + } + + // algolia ------------------------------------------------------------------ + // partially copied from @docsearch/react/dist/esm/DocSearch.d.ts + export interface AlgoliaSearchOptions { + appId?: string + apiKey: string + indexName: string + placeholder?: string + searchParameters?: any + disableUserPersonalization?: boolean + initialQuery?: string + } + + // locales ------------------------------------------------------------------- + + export interface LocaleConfig { + /** + * Text for the language dropdown. + */ + selectText?: string + + /** + * Label for this locale in the language dropdown. + */ + label?: string + } +} diff --git a/types/index.d.ts b/types/index.d.ts index 10a5d0c2..ea74a292 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,4 +1,4 @@ export * from './shared' +export * from './default-theme' export * from '../dist/node/index' export * from '../dist/client/index' -export * from '../dist/client/theme-default/config' diff --git a/types/shared.d.ts b/types/shared.d.ts index 4f6558a6..2beaf53f 100644 --- a/types/shared.d.ts +++ b/types/shared.d.ts @@ -1,5 +1,7 @@ // types shared between server and client +export { DefaultTheme } from './default-theme' + export interface LocaleConfig { lang: string title?: string