From ba41bb90551c01b9f84de2d2d3bc1920ce2ebe93 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Thu, 6 Jan 2022 10:18:05 +0800 Subject: [PATCH 01/24] feat: improve typescript support for config file (#465) --- docs/.vitepress/config.ts | 6 +- docs/guide/configuration.md | 56 +++++++++++ src/client/theme-default/config.ts | 147 +---------------------------- src/client/theme-default/index.ts | 1 + src/client/tsconfig.json | 2 +- src/node/config.ts | 24 +++-- src/node/index.ts | 2 +- src/shared/shared.ts | 3 +- src/shared/tsconfig.json | 2 +- types/default-theme.d.ts | 146 ++++++++++++++++++++++++++++ types/index.d.ts | 2 +- types/shared.d.ts | 2 + 12 files changed, 233 insertions(+), 160 deletions(-) create mode 100644 types/default-theme.d.ts diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8ef0d400..3b4f5aa2 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,4 +1,6 @@ -export default { +import { defineConfig } from '../../src/node' + +export default defineConfig({ lang: 'en-US', title: 'VitePress', description: 'Vite & Vue powered static site generator.', @@ -50,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/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/config.ts b/src/node/config.ts index d3ff1c70..19130e8a 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -14,7 +14,8 @@ import { SiteData, HeadConfig, LocaleConfig, - createLangDictionary + createLangDictionary, + DefaultTheme } from './shared' import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias' import { MarkdownOptions } from './markdown/markdown' @@ -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 @@ -56,10 +57,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< @@ -83,7 +84,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 } 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/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 From b104d959937497af1086dead208e07df9b120f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E6=B8=B8=E5=90=9B?= Date: Thu, 6 Jan 2022 10:48:57 +0800 Subject: [PATCH 02/24] style: make img center in nav-bar-title (#377) --- src/client/theme-default/components/NavBarTitle.vue | 3 +++ 1 file changed, 3 insertions(+) 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 { From a3bf52fed53e82b9756c844f6bdd576662d2e726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=9F=E3=82=8A=E3=82=89=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E6=81=8B?= <1176789241@QQ.COM> Date: Thu, 6 Jan 2022 11:00:40 +0800 Subject: [PATCH 03/24] fix: initial render of 404 pages (#418) --- src/client/app/router.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 { From ae029ae9e17fa6df1d2f89043f1891271e9c5b9b Mon Sep 17 00:00:00 2001 From: fuchunhui Date: Thu, 6 Jan 2022 11:05:32 +0800 Subject: [PATCH 04/24] fix: Chinese file link build failed (#425) fix #424 --- src/node/markdownToVue.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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) && From bd35451ed42d7b5c47e2b49a7e659807cd7d7a0c Mon Sep 17 00:00:00 2001 From: Jonas <30421456+jonaskuske@users.noreply.github.com> Date: Thu, 6 Jan 2022 04:19:24 +0100 Subject: [PATCH 05/24] fix: set tempDir outside package root (#439) package root can be read-only when install strategies like PnP are used, so the tempDir needs to be outside the vitepress package itself close #435 --- src/node/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/config.ts b/src/node/config.ts index 19130e8a..e956bbb4 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -17,7 +17,7 @@ import { 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' @@ -133,7 +133,7 @@ export async function resolveConfig( pages, configPath, outDir: resolve(root, 'dist'), - tempDir: path.resolve(APP_PATH, 'temp'), + tempDir: resolve(root, '.tmp'), markdown: userConfig.markdown, alias: resolveAliases(themeDir), vue: userConfig.vue, From fb6a4ad3e008af9ce4393fb3ca37645f4efba951 Mon Sep 17 00:00:00 2001 From: Jeff Yang <32727188+ydcjeff@users.noreply.github.com> Date: Thu, 6 Jan 2022 10:06:46 +0630 Subject: [PATCH 06/24] fix: remove `.` for mjs in `supportedConfigExtensions` (#447) --- src/node/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/config.ts b/src/node/config.ts index e956bbb4..dc30312d 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -145,7 +145,7 @@ export async function resolveConfig( return config } -const supportedConfigExtensions = ['js', 'ts', '.mjs', 'mts'] +const supportedConfigExtensions = ['js', 'ts', 'mjs', 'mts'] async function resolveUserConfig( root: string, From 5b04bb9eb5ced720414f4b0d729fde36432dd451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20M=C3=BCller?= Date: Wed, 5 Jan 2022 21:41:26 -0600 Subject: [PATCH 07/24] feat: user configurable `outDir` (#448) --- .gitignore | 3 ++- docs/guide/deploy.md | 2 +- src/node/config.ts | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0e9bd5c8..f71faa25 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist node_modules TODOs.md -.vscode \ No newline at end of file +.vscode +.idea diff --git a/docs/guide/deploy.md b/docs/guide/deploy.md index 4221893c..9c884e5f 100644 --- a/docs/guide/deploy.md +++ b/docs/guide/deploy.md @@ -136,7 +136,7 @@ deploy: If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `'//'`. -2. Set `dest` in `.vitepress/config.js` to `public`. +2. Set `outDir` in `.vitepress/config.js` to `../public`. 3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: diff --git a/src/node/config.ts b/src/node/config.ts index dc30312d..3080395d 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -38,7 +38,7 @@ export interface UserConfig { locales?: Record markdown?: MarkdownOptions /** - * Opitons to pass on to `@vitejs/plugin-vue` + * Options to pass on to `@vitejs/plugin-vue` */ vue?: VuePluginOptions /** @@ -48,6 +48,7 @@ export interface UserConfig { srcDir?: string srcExclude?: string[] + outDir?: string shouldPreload?: (link: string, page: string) => boolean /** @@ -105,6 +106,7 @@ export async function resolveConfig( const [userConfig, configPath] = await resolveUserConfig(root, command, mode) const site = await resolveSiteData(root, userConfig) const srcDir = path.resolve(root, userConfig.srcDir || '.') + const outDir = userConfig.outDir ? path.resolve(root, userConfig.outDir) : resolve(root, 'dist') // resolve theme path const userThemeDir = resolve(root, 'theme') @@ -132,7 +134,7 @@ export async function resolveConfig( themeDir, pages, configPath, - outDir: resolve(root, 'dist'), + outDir, tempDir: resolve(root, '.tmp'), markdown: userConfig.markdown, alias: resolveAliases(themeDir), From a8f147f153efdd17989a02eb620c3ae9ab0d13dd Mon Sep 17 00:00:00 2001 From: Jeff Yang <32727188+ydcjeff@users.noreply.github.com> Date: Thu, 6 Jan 2022 10:12:58 +0630 Subject: [PATCH 08/24] feat: add details custom container (#455) --- docs/guide/markdown.md | 22 +++++++++++++++++++++- src/node/markdown/plugins/containers.ts | 6 +++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md index e0efc2f0..63cc8471 100644 --- a/docs/guide/markdown.md +++ b/docs/guide/markdown.md @@ -138,6 +138,10 @@ This is a warning ::: danger This is a dangerous warning ::: + +::: details +This is a details block, which does not work in Internet Explorer or Edge. +::: ``` **Output** @@ -158,15 +162,25 @@ This is a warning This is a dangerous warning ::: +::: details +This is a details block, which does not work in Internet Explorer or Edge. +::: + ### Custom Title **Input** -```md +````md ::: danger STOP Danger zone, do not proceed ::: + +::: details Click me to view the code +```js +console.log('Hello, VitePress!') ``` +::: +```` **Output** @@ -174,6 +188,12 @@ Danger zone, do not proceed Danger zone, do not proceed ::: +::: details Click me to view the code +```js +console.log('Hello, VitePress!') +``` +::: + ## Syntax Highlighting in Code Blocks VitePress uses [Prism](https://prismjs.com/) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block: diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts index 1afd55ca..61dd7b91 100644 --- a/src/node/markdown/plugins/containers.ts +++ b/src/node/markdown/plugins/containers.ts @@ -7,6 +7,7 @@ export const containerPlugin = (md: MarkdownIt) => { .use(...createContainer('info', 'INFO')) .use(...createContainer('warning', 'WARNING')) .use(...createContainer('danger', 'WARNING')) + .use(...createContainer('details', 'Details')) // explicitly escape Vue syntax .use(container, 'v-pre', { render: (tokens: Token[], idx: number) => @@ -31,11 +32,14 @@ function createContainer(klass: string, defaultTitle: string): ContainerArgs { const token = tokens[idx] const info = token.info.trim().slice(klass.length).trim() if (token.nesting === 1) { + if (klass === 'details') { + return `
${info ? `${info}` : ''}\n` + } return `

${ info || defaultTitle }

\n` } else { - return `
\n` + return klass === 'details' ? `
\n` : `\n` } } } From 444562c3a763bab7a9c0ebfca5eec635e142a61f Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 6 Jan 2022 04:43:37 +0100 Subject: [PATCH 09/24] fix: use algolia search lang (#459) --- docs/config/algolia-search.md | 39 +------------------ .../components/AlgoliaSearchBox.vue | 6 +-- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/docs/config/algolia-search.md b/docs/config/algolia-search.md index 9f5208f6..3ee35eb1 100644 --- a/docs/config/algolia-search.md +++ b/docs/config/algolia-search.md @@ -47,41 +47,4 @@ module.exports = { } ``` -VitePress will automatically add a `language` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. **Make sure to properly configure your DocSearch config as well** by adding `language` as a _custom attribute for faceting_ and by setting it based on the `lang` attribute of the `` element. Here is a short example of DocSearch config: - -```json -{ - "index_name": "", - "start_urls": [ - { - "url": "" - } - ], - "stop_urls": ["(?:(? .sidebar-links > .sidebar-link .sidebar-link-item.active", - "global": true, - "default_value": "Documentation" - }, - "lvl1": ".content h1", - "lvl2": ".content h2", - "lvl3": ".content h3", - "lvl4": ".content h4", - "lvl5": ".content h5", - "lvl6": ".content p, .content li", - "text": ".content [class^=language-]", - "language": { - "selector": "/html/@lang", - "type": "xpath", - "global": true, - "default_value": "en-US" - } - }, - "custom_settings": { - "attributesForFaceting": ["language"] - } -} -``` - -You can take a look at the [DocSearch config used by Vue Router](https://github.com/algolia/docsearch-configs/blob/master/configs/next_router_vuejs.json) for a complete example. +VitePress will automatically add a `lang` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. Algolia automatically adds the correct facet filter based on the `lang` attribute on the `` tag. This will match search results with the currently viewed language of the page. diff --git a/src/client/theme-default/components/AlgoliaSearchBox.vue b/src/client/theme-default/components/AlgoliaSearchBox.vue index a354c8f6..928fd4b1 100644 --- a/src/client/theme-default/components/AlgoliaSearchBox.vue +++ b/src/client/theme-default/components/AlgoliaSearchBox.vue @@ -55,7 +55,7 @@ const { lang } = useData() // if the user has multiple locales, the search results should be filtered // based on the language const facetFilters: string[] = props.multilang - ? ['language:' + lang.value] + ? ['lang:' + lang.value] : [] if (props.options.searchParameters?.facetFilters) { @@ -66,10 +66,10 @@ watch( lang, (newLang, oldLang) => { const index = facetFilters.findIndex( - (filter) => filter === 'language:' + oldLang + (filter) => filter === 'lang:' + oldLang ) if (index > -1) { - facetFilters.splice(index, 1, 'language:' + newLang) + facetFilters.splice(index, 1, 'lang:' + newLang) } } ) From 08a0b129928cef44e613ff410d769a7ac7bf5fa3 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Thu, 6 Jan 2022 03:45:24 +0000 Subject: [PATCH 10/24] fix(serve): respect base config in serve mode (#470) Fixes #416 Signed-off-by: Gordon Smith --- src/node/serve/serve.ts | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/node/serve/serve.ts b/src/node/serve/serve.ts index fe84e965..63a2aedd 100644 --- a/src/node/serve/serve.ts +++ b/src/node/serve/serve.ts @@ -3,6 +3,18 @@ import compression from 'compression' import { resolveConfig } from '../config' import polka from 'polka' +function trimChar(str: string, char: string) { + while (str.charAt(0) === char) { + str = str.substring(1) + } + + while (str.charAt(str.length - 1) === char) { + str = str.substring(0, str.length - 1) + } + + return str +} + export interface ServeOptions { root?: string port?: number @@ -11,6 +23,7 @@ export interface ServeOptions { export async function serve(options: ServeOptions = {}) { const port = options.port !== undefined ? options.port : 5000 const site = await resolveConfig(options.root, 'serve', 'production') + const base = trimChar(site?.site?.base ?? "", "/") const compress = compression() const serve = sirv(site.outDir, { @@ -27,10 +40,19 @@ export async function serve(options: ServeOptions = {}) { } }) - polka() - .use(compress, serve) - .listen(port, (err: any) => { - if (err) throw err - console.log(`Built site served at http://localhost:${port}/\n`) - }) + if (base) { + polka() + .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() + .use(compress, serve) + .listen(port, (err: any) => { + if (err) throw err + console.log(`Built site served at http://localhost:${port}/\n`) + }) + } } From 7950e884a58aab48073a707a0c979fc073dc3324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E6=B8=B8=E5=90=9B?= Date: Thu, 6 Jan 2022 11:48:00 +0800 Subject: [PATCH 11/24] chore: fix typo (#478) [ci skip] --- src/client/theme-default/composables/sideBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/composables/sideBar.ts b/src/client/theme-default/composables/sideBar.ts index 7781c5c6..96fb6b24 100644 --- a/src/client/theme-default/composables/sideBar.ts +++ b/src/client/theme-default/composables/sideBar.ts @@ -22,7 +22,7 @@ export function useSideBar() { return [] } - // if it's `atuo`, render headers of the current page + // if it's `auto`, render headers of the current page if (frontSidebar === 'auto') { return resolveAutoSidebar(headers, sidebarDepth) } From 7387649ff7c621402e49e26493b4eed25006fb4b Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 Jan 2022 12:12:26 +0800 Subject: [PATCH 12/24] feat: catch localhost links as dead links --- docs/guide/deploy.md | 4 ++-- docs/guide/getting-started.md | 2 +- src/node/markdown/plugins/link.ts | 14 +++++++++++--- src/node/markdownToVue.ts | 29 +++++++++++++++++++---------- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/guide/deploy.md b/docs/guide/deploy.md index 9c884e5f..25c3058f 100644 --- a/docs/guide/deploy.md +++ b/docs/guide/deploy.md @@ -38,7 +38,7 @@ $ yarn docs:build $ yarn docs:serve ``` -The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at http://localhost:5000. It's an easy way to check if the production build looks OK in your local environment. +The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at `http://localhost:5000`. It's an easy way to check if the production build looks OK in your local environment. You may configure the port of the server py passing `--port` flag as an argument. @@ -50,7 +50,7 @@ You may configure the port of the server py passing `--port` flag as an argument } ``` -Now the `docs:serve` method will launch the server at http://localhost:8080. +Now the `docs:serve` method will launch the server at `http://localhost:8080`. ## GitHub Pages diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 8436901e..2b891fd3 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -44,7 +44,7 @@ This section will help you build a basic VitePress documentation site from groun $ yarn docs:dev ``` - VitePress will start a hot-reloading development server at http://localhost:3000. + VitePress will start a hot-reloading development server at `http://localhost:3000`. By now, you should have a basic but functional VitePress documentation site. diff --git a/src/node/markdown/plugins/link.ts b/src/node/markdown/plugins/link.ts index 7d7c71f7..38063208 100644 --- a/src/node/markdown/plugins/link.ts +++ b/src/node/markdown/plugins/link.ts @@ -24,6 +24,10 @@ export const linkPlugin = ( Object.entries(externalAttrs).forEach(([key, val]) => { token.attrSet(key, val) }) + // catch localhost links as dead link + if (url.replace(EXTERNAL_URL_RE, '').startsWith('//localhost:')) { + pushLink(url) + } } else if ( // internal anchor links !url.startsWith('#') && @@ -70,11 +74,15 @@ export const linkPlugin = ( } // export it for existence check - const data = (md as any).__data as MarkdownParsedData - const links = data.links || (data.links = []) - links.push(url.replace(/\.html$/, '')) + pushLink(url.replace(/\.html$/, '')) // markdown-it encodes the uri hrefAttr[1] = decodeURI(url) } + + function pushLink(link: string) { + const data = (md as any).__data as MarkdownParsedData + const links = data.links || (data.links = []) + links.push(link) + } } diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index 083f60da..fc3c755d 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -4,7 +4,7 @@ import matter from 'gray-matter' import LRUCache from 'lru-cache' import { createMarkdownRenderer, MarkdownOptions } from './markdown/markdown' import { deeplyParseHeader } from './utils/parseHeader' -import { PageData, HeadConfig } from './shared' +import { PageData, HeadConfig, EXTERNAL_URL_RE } from './shared' import { slash } from './utils/slash' import chalk from 'chalk' import _debug from 'debug' @@ -83,10 +83,26 @@ export function createMarkdownToVueRenderFn( } // validate data.links - const deadLinks = [] + const deadLinks: string[] = [] + const recordDeadLink = (url: string) => { + console.warn( + chalk.yellow( + `\n(!) Found dead link ${chalk.cyan(url)} in file ${chalk.white.dim( + file + )}` + ) + ) + deadLinks.push(url) + } + if (data.links) { const dir = path.dirname(file) for (let url of data.links) { + if (url.replace(EXTERNAL_URL_RE, '').startsWith('//localhost:')) { + recordDeadLink(url) + continue + } + url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '') if (url.endsWith('/')) url += `index` const resolved = decodeURIComponent( @@ -100,14 +116,7 @@ export function createMarkdownToVueRenderFn( !pages.includes(resolved) && !fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`)) ) { - console.warn( - chalk.yellow( - `\n(!) Found dead link ${chalk.cyan( - url - )} in file ${chalk.white.dim(file)}` - ) - ) - deadLinks.push(url) + recordDeadLink(url) } } } From 4cec660401d8d01830e5a11b9c66bc0ac5a935db Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 Jan 2022 12:22:44 +0800 Subject: [PATCH 13/24] feat: expose __path and __relativePath on md instance for md plugins --- src/node/markdown/markdown.ts | 21 +++++++++++++++------ src/node/markdownToVue.ts | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index ff8a637f..1b204b14 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -40,8 +40,14 @@ export interface MarkdownParsedData { } export interface MarkdownRenderer { + __path: string + __relativePath: string __data: MarkdownParsedData - render: (src: string, env?: any) => { html: string; data: any } + render: ( + src: string, + path: string, + relatiovePath: string + ) => { html: string; data: any } } export type { Header } @@ -94,17 +100,20 @@ export const createMarkdownRenderer = ( md.use(lineNumberPlugin) } + const wrappedMd = md as any as MarkdownRenderer + // wrap render so that we can return both the html and extracted data. const render = md.render - const wrappedRender: MarkdownRenderer['render'] = (src) => { - ;(md as any).__data = {} + wrappedMd.render = (src, path, relativePath) => { + wrappedMd.__data = {} + wrappedMd.__path = path + wrappedMd.__relativePath = relativePath const html = render.call(md, src) return { html, - data: (md as any).__data + data: wrappedMd.__data } } - ;(md as any).render = wrappedRender - return md as any + return wrappedMd } diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index fc3c755d..d0d50ef3 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -65,7 +65,7 @@ export function createMarkdownToVueRenderFn( }) const { content, data: frontmatter } = matter(src) - let { html, data } = md.render(content) + let { html, data } = md.render(content, file, relativePath) if (isBuild) { // avoid env variables being replaced by vite From a4b7758a0047ac81dedb0df27403d24be9fe83e8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 Jan 2022 12:26:17 +0800 Subject: [PATCH 14/24] chore: tweak temp dir name --- src/node/config.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node/config.ts b/src/node/config.ts index 3080395d..7a01377b 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -106,7 +106,9 @@ export async function resolveConfig( const [userConfig, configPath] = await resolveUserConfig(root, command, mode) const site = await resolveSiteData(root, userConfig) const srcDir = path.resolve(root, userConfig.srcDir || '.') - const outDir = userConfig.outDir ? path.resolve(root, userConfig.outDir) : resolve(root, 'dist') + const outDir = userConfig.outDir + ? path.resolve(root, userConfig.outDir) + : resolve(root, 'dist') // resolve theme path const userThemeDir = resolve(root, 'theme') @@ -135,7 +137,7 @@ export async function resolveConfig( pages, configPath, outDir, - tempDir: resolve(root, '.tmp'), + tempDir: resolve(root, '.temp'), markdown: userConfig.markdown, alias: resolveAliases(themeDir), vue: userConfig.vue, From 294b1d281750b3abc321b05d0fdb88f355d298cf Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 Jan 2022 14:50:38 +0800 Subject: [PATCH 15/24] refactor: make MarkdowRenderer type compatible with MarkdownIt --- src/node/markdown/markdown.ts | 24 ++---------------------- src/node/markdown/plugins/header.ts | 6 +++--- src/node/markdown/plugins/hoist.ts | 4 ++-- src/node/markdown/plugins/link.ts | 4 ++-- src/node/markdownToVue.ts | 11 +++++++++-- 5 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 1b204b14..934f15e2 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -39,15 +39,10 @@ export interface MarkdownParsedData { headers?: Header[] } -export interface MarkdownRenderer { +export interface MarkdownRenderer extends MarkdownIt { __path: string __relativePath: string __data: MarkdownParsedData - render: ( - src: string, - path: string, - relatiovePath: string - ) => { html: string; data: any } } export type { Header } @@ -100,20 +95,5 @@ export const createMarkdownRenderer = ( md.use(lineNumberPlugin) } - const wrappedMd = md as any as MarkdownRenderer - - // wrap render so that we can return both the html and extracted data. - const render = md.render - wrappedMd.render = (src, path, relativePath) => { - wrappedMd.__data = {} - wrappedMd.__path = path - wrappedMd.__relativePath = relativePath - const html = render.call(md, src) - return { - html, - data: wrappedMd.__data - } - } - - return wrappedMd + return md as MarkdownRenderer } diff --git a/src/node/markdown/plugins/header.ts b/src/node/markdown/plugins/header.ts index df44d2ca..8c6e5923 100644 --- a/src/node/markdown/plugins/header.ts +++ b/src/node/markdown/plugins/header.ts @@ -1,7 +1,7 @@ -import MarkdownIt from 'markdown-it' -import { MarkdownParsedData } from '../markdown' +import { MarkdownRenderer } from '../markdown' import { deeplyParseHeader } from '../../utils/parseHeader' import { slugify } from './slugify' +import MarkdownIt from 'markdown-it' export const extractHeaderPlugin = (md: MarkdownIt, include = ['h2', 'h3']) => { md.renderer.rules.heading_open = (tokens, i, options, env, self) => { @@ -10,7 +10,7 @@ export const extractHeaderPlugin = (md: MarkdownIt, include = ['h2', 'h3']) => { const title = tokens[i + 1].content const idAttr = token.attrs!.find(([name]) => name === 'id') const slug = idAttr && idAttr[1] - const data = (md as any).__data as MarkdownParsedData + const data = (md as MarkdownRenderer).__data const headers = data.headers || (data.headers = []) headers.push({ level: parseInt(token.tag.slice(1), 10), diff --git a/src/node/markdown/plugins/hoist.ts b/src/node/markdown/plugins/hoist.ts index 2a152d0e..ca309d5f 100644 --- a/src/node/markdown/plugins/hoist.ts +++ b/src/node/markdown/plugins/hoist.ts @@ -1,5 +1,5 @@ import MarkdownIt from 'markdown-it' -import { MarkdownParsedData } from '../markdown' +import { MarkdownRenderer } from '../markdown' // hoist