UNPKG

vitepress-plugin-catalogue

Version:
241 lines (230 loc) 10.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const node_fs = require('node:fs'); const node_path = require('node:path'); const matter = require('gray-matter'); const vite = require('vite'); const picocolors = require('picocolors'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const matter__default = /*#__PURE__*/_interopDefaultCompat(matter); const picocolors__default = /*#__PURE__*/_interopDefaultCompat(picocolors); const getTitleFromMarkdown = (mdContent) => { const lines = mdContent.trimStart().split(/\r?\n/); for (const line of lines) { if (line.startsWith("# ")) { return line.substring(2).trim(); } } return void 0; }; const isIllegalIndex = (index) => { return isNaN(index) || index < 0; }; const isMarkdownFile = (filePath) => { return filePath.includes("md") || filePath.includes("MD"); }; const isSome = (arr, name) => { return arr.some((item) => item === name || item instanceof RegExp && item.test(name)); }; const removeMarkdownExt = (filePath) => { if (!filePath) return ""; return filePath.replace(/\.md$/, ""); }; const DEFAULT_IGNORE_DIR = ["node_modules", "dist", ".vitepress", "public"]; const catalogueInfo = []; const createCatalogues = (option = {}, vitepressConfig = {}) => { const { path = "", ignoreList = [] } = option; if (!path) return []; const dirPaths = readDirPaths(path, ignoreList); dirPaths.forEach((dirPath) => scannerMdFile(dirPath, option, node_path.basename(dirPath), vitepressConfig)); return catalogueInfo; }; const readDirPaths = (sourceDir, ignoreList = []) => { const ignoreListAll = [...DEFAULT_IGNORE_DIR, ...ignoreList]; const dirPaths = []; const dirOrFilenames = node_fs.readdirSync(sourceDir); dirOrFilenames.forEach((dirOrFilename) => { const secondDirPath = node_path.resolve(sourceDir, dirOrFilename); if (!isSome(ignoreListAll, dirOrFilename) && node_fs.statSync(secondDirPath).isDirectory()) { dirPaths.push(secondDirPath); } }); return dirPaths; }; const scannerMdFile = (root, option, prefix = "", vitepressConfig = {}) => { const { path: srcDir = "", ignoreList = [] } = option; const ignoreListAll = [...DEFAULT_IGNORE_DIR, ...ignoreList]; const dirOrFilenames = node_fs.readdirSync(root); dirOrFilenames.forEach((dirOrFilename) => { if (isSome(ignoreListAll, dirOrFilename)) return []; const filePath = node_path.resolve(root, dirOrFilename); if (node_fs.statSync(filePath).isDirectory()) { scannerMdFile(filePath, option, `${prefix}/${dirOrFilename}`, vitepressConfig); } else { if (!isMarkdownFile(dirOrFilename)) return; const content = node_fs.readFileSync(filePath, "utf-8"); const { data: { catalogue, path = "" } = {} } = matter__default(content, {}); if (catalogue && path) { const filename = node_path.basename(dirOrFilename, node_path.extname(dirOrFilename)); catalogueInfo.push({ filePath: `${prefix}/${filename}`, path, catalogues: createCatalogueList(node_path.join(srcDir, path), option, `/${path}/`, vitepressConfig) }); } } }); }; const createCatalogueList = (root, option, prefix = "/", vitepressConfig = {}) => { if (!node_fs.existsSync(root)) { console.warn(`'${root}' \u8DEF\u5F84\u4E0D\u5B58\u5728\uFF0C\u5C06\u5FFD\u7565\u8BE5\u76EE\u5F55\u9875\u7684\u751F\u6210`); return []; } const { ignoreIndexMd = false, titleFormMd = false, indexSeparator, catalogueItemResolved } = option; const catalogueItemList = []; const catalogueItemListNoIndex = []; const dirOrFilenames = node_fs.readdirSync(root); dirOrFilenames.forEach((dirOrFilename) => { const fileAbsolutePath = node_path.resolve(root, dirOrFilename); const { index: indexStr, title, name } = resolveFileName(dirOrFilename, fileAbsolutePath, indexSeparator); const index = parseInt(indexStr, 10); if (node_fs.statSync(fileAbsolutePath).isDirectory()) { const mdTitle = titleFormMd ? tryGetMarkdownTitle(root, dirOrFilename) : ""; const catalogueItem = { title: mdTitle || title, children: createCatalogueList(fileAbsolutePath, option, `${prefix}${dirOrFilename}/`, vitepressConfig), frontmatter: {} }; if (isIllegalIndex(index)) catalogueItemListNoIndex.push(catalogueItem); else catalogueItemList[index] = catalogueItem; } else { if (!isMarkdownFile(dirOrFilename)) return []; if (ignoreIndexMd && ["index.md", "index.MD"].includes(dirOrFilename)) return []; const content = node_fs.readFileSync(fileAbsolutePath, "utf-8"); const { data: frontmatter = {}, content: mdContent } = matter__default(content, {}); const { title: frontmatterTitle, catalogue, inCatalogue = true } = frontmatter; if (catalogue || !inCatalogue) return []; const mdTitle = titleFormMd ? getTitleFromMarkdown(mdContent) : ""; const finalTitle = frontmatterTitle || mdTitle || title; const filePath = prefix + name; const { rewrites = {}, cleanUrls } = vitepressConfig; const catalogueItem = { title: finalTitle, url: (removeMarkdownExt(rewrites[`${filePath.replace(/^\//, "")}.md`]) || filePath) + (cleanUrls ? "" : ".html"), frontmatter }; if (isIllegalIndex(index)) catalogueItemListNoIndex.push(catalogueItem); else catalogueItemList[index] = catalogueItem; } }); const data = [...catalogueItemList, ...catalogueItemListNoIndex].filter(Boolean); return catalogueItemResolved?.(data) ?? data; }; const tryGetMarkdownTitle = (root, dirOrFilename) => { const filePaths = [ node_path.join(root, dirOrFilename, "index.md"), node_path.join(root, dirOrFilename, "index.MD"), node_path.join(root, dirOrFilename, dirOrFilename + ".md") ]; for (const filePath of filePaths) { if (!node_fs.existsSync(filePath)) continue; const content = node_fs.readFileSync(filePath, "utf-8"); const { data: { title } = {}, content: mdContent } = matter__default(content, {}); const t = title || getTitleFromMarkdown(mdContent); if (t) return t; } return ""; }; const resolveFileName = (filename, filePath, separator = ".") => { const stat = node_fs.statSync(filePath); if (separator !== "." && isExtraSeparator(filename, separator)) { return parseExtraSeparator(filename, stat.isDirectory(), separator); } if (filename.includes(".")) { return parseDotSeparator(filename, stat.isDirectory()); } return { index: "", title: filename, type: "", name: filename }; }; const parseDotSeparator = (filename, isDirectory) => { const parts = filename.split("."); if (parts.length === 2) { const index = parts[0] === "index" ? "0" : parts[0]; const title = isDirectory ? parts[1] : parts[0]; const type = isDirectory ? "" : parts[1]; const name = parts[0]; return { index, title, type, name }; } else { const firstDotIndex = filename.indexOf("."); const lastDotIndex = filename.lastIndexOf("."); const index = filename.substring(0, firstDotIndex); const title = filename.substring(firstDotIndex + 1, lastDotIndex); const type = isDirectory ? "" : filename.substring(lastDotIndex + 1); const name = isDirectory ? filename : filename.substring(0, lastDotIndex); return { index, title, type, name }; } }; const isExtraSeparator = (filename, separator) => { if (!filename.includes(separator)) return false; const parts = filename.split(separator, 2); if (!/^\d+$/.test(parts[0])) return false; return true; }; const parseExtraSeparator = (filename, isDirectory, separator) => { const firstSeparatorIndex = filename.indexOf(separator); const lastDotIndex = filename.lastIndexOf("."); const index = filename.substring(0, firstSeparatorIndex); const title = isDirectory ? filename.substring(firstSeparatorIndex + 1) : filename.substring(firstSeparatorIndex + 1, lastDotIndex); const type = isDirectory ? "" : filename.substring(lastDotIndex + 1); const name = isDirectory ? filename : filename.substring(0, lastDotIndex); return { index, title, type, name }; }; const version = "1.1.1"; const logger = vite.createLogger("info", { prefix: `[vitepress-plugin-catalogue v${version}]` }); const info = (message, level = "green", option = { timestamp: true }) => { logger.info(picocolors__default[level](message), option); }; const warn = (message, level = "yellow", option = { timestamp: true }) => { logger.warn(picocolors__default[level](message), option); }; const warnOnce = (message, level = "yellow", option = { timestamp: true }) => { logger.info(picocolors__default[level](message), option); }; const error = (message, level = "red", option = { timestamp: true }) => { logger.error(picocolors__default[level](message), option); }; const logger$1 = { info, warn, warnOnce, error }; function VitePluginVitePressCatalogue(option = {}) { let isExecute = false; return { name: "vite-plugin-vitepress-catalogue", config(config) { if (isExecute) return; isExecute = true; const { site: { themeConfig }, srcDir, rewrites, cleanUrls } = config.vitepress; const baseDir = option.path ? node_path.join(srcDir, option.path) : srcDir; const catalogues = createCatalogues({ ...option, path: baseDir }, { rewrites: rewrites.map, cleanUrls }); const finalCatalogues = { arr: catalogues, map: {}, inv: {} }; catalogues.forEach((item) => { const { filePath, path, catalogues: catalogues2 = [] } = item; const url = (removeMarkdownExt(rewrites.map[`${filePath}.md`]) || filePath) + (cleanUrls ? "" : ".html"); finalCatalogues.map[filePath] = { url, path, catalogues: catalogues2 }; finalCatalogues.inv[path] = { url, filePath, catalogues: catalogues2 }; }); themeConfig.catalogues = finalCatalogues; logger$1.info("Injected Catalogues Data Successfully. \u6CE8\u5165\u76EE\u5F55\u9875\u6570\u636E\u6210\u529F!"); } }; } exports.default = VitePluginVitePressCatalogue;