refactor: resolve circular dependency (#2283)

pull/2286/head
btea 1 year ago committed by GitHub
parent f3d3332fff
commit 87d309c088
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,4 @@
import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import _debug from 'debug'
import fg from 'fast-glob'
import fs from 'fs-extra'
import path from 'path'
import c from 'picocolors'
@ -8,207 +6,26 @@ import {
createLogger,
loadConfigFromFile,
mergeConfig as mergeViteConfig,
normalizePath,
type Logger,
type UserConfig as ViteConfig
normalizePath
} from 'vite'
import { DEFAULT_THEME_PATH } from './alias'
import type { MarkdownOptions } from './markdown/markdown'
import {
dynamicRouteRE,
resolveDynamicRoutes,
type ResolvedRouteConfig
} from './plugins/dynamicRoutesPlugin'
import { resolveRewrites } from './plugins/rewritesPlugin'
import { resolvePages } from './plugins/dynamicRoutesPlugin'
import {
APPEARANCE_KEY,
type Awaitable,
type DefaultTheme,
type HeadConfig,
type LocaleConfig,
type LocaleSpecificConfig,
type PageData,
type SiteData,
type SSGContext
type SiteData
} from './shared'
import {
type UserConfig,
type RawConfigExports,
type SiteConfig
} from './siteConfig'
const debug = _debug('vitepress:config')
export interface UserConfig<ThemeConfig = any>
extends LocaleSpecificConfig<ThemeConfig> {
extends?: RawConfigExports<ThemeConfig>
base?: string
srcDir?: string
srcExclude?: string[]
outDir?: string
cacheDir?: string
shouldPreload?: (link: string, page: string) => boolean
locales?: LocaleConfig<ThemeConfig>
appearance?: boolean | 'dark'
lastUpdated?: boolean
/**
* MarkdownIt options
*/
markdown?: MarkdownOptions
/**
* Options to pass on to `@vitejs/plugin-vue`
*/
vue?: VuePluginOptions
/**
* Vite config
*/
vite?: ViteConfig
/**
* Configure the scroll offset when the theme has a sticky header.
* Can be a number or a selector element to get the offset from.
* Can also be an array of selectors in case some elements will be
* invisible due to responsive layout. VitePress will fallback to the next
* selector if a selector fails to match, or the matched element is not
* currently visible in viewport.
*/
scrollOffset?: number | string | string[]
/**
* Enable MPA / zero-JS mode.
* @experimental
*/
mpa?: boolean
/**
* Don't fail builds due to dead links.
*
* @default false
*/
ignoreDeadLinks?:
| boolean
| 'localhostLinks'
| (string | RegExp | ((link: string) => boolean))[]
/**
* Don't force `.html` on URLs.
*
* @default false
*/
cleanUrls?: boolean
/**
* Use web fonts instead of emitting font files to dist.
* The used theme should import a file named `fonts.(s)css` for this to work.
* If you are a theme author, to support this, place your web font import
* between `webfont-marker-begin` and `webfont-marker-end` comments.
*
* @default true in webcontainers, else false
*/
useWebFonts?: boolean
/**
* @experimental
*
* source -> destination
*/
rewrites?: Record<string, string>
/**
* Build end hook: called when SSG finish.
* @param siteConfig The resolved configuration.
*/
buildEnd?: (siteConfig: SiteConfig) => Awaitable<void>
/**
* Render end hook: called when SSR rendering is done.
*/
postRender?: (context: SSGContext) => Awaitable<SSGContext | void>
/**
* Head transform hook: runs before writing HTML to dist.
*
* This build hook will allow you to modify the head adding new entries that cannot be statically added.
*/
transformHead?: (context: TransformContext) => Awaitable<HeadConfig[] | void>
/**
* HTML transform hook: runs before writing HTML to dist.
*/
transformHtml?: (
code: string,
id: string,
ctx: TransformContext
) => Awaitable<string | void>
/**
* PageData transform hook: runs when rendering markdown to vue
*/
transformPageData?: (
pageData: PageData,
ctx: TransformPageContext
) => Awaitable<Partial<PageData> | { [key: string]: any } | void>
}
export interface TransformPageContext {
siteConfig: SiteConfig
}
export interface TransformContext {
page: string
siteConfig: SiteConfig
siteData: SiteData
pageData: PageData
title: string
description: string
head: HeadConfig[]
content: string
assets: string[]
}
export type RawConfigExports<ThemeConfig = any> =
| Awaitable<UserConfig<ThemeConfig>>
| (() => Awaitable<UserConfig<ThemeConfig>>)
export * from './siteConfig'
export { resolvePages } from './plugins/dynamicRoutesPlugin'
export interface SiteConfig<ThemeConfig = any>
extends Pick<
UserConfig,
| 'markdown'
| 'vue'
| 'vite'
| 'shouldPreload'
| 'mpa'
| 'lastUpdated'
| 'ignoreDeadLinks'
| 'cleanUrls'
| 'useWebFonts'
| 'postRender'
| 'buildEnd'
| 'transformHead'
| 'transformHtml'
| 'transformPageData'
> {
root: string
srcDir: string
site: SiteData<ThemeConfig>
configPath: string | undefined
configDeps: string[]
themeDir: string
outDir: string
cacheDir: string
tempDir: string
pages: string[]
dynamicRoutes: {
routes: ResolvedRouteConfig[]
fileToModulesMap: Record<string, Set<string>>
}
rewrites: {
map: Record<string, string | undefined>
inv: Record<string, string | undefined>
}
logger: Logger
userConfig: UserConfig
}
const debug = _debug('vitepress:config')
const resolve = (root: string, file: string) =>
normalizePath(path.resolve(root, `.vitepress`, file))
@ -438,33 +255,3 @@ function resolveSiteDataHead(userConfig?: UserConfig): HeadConfig[] {
return head
}
export async function resolvePages(srcDir: string, userConfig: UserConfig) {
// 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
// builds - otherwise different input order could result in different exports
// order in shared chunks which in turns invalidates the hash of every chunk!
// JavaScript built-in sort() is mandated to be stable as of ES2019 and
// supported in Node 12+, which is required by Vite.
const allMarkdownFiles = (
await fg(['**.md'], {
cwd: srcDir,
ignore: ['**/node_modules', ...(userConfig.srcExclude || [])]
})
).sort()
const pages = allMarkdownFiles.filter((p) => !dynamicRouteRE.test(p))
const dynamicRouteFiles = allMarkdownFiles.filter((p) =>
dynamicRouteRE.test(p)
)
const dynamicRoutes = await resolveDynamicRoutes(srcDir, dynamicRouteFiles)
pages.push(...dynamicRoutes.routes.map((r) => r.path))
const rewrites = resolveRewrites(pages, userConfig.rewrites)
return {
pages,
dynamicRoutes,
rewrites
}
}

@ -7,10 +7,42 @@ import {
import fs from 'fs-extra'
import c from 'picocolors'
import path from 'path'
import { resolvePages, type SiteConfig } from '../config'
import fg from 'fast-glob'
import { type SiteConfig, type UserConfig } from '../siteConfig'
import { resolveRewrites } from './rewritesPlugin'
export const dynamicRouteRE = /\[(\w+?)\]/g
export async function resolvePages(srcDir: string, userConfig: UserConfig) {
// 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
// builds - otherwise different input order could result in different exports
// order in shared chunks which in turns invalidates the hash of every chunk!
// JavaScript built-in sort() is mandated to be stable as of ES2019 and
// supported in Node 12+, which is required by Vite.
const allMarkdownFiles = (
await fg(['**.md'], {
cwd: srcDir,
ignore: ['**/node_modules', ...(userConfig.srcExclude || [])]
})
).sort()
const pages = allMarkdownFiles.filter((p) => !dynamicRouteRE.test(p))
const dynamicRouteFiles = allMarkdownFiles.filter((p) =>
dynamicRouteRE.test(p)
)
const dynamicRoutes = await resolveDynamicRoutes(srcDir, dynamicRouteFiles)
pages.push(...dynamicRoutes.routes.map((r) => r.path))
const rewrites = resolveRewrites(pages, userConfig.rewrites)
return {
pages,
dynamicRoutes,
rewrites
}
}
interface UserRouteConfig {
params: Record<string, string>
content?: string

@ -1,6 +1,6 @@
import type { Plugin } from 'vite'
import { compile, match } from 'path-to-regexp'
import type { SiteConfig, UserConfig } from '../config'
import type { SiteConfig, UserConfig } from '../siteConfig'
export function resolveRewrites(
pages: string[],

@ -0,0 +1,208 @@
import {
type Awaitable,
type HeadConfig,
type LocaleConfig,
type LocaleSpecificConfig,
type PageData,
type SiteData,
type SSGContext
} from './shared'
import type { MarkdownOptions } from './markdown/markdown'
import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import { type Logger, type UserConfig as ViteConfig } from 'vite'
export type RawConfigExports<ThemeConfig = any> =
| Awaitable<UserConfig<ThemeConfig>>
| (() => Awaitable<UserConfig<ThemeConfig>>)
export interface TransformContext {
page: string
siteConfig: SiteConfig
siteData: SiteData
pageData: PageData
title: string
description: string
head: HeadConfig[]
content: string
assets: string[]
}
interface UserRouteConfig {
params: Record<string, string>
content?: string
}
export type ResolvedRouteConfig = UserRouteConfig & {
/**
* the raw route (relative to src root), e.g. foo/[bar].md
*/
route: string
/**
* the actual path with params resolved (relative to src root), e.g. foo/1.md
*/
path: string
/**
* absolute fs path
*/
fullPath: string
}
export interface TransformPageContext {
siteConfig: SiteConfig
}
export interface UserConfig<ThemeConfig = any>
extends LocaleSpecificConfig<ThemeConfig> {
extends?: RawConfigExports<ThemeConfig>
base?: string
srcDir?: string
srcExclude?: string[]
outDir?: string
cacheDir?: string
shouldPreload?: (link: string, page: string) => boolean
locales?: LocaleConfig<ThemeConfig>
appearance?: boolean | 'dark'
lastUpdated?: boolean
/**
* MarkdownIt options
*/
markdown?: MarkdownOptions
/**
* Options to pass on to `@vitejs/plugin-vue`
*/
vue?: VuePluginOptions
/**
* Vite config
*/
vite?: ViteConfig
/**
* Configure the scroll offset when the theme has a sticky header.
* Can be a number or a selector element to get the offset from.
* Can also be an array of selectors in case some elements will be
* invisible due to responsive layout. VitePress will fallback to the next
* selector if a selector fails to match, or the matched element is not
* currently visible in viewport.
*/
scrollOffset?: number | string | string[]
/**
* Enable MPA / zero-JS mode.
* @experimental
*/
mpa?: boolean
/**
* Don't fail builds due to dead links.
*
* @default false
*/
ignoreDeadLinks?:
| boolean
| 'localhostLinks'
| (string | RegExp | ((link: string) => boolean))[]
/**
* Don't force `.html` on URLs.
*
* @default false
*/
cleanUrls?: boolean
/**
* Use web fonts instead of emitting font files to dist.
* The used theme should import a file named `fonts.(s)css` for this to work.
* If you are a theme author, to support this, place your web font import
* between `webfont-marker-begin` and `webfont-marker-end` comments.
*
* @default true in webcontainers, else false
*/
useWebFonts?: boolean
/**
* @experimental
*
* source -> destination
*/
rewrites?: Record<string, string>
/**
* Build end hook: called when SSG finish.
* @param siteConfig The resolved configuration.
*/
buildEnd?: (siteConfig: SiteConfig) => Awaitable<void>
/**
* Render end hook: called when SSR rendering is done.
*/
postRender?: (context: SSGContext) => Awaitable<SSGContext | void>
/**
* Head transform hook: runs before writing HTML to dist.
*
* This build hook will allow you to modify the head adding new entries that cannot be statically added.
*/
transformHead?: (context: TransformContext) => Awaitable<HeadConfig[] | void>
/**
* HTML transform hook: runs before writing HTML to dist.
*/
transformHtml?: (
code: string,
id: string,
ctx: TransformContext
) => Awaitable<string | void>
/**
* PageData transform hook: runs when rendering markdown to vue
*/
transformPageData?: (
pageData: PageData,
ctx: TransformPageContext
) => Awaitable<Partial<PageData> | { [key: string]: any } | void>
}
export interface SiteConfig<ThemeConfig = any>
extends Pick<
UserConfig,
| 'markdown'
| 'vue'
| 'vite'
| 'shouldPreload'
| 'mpa'
| 'lastUpdated'
| 'ignoreDeadLinks'
| 'cleanUrls'
| 'useWebFonts'
| 'postRender'
| 'buildEnd'
| 'transformHead'
| 'transformHtml'
| 'transformPageData'
> {
root: string
srcDir: string
site: SiteData<ThemeConfig>
configPath: string | undefined
configDeps: string[]
themeDir: string
outDir: string
cacheDir: string
tempDir: string
pages: string[]
dynamicRoutes: {
routes: ResolvedRouteConfig[]
fileToModulesMap: Record<string, Set<string>>
}
rewrites: {
map: Record<string, string | undefined>
inv: Record<string, string | undefined>
}
logger: Logger
userConfig: UserConfig
}
Loading…
Cancel
Save