import { deepMerge, hasOwn, isArray, isEmptyObject, isNumber, isPlainObject, isString, promiseParallel, random, sleep, toArray, uniq } from "@pengzhanbo/utils";
import chokidar, { watch } from "chokidar";
import { createFilter } from "create-filter";
import grayMatter from "gray-matter";
import yaml from "js-yaml";
import { colors, fs, getDirname, hash, importFileDefault, path, templateRenderer } from "vuepress/utils";
import fs$1, { constants, promises } from "node:fs";
import path$1, { resolve } from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
import { build } from "esbuild";
import fs$2 from "node:fs/promises";
import { genSaltSync, hashSync } from "bcrypt-ts";
import { createHash } from "node:crypto";
import { customAlphabet } from "nanoid";
import { Logger, addViteConfig, addViteOptimizeDepsExclude, addViteOptimizeDepsInclude, addViteSsrNoExternal, ensureEndingSlash, ensureLeadingSlash, entries, fromEntries, getFullLocaleConfig, isArray as isArray$1, isBoolean, isFunction, isLinkAbsolute, isLinkHttp, isLinkWithProtocol, isPlainObject as isPlainObject$1, removeLeadingSlash } from "@vuepress/helper";
import fg from "fast-glob";
import { isPlainObject as isPlainObject$2, resolveLocalePath } from "vuepress/shared";
import { isPackageExists } from "local-pkg";
import dayjs from "dayjs";
import { getUserAgent, resolveCommand } from "package-manager-detector";
import { createPage } from "vuepress/core";
import { copyCodePlugin } from "@vuepress/plugin-copy-code";
import { shikiPlugin } from "@vuepress/plugin-shiki";
import { createCodeTabIconGetter, markdownPowerPlugin, resolveImageSize } from "vuepress-plugin-md-power";
import { markdownChartPlugin } from "@vuepress/plugin-markdown-chart";
import { markdownHintPlugin } from "@vuepress/plugin-markdown-hint";
import { markdownImagePlugin } from "@vuepress/plugin-markdown-image";
import { markdownIncludePlugin } from "@vuepress/plugin-markdown-include";
import { markdownMathPlugin } from "@vuepress/plugin-markdown-math";
import { fontsPlugin } from "@vuepress-plume/plugin-fonts";
import { searchPlugin } from "@vuepress-plume/plugin-search";
import { cachePlugin } from "@vuepress/plugin-cache";
import { commentPlugin } from "@vuepress/plugin-comment";
import { docsearchPlugin } from "@vuepress/plugin-docsearch";
import { nprogressPlugin } from "@vuepress/plugin-nprogress";
import { photoSwipePlugin } from "@vuepress/plugin-photo-swipe";
import { readingTimePlugin } from "@vuepress/plugin-reading-time";
import { replaceAssetsPlugin } from "@vuepress/plugin-replace-assets";
import { seoPlugin } from "@vuepress/plugin-seo";
import { sitemapPlugin } from "@vuepress/plugin-sitemap";
import { watermarkPlugin } from "@vuepress/plugin-watermark";
import { gitPlugin } from "@vuepress/plugin-git";
import { getIconContentCSS, getIconData } from "@iconify/utils";
export * from "../shared/index.js"
//#region src/node/utils/constants.ts
const THEME_NAME = "vuepress-theme-plume";
//#endregion
//#region src/node/utils/createFsCache.ts
const CACHE_BASE = "markdown";
function createFsCache(app, name) {
const filepath = app.dir.cache(`${CACHE_BASE}/${name}.json`);
const cache$3 = {
hash: "",
data: null
};
const read = async () => {
if (!cache$3.data) try {
const content = await fs$2.readFile(filepath, "utf-8");
if (content) {
const res = JSON.parse(content);
cache$3.data = res.data ?? null;
cache$3.hash = hash(res.hash || "");
}
} catch {}
return cache$3.data;
};
let timer = null;
const write = async (data) => {
const currentHash = hash(data);
if (cache$3.hash && currentHash === cache$3.hash) return;
cache$3.data = data;
cache$3.hash = currentHash;
timer && clearTimeout(timer);
timer = setTimeout(async () => {
await fs$2.mkdir(path$1.dirname(filepath), { recursive: true });
await fs$2.writeFile(filepath, JSON.stringify(cache$3), "utf-8");
}, 300);
};
return {
get hash() {
return cache$3.hash;
},
get data() {
return cache$3.data;
},
read,
write
};
}
//#endregion
//#region src/node/utils/encrypt.ts
function genEncrypt(pwd) {
return hashSync(String(pwd), genSaltSync(random(8, 16)));
}
//#endregion
//#region src/node/utils/hash.ts
const hash$1 = (content) => createHash("md5").update(content).digest("hex");
const nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 8);
//#endregion
//#region src/node/utils/interopDefault.ts
async function interopDefault(m) {
const resolved = await m;
return resolved.default || resolved;
}
//#endregion
//#region src/node/utils/logger.ts
const logger = new Logger(THEME_NAME);
var Perf = class {
isDebug = false;
collect = {};
init(isDebug = false) {
this.isDebug = isDebug;
}
mark(mark) {
this.collect[mark] = performance.now();
}
log(mark) {
const startTime = this.collect[mark];
if (!this.isDebug || !startTime) return;
logger.info("[perf spent time] ", `${colors.green(mark)}: ${colors.cyan(`${(performance.now() - startTime).toFixed(2)}ms`)}`);
}
};
const perf = new Perf();
//#endregion
//#region src/node/utils/path.ts
const __dirname = getDirname(import.meta.url);
const resolve$1 = (...args) => path.resolve(__dirname, "../", ...args);
const templates = (url) => resolve$1("../templates", url);
const RE_SLASH = /(\\|\/)+/g;
function normalizePath(path$2) {
return path$2.replace(RE_SLASH, "/");
}
function pathJoin(...args) {
return normalizePath(path.join(...args));
}
function normalizeLink(base, link = "") {
return isLinkAbsolute(link) || isLinkWithProtocol(link) ? link : ensureLeadingSlash(normalizePath(`${base}/${link}/`));
}
const RE_START_END_SLASH = /^\/|\/$/g;
function getCurrentDirname(basePath, filepath) {
const dirList = normalizePath(basePath || path.dirname(filepath)).replace(RE_START_END_SLASH, "").split("/");
return dirList.length > 0 ? dirList[dirList.length - 1] : "";
}
function withBase(path$2 = "", base = "/") {
path$2 = ensureEndingSlash(ensureLeadingSlash(path$2));
if (path$2.startsWith(base)) return normalizePath(path$2);
return normalizePath(`${base}${path$2}`);
}
//#endregion
//#region src/node/utils/package.ts
function readJsonFileAsync(filePath) {
try {
const content = fs.readFileSync(filePath, "utf-8");
return JSON.parse(content);
} catch {}
return {};
}
function getPackage() {
return readJsonFileAsync(path.join(process.cwd(), "package.json"));
}
function getThemePackage() {
return readJsonFileAsync(resolve$1("../package.json"));
}
//#endregion
//#region src/node/utils/resolveContent.ts
function resolveContent(app, { name, content, before, after }) {
content = `${before ? `${before}\n` : ""}export const ${name} = ${JSON.stringify(content)}${after ? `\n${after}` : ""}`;
if (app.env.isDev) {
const func = `update${name[0].toUpperCase()}${name.slice(1)}`;
content += `\n
if (import.meta.webpackHot) {
import.meta.webpackHot.accept()
if (__VUE_HMR_RUNTIME__.${func}) {
__VUE_HMR_RUNTIME__.${func}(${name})
}
}
if (import.meta.hot) {
import.meta.hot.accept(({ ${name} }) => {
__VUE_HMR_RUNTIME__.${func}(${name})
})
}
`;
}
return content;
}
//#endregion
//#region src/node/utils/translate.ts
let lang = "en";
function setTranslateLang(current) {
if ([
"zh-CN",
"zh",
"zh-Hans",
"zh-Hant"
].includes(current)) lang = "zh";
else lang = "en";
}
function createTranslate(locales) {
return function t$4(key, data) {
const resolved = locales[lang][key];
if (!resolved) return String(key);
if (data && !isEmptyObject(data)) return resolved.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key$1) => data[key$1] || _);
return resolved;
};
}
//#endregion
//#region src/node/utils/writeTemp.ts
const contentHash$1 = /* @__PURE__ */ new Map();
async function writeTemp(app, filepath, content) {
const currentHash = hash$1(content);
if (!contentHash$1.has(filepath) || contentHash$1.get(filepath) !== currentHash) {
contentHash$1.set(filepath, currentHash);
await app.writeTemp(filepath, content);
}
}
//#endregion
//#region src/node/loadConfig/compiler.ts
async function compiler(configPath) {
if (!configPath) return {
config: {},
dependencies: []
};
const dirnameVarName = "__vite_injected_original_dirname";
const filenameVarName = "__vite_injected_original_filename";
const importMetaUrlVarName = "__vite_injected_original_import_meta_url";
const result = await build({
absWorkingDir: process.cwd(),
entryPoints: [configPath],
outfile: "out.js",
write: false,
target: [`node${process.versions.node}`],
platform: "node",
bundle: true,
format: "esm",
mainFields: ["main"],
sourcemap: "inline",
metafile: true,
define: {
"__dirname": dirnameVarName,
"__filename": filenameVarName,
"import.meta.url": importMetaUrlVarName,
"import.meta.dirname": dirnameVarName,
"import.meta.filename": filenameVarName
},
plugins: [{
name: "externalize-deps",
setup(build$1) {
build$1.onResolve({ filter: /.*/ }, ({ path: id }) => {
if (id[0] !== "." && !path$1.isAbsolute(id)) return { external: true };
return null;
});
}
}, {
name: "inject-file-scope-variables",
setup(build$1) {
build$1.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => {
const contents = await promises.readFile(args.path, "utf-8");
const injectValues = `const ${dirnameVarName} = ${JSON.stringify(path$1.dirname(args.path))};const ${filenameVarName} = ${JSON.stringify(args.path)};const ${importMetaUrlVarName} = ${JSON.stringify(pathToFileURL(args.path).href)};`;
return {
loader: args.path.endsWith("ts") ? "ts" : "js",
contents: injectValues + contents
};
});
}
}]
});
const { text } = result.outputFiles[0];
const tempFilePath = `${configPath}.${hash$1(text)}.mjs`;
let config;
try {
await promises.writeFile(tempFilePath, text);
config = await importFileDefault(tempFilePath);
} finally {
await promises.rm(tempFilePath);
}
return {
config,
dependencies: Object.keys(result.metafile?.inputs ?? {})
};
}
//#endregion
//#region src/node/loadConfig/findConfigPath.ts
const CONFIG_FILE_NAME = "plume.config";
const extensions = [
"ts",
"js",
"mjs",
"cjs",
"mts",
"cts"
];
async function findConfigPath(app, configPath) {
const cwd = process.cwd();
const source = app.dir.source(".vuepress");
const paths = [];
if (configPath) {
const path$2 = resolve(cwd, configPath);
if (existsSync(path$2) && (await promises.stat(path$2)).isFile()) return path$2;
}
extensions.forEach((ext) => paths.push(resolve(cwd, `${source}/${CONFIG_FILE_NAME}.${ext}`), resolve(cwd, `./${CONFIG_FILE_NAME}.${ext}`), resolve(cwd, `./.vuepress/${CONFIG_FILE_NAME}.${ext}`)));
let current;
for (const path$2 of paths) if (existsSync(path$2) && (await promises.stat(path$2)).isFile()) {
current = path$2;
break;
}
if (configPath && current) logger.warn(`Can not find config file: ${colors.gray(configPath)}\nUse config file: ${colors.gray(current)}`);
return current;
}
function existsSync(fp) {
try {
fs$1.accessSync(fp, constants.R_OK);
return true;
} catch {
return false;
}
}
//#endregion
//#region src/node/locales/de.ts
const deLocale = {
selectLanguageName: "Deutsch",
selectLanguageText: "Sprache auswählen",
appearanceText: "Erscheinungsbild",
lightModeSwitchTitle: "Zu hellem Thema wechseln",
darkModeSwitchTitle: "Zu dunklem Thema wechseln",
outlineLabel: "Inhalt dieser Seite",
returnToTopLabel: "Zurück nach oben",
editLinkText: "Diese Seite bearbeiten",
contributorsText: "Mitwirkende",
prevPageLabel: "Vorherige Seite",
nextPageLabel: "Nächste Seite",
lastUpdatedText: "Zuletzt aktualisiert am",
changelogText: "Änderungsprotokoll",
changelogOnText: "am",
changelogButtonText: "Alle Änderungen anzeigen",
copyrightText: "Alle Rechte vorbehalten",
copyrightAuthorText: "Urheberrecht liegt bei:",
copyrightCreationOriginalText: "Originalartikel:",
copyrightCreationTranslateText: "Übersetzt aus:",
copyrightCreationReprintText: "Nachdruck von:",
copyrightLicenseText: "Lizenz:",
notFound: {
code: "404",
title: "Seite nicht gefunden",
quote: "Aber wenn du deine Richtung nicht änderst und weiter suchst, könntest du schließlich dorthin gelangen, wohin du gehen willst.",
linkText: "Zur Startseite"
},
homeText: "Startseite",
blogText: "Blog",
tagText: "Tag",
archiveText: "Archiv",
categoryText: "Kategorie",
archiveTotalText: "{count} Beiträge",
encryptButtonText: "Bestätigen",
encryptPlaceholder: "Bitte Passwort eingeben",
encryptGlobalText: "Diese Website ist nur mit Passwort zugänglich",
encryptPageText: "Diese Seite ist nur mit Passwort zugänglich",
footer: { message: "Unterstützt von VuePress & vuepress-theme-plume" }
};
const dePresetLocale = {
"CC0": "CC0 1.0 Universell",
"CC-BY-4.0": "Namensnennung 4.0 International",
"CC-BY-NC-4.0": "Namensnennung-Nicht kommerziell 4.0 International",
"CC-BY-NC-SA-4.0": "Namensnennung-Nicht kommerziell-Weitergabe unter gleichen Bedingungen 4.0 International",
"CC-BY-NC-ND-4.0": "Namensnennung-Nicht kommerziell-Keine Bearbeitung 4.0 International",
"CC-BY-ND-4.0": "Namensnennung-Keine Bearbeitung 4.0 International",
"CC-BY-SA-4.0": "Namensnennung-Weitergabe unter gleichen Bedingungen 4.0 International"
};
//#endregion
//#region src/node/locales/en.ts
const enLocale = {
selectLanguageName: "English",
selectLanguageText: "Languages",
appearanceText: "Appearance",
lightModeSwitchTitle: "Switch to light theme",
darkModeSwitchTitle: "Switch to dark theme",
editLinkText: "Edit this page",
contributorsText: "Contributors",
lastUpdatedText: "Last Updated",
changelogText: "Changelog",
changelogOnText: "On",
changelogButtonText: "View All Changelog",
copyrightText: "Copyright",
copyrightAuthorText: "Copyright Ownership:",
copyrightCreationOriginalText: "This article link:",
copyrightCreationTranslateText: "This article is translated from:",
copyrightCreationReprintText: "This article is reprint from:",
copyrightLicenseText: "License under:",
encryptButtonText: "Confirm",
encryptPlaceholder: "Enter password",
encryptGlobalText: "Only password can access this site",
encryptPageText: "Only password can access this page",
homeText: "Home",
blogText: "Blog",
tagText: "Tags",
archiveText: "Archives",
categoryText: "Categories",
archiveTotalText: "{count} articles",
footer: { message: "Powered by VuePress & vuepress-theme-plume" }
};
const enPresetLocale = {
"CC0": "CC0 1.0 Universal",
"CC-BY-4.0": "Attribution 4.0 International",
"CC-BY-NC-4.0": "Attribution-NonCommercial 4.0 International",
"CC-BY-NC-SA-4.0": "Attribution-NonCommercial-ShareAlike 4.0 International",
"CC-BY-NC-ND-4.0": "Attribution-NonCommercial-NoDerivatives 4.0 International",
"CC-BY-ND-4.0": "Attribution-NoDerivatives 4.0 International",
"CC-BY-SA-4.0": "Attribution-ShareAlike 4.0 International"
};
//#endregion
//#region src/node/locales/fr.ts
const frLocale = {
selectLanguageName: "Français",
selectLanguageText: "Choisir la langue",
appearanceText: "Apparence",
lightModeSwitchTitle: "Passer au thème clair",
darkModeSwitchTitle: "Passer au thème sombre",
outlineLabel: "Contenu de cette page",
returnToTopLabel: "Retour en haut",
editLinkText: "Modifier cette page",
contributorsText: "Contributeurs",
prevPageLabel: "Page précédente",
nextPageLabel: "Page suivante",
lastUpdatedText: "Dernière mise à jour",
changelogText: "Historique des changements",
changelogOnText: "le",
changelogButtonText: "Voir tout l'historique des changements",
copyrightText: "Tous droits réservés",
copyrightAuthorText: "Copyright appartenant à :",
copyrightCreationOriginalText: "Lien de l'article :",
copyrightCreationTranslateText: "Traduit de :",
copyrightCreationReprintText: "Reproduit de :",
copyrightLicenseText: "Licence :",
notFound: {
code: "404",
title: "Page non trouvée",
quote: "Mais si tu ne changes pas de direction et que tu continues à chercher, tu finiras par arriver à destination.",
linkText: "Retour à l'accueil"
},
homeText: "Accueil",
blogText: "Blog",
tagText: "Étiquette",
archiveText: "Archives",
categoryText: "Catégorie",
archiveTotalText: "{count} articles",
encryptButtonText: "Confirmer",
encryptPlaceholder: "Veuillez entrer le mot de passe",
encryptGlobalText: "Ce site n'est accessible qu'avec un mot de passe",
encryptPageText: "Cette page n'est accessible qu'avec un mot de passe",
footer: { message: "Propulsé par VuePress & vuepress-theme-plume" }
};
const frPresetLocale = {
"CC0": "CC0 1.0 Universel",
"CC-BY-4.0": "Attribution 4.0 International",
"CC-BY-NC-4.0": "Attribution-Pas d'Utilisation Commerciale 4.0 International",
"CC-BY-NC-SA-4.0": "Attribution-Pas d'Utilisation Commerciale-Partage dans les Mêmes Conditions 4.0 International",
"CC-BY-NC-ND-4.0": "Attribution-Pas d'Utilisation Commerciale-Pas de Modification 4.0 International",
"CC-BY-ND-4.0": "Attribution-Pas de Modification 4.0 International",
"CC-BY-SA-4.0": "Attribution-Partage dans les Mêmes Conditions 4.0 International"
};
//#endregion
//#region src/node/locales/ja.ts
const jaLocale = {
selectLanguageName: "日本語",
selectLanguageText: "言語を選択",
appearanceText: "外観",
lightModeSwitchTitle: "ライトモードに切り替え",
darkModeSwitchTitle: "ダークモードに切り替え",
outlineLabel: "このページの内容",
returnToTopLabel: "トップに戻る",
editLinkText: "このページを編集",
contributorsText: "貢献者",
prevPageLabel: "前のページ",
nextPageLabel: "次のページ",
lastUpdatedText: "最終更新日",
changelogText: "変更履歴",
changelogOnText: "に",
changelogButtonText: "すべての変更履歴を見る",
copyrightText: "著作権",
copyrightAuthorText: "著作権者:",
copyrightCreationOriginalText: "本文リンク:",
copyrightCreationTranslateText: "本文の翻訳元:",
copyrightCreationReprintText: "本文の転載元:",
copyrightLicenseText: "ライセンス:",
notFound: {
code: "404",
title: "ページが見つかりません",
quote: "しかし、方向を変えずに探し続ければ、最終的には行きたい場所にたどり着くかもしれません。",
linkText: "ホームに戻る"
},
homeText: "ホーム",
blogText: "ブログ",
tagText: "タグ",
archiveText: "アーカイブ",
categoryText: "カテゴリー",
archiveTotalText: "{count} 件",
encryptButtonText: "確認",
encryptPlaceholder: "パスワードを入力してください",
encryptGlobalText: "このサイトはパスワードでのみアクセス可能です",
encryptPageText: "このページはパスワードでのみアクセス可能です",
footer: { message: "VuePress & vuepress-theme-plume によって提供されています" }
};
const jaPresetLocale = {
"CC0": "CC0 1.0 パブリックドメイン",
"CC-BY-4.0": "表示 4.0 国際",
"CC-BY-NC-4.0": "表示-非営利 4.0 国際",
"CC-BY-NC-SA-4.0": "表示-非営利-継承 4.0 国際",
"CC-BY-NC-ND-4.0": "表示-非営利-改変禁止 4.0 国際",
"CC-BY-ND-4.0": "表示-改変禁止 4.0 国際",
"CC-BY-SA-4.0": "表示-継承 4.0 国際"
};
//#endregion
//#region src/node/locales/ko.ts
const koLocale = {
selectLanguageName: "한국어",
selectLanguageText: "",
appearanceText: "모양",
lightModeSwitchTitle: "밝은 테마로 전환",
darkModeSwitchTitle: "어두운 테마로 전환",
sidebarMenuLabel: "메뉴",
returnToTopLabel: "위로 이동",
outlineLabel: "목차",
editLinkText: "편집하기",
contributorsText: "기여자",
lastUpdatedText: "마지막 업데이트",
changelogText: "변경 내역",
changelogOnText: "On",
changelogButtonText: "변경 내역 모두 보기",
prevPageLabel: "이전 페이지",
nextPageLabel: "다음 페이지",
copyrightText: "Copyright",
copyrightAuthorText: "저작권 소유자:",
copyrightCreationOriginalText: "This article link:",
copyrightCreationTranslateText: "This article is translated from:",
copyrightCreationReprintText: "This article is reprint from:",
copyrightLicenseText: "License under:",
encryptButtonText: "확인",
encryptPlaceholder: "비밀번호를 입력하세요",
encryptGlobalText: "이 사이트를 이용하려면 비밀번호가 필요합니다",
encryptPageText: "이 페이지를 이용하려면 비밀번호가 필요합니다",
homeText: "홈",
blogText: "블로그",
tagText: "태그",
archiveText: "아카이브",
categoryText: "카테고리",
archiveTotalText: "{count}개의 글",
notFound: {
code: "404",
title: "페이지를 찾을 수 없습니다",
quote: "방향을 잃지 않고 꾸준히 나아가다 보면 결국엔 목적지에 닿을 수 있습니다.",
linkText: "홈으로"
},
footer: { message: "Powered by VuePress & vuepress-theme-plume" }
};
const koPresetLocale = {
"CC0": "CC0 1.0 Universal",
"CC-BY-4.0": "Attribution 4.0 International",
"CC-BY-NC-4.0": "Attribution-NonCommercial 4.0 International",
"CC-BY-NC-SA-4.0": "Attribution-NonCommercial-ShareAlike 4.0 International",
"CC-BY-NC-ND-4.0": "Attribution-NonCommercial-NoDerivatives 4.0 International",
"CC-BY-ND-4.0": "Attribution-NoDerivatives 4.0 International",
"CC-BY-SA-4.0": "Attribution-ShareAlike 4.0 International"
};
//#endregion
//#region src/node/locales/ru.ts
const ruLocale = {
selectLanguageName: "Русский",
selectLanguageText: "Выберите язык",
appearanceText: "Внешний вид",
lightModeSwitchTitle: "Переключить на светлую тему",
darkModeSwitchTitle: "Переключить на темную тему",
outlineLabel: "Содержание страницы",
returnToTopLabel: "Вернуться наверх",
editLinkText: "Редактировать страницу",
contributorsText: "Авторы",
prevPageLabel: "Предыдущая страница",
nextPageLabel: "Следующая страница",
lastUpdatedText: "Последнее обновление",
changelogText: "История изменений",
changelogOnText: "от",
changelogButtonText: "Посмотреть все изменения",
copyrightText: "Все права защищены",
copyrightAuthorText: "Авторские права принадлежат:",
copyrightCreationOriginalText: "Ссылка на статью:",
copyrightCreationTranslateText: "Перевод статьи:",
copyrightCreationReprintText: "Перепечатано из:",
copyrightLicenseText: "Лицензия:",
notFound: {
code: "404",
title: "Страница не найдена",
quote: "Но если вы не меняете курс и продолжаете искать, в конечном итоге вы можете добраться до места назначения.",
linkText: "Вернуться на главную"
},
homeText: "Главная",
blogText: "Блог",
tagText: "Теги",
archiveText: "Архив",
categoryText: "Категории",
archiveTotalText: "{count} статей",
encryptButtonText: "Подтвердить",
encryptPlaceholder: "Введите пароль",
encryptGlobalText: "Доступ к сайту только по паролю",
encryptPageText: "Доступ к странице только по паролю",
footer: { message: "Работает на VuePress & vuepress-theme-plume" }
};
const ruPresetLocale = {
"CC0": "CC0 1.0 Универсальная",
"CC-BY-4.0": "Атрибуция 4.0 Международный",
"CC-BY-NC-4.0": "Атрибуция-Некоммерческое 4.0 Международный",
"CC-BY-NC-SA-4.0": "Атрибуция-Некоммерческое-С сохранением условий 4.0 Международный",
"CC-BY-NC-ND-4.0": "Атрибуция-Некоммерческое-Без производных 4.0 Международный",
"CC-BY-ND-4.0": "Атрибуция-Без производных 4.0 Международный",
"CC-BY-SA-4.0": "Атрибуция-С сохранением условий 4.0 Международный"
};
//#endregion
//#region src/node/locales/zh-tw.ts
const zhTwLocale = {
selectLanguageName: "繁體中文",
selectLanguageText: "選擇語言",
appearanceText: "外觀",
lightModeSwitchTitle: "切換為淺色主題",
darkModeSwitchTitle: "切換為深色主題",
outlineLabel: "此頁內容",
returnToTopLabel: "返回頂部",
editLinkText: "編輯此頁",
contributorsText: "貢獻者",
prevPageLabel: "上一頁",
nextPageLabel: "下一頁",
lastUpdatedText: "最後更新於",
changelogText: "變更歷史",
changelogOnText: "於",
changelogButtonText: "查看全部變更歷史",
copyrightText: "版權所有",
copyrightAuthorText: "版權歸屬:",
copyrightCreationOriginalText: "本文連結:",
copyrightCreationTranslateText: "本文翻譯自:",
copyrightCreationReprintText: "本文轉載自:",
copyrightLicenseText: "許可證:",
notFound: {
code: "404",
title: "頁面未找到",
quote: "但是,如果你不改變方向,並且一直尋找,最終可能會到達你要去的地方。",
linkText: "返回首頁"
},
homeText: "首頁",
blogText: "博客",
tagText: "標籤",
archiveText: "歸檔",
categoryText: "分類",
archiveTotalText: "{count} 篇",
encryptButtonText: "確認",
encryptPlaceholder: "請輸入密碼",
encryptGlobalText: "本站只允許密碼訪問",
encryptPageText: "本頁面只允許密碼訪問",
footer: { message: "Powered by VuePress & vuepress-theme-plume" }
};
const zhTwPresetLocale = {
"CC0": "CC0 1.0 通用",
"CC-BY-4.0": "署名 4.0 國際",
"CC-BY-NC-4.0": "署名-非商業性 4.0 國際",
"CC-BY-NC-SA-4.0": "署名-非商業性-相同方式共享 4.0 國際",
"CC-BY-NC-ND-4.0": "署名-非商業性-禁止演繹 4.0 國際",
"CC-BY-ND-4.0": "署名-禁止演繹 4.0 國際",
"CC-BY-SA-4.0": "署名-相同方式共享 4.0 國際"
};
//#endregion
//#region src/node/locales/zh.ts
const zhLocale = {
selectLanguageName: "简体中文",
selectLanguageText: "选择语言",
appearanceText: "外观",
lightModeSwitchTitle: "切换为浅色主题",
darkModeSwitchTitle: "切换为深色主题",
outlineLabel: "此页内容",
returnToTopLabel: "返回顶部",
editLinkText: "编辑此页",
contributorsText: "贡献者",
prevPageLabel: "上一页",
nextPageLabel: "下一页",
lastUpdatedText: "最后更新于",
changelogText: "变更历史",
changelogOnText: "于",
changelogButtonText: "查看全部变更历史",
copyrightText: "版权所有",
copyrightAuthorText: "版权归属:",
copyrightCreationOriginalText: "本文链接:",
copyrightCreationTranslateText: "本文翻译自:",
copyrightCreationReprintText: "本文转载自:",
copyrightLicenseText: "许可证:",
notFound: {
code: "404",
title: "页面未找到",
quote: "但是,如果你不改变方向,并且一直寻找,最终可能会到达你要去的地方。",
linkText: "返回首页"
},
homeText: "首页",
blogText: "博客",
tagText: "标签",
archiveText: "归档",
categoryText: "分类",
archiveTotalText: "{count} 篇",
encryptButtonText: "确认",
encryptPlaceholder: "请输入密码",
encryptGlobalText: "本站只允许密码访问",
encryptPageText: "本页面只允许密码访问",
footer: { message: "Powered by VuePress & vuepress-theme-plume" }
};
const zhPresetLocale = {
"CC0": "CC0 1.0 通用",
"CC-BY-4.0": "署名 4.0 国际",
"CC-BY-NC-4.0": "署名-非商业性 4.0 国际",
"CC-BY-NC-SA-4.0": "署名-非商业性-相同方式共享 4.0 国际",
"CC-BY-NC-ND-4.0": "署名-非商业性-禁止演绎 4.0 国际",
"CC-BY-ND-4.0": "署名-禁止演绎 4.0 国际",
"CC-BY-SA-4.0": "署名-相同方式共享 4.0 国际"
};
//#endregion
//#region src/node/locales/index.ts
const LOCALE_OPTIONS = [
[["ko", "ko-KR"], koLocale],
[["en", "en-US"], enLocale],
[[
"zh",
"zh-CN",
"zh-Hans",
"zh-Hant"
], zhLocale],
[["zh-TW"], zhTwLocale],
[["de", "de-DE"], deLocale],
[["fr", "fr-FR"], frLocale],
[["ru", "ru-RU"], ruLocale],
[["ja", "ja-JP"], jaLocale]
];
const PRESET_LOCALES = [
[["ko", "ko-KR"], koPresetLocale],
[["en", "en-US"], enPresetLocale],
[[
"zh",
"zh-CN",
"zh-Hans",
"zh-Hant"
], zhPresetLocale],
[["zh-TW"], zhTwPresetLocale],
[["de", "de-DE"], dePresetLocale],
[["fr", "fr-FR"], frPresetLocale],
[["ru", "ru-RU"], ruPresetLocale],
[["ja", "ja-JP"], jaPresetLocale]
];
//#endregion
//#region src/node/config/initThemeOptions.ts
const FALLBACK_OPTIONS = {
appearance: true,
blog: {
pagination: 15,
postList: true,
tags: true,
archives: true,
categories: true,
link: "/blog/",
tagsLink: "/blog/tags/",
archivesLink: "/blog/archives/",
categoriesLink: "/blog/categories/"
},
article: "/article/",
notes: {
link: "/",
dir: "/notes/",
notes: []
},
navbarSocialInclude: [
"github",
"twitter",
"discord",
"facebook"
],
aside: true,
outline: [2, 3],
externalLinkIcon: true,
editLink: true,
contributors: true,
changelog: false,
prevPage: true,
nextPage: true,
footer: { message: "Power by VuePress & vuepress-theme-plume" }
};
/**
* 初始化主题配置,
* 1. 合并默认配置
* 2. 合并多语言配置
*/
function initThemeOptions(app, { locales,...options }) {
return {
...mergeOptions(FALLBACK_OPTIONS, options),
locales: getFullLocaleConfig({
app,
name: THEME_NAME,
default: LOCALE_OPTIONS,
config: fromEntries(entries({
"/": {},
...locales
}).map(([locale, opt]) => [locale, mergeOptions(options, opt)]))
})
};
}
function mergeOptions(target, source) {
const res = {};
const keys = uniq([...Object.keys(target), ...Object.keys(source)]);
for (const key of keys) if (hasOwn(source, key)) {
const value = source[key];
const targetValue = target[key];
if (isPlainObject$1(targetValue) && isPlainObject$1(value)) res[key] = Object.assign({}, targetValue, value);
else res[key] = value;
} else res[key] = target[key];
return res;
}
//#endregion
//#region src/node/loadConfig/loader.ts
let loader = null;
async function initConfigLoader(app, { configFile, onChange, defaultConfig }) {
perf.mark("load-config");
loader = {
configFile,
dependencies: [],
load: () => compiler(loader.configFile),
loaded: false,
changeEvents: [],
whenLoaded: [],
defaultConfig,
config: initThemeOptions(app, defaultConfig)
};
perf.mark("load-config:find");
loader.configFile = await findConfigPath(app, configFile);
perf.log("load-config:find");
if (onChange) loader.changeEvents.push(onChange);
perf.mark("load-config:loaded");
const { config, dependencies = [] } = await loader.load();
perf.log("load-config:loaded");
loader.loaded = true;
loader.dependencies = [...dependencies];
updateResolvedConfig(app, config);
loader.whenLoaded.forEach((fn) => fn(loader.config));
loader.whenLoaded = [];
perf.log("load-config");
}
function watchConfigFile(app, watchers, onChange) {
if (!loader || !loader.configFile) return;
const watcher = watch(loader.configFile, {
ignoreInitial: true,
cwd: process.cwd()
});
addDependencies(watcher);
onConfigChange(onChange);
watcher.on("change", async () => {
if (loader) {
loader.loaded = false;
const { config, dependencies = [] } = await loader.load();
loader.loaded = true;
addDependencies(watcher, dependencies);
updateResolvedConfig(app, config);
runChangeEvents();
}
});
watcher.on("unlink", async () => {
updateResolvedConfig(app);
runChangeEvents();
});
watchers.push(watcher);
}
async function onConfigChange(onChange) {
if (loader && !loader.changeEvents.includes(onChange)) {
loader.changeEvents.push(onChange);
if (loader.loaded) await onChange(loader.config);
}
}
function waitForConfigLoaded() {
return new Promise((resolve$2) => {
if (loader?.loaded) resolve$2(loader.config);
else loader?.whenLoaded.push(resolve$2);
});
}
function getThemeConfig() {
return loader.config;
}
function updateResolvedConfig(app, userConfig = {}) {
if (loader) {
const config = deepMerge({}, loader.defaultConfig, userConfig);
loader.config = initThemeOptions(app, config);
}
}
async function runChangeEvents() {
if (loader) await Promise.all(loader.changeEvents.map((fn) => fn(loader.config)));
}
function addDependencies(watcher, dependencies) {
if (!loader) return;
if (dependencies?.length) {
const deps = dependencies.filter((dep) => !loader.dependencies.includes(dep) && dep[0] === ".");
loader.dependencies.push(...deps);
watcher.add(deps);
} else watcher.add(loader.dependencies);
}
//#endregion
//#region src/node/autoFrontmatter/readFile.ts
async function readMarkdownList(app, { globFilter, checkCache }) {
const source = app.dir.source();
const files = await fg(["**/*.md"], {
cwd: source,
ignore: ["node_modules", ".vuepress"]
});
return await Promise.all(files.filter((id) => {
if (!globFilter(id)) return false;
return checkCache(path.join(source, id));
}).map((file) => readMarkdown(source, file)));
}
async function readMarkdown(sourceDir, relativePath) {
const filepath = path.join(sourceDir, relativePath);
const stats = await fs.promises.stat(filepath);
return {
filepath,
relativePath: normalizePath(relativePath),
content: await fs.promises.readFile(filepath, "utf-8"),
createTime: getFileCreateTime(stats),
stats
};
}
function getFileCreateTime(stats) {
return stats.birthtime.getFullYear() !== 1970 ? stats.birthtime : stats.atime;
}
//#endregion
//#region src/node/config/extendsBundlerOptions.ts
function extendsBundlerOptions(bundlerOptions, app) {
addViteConfig(bundlerOptions, app, { build: { chunkSizeWarningLimit: 2048 } });
addViteOptimizeDepsInclude(bundlerOptions, app, [
"@vueuse/core",
"bcrypt-ts/browser",
"@vuepress/helper/client",
"@iconify/vue",
"@iconify/vue/offline",
"@vuepress/plugin-git/client"
]);
addViteOptimizeDepsExclude(bundlerOptions, app, "@theme");
addViteSsrNoExternal(bundlerOptions, app, [
"@vuepress/helper",
"@vuepress/plugin-reading-time",
"@vuepress/plugin-watermark"
]);
if (isPackageExists("swiper")) {
addViteOptimizeDepsInclude(bundlerOptions, app, ["swiper/modules", "swiper/vue"]);
addViteSsrNoExternal(bundlerOptions, app, ["swiper"]);
}
}
//#endregion
//#region src/node/config/resolveNotesOptions.ts
function resolveNotesLinkList(options) {
const locales = options.locales || {};
const notesLinks = [];
for (const [locale, opt] of entries(locales)) {
const config = locale === "/" ? opt.notes || options.notes : opt.notes;
if (config && config.notes?.length) {
const prefix = config.link || "";
notesLinks.push(...config.notes.map((note) => withBase(`${prefix}/${note.link || ""}`, locale)));
}
}
return uniq(notesLinks);
}
function resolveNotesOptions(options) {
const locales = options.locales || {};
const notesOptionsList = [];
for (const [locale, opt] of entries(locales)) {
const current = locale === "/" ? opt.notes || options.notes : opt.notes;
if (current) {
current.dir = withBase(current.dir, locale);
notesOptionsList.push(current);
}
}
return notesOptionsList;
}
function resolveNotesDirs(options) {
const notesList = resolveNotesOptions(options);
return uniq(notesList.flatMap(({ notes, dir }) => notes.map((note) => removeLeadingSlash(normalizePath(`${dir}/${note.dir || ""}/`)))));
}
//#endregion
//#region src/node/config/resolveThemeData.ts
const EXCLUDE_LIST = [
"hostname",
"locales",
"sidebar",
"navbar",
"notes",
"sidebar",
"article",
"changelog",
"contributors",
"bulletin",
"cache",
"autoFrontmatter",
"comment",
"codeHighlighter",
"markdown",
"configFile",
"encrypt",
"plugins",
"search",
"watermark",
"readingTime",
"copyCode"
];
const EXCLUDE_LOCALE_LIST = [
...EXCLUDE_LIST,
"blog",
"appearance"
];
function resolveThemeData(app, options) {
const themeData = { locales: {} };
entries(options).forEach(([key, value]) => {
if (!EXCLUDE_LIST.includes(key)) themeData[key] = value;
});
themeData.contributors = isPlainObject$1(options.contributors) ? { mode: options.contributors.mode || "inline" } : isBoolean(options.contributors) ? options.contributors : true;
themeData.changelog = !!options.changelog;
if (isPlainObject$1(options.bulletin)) {
const { enablePage: _,...opt } = options.bulletin;
themeData.bulletin = opt;
} else if (options.bulletin) themeData.bulletin = options.bulletin;
if (isPlainObject$1(options.blog)) {
const { categoriesTransform, include, exclude,...blog } = options.blog;
themeData.blog = blog;
} else themeData.blog = options.blog;
entries(options.locales || {}).forEach(([locale, opt]) => {
themeData.locales[locale] = {};
entries(opt).forEach(([key, value]) => {
if (!EXCLUDE_LOCALE_LIST.includes(key)) themeData.locales[locale][key] = value;
});
if (isPlainObject$1(opt.bulletin)) {
const { enablePage: _,...rest } = opt.bulletin;
themeData.locales[locale].bulletin = rest;
} else if (opt.bulletin) themeData.locales[locale].bulletin = opt.bulletin;
});
entries(options.locales || {}).forEach(([locale, opt]) => {
if (opt.navbar !== false && (!opt.navbar || opt.navbar.length === 0)) {
const navbar = [{
text: opt.homeText || options.homeText || "Home",
link: locale
}];
if (options.blog !== false) {
const blog = options.blog || {};
const blogLink = blog.link || "/blog/";
navbar.push({
text: opt.blogText || options.blogText || "Blog",
link: withBase(blogLink, locale)
});
if (blog.tags !== false) navbar.push({
text: opt.tagText || options.tagText || "Tags",
link: withBase(blog.tagsLink || `${blogLink}/tags/`, locale)
});
if (blog.archives !== false) navbar.push({
text: opt.archiveText || options.archiveText || "Archives",
link: withBase(blog.archivesLink || `${blogLink}/archives/`, locale)
});
}
themeData.locales[locale].navbar = navbar;
} else themeData.locales[locale].navbar = opt.navbar;
});
return themeData;
}
//#endregion
//#region src/node/config/setupAlias.ts
function setupAlias() {
return { ...Object.fromEntries(fs.readdirSync(resolve$1("client/components"), {
encoding: "utf-8",
recursive: true
}).filter((file) => file.endsWith(".vue")).map((file) => [path.join("@theme", file), resolve$1("client/components", file)])) };
}
//#endregion
//#region src/node/config/setupProvideData.ts
function setupProvideData(app, plugins) {
const watermark = getThemeConfig().watermark ?? plugins.watermark;
return {
__PLUME_WM_FP__: isPlainObject$1(watermark) ? watermark.fullPage !== false : true,
__PLUME_PRESET_LOCALE__: getFullLocaleConfig({
app,
name: "vuepress-theme-plume/preset-locales",
default: PRESET_LOCALES
})
};
}
//#endregion
//#region src/node/config/templateBuildRenderer.ts
function templateBuildRenderer(template, context) {
const options = getThemeConfig();
const pkg = getThemePackage();
template = template.replace("{{ themeVersion }}", pkg.version || "").replace(/^\s+|\s+$/gm, "").replace(/\n/g, "");
if (options.appearance ?? true) {
const appearance = typeof options.appearance === "string" ? options.appearance : "auto";
const script = appearance === "force-dark" ? `document.documentElement.dataset.theme = 'dark'` : `;(function () {
const um= localStorage.getItem('vuepress-theme-appearance') || '${appearance}';
const sm = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const isDark = um === 'dark' || (um !== 'light' && sm);
document.documentElement.dataset.theme = isDark ? 'dark' : 'light';
})();`.replace(/^\s+|\s+$/gm, "").replace(/\n/g, "");
template = template.replace("", `