From 87d309c088ce7023fbf26ba7d62baf8c65a37830 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Mon, 24 Apr 2023 22:50:43 +0800 Subject: [PATCH] refactor: resolve circular dependency (#2283) --- src/node/config.ts | 235 ++---------------------- src/node/plugins/dynamicRoutesPlugin.ts | 34 +++- src/node/plugins/rewritesPlugin.ts | 2 +- src/node/siteConfig.ts | 208 +++++++++++++++++++++ 4 files changed, 253 insertions(+), 226 deletions(-) create mode 100644 src/node/siteConfig.ts diff --git a/src/node/config.ts b/src/node/config.ts index 6085ef70..910cd1ba 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -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 - extends LocaleSpecificConfig { - extends?: RawConfigExports - - base?: string - srcDir?: string - srcExclude?: string[] - outDir?: string - cacheDir?: string - - shouldPreload?: (link: string, page: string) => boolean - - locales?: LocaleConfig - - 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 - - /** - * Build end hook: called when SSG finish. - * @param siteConfig The resolved configuration. - */ - buildEnd?: (siteConfig: SiteConfig) => Awaitable - - /** - * Render end hook: called when SSR rendering is done. - */ - postRender?: (context: SSGContext) => Awaitable - - /** - * 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 - - /** - * HTML transform hook: runs before writing HTML to dist. - */ - transformHtml?: ( - code: string, - id: string, - ctx: TransformContext - ) => Awaitable - - /** - * PageData transform hook: runs when rendering markdown to vue - */ - transformPageData?: ( - pageData: PageData, - ctx: TransformPageContext - ) => Awaitable | { [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 = - | Awaitable> - | (() => Awaitable>) +export * from './siteConfig' +export { resolvePages } from './plugins/dynamicRoutesPlugin' -export interface SiteConfig - extends Pick< - UserConfig, - | 'markdown' - | 'vue' - | 'vite' - | 'shouldPreload' - | 'mpa' - | 'lastUpdated' - | 'ignoreDeadLinks' - | 'cleanUrls' - | 'useWebFonts' - | 'postRender' - | 'buildEnd' - | 'transformHead' - | 'transformHtml' - | 'transformPageData' - > { - root: string - srcDir: string - site: SiteData - configPath: string | undefined - configDeps: string[] - themeDir: string - outDir: string - cacheDir: string - tempDir: string - pages: string[] - dynamicRoutes: { - routes: ResolvedRouteConfig[] - fileToModulesMap: Record> - } - rewrites: { - map: Record - inv: Record - } - 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 - } -} diff --git a/src/node/plugins/dynamicRoutesPlugin.ts b/src/node/plugins/dynamicRoutesPlugin.ts index 49e2b80a..f24ebe6b 100644 --- a/src/node/plugins/dynamicRoutesPlugin.ts +++ b/src/node/plugins/dynamicRoutesPlugin.ts @@ -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 content?: string diff --git a/src/node/plugins/rewritesPlugin.ts b/src/node/plugins/rewritesPlugin.ts index 577964af..868f7967 100644 --- a/src/node/plugins/rewritesPlugin.ts +++ b/src/node/plugins/rewritesPlugin.ts @@ -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[], diff --git a/src/node/siteConfig.ts b/src/node/siteConfig.ts new file mode 100644 index 00000000..cf47f750 --- /dev/null +++ b/src/node/siteConfig.ts @@ -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 = + | Awaitable> + | (() => Awaitable>) + +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 + 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 + extends LocaleSpecificConfig { + extends?: RawConfigExports + + base?: string + srcDir?: string + srcExclude?: string[] + outDir?: string + cacheDir?: string + + shouldPreload?: (link: string, page: string) => boolean + + locales?: LocaleConfig + + 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 + + /** + * Build end hook: called when SSG finish. + * @param siteConfig The resolved configuration. + */ + buildEnd?: (siteConfig: SiteConfig) => Awaitable + + /** + * Render end hook: called when SSR rendering is done. + */ + postRender?: (context: SSGContext) => Awaitable + + /** + * 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 + + /** + * HTML transform hook: runs before writing HTML to dist. + */ + transformHtml?: ( + code: string, + id: string, + ctx: TransformContext + ) => Awaitable + + /** + * PageData transform hook: runs when rendering markdown to vue + */ + transformPageData?: ( + pageData: PageData, + ctx: TransformPageContext + ) => Awaitable | { [key: string]: any } | void> +} + +export interface SiteConfig + extends Pick< + UserConfig, + | 'markdown' + | 'vue' + | 'vite' + | 'shouldPreload' + | 'mpa' + | 'lastUpdated' + | 'ignoreDeadLinks' + | 'cleanUrls' + | 'useWebFonts' + | 'postRender' + | 'buildEnd' + | 'transformHead' + | 'transformHtml' + | 'transformPageData' + > { + root: string + srcDir: string + site: SiteData + configPath: string | undefined + configDeps: string[] + themeDir: string + outDir: string + cacheDir: string + tempDir: string + pages: string[] + dynamicRoutes: { + routes: ResolvedRouteConfig[] + fileToModulesMap: Record> + } + rewrites: { + map: Record + inv: Record + } + logger: Logger + userConfig: UserConfig +}