vuepress-theme-plume
Version:
A Blog&Document Theme for VuePress 2.0
254 lines (253 loc) • 9.01 kB
JavaScript
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 };