Skip to content

feat: improve typescript support for config file #465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -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.',
Expand Down Expand Up @@ -42,7 +44,7 @@ export default {
'/': getGuideSidebar()
}
}
}
})

function getGuideSidebar() {
return [
Expand Down
56 changes: 56 additions & 0 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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>({
themeConfig: {
// Type is `ThemeConfig`
}
})
```
147 changes: 1 addition & 146 deletions src/client/theme-default/config.ts
Original file line number Diff line number Diff line change
@@ -1,146 +1 @@
export namespace DefaultTheme {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this file to shared types since we need it between server and client.

export interface Config {
logo?: string
nav?: NavItem[] | false
sidebar?: SideBarConfig | MultiSideBarConfig

/**
* GitHub repository following the format <user>/<project>.
*
* @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<string, LocaleConfig & Omit<Config, 'locales'>>

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'
1 change: 1 addition & 0 deletions src/client/theme-default/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"vitepress": ["index.ts"]
}
},
"include": [".", "../../types/shared.d.ts"]
"include": ["."]
}
24 changes: 17 additions & 7 deletions src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -27,7 +28,7 @@ const debug = _debug('vitepress:config')
export type { MarkdownOptions }

export interface UserConfig<ThemeConfig = any> {
extends?: RawConfigExports
extends?: RawConfigExports<ThemeConfig>
lang?: string
base?: string
title?: string
Expand Down Expand Up @@ -55,10 +56,10 @@ export interface UserConfig<ThemeConfig = any> {
mpa?: boolean
}

export type RawConfigExports =
| UserConfig
| Promise<UserConfig>
| (() => UserConfig | Promise<UserConfig>)
export type RawConfigExports<ThemeConfig = any> =
| UserConfig<ThemeConfig>
| Promise<UserConfig<ThemeConfig>>
| (() => UserConfig<ThemeConfig> | Promise<UserConfig<ThemeConfig>>)

export interface SiteConfig<ThemeConfig = any> {
root: string
Expand All @@ -82,7 +83,16 @@ const resolve = (root: string, file: string) =>
/**
* Type config helper
*/
export function defineConfig(config: RawConfigExports) {
export function defineConfig(config: UserConfig<DefaultTheme.Config>) {
return config
}

/**
* Type config helper for custom theme config
*/
export function defineConfigWithTheme<ThemeConfig>(
config: UserConfig<ThemeConfig>
) {
return config
}

Expand Down
2 changes: 1 addition & 1 deletion src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
3 changes: 2 additions & 1 deletion src/shared/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export type {
PageData,
HeadConfig,
LocaleConfig,
Header
Header,
DefaultTheme,
} from '../../types/shared'

export const EXTERNAL_URL_RE = /^https?:/i
Expand Down
2 changes: 1 addition & 1 deletion src/shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"compilerOptions": {
"baseUrl": "."
},
"include": [".", "../../types/shared.d.ts"]
"include": ["."]
}
Loading