diff --git a/cli/src/prompt.ts b/cli/src/prompt.ts index da5a30cc..5d802b43 100644 --- a/cli/src/prompt.ts +++ b/cli/src/prompt.ts @@ -1,4 +1,4 @@ -import type { Bundler, Langs, Options, PromptResult } from './types.js' +import type { Bundler, Langs, PromptResult } from './types.js' import { createRequire } from 'node:module' import process from 'node:process' import { cancel, confirm, group, select, text } from '@clack/prompts' @@ -20,7 +20,7 @@ export async function prompt(mode: Mode, root?: string): Promise { const result: PromptResult = await group({ displayLang: async () => { - const lang = await select, Langs>({ + const lang = await select({ message: 'Select a language to display / 选择显示语言', options: languageOptions, }) @@ -68,7 +68,7 @@ export async function prompt(mode: Mode, root?: string): Promise { initialValue: false, }), - defaultLanguage: () => select, Langs>({ + defaultLanguage: () => select({ message: t('question.defaultLanguage'), options: languageOptions, }), @@ -93,7 +93,7 @@ export async function prompt(mode: Mode, root?: string): Promise { }) }, - bundler: () => select, Bundler>({ + bundler: () => select({ message: t('question.bundler'), options: bundlerOptions, }), @@ -102,7 +102,7 @@ export async function prompt(mode: Mode, root?: string): Promise { if (mode === Mode.init) { return DeployType.custom } - return await select, DeployType>({ + return await select({ message: t('question.deploy'), options: deployOptions, initialValue: DeployType.custom, diff --git a/plugins/plugin-shikiji/src/node/copy-code-button/createCopyCodeButtonRender.ts b/plugins/plugin-shikiji/src/node/copy-code-button/createCopyCodeButtonRender.ts index f965ce15..7daaedf5 100644 --- a/plugins/plugin-shikiji/src/node/copy-code-button/createCopyCodeButtonRender.ts +++ b/plugins/plugin-shikiji/src/node/copy-code-button/createCopyCodeButtonRender.ts @@ -5,7 +5,7 @@ import type { } from '../types.js' import { getLocalePaths, - getRootLangPath, + inferRootLocalePath, isPlainObject, } from '@vuepress/helper' import { ensureLeadingSlash, resolveLocalePath } from 'vuepress/shared' @@ -18,7 +18,7 @@ export function createCopyCodeButtonRender(app: App, options?: boolean | CopyCod const { className = 'copy', locales: userLocales = {} } = isPlainObject(options) ? options : {} - const root = getRootLangPath(app) + const root = inferRootLocalePath(app) const locales: LocaleConfig = { // fallback locale '/': userLocales['/'] || copyCodeButtonLocales[root], diff --git a/theme/src/node/autoFrontmatter/generator.ts b/theme/src/node/autoFrontmatter/generator.ts index 6981d65a..889eeea1 100644 --- a/theme/src/node/autoFrontmatter/generator.ts +++ b/theme/src/node/autoFrontmatter/generator.ts @@ -13,7 +13,7 @@ import grayMatter from 'gray-matter' import jsonToYaml from 'json2yaml' import { fs, hash, path } from 'vuepress/utils' import { getThemeConfig } from '../loadConfig/index.js' -import { logger } from '../utils/index.js' +import { perfLog, perfMark } from '../utils/index.js' import { readMarkdown, readMarkdownList } from './readFile.js' import { resolveOptions } from './resolveOptions.js' @@ -88,7 +88,7 @@ export function initAutoFrontmatter( } export async function generateAutoFrontmatter(app: App) { - const start = performance.now() + perfMark('generate:frontmatter') if (!generate) return @@ -109,9 +109,7 @@ export async function generateAutoFrontmatter(app: App) { await generate.updateCache(app) - if (app.env.isDebug) { - logger.info(`Generate auto frontmatter: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('generate:frontmatter', app.env.isDebug) } export async function watchAutoFrontmatter(app: App, watchers: any[]) { diff --git a/theme/src/node/config/resolveProvideData.ts b/theme/src/node/config/resolveProvideData.ts index c7a0f827..f30f016f 100644 --- a/theme/src/node/config/resolveProvideData.ts +++ b/theme/src/node/config/resolveProvideData.ts @@ -1,13 +1,13 @@ import type { App } from 'vuepress' import type { PlumeThemePluginOptions } from '../../shared/index.js' -import { entries, fromEntries, getLocalePaths, getRootLangPath, isPlainObject } from '@vuepress/helper' +import { entries, fromEntries, getLocalePaths, inferRootLocalePath, isPlainObject } from '@vuepress/helper' import { PRESET_LOCALES } from '../locales/index.js' export function resolveProvideData( app: App, plugins: PlumeThemePluginOptions, ): Record { - const root = getRootLangPath(app) + const root = inferRootLocalePath(app) const locales = [...getLocalePaths(app), root] return { // 注入水印配置 diff --git a/theme/src/node/loadConfig/findConfigPath.ts b/theme/src/node/loadConfig/findConfigPath.ts index deb88c2a..c3f37765 100644 --- a/theme/src/node/loadConfig/findConfigPath.ts +++ b/theme/src/node/loadConfig/findConfigPath.ts @@ -6,7 +6,7 @@ import { colors } from 'vuepress/utils' import { logger } from '../utils/index.js' const CONFIG_FILE_NAME = 'plume.config' -const extensions: string[] = ['ts', 'js', 'mts', 'cts', 'mjs', 'cjs'] +const extensions: string[] = ['ts', 'js', 'mjs', 'cjs', 'mts', 'cts'] export async function findConfigPath(app: App, configPath?: string): Promise { const cwd = process.cwd() diff --git a/theme/src/node/loadConfig/loader.ts b/theme/src/node/loadConfig/loader.ts index 3f4b2179..37611978 100644 --- a/theme/src/node/loadConfig/loader.ts +++ b/theme/src/node/loadConfig/loader.ts @@ -6,7 +6,7 @@ import { deepMerge } from '@pengzhanbo/utils' import { watch } from 'chokidar' import { path } from 'vuepress/utils' import { resolveLocaleOptions } from '../config/resolveLocaleOptions.js' -import { logger } from '../utils/index.js' +import { perfLog, perfMark } from '../utils/index.js' import { compiler } from './compiler.js' import { findConfigPath } from './findConfigPath.js' @@ -41,7 +41,7 @@ export async function initConfigLoader( defaultConfig: ThemeConfig, { configFile, onChange }: InitConfigLoaderOptions = {}, ) { - const start = performance.now() + perfMark('load-config') const { encrypt, autoFrontmatter, ...localeOptions } = defaultConfig loader = { configFile, @@ -58,21 +58,18 @@ export async function initConfigLoader( }, } - const findStart = performance.now() + perfMark('load-config:find') loader.configFile = await findConfigPath(app, configFile) - if (app.env.isDebug) { - logger.info(`Find config path: ${(performance.now() - findStart).toFixed(2)}ms`) - } + perfLog('load-config:find', app.env.isDebug) if (onChange) { loader.changeEvents.push(onChange) } - const loadStart = performance.now() + perfMark('load-config:loaded') const { config, dependencies = [] } = await loader.load() - if (app.env.isDebug) { - logger.info(`theme config call load: ${(performance.now() - loadStart).toFixed(2)}ms`) - } + perfLog('load-config:loaded', app.env.isDebug) + loader.loaded = true loader.dependencies = [...dependencies] updateResolvedConfig(app, config) @@ -80,9 +77,7 @@ export async function initConfigLoader( loader.whenLoaded.forEach(fn => fn(loader!.resolvedConfig)) loader.whenLoaded = [] - if (app.env.isDebug) { - logger.info(`Load config: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('load-config', app.env.isDebug) } export function watchConfigFile(app: App, watchers: any[], onChange: ChangeEvent) { diff --git a/theme/src/node/pages/createPages.ts b/theme/src/node/pages/createPages.ts index 4bc61c87..e6e6c1f4 100644 --- a/theme/src/node/pages/createPages.ts +++ b/theme/src/node/pages/createPages.ts @@ -2,12 +2,13 @@ import type { App, Page } from 'vuepress/core' import type { PlumeThemeLocaleOptions } from '../../shared/index.js' import { getRootLang } from '@vuepress/helper' import { createPage } from 'vuepress/core' -import { withBase } from '../utils/index.js' +import { perfLog, perfMark, withBase } from '../utils/index.js' export async function createPages(app: App, localeOption: PlumeThemeLocaleOptions) { if (localeOption.blog === false) return + perfMark('create:blog-pages') const pageList: Promise[] = [] const locales = localeOption.locales || {} const rootLang = getRootLang(app) @@ -53,4 +54,6 @@ export async function createPages(app: App, localeOption: PlumeThemeLocaleOption } app.pages.push(...await Promise.all(pageList)) + + perfLog('create:blog-pages', app.env.isDebug) } diff --git a/theme/src/node/plugins/getPlugins.ts b/theme/src/node/plugins/getPlugins.ts index 736258d5..b6f58b45 100644 --- a/theme/src/node/plugins/getPlugins.ts +++ b/theme/src/node/plugins/getPlugins.ts @@ -21,7 +21,7 @@ import { watermarkPlugin } from '@vuepress/plugin-watermark' import { type MarkdownEnhancePluginOptions, mdEnhancePlugin } from 'vuepress-plugin-md-enhance' import { markdownPowerPlugin } from 'vuepress-plugin-md-power' import { resolveDocsearchOptions, resolveSearchOptions } from '../config/index.js' -import { deleteAttrs } from '../utils/index.js' +import { omit } from '../utils/index.js' export interface SetupPluginOptions { app: App @@ -119,7 +119,7 @@ export function getPlugins( const options: MarkdownEnhancePluginOptions = { ...pluginOptions.markdownEnhance, } - plugins.push(mdEnhancePlugin(deleteAttrs(options, 'hint', 'alert', 'imgSize', 'imgLazyload', 'imgMark', 'figure', 'obsidianImgSize', 'katex', 'mathjax', 'tabs', 'codetabs', 'align', 'mark', 'sub', 'sup', 'attrs', 'tasklist', 'footnote'))) + plugins.push(mdEnhancePlugin(omit(options, 'hint', 'alert', 'imgSize', 'imgLazyload', 'imgMark', 'figure', 'obsidianImgSize', 'katex', 'mathjax', 'tabs', 'codetabs', 'align', 'mark', 'sub', 'sup', 'attrs', 'tasklist', 'footnote'))) } if (pluginOptions.markdownPower !== false) { diff --git a/theme/src/node/prepare/index.ts b/theme/src/node/prepare/index.ts index 0db2fc11..7efe56d0 100644 --- a/theme/src/node/prepare/index.ts +++ b/theme/src/node/prepare/index.ts @@ -1,7 +1,7 @@ import type { App } from 'vuepress' import { watch } from 'chokidar' import { getThemeConfig } from '../loadConfig/index.js' -import { logger } from '../utils/index.js' +import { perfLog, perfMark } from '../utils/index.js' import { prepareArticleTagColors } from './prepareArticleTagColor.js' import { preparedBlogData } from './prepareBlogData.js' import { prepareEncrypt } from './prepareEncrypt.js' @@ -11,7 +11,7 @@ import { prepareSidebar } from './prepareSidebar.js' export async function prepareData( app: App, ): Promise { - const start = performance.now() + perfMark('prepare:data') const { localeOptions, encrypt } = getThemeConfig() await Promise.all([ prepareArticleTagColors(app, localeOptions), @@ -21,9 +21,7 @@ export async function prepareData( prepareIcons(app, localeOptions), ]) - if (app.env.isDebug) { - logger.info(`Prepare data: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('prepare:data', app.env.isDebug) } export function watchPrepare( diff --git a/theme/src/node/prepare/prepareArticleTagColor.ts b/theme/src/node/prepare/prepareArticleTagColor.ts index 1b4a9a41..8e67226c 100644 --- a/theme/src/node/prepare/prepareArticleTagColor.ts +++ b/theme/src/node/prepare/prepareArticleTagColor.ts @@ -2,7 +2,7 @@ import type { App } from 'vuepress' import type { PlumeThemeLocaleOptions } from '../../shared/index.js' import { toArray } from '@pengzhanbo/utils' import { isPlainObject } from 'vuepress/shared' -import { logger, nanoid, resolveContent, writeTemp } from '../utils/index.js' +import { nanoid, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' export type TagsColorsItem = readonly [ string, // normal color @@ -35,19 +35,14 @@ export const PRESET: TagsColorsItem[] = [ const cache: Record = {} export async function prepareArticleTagColors(app: App, localeOptions: PlumeThemeLocaleOptions): Promise { - const start = performance.now() + perfMark('prepare:tag-colors') const blog = isPlainObject(localeOptions.blog) ? localeOptions.blog : {} - const { js, css } = genCode(app, blog.tagsTheme ?? 'colored') await writeTemp(app, 'internal/articleTagColors.css', css) await writeTemp(app, 'internal/articleTagColors.js', js) - if (app.env.isDebug) { - logger.info( - `Generate article tag colors: ${(performance.now() - start).toFixed(2)}ms`, - ) - } + perfLog('prepare:tag-colors', app.env.isDebug) } export function genCode(app: App, theme: 'colored' | 'brand' | 'gray'): { js: string, css: string } { diff --git a/theme/src/node/prepare/prepareBlogData.ts b/theme/src/node/prepare/prepareBlogData.ts index a340c7ce..0dca349e 100644 --- a/theme/src/node/prepare/prepareBlogData.ts +++ b/theme/src/node/prepare/prepareBlogData.ts @@ -11,7 +11,7 @@ import { removeLeadingSlash } from '@vuepress/helper' import { createFilter } from 'create-filter' import dayjs from 'dayjs' import { resolveNotesOptions } from '../config/index.js' -import { logger, normalizePath, resolveContent, writeTemp } from '../utils/index.js' +import { normalizePath, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' import { isEncryptPage } from './prepareEncrypt.js' const HEADING_RE = /]*>.*?<\/h\1>/gi @@ -32,7 +32,7 @@ export async function preparedBlogData( return } - const start = performance.now() + perfMark('prepare:blog-data') const blog = localeOptions.blog || {} const notesList = resolveNotesOptions(localeOptions) @@ -100,6 +100,5 @@ export async function preparedBlogData( const content = resolveContent(app, { name: 'blogPostData', content: blogData }) await writeTemp(app, 'internal/blogData.js', content) - if (app.env.isDebug) - logger.info(`prepare blog data time spent: ${(performance.now() - start).toFixed(2)}ms`) + perfLog('prepare:blog-data', app.env.isDebug) } diff --git a/theme/src/node/prepare/prepareEncrypt.ts b/theme/src/node/prepare/prepareEncrypt.ts index 9748a694..cb286389 100644 --- a/theme/src/node/prepare/prepareEncrypt.ts +++ b/theme/src/node/prepare/prepareEncrypt.ts @@ -3,7 +3,7 @@ import type { Page } from 'vuepress/core' import type { EncryptOptions, PlumeThemePageData } from '../../shared/index.js' import { isNumber, isString, random, toArray } from '@pengzhanbo/utils' import { genSaltSync, hashSync } from 'bcrypt-ts' -import { createFsCache, type FsCache, hash, logger, resolveContent, writeTemp } from '../utils/index.js' +import { createFsCache, type FsCache, hash, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' export type EncryptConfig = readonly [ boolean, // global @@ -19,7 +19,7 @@ let contentHash = '' let fsCache: FsCache<[string, EncryptConfig]> | null = null export async function prepareEncrypt(app: App, encrypt?: EncryptOptions) { - const start = performance.now() + perfMark('prepare:encrypt') if (!fsCache && app.env.isDev) { fsCache = createFsCache(app, 'encrypt') @@ -40,9 +40,7 @@ export async function prepareEncrypt(app: App, encrypt?: EncryptOptions) { fsCache?.write([currentHash, resolvedEncrypt]) - if (app.env.isDebug) { - logger.info(`Generate encrypt: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('prepare:encrypt', app.env.isDebug) } const salt = () => genSaltSync(random(8, 16)) diff --git a/theme/src/node/prepare/prepareIcons.ts b/theme/src/node/prepare/prepareIcons.ts index 63f48242..2050b7c8 100644 --- a/theme/src/node/prepare/prepareIcons.ts +++ b/theme/src/node/prepare/prepareIcons.ts @@ -5,7 +5,7 @@ import { isArray, uniq } from '@pengzhanbo/utils' import { entries, isLinkAbsolute, isLinkHttp, isPlainObject } from '@vuepress/helper' import { isPackageExists } from 'local-pkg' import { fs } from 'vuepress/utils' -import { createFsCache, type FsCache, interopDefault, logger, nanoid, resolveContent, writeTemp } from '../utils/index.js' +import { createFsCache, type FsCache, interopDefault, logger, nanoid, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' interface IconData { className: string @@ -37,7 +37,7 @@ function isIconify(icon: any): icon is string { } export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOptions) { - const start = performance.now() + perfMark('prepare:icons:total') if (!isInstalled) { await writeTemp(app, JS_FILENAME, resolveContent(app, { name: 'icons', content: '{}' })) return @@ -47,6 +47,7 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti await fsCache.read() } + perfMark('prepare:pages:icons') const iconList: string[] = [] app.pages.forEach(page => iconList.push(...getIconsWithPage(page))) iconList.push(...getIconWithThemeConfig(localeOptions)) @@ -64,11 +65,9 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti collectMap[collect].push(name) }) - if (app.env.isDebug) { - logger.info(`Generate icons with pages and theme config: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('prepare:pages:icons', app.env.isDebug) - const collectStart = performance.now() + perfMark('prepare:icons:imports') if (!locate) { const mod = await interopDefault(import('@iconify/json')) @@ -83,9 +82,7 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti logger.warn(`[iconify] Unknown icons: ${unknownList.join(', ')}`) } - if (app.env.isDebug) { - logger.info(`Generate icons with iconify collect: ${(performance.now() - collectStart).toFixed(2)}ms`) - } + perfLog('prepare:icons:imports', app.env.isDebug) let cssCode = '' const map: Record = {} @@ -104,9 +101,8 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti ]) fsCache?.write(cache) - if (app.env.isDebug) { - logger.info(`Generate icons total time: ${(performance.now() - start).toFixed(2)}ms`) - } + + perfLog('prepare:icons:total', app.env.isDebug) } function getIconsWithPage(page: Page): string[] { diff --git a/theme/src/node/prepare/prepareSidebar.ts b/theme/src/node/prepare/prepareSidebar.ts index 19621e68..c903e0fe 100644 --- a/theme/src/node/prepare/prepareSidebar.ts +++ b/theme/src/node/prepare/prepareSidebar.ts @@ -13,10 +13,10 @@ import { isPlainObject, removeLeadingSlash, } from '@vuepress/helper' -import { logger, normalizeLink, resolveContent, writeTemp } from '../utils/index.js' +import { normalizeLink, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' export async function prepareSidebar(app: App, localeOptions: PlumeThemeLocaleOptions) { - const start = performance.now() + perfMark('prepare:sidebar') const sidebar = getAllSidebar(localeOptions) const { resolved, autoHome } = getSidebarData(app, sidebar) @@ -24,9 +24,7 @@ export async function prepareSidebar(app: App, localeOptions: PlumeThemeLocaleOp sidebar.__home__ = autoHome as any await writeTemp(app, 'internal/sidebar.js', resolveContent(app, { name: 'sidebar', content: sidebar })) - if (app.env.isDebug) { - logger.info(`Generate sidebar: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('prepare:sidebar', app.env.isDebug) } function getSidebarData( diff --git a/theme/src/node/prepare/prepareThemeData.ts b/theme/src/node/prepare/prepareThemeData.ts index 5544f566..24bab9c6 100644 --- a/theme/src/node/prepare/prepareThemeData.ts +++ b/theme/src/node/prepare/prepareThemeData.ts @@ -6,7 +6,7 @@ import { type FSWatcher, watch } from 'chokidar' import { resolveImageSize } from 'vuepress-plugin-md-power' import { hash } from 'vuepress/utils' import { resolveThemeData } from '../config/resolveThemeData.js' -import { logger, resolveContent, writeTemp } from '../utils/index.js' +import { perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js' let bulletinFileWatcher: FSWatcher | null = null const bulletinFiles: Record = {} @@ -18,7 +18,7 @@ export async function prepareThemeData( localeOptions: PlumeThemeLocaleOptions, pluginOptions: PlumeThemePluginOptions, ): Promise { - const start = performance.now() + perfMark('prepare:theme-data') const resolvedThemeData = resolveThemeData(app, localeOptions) await resolveProfileImage(app, resolvedThemeData, pluginOptions) @@ -31,9 +31,7 @@ export async function prepareThemeData( await resolveBulletin(app, resolvedThemeData) await updateThemeData(app, resolvedThemeData) - if (app.env.isDebug) { - logger.info(`Generate theme data: ${(performance.now() - start).toFixed(2)}ms`) - } + perfLog('prepare:theme-data', app.env.isDebug) } async function updateThemeData(app: App, themeData: PlumeThemeData) { diff --git a/theme/src/node/utils/constants.ts b/theme/src/node/utils/constants.ts new file mode 100644 index 00000000..253414ba --- /dev/null +++ b/theme/src/node/utils/constants.ts @@ -0,0 +1 @@ +export const THEME_NAME = 'vuepress-theme-plume' diff --git a/theme/src/node/utils/index.ts b/theme/src/node/utils/index.ts index a017b027..6575b1e9 100644 --- a/theme/src/node/utils/index.ts +++ b/theme/src/node/utils/index.ts @@ -1,13 +1,9 @@ -import { Logger } from '@vuepress/helper' - -export const THEME_NAME = 'vuepress-theme-plume' - -export const logger = new Logger(THEME_NAME) - +export * from './constants.js' export * from './createFsCache.js' -export * from './deleteAttrs.js' export * from './hash.js' export * from './interopDefault.js' +export * from './logger.js' +export * from './omit.js' export * from './package.js' export * from './path.js' export * from './resolveContent.js' diff --git a/theme/src/node/utils/logger.ts b/theme/src/node/utils/logger.ts new file mode 100644 index 00000000..b589a35f --- /dev/null +++ b/theme/src/node/utils/logger.ts @@ -0,0 +1,20 @@ +import { Logger } from '@vuepress/helper' +import { colors } from 'vuepress/utils' +import { THEME_NAME } from './constants.js' + +export const logger = new Logger(THEME_NAME) + +// { mark: startTime } +const perf: Record = {} + +export function perfMark(mark: string): void { + perf[mark] = performance.now() +} + +export function perfLog(mark: string, isDebug = false): void { + const startTime = perf[mark] + if (!startTime || !isDebug) + return + + logger.info(`${colors.magenta('[perf spent time]')} ${colors.green(mark)}: ${colors.cyan(`${(performance.now() - startTime).toFixed(2)}ms`)}`) +} diff --git a/theme/src/node/utils/deleteAttrs.ts b/theme/src/node/utils/omit.ts similarity index 89% rename from theme/src/node/utils/deleteAttrs.ts rename to theme/src/node/utils/omit.ts index 8f131437..878c7c6d 100644 --- a/theme/src/node/utils/deleteAttrs.ts +++ b/theme/src/node/utils/omit.ts @@ -1,4 +1,4 @@ -export function deleteAttrs< +export function omit< T extends Record = Record, >(obj: T, ...attrs: (keyof T)[]): Omit { const res = {} as T