Merge branch 'main' into feat/multithread-render

pull/3386/head
Yuxuan Zhang 2 years ago committed by GitHub
commit f196fb6d17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -86,6 +86,11 @@ const sidebar: DefaultTheme.Config['sidebar'] = {
export default defineConfig({ export default defineConfig({
title: 'Example', title: 'Example',
description: 'An example app using VitePress.', description: 'An example app using VitePress.',
markdown: {
image: {
lazyLoading: true
}
},
themeConfig: { themeConfig: {
sidebar, sidebar,
search: { search: {

@ -196,3 +196,7 @@ export default config
## Markdown File Inclusion with Range without End ## Markdown File Inclusion with Range without End
<!--@include: ./foo.md{6,}--> <!--@include: ./foo.md{6,}-->
## Image Lazy Loading
![vitepress logo](/vitepress.png)

@ -65,7 +65,7 @@ describe('Table of Contents', () => {
test('render toc', async () => { test('render toc', async () => {
const items = page.locator('#table-of-contents + nav ul li') const items = page.locator('#table-of-contents + nav ul li')
const count = await items.count() const count = await items.count()
expect(count).toBe(35) expect(count).toBe(36)
}) })
}) })
@ -280,3 +280,10 @@ describe('Markdown File Inclusion', () => {
expect(await p.textContent()).not.toContain('title') expect(await p.textContent()).not.toContain('title')
}) })
}) })
describe('Image Lazy Loading', () => {
test('render loading="lazy" in the <img> tag', async () => {
const img = page.locator('#image-lazy-loading + p img')
expect(await img.getAttribute('loading')).toBe('lazy')
})
})

@ -847,6 +847,21 @@ $$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ | | $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_ | | $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_ |
## Image Lazy Loading
You can enable lazy loading for each image added via markdown by setting `lazyLoading` to `true` in your config file:
```js
export default {
markdown: {
image: {
// image lazy loading is disabled by default
lazyLoading: true
}
}
}
```
## Advanced Configuration ## Advanced Configuration
VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`: VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`:

@ -406,6 +406,20 @@ export interface DocFooter {
Can be used to customize the dark mode switch label. This label is only displayed in the mobile view. Can be used to customize the dark mode switch label. This label is only displayed in the mobile view.
## lightModeSwitchTitle
- Type: `string`
- Default: `Switch to light theme`
Can be used to customize the light mode switch title that appears on hovering.
## darkModeSwitchTitle
- Type: `string`
- Default: `Switch to dark theme`
Can be used to customize the dark mode switch title that appears on hovering.
## sidebarMenuLabel ## sidebarMenuLabel
- Type: `string` - Type: `string`

@ -24,6 +24,62 @@ export default {
} }
``` ```
:::details Dynamic (Async) Config
If you need to dynamically generate the config, you can also default export a function. For example:
```ts
import { defineConfig } from 'vitepress'
export default async () => defineConfig({
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
return {
// app level config options
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
// theme level config options
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
}
})
```
You can also use top-level `await`. For example:
```ts
import { defineConfig } from 'vitepress'
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
export default defineConfig({
// app level config options
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
// theme level config options
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
})
```
:::
### Config Intellisense ### Config Intellisense
Using the `defineConfig` helper will provide TypeScript-powered intellisense for config options. Assuming your IDE supports it, this should work in both JavaScript and TypeScript. Using the `defineConfig` helper will provide TypeScript-powered intellisense for config options. Assuming your IDE supports it, this should work in both JavaScript and TypeScript.

@ -3,7 +3,7 @@
"version": "1.0.0-rc.33", "version": "1.0.0-rc.33",
"description": "Vite & Vue powered static site generator", "description": "Vite & Vue powered static site generator",
"type": "module", "type": "module",
"packageManager": "pnpm@8.12.1", "packageManager": "pnpm@8.13.1",
"main": "dist/node/index.js", "main": "dist/node/index.js",
"types": "types/index.d.ts", "types": "types/index.d.ts",
"exports": { "exports": {
@ -93,19 +93,20 @@
"@docsearch/css": "^3.5.2", "@docsearch/css": "^3.5.2",
"@docsearch/js": "^3.5.2", "@docsearch/js": "^3.5.2",
"@types/markdown-it": "^13.0.7", "@types/markdown-it": "^13.0.7",
"@vitejs/plugin-vue": "^5.0.0", "@vitejs/plugin-vue": "^5.0.2",
"@vue/devtools-api": "^6.5.1", "@vue/devtools-api": "^6.5.1",
"@vueuse/core": "^10.7.0", "@vueuse/core": "^10.7.1",
"@vueuse/integrations": "^10.7.0", "@vueuse/integrations": "^10.7.1",
"focus-trap": "^7.5.4", "focus-trap": "^7.5.4",
"mark.js": "8.11.1", "mark.js": "8.11.1",
"minisearch": "^6.3.0", "minisearch": "^6.3.0",
"mrmime": "^2.0.0", "mrmime": "^2.0.0",
"rpc-magic-proxy": "0.0.0-beta.1", "rpc-magic-proxy": "0.0.0-beta.1",
"shikiji": "^0.9.12", "shikiji": "^0.9.15",
"shikiji-transformers": "^0.9.12", "shikiji-core": "^0.9.15",
"shikiji-transformers": "^0.9.15",
"vite": "^5.0.10", "vite": "^5.0.10",
"vue": "^3.4.0-rc.2" "vue": "^3.4.3"
}, },
"peerDependencies": { "peerDependencies": {
"markdown-it-mathjax3": "^4.3.2", "markdown-it-mathjax3": "^4.3.2",
@ -146,16 +147,16 @@
"@types/markdown-it-emoji": "^2.0.4", "@types/markdown-it-emoji": "^2.0.4",
"@types/micromatch": "^4.0.6", "@types/micromatch": "^4.0.6",
"@types/minimist": "^1.2.5", "@types/minimist": "^1.2.5",
"@types/node": "^20.10.5", "@types/node": "^20.10.6",
"@types/postcss-prefix-selector": "^1.16.3", "@types/postcss-prefix-selector": "^1.16.3",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"@vue/shared": "^3.3.13", "@vue/shared": "^3.4.3",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"compression": "^1.7.4", "compression": "^1.7.4",
"conventional-changelog-cli": "^4.1.0", "conventional-changelog-cli": "^4.1.0",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"debug": "^4.3.4", "debug": "^4.3.4",
"esbuild": "^0.19.10", "esbuild": "^0.19.11",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"execa": "^8.0.1", "execa": "^8.0.1",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
@ -177,7 +178,7 @@
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"ora": "^8.0.1", "ora": "^8.0.1",
"p-map": "^7.0.0", "p-map": "^7.0.1",
"path-to-regexp": "^6.2.1", "path-to-regexp": "^6.2.1",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"pkg-dir": "^8.0.0", "pkg-dir": "^8.0.0",
@ -188,18 +189,17 @@
"prompts": "^2.4.2", "prompts": "^2.4.2",
"punycode": "^2.3.1", "punycode": "^2.3.1",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^4.9.1", "rollup": "^4.9.2",
"rollup-plugin-dts": "^6.1.0", "rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.0", "rollup-plugin-esbuild": "^6.1.0",
"semver": "^7.5.4", "semver": "^7.5.4",
"shikiji-core": "^0.9.12",
"simple-git-hooks": "^2.9.0", "simple-git-hooks": "^2.9.0",
"sirv": "^2.0.4", "sirv": "^2.0.4",
"sitemap": "^7.1.1", "sitemap": "^7.1.1",
"supports-color": "^9.4.0", "supports-color": "^9.4.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vitest": "^1.1.0", "vitest": "^1.1.0",
"vue-tsc": "^1.8.26", "vue-tsc": "^1.8.27",
"wait-on": "^7.2.0" "wait-on": "^7.2.0"
}, },
"simple-git-hooks": { "simple-git-hooks": {

File diff suppressed because it is too large Load Diff

@ -89,7 +89,7 @@ export function initData(route: Route): VitePressData {
frontmatter: computed(() => route.data.frontmatter), frontmatter: computed(() => route.data.frontmatter),
params: computed(() => route.data.params), params: computed(() => route.data.params),
lang: computed(() => site.value.lang), lang: computed(() => site.value.lang),
dir: computed(() => site.value.dir), dir: computed(() => route.data.frontmatter.dir || site.value.dir || 'ltr'),
localeIndex: computed(() => site.value.localeIndex || 'root'), localeIndex: computed(() => site.value.localeIndex || 'root'),
title: computed(() => { title: computed(() => {
return createTitle(site.value, route.data) return createTitle(site.value, route.data)

@ -5,14 +5,16 @@ import VPSwitch from './VPSwitch.vue'
import VPIconMoon from './icons/VPIconMoon.vue' import VPIconMoon from './icons/VPIconMoon.vue'
import VPIconSun from './icons/VPIconSun.vue' import VPIconSun from './icons/VPIconSun.vue'
const { isDark } = useData() const { isDark, theme } = useData()
const toggleAppearance = inject('toggle-appearance', () => { const toggleAppearance = inject('toggle-appearance', () => {
isDark.value = !isDark.value isDark.value = !isDark.value
}) })
const switchTitle = computed(() => { const switchTitle = computed(() => {
return isDark.value ? 'Switch to light theme' : 'Switch to dark theme' return isDark.value
? theme.value.lightModeSwitchTitle || 'Switch to light theme'
: theme.value.darkModeSwitchTitle || 'Switch to dark theme'
}) })
</script> </script>

@ -39,7 +39,6 @@ body {
font-weight: 400; font-weight: 400;
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
background-color: var(--vp-c-bg); background-color: var(--vp-c-bg);
direction: ltr;
font-synthesis: style; font-synthesis: style;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;

@ -105,7 +105,8 @@ export async function resolveConfig(
const { pages, dynamicRoutes, rewrites } = await resolvePages( const { pages, dynamicRoutes, rewrites } = await resolvePages(
srcDir, srcDir,
userConfig userConfig,
logger
) )
const config: SiteConfig = { const config: SiteConfig = {

@ -19,31 +19,31 @@ import anchorPlugin from 'markdown-it-anchor'
import attrsPlugin from 'markdown-it-attrs' import attrsPlugin from 'markdown-it-attrs'
// @ts-ignore // @ts-ignore
import { full as emojiPlugin } from 'markdown-it-emoji' import { full as emojiPlugin } from 'markdown-it-emoji'
import type {
BuiltinTheme,
Highlighter,
LanguageInput,
ShikijiTransformer,
ThemeRegistrationAny
} from 'shikiji'
import type { Logger } from 'vite' import type { Logger } from 'vite'
import { containerPlugin, type ContainerOptions } from './plugins/containers' import { containerPlugin, type ContainerOptions } from './plugins/containers'
import { highlight } from './plugins/highlight' import { highlight } from './plugins/highlight'
import { highlightLinePlugin } from './plugins/highlightLines' import { highlightLinePlugin } from './plugins/highlightLines'
import { imagePlugin } from './plugins/image' import { imagePlugin, type Options as ImageOptions } from './plugins/image'
import { lineNumberPlugin } from './plugins/lineNumbers' import { lineNumberPlugin } from './plugins/lineNumbers'
import { linkPlugin } from './plugins/link' import { linkPlugin } from './plugins/link'
import { preWrapperPlugin } from './plugins/preWrapper' import { preWrapperPlugin } from './plugins/preWrapper'
import { snippetPlugin } from './plugins/snippet' import { snippetPlugin } from './plugins/snippet'
import type {
ThemeRegistration,
BuiltinTheme,
LanguageInput,
ShikijiTransformer,
Highlighter
} from 'shikiji'
export type { Header } from '../shared' export type { Header } from '../shared'
export type ThemeOptions = export type ThemeOptions =
| ThemeRegistration | ThemeRegistrationAny
| BuiltinTheme | BuiltinTheme
| { | {
light: ThemeRegistration | BuiltinTheme light: ThemeRegistrationAny | BuiltinTheme
dark: ThemeRegistration | BuiltinTheme dark: ThemeRegistrationAny | BuiltinTheme
} }
export interface MarkdownOptions extends MarkdownIt.Options { export interface MarkdownOptions extends MarkdownIt.Options {
@ -166,6 +166,7 @@ export interface MarkdownOptions extends MarkdownIt.Options {
* @see https://vitepress.dev/guide/markdown#math-equations * @see https://vitepress.dev/guide/markdown#math-equations
*/ */
math?: boolean | any math?: boolean | any
image?: ImageOptions
} }
export type MarkdownRenderer = MarkdownIt export type MarkdownRenderer = MarkdownIt
@ -198,7 +199,7 @@ export const createMarkdownRenderer = async (
.use(preWrapperPlugin, { hasSingleTheme }) .use(preWrapperPlugin, { hasSingleTheme })
.use(snippetPlugin, srcDir) .use(snippetPlugin, srcDir)
.use(containerPlugin, { hasSingleTheme }, options.container) .use(containerPlugin, { hasSingleTheme }, options.container)
.use(imagePlugin) .use(imagePlugin, options.image)
.use( .use(
linkPlugin, linkPlugin,
{ target: '_blank', rel: 'noreferrer', ...options.externalLinks }, { target: '_blank', rel: 'noreferrer', ...options.externalLinks },

@ -2,14 +2,12 @@ import { customAlphabet } from 'nanoid'
import c from 'picocolors' import c from 'picocolors'
import type { ShikijiTransformer } from 'shikiji' import type { ShikijiTransformer } from 'shikiji'
import { import {
addClassToHast,
bundledLanguages, bundledLanguages,
getHighlighter, getHighlighter,
addClassToHast,
isPlaintext as isPlainLang, isPlaintext as isPlainLang,
isSpecialLang isSpecialLang
} from 'shikiji' } from 'shikiji'
import type { Logger } from 'vite'
import type { MarkdownOptions, ThemeOptions } from '../markdown'
import { import {
transformerCompactLineOptions, transformerCompactLineOptions,
transformerNotationDiff, transformerNotationDiff,
@ -18,6 +16,8 @@ import {
transformerNotationHighlight, transformerNotationHighlight,
type TransformerCompactLineOption type TransformerCompactLineOption
} from 'shikiji-transformers' } from 'shikiji-transformers'
import type { Logger } from 'vite'
import type { MarkdownOptions, ThemeOptions } from '../markdown'
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
@ -65,9 +65,9 @@ export async function highlight(
const highlighter = await getHighlighter({ const highlighter = await getHighlighter({
themes: themes:
typeof theme === 'string' || 'name' in theme typeof theme === 'object' && 'light' in theme && 'dark' in theme
? [theme] ? [theme.light, theme.dark]
: [theme.light, theme.dark], : [theme],
langs: [...Object.keys(bundledLanguages), ...(options.languages || [])], langs: [...Object.keys(bundledLanguages), ...(options.languages || [])],
langAlias: options.languageAlias langAlias: options.languageAlias
}) })
@ -169,15 +169,10 @@ export async function highlight(
}, },
...userTransformers ...userTransformers
], ],
meta: { meta: { __raw: attrs },
__raw: attrs ...(typeof theme === 'object' && 'light' in theme && 'dark' in theme
}, ? { themes: theme, defaultColor: false }
...(typeof theme === 'string' || 'name' in theme : { theme })
? { theme }
: {
themes: theme,
defaultColor: false
})
}) })
return fillEmptyHighlightedLine(restoreMustache(highlighted)) return fillEmptyHighlightedLine(restoreMustache(highlighted))

@ -3,7 +3,15 @@
import type MarkdownIt from 'markdown-it' import type MarkdownIt from 'markdown-it'
import { EXTERNAL_URL_RE } from '../../shared' import { EXTERNAL_URL_RE } from '../../shared'
export const imagePlugin = (md: MarkdownIt) => { export interface Options {
/**
* Support native lazy loading for the `<img>` tag.
* @default false
*/
lazyLoading?: boolean
}
export const imagePlugin = (md: MarkdownIt, { lazyLoading }: Options = {}) => {
const imageRule = md.renderer.rules.image! const imageRule = md.renderer.rules.image!
md.renderer.rules.image = (tokens, idx, options, env, self) => { md.renderer.rules.image = (tokens, idx, options, env, self) => {
const token = tokens[idx] const token = tokens[idx]
@ -12,6 +20,9 @@ export const imagePlugin = (md: MarkdownIt) => {
if (!/^\.?\//.test(url)) url = './' + url if (!/^\.?\//.test(url)) url = './' + url
token.attrSet('src', decodeURIComponent(url)) token.attrSet('src', decodeURIComponent(url))
} }
if (lazyLoading) {
token.attrSet('loading', 'lazy')
}
return imageRule(tokens, idx, options, env, self) return imageRule(tokens, idx, options, env, self)
} }
} }

@ -268,7 +268,11 @@ export async function createVitePressPlugin(
if (file.endsWith('.md')) { if (file.endsWith('.md')) {
Object.assign( Object.assign(
siteConfig, siteConfig,
await resolvePages(siteConfig.srcDir, siteConfig.userConfig) await resolvePages(
siteConfig.srcDir,
siteConfig.userConfig,
siteConfig.logger
)
) )
} }

@ -1,6 +1,7 @@
import { import {
loadConfigFromFile, loadConfigFromFile,
normalizePath, normalizePath,
type Logger,
type Plugin, type Plugin,
type ViteDevServer type ViteDevServer
} from 'vite' } from 'vite'
@ -13,7 +14,11 @@ import { resolveRewrites } from './rewritesPlugin'
export const dynamicRouteRE = /\[(\w+?)\]/g export const dynamicRouteRE = /\[(\w+?)\]/g
export async function resolvePages(srcDir: string, userConfig: UserConfig) { export async function resolvePages(
srcDir: string,
userConfig: UserConfig,
logger: Logger
) {
// Important: fast-glob doesn't guarantee order of the returned files. // Important: fast-glob doesn't guarantee order of the returned files.
// We must sort the pages so the input list to rollup is stable across // We must sort the pages so the input list to rollup is stable across
// builds - otherwise different input order could result in different exports // builds - otherwise different input order could result in different exports
@ -39,7 +44,11 @@ export async function resolvePages(srcDir: string, userConfig: UserConfig) {
;(dynamicRouteRE.test(file) ? dynamicRouteFiles : pages).push(file) ;(dynamicRouteRE.test(file) ? dynamicRouteFiles : pages).push(file)
}) })
const dynamicRoutes = await resolveDynamicRoutes(srcDir, dynamicRouteFiles) const dynamicRoutes = await resolveDynamicRoutes(
srcDir,
dynamicRouteFiles,
logger
)
pages.push(...dynamicRoutes.routes.map((r) => r.path)) pages.push(...dynamicRoutes.routes.map((r) => r.path))
const rewrites = resolveRewrites(pages, userConfig.rewrites) const rewrites = resolveRewrites(pages, userConfig.rewrites)
@ -141,7 +150,7 @@ export const dynamicRoutesPlugin = async (
if (!/\.md$/.test(ctx.file)) { if (!/\.md$/.test(ctx.file)) {
Object.assign( Object.assign(
config, config,
await resolvePages(config.srcDir, config.userConfig) await resolvePages(config.srcDir, config.userConfig, config.logger)
) )
} }
for (const id of mods) { for (const id of mods) {
@ -154,7 +163,8 @@ export const dynamicRoutesPlugin = async (
export async function resolveDynamicRoutes( export async function resolveDynamicRoutes(
srcDir: string, srcDir: string,
routes: string[] routes: string[],
logger: Logger
): Promise<SiteConfig['dynamicRoutes']> { ): Promise<SiteConfig['dynamicRoutes']> {
const pendingResolveRoutes: Promise<ResolvedRouteConfig[]>[] = [] const pendingResolveRoutes: Promise<ResolvedRouteConfig[]>[] = []
const routeFileToModulesMap: Record<string, Set<string>> = {} const routeFileToModulesMap: Record<string, Set<string>> = {}
@ -170,7 +180,7 @@ export async function resolveDynamicRoutes(
const pathsFile = paths.find((p) => fs.existsSync(p)) const pathsFile = paths.find((p) => fs.existsSync(p))
if (pathsFile == null) { if (pathsFile == null) {
console.warn( logger.warn(
c.yellow( c.yellow(
`Missing paths file for dynamic route ${route}: ` + `Missing paths file for dynamic route ${route}: ` +
`a corresponding ${paths[0]} (or .ts/.mjs/.mts) file is needed.` `a corresponding ${paths[0]} (or .ts/.mjs/.mts) file is needed.`
@ -183,15 +193,15 @@ export async function resolveDynamicRoutes(
let mod = routeModuleCache.get(pathsFile) let mod = routeModuleCache.get(pathsFile)
if (!mod) { if (!mod) {
try { try {
mod = (await loadConfigFromFile({} as any, pathsFile)) as RouteModule mod = (await loadConfigFromFile(
{} as any,
pathsFile,
undefined,
'silent'
)) as RouteModule
routeModuleCache.set(pathsFile, mod) routeModuleCache.set(pathsFile, mod)
} catch (e) { } catch (e: any) {
console.warn( logger.warn(`${c.yellow(`Failed to load ${pathsFile}:`)}\n${e.stack}`)
c.yellow(
`Invalid paths file export in ${pathsFile}. ` +
`Expects default export of an object with a "paths" property.`
)
)
continue continue
} }
} }
@ -210,7 +220,7 @@ export async function resolveDynamicRoutes(
const loader = mod!.config.paths const loader = mod!.config.paths
if (!loader) { if (!loader) {
console.warn( logger.warn(
c.yellow( c.yellow(
`Invalid paths file export in ${pathsFile}. ` + `Invalid paths file export in ${pathsFile}. ` +
`Missing "paths" property from default export.` `Missing "paths" property from default export.`

@ -96,6 +96,16 @@ export namespace DefaultTheme {
*/ */
darkModeSwitchLabel?: string darkModeSwitchLabel?: string
/**
* @default 'Switch to light theme'
*/
lightModeSwitchTitle?: string
/**
* @default 'Switch to dark theme'
*/
darkModeSwitchTitle?: string
/** /**
* @default 'Menu' * @default 'Menu'
*/ */

Loading…
Cancel
Save