UNPKG

vuepress-theme-plume

Version:

A Blog&Document Theme for VuePress 2.0

254 lines (253 loc) 9.01 kB
import { isLinkHttp, removeEndingSlash, removeLeadingSlash } from "vuepress/shared"; import { ensureEndingSlash, ensureLeadingSlash, isLinkAbsolute, isLinkWithProtocol } from "@vuepress/helper/client"; import { resolveRoute } from "vuepress/client"; //#region src/client/utils/resolveRepoType.ts /** * Resolve the repository type from a repository URL * Detects the platform based on URL patterns * * 从仓库 URL 解析仓库类型 * 基于 URL 模式检测平台 * * @param repo - Repository URL or path / 仓库 URL 或路径 * @returns The detected repository type or null if unknown / 检测到的仓库类型,如果未知则返回 null */ function resolveRepoType(repo) { if (!isLinkHttp(repo) || /github\.com/.test(repo)) return "GitHub"; if (/bitbucket\.org/.test(repo)) return "Bitbucket"; if (/gitlab\.com/.test(repo)) return "GitLab"; if (/gitee\.com/.test(repo)) return "Gitee"; return null; } //#endregion //#region src/client/utils/resolveEditLink.ts /** * Edit link patterns for different repository platforms * Maps repository types to their respective edit URL patterns * * 不同仓库平台的编辑链接模式 * 将仓库类型映射到各自的编辑 URL 模式 */ const editLinkPatterns = { GitHub: ":repo/edit/:branch/:path", GitLab: ":repo/-/edit/:branch/:path", Gitee: ":repo/edit/:branch/:path", Bitbucket: ":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default" }; /** * Resolve the edit link pattern based on repository configuration * * 根据仓库配置解析编辑链接模式 * * @param params - Parameters object / 参数对象 * @param params.docsRepo - Repository URL / 仓库 URL * @param params.editLinkPattern - Custom edit link pattern / 自定义编辑链接模式 * @returns The resolved edit link pattern or null / 解析后的编辑链接模式,如果没有则返回 null */ function resolveEditLinkPatterns({ docsRepo, editLinkPattern }) { if (editLinkPattern) return editLinkPattern; const repoType = resolveRepoType(docsRepo); if (repoType !== null) return editLinkPatterns[repoType]; return null; } /** * Resolve the complete edit link URL for a file * Generates an edit link based on repository configuration and file path * * 解析文件的完整编辑链接 URL * 根据仓库配置和文件路径生成编辑链接 * * @param params - Parameters object / 参数对象 * @param params.docsRepo - Repository URL / 仓库 URL * @param params.docsBranch - Branch name / 分支名称 * @param params.docsDir - Documentation directory / 文档目录 * @param params.filePathRelative - Relative file path / 相对文件路径 * @param params.editLinkPattern - Custom edit link pattern / 自定义编辑链接模式 * @returns The complete edit link URL or null / 完整的编辑链接 URL,如果无法生成则返回 null */ function resolveEditLink({ docsRepo, docsBranch, docsDir, filePathRelative, editLinkPattern }) { if (!filePathRelative) return null; const pattern = resolveEditLinkPatterns({ docsRepo, editLinkPattern }); if (!pattern) return null; return pattern.replace(/:repo/, isLinkHttp(docsRepo) ? docsRepo : `https://github.com/${docsRepo}`).replace(/:branch/, docsBranch).replace(/:path/, removeLeadingSlash(`${removeEndingSlash(docsDir)}/${filePathRelative}`)); } //#endregion //#region src/client/utils/resolveNavLink.ts /** * Resolve NavLink props from string * Converts a link string to a resolved navigation item with metadata * * 从字符串解析 NavLink 属性 * 将链接字符串转换为带有元数据的解析导航项 * * @param link - The link string to resolve / 要解析的链接字符串 * @returns Resolved navigation item with link, text, icon and badge / 解析后的导航项,包含链接、文本、图标和徽章 * @example * - Input: '/README.md' * - Output: { text: 'Home', link: '/' } */ function resolveNavLink(link) { const { notFound, meta, path } = resolveRoute(link); return notFound ? { text: path, link: path } : { text: meta.title || normalizeTitleWithPath(path), link: path, icon: meta.icon, badge: meta.badge }; } /** * Normalize a path to extract a readable title * Removes index.html, .html extension and trailing slash * * 规范化路径以提取可读的标题 * 移除 index.html、.html 扩展名和尾部斜杠 * * @param path - The path to normalize / 要规范化的路径 * @returns The extracted title from path / 从路径提取的标题 */ function normalizeTitleWithPath(path) { path = path.replace(/index\.html?$/i, "").replace(/\.html?$/i, "").replace(/\/$/, ""); return decodeURIComponent(path.slice(path.lastIndexOf("/") + 1)); } /** * Normalize a link by combining base and link * Handles absolute links and protocol links correctly * * 通过组合 base 和 link 来规范化链接 * 正确处理绝对链接和协议链接 * * @param base - Base URL / 基础 URL * @param link - Link to normalize / 要规范化的链接 * @returns Normalized link / 规范化后的链接 */ function normalizeLink(base = "", link = "") { return isLinkAbsolute(link) || isLinkWithProtocol(link) ? link : ensureLeadingSlash(`${base}/${link}`.replace(/\/+/g, "/")); } /** * Normalize a prefix by ensuring it ends with a slash * * 规范化前缀,确保它以斜杠结尾 * * @param base - Base URL / 基础 URL * @param link - Link to normalize / 要规范化的链接 * @returns Normalized prefix with trailing slash / 带有尾部斜杠的规范化前缀 */ function normalizePrefix(base, link = "") { return ensureEndingSlash(normalizeLink(base, link)); } //#endregion //#region src/client/utils/shared.ts /** * Regular expression to match external URLs * * 匹配外部 URL 的正则表达式 */ const EXTERNAL_URL_RE = /^[a-z]+:/i; /** * Regular expression to match pathname protocol * * 匹配 pathname 协议的正则表达式 */ const PATHNAME_PROTOCOL_RE = /^pathname:\/\//; /** * Regular expression to match hash * * 匹配哈希值的正则表达式 */ const HASH_RE = /#.*/; /** * Regular expression to match file extension * * 匹配文件扩展名的正则表达式 */ const EXT_RE = /(index|README)?\.(md|html)$/; /** * Whether running in browser * * 是否在浏览器中运行 */ const inBrowser = typeof document !== "undefined"; /** * Convert value to array * * 将值转换为数组 * * @param value - Value to convert, can be single value or array / 要转换的值,可以是单个值或数组 * @returns Array containing the value(s) / 包含值的数组 */ function toArray(value) { return Array.isArray(value) ? value : [value]; } /** * Check if the current path matches the given match path * Supports both exact matching and regex matching * * 检查当前路径是否匹配给定的匹配路径 * 支持精确匹配和正则匹配 * * @param currentPath - Current path to check / 要检查的当前路径 * @param matchPath - Path pattern to match against / 要匹配的路径模式 * @param asRegex - Whether to treat matchPath as regex / 是否将 matchPath 视为正则表达式 * @returns True if paths match / 如果路径匹配则返回 true */ function isActive(currentPath, matchPath, asRegex = false) { if (matchPath === void 0) return false; currentPath = normalize(`/${currentPath.replace(/^\//, "")}`); if (asRegex) return new RegExp(matchPath).test(currentPath); if (normalize(matchPath) !== currentPath) return false; const hashMatch = matchPath.match(HASH_RE); if (hashMatch) return (inBrowser ? location.hash : "") === hashMatch[0]; return true; } /** * Normalize a path by removing hash and file extension * * 通过移除哈希值和文件扩展名来规范化路径 * * @param path - Path to normalize / 要规范化的路径 * @returns Normalized path / 规范化后的路径 */ function normalize(path) { return decodeURI(path).replace(HASH_RE, "").replace(EXT_RE, ""); } /** * Convert a numeric value to CSS unit string * Adds 'px' suffix if the value is a plain number * * 将数值转换为 CSS 单位字符串 * 如果值是纯数字则添加 'px' 后缀 * * @param value - Value to convert, can be number or string with unit / 要转换的值,可以是数字或带单位的字符串 * @returns CSS unit string / CSS 单位字符串 */ function numToUnit(value) { if (typeof value === "undefined") return ""; if (String(Number(value)) === String(value)) return `${value}px`; return value; } const gradient = [ "linear-gradient", "radial-gradient", "repeating-linear-gradient", "repeating-radial-gradient", "conic-gradient" ]; /** * Check if a value is a CSS gradient * * 检查值是否为 CSS 渐变 * * @param value - Value to check / 要检查的值 * @returns True if value is a gradient / 如果值是渐变则返回 true */ function isGradient(value) { return gradient.some((v) => value.startsWith(v)); } //#endregion export { EXTERNAL_URL_RE, EXT_RE, HASH_RE, PATHNAME_PROTOCOL_RE, editLinkPatterns, inBrowser, isActive, isGradient, normalize, normalizeLink, normalizePrefix, numToUnit, resolveEditLink, resolveNavLink, resolveRepoType, toArray };