@tobi-or-not/docpress 0.12.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../package.json","../src/utils/const.ts","../src/utils/logger.ts","../src/utils/regex.ts","../src/utils/functions.ts","../src/schemas/global.ts","../src/utils/commands.ts","../src/lib/git.ts","../src/lib/gitlab.ts","../src/lib/fetch.ts","../src/commands/global.ts","../src/commands/fetch.ts","../src/commands/build.ts","../src/utils/templates.ts","../src/lib/vitepress.ts","../src/lib/prepare.ts","../src/commands/prepare.ts","../src/cli.ts"],"sourcesContent":["{\n \"name\": \"@tobi-or-not/docpress\",\n \"type\": \"module\",\n \"version\": \"0.12.3\",\n \"packageManager\": \"bun@1.4.0\",\n \"homepage\": \"https://this-is-tobi.com/docpress/introduction\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/this-is-tobi/docpress.git\"\n },\n \"bin\": {\n \"docpress\": \"bin/docpress.js\"\n },\n \"files\": [\n \"bin\",\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=22.12.0\",\n \"bun\": \">=1.3.0\"\n },\n \"scripts\": {\n \"build\": \"bun run build:clean && bun run build:types && bun run build:vite\",\n \"build:clean\": \"node -e \\\"for (const p of ['./dist','./types','./tsconfig.tsbuildinfo']) require('node:fs').rmSync(p,{recursive:true,force:true})\\\"\",\n \"build:docker\": \"docker build --tag tobi-or-not/docpress --target prod .\",\n \"build:types\": \"tsc\",\n \"build:vite\": \"vite build\",\n \"dev\": \"NODE_ENV=development bun ./dev/docpress.ts\",\n \"dev:clean\": \"node -e \\\"require('node:fs').rmSync('./docpress',{recursive:true,force:true})\\\" && bun run dev\",\n \"format\": \"eslint . --fix\",\n \"lint\": \"eslint .\",\n \"prepare\": \"husky || true\",\n \"start\": \"./bin/docpress.js\",\n \"start:docker\": \"docker run --name docpress --rm -v $(pwd)/docpress:/app/docpress:rw tobi-or-not/docpress\",\n \"stop:docker\": \"docker rm --force docpress\",\n \"test\": \"vitest run\",\n \"test:cov\": \"vitest run --coverage\",\n \"vp:build\": \"vitepress build ./docpress\",\n \"vp:build:docker\": \"docker build --tag tobi-or-not/docs --target prod .\",\n \"vp:dev\": \"vitepress dev ./docpress --port 8080\",\n \"vp:dev:docker\": \"docker run --publish 8080:8080 --name docpress-vp --rm tobi-or-not/docs\",\n \"vp:preview\": \"vitepress preview ./docpress --port 8080\"\n },\n \"dependencies\": {\n \"@octokit/rest\": \"^22.0.1\",\n \"commander\": \"^15.0.0\",\n \"mermaid\": \"^11.17.2\",\n \"picocolors\": \"^1.1.1\",\n \"simple-git\": \"^3.36.0\",\n \"vitepress\": \"^1.6.4\",\n \"vitepress-plugin-mermaid\": \"^2.0.17\",\n \"yaml\": \"^2.9.0\",\n \"zod\": \"^4.5.4\"\n },\n \"devDependencies\": {\n \"@antfu/eslint-config\": \"^9.5.1\",\n \"@commitlint/cli\": \"^21.2.2\",\n \"@commitlint/config-conventional\": \"^21.2.2\",\n \"@octokit/types\": \"^18.0.0\",\n \"@types/node\": \"^26.4.1\",\n \"@vitejs/plugin-vue\": \"^6.0.8\",\n \"@vitest/coverage-v8\": \"^4.1.11\",\n \"eslint\": \"^10.9.1\",\n \"husky\": \"^9.1.7\",\n \"nano-staged\": \"^1.0.2\",\n \"typescript\": \"^6.0.3\",\n \"vite\": \"^8.2.2\",\n \"vitest\": \"^4.1.11\",\n \"vue\": \"^3.5.42\"\n },\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org/\",\n \"access\": \"public\",\n \"tag\": \"latest\"\n },\n \"overrides\": {\n \"brace-expansion\": \"^5.0.8\",\n \"dompurify\": \"^3.4.14\",\n \"fast-uri\": \"^3.1.6\",\n \"js-yaml\": \"^4.3.1\",\n \"nanoid\": \"^3.3.16\",\n \"postcss\": \"^8.5.26\"\n }\n}\n","import { resolve } from 'node:path'\n\n/**\n * Main directory for DocPress files\n */\nexport const DOCPRESS_DIR = resolve(process.cwd(), 'docpress')\n\n/**\n * Directory for documentation files\n */\nexport const DOCS_DIR = resolve(process.cwd(), 'docpress/docs')\n\n/**\n * Path to the main index file\n */\nexport const INDEX_FILE = resolve(process.cwd(), 'docpress/docs/index.md')\n\n/**\n * Path to the forks documentation file\n */\nexport const FORKS_FILE = resolve(process.cwd(), 'docpress/docs/forks.md')\n\n/**\n * Path to VitePress configuration file\n */\nexport const VITEPRESS_CONFIG = resolve(process.cwd(), 'docpress/.vitepress/config.ts')\n\n/**\n * Directory for VitePress theme\n */\nexport const VITEPRESS_THEME = resolve(process.cwd(), 'docpress/.vitepress/theme')\n\n/**\n * Directory for user custom theme files\n */\nexport const VITEPRESS_USER_THEME = resolve(process.cwd(), 'docpress/.vitepress/theme/extras')\n\n/**\n * Path to template theme files\n */\n// eslint-disable-next-line dot-notation\nexport const TEMPLATE_THEME = process.env['NODE_ENV'] === 'development'\n ? resolve(import.meta.dirname, '../../public/templates/theme')\n : resolve(import.meta.dirname, './templates/theme')\n","/* eslint-disable dot-notation */\nimport pc from 'picocolors'\n\n/**\n * Log message types\n */\ntype LogType = keyof Pick<Console, 'info' | 'warn' | 'error' | 'debug' | 'trace'> | 'success'\n\n/**\n * Colors supported by the logger\n */\ntype Color = 'red' | 'yellow' | 'white' | 'green' | 'blue'\n\n/**\n * Maps a supported color to its picocolors formatter\n */\nconst colorFns: Record<Color, (input: string) => string> = {\n red: pc.red,\n yellow: pc.yellow,\n white: pc.white,\n green: pc.green,\n blue: pc.blue,\n}\n\n/**\n * Mapping of log types to colors and levels\n */\ntype ColorMap = {\n [K in LogType]: {\n color: Color\n level: number\n }\n}\n\n/**\n * Gets the current log level from environment variables\n *\n * @returns Log level number (default: 30)\n */\nfunction getLogLevel() {\n const level = Number(process.env['LOG_LEVEL'])\n return Number.isNaN(level) ? 30 : level\n}\n\n/**\n * Color mapping configuration for different log types\n */\nconst colorMap: ColorMap = {\n error: { color: 'red', level: 10 },\n warn: { color: 'yellow', level: 20 },\n info: { color: 'white', level: 30 },\n success: { color: 'green', level: 30 },\n trace: { color: 'white', level: 40 },\n debug: { color: 'blue', level: 50 },\n}\n\n/**\n * Log level names selectable via the --log-level flag / logLevel config key,\n * kept in sync with the numeric scale in colorMap ('success' excluded: it is\n * not an independently selectable verbosity level, only an info-level color)\n */\nexport const LOG_LEVEL_NAMES = ['error', 'warn', 'info', 'trace', 'debug'] as const\n\n/**\n * A log level name selectable via the --log-level flag / logLevel config key\n */\nexport type LogLevelName = typeof LOG_LEVEL_NAMES[number]\n\n/**\n * Resolves the numeric LOG_LEVEL value for a named log level\n *\n * @param name - Log level name\n * @returns The numeric level consumed by LOG_LEVEL / colorMap\n */\nexport function logLevelValue(name: LogLevelName): number {\n return colorMap[name].level\n}\n\n/**\n * Logs a message with color formatting based on type and level\n *\n * @param msg - Message to log\n * @param type - Type of log message (info, warn, error, debug, trace, success)\n * @param color - Optional specific color to use instead of the default for the type\n */\nexport function log(msg: string, type: LogType = 'info', color?: Color) {\n const computedMsg = color ? colorFns[color](msg) : colorFns[colorMap[type].color](msg)\n const computedType = type === 'success' ? 'info' : type\n\n if (getLogLevel() >= colorMap[type].level) {\n console[computedType](computedMsg)\n }\n}\n","// Markdown link whose target is a parent-relative path, e.g. `[text](../foo/bar)`\n// Captures: 1) link text, 2) the path after `../`\nexport const relativePathRegex = /\\[([^\\]]+)\\]\\(\\.\\.\\/([^)]+)\\)/g\n\n// Markdown link in a README pointing outside `docs/` and not an http/anchor link,\n// e.g. `[text](./src/index.ts)`. Captures: 1) text, 2) optional `./`, 3) the path\nexport const readmePathRegex = /\\[([^\\]]+)\\]\\((?!\\.?\\/docs\\/|docs\\/|http|#)(\\.\\/)?([^/][^)]+)\\)/g\n\n// Markdown link in a README pointing into `docs/`, e.g. `[text](./docs/guide.md)`\n// Captures: 1) text, 2) the `docs/` prefix, 3) the path within docs\nexport const readmeDocsPathRegex = /\\[([^\\]]+)\\]\\((\\.?\\/docs\\/|docs\\/)([^)]*)\\)/g\n\n// Leading two-digit ordering prefix stripped from file names, e.g. `01-` in `01-intro.md`\nexport const removeIdxRegex = /^\\d{2}-/\n\n// YAML frontmatter block at the top of a markdown file. Captures: 1) the inner YAML\nexport const frontmatterRegex = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/\n\n// Relative markdown-to-markdown link (not http/anchor/mailto/root), e.g. `[t](./dir/page.md#a)`\n// Captures: 1) text, 2) optional directory, 3) the `.md` file, 4) optional `#anchor`\nexport const internalMdLinkRegex = /\\[([^\\]]+)\\]\\((?!https?:|\\/|#|mailto:)([^)#]*\\/)?([^)#/]+\\.md)(#[^)]*)?\\)/g\n","import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'\nimport { basename, join, resolve } from 'node:path'\nimport YAML from 'yaml'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { EnhancedRepository } from '../lib/fetch.js'\nimport type { getInfos } from '../lib/git.js'\nimport { DOCPRESS_DIR } from './const.js'\nimport { log } from './logger.js'\nimport { frontmatterRegex, internalMdLinkRegex, readmeDocsPathRegex, readmePathRegex, relativePathRegex, removeIdxRegex } from './regex.js'\n\n/**\n * Checks the HTTP status code of a URL\n * Redirects are not followed: some providers (e.g. GitLab) redirect to a\n * sign-in page instead of returning 404 for missing resources\n *\n * @param url - URL to check\n * @returns HTTP status code (500 for network/transport errors)\n */\nexport async function checkHttpStatus(url: string): Promise<number> {\n try {\n const response = await fetch(url, { method: 'HEAD', redirect: 'manual' })\n return response.status\n } catch (error) {\n log(` Network error while checking '${url}': ${formatError(error)}`, 'debug')\n return 500\n }\n}\n\n/**\n * Reduces an arbitrary string to a safe single path segment: basename only,\n * with no path separators or leading dots\n *\n * @param name - Raw name (repository name, username, login, ...)\n * @returns A filesystem-safe segment\n */\nexport function sanitizeSegment(name: string): string {\n const segment = name.split(/[/\\\\]/).pop() ?? ''\n const safe = segment.replace(/[^\\w.-]/g, '_').replace(/^\\.+/, '')\n // Never return an empty segment: an empty path component would let callers\n // that join it onto a base dir (and clean that dir) operate on the base itself\n return safe || '_'\n}\n\n/**\n * Returns a shallow copy of an options object with the `token` value masked\n *\n * @param obj - Options or config object that may contain a `token` field\n * @returns A copy with `token` replaced by a placeholder\n */\nexport function redactToken<T extends { token?: unknown }>(obj: T): T {\n if (!isObject(obj) || !('token' in obj)) {\n return obj\n }\n return { ...obj, token: obj.token ? '***' : obj.token } as T\n}\n\n/**\n * Extracts a human-readable message from a caught value\n *\n * @param error - The caught value (typically an Error, but not guaranteed)\n * @returns The error's message, or its string representation when it isn't an Error\n */\nexport function formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * Formats a duration in milliseconds as a short human-readable string\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted duration (e.g. \"350ms\", \"1.2s\", \"1m 05s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`\n }\n const totalSeconds = ms / 1000\n if (totalSeconds < 60) {\n return `${totalSeconds.toFixed(1)}s`\n }\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = Math.round(totalSeconds % 60)\n return `${minutes}m ${String(seconds).padStart(2, '0')}s`\n}\n\n/**\n * Options for prettifying strings\n */\ninterface PrettifyOpts {\n mode?: 'capitalize' | 'uppercase' | 'lowercase'\n replaceDash?: boolean\n removeIdx?: boolean\n removeDot?: boolean\n removeExt?: boolean\n}\n\n/**\n * Formats a string based on specified options\n *\n * @param s - Input string to format\n * @param opts - Formatting options\n * @returns Formatted string\n */\nexport function prettify(s: string, opts: PrettifyOpts) {\n let u: string = ''\n\n if (opts?.removeDot) {\n if (s.startsWith('.')) {\n u = s.slice(1)\n } else {\n u = s\n }\n // Replace all remaining dots with dashes to ensure consistency\n u = u.replaceAll('.', '-')\n }\n\n if (opts?.removeIdx) {\n u = (u || s).replace(removeIdxRegex, '')\n }\n\n if (opts?.removeExt) {\n u = (u || s).split('.')[0]\n }\n\n if (opts?.replaceDash) {\n u = (u || s).replaceAll('-', ' ')\n }\n\n if (opts?.mode === 'capitalize') {\n u = (u || s).slice(0, 1).toUpperCase() + (u || s).slice(1).toLowerCase()\n } else if (opts?.mode === 'lowercase') {\n u = (u || s).toLowerCase()\n } else if (opts?.mode === 'uppercase') {\n u = (u || s).toUpperCase()\n }\n\n return u || s\n}\n\n/**\n * Creates a directory, optionally cleaning it first\n *\n * @param directory - Path to the directory to create\n * @param options - Options object\n * @param options.clean - Whether to clean the directory before creating it\n */\nexport function createDir(directory: string, { clean }: { clean?: boolean } = { clean: false }) {\n try {\n if (clean && existsSync(directory)) {\n // Empty the directory's contents rather than removing the directory itself: it may be a bind-mounted volume (e.g. Docker), and removing the mount point fails with EBUSY.\n for (const entry of readdirSync(directory)) {\n rmSync(resolve(directory, entry), { recursive: true, force: true })\n }\n }\n if (!existsSync(directory)) {\n mkdirSync(directory, { recursive: true })\n }\n } catch (err) {\n throw new Error(`Unable to create directory '${directory}': ${err instanceof Error ? err.message : String(err)}`)\n }\n}\n\n/**\n * Checks if a path is a directory\n *\n * @param path - Path to check\n * @returns True if path is a directory, false otherwise\n */\nexport function isDir(path: string) {\n try {\n return statSync(path).isDirectory()\n } catch (_error) {\n return false\n }\n}\n\n/**\n * Checks if a path is a file\n *\n * @param path - Path to check\n * @returns True if path is a file, false otherwise\n */\nexport function isFile(path: string) {\n try {\n return statSync(path).isFile()\n } catch (_error) {\n return false\n }\n}\n\n/**\n * Extracts a list of file paths from directories and files\n *\n * @param paths - Path or array of paths to extract files from\n * @returns Array of file paths\n */\nexport function extractFiles(paths: string[] | string): string[] {\n return (Array.isArray(paths) ? paths : [paths]).flatMap((path) => {\n if (isFile(path)) {\n return [path]\n }\n if (isDir(path)) {\n return readdirSync(path).filter(file => file !== '.git').sort().flatMap(file => extractFiles(join(path, file)))\n }\n return []\n })\n}\n\n/**\n * Gets all markdown files from specified paths\n *\n * @param path - Array of paths to search for markdown files\n * @returns Array of markdown file paths\n */\nexport function getMdFiles(path: string[]) {\n return extractFiles(path).filter(file => basename(file).endsWith('.md'))\n}\n\n/**\n * Formats an array of strings into a readable comma-separated list\n *\n * @param arr - Array of strings to format\n * @returns Formatted string (e.g., \"a\", \"b\" or \"c\")\n */\nexport function prettifyEnum(arr: readonly string[]) {\n return arr.reduce((acc, cur, idx, arr) => {\n if (!idx) {\n return `\"${cur}\"`\n } else if (idx === arr.length - 1) {\n return `${acc} or \"${cur}\"`\n } else {\n return `${acc}, \"${cur}\"`\n }\n }, '')\n}\n\n/**\n * Gets user information from cached JSON file\n *\n * @param username - GitHub username\n * @returns User information object\n */\nexport function getUserInfos(username: GlobalOpts['usernames'][number]) {\n return JSON.parse(readFileSync(`${DOCPRESS_DIR}/user-${sanitizeSegment(username)}.json`).toString()) as Awaited<ReturnType<typeof getInfos>>['user']\n}\n\n/**\n * Gets repository information from cached JSON file\n *\n * @param username - GitHub username\n * @returns Array of enhanced repository objects\n */\nexport function getUserRepos(username: GlobalOpts['usernames'][number]) {\n return JSON.parse(readFileSync(`${DOCPRESS_DIR}/repos-${sanitizeSegment(username)}.json`).toString()) as EnhancedRepository[]\n}\n\n/**\n * Keys skipped during a merge to prevent prototype pollution\n */\nconst FORBIDDEN_MERGE_KEYS = new Set(['__proto__', 'constructor', 'prototype'])\n\n/**\n * Deep merges multiple objects, cloning object values rather than sharing them by reference\n *\n * @param objects - Objects to merge\n * @returns Merged object\n */\nexport function deepMerge<T extends Record<string, any> | null>(...objects: T[]): T {\n return objects.reduce((acc, obj) => {\n if (obj === null) {\n return acc\n }\n\n Object.keys(obj).forEach((key) => {\n if (FORBIDDEN_MERGE_KEYS.has(key)) {\n return\n }\n\n const accValue = acc ? acc[key] : null\n const objValue = obj[key]\n\n if (isObject(accValue) && isObject(objValue)) {\n (acc as Record<string, any>)[key] = deepMerge(accValue, objValue)\n } else if (isObject(objValue)) {\n (acc as Record<string, any>)[key] = deepMerge({}, objValue)\n } else {\n (acc as Record<string, any>)[key] = objValue\n }\n })\n return acc\n }, {} as T)\n}\n\n/**\n * Checks if a value is an object (not an array or null)\n *\n * @param val - Value to check\n * @returns True if value is an object, false otherwise\n */\nexport function isObject(val: any): val is object {\n return val && typeof val === 'object' && !Array.isArray(val)\n}\n\n/**\n * Loads and parses a JSON configuration file\n *\n * @param configPath - Path to the configuration file\n * @returns Parsed configuration object, or an empty object when no path is given\n * @throws Error if the file cannot be read or contains invalid JSON\n */\nexport function loadConfigFile(configPath?: string) {\n if (!configPath) {\n return {}\n }\n const fullPath = resolve(process.cwd(), configPath)\n let fileContents: string\n try {\n fileContents = readFileSync(fullPath, 'utf8')\n } catch (_error) {\n throw new Error(`Cannot read config file '${configPath}'.`)\n }\n try {\n return JSON.parse(fileContents)\n } catch (_error) {\n throw new Error(`Invalid JSON in config file '${configPath}'.`)\n }\n}\n\n/**\n * Splits a comma-separated string into an array of strings\n *\n * @param s - Comma-separated string\n * @returns Array of strings\n */\nexport function splitByComma(s: string) {\n return s.split(',')\n}\n\n/**\n * Replaces relative paths in markdown links with absolute URLs\n *\n * @param file - Path to the markdown file to process\n * @param url - Base URL to use for absolute links\n */\nexport function replaceRelativePath(file: string, url: string) {\n const fileContent = readFileSync(file, 'utf8')\n const updatedContent = fileContent.replace(relativePathRegex, (_match, p1, p2) => {\n return `[${p1}](${url}/${p2})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n\n/**\n * Adds or updates the \"lastUpdated\" frontmatter field of a markdown file, preserving\n * any frontmatter already present\n *\n * @param file - Path to the markdown file to update\n * @param date - Datetime to set as the \"lastUpdated\" value\n */\nexport function addLastUpdatedFrontmatter(file: string, date: string) {\n const content = readFileSync(file, 'utf8')\n const match = content.match(frontmatterRegex)\n\n if (match) {\n const data = YAML.parse(match[1]) ?? {}\n data.lastUpdated = date\n writeFileSync(file, content.replace(frontmatterRegex, `---\\n${YAML.stringify(data)}---\\n`), 'utf8')\n } else {\n writeFileSync(file, `---\\nlastUpdated: ${date}\\n---\\n\\n${content}`, 'utf8')\n }\n}\n\n/**\n * Strips the ordering prefix from every named segment of a relative link's directory\n * Folders are renamed during sidebar generation exactly as files are, so a link that\n * crosses one has to follow the rename on both halves of the path — rewriting only\n * the filename would leave `../01-guide/setup.md` pointing at a folder that is now\n * `guide`, which resolves at publish time and fails for the reader rather than the build\n *\n * @param dir - Directory portion of a relative link, or undefined when the link has none\n * @returns The directory with ordering prefixes removed, `.` and `..` left alone\n */\nfunction prettifyLinkDir(dir?: string): string {\n if (!dir) {\n return ''\n }\n return dir\n .split('/')\n .map(segment => segment === '' || segment === '.' || segment === '..'\n ? segment\n : prettify(segment, { removeIdx: true }))\n .join('/')\n}\n\n/**\n * Rewrites relative markdown links to match the renamed target files\n * Files and folders are renamed during sidebar generation (index prefix stripped, name\n * lowercased, readme becoming introduction), so in-page links must follow the same\n * convention to avoid pointing to files that no longer exist\n *\n * @param file - Path to the markdown file to process\n */\nexport function replaceInternalMdLinks(file: string) {\n const fileContent = readFileSync(file, 'utf8')\n const updatedContent = fileContent.replace(internalMdLinkRegex, (_match, text, dir, target, anchor) => {\n let filename = prettify(target, { mode: 'lowercase', removeIdx: true })\n if (filename === 'readme.md') {\n filename = 'introduction.md'\n }\n return `[${text}](${prettifyLinkDir(dir)}${filename}${anchor ?? ''})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n\n/**\n * Processes README files to fix various link formats\n *\n * @param file - Path to the README file to process\n * @param url - Base URL to use for absolute links\n */\nexport function replaceReadmePath(file: string, url: string) {\n const readmeContent = readFileSync(file, 'utf8')\n const updatedContent = readmeContent\n .replace(readmePathRegex, (_match, p1, _p2, p3) => {\n return `[${p1}](${url}/${p3})`\n })\n .replace(readmeDocsPathRegex, (_match, p1, _p2, p3) => {\n return `[${p1}](${p3})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n","import { z } from 'zod'\nimport { loadConfigFile, prettifyEnum, splitByComma } from '../utils/functions.js'\nimport type { LogLevelName } from '../utils/logger.js'\nimport { LOG_LEVEL_NAMES, logLevelValue } from '../utils/logger.js'\n\n/**\n * List of supported Git providers\n */\nconst providers = ['github', 'gitlab'] as const\n\n/**\n * Supported sidebar shapes\n */\nconst sidebarModes = ['single', 'multi'] as const\n\n/**\n * Accepted CLI values for the tri-state sidebar collapse setting\n */\nconst sidebarCollapsedValues = ['true', 'false', 'null'] as const\n\n/**\n * Allowed characters for a Git branch/ref name (must not start with \"-\")\n */\nconst safeRefRegex = /^\\w[\\w./-]*$/\nconst safeRefMessage = 'Branch name must start with a letter, digit or underscore and only contain letters, digits, \".\", \"-\", \"_\" or \"/\".'\n\n/**\n * Schema for the DocPress configuration file\n * Defines the structure and validation rules for the configuration\n */\nexport const configSchema = z.object({\n // Global\n usernames: z.string()\n .array()\n .describe('List of comma separated Git provider usernames used to collect data.'),\n logLevel: z.enum(LOG_LEVEL_NAMES)\n .optional()\n .describe(`Verbosity of the CLI output. Values should be ${prettifyEnum(LOG_LEVEL_NAMES)}. Defaults to \"info\", or to the LOG_LEVEL environment variable when set.`),\n // Fetch\n branch: z.string()\n .regex(safeRefRegex, safeRefMessage)\n .optional()\n .describe('Branch used to collect Git provider data. Defaults to each repository\\'s own default branch when not set.'),\n gitProvider: z.enum(providers)\n .default('github')\n .describe(`Git provider used to retrieve data. Values should be ${prettifyEnum(providers)}.`),\n reposFilter: z.string()\n .array()\n .describe('List of comma separated repositories to retrieve from Git provider. Default to all user\\'s public repositories.'),\n // Prepare\n extraHeaderPages: z.string()\n .array()\n .describe('List of comma separated additional files or directories to process Vitepress header pages.'),\n extraPublicContent: z.string()\n .array()\n .describe('List of comma separated additional files or directories to process Vitepress public folder.'),\n extraTheme: z.string()\n .array()\n .describe('List of comma separated additional files or directories to use as Vitepress theme.'),\n forks: z.boolean()\n .default(false)\n .describe('Whether or not to create the dedicated fork page that aggregate external contributions.'),\n lastUpdated: z.boolean()\n .default(false)\n .describe('Whether or not to inject each page\\'s last Git commit date as Vitepress \"lastUpdated\" frontmatter.'),\n sidebarMode: z.enum(sidebarModes)\n .default('single')\n .describe(`Shape of the generated sidebar. Values should be ${prettifyEnum(sidebarModes)}. \"single\" lists every repository in one sidebar, \"multi\" gives each repository its own sidebar.`),\n sidebarCollapsed: z.union([z.boolean(), z.null()])\n .default(true)\n .describe('Collapse behaviour of generated sidebar groups. \"true\" collapses them by default, \"false\" expands them, \"null\" makes them non-collapsible.'),\n vitepressConfig: z.any()\n .optional()\n .describe('Path to the vitepress configuration file.'),\n websiteTitle: z.string()\n .describe('Website title.'),\n websiteTagline: z.string()\n .describe('Website tagline.'),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\n/**\n * CLI schema with transformations applied to handle string-to-array conversions\n * Fields with config defaults are redefined without them, so that values coming\n * from a config file are not overridden by schema defaults\n */\nexport const cliSchema = configSchema\n .partial()\n .extend({\n branch: z.string()\n .regex(safeRefRegex, safeRefMessage)\n .optional()\n .describe(configSchema.shape.branch.description || ''),\n gitProvider: z.enum(providers)\n .optional()\n .describe(configSchema.shape.gitProvider.description || ''),\n logLevel: z.enum(LOG_LEVEL_NAMES)\n .optional()\n .describe(configSchema.shape.logLevel.description || ''),\n forks: z.boolean()\n .optional()\n .describe(configSchema.shape.forks.description || ''),\n lastUpdated: z.boolean()\n .optional()\n .describe(configSchema.shape.lastUpdated.description || ''),\n config: z.string()\n .optional()\n .describe('Path to the docpress configuration file.'),\n token: z.string()\n .optional()\n .describe('Git provider token used to collect data.'),\n usernames: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.usernames.description || ''),\n reposFilter: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.reposFilter.description || ''),\n extraHeaderPages: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraHeaderPages.description || ''),\n extraPublicContent: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraPublicContent.description || ''),\n extraTheme: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraTheme.description || ''),\n vitepressConfig: z.string()\n .optional()\n .describe(configSchema.shape.vitepressConfig.description || ''),\n sidebarMode: z.enum(sidebarModes)\n .optional()\n .describe(configSchema.shape.sidebarMode.description || ''),\n sidebarCollapsed: z.enum(sidebarCollapsedValues)\n .transform(value => value === 'null' ? null : value === 'true')\n .optional()\n .describe(`${configSchema.shape.sidebarCollapsed.description} Values should be ${prettifyEnum(sidebarCollapsedValues)}.`),\n })\n\nexport type Cli = z.infer<typeof cliSchema>\n\n/**\n * Prepares configuration data by converting string values to arrays when needed\n *\n * @param configData - Raw configuration data to process\n * @returns Processed configuration data with proper array types\n */\nfunction prepareConfigData(configData: any) {\n if (!configData) return {}\n\n const result = { ...configData }\n const arrayKeys = ['usernames', 'reposFilter', 'extraHeaderPages', 'extraPublicContent', 'extraTheme'] as const\n for (const key of arrayKeys) {\n if (typeof result[key] === 'string') {\n result[key] = [result[key]]\n }\n }\n\n return result\n}\n\n/**\n * Validates configuration file data against the config schema\n *\n * @param configData - Prepared configuration data to validate\n * @returns The validated configuration data\n * @throws Error if the configuration data is invalid\n */\nfunction validateConfigData(configData: any) {\n const result = configSchema.partial().safeParse(configData)\n if (!result.success) {\n throw new Error(z.prettifyError(result.error))\n }\n return result.data\n}\n\n/**\n * Validates the final merged configuration to ensure required fields are present\n *\n * @param mergedConfig - The merged configuration to validate\n * @returns The validated configuration if it passes validation\n * @throws Error if required fields are missing\n */\nfunction validateFinalConfig(mergedConfig: any) {\n if (!mergedConfig.usernames?.length) {\n throw new Error('The usernames field is required')\n }\n\n return mergedConfig\n}\n\n/**\n * Resolves the API token, with precedence: explicit token > DOCPRESS_TOKEN >\n * provider-specific env var (GITHUB_TOKEN / GITLAB_TOKEN). Empty values count as\n * unset at every step\n *\n * @param token - Token explicitly provided via CLI flag or config file\n * @param gitProvider - Git provider used to pick the provider-specific env var\n * @returns The resolved token, or undefined when none is available\n */\nfunction resolveToken(token: string | undefined, gitProvider: string | undefined): string | undefined {\n if (token) {\n return token\n }\n /* eslint-disable dot-notation */\n const providerToken = gitProvider === 'gitlab' ? process.env['GITLAB_TOKEN'] : process.env['GITHUB_TOKEN']\n // Empty values are treated as unset: a CI secret that did not resolve arrives as\n // an empty string, which must not shadow the next source or pass as a token\n return process.env['DOCPRESS_TOKEN'] || providerToken || undefined\n /* eslint-enable dot-notation */\n}\n\n/**\n * Applies an explicitly resolved log level (CLI flag > config file) as the\n * LOG_LEVEL env var consumed by the logger. Left untouched when neither the CLI\n * flag nor the config file set one, so an existing numeric LOG_LEVEL env var\n * (or the logger's own default) keeps working exactly as before\n *\n * @param logLevel - Log level name explicitly provided via CLI flag or config file\n */\nfunction resolveLogLevel(logLevel: LogLevelName | undefined): void {\n if (logLevel) {\n // eslint-disable-next-line dot-notation\n process.env['LOG_LEVEL'] = String(logLevelValue(logLevel))\n }\n}\n\n/**\n * Schema for global options that combines CLI arguments and configuration files\n * Merges config sources with precedence: CLI options > config file > defaults\n */\nexport const globalOptsSchema = cliSchema\n .partial()\n .transform((data, ctx) => {\n try {\n const { config, vitepressConfig, token, ...rest } = data\n\n // Load and validate configuration from file\n const configData = validateConfigData(prepareConfigData(loadConfigFile(config)))\n\n // Create final config. `branch` is intentionally NOT defaulted here: when\n // the user does not pass --branch, it stays undefined so the fetch step\n // can fall back to each repository's own default branch (see fetch.ts).\n const mergedConfig = {\n gitProvider: 'github',\n forks: false,\n lastUpdated: false,\n sidebarMode: 'single',\n sidebarCollapsed: true,\n ...configData,\n ...rest,\n ...(vitepressConfig\n ? {\n vitepressConfig: {\n ...(configData.vitepressConfig || {}),\n ...(loadConfigFile(vitepressConfig) || {}),\n },\n }\n : {}),\n }\n\n resolveLogLevel(mergedConfig.logLevel)\n\n return validateFinalConfig({ ...mergedConfig, token: resolveToken(token, mergedConfig.gitProvider) })\n } catch (error) {\n // Surface the failure as a validation issue instead of terminating the\n // process from inside the schema. The caller (parseOptions) reports it and\n // owns the single exit point, which keeps this schema pure and testable.\n ctx.addIssue({\n code: 'custom',\n message: error instanceof Error ? error.message : JSON.stringify(error, null, 2),\n })\n return z.NEVER\n }\n })\n\nexport type GlobalOpts = Required<Pick<z.infer<typeof globalOptsSchema>, 'gitProvider'>>\n & Omit<z.infer<typeof globalOptsSchema>, 'gitProvider'>\n","import type { Command, Option } from 'commander'\nimport { z } from 'zod'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport { globalOptsSchema } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport { redactToken } from './functions.js'\nimport { log } from './logger.js'\n\n/**\n * Type definition for command names\n */\ntype Cmd = 'fetch' | 'prepare' | 'global'\n\n/**\n * Interface mapping command names to their option types\n */\ninterface Options {\n fetch: FetchOpts\n prepare: PrepareOpts\n global: GlobalOpts\n}\n\n/**\n * Validates raw command options against the global schema\n * The merging with precedence CLI options > config file > defaults is performed by\n * `globalOptsSchema` (see src/schemas/global.ts); this function only runs that schema\n *\n * @param cmd - The command name ('fetch', 'prepare' or 'global')\n * @param opts - Raw options from the command line\n * @returns Validated and processed options for the specified command\n */\nexport function parseOptions<T extends Cmd>(cmd: T, opts: Record<string, unknown>): Options[T] {\n log(`Initializing Docpress...`, 'info', 'blue')\n log(`\\n\\n-> Checking for required environment settings and configurations.`, 'info')\n log(` Debug info - cmd: ${cmd}`, 'debug')\n\n const res = globalOptsSchema.safeParse(opts)\n if (!res.success) {\n log(` An error occurred while checking configuration.\\n ${z.prettifyError(res.error)}`, 'error')\n process.exit(1)\n }\n // Logged after validation (not the raw input) so this reflects the fully merged\n // CLI/config/defaults result, and so a `--log-level debug` passed in this same\n // invocation - only resolved inside the schema above - is already in effect.\n log(` Debug info - resolved options: ${JSON.stringify(redactToken(res.data))}`, 'debug')\n log(' Setup complete! Ready to process your documentation.', 'info')\n return res.data as Options[T]\n}\n\n/**\n * Extracts options that were explicitly provided by the user, skipping Commander defaults\n * This lets config file values take precedence over CLI defaults\n *\n * @param cmd - The Commander Command instance\n * @param opts - Raw options from the command line\n * @returns Options object containing only user-provided values\n */\nexport function explicitOptions(cmd: Command, opts: Record<string, unknown>) {\n return Object.fromEntries(\n Object.entries(opts).filter(([key]) => cmd.getOptionValueSource(key) !== 'default'),\n )\n}\n\n/**\n * Adds option definitions to a Command instance\n *\n * @param cmd - The Commander Command instance\n * @param opts - Array of option definitions to add\n * @returns The Command instance with options added\n */\nexport function addOptions(cmd: Command, opts: Option[]) {\n for (const opt of opts) {\n cmd.addOption(opt)\n }\n return cmd\n}\n","import { appendFileSync, cpSync, rmSync } from 'node:fs'\nimport { relative, resolve } from 'node:path'\nimport { Octokit } from '@octokit/rest'\nimport { simpleGit } from 'simple-git'\nimport type { SimpleGit } from 'simple-git'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { addLastUpdatedFrontmatter, createDir, formatError, getMdFiles } from '../utils/functions.js'\nimport { log } from '../utils/logger.js'\nimport type { EnhancedRepository } from './fetch.js'\n\n/**\n * Octokit logger that stays quiet on 404s, which are handled explicitly\n */\nexport const quietOctokitLog = {\n debug: () => {},\n info: () => {},\n warn: (message: string) => {\n if (!message.includes('404')) log(message, 'warn')\n },\n error: (message: string) => {\n if (!message.includes('404')) log(message, 'error')\n },\n}\n\n/**\n * Fetches user and repository information from GitHub\n *\n * @param options - Options for retrieving GitHub data\n * @param options.username - GitHub username\n * @param options.token - GitHub API token\n * @param options.branch - Branch to use for documentation\n * @returns Object containing user information, repositories, and branch\n */\nexport async function getInfos({ username, token, branch }: Pick<FetchOpts, 'branch'> & Pick<GlobalOpts, 'token'> & { username: GlobalOpts['usernames'][number] }) {\n log(` Get infos for username '${username}'.`, 'info')\n const octokit = new Octokit({ auth: token, log: quietOctokitLog })\n log(` Get user infos.`, 'debug')\n const { data: user } = await octokit.rest.users.getByUsername({ username })\n .catch((error) => {\n throw error.status === 404 ? new Error(`No GitHub user found for '${username}'.`) : error\n })\n log(` Get repositories infos.`, 'debug')\n const repos = await octokit.paginate(octokit.rest.repos.listForUser, { username, sort: 'full_name', per_page: 100 })\n\n return { user, repos, branch }\n}\n\n/**\n * Gets contributors for a repository\n *\n * @param options - Options for retrieving contributors\n * @param options.repository - Repository information\n * @param options.token - GitHub API token\n * @returns Object with source repository and contributors list\n */\nexport async function getContributors({\n repository,\n token,\n}: {\n repository: EnhancedRepository\n token: GlobalOpts['token']\n}) {\n log(` Get contributors infos for repository '${repository.name}'.`, 'info')\n const octokit = new Octokit({ auth: token, log: quietOctokitLog })\n let repo\n try {\n repo = (await octokit.rest.repos.get({\n owner: repository.owner.login,\n repo: repository.name,\n })).data\n } catch (error) {\n // A deleted / private / rate-limited upstream must not abort the whole\n // prepare step: degrade to \"no source\" so this single fork is skipped.\n log(` Failed to get repository infos for '${repository.name}'. Error : ${formatError(error)}`, 'warn')\n return { source: undefined, contributors: [] }\n }\n if (!repo.source?.owner?.login) {\n return { source: repo.source, contributors: [] }\n }\n const contributors: Array<any> = []\n try {\n for await (const { data } of octokit.paginate.iterator(\n octokit.rest.repos.listContributors,\n {\n owner: repo.source.owner.login,\n repo: repo.parent?.name ?? repo.name,\n per_page: 100,\n },\n )) {\n contributors.push(...data)\n }\n } catch (error) {\n log(\n ` Failed to get contributors infos for repository '${repository.name}'. Error : ${formatError(error)}`,\n 'warn',\n )\n return { source: repo.source, contributors: [] }\n }\n\n return { source: repo.source, contributors }\n}\n\n/**\n * Parses `git log --format=%cI --name-only` output into a map of file path to\n * its most recent commit date\n * The log is newest-first, so the first date seen for a given file is the latest one\n *\n * @param logOutput - Raw output of the git log command\n * @returns Map of repository-relative file path to ISO commit date\n */\nexport function parseLastCommitDates(logOutput: string): Map<string, string> {\n const dates = new Map<string, string>()\n let currentDate: string | null = null\n\n for (const rawLine of logOutput.split('\\n')) {\n const line = rawLine.trim()\n if (!line) {\n continue\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(line)) {\n currentDate = line\n continue\n }\n if (currentDate && !dates.has(line)) {\n dates.set(line, currentDate)\n }\n }\n\n return dates\n}\n\n/**\n * Injects each markdown file's last Git commit date as a \"lastUpdated\" frontmatter field,\n * reading the local clone's history in a single pass\n *\n * @param git - Simple-git instance scoped to the cloned repository, with history still intact\n * @param projectDir - Directory containing the cloned repository\n * @param name - Repository name, used for logging\n */\nexport async function applyLastUpdated(git: SimpleGit, projectDir: string, name: string) {\n const files = getMdFiles([projectDir])\n if (!files.length) {\n return\n }\n\n let dates: Map<string, string>\n try {\n // core.quotePath=false keeps non-ASCII paths unescaped so they match the\n // relative() lookup below (default quoting would octal-escape them)\n const logOutput = await git.raw(['-c', 'core.quotePath=false', 'log', '--format=%cI', '--name-only'])\n dates = parseLastCommitDates(logOutput)\n } catch (error) {\n log(` Unable to read commit history for repository '${name}'. Error : ${formatError(error)}`, 'warn')\n return\n }\n\n for (const file of files) {\n const date = dates.get(relative(projectDir, file))\n if (date) {\n addLastUpdatedFrontmatter(file, date)\n }\n }\n}\n\n/**\n * Clones a repository with sparse checkout\n *\n * @param name - Repository name\n * @param url - Repository URL\n * @param projectDir - Directory to clone into\n * @param branch - Branch to clone\n * @param includes - Patterns to include in sparse checkout\n * @param lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n * @returns True when the repository was cloned successfully, false otherwise\n */\nexport async function cloneRepo(name: string, url: string, projectDir: string, branch: string, includes: string[], lastUpdated?: boolean): Promise<boolean> {\n // Refuse values Git could parse as CLI options (argument injection)\n if (url.startsWith('-') || branch.startsWith('-')) {\n log(` Refusing to clone '${name}': unsafe repository URL or branch name.`, 'error')\n return false\n }\n\n createDir(projectDir, { clean: true })\n\n try {\n // Background maintenance is disabled so no git process keeps writing into\n // .git (e.g. commit-graph files) while it is being removed below\n const git = simpleGit({ baseDir: projectDir, config: ['maintenance.auto=false', 'gc.auto=0', 'fetch.writeCommitGraph=false'] })\n await git.init()\n .addConfig('core.sparseCheckout', 'true', true, 'local')\n .addRemote('origin', url)\n\n for (const item of includes) {\n log(` Add '${item}' to '${name}' sparse-checkout file.`, 'debug')\n appendFileSync(resolve(projectDir, '.git/info/sparse-checkout'), `${item}\\n`, 'utf8')\n }\n\n log(` Clone repository '${name}'.`, 'info')\n await git.pull('origin', branch)\n\n if (lastUpdated) {\n await applyLastUpdated(git, projectDir, name)\n }\n\n if (includes.some(item => item.includes('docs'))) {\n cpSync(resolve(projectDir, 'docs'), projectDir, { recursive: true })\n rmSync(resolve(projectDir, 'docs'), { recursive: true })\n }\n rmSync(resolve(projectDir, '.git'), { recursive: true })\n return true\n } catch (error) {\n log(` Error when cloning repository '${name}': ${formatError(error)}.`, 'error')\n return false\n }\n}\n","import type { GlobalOpts } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { log } from '../utils/logger.js'\nimport type { getInfos as getGithubInfos } from './git.js'\n\n/**\n * Base URL of the GitLab REST API\n */\nexport const GITLAB_API_URL = 'https://gitlab.com/api/v4'\n\ntype User = Awaited<ReturnType<typeof getGithubInfos>>['user']\ntype Repository = Awaited<ReturnType<typeof getGithubInfos>>['repos'][number]\n\n/**\n * Parsed GitLab API response paired with its HTTP headers (used for pagination)\n */\ninterface GitlabResponse<T> {\n data: T\n headers: Headers\n}\n\n/**\n * Subset of the GitLab user/group API response consumed by DocPress\n */\ninterface GitlabUser {\n id: number\n username?: string\n path?: string\n name?: string\n bio?: string | null\n description?: string | null\n avatar_url?: string\n web_url?: string\n}\n\n/**\n * Subset of the GitLab project API response consumed by DocPress\n */\ninterface GitlabProject {\n path: string\n forked_from_project?: unknown\n visibility?: string\n default_branch?: string\n http_url_to_repo?: string\n web_url?: string\n description?: string | null\n star_count?: number\n}\n\n/**\n * Performs a GET request against the GitLab API\n *\n * @param path - API path starting with '/'\n * @param token - Optional GitLab token\n * @returns Object containing the parsed JSON body and the response headers\n * @throws Error if the request fails\n */\nasync function gitlabGet<T>(path: string, token?: GlobalOpts['token']): Promise<GitlabResponse<T>> {\n const response = await fetch(`${GITLAB_API_URL}${path}`, {\n headers: token ? { 'PRIVATE-TOKEN': token } : {},\n })\n if (!response.ok) {\n throw new Error(`GitLab API request failed for '${path}' (status: ${response.status}).`)\n }\n return { data: await response.json() as T, headers: response.headers }\n}\n\n/**\n * Fetches all pages of a GitLab API collection\n *\n * @param path - API path starting with '/' (query params allowed)\n * @param token - Optional GitLab token\n * @returns Array containing all items from all pages\n */\nasync function gitlabGetAll<T>(path: string, token?: GlobalOpts['token']): Promise<T[]> {\n const separator = path.includes('?') ? '&' : '?'\n const items: T[] = []\n let page: string | null = '1'\n\n while (page) {\n const res: GitlabResponse<T[]> = await gitlabGet<T[]>(`${path}${separator}per_page=100&page=${page}`, token)\n items.push(...res.data)\n page = res.headers.get('x-next-page') || null\n }\n return items\n}\n\n/**\n * Maps a GitLab user or group to the internal (GitHub-derived) user shape\n *\n * @param user - GitLab user or group object\n * @returns User object with the fields used by DocPress\n */\nfunction mapUser(user: GitlabUser) {\n return {\n login: user.username ?? user.path,\n name: user.name,\n bio: user.bio ?? user.description ?? null,\n avatar_url: user.avatar_url,\n html_url: user.web_url,\n } as unknown as User\n}\n\n/**\n * Maps a GitLab project to the internal (GitHub-derived) repository shape\n *\n * @param project - GitLab project object\n * @param owner - Login of the user or group that owns the project\n * @returns Repository object with the fields used by DocPress\n */\nfunction mapRepository(project: GitlabProject, owner: string) {\n return {\n name: project.path,\n owner: { login: owner },\n fork: !!project.forked_from_project,\n private: project.visibility !== 'public',\n // An empty repository has no default branch\n size: project.default_branch ? 1 : 0,\n default_branch: project.default_branch,\n clone_url: project.http_url_to_repo,\n html_url: project.web_url,\n description: project.description,\n stargazers_count: project.star_count,\n } as unknown as Repository\n}\n\n/**\n * Fetches user and repository information from GitLab\n * Falls back to the groups API when no user matches the given name\n *\n * @param options - Options for retrieving GitLab data\n * @param options.username - GitLab username or group path\n * @param options.token - GitLab API token\n * @param options.branch - Branch to use for documentation\n * @returns Object containing user information, repositories, and branch\n */\nexport async function getInfos({ username, token, branch }: Pick<FetchOpts, 'branch'> & Pick<GlobalOpts, 'token'> & { username: GlobalOpts['usernames'][number] }) {\n log(` Get infos for username '${username}'.`, 'info')\n\n const { data: users } = await gitlabGet<GitlabUser[]>(`/users?username=${encodeURIComponent(username)}`, token)\n\n if (users.length) {\n let user = users[0]\n if (token) {\n // The user detail endpoint requires authentication but provides extra fields (e.g. bio)\n user = await gitlabGet<GitlabUser>(`/users/${users[0].id}`, token).then(res => res.data).catch(() => users[0])\n }\n log(` Get repositories infos.`, 'debug')\n const projects = await gitlabGetAll<GitlabProject>(`/users/${users[0].id}/projects`, token)\n return { user: mapUser(user), repos: projects.map(project => mapRepository(project, user.username ?? username)), branch }\n }\n\n log(` No GitLab user '${username}' found, trying groups.`, 'debug')\n const group = await gitlabGet<GitlabUser>(`/groups/${encodeURIComponent(username)}?with_projects=false`, token)\n .then(res => res.data)\n .catch(() => {\n throw new Error(`No GitLab user or group found for '${username}'.`)\n })\n const projects = await gitlabGetAll<GitlabProject>(`/groups/${encodeURIComponent(username)}/projects`, token)\n return { user: mapUser(group), repos: projects.map(project => mapRepository(project, group.path ?? username)), branch }\n}\n","import { resolve } from 'node:path'\nimport { writeFileSync } from 'node:fs'\nimport type { FetchOpts, FetchOptsUser } from '../schemas/fetch.js'\nimport { checkHttpStatus, prettify, sanitizeSegment } from '../utils/functions.js'\nimport { DOCPRESS_DIR, DOCS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { cloneRepo, getInfos } from './git.js'\nimport { getInfos as getGitlabInfos } from './gitlab.js'\n\n/**\n * Enhanced repository type with DocPress-specific metadata\n */\nexport type EnhancedRepository = Awaited<ReturnType<typeof getInfos>>['repos'][number] & {\n docpress: {\n branch: string\n filtered: boolean\n includes: string[]\n projectPath: string\n raw_url: string\n replace_url: string\n /** URL/path prefix ('' or '<username>/') used to namespace multi-user runs */\n routePrefix: string\n }\n}\n\n/**\n * Web URLs of a repository for a given branch, depending on the Git provider\n */\nexport interface ProviderUrls {\n blob_url: string\n tree_url: string\n raw_url: string\n}\n\n/**\n * Builds the web URLs of a repository for a given branch and Git provider\n *\n * @param repo - Repository information\n * @param branch - Branch to build URLs for\n * @param gitProvider - Git provider used to retrieve data\n * @returns Object containing blob, tree and raw base URLs\n */\nexport function getProviderUrls(repo: Awaited<ReturnType<typeof getInfos>>['repos'][number], branch: FetchOpts['branch'], gitProvider?: FetchOpts['gitProvider']): ProviderUrls {\n if (gitProvider === 'gitlab') {\n const base = repo.html_url ?? `https://gitlab.com/${repo.owner.login}/${repo.name}`\n return {\n blob_url: `${base}/-/blob/${branch}`,\n tree_url: `${base}/-/tree/${branch}`,\n raw_url: `${base}/-/raw/${branch}`,\n }\n }\n const base = repo.html_url ?? `https://github.com/${repo.owner.login}/${repo.name}`\n return {\n blob_url: `${base}/blob/${branch}`,\n tree_url: `${base}/tree/${branch}`,\n raw_url: `https://raw.githubusercontent.com/${repo.owner.login}/${repo.name}/${branch}`,\n }\n}\n\n/**\n * Checks for the existence of documentation files in a repository\n *\n * @param urls - Web URLs of the repository for the target branch\n * @returns Object containing HTTP status codes for different documentation paths\n */\nexport async function checkDoc(urls: ProviderUrls) {\n const [rootReadmeStatus, docsFolderStatus, docsReadmeStatus] = await Promise.all([\n checkHttpStatus(`${urls.blob_url}/README.md`),\n checkHttpStatus(`${urls.tree_url}/docs`),\n checkHttpStatus(`${urls.blob_url}/docs/01-readme.md`),\n ])\n\n return {\n rootReadmeStatus,\n docsFolderStatus,\n docsReadmeStatus,\n }\n}\n\n/**\n * Fetches documentation from a user's repositories\n *\n * @param options - Options for fetching documentation\n * @param options.username - Git provider username\n * @param options.branch - Branch to use for documentation\n * @param options.reposFilter - Optional filter for repositories\n * @param options.gitProvider - Git provider used to retrieve data\n * @param options.token - Git provider API token\n * @param options.lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n * @param options.multiUser - Whether the run spans multiple usernames; namespaces paths/routes by username to avoid collisions\n */\nexport async function fetchDoc({ username, branch, reposFilter, gitProvider, token, lastUpdated, multiUser }: FetchOptsUser & { multiUser?: boolean }) {\n const getProviderInfos = gitProvider === 'gitlab' ? getGitlabInfos : getInfos\n const { user, repos, branch: resolvedBranch } = await getProviderInfos({ username, token, branch })\n // In multi-user runs, namespace on-disk paths and site routes by username so\n // two users owning a same-named repository do not collide (data loss / merged routes)\n const routePrefix = multiUser ? `${sanitizeSegment(username)}/` : ''\n const { repos: enhancedRepos } = await generateInfos(user, repos, resolvedBranch, reposFilter, gitProvider, routePrefix)\n await getDoc(enhancedRepos, reposFilter, lastUpdated)\n}\n\n/**\n * Enhances repository information with DocPress metadata\n *\n * @param repos - List of repositories from Git provider\n * @param branch - Branch to use for documentation\n * @param reposFilter - Optional filter for repositories\n * @returns Array of enhanced repositories with DocPress metadata\n */\nexport async function enhanceRepositories(repos: Awaited<ReturnType<typeof getInfos>>['repos'], branch?: FetchOpts['branch'], reposFilter?: FetchOpts['reposFilter'], gitProvider?: FetchOpts['gitProvider'], routePrefix: string = '') {\n const enhancedRepos: EnhancedRepository[] = []\n\n await Promise.all(\n repos.map(async (repo) => {\n const computedBranch = branch ?? repo.default_branch ?? 'main'\n const projectPath = resolve(DOCS_DIR, `${routePrefix}${prettify(sanitizeSegment(repo.name), { removeDot: true })}`)\n const filtered = isRepoFiltered(repo, reposFilter)\n const urls = getProviderUrls(repo, computedBranch, gitProvider)\n let includes: string[] = []\n\n if (!repo.fork && !repo.private && !filtered) {\n includes = await getSparseCheckout(repo, computedBranch, gitProvider, urls)\n }\n\n enhancedRepos.push({\n ...repo,\n docpress: {\n filtered,\n branch: computedBranch,\n includes,\n projectPath,\n raw_url: urls.raw_url,\n replace_url: urls.tree_url,\n routePrefix,\n },\n })\n }),\n )\n\n return enhancedRepos\n}\n\n/**\n * Determines which files to include in the sparse checkout based on repository structure\n *\n * @param repo - Repository information\n * @param branch - Branch to check for documentation\n * @param gitProvider - Git provider used to retrieve data\n * @param urls - Optional pre-computed provider URLs to avoid recomputing them\n * @returns Array of file patterns to include in sparse checkout\n */\nexport async function getSparseCheckout(repo: Awaited<ReturnType<typeof getInfos>>['repos'][number], branch: FetchOpts['branch'], gitProvider?: FetchOpts['gitProvider'], urls?: ProviderUrls) {\n const docsStatus = await checkDoc(urls ?? getProviderUrls(repo, branch, gitProvider))\n\n const includes: string[] = []\n if (Object.values(docsStatus).every(status => status !== 200) || !repo.size) {\n return []\n }\n if (docsStatus.docsFolderStatus === 200) {\n includes.push('docs/*')\n }\n if (docsStatus.docsFolderStatus !== 200 || docsStatus.docsReadmeStatus !== 200) {\n includes.push('README.md', '!*/**/README.md')\n }\n return includes\n}\n\n/**\n * Generates and saves user and repository information\n *\n * @param user - User information from Git provider\n * @param repos - List of repositories from Git provider\n * @param branch - Branch to use for documentation\n * @param reposFilter - Optional filter for repositories\n * @param gitProvider - Git provider used to retrieve data\n * @returns Object containing user and enhanced repository information\n */\nexport async function generateInfos(user: Awaited<ReturnType<typeof getInfos>>['user'], repos: Awaited<ReturnType<typeof getInfos>>['repos'], branch?: FetchOpts['branch'], reposFilter?: FetchOpts['reposFilter'], gitProvider?: FetchOpts['gitProvider'], routePrefix: string = '') {\n writeFileSync(`${DOCPRESS_DIR}/user-${sanitizeSegment(user.login)}.json`, JSON.stringify(user, null, 2))\n\n const enhancedRepos = await enhanceRepositories(repos, branch, reposFilter, gitProvider, routePrefix)\n writeFileSync(`${DOCPRESS_DIR}/repos-${sanitizeSegment(user.login)}.json`, JSON.stringify(enhancedRepos, null, 2))\n\n return { user, repos: enhancedRepos }\n}\n\n/**\n * Fetches documentation from repositories\n *\n * @param repos - List of enhanced repositories\n * @param reposFilter - Optional filter for repositories\n * @param lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n */\nexport async function getDoc(repos?: EnhancedRepository[], reposFilter?: FetchOpts['reposFilter'], lastUpdated?: boolean) {\n if (!repos) {\n log(` No repositories to process.`, 'warn')\n return\n }\n\n const results = await Promise.all(\n repos\n .filter(repo => !isRepoFiltered(repo, reposFilter))\n .map(async (repo) => {\n const success = await cloneRepo(repo.name, repo.clone_url as string, repo.docpress.projectPath, repo.docpress.branch, repo.docpress.includes, lastUpdated)\n return { name: repo.name, success }\n }),\n )\n\n if (!results.length) {\n log(` No repository matched the current filters.`, 'warn')\n return\n }\n\n const failed = results.filter(({ success }) => !success).map(({ name }) => name)\n const succeeded = results.length - failed.length\n if (failed.length) {\n log(` Cloned ${succeeded}/${results.length} repository(ies), ${failed.length} failed: ${failed.join(', ')}.`, 'warn')\n } else {\n log(` Cloned ${succeeded}/${results.length} repository(ies).`, 'success')\n }\n}\n\n/**\n * Determines if a repository should be filtered out based on filter rules\n *\n * @param repo - Repository information\n * @param reposFilter - Optional filter for repositories\n * @returns True if the repository should be filtered out, false otherwise\n */\nexport function isRepoFiltered(repo: EnhancedRepository | Awaited<ReturnType<typeof getInfos>>['repos'][number], reposFilter?: FetchOpts['reposFilter']) {\n if ('docpress' in repo && !repo.docpress.includes.length) {\n return true\n }\n\n const inclusions = reposFilter?.filter((filter: string) => !filter.startsWith('!')) ?? []\n const hasOnlyExclusions = reposFilter?.every((filter: string) => filter.startsWith('!'))\n const isExcluded = reposFilter?.filter((filter: string) => filter.startsWith('!'))\n .some((filter: string) => repo.name === filter.substring(1))\n\n const isIncluded = !reposFilter\n || inclusions.includes(repo.name)\n || (repo.fork && !isExcluded)\n || (hasOnlyExclusions && !isExcluded)\n\n const isFiltered = isExcluded || !isIncluded\n\n return !(!!repo.clone_url && !repo.private && !isFiltered)\n}\n","import { createOption } from 'commander'\nimport { cliSchema } from '../schemas/global.js'\n\n/**\n * Global command line options shared across all commands\n */\nexport const globalOpts = [\n createOption('-C, --config <string>', cliSchema.shape.config.description),\n createOption('--log-level <string>', cliSchema.shape.logLevel.description),\n createOption('-T, --token <string>', cliSchema.shape.token.description),\n createOption('-U, --usernames <string>', cliSchema.shape.usernames.description),\n]\n","import { createCommand, createOption } from 'commander'\nimport { createDir, formatDuration, formatError } from '../utils/functions.js'\nimport { DOCPRESS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { addOptions, explicitOptions, parseOptions } from '../utils/commands.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { fetchDoc } from '../lib/fetch.js'\nimport { configSchema } from '../schemas/global.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Command name for the fetch operation\n */\nconst cmdName = 'fetch'\n\n/**\n * Command line options specific to the fetch command\n */\nexport const fetchOpts = [\n createOption('-b, --branch <string>', configSchema.shape.branch.description),\n createOption('-g, --git-provider <string>', configSchema.shape.gitProvider.description)\n .default(configSchema.shape.gitProvider.def.defaultValue),\n createOption('-l, --last-updated', configSchema.shape.lastUpdated.description),\n createOption('-r, --repos-filter <string>', configSchema.shape.reposFilter.description),\n]\n\n/**\n * The fetch command definition for Commander\n */\nexport const fetchCmd = addOptions(createCommand(cmdName), [...fetchOpts, ...globalOpts])\n .description('Fetch docs with the given username(s) and git provider.')\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions(cmdName, explicitOptions(cmd, opts))\n await main(parsedOpts)\n })\n\n/**\n * Main function to execute the fetch command\n *\n * @param opts - Validated fetch options\n */\nexport async function main(opts: FetchOpts) {\n const { usernames, reposFilter, token, branch, gitProvider, lastUpdated } = opts\n log(`\\n-> Start fetching documentation files. This may take a moment, especially for larger repositories.`, 'info')\n const start = Date.now()\n\n createDir(DOCPRESS_DIR, { clean: true })\n const failedUsers: string[] = []\n for (const username of usernames) {\n // With multiple users, filters are scoped as '<username>/<repo>' or '!<username>/<repo>'\n const finalRF = usernames.length > 1\n ? reposFilter?.filter((rf: string) => rf.replace(/^!/, '').startsWith(`${username}/`)).map((rf: string) => rf.replace(`${username}/`, ''))\n : reposFilter\n try {\n await fetchDoc({ username, branch, reposFilter: finalRF, gitProvider, token, lastUpdated, multiUser: usernames.length > 1 })\n } catch (error) {\n failedUsers.push(username)\n log(` Failed to fetch documentation for '${username}': ${formatError(error)}`, 'error')\n }\n }\n\n if (failedUsers.length === usernames.length) {\n throw new Error(`Failed to fetch documentation for all requested username(s): ${failedUsers.join(', ')}.`)\n }\n\n const succeeded = usernames.length - failedUsers.length\n log(` Fetched documentation for ${succeeded}/${usernames.length} username(s) in ${formatDuration(Date.now() - start)}.`, failedUsers.length ? 'warn' : 'success')\n}\n","import { build as vitepressBuild } from 'vitepress'\nimport type { UserConfig } from 'vitepress'\nimport { withMermaid } from 'vitepress-plugin-mermaid'\nimport { createCommand } from 'commander'\nimport { addOptions } from '../utils/commands.js'\nimport { DOCPRESS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { formatDuration, formatError } from '../utils/functions.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Suppress specific Vue warnings during the build process\n *\n * @param callback - Function to execute while warnings are suppressed\n * @returns The result of the callback function\n */\nexport async function suppressVueWarnings<T>(callback: () => Promise<T>): Promise<T> {\n // Store the original console.warn\n const originalWarn = console.warn\n\n // Override console.warn to suppress two specific, noisy Vue/Vitepress warnings\n // and forward everything else, untouched, to the real console.warn.\n //\n // These must NOT be routed through log(): log() writes via console.warn, which\n // is this very override, so forwarding a non-suppressed warning through it would\n // recurse until the stack overflows and aborts the whole Vitepress build. Vite's\n // \"chunks are larger than 500 kB\" warning is exactly such a case, which is why\n // the build failed with \"[vite:reporter] Maximum call stack size exceeded\".\n console.warn = function (...args) {\n if (\n args.length > 0\n && typeof args[0] === 'string'\n && (args[0].includes('[Vue warn]: Invalid watch source:')\n || args[0].includes('{ open: false }'))\n ) {\n // Skip this warning\n return\n }\n // Pass other warnings straight to the original console.warn\n originalWarn.apply(console, args)\n }\n\n try {\n // Execute the callback with warnings suppressed\n return await callback()\n } finally {\n // Restore the original console.warn\n console.warn = originalWarn\n }\n}\n\n/**\n * Command name for the build operation\n */\nconst cmdName = 'build'\n\n/**\n * The build command definition for Commander\n */\nexport const buildCmd = addOptions(createCommand(cmdName), globalOpts)\n .description('Build vitepress website.')\n .action(async (_opts) => {\n await main()\n })\n\n/**\n * Main function to execute the build command\n * Builds the VitePress static site\n */\nexport async function main() {\n log(`\\n-> Start building Vitepress website.\\n\\n`, 'info')\n const start = Date.now()\n\n try {\n // Build VitePress with Vue warnings suppressed\n await suppressVueWarnings(async () => {\n await vitepressBuild(DOCPRESS_DIR, {\n onAfterConfigResolve(siteConfig) {\n withMermaid(siteConfig as unknown as UserConfig)\n },\n })\n })\n } catch (error) {\n // Re-thrown (not logged/exited here) so cli.ts's single top-level catch\n // is the only place that reports a failure and sets the exit code.\n throw new Error(`Docpress build failed: ${formatError(error)}`)\n }\n\n log(`\\n\\nDocpress build succeeded in ${formatDuration(Date.now() - start)}.`, 'success')\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createDir } from './functions.js'\n\n/**\n * Generates a file by copying content from source to destination\n *\n * @param src - Path to the source file\n * @param dest - Path to the destination file\n */\nexport function generateFile(src: string, dest: string) {\n const content = readFileSync(src, { encoding: 'utf8' })\n createDir(dirname(dest))\n writeFileSync(dest, content)\n}\n","import type { DefaultTheme, defineConfig } from 'vitepress'\nimport { deepMerge } from '../utils/functions.js'\nimport type { Page } from './prepare.js'\n\n/**\n * Default VitePress configuration\n */\nconst defaultVitepressConfig: ReturnType<typeof defineConfig> = {\n base: '/',\n lang: 'en-US',\n vite: {\n publicDir: '../public',\n },\n title: 'Home',\n description: 'Docpress',\n srcDir: './docs',\n cleanUrls: false,\n ignoreDeadLinks: 'localhostLinks',\n themeConfig: {\n outline: [2, 3],\n search: {\n provider: 'local',\n options: {\n translations: {\n button: {\n buttonText: 'Search...',\n buttonAriaLabel: 'Search',\n },\n modal: {\n backButtonTitle: 'erase search',\n displayDetails: 'show details',\n noResultsText: 'No results for : ',\n resetButtonTitle: 'cancel search',\n footer: {\n selectText: 'go to',\n navigateText: 'navigate in results',\n closeText: 'close',\n },\n },\n },\n },\n },\n },\n}\n\n/**\n * Generates VitePress configuration by merging default and custom configs\n *\n * @param sidebar - Sidebar configuration, either one flat list or one sidebar per route\n * @param nav - Navigation configuration\n * @param vitepressConfig - Optional custom VitePress configuration\n * @returns Merged VitePress configuration\n */\nexport function getVitepressConfig(sidebar: DefaultTheme.Sidebar, nav: Page[], vitepressConfig?: ReturnType<typeof defineConfig>): Partial<ReturnType<typeof defineConfig>> {\n const config = vitepressConfig\n ? deepMerge(defaultVitepressConfig, vitepressConfig, { themeConfig: { sidebar, nav } })\n : deepMerge(defaultVitepressConfig, { themeConfig: { sidebar, nav } })\n return config\n}\n","import type { DefaultTheme, defineConfig } from 'vitepress'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport type { EnhancedRepository } from './fetch.js'\nimport type { getInfos } from './git.js'\nimport { appendFileSync, cpSync, existsSync, readdirSync, renameSync, writeFileSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { basename, dirname, parse, resolve } from 'node:path'\nimport YAML from 'yaml'\nimport { DOCPRESS_DIR, DOCS_DIR, FORKS_FILE, INDEX_FILE, TEMPLATE_THEME, VITEPRESS_CONFIG, VITEPRESS_THEME, VITEPRESS_USER_THEME } from '../utils/const.js'\nimport { createDir, extractFiles, formatError, getMdFiles, getUserInfos, getUserRepos, isFile, prettify, replaceInternalMdLinks, replaceReadmePath, replaceRelativePath } from '../utils/functions.js'\nimport { log } from '../utils/logger.js'\nimport { generateFile } from '../utils/templates.js'\nimport { getContributors } from './git.js'\nimport { getVitepressConfig } from './vitepress.js'\n\n/**\n * Represents a navigation page item with text and link\n */\nexport interface Page {\n text: string\n link: string\n}\n\n/**\n * Represents a sidebar project with nested items\n */\nexport interface SidebarProject {\n text: string\n collapsed?: boolean\n items: (Page | SidebarProject)[]\n}\n\n/**\n * Collapse behaviour applied to generated sidebar groups, mirroring Vitepress\n * semantics: `true` collapses groups by default, `false` expands them and\n * `null` makes them plain, non-collapsible sections\n */\nexport type SidebarCollapsed = boolean | null\n\n/**\n * Shape of the generated sidebar: `single` emits one flat list holding every\n * repository, `multi` emits one sidebar per repository keyed by its route so\n * each project only shows its own pages\n */\nexport type SidebarMode = 'single' | 'multi'\n\n/**\n * Options driving sidebar generation\n */\nexport interface SidebarOpts {\n mode?: SidebarMode\n collapsed?: SidebarCollapsed\n}\n\n/**\n * Builds the Vitepress `collapsed` property for a sidebar group, omitting it\n * entirely when groups should not be collapsible\n *\n * @param collapsed - Configured collapse behaviour\n * @returns An object holding the `collapsed` key, or an empty object\n */\nfunction collapsedProp(collapsed: SidebarCollapsed) {\n return collapsed === null ? {} : { collapsed }\n}\n\n/**\n * Represents a feature to display on the index page\n */\nexport interface Feature {\n title: string\n details: string\n link: string\n}\n\n/**\n * Represents the structure of the VitePress index page\n */\nexport interface Index {\n layout: string\n hero: {\n name: string\n tagline?: string\n }\n /** List of features to display */\n features: Feature[]\n}\n\n/**\n * Prepares the documentation structure for a user's repositories\n *\n * @param options - Options for documentation preparation\n * @param options.extraHeaderPages - Array of paths to additional header pages\n * @param options.extraPublicContent - Array of paths to additional public content\n * @param options.extraTheme - Array of paths to additional theme files\n * @param options.vitepressConfig - Path to the VitePress configuration file\n * @param options.forks - Flag to include forked repositories\n * @param options.gitProvider - Git provider used to retrieve data\n * @param options.lastUpdated - Flag to display each page's last Git commit date, computed by the fetch step\n * @param options.sidebarMode - Shape of the generated sidebar, one flat list or one sidebar per repository\n * @param options.sidebarCollapsed - Collapse behaviour applied to generated sidebar groups\n * @param options.token - Git provider token for API access\n * @param options.username - Git provider username to fetch repositories for\n * @param options.websiteTitle - Custom title for the documentation website\n * @param options.websiteTagline - Custom tagline for the documentation website\n */\nexport async function prepareDoc({ extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, token, username, websiteTitle, websiteTagline }: Omit<PrepareOpts, 'usernames' | 'branch' | 'reposFilter'> & { username: PrepareOpts['usernames'][number] }) {\n // The forks page relies on matching contributors by login, which the GitLab API does not expose\n const forksEnabled = forks && gitProvider !== 'gitlab'\n if (forks && !forksEnabled) {\n log(` The forks page is not supported with the 'gitlab' provider, skipping.`, 'warn')\n }\n const user = getUserInfos(username)\n const repositories = getUserRepos(username)\n .reduce(({ internals, forks }: { internals: EnhancedRepository[], forks: EnhancedRepository[] }, cur) => {\n const { clone_url, private: privateRepo, fork, docpress } = cur\n if (clone_url && !privateRepo && !docpress.filtered) {\n if (!fork && docpress.includes.length) {\n return { internals: [...internals, cur], forks }\n } else if (fork) {\n return { internals, forks: [...forks, cur] }\n }\n }\n return { internals, forks }\n }, { internals: [], forks: [] })\n\n const websiteInfos = { title: websiteTitle, tagline: websiteTagline }\n const { index, sidebar } = transformDoc(repositories.internals, user, websiteInfos, { mode: sidebarMode, collapsed: sidebarCollapsed })\n\n let finalSB\n let finalIndex\n if (existsSync(INDEX_FILE) && existsSync(VITEPRESS_CONFIG)) {\n const actualConfig = await parseVitepressConfig(VITEPRESS_CONFIG)\n const actualIndex = await parseVitepressIndex(INDEX_FILE)\n finalSB = mergeSidebars(actualConfig.themeConfig?.sidebar, sidebar)\n finalIndex = {\n ...index,\n features: [\n ...actualIndex.features,\n ...index.features,\n ].sort((a, b) => a.title.localeCompare(b.title)),\n }\n } else {\n finalSB = sidebar\n finalIndex = index\n }\n const nav: Page[] = []\n\n if (extraHeaderPages) {\n const pages = addExtraPages(extraHeaderPages)\n if (forksEnabled) {\n nav.push(...pages.filter(p => p.link !== '/forks'))\n } else {\n nav.push(...pages)\n }\n }\n if (extraPublicContent) {\n log(` Add extras Vitepress public folder content.`, 'info')\n addContent(extraPublicContent, resolve(DOCPRESS_DIR, 'public'))\n }\n if (extraTheme) {\n log(` Add extras Vitepress theme files.`, 'info')\n addContent(extraTheme, resolve(VITEPRESS_USER_THEME))\n }\n if (forksEnabled && username.length) {\n log(` Add fork page to display external contributions.`, 'info')\n await processForks(repositories.forks, username, token)\n nav.push({ text: 'Forks', link: '/forks' })\n }\n\n // Vitepress only renders the last updated date once its own \"lastUpdated\" flag is on;\n // default it to true when the docpress equivalent is enabled, without overriding an explicit user choice\n const finalVitepressConfig = lastUpdated ? { lastUpdated: true, ...vitepressConfig } : vitepressConfig\n const config = getVitepressConfig(finalSB, nav, finalVitepressConfig)\n\n generateVitepressFiles(config, finalIndex)\n}\n\n/**\n * Adds a source reference section to a markdown file\n *\n * @param repoUrl - URL of the GitHub repository\n * @param outputPath - Path to the markdown file where sources will be added\n */\nexport function addSources(repoUrl: string, outputPath: string) {\n const fileName = basename(outputPath)\n const title = prettify(fileName, { mode: 'lowercase', removeIdx: true }) === 'readme.md' ? '\\n## Sources' : '# Sources'\n\n const sourcesContent = `${title}\\n\\nTake a look at the [project sources](${repoUrl}).\\n`\n\n appendFileSync(outputPath, sourcesContent, 'utf8')\n}\n\n/**\n * Information about website title and tagline\n */\nexport interface WebsiteInfos {\n title?: string\n tagline?: string\n}\n\n/**\n * Generates the index page content for the documentation site\n *\n * @param features - Array of features to display on the homepage\n * @param user - User information retrieved from GitHub\n * @param websiteInfos - Custom title and tagline information\n * @returns An object with index page configuration\n */\nexport function generateIndex(features: Feature[], user: ReturnType<typeof getUserInfos>, websiteInfos: WebsiteInfos) {\n const { name, login, bio } = user\n const { title, tagline } = websiteInfos\n\n const hero = title\n ? { name: title, tagline }\n : { name: name ? `${name}'s projects` : `${login}'s projects`, tagline: bio ?? 'Robots are everywhere 🤖' }\n\n return {\n layout: 'home',\n hero,\n features,\n }\n}\n\n/**\n * Generates feature cards for the homepage\n *\n * @param repoName - Name of the repository\n * @param description - Description of the repository\n * @param features - Optional existing features to append to\n * @returns Array of feature objects for the homepage\n */\nexport function generateFeatures(repoName: string, description: string, features?: Feature[], routePrefix: string = '') {\n const content = {\n title: prettify(repoName, { mode: 'capitalize', replaceDash: true }),\n details: description,\n link: `/${routePrefix}${prettify(repoName, { removeDot: true })}/introduction`,\n }\n\n return features ? [...features, content] : [content]\n}\n\n/**\n * Generates a sidebar project entry for the documentation navigation\n *\n * @param repoName - Name of the repository\n * @param sidebarPages - Array of sidebar pages to include in this project\n * @param collapsed - Collapse behaviour applied to the generated group\n * @returns A sidebar project configuration object\n */\nexport function generateSidebarProject(repoName: string, sidebarPages: (SidebarProject | Page)[], collapsed: SidebarCollapsed = true) {\n return {\n text: prettify(repoName, { mode: 'capitalize', replaceDash: true }),\n ...collapsedProp(collapsed),\n items: sidebarPages,\n }\n}\n\n/**\n * Generates sidebar page entries for the documentation navigation\n *\n * @param repoName - Name of the repository\n * @param fileName - Name of the file\n * @param sidebarPages - Optional existing sidebar pages to append to\n * @returns Array of page objects for the sidebar\n */\nexport function generateSidebarPages(repoName: string, fileName: string, sidebarPages?: Page[]) {\n const content = {\n text: fileName === 'introduction' ? 'Introduction' : prettify(fileName, { mode: 'capitalize', replaceDash: true }),\n link: `/${prettify(repoName, { removeDot: true })}/${fileName}`,\n }\n\n return sidebarPages ? [...sidebarPages, content] : [content]\n}\n\n/**\n * Generates sidebar items from a repository's file structure\n *\n * @param repository - Repository information\n * @param obj - Object representing the file tree structure\n * @param collapsed - Collapse behaviour applied to generated folder groups\n * @returns Array of sidebar items (projects and pages)\n */\nexport function generateSidebarItems(repository: EnhancedRepository, obj: any, collapsed: SidebarCollapsed = true): (SidebarProject | Page)[] {\n return Object.entries(obj).flatMap(([key, value]): (SidebarProject | Page)[] => {\n if (key === '$') {\n if (Array.isArray(value)) {\n return value.map((element) => {\n const file = resolve(repository.docpress.projectPath, element)\n let filename = prettify(basename(file), { mode: 'lowercase', removeIdx: true })\n if (filename === 'readme.md') {\n filename = 'introduction.md'\n }\n if (filename !== basename(file)) {\n renameSync(file, resolve(dirname(file), filename))\n }\n\n return {\n text: parse(filename).name === 'introduction'\n ? 'Introduction'\n : prettify(filename, { mode: 'capitalize', replaceDash: true, removeExt: true }),\n link: `/${repository.docpress.routePrefix ?? ''}${prettify(repository.name, { removeDot: true })}/${parse(filename).name}`,\n } as Page\n })\n }\n return []\n } else if (typeof value === 'object') {\n // Folders carry the ordering prefix for the same reason files do, and it is\n // stripped the same way. Without this a folder is ordered alphabetically and\n // nothing can change that, so a project whose sections have a reading order —\n // install before configure before reference — cannot express it at all, while\n // the files inside those sections can. The prefix also has to leave the path,\n // not just the label: the route is built from the folder name, so a group\n // reading 'Guide' whose pages live under '01-guide' would link into nothing.\n const folder = prettify(key, { removeIdx: true })\n const source = resolve(repository.docpress.projectPath, key)\n const renamed = resolve(repository.docpress.projectPath, folder)\n if (folder !== key) {\n renameSync(source, renamed)\n }\n\n return [{\n text: prettify(folder, { mode: 'capitalize', replaceDash: true }),\n ...collapsedProp(collapsed),\n items: generateSidebarItems({\n ...repository,\n name: `${repository.name}/${folder}`,\n // Descend into the subfolder so file renames target the nested file and\n // not a same-named file at the repository root\n docpress: { ...repository.docpress, projectPath: renamed },\n }, value, collapsed),\n } as SidebarProject]\n }\n\n return []\n })\n}\n\n/**\n * Builds a tree structure from an array of file paths\n *\n * @param files - Array of file paths to be organized into a tree\n * @returns A nested object representing the directory structure\n */\nexport function buildTree(files: string[]): any {\n return files.reduce((tree, file) => {\n const [first, ...rest] = file.split('/')\n if (!rest.length) {\n // eslint-disable-next-line dot-notation\n tree['$'] = [...(tree['$'] || []), first]\n } else {\n tree[first] = buildTree([\n ...(tree[first] ? flattenTree(tree[first], '') : []),\n rest.join('/'),\n ])\n }\n return tree\n }, {} as Record<string, any>)\n}\n\n/**\n * Flattens a nested tree structure into an array of file paths\n *\n * @param subtree - The tree object to flatten\n * @param prefix - Optional path prefix to prepend to each result\n * @returns An array of file paths\n */\nexport function flattenTree(subtree: any, prefix = ''): string[] {\n return Object.entries(subtree).flatMap(([key, value]) => {\n if (key === '$') {\n if (Array.isArray(value)) {\n return value.map((v: string) => (prefix ? `${prefix}/${v}` : v))\n } else {\n return []\n }\n }\n return flattenTree(value, prefix ? `${prefix}/${key}` : key)\n })\n}\n\n/**\n * Reorders sidebar items to ensure 'Sources' appears last\n *\n * @param arr - Array of sidebar items to reorder\n * @returns Reordered array with 'Sources' as the last item\n */\nexport function moveSourcesLast(arr: (SidebarProject | Page)[]) {\n if (!Array.isArray(arr)) {\n return arr\n }\n const sourcesIdx = arr.findIndex(item => item.text === 'Sources')\n if (sourcesIdx === -1) {\n return arr\n }\n const [sources] = arr.splice(sourcesIdx, 1)\n arr.push(sources)\n return arr\n}\n\n/**\n * Deepest sidebar level Vitepress renders. Its `VPSidebarGroup` mounts top level\n * items at depth 0 and `VPSidebarItem` only recurses while `depth < 5`, so the\n * children of a group sitting at this depth are silently dropped\n */\nconst MAX_SIDEBAR_DEPTH = 5\n\n/**\n * Finds sidebar groups nested so deeply that Vitepress will not render their\n * children, so the caller can warn instead of silently losing navigation\n *\n * @param items - Sidebar items to inspect\n * @param depth - Depth at which those items are rendered\n * @param trail - Group texts walked so far, used to build a readable path\n * @returns Paths of the groups whose children will not be displayed\n */\nexport function findHiddenSidebarPaths(items: (SidebarProject | Page)[], depth: number, trail: string[] = []): string[] {\n return items.flatMap((item) => {\n if (!('items' in item) || !item.items?.length) {\n return []\n }\n const path = [...trail, item.text]\n return depth >= MAX_SIDEBAR_DEPTH\n ? [path.join('/')]\n : findHiddenSidebarPaths(item.items, depth + 1, path)\n })\n}\n\n/**\n * Merges a previously generated sidebar with the one built by the current run,\n * used to accumulate repositories across usernames\n *\n * Flat sidebars are concatenated and sorted by group text, route-keyed sidebars\n * are merged by key with the current run winning. When the two shapes disagree,\n * the flat side is filed under the root key so nothing is silently dropped\n *\n * @param previous - Sidebar read back from the config written by a previous run\n * @param current - Sidebar generated by the current run\n * @returns The merged sidebar\n */\nexport function mergeSidebars(previous: DefaultTheme.Sidebar | undefined, current: DefaultTheme.Sidebar): DefaultTheme.Sidebar {\n if (!previous) {\n return current\n }\n if (Array.isArray(previous) && Array.isArray(current)) {\n return [...previous, ...current]\n .toSorted((a, b) => (a.text ?? '').localeCompare(b.text ?? ''))\n }\n const toMulti = (sidebar: DefaultTheme.Sidebar): DefaultTheme.SidebarMulti =>\n Array.isArray(sidebar) ? { '/': sidebar } : sidebar\n\n return sortSidebarRoutes({ ...toMulti(previous), ...toMulti(current) })\n}\n\n/**\n * Sorts the route keys of a multi sidebar so generated configurations stay\n * stable between runs regardless of repository processing order\n *\n * @param sidebarByRoute - Multi sidebar keyed by repository route\n * @returns The same sidebar with alphabetically ordered route keys\n */\nexport function sortSidebarRoutes(sidebarByRoute: DefaultTheme.SidebarMulti): DefaultTheme.SidebarMulti {\n return Object.fromEntries(\n Object.entries(sidebarByRoute).toSorted(([routeA], [routeB]) => routeA.localeCompare(routeB)),\n )\n}\n\n/**\n * Transforms repository data into documentation structure\n *\n * @param repositories - Array of enhanced repositories\n * @param user - User information retrieved from GitHub\n * @param websiteInfos - Custom title and tagline information\n * @param sidebarOpts - Options driving sidebar shape and collapse behaviour\n * @returns Object containing sidebar and index page configurations\n */\nexport function transformDoc(repositories: EnhancedRepository[], user: ReturnType<typeof getUserInfos>, websiteInfos: WebsiteInfos, sidebarOpts: SidebarOpts = {}) {\n const { mode = 'single', collapsed = true } = sidebarOpts\n const features: Feature[] = []\n const sidebar: SidebarProject[] = []\n const sidebarByRoute: DefaultTheme.SidebarMulti = {}\n\n for (const repository of repositories) {\n log(` Replace urls for repository '${repository.name}'.`, 'info')\n getMdFiles([repository.docpress.projectPath]).forEach((file) => {\n log(` Processing file '${basename(file)}' for repository '${repository.name}'.`, 'debug')\n replaceRelativePath(file, repository.docpress.replace_url)\n\n if (basename(file).toLowerCase() === 'readme.md') {\n replaceReadmePath(file, repository.docpress.replace_url)\n }\n // Runs last so remaining relative links are rewritten to match the\n // renamed files (index prefix stripped, lowercased, readme -> introduction)\n replaceInternalMdLinks(file)\n })\n\n log(` Generate sidebar for repository '${repository.name}'.`, 'info')\n const projectFiles = readdirSync(repository.docpress.projectPath, { recursive: true })\n .filter((file) => {\n const filePath = resolve(repository.docpress.projectPath, file.toString())\n // Name check first, then a stat that tolerates files vanishing between\n // the directory scan and the check (e.g. leftover git lock files)\n return basename(filePath).endsWith('.md') && isFile(filePath)\n })\n .sort() as string[]\n\n log(` Add sources for repository '${repository.name}'.`, 'info')\n let sourceFile\n if (projectFiles.length > 1) {\n sourceFile = resolve(repository.docpress.projectPath, 'sources.md')\n projectFiles.push('sources.md')\n } else if (projectFiles.length === 1) {\n sourceFile = resolve(repository.docpress.projectPath, projectFiles[0])\n }\n if (sourceFile) {\n addSources(repository.html_url, sourceFile)\n } else {\n log(` No markdown files found for repository '${repository.name}', skipping sources.`, 'warn')\n }\n\n const projectTree = buildTree(projectFiles)\n const sidebarItems = moveSourcesLast(generateSidebarItems(repository, projectTree, collapsed))\n\n // Single mode wraps each repository in its own group, which costs one level\n const hidden = findHiddenSidebarPaths(sidebarItems, mode === 'multi' ? 0 : 1)\n if (hidden.length) {\n log(` Sidebar for repository '${repository.name}' nests deeper than Vitepress renders, pages under ${hidden.map(path => `'${path}'`).join(', ')} will not appear in the sidebar.`, 'warn')\n }\n\n if (mode === 'multi') {\n // Vitepress picks the sidebar whose key is the longest prefix of the current\n // route, so keying on the repository route scopes it to that project alone\n sidebarByRoute[`/${repository.docpress.routePrefix ?? ''}${prettify(repository.name, { removeDot: true })}/`] = sidebarItems\n } else {\n sidebar.push(generateSidebarProject(prettify(repository.name, { removeDot: true }), sidebarItems, collapsed))\n }\n features.push(...generateFeatures(prettify(repository.name, { removeDot: true }), repository.description || '', undefined, repository.docpress.routePrefix ?? ''))\n }\n\n log(` Generate index content.`, 'info')\n const index = generateIndex(features.toSorted((a, b) => a.title.localeCompare(b.title)), user, websiteInfos)\n return {\n sidebar: mode === 'multi'\n ? sortSidebarRoutes(sidebarByRoute)\n : sidebar.toSorted((a, b) => a.text.localeCompare(b.text)),\n index,\n }\n}\n\n/**\n * Adds extra pages to the documentation from specified paths\n *\n * @param paths - Array of paths to markdown files\n * @returns Array of navigation page objects\n */\nexport function addExtraPages(paths: string[]) {\n const files = getMdFiles(paths)\n const nav: Page[] = []\n\n log(` Add extras Vitepress headers pages.`, 'info')\n for (const file of files) {\n log(` Processing file '${file}'.`, 'debug')\n const src = resolve(process.cwd(), file)\n const dest = resolve(DOCS_DIR, prettify(basename(file), { mode: 'lowercase', removeIdx: true }))\n cpSync(src, dest)\n nav.push({\n text: prettify(parse(src).name, { replaceDash: true, removeIdx: true }),\n link: `/${prettify(parse(src).name, { removeIdx: true, mode: 'lowercase' })}`,\n })\n }\n return nav\n}\n\n/**\n * Adds content from specified paths to a target directory\n *\n * @param paths - String or array of paths to content\n * @param dir - Target directory to copy content to\n * @param fn - Optional callback function to execute for each file\n */\nexport function addContent(paths: string | string[], dir: string, fn?: () => void) {\n for (const path of Array.isArray(paths) ? paths : [paths]) {\n const absolutePath = resolve(process.cwd(), path)\n const files = extractFiles(absolutePath)\n\n for (const file of files) {\n const formattedFile = file.replace(absolutePath, '.')\n log(` Processing file '${formattedFile}' for entry '${path}'.`, 'debug')\n const src = resolve(process.cwd(), file)\n const dest = resolve(dir, formattedFile)\n if (fn) {\n fn()\n }\n cpSync(src, dest)\n }\n }\n}\n\n/**\n * Creates a fork page to display external contributions\n *\n * @param forks - Array of repositories and contribution counts\n */\nexport function addForkPage(forks: { repository: Awaited<ReturnType<typeof getInfos>>['repos'][number], contributions: number }[]) {\n const separator = '---\\n'\n const header = 'layout: fork-page\\nrepoList:\\n'\n const text = '\\n# External contributions\\n\\nThis gallery is a visual representation of the collaborative work done across a variety of open-source projects, each driven by a shared passion for innovation and community growth.\\n\\nEvery tile below represents a unique project where contributions have been made-ranging from code enhancements to documentation improvements. Each project includes a summary of its goals, features, and links to GitHub for direct access.\\n\\nThis page serves as both a portfolio of past work and a resource for revisiting projects that have made a meaningful impact.\\n'\n const frontmatter = forks.map(({ repository, contributions }) => {\n const { name, owner, html_url, description, stargazers_count } = repository\n return { name, owner: owner.login, html_url, description, stargazers_count, contributions }\n })\n log(` Generate forks page.`, 'info')\n writeFileSync(FORKS_FILE, separator + header + YAML.stringify(frontmatter) + separator + text)\n}\n\ntype Source = Required<Awaited<ReturnType<typeof getContributors>>['source']>\n\ntype AdaptedLicense = Omit<NonNullable<Source>['license'], 'spdx_id'> & { spdx_id?: string }\n\ntype AdaptedRepository = Omit<NonNullable<Source>, 'license'> & { license: AdaptedLicense }\n\n/**\n * Processes forked repositories to generate contribution information\n *\n * @param repositories - Array of enhanced repositories\n * @param username - GitHub username\n * @param token - Optional GitHub API token\n */\nexport async function processForks(repositories: EnhancedRepository[], username: GlobalOpts['usernames'][number], token?: GlobalOpts['token']) {\n const forks = await Promise.all(\n repositories.map(async (repository) => {\n const { source, contributors } = await getContributors({ repository, token })\n return {\n contributions: contributors?.find(contributor => contributor.login === username)?.contributions ?? 0,\n repository: source as AdaptedRepository,\n }\n }),\n ).then(f => f.filter(({ repository, contributions }) => !!repository && contributions))\n\n addForkPage(forks)\n}\n\n/**\n * Parses a VitePress configuration file\n *\n * @param path - Path to the VitePress config file\n * @returns The parsed configuration object\n */\nexport async function parseVitepressConfig(path: string): Promise<Partial<ReturnType<typeof defineConfig>>> {\n try {\n const { config } = await import(resolve(process.cwd(), path))\n return config ?? {}\n } catch (error) {\n log(` Unable to load existing Vitepress config '${path}', starting from an empty config. ${formatError(error)}`, 'warn')\n return {}\n }\n}\n\n/**\n * Parses a VitePress index file\n *\n * @param path - Path to the index file\n * @returns The parsed index object\n */\nexport async function parseVitepressIndex(path: string): Promise<Index> {\n const index = (await readFile(resolve(process.cwd(), path))).toString()\n return YAML.parse(index)\n}\n\n/**\n * Tail of the generated VitePress config. `config` stays plain JSON so a later run can\n * re-import it, while the default export adds what JSON cannot hold.\n *\n * Vue compiles every page as a template: VitePress keeps fenced blocks out of it with\n * `v-pre`, but not inline code, so a `${{ always() }}` from a GitHub Actions doc is\n * evaluated as an expression and breaks the build.\n */\nconst VITEPRESS_CONFIG_EXPORT = `\nfunction escapeInlineCode(md) {\n const render = md.renderer.rules.code_inline\n md.renderer.rules.code_inline = (tokens, idx, options, env, self) => {\n tokens[idx].attrSet('v-pre', '')\n return render(tokens, idx, options, env, self)\n }\n}\n\nexport default { ...config, markdown: { ...config.markdown, config: escapeInlineCode } }\n`\n\n/**\n * Generates VitePress configuration and index files\n *\n * @param vitepressConfig - VitePress configuration object\n * @param index - Index page configuration\n * @throws Error if no template theme files are found\n */\nexport function generateVitepressFiles(vitepressConfig: Partial<ReturnType<typeof defineConfig>>, index: Index) {\n const separator = '---\\n'\n createDir(dirname(VITEPRESS_CONFIG))\n\n log(` Generate Vitepress config.`, 'info')\n writeFileSync(VITEPRESS_CONFIG, `export const config = ${JSON.stringify(vitepressConfig, null, 2)}\\n${VITEPRESS_CONFIG_EXPORT}`)\n log(` Generate index file.`, 'info')\n writeFileSync(INDEX_FILE, separator + YAML.stringify(index))\n\n // Fail here (not during the later Vitepress build) so the error points at the\n // actual missing source instead of surfacing as an opaque Vite ENOENT\n const themeFiles = extractFiles(TEMPLATE_THEME)\n if (!themeFiles.length) {\n throw new Error(`No template theme files found at '${TEMPLATE_THEME}'. The Vitepress build cannot succeed without them.`)\n }\n\n log(` Add Docpress theme files.`, 'info')\n themeFiles.forEach((path) => {\n const relativePath = path.replace(`${TEMPLATE_THEME}/`, '')\n generateFile(path, resolve(VITEPRESS_THEME, relativePath))\n })\n}\n","import { dirname } from 'node:path'\nimport { createCommand, createOption } from 'commander'\nimport { configSchema } from '../schemas/global.js'\nimport { addOptions, explicitOptions, parseOptions } from '../utils/commands.js'\nimport { prepareDoc } from '../lib/prepare.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport { log } from '../utils/logger.js'\nimport { createDir, formatDuration, formatError } from '../utils/functions.js'\nimport { VITEPRESS_CONFIG } from '../utils/const.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Command name for the prepare operation\n */\nconst cmdName = 'prepare'\n\n/**\n * Command line options specific to the prepare command\n */\nexport const prepareOpts = [\n createOption('-c, --extra-public-content <string>', configSchema.shape.extraPublicContent.description),\n createOption('-f, --forks', configSchema.shape.forks.description),\n createOption('-p, --extra-header-pages <string>', configSchema.shape.extraHeaderPages.description),\n createOption('-t, --extra-theme <string>', configSchema.shape.extraTheme.description),\n createOption('--sidebar-mode <string>', configSchema.shape.sidebarMode.description),\n createOption('--sidebar-collapsed <string>', configSchema.shape.sidebarCollapsed.description),\n createOption('-v, --vitepress-config <string>', configSchema.shape.vitepressConfig.description),\n createOption('--website-title <string>', configSchema.shape.websiteTitle.description),\n createOption('--website-tagline <string>', configSchema.shape.websiteTagline.description),\n]\n\n/**\n * The prepare command definition for Commander\n */\nexport const prepareCmd = addOptions(createCommand(cmdName), [...prepareOpts, ...globalOpts])\n .description('Transform doc to the target vitepress format.')\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions(cmdName, explicitOptions(cmd, opts))\n await main(parsedOpts)\n })\n\n/**\n * Main function to execute the prepare command\n *\n * @param opts - Validated prepare options\n */\nexport async function main(opts: PrepareOpts) {\n const { extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, token, usernames, websiteTitle, websiteTagline } = opts\n log(`\\n-> Start transform files to prepare Vitepress build.`, 'info')\n const start = Date.now()\n\n createDir(dirname(VITEPRESS_CONFIG), { clean: true })\n const failedUsers: string[] = []\n for (const username of usernames) {\n try {\n await prepareDoc({ extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, token, username, websiteTitle, websiteTagline })\n } catch (error) {\n failedUsers.push(username)\n log(` Failed to prepare documentation for '${username}': ${formatError(error)}`, 'error')\n }\n }\n\n if (failedUsers.length === usernames.length) {\n throw new Error(`Failed to prepare documentation for all requested username(s): ${failedUsers.join(', ')}.`)\n }\n\n const succeeded = usernames.length - failedUsers.length\n log(` Prepared documentation for ${succeeded}/${usernames.length} username(s) in ${formatDuration(Date.now() - start)}.`, failedUsers.length ? 'warn' : 'success')\n}\n","import { Command } from 'commander'\nimport pkg from '../package.json' with { type: 'json' }\nimport { fetchCmd, main as fetchFn, fetchOpts } from './commands/fetch.js'\nimport { buildCmd, main as buildFn } from './commands/build.js'\nimport { prepareCmd, main as prepareFn, prepareOpts } from './commands/prepare.js'\nimport { addOptions, explicitOptions, parseOptions } from './utils/commands.js'\nimport { globalOpts } from './commands/global.js'\nimport { log } from './utils/logger.js'\nimport { formatError } from './utils/functions.js'\n\n/**\n * Creates and configures the Command Line Interface for DocPress\n *\n * @returns A configured Commander program instance\n */\nexport function getProgram() {\n const pm = new Command()\n .name('docpress')\n .description('Build your doc website faster than light ⚡️⚡️⚡️')\n .version(`${pkg.version}`)\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions('global', explicitOptions(cmd, opts))\n await fetchFn(parsedOpts)\n await prepareFn(parsedOpts)\n await buildFn()\n })\n .configureHelp({\n sortSubcommands: true,\n sortOptions: true,\n showGlobalOptions: true,\n })\n .enablePositionalOptions()\n .passThroughOptions()\n .addCommand(fetchCmd)\n .addCommand(prepareCmd)\n .addCommand(buildCmd)\n\n addOptions(pm, [...fetchOpts, ...prepareOpts, ...globalOpts])\n\n return pm\n}\n\n/**\n * Entry point for the DocPress CLI\n * Initializes and runs the command parser\n */\nexport function main() {\n const pm = getProgram()\n pm.parseAsync(process.argv).catch((error) => {\n log(`\\n${formatError(error)}`, 'error')\n process.exit(1)\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../package.json","../src/utils/const.ts","../src/utils/logger.ts","../src/utils/regex.ts","../src/utils/functions.ts","../src/schemas/global.ts","../src/utils/commands.ts","../src/lib/git.ts","../src/lib/gitlab.ts","../src/lib/fetch.ts","../src/commands/global.ts","../src/commands/fetch.ts","../src/commands/build.ts","../src/utils/templates.ts","../src/lib/vitepress.ts","../src/lib/prepare.ts","../src/commands/prepare.ts","../src/cli.ts"],"sourcesContent":["{\n \"name\": \"@tobi-or-not/docpress\",\n \"type\": \"module\",\n \"version\": \"0.13.0\",\n \"packageManager\": \"bun@1.4.0\",\n \"homepage\": \"https://this-is-tobi.com/docpress/introduction\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/this-is-tobi/docpress.git\"\n },\n \"bin\": {\n \"docpress\": \"bin/docpress.js\"\n },\n \"files\": [\n \"bin\",\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=22.12.0\",\n \"bun\": \">=1.3.0\"\n },\n \"scripts\": {\n \"build\": \"bun run build:clean && bun run build:types && bun run build:vite\",\n \"build:clean\": \"node -e \\\"for (const p of ['./dist','./types','./tsconfig.tsbuildinfo']) require('node:fs').rmSync(p,{recursive:true,force:true})\\\"\",\n \"build:docker\": \"docker build --tag tobi-or-not/docpress --target prod .\",\n \"build:types\": \"tsc\",\n \"build:vite\": \"vite build\",\n \"dev\": \"NODE_ENV=development bun ./dev/docpress.ts\",\n \"dev:clean\": \"node -e \\\"require('node:fs').rmSync('./docpress',{recursive:true,force:true})\\\" && bun run dev\",\n \"format\": \"eslint . --fix\",\n \"lint\": \"eslint .\",\n \"prepare\": \"husky || true\",\n \"start\": \"./bin/docpress.js\",\n \"start:docker\": \"docker run --name docpress --rm -v $(pwd)/docpress:/app/docpress:rw tobi-or-not/docpress\",\n \"stop:docker\": \"docker rm --force docpress\",\n \"test\": \"vitest run\",\n \"test:cov\": \"vitest run --coverage\",\n \"typecheck\": \"tsc -p tsconfig.spec.json\",\n \"vp:build\": \"vitepress build ./docpress\",\n \"vp:build:docker\": \"docker build --tag tobi-or-not/docs --target prod .\",\n \"vp:dev\": \"vitepress dev ./docpress --port 8080\",\n \"vp:dev:docker\": \"docker run --publish 8080:8080 --name docpress-vp --rm tobi-or-not/docs\",\n \"vp:preview\": \"vitepress preview ./docpress --port 8080\"\n },\n \"dependencies\": {\n \"@octokit/rest\": \"^22.0.1\",\n \"commander\": \"^15.0.0\",\n \"mermaid\": \"^11.17.2\",\n \"picocolors\": \"^1.1.1\",\n \"simple-git\": \"^3.36.0\",\n \"vitepress\": \"^1.6.4\",\n \"vitepress-plugin-mermaid\": \"^2.0.17\",\n \"yaml\": \"^2.9.0\",\n \"zod\": \"^4.5.4\"\n },\n \"devDependencies\": {\n \"@antfu/eslint-config\": \"^9.5.1\",\n \"@commitlint/cli\": \"^21.2.2\",\n \"@commitlint/config-conventional\": \"^21.2.2\",\n \"@octokit/types\": \"^18.0.0\",\n \"@types/node\": \"^26.4.1\",\n \"@vitejs/plugin-vue\": \"^6.0.8\",\n \"@vitest/coverage-v8\": \"^4.1.11\",\n \"eslint\": \"^10.9.1\",\n \"husky\": \"^9.1.7\",\n \"nano-staged\": \"^1.0.2\",\n \"typescript\": \"^6.0.3\",\n \"vite\": \"^8.2.2\",\n \"vitest\": \"^4.1.11\",\n \"vue\": \"^3.5.42\"\n },\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org/\",\n \"access\": \"public\",\n \"tag\": \"latest\"\n },\n \"overrides\": {\n \"brace-expansion\": \"^5.0.8\",\n \"dompurify\": \"^3.4.14\",\n \"fast-uri\": \"^3.1.6\",\n \"js-yaml\": \"^4.3.1\",\n \"nanoid\": \"^3.3.16\",\n \"postcss\": \"^8.5.26\"\n }\n}\n","import { resolve } from 'node:path'\n\n/**\n * Main directory for DocPress files\n */\nexport const DOCPRESS_DIR = resolve(process.cwd(), 'docpress')\n\n/**\n * Directory for documentation files\n */\nexport const DOCS_DIR = resolve(process.cwd(), 'docpress/docs')\n\n/**\n * Path to the main index file\n */\nexport const INDEX_FILE = resolve(process.cwd(), 'docpress/docs/index.md')\n\n/**\n * Path to the forks documentation file\n */\nexport const FORKS_FILE = resolve(process.cwd(), 'docpress/docs/forks.md')\n\n/**\n * Path to VitePress configuration file\n */\nexport const VITEPRESS_CONFIG = resolve(process.cwd(), 'docpress/.vitepress/config.ts')\n\n/**\n * Directory for VitePress theme\n */\nexport const VITEPRESS_THEME = resolve(process.cwd(), 'docpress/.vitepress/theme')\n\n/**\n * Directory for user custom theme files\n */\nexport const VITEPRESS_USER_THEME = resolve(process.cwd(), 'docpress/.vitepress/theme/extras')\n\n/**\n * Path to template theme files\n */\n// eslint-disable-next-line dot-notation\nexport const TEMPLATE_THEME = process.env['NODE_ENV'] === 'development'\n ? resolve(import.meta.dirname, '../../public/templates/theme')\n : resolve(import.meta.dirname, './templates/theme')\n","/* eslint-disable dot-notation */\nimport pc from 'picocolors'\n\n/**\n * Log message types\n */\ntype LogType = keyof Pick<Console, 'info' | 'warn' | 'error' | 'debug' | 'trace'> | 'success'\n\n/**\n * Colors supported by the logger\n */\ntype Color = 'red' | 'yellow' | 'white' | 'green' | 'blue'\n\n/**\n * Maps a supported color to its picocolors formatter\n */\nconst colorFns: Record<Color, (input: string) => string> = {\n red: pc.red,\n yellow: pc.yellow,\n white: pc.white,\n green: pc.green,\n blue: pc.blue,\n}\n\n/**\n * Mapping of log types to colors and levels\n */\ntype ColorMap = {\n [K in LogType]: {\n color: Color\n level: number\n }\n}\n\n/**\n * Gets the current log level from environment variables\n *\n * @returns Log level number (default: 30)\n */\nfunction getLogLevel() {\n const level = Number(process.env['LOG_LEVEL'])\n return Number.isNaN(level) ? 30 : level\n}\n\n/**\n * Color mapping configuration for different log types\n */\nconst colorMap: ColorMap = {\n error: { color: 'red', level: 10 },\n warn: { color: 'yellow', level: 20 },\n info: { color: 'white', level: 30 },\n success: { color: 'green', level: 30 },\n trace: { color: 'white', level: 40 },\n debug: { color: 'blue', level: 50 },\n}\n\n/**\n * Log level names selectable via the --log-level flag / logLevel config key,\n * kept in sync with the numeric scale in colorMap ('success' excluded: it is\n * not an independently selectable verbosity level, only an info-level color)\n */\nexport const LOG_LEVEL_NAMES = ['error', 'warn', 'info', 'trace', 'debug'] as const\n\n/**\n * A log level name selectable via the --log-level flag / logLevel config key\n */\nexport type LogLevelName = typeof LOG_LEVEL_NAMES[number]\n\n/**\n * Resolves the numeric LOG_LEVEL value for a named log level\n *\n * @param name - Log level name\n * @returns The numeric level consumed by LOG_LEVEL / colorMap\n */\nexport function logLevelValue(name: LogLevelName): number {\n return colorMap[name].level\n}\n\n/**\n * Logs a message with color formatting based on type and level\n *\n * @param msg - Message to log\n * @param type - Type of log message (info, warn, error, debug, trace, success)\n * @param color - Optional specific color to use instead of the default for the type\n */\nexport function log(msg: string, type: LogType = 'info', color?: Color) {\n const computedMsg = color ? colorFns[color](msg) : colorFns[colorMap[type].color](msg)\n const computedType = type === 'success' ? 'info' : type\n\n if (getLogLevel() >= colorMap[type].level) {\n console[computedType](computedMsg)\n }\n}\n","// Markdown link whose target is a parent-relative path, e.g. `[text](../foo/bar)`\n// Captures: 1) link text, 2) the path after `../`\nexport const relativePathRegex = /\\[([^\\]]+)\\]\\(\\.\\.\\/([^)]+)\\)/g\n\n// Markdown link in a README pointing outside `docs/` and not an http/anchor link,\n// e.g. `[text](./src/index.ts)`. Captures: 1) text, 2) optional `./`, 3) the path\nexport const readmePathRegex = /\\[([^\\]]+)\\]\\((?!\\.?\\/docs\\/|docs\\/|http|#)(\\.\\/)?([^/][^)]+)\\)/g\n\n// Markdown link in a README pointing into `docs/`, e.g. `[text](./docs/guide.md)`\n// Captures: 1) text, 2) the `docs/` prefix, 3) the path within docs\nexport const readmeDocsPathRegex = /\\[([^\\]]+)\\]\\((\\.?\\/docs\\/|docs\\/)([^)]*)\\)/g\n\n// Leading two-digit ordering prefix stripped from file names, e.g. `01-` in `01-intro.md`\nexport const removeIdxRegex = /^\\d{2}-/\n\n// YAML frontmatter block at the top of a markdown file. Captures: 1) the inner YAML\nexport const frontmatterRegex = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/\n\n// Relative markdown-to-markdown link (not http/anchor/mailto/root), e.g. `[t](./dir/page.md#a)`\n// Captures: 1) text, 2) optional directory, 3) the `.md` file, 4) optional `#anchor`\nexport const internalMdLinkRegex = /\\[([^\\]]+)\\]\\((?!https?:|\\/|#|mailto:)([^)#]*\\/)?([^)#/]+\\.md)(#[^)]*)?\\)/g\n","import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'\nimport { basename, join, resolve } from 'node:path'\nimport YAML from 'yaml'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { EnhancedRepository } from '../lib/fetch.js'\nimport type { getInfos } from '../lib/git.js'\nimport { DOCPRESS_DIR } from './const.js'\nimport { log } from './logger.js'\nimport { frontmatterRegex, internalMdLinkRegex, readmeDocsPathRegex, readmePathRegex, relativePathRegex, removeIdxRegex } from './regex.js'\n\n/**\n * Checks the HTTP status code of a URL\n * Redirects are not followed: some providers (e.g. GitLab) redirect to a\n * sign-in page instead of returning 404 for missing resources\n *\n * @param url - URL to check\n * @returns HTTP status code (500 for network/transport errors)\n */\nexport async function checkHttpStatus(url: string): Promise<number> {\n try {\n const response = await fetch(url, { method: 'HEAD', redirect: 'manual' })\n return response.status\n } catch (error) {\n log(` Network error while checking '${url}': ${formatError(error)}`, 'debug')\n return 500\n }\n}\n\n/**\n * Reduces an arbitrary string to a safe single path segment: basename only,\n * with no path separators or leading dots\n *\n * @param name - Raw name (repository name, username, login, ...)\n * @returns A filesystem-safe segment\n */\nexport function sanitizeSegment(name: string): string {\n const segment = name.split(/[/\\\\]/).pop() ?? ''\n const safe = segment.replace(/[^\\w.-]/g, '_').replace(/^\\.+/, '')\n // Never return an empty segment: an empty path component would let callers\n // that join it onto a base dir (and clean that dir) operate on the base itself\n return safe || '_'\n}\n\n/**\n * Returns a shallow copy of an options object with the `token` value masked\n *\n * @param obj - Options or config object that may contain a `token` field\n * @returns A copy with `token` replaced by a placeholder\n */\nexport function redactToken<T extends { token?: unknown }>(obj: T): T {\n if (!isObject(obj) || !('token' in obj)) {\n return obj\n }\n return { ...obj, token: obj.token ? '***' : obj.token } as T\n}\n\n/**\n * Extracts a human-readable message from a caught value\n *\n * @param error - The caught value (typically an Error, but not guaranteed)\n * @returns The error's message, or its string representation when it isn't an Error\n */\nexport function formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * Formats a duration in milliseconds as a short human-readable string\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted duration (e.g. \"350ms\", \"1.2s\", \"1m 05s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`\n }\n const totalSeconds = ms / 1000\n if (totalSeconds < 60) {\n return `${totalSeconds.toFixed(1)}s`\n }\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = Math.round(totalSeconds % 60)\n return `${minutes}m ${String(seconds).padStart(2, '0')}s`\n}\n\n/**\n * Options for prettifying strings\n */\ninterface PrettifyOpts {\n mode?: 'capitalize' | 'uppercase' | 'lowercase'\n replaceDash?: boolean\n removeIdx?: boolean\n removeDot?: boolean\n removeExt?: boolean\n acronyms?: string[]\n}\n\n/**\n * Gives every word of a label that matches an entry, ignoring case, the entry's\n * own spelling\n *\n * Runs after the case mode because `capitalize` lowercases everything past the\n * first letter, which is right for prose ('Getting started') and wrong for the\n * words a project always spells one way: a `cli` page reads 'Cli' and an\n * `ai-clients` folder 'Ai clients'. Matching whole words only keeps 'AI' out of\n * 'email', and the entry wins over the first-letter capital, so a name written\n * in lowercase on purpose can be listed too\n *\n * @param s - Label to adjust, words separated by single spaces\n * @param acronyms - Words to spell exactly as given\n * @returns The label with each listed word respelled\n */\nfunction keepAcronyms(s: string, acronyms: string[]) {\n const spellings = new Map(acronyms.map(acronym => [acronym.toLowerCase(), acronym]))\n return s.split(' ').map(word => spellings.get(word.toLowerCase()) ?? word).join(' ')\n}\n\n/**\n * Formats a string based on specified options\n *\n * @param s - Input string to format\n * @param opts - Formatting options\n * @returns Formatted string\n */\nexport function prettify(s: string, opts: PrettifyOpts) {\n let u: string = ''\n\n if (opts?.removeDot) {\n if (s.startsWith('.')) {\n u = s.slice(1)\n } else {\n u = s\n }\n // Replace all remaining dots with dashes to ensure consistency\n u = u.replaceAll('.', '-')\n }\n\n if (opts?.removeIdx) {\n u = (u || s).replace(removeIdxRegex, '')\n }\n\n if (opts?.removeExt) {\n u = (u || s).split('.')[0]\n }\n\n if (opts?.replaceDash) {\n u = (u || s).replaceAll('-', ' ')\n }\n\n if (opts?.mode === 'capitalize') {\n u = (u || s).slice(0, 1).toUpperCase() + (u || s).slice(1).toLowerCase()\n } else if (opts?.mode === 'lowercase') {\n u = (u || s).toLowerCase()\n } else if (opts?.mode === 'uppercase') {\n u = (u || s).toUpperCase()\n }\n\n if (opts?.acronyms?.length) {\n u = keepAcronyms(u || s, opts.acronyms)\n }\n\n return u || s\n}\n\n/**\n * Creates a directory, optionally cleaning it first\n *\n * @param directory - Path to the directory to create\n * @param options - Options object\n * @param options.clean - Whether to clean the directory before creating it\n */\nexport function createDir(directory: string, { clean }: { clean?: boolean } = { clean: false }) {\n try {\n if (clean && existsSync(directory)) {\n // Empty the directory's contents rather than removing the directory itself: it may be a bind-mounted volume (e.g. Docker), and removing the mount point fails with EBUSY.\n for (const entry of readdirSync(directory)) {\n rmSync(resolve(directory, entry), { recursive: true, force: true })\n }\n }\n if (!existsSync(directory)) {\n mkdirSync(directory, { recursive: true })\n }\n } catch (err) {\n throw new Error(`Unable to create directory '${directory}': ${err instanceof Error ? err.message : String(err)}`)\n }\n}\n\n/**\n * Checks if a path is a directory\n *\n * @param path - Path to check\n * @returns True if path is a directory, false otherwise\n */\nexport function isDir(path: string) {\n try {\n return statSync(path).isDirectory()\n } catch (_error) {\n return false\n }\n}\n\n/**\n * Checks if a path is a file\n *\n * @param path - Path to check\n * @returns True if path is a file, false otherwise\n */\nexport function isFile(path: string) {\n try {\n return statSync(path).isFile()\n } catch (_error) {\n return false\n }\n}\n\n/**\n * Extracts a list of file paths from directories and files\n *\n * @param paths - Path or array of paths to extract files from\n * @returns Array of file paths\n */\nexport function extractFiles(paths: string[] | string): string[] {\n return (Array.isArray(paths) ? paths : [paths]).flatMap((path) => {\n if (isFile(path)) {\n return [path]\n }\n if (isDir(path)) {\n return readdirSync(path).filter(file => file !== '.git').sort().flatMap(file => extractFiles(join(path, file)))\n }\n return []\n })\n}\n\n/**\n * Gets all markdown files from specified paths\n *\n * @param path - Array of paths to search for markdown files\n * @returns Array of markdown file paths\n */\nexport function getMdFiles(path: string[]) {\n return extractFiles(path).filter(file => basename(file).endsWith('.md'))\n}\n\n/**\n * Formats an array of strings into a readable comma-separated list\n *\n * @param arr - Array of strings to format\n * @returns Formatted string (e.g., \"a\", \"b\" or \"c\")\n */\nexport function prettifyEnum(arr: readonly string[]) {\n return arr.reduce((acc, cur, idx, arr) => {\n if (!idx) {\n return `\"${cur}\"`\n } else if (idx === arr.length - 1) {\n return `${acc} or \"${cur}\"`\n } else {\n return `${acc}, \"${cur}\"`\n }\n }, '')\n}\n\n/**\n * Gets user information from cached JSON file\n *\n * @param username - GitHub username\n * @returns User information object\n */\nexport function getUserInfos(username: GlobalOpts['usernames'][number]) {\n return JSON.parse(readFileSync(`${DOCPRESS_DIR}/user-${sanitizeSegment(username)}.json`).toString()) as Awaited<ReturnType<typeof getInfos>>['user']\n}\n\n/**\n * Gets repository information from cached JSON file\n *\n * @param username - GitHub username\n * @returns Array of enhanced repository objects\n */\nexport function getUserRepos(username: GlobalOpts['usernames'][number]) {\n return JSON.parse(readFileSync(`${DOCPRESS_DIR}/repos-${sanitizeSegment(username)}.json`).toString()) as EnhancedRepository[]\n}\n\n/**\n * Keys skipped during a merge to prevent prototype pollution\n */\nconst FORBIDDEN_MERGE_KEYS = new Set(['__proto__', 'constructor', 'prototype'])\n\n/**\n * Deep merges multiple objects, cloning object values rather than sharing them by reference\n *\n * @param objects - Objects to merge\n * @returns Merged object\n */\nexport function deepMerge<T extends Record<string, any> | null>(...objects: T[]): T {\n return objects.reduce((acc, obj) => {\n if (obj === null) {\n return acc\n }\n\n Object.keys(obj).forEach((key) => {\n if (FORBIDDEN_MERGE_KEYS.has(key)) {\n return\n }\n\n const accValue = acc ? acc[key] : null\n const objValue = obj[key]\n\n if (isObject(accValue) && isObject(objValue)) {\n (acc as Record<string, any>)[key] = deepMerge(accValue, objValue)\n } else if (isObject(objValue)) {\n (acc as Record<string, any>)[key] = deepMerge({}, objValue)\n } else {\n (acc as Record<string, any>)[key] = objValue\n }\n })\n return acc\n }, {} as T)\n}\n\n/**\n * Checks if a value is an object (not an array or null)\n *\n * @param val - Value to check\n * @returns True if value is an object, false otherwise\n */\nexport function isObject(val: any): val is object {\n return val && typeof val === 'object' && !Array.isArray(val)\n}\n\n/**\n * Loads and parses a JSON configuration file\n *\n * @param configPath - Path to the configuration file\n * @returns Parsed configuration object, or an empty object when no path is given\n * @throws Error if the file cannot be read or contains invalid JSON\n */\nexport function loadConfigFile(configPath?: string) {\n if (!configPath) {\n return {}\n }\n const fullPath = resolve(process.cwd(), configPath)\n let fileContents: string\n try {\n fileContents = readFileSync(fullPath, 'utf8')\n } catch (_error) {\n throw new Error(`Cannot read config file '${configPath}'.`)\n }\n try {\n return JSON.parse(fileContents)\n } catch (_error) {\n throw new Error(`Invalid JSON in config file '${configPath}'.`)\n }\n}\n\n/**\n * Splits a comma-separated string into an array of strings\n *\n * @param s - Comma-separated string\n * @returns Array of strings\n */\nexport function splitByComma(s: string) {\n return s.split(',')\n}\n\n/**\n * Replaces relative paths in markdown links with absolute URLs\n *\n * @param file - Path to the markdown file to process\n * @param url - Base URL to use for absolute links\n */\nexport function replaceRelativePath(file: string, url: string) {\n const fileContent = readFileSync(file, 'utf8')\n const updatedContent = fileContent.replace(relativePathRegex, (_match, p1, p2) => {\n return `[${p1}](${url}/${p2})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n\n/**\n * Adds or updates the \"lastUpdated\" frontmatter field of a markdown file, preserving\n * any frontmatter already present\n *\n * @param file - Path to the markdown file to update\n * @param date - Datetime to set as the \"lastUpdated\" value\n */\nexport function addLastUpdatedFrontmatter(file: string, date: string) {\n const content = readFileSync(file, 'utf8')\n const match = content.match(frontmatterRegex)\n\n if (match) {\n const data = YAML.parse(match[1]) ?? {}\n data.lastUpdated = date\n writeFileSync(file, content.replace(frontmatterRegex, `---\\n${YAML.stringify(data)}---\\n`), 'utf8')\n } else {\n writeFileSync(file, `---\\nlastUpdated: ${date}\\n---\\n\\n${content}`, 'utf8')\n }\n}\n\n/**\n * Strips the ordering prefix from every named segment of a relative link's directory\n * Folders are renamed during sidebar generation exactly as files are, so a link that\n * crosses one has to follow the rename on both halves of the path — rewriting only\n * the filename would leave `../01-guide/setup.md` pointing at a folder that is now\n * `guide`, which resolves at publish time and fails for the reader rather than the build\n *\n * @param dir - Directory portion of a relative link, or undefined when the link has none\n * @returns The directory with ordering prefixes removed, `.` and `..` left alone\n */\nfunction prettifyLinkDir(dir?: string): string {\n if (!dir) {\n return ''\n }\n return dir\n .split('/')\n .map(segment => segment === '' || segment === '.' || segment === '..'\n ? segment\n : prettify(segment, { removeIdx: true }))\n .join('/')\n}\n\n/**\n * Rewrites relative markdown links to match the renamed target files\n * Files and folders are renamed during sidebar generation (index prefix stripped, name\n * lowercased, readme becoming introduction), so in-page links must follow the same\n * convention to avoid pointing to files that no longer exist\n *\n * @param file - Path to the markdown file to process\n */\nexport function replaceInternalMdLinks(file: string) {\n const fileContent = readFileSync(file, 'utf8')\n const updatedContent = fileContent.replace(internalMdLinkRegex, (_match, text, dir, target, anchor) => {\n let filename = prettify(target, { mode: 'lowercase', removeIdx: true })\n if (filename === 'readme.md') {\n filename = 'introduction.md'\n }\n return `[${text}](${prettifyLinkDir(dir)}${filename}${anchor ?? ''})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n\n/**\n * Processes README files to fix various link formats\n *\n * @param file - Path to the README file to process\n * @param url - Base URL to use for absolute links\n */\nexport function replaceReadmePath(file: string, url: string) {\n const readmeContent = readFileSync(file, 'utf8')\n const updatedContent = readmeContent\n .replace(readmePathRegex, (_match, p1, _p2, p3) => {\n return `[${p1}](${url}/${p3})`\n })\n .replace(readmeDocsPathRegex, (_match, p1, _p2, p3) => {\n return `[${p1}](${p3})`\n })\n writeFileSync(file, updatedContent, 'utf8')\n}\n","import { z } from 'zod'\nimport { loadConfigFile, prettifyEnum, splitByComma } from '../utils/functions.js'\nimport type { LogLevelName } from '../utils/logger.js'\nimport { LOG_LEVEL_NAMES, logLevelValue } from '../utils/logger.js'\n\n/**\n * List of supported Git providers\n */\nconst providers = ['github', 'gitlab'] as const\n\n/**\n * Supported sidebar shapes\n */\nconst sidebarModes = ['single', 'multi'] as const\n\n/**\n * Accepted CLI values for the tri-state sidebar collapse setting\n */\nconst sidebarCollapsedValues = ['true', 'false', 'null'] as const\n\n/**\n * Allowed characters for a Git branch/ref name (must not start with \"-\")\n */\nconst safeRefRegex = /^\\w[\\w./-]*$/\nconst safeRefMessage = 'Branch name must start with a letter, digit or underscore and only contain letters, digits, \".\", \"-\", \"_\" or \"/\".'\n\n/**\n * A label word never holds a space or a dash: labels are split into words on\n * spaces, and every dash of a file or folder name has become a space by then.\n * An entry holding either could never match, so it is refused rather than\n * silently ignored\n */\nconst acronymRegex = /^[^\\s-]+$/\nconst acronymMessage = 'Each acronym must be a single word, with no space or dash, as it is matched against one word of a label.'\n\n/**\n * Schema for the DocPress configuration file\n * Defines the structure and validation rules for the configuration\n */\nexport const configSchema = z.object({\n // Global\n usernames: z.string()\n .array()\n .describe('List of comma separated Git provider usernames used to collect data.'),\n logLevel: z.enum(LOG_LEVEL_NAMES)\n .optional()\n .describe(`Verbosity of the CLI output. Values should be ${prettifyEnum(LOG_LEVEL_NAMES)}. Defaults to \"info\", or to the LOG_LEVEL environment variable when set.`),\n // Fetch\n branch: z.string()\n .regex(safeRefRegex, safeRefMessage)\n .optional()\n .describe('Branch used to collect Git provider data. Defaults to each repository\\'s own default branch when not set.'),\n gitProvider: z.enum(providers)\n .default('github')\n .describe(`Git provider used to retrieve data. Values should be ${prettifyEnum(providers)}.`),\n reposFilter: z.string()\n .array()\n .describe('List of comma separated repositories to retrieve from Git provider. Default to all user\\'s public repositories.'),\n // Prepare\n extraHeaderPages: z.string()\n .array()\n .describe('List of comma separated additional files or directories to process Vitepress header pages.'),\n extraPublicContent: z.string()\n .array()\n .describe('List of comma separated additional files or directories to process Vitepress public folder.'),\n extraTheme: z.string()\n .array()\n .describe('List of comma separated additional files or directories to use as Vitepress theme.'),\n forks: z.boolean()\n .default(false)\n .describe('Whether or not to create the dedicated fork page that aggregate external contributions.'),\n lastUpdated: z.boolean()\n .default(false)\n .describe('Whether or not to inject each page\\'s last Git commit date as Vitepress \"lastUpdated\" frontmatter.'),\n sidebarMode: z.enum(sidebarModes)\n .default('single')\n .describe(`Shape of the generated sidebar. Values should be ${prettifyEnum(sidebarModes)}. \"single\" lists every repository in one sidebar, \"multi\" gives each repository its own sidebar.`),\n sidebarCollapsed: z.union([z.boolean(), z.null()])\n .default(true)\n .describe('Collapse behaviour of generated sidebar groups. \"true\" collapses them by default, \"false\" expands them, \"null\" makes them non-collapsible.'),\n acronyms: z.string()\n .regex(acronymRegex, acronymMessage)\n .array()\n .describe('List of comma separated words kept exactly as written in generated sidebar labels and home page titles, such as \"CLI\" or \"API\", matched whole and ignoring case.'),\n vitepressConfig: z.any()\n .optional()\n .describe('Path to the vitepress configuration file.'),\n websiteTitle: z.string()\n .describe('Website title.'),\n websiteTagline: z.string()\n .describe('Website tagline.'),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\n/**\n * CLI schema with transformations applied to handle string-to-array conversions\n * Fields with config defaults are redefined without them, so that values coming\n * from a config file are not overridden by schema defaults\n */\nexport const cliSchema = configSchema\n .partial()\n .extend({\n branch: z.string()\n .regex(safeRefRegex, safeRefMessage)\n .optional()\n .describe(configSchema.shape.branch.description || ''),\n gitProvider: z.enum(providers)\n .optional()\n .describe(configSchema.shape.gitProvider.description || ''),\n logLevel: z.enum(LOG_LEVEL_NAMES)\n .optional()\n .describe(configSchema.shape.logLevel.description || ''),\n forks: z.boolean()\n .optional()\n .describe(configSchema.shape.forks.description || ''),\n lastUpdated: z.boolean()\n .optional()\n .describe(configSchema.shape.lastUpdated.description || ''),\n config: z.string()\n .optional()\n .describe('Path to the docpress configuration file.'),\n token: z.string()\n .optional()\n .describe('Git provider token used to collect data.'),\n usernames: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.usernames.description || ''),\n reposFilter: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.reposFilter.description || ''),\n extraHeaderPages: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraHeaderPages.description || ''),\n extraPublicContent: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraPublicContent.description || ''),\n extraTheme: z.string()\n .transform(splitByComma)\n .optional()\n .describe(configSchema.shape.extraTheme.description || ''),\n vitepressConfig: z.string()\n .optional()\n .describe(configSchema.shape.vitepressConfig.description || ''),\n sidebarMode: z.enum(sidebarModes)\n .optional()\n .describe(configSchema.shape.sidebarMode.description || ''),\n sidebarCollapsed: z.enum(sidebarCollapsedValues)\n .transform(value => value === 'null' ? null : value === 'true')\n .optional()\n .describe(`${configSchema.shape.sidebarCollapsed.description} Values should be ${prettifyEnum(sidebarCollapsedValues)}.`),\n // Piped back through the config rule so a CLI entry is held to the same\n // single-word check as a config file one\n acronyms: z.string()\n .transform(splitByComma)\n .pipe(configSchema.shape.acronyms)\n .optional()\n .describe(configSchema.shape.acronyms.description || ''),\n })\n\nexport type Cli = z.infer<typeof cliSchema>\n\n/**\n * Settings that always hold a value once CLI options, config file and defaults are merged\n */\ntype ResolvedKeys = 'usernames' | 'gitProvider' | 'forks' | 'lastUpdated' | 'sidebarMode' | 'sidebarCollapsed'\n\n/**\n * Options resolved from CLI arguments, configuration file and defaults\n */\nexport type ResolvedOpts = Omit<Partial<Config>, ResolvedKeys> & Pick<Config, ResolvedKeys> & { token?: string }\n\n/**\n * Prepares configuration data by converting string values to arrays when needed\n *\n * @param configData - Raw configuration data to process\n * @returns Processed configuration data with proper array types\n */\nfunction prepareConfigData(configData: any) {\n if (!configData) return {}\n\n const result = { ...configData }\n const arrayKeys = ['usernames', 'reposFilter', 'extraHeaderPages', 'extraPublicContent', 'extraTheme', 'acronyms'] as const\n for (const key of arrayKeys) {\n if (typeof result[key] === 'string') {\n result[key] = [result[key]]\n }\n }\n\n return result\n}\n\n/**\n * Validates configuration file data against the config schema\n *\n * @param configData - Prepared configuration data to validate\n * @returns The validated configuration data\n * @throws ZodError if the configuration data is invalid, its issues left\n * unformatted for globalOptsSchema to forward\n */\nfunction validateConfigData(configData: any) {\n const result = configSchema.partial().safeParse(configData)\n if (!result.success) {\n throw result.error\n }\n return result.data\n}\n\n/**\n * Validates the final merged configuration to ensure required fields are present\n *\n * @param mergedConfig - The merged configuration to validate\n * @returns The validated configuration if it passes validation\n * @throws Error if required fields are missing\n */\nfunction validateFinalConfig(mergedConfig: any): ResolvedOpts {\n if (!mergedConfig.usernames?.length) {\n throw new Error('The usernames field is required')\n }\n\n return mergedConfig\n}\n\n/**\n * Resolves the API token, with precedence: explicit token > DOCPRESS_TOKEN >\n * provider-specific env var (GITHUB_TOKEN / GITLAB_TOKEN). Empty values count as\n * unset at every step\n *\n * @param token - Token explicitly provided via CLI flag or config file\n * @param gitProvider - Git provider used to pick the provider-specific env var\n * @returns The resolved token, or undefined when none is available\n */\nfunction resolveToken(token: string | undefined, gitProvider: string | undefined): string | undefined {\n if (token) {\n return token\n }\n /* eslint-disable dot-notation */\n const providerToken = gitProvider === 'gitlab' ? process.env['GITLAB_TOKEN'] : process.env['GITHUB_TOKEN']\n // Empty values are treated as unset: a CI secret that did not resolve arrives as\n // an empty string, which must not shadow the next source or pass as a token\n return process.env['DOCPRESS_TOKEN'] || providerToken || undefined\n /* eslint-enable dot-notation */\n}\n\n/**\n * Applies an explicitly resolved log level (CLI flag > config file) as the\n * LOG_LEVEL env var consumed by the logger. Left untouched when neither the CLI\n * flag nor the config file set one, so an existing numeric LOG_LEVEL env var\n * (or the logger's own default) keeps working exactly as before\n *\n * @param logLevel - Log level name explicitly provided via CLI flag or config file\n */\nfunction resolveLogLevel(logLevel: LogLevelName | undefined): void {\n if (logLevel) {\n // eslint-disable-next-line dot-notation\n process.env['LOG_LEVEL'] = String(logLevelValue(logLevel))\n }\n}\n\n/**\n * Schema for global options that combines CLI arguments and configuration files\n * Merges config sources with precedence: CLI options > config file > defaults\n */\nexport const globalOptsSchema = cliSchema\n .partial()\n .transform((data, ctx) => {\n try {\n const { config, vitepressConfig, token, ...rest } = data\n\n // Load and validate configuration from file\n const configData = validateConfigData(prepareConfigData(loadConfigFile(config)))\n\n // Create final config. `branch` is intentionally NOT defaulted here: when\n // the user does not pass --branch, it stays undefined so the fetch step\n // can fall back to each repository's own default branch (see fetch.ts).\n const mergedConfig = {\n gitProvider: 'github',\n forks: false,\n lastUpdated: false,\n sidebarMode: 'single',\n sidebarCollapsed: true,\n ...configData,\n ...rest,\n ...(vitepressConfig\n ? {\n vitepressConfig: {\n ...(configData.vitepressConfig || {}),\n ...(loadConfigFile(vitepressConfig) || {}),\n },\n }\n : {}),\n }\n\n resolveLogLevel(mergedConfig.logLevel)\n\n return validateFinalConfig({ ...mergedConfig, token: resolveToken(token, mergedConfig.gitProvider) })\n } catch (error) {\n // Surface the failure as a validation issue instead of terminating the\n // process from inside the schema. The caller (parseOptions) reports it and\n // owns the single exit point, which keeps this schema pure and testable.\n //\n // A configuration file's issues are forwarded one by one with their own\n // paths, as the CLI's are, since that exit point formats every issue.\n // Formatted here as well, the whole list became the text of one pathless\n // issue, and the report opened with a doubled mark (\"✖ ✖\").\n if (error instanceof z.ZodError) {\n for (const issue of error.issues) {\n ctx.addIssue({ code: 'custom', message: issue.message, path: issue.path })\n }\n return z.NEVER\n }\n ctx.addIssue({\n code: 'custom',\n message: error instanceof Error ? error.message : JSON.stringify(error, null, 2),\n })\n return z.NEVER\n }\n })\n\nexport type GlobalOpts = z.output<typeof globalOptsSchema>\n","import type { Command, Option } from 'commander'\nimport { z } from 'zod'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport { globalOptsSchema } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport { redactToken } from './functions.js'\nimport { log } from './logger.js'\n\n/**\n * Type definition for command names\n */\ntype Cmd = 'fetch' | 'prepare' | 'global'\n\n/**\n * Interface mapping command names to their option types\n */\ninterface Options {\n fetch: FetchOpts\n prepare: PrepareOpts\n global: GlobalOpts\n}\n\n/**\n * Validates raw command options against the global schema\n * The merging with precedence CLI options > config file > defaults is performed by\n * `globalOptsSchema` (see src/schemas/global.ts); this function only runs that schema\n *\n * @param cmd - The command name ('fetch', 'prepare' or 'global')\n * @param opts - Raw options from the command line\n * @returns Validated and processed options for the specified command\n */\nexport function parseOptions<T extends Cmd>(cmd: T, opts: Record<string, unknown>): Options[T] {\n log(`Initializing Docpress...`, 'info', 'blue')\n log(`\\n\\n-> Checking for required environment settings and configurations.`, 'info')\n log(` Debug info - cmd: ${cmd}`, 'debug')\n\n const res = globalOptsSchema.safeParse(opts)\n if (!res.success) {\n log(` An error occurred while checking configuration.\\n ${z.prettifyError(res.error)}`, 'error')\n process.exit(1)\n }\n // Logged after validation (not the raw input) so this reflects the fully merged\n // CLI/config/defaults result, and so a `--log-level debug` passed in this same\n // invocation - only resolved inside the schema above - is already in effect.\n log(` Debug info - resolved options: ${JSON.stringify(redactToken(res.data))}`, 'debug')\n log(' Setup complete! Ready to process your documentation.', 'info')\n return res.data as Options[T]\n}\n\n/**\n * Extracts options that were explicitly provided by the user, skipping Commander defaults\n * This lets config file values take precedence over CLI defaults\n *\n * @param cmd - The Commander Command instance\n * @param opts - Raw options from the command line\n * @returns Options object containing only user-provided values\n */\nexport function explicitOptions(cmd: Command, opts: Record<string, unknown>) {\n return Object.fromEntries(\n Object.entries(opts).filter(([key]) => cmd.getOptionValueSource(key) !== 'default'),\n )\n}\n\n/**\n * Adds option definitions to a Command instance\n *\n * @param cmd - The Commander Command instance\n * @param opts - Array of option definitions to add\n * @returns The Command instance with options added\n */\nexport function addOptions(cmd: Command, opts: Option[]) {\n for (const opt of opts) {\n cmd.addOption(opt)\n }\n return cmd\n}\n","import { appendFileSync, cpSync, rmSync } from 'node:fs'\nimport { relative, resolve } from 'node:path'\nimport { Octokit } from '@octokit/rest'\nimport { simpleGit } from 'simple-git'\nimport type { SimpleGit } from 'simple-git'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { addLastUpdatedFrontmatter, createDir, formatError, getMdFiles } from '../utils/functions.js'\nimport { log } from '../utils/logger.js'\nimport type { EnhancedRepository } from './fetch.js'\n\n/**\n * Octokit logger that stays quiet on 404s, which are handled explicitly\n */\nexport const quietOctokitLog = {\n debug: () => {},\n info: () => {},\n warn: (message: string) => {\n if (!message.includes('404')) log(message, 'warn')\n },\n error: (message: string) => {\n if (!message.includes('404')) log(message, 'error')\n },\n}\n\n/**\n * Fetches user and repository information from GitHub\n *\n * @param options - Options for retrieving GitHub data\n * @param options.username - GitHub username\n * @param options.token - GitHub API token\n * @param options.branch - Branch to use for documentation\n * @returns Object containing user information, repositories, and branch\n */\nexport async function getInfos({ username, token, branch }: Pick<FetchOpts, 'branch'> & Pick<GlobalOpts, 'token'> & { username: GlobalOpts['usernames'][number] }) {\n log(` Get infos for username '${username}'.`, 'info')\n const octokit = new Octokit({ auth: token, log: quietOctokitLog })\n log(` Get user infos.`, 'debug')\n const { data: user } = await octokit.rest.users.getByUsername({ username })\n .catch((error) => {\n throw error.status === 404 ? new Error(`No GitHub user found for '${username}'.`) : error\n })\n log(` Get repositories infos.`, 'debug')\n const repos = await octokit.paginate(octokit.rest.repos.listForUser, { username, sort: 'full_name', per_page: 100 })\n\n return { user, repos, branch }\n}\n\n/**\n * Gets contributors for a repository\n *\n * @param options - Options for retrieving contributors\n * @param options.repository - Repository information\n * @param options.token - GitHub API token\n * @returns Object with source repository and contributors list\n */\nexport async function getContributors({\n repository,\n token,\n}: {\n repository: EnhancedRepository\n token: GlobalOpts['token']\n}) {\n log(` Get contributors infos for repository '${repository.name}'.`, 'info')\n const octokit = new Octokit({ auth: token, log: quietOctokitLog })\n let repo\n try {\n repo = (await octokit.rest.repos.get({\n owner: repository.owner.login,\n repo: repository.name,\n })).data\n } catch (error) {\n // A deleted / private / rate-limited upstream must not abort the whole\n // prepare step: degrade to \"no source\" so this single fork is skipped.\n log(` Failed to get repository infos for '${repository.name}'. Error : ${formatError(error)}`, 'warn')\n return { source: undefined, contributors: [] }\n }\n if (!repo.source?.owner?.login) {\n return { source: repo.source, contributors: [] }\n }\n const contributors: Array<any> = []\n try {\n for await (const { data } of octokit.paginate.iterator(\n octokit.rest.repos.listContributors,\n {\n owner: repo.source.owner.login,\n repo: repo.parent?.name ?? repo.name,\n per_page: 100,\n },\n )) {\n contributors.push(...data)\n }\n } catch (error) {\n log(\n ` Failed to get contributors infos for repository '${repository.name}'. Error : ${formatError(error)}`,\n 'warn',\n )\n return { source: repo.source, contributors: [] }\n }\n\n return { source: repo.source, contributors }\n}\n\n/**\n * Parses `git log --format=%cI --name-only` output into a map of file path to\n * its most recent commit date\n * The log is newest-first, so the first date seen for a given file is the latest one\n *\n * @param logOutput - Raw output of the git log command\n * @returns Map of repository-relative file path to ISO commit date\n */\nexport function parseLastCommitDates(logOutput: string): Map<string, string> {\n const dates = new Map<string, string>()\n let currentDate: string | null = null\n\n for (const rawLine of logOutput.split('\\n')) {\n const line = rawLine.trim()\n if (!line) {\n continue\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(line)) {\n currentDate = line\n continue\n }\n if (currentDate && !dates.has(line)) {\n dates.set(line, currentDate)\n }\n }\n\n return dates\n}\n\n/**\n * Injects each markdown file's last Git commit date as a \"lastUpdated\" frontmatter field,\n * reading the local clone's history in a single pass\n *\n * @param git - Simple-git instance scoped to the cloned repository, with history still intact\n * @param projectDir - Directory containing the cloned repository\n * @param name - Repository name, used for logging\n */\nexport async function applyLastUpdated(git: SimpleGit, projectDir: string, name: string) {\n const files = getMdFiles([projectDir])\n if (!files.length) {\n return\n }\n\n let dates: Map<string, string>\n try {\n // core.quotePath=false keeps non-ASCII paths unescaped so they match the\n // relative() lookup below (default quoting would octal-escape them)\n const logOutput = await git.raw(['-c', 'core.quotePath=false', 'log', '--format=%cI', '--name-only'])\n dates = parseLastCommitDates(logOutput)\n } catch (error) {\n log(` Unable to read commit history for repository '${name}'. Error : ${formatError(error)}`, 'warn')\n return\n }\n\n for (const file of files) {\n const date = dates.get(relative(projectDir, file))\n if (date) {\n addLastUpdatedFrontmatter(file, date)\n }\n }\n}\n\n/**\n * Clones a repository with sparse checkout\n *\n * @param name - Repository name\n * @param url - Repository URL\n * @param projectDir - Directory to clone into\n * @param branch - Branch to clone\n * @param includes - Patterns to include in sparse checkout\n * @param lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n * @returns True when the repository was cloned successfully, false otherwise\n */\nexport async function cloneRepo(name: string, url: string, projectDir: string, branch: string, includes: string[], lastUpdated?: boolean): Promise<boolean> {\n // Refuse values Git could parse as CLI options (argument injection)\n if (url.startsWith('-') || branch.startsWith('-')) {\n log(` Refusing to clone '${name}': unsafe repository URL or branch name.`, 'error')\n return false\n }\n\n createDir(projectDir, { clean: true })\n\n try {\n // Background maintenance is disabled so no git process keeps writing into\n // .git (e.g. commit-graph files) while it is being removed below\n const git = simpleGit({ baseDir: projectDir, config: ['maintenance.auto=false', 'gc.auto=0', 'fetch.writeCommitGraph=false'] })\n await git.init()\n .addConfig('core.sparseCheckout', 'true', true, 'local')\n .addRemote('origin', url)\n\n for (const item of includes) {\n log(` Add '${item}' to '${name}' sparse-checkout file.`, 'debug')\n appendFileSync(resolve(projectDir, '.git/info/sparse-checkout'), `${item}\\n`, 'utf8')\n }\n\n log(` Clone repository '${name}'.`, 'info')\n await git.pull('origin', branch)\n\n if (lastUpdated) {\n await applyLastUpdated(git, projectDir, name)\n }\n\n if (includes.some(item => item.includes('docs'))) {\n cpSync(resolve(projectDir, 'docs'), projectDir, { recursive: true })\n rmSync(resolve(projectDir, 'docs'), { recursive: true })\n }\n rmSync(resolve(projectDir, '.git'), { recursive: true })\n return true\n } catch (error) {\n log(` Error when cloning repository '${name}': ${formatError(error)}.`, 'error')\n return false\n }\n}\n","import type { GlobalOpts } from '../schemas/global.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { log } from '../utils/logger.js'\nimport type { getInfos as getGithubInfos } from './git.js'\n\n/**\n * Base URL of the GitLab REST API\n */\nexport const GITLAB_API_URL = 'https://gitlab.com/api/v4'\n\ntype User = Awaited<ReturnType<typeof getGithubInfos>>['user']\ntype Repository = Awaited<ReturnType<typeof getGithubInfos>>['repos'][number]\n\n/**\n * Parsed GitLab API response paired with its HTTP headers (used for pagination)\n */\ninterface GitlabResponse<T> {\n data: T\n headers: Headers\n}\n\n/**\n * Subset of the GitLab user/group API response consumed by DocPress\n */\ninterface GitlabUser {\n id: number\n username?: string\n path?: string\n name?: string\n bio?: string | null\n description?: string | null\n avatar_url?: string\n web_url?: string\n}\n\n/**\n * Subset of the GitLab project API response consumed by DocPress\n */\ninterface GitlabProject {\n path: string\n forked_from_project?: unknown\n visibility?: string\n default_branch?: string\n http_url_to_repo?: string\n web_url?: string\n description?: string | null\n star_count?: number\n}\n\n/**\n * Performs a GET request against the GitLab API\n *\n * @param path - API path starting with '/'\n * @param token - Optional GitLab token\n * @returns Object containing the parsed JSON body and the response headers\n * @throws Error if the request fails\n */\nasync function gitlabGet<T>(path: string, token?: GlobalOpts['token']): Promise<GitlabResponse<T>> {\n const response = await fetch(`${GITLAB_API_URL}${path}`, {\n headers: token ? { 'PRIVATE-TOKEN': token } : {},\n })\n if (!response.ok) {\n throw new Error(`GitLab API request failed for '${path}' (status: ${response.status}).`)\n }\n return { data: await response.json() as T, headers: response.headers }\n}\n\n/**\n * Fetches all pages of a GitLab API collection\n *\n * @param path - API path starting with '/' (query params allowed)\n * @param token - Optional GitLab token\n * @returns Array containing all items from all pages\n */\nasync function gitlabGetAll<T>(path: string, token?: GlobalOpts['token']): Promise<T[]> {\n const separator = path.includes('?') ? '&' : '?'\n const items: T[] = []\n let page: string | null = '1'\n\n while (page) {\n const res: GitlabResponse<T[]> = await gitlabGet<T[]>(`${path}${separator}per_page=100&page=${page}`, token)\n items.push(...res.data)\n page = res.headers.get('x-next-page') || null\n }\n return items\n}\n\n/**\n * Maps a GitLab user or group to the internal (GitHub-derived) user shape\n *\n * @param user - GitLab user or group object\n * @returns User object with the fields used by DocPress\n */\nfunction mapUser(user: GitlabUser) {\n return {\n login: user.username ?? user.path,\n name: user.name,\n bio: user.bio ?? user.description ?? null,\n avatar_url: user.avatar_url,\n html_url: user.web_url,\n } as unknown as User\n}\n\n/**\n * Maps a GitLab project to the internal (GitHub-derived) repository shape\n *\n * @param project - GitLab project object\n * @param owner - Login of the user or group that owns the project\n * @returns Repository object with the fields used by DocPress\n */\nfunction mapRepository(project: GitlabProject, owner: string) {\n return {\n name: project.path,\n owner: { login: owner },\n fork: !!project.forked_from_project,\n private: project.visibility !== 'public',\n // An empty repository has no default branch\n size: project.default_branch ? 1 : 0,\n default_branch: project.default_branch,\n clone_url: project.http_url_to_repo,\n html_url: project.web_url,\n description: project.description,\n stargazers_count: project.star_count,\n } as unknown as Repository\n}\n\n/**\n * Fetches user and repository information from GitLab\n * Falls back to the groups API when no user matches the given name\n *\n * @param options - Options for retrieving GitLab data\n * @param options.username - GitLab username or group path\n * @param options.token - GitLab API token\n * @param options.branch - Branch to use for documentation\n * @returns Object containing user information, repositories, and branch\n */\nexport async function getInfos({ username, token, branch }: Pick<FetchOpts, 'branch'> & Pick<GlobalOpts, 'token'> & { username: GlobalOpts['usernames'][number] }) {\n log(` Get infos for username '${username}'.`, 'info')\n\n const { data: users } = await gitlabGet<GitlabUser[]>(`/users?username=${encodeURIComponent(username)}`, token)\n\n if (users.length) {\n let user = users[0]\n if (token) {\n // The user detail endpoint requires authentication but provides extra fields (e.g. bio)\n user = await gitlabGet<GitlabUser>(`/users/${users[0].id}`, token).then(res => res.data).catch(() => users[0])\n }\n log(` Get repositories infos.`, 'debug')\n const projects = await gitlabGetAll<GitlabProject>(`/users/${users[0].id}/projects`, token)\n return { user: mapUser(user), repos: projects.map(project => mapRepository(project, user.username ?? username)), branch }\n }\n\n log(` No GitLab user '${username}' found, trying groups.`, 'debug')\n const group = await gitlabGet<GitlabUser>(`/groups/${encodeURIComponent(username)}?with_projects=false`, token)\n .then(res => res.data)\n .catch(() => {\n throw new Error(`No GitLab user or group found for '${username}'.`)\n })\n const projects = await gitlabGetAll<GitlabProject>(`/groups/${encodeURIComponent(username)}/projects`, token)\n return { user: mapUser(group), repos: projects.map(project => mapRepository(project, group.path ?? username)), branch }\n}\n","import { resolve } from 'node:path'\nimport { writeFileSync } from 'node:fs'\nimport type { FetchOpts, FetchOptsUser } from '../schemas/fetch.js'\nimport { checkHttpStatus, prettify, sanitizeSegment } from '../utils/functions.js'\nimport { DOCPRESS_DIR, DOCS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { cloneRepo, getInfos } from './git.js'\nimport { getInfos as getGitlabInfos } from './gitlab.js'\n\n/**\n * Enhanced repository type with DocPress-specific metadata\n */\nexport type EnhancedRepository = Awaited<ReturnType<typeof getInfos>>['repos'][number] & {\n docpress: {\n branch: string\n filtered: boolean\n includes: string[]\n projectPath: string\n raw_url: string\n replace_url: string\n /** URL/path prefix ('' or '<username>/') used to namespace multi-user runs */\n routePrefix: string\n }\n}\n\n/**\n * Web URLs of a repository for a given branch, depending on the Git provider\n */\nexport interface ProviderUrls {\n blob_url: string\n tree_url: string\n raw_url: string\n}\n\n/**\n * Builds the web URLs of a repository for a given branch and Git provider\n *\n * @param repo - Repository information\n * @param branch - Branch to build URLs for\n * @param gitProvider - Git provider used to retrieve data\n * @returns Object containing blob, tree and raw base URLs\n */\nexport function getProviderUrls(repo: Awaited<ReturnType<typeof getInfos>>['repos'][number], branch: FetchOpts['branch'], gitProvider?: FetchOpts['gitProvider']): ProviderUrls {\n if (gitProvider === 'gitlab') {\n const base = repo.html_url ?? `https://gitlab.com/${repo.owner.login}/${repo.name}`\n return {\n blob_url: `${base}/-/blob/${branch}`,\n tree_url: `${base}/-/tree/${branch}`,\n raw_url: `${base}/-/raw/${branch}`,\n }\n }\n const base = repo.html_url ?? `https://github.com/${repo.owner.login}/${repo.name}`\n return {\n blob_url: `${base}/blob/${branch}`,\n tree_url: `${base}/tree/${branch}`,\n raw_url: `https://raw.githubusercontent.com/${repo.owner.login}/${repo.name}/${branch}`,\n }\n}\n\n/**\n * Checks for the existence of documentation files in a repository\n *\n * @param urls - Web URLs of the repository for the target branch\n * @returns Object containing HTTP status codes for different documentation paths\n */\nexport async function checkDoc(urls: ProviderUrls) {\n const [rootReadmeStatus, docsFolderStatus, docsReadmeStatus] = await Promise.all([\n checkHttpStatus(`${urls.blob_url}/README.md`),\n checkHttpStatus(`${urls.tree_url}/docs`),\n checkHttpStatus(`${urls.blob_url}/docs/01-readme.md`),\n ])\n\n return {\n rootReadmeStatus,\n docsFolderStatus,\n docsReadmeStatus,\n }\n}\n\n/**\n * Fetches documentation from a user's repositories\n *\n * @param options - Options for fetching documentation\n * @param options.username - Git provider username\n * @param options.branch - Branch to use for documentation\n * @param options.reposFilter - Optional filter for repositories\n * @param options.gitProvider - Git provider used to retrieve data\n * @param options.token - Git provider API token\n * @param options.lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n * @param options.multiUser - Whether the run spans multiple usernames; namespaces paths/routes by username to avoid collisions\n */\nexport async function fetchDoc({ username, branch, reposFilter, gitProvider, token, lastUpdated, multiUser }: FetchOptsUser & { multiUser?: boolean }) {\n const getProviderInfos = gitProvider === 'gitlab' ? getGitlabInfos : getInfos\n const { user, repos, branch: resolvedBranch } = await getProviderInfos({ username, token, branch })\n // In multi-user runs, namespace on-disk paths and site routes by username so\n // two users owning a same-named repository do not collide (data loss / merged routes)\n const routePrefix = multiUser ? `${sanitizeSegment(username)}/` : ''\n const { repos: enhancedRepos } = await generateInfos(user, repos, resolvedBranch, reposFilter, gitProvider, routePrefix)\n await getDoc(enhancedRepos, reposFilter, lastUpdated)\n}\n\n/**\n * Enhances repository information with DocPress metadata\n *\n * @param repos - List of repositories from Git provider\n * @param branch - Branch to use for documentation\n * @param reposFilter - Optional filter for repositories\n * @returns Array of enhanced repositories with DocPress metadata\n */\nexport async function enhanceRepositories(repos: Awaited<ReturnType<typeof getInfos>>['repos'], branch?: FetchOpts['branch'], reposFilter?: FetchOpts['reposFilter'], gitProvider?: FetchOpts['gitProvider'], routePrefix: string = '') {\n const enhancedRepos: EnhancedRepository[] = []\n\n await Promise.all(\n repos.map(async (repo) => {\n const computedBranch = branch ?? repo.default_branch ?? 'main'\n const projectPath = resolve(DOCS_DIR, `${routePrefix}${prettify(sanitizeSegment(repo.name), { removeDot: true })}`)\n const filtered = isRepoFiltered(repo, reposFilter)\n const urls = getProviderUrls(repo, computedBranch, gitProvider)\n let includes: string[] = []\n\n if (!repo.fork && !repo.private && !filtered) {\n includes = await getSparseCheckout(repo, computedBranch, gitProvider, urls)\n }\n\n enhancedRepos.push({\n ...repo,\n docpress: {\n filtered,\n branch: computedBranch,\n includes,\n projectPath,\n raw_url: urls.raw_url,\n replace_url: urls.tree_url,\n routePrefix,\n },\n })\n }),\n )\n\n return enhancedRepos\n}\n\n/**\n * Determines which files to include in the sparse checkout based on repository structure\n *\n * @param repo - Repository information\n * @param branch - Branch to check for documentation\n * @param gitProvider - Git provider used to retrieve data\n * @param urls - Optional pre-computed provider URLs to avoid recomputing them\n * @returns Array of file patterns to include in sparse checkout\n */\nexport async function getSparseCheckout(repo: Awaited<ReturnType<typeof getInfos>>['repos'][number], branch: FetchOpts['branch'], gitProvider?: FetchOpts['gitProvider'], urls?: ProviderUrls) {\n const docsStatus = await checkDoc(urls ?? getProviderUrls(repo, branch, gitProvider))\n\n const includes: string[] = []\n if (Object.values(docsStatus).every(status => status !== 200) || !repo.size) {\n return []\n }\n if (docsStatus.docsFolderStatus === 200) {\n includes.push('docs/*')\n }\n if (docsStatus.docsFolderStatus !== 200 || docsStatus.docsReadmeStatus !== 200) {\n includes.push('README.md', '!*/**/README.md')\n }\n return includes\n}\n\n/**\n * Generates and saves user and repository information\n *\n * @param user - User information from Git provider\n * @param repos - List of repositories from Git provider\n * @param branch - Branch to use for documentation\n * @param reposFilter - Optional filter for repositories\n * @param gitProvider - Git provider used to retrieve data\n * @returns Object containing user and enhanced repository information\n */\nexport async function generateInfos(user: Awaited<ReturnType<typeof getInfos>>['user'], repos: Awaited<ReturnType<typeof getInfos>>['repos'], branch?: FetchOpts['branch'], reposFilter?: FetchOpts['reposFilter'], gitProvider?: FetchOpts['gitProvider'], routePrefix: string = '') {\n writeFileSync(`${DOCPRESS_DIR}/user-${sanitizeSegment(user.login)}.json`, JSON.stringify(user, null, 2))\n\n const enhancedRepos = await enhanceRepositories(repos, branch, reposFilter, gitProvider, routePrefix)\n writeFileSync(`${DOCPRESS_DIR}/repos-${sanitizeSegment(user.login)}.json`, JSON.stringify(enhancedRepos, null, 2))\n\n return { user, repos: enhancedRepos }\n}\n\n/**\n * Fetches documentation from repositories\n *\n * @param repos - List of enhanced repositories\n * @param reposFilter - Optional filter for repositories\n * @param lastUpdated - Whether or not to inject each page's last Git commit date as frontmatter\n */\nexport async function getDoc(repos?: EnhancedRepository[], reposFilter?: FetchOpts['reposFilter'], lastUpdated?: boolean) {\n if (!repos) {\n log(` No repositories to process.`, 'warn')\n return\n }\n\n const results = await Promise.all(\n repos\n .filter(repo => !isRepoFiltered(repo, reposFilter))\n .map(async (repo) => {\n const success = await cloneRepo(repo.name, repo.clone_url as string, repo.docpress.projectPath, repo.docpress.branch, repo.docpress.includes, lastUpdated)\n return { name: repo.name, success }\n }),\n )\n\n if (!results.length) {\n log(` No repository matched the current filters.`, 'warn')\n return\n }\n\n const failed = results.filter(({ success }) => !success).map(({ name }) => name)\n const succeeded = results.length - failed.length\n if (failed.length) {\n log(` Cloned ${succeeded}/${results.length} repository(ies), ${failed.length} failed: ${failed.join(', ')}.`, 'warn')\n } else {\n log(` Cloned ${succeeded}/${results.length} repository(ies).`, 'success')\n }\n}\n\n/**\n * Determines if a repository should be filtered out based on filter rules\n *\n * @param repo - Repository information\n * @param reposFilter - Optional filter for repositories\n * @returns True if the repository should be filtered out, false otherwise\n */\nexport function isRepoFiltered(repo: EnhancedRepository | Awaited<ReturnType<typeof getInfos>>['repos'][number], reposFilter?: FetchOpts['reposFilter']) {\n if ('docpress' in repo && !repo.docpress.includes.length) {\n return true\n }\n\n const inclusions = reposFilter?.filter((filter: string) => !filter.startsWith('!')) ?? []\n const hasOnlyExclusions = reposFilter?.every((filter: string) => filter.startsWith('!'))\n const isExcluded = reposFilter?.filter((filter: string) => filter.startsWith('!'))\n .some((filter: string) => repo.name === filter.substring(1))\n\n const isIncluded = !reposFilter\n || inclusions.includes(repo.name)\n || (repo.fork && !isExcluded)\n || (hasOnlyExclusions && !isExcluded)\n\n const isFiltered = isExcluded || !isIncluded\n\n return !(!!repo.clone_url && !repo.private && !isFiltered)\n}\n","import { createOption } from 'commander'\nimport { cliSchema } from '../schemas/global.js'\n\n/**\n * Global command line options shared across all commands\n */\nexport const globalOpts = [\n createOption('-C, --config <string>', cliSchema.shape.config.description),\n createOption('--log-level <string>', cliSchema.shape.logLevel.description),\n createOption('-T, --token <string>', cliSchema.shape.token.description),\n createOption('-U, --usernames <string>', cliSchema.shape.usernames.description),\n]\n","import { createCommand, createOption } from 'commander'\nimport { createDir, formatDuration, formatError } from '../utils/functions.js'\nimport { DOCPRESS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { addOptions, explicitOptions, parseOptions } from '../utils/commands.js'\nimport type { FetchOpts } from '../schemas/fetch.js'\nimport { fetchDoc } from '../lib/fetch.js'\nimport { configSchema } from '../schemas/global.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Command name for the fetch operation\n */\nconst cmdName = 'fetch'\n\n/**\n * Command line options specific to the fetch command\n */\nexport const fetchOpts = [\n createOption('-b, --branch <string>', configSchema.shape.branch.description),\n createOption('-g, --git-provider <string>', configSchema.shape.gitProvider.description)\n .default(configSchema.shape.gitProvider.def.defaultValue),\n createOption('-l, --last-updated', configSchema.shape.lastUpdated.description),\n createOption('-r, --repos-filter <string>', configSchema.shape.reposFilter.description),\n]\n\n/**\n * The fetch command definition for Commander\n */\nexport const fetchCmd = addOptions(createCommand(cmdName), [...fetchOpts, ...globalOpts])\n .description('Fetch docs with the given username(s) and git provider.')\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions(cmdName, explicitOptions(cmd, opts))\n await main(parsedOpts)\n })\n\n/**\n * Main function to execute the fetch command\n *\n * @param opts - Validated fetch options\n */\nexport async function main(opts: FetchOpts) {\n const { usernames, reposFilter, token, branch, gitProvider, lastUpdated } = opts\n log(`\\n-> Start fetching documentation files. This may take a moment, especially for larger repositories.`, 'info')\n const start = Date.now()\n\n createDir(DOCPRESS_DIR, { clean: true })\n const failedUsers: string[] = []\n for (const username of usernames) {\n // With multiple users, filters are scoped as '<username>/<repo>' or '!<username>/<repo>'\n const finalRF = usernames.length > 1\n ? reposFilter?.filter((rf: string) => rf.replace(/^!/, '').startsWith(`${username}/`)).map((rf: string) => rf.replace(`${username}/`, ''))\n : reposFilter\n try {\n await fetchDoc({ username, branch, reposFilter: finalRF, gitProvider, token, lastUpdated, multiUser: usernames.length > 1 })\n } catch (error) {\n failedUsers.push(username)\n log(` Failed to fetch documentation for '${username}': ${formatError(error)}`, 'error')\n }\n }\n\n if (failedUsers.length === usernames.length) {\n throw new Error(`Failed to fetch documentation for all requested username(s): ${failedUsers.join(', ')}.`)\n }\n\n const succeeded = usernames.length - failedUsers.length\n log(` Fetched documentation for ${succeeded}/${usernames.length} username(s) in ${formatDuration(Date.now() - start)}.`, failedUsers.length ? 'warn' : 'success')\n}\n","import { build as vitepressBuild } from 'vitepress'\nimport type { UserConfig } from 'vitepress'\nimport { withMermaid } from 'vitepress-plugin-mermaid'\nimport { createCommand } from 'commander'\nimport { addOptions } from '../utils/commands.js'\nimport { DOCPRESS_DIR } from '../utils/const.js'\nimport { log } from '../utils/logger.js'\nimport { formatDuration, formatError } from '../utils/functions.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Suppress specific Vue warnings during the build process\n *\n * @param callback - Function to execute while warnings are suppressed\n * @returns The result of the callback function\n */\nexport async function suppressVueWarnings<T>(callback: () => Promise<T>): Promise<T> {\n // Store the original console.warn\n const originalWarn = console.warn\n\n // Override console.warn to suppress two specific, noisy Vue/Vitepress warnings\n // and forward everything else, untouched, to the real console.warn.\n //\n // These must NOT be routed through log(): log() writes via console.warn, which\n // is this very override, so forwarding a non-suppressed warning through it would\n // recurse until the stack overflows and aborts the whole Vitepress build. Vite's\n // \"chunks are larger than 500 kB\" warning is exactly such a case, which is why\n // the build failed with \"[vite:reporter] Maximum call stack size exceeded\".\n console.warn = function (...args) {\n if (\n args.length > 0\n && typeof args[0] === 'string'\n && (args[0].includes('[Vue warn]: Invalid watch source:')\n || args[0].includes('{ open: false }'))\n ) {\n // Skip this warning\n return\n }\n // Pass other warnings straight to the original console.warn\n originalWarn.apply(console, args)\n }\n\n try {\n // Execute the callback with warnings suppressed\n return await callback()\n } finally {\n // Restore the original console.warn\n console.warn = originalWarn\n }\n}\n\n/**\n * Command name for the build operation\n */\nconst cmdName = 'build'\n\n/**\n * The build command definition for Commander\n */\nexport const buildCmd = addOptions(createCommand(cmdName), globalOpts)\n .description('Build vitepress website.')\n .action(async (_opts) => {\n await main()\n })\n\n/**\n * Main function to execute the build command\n * Builds the VitePress static site\n */\nexport async function main() {\n log(`\\n-> Start building Vitepress website.\\n\\n`, 'info')\n const start = Date.now()\n\n try {\n // Build VitePress with Vue warnings suppressed\n await suppressVueWarnings(async () => {\n await vitepressBuild(DOCPRESS_DIR, {\n onAfterConfigResolve(siteConfig) {\n withMermaid(siteConfig as unknown as UserConfig)\n },\n })\n })\n } catch (error) {\n // Re-thrown (not logged/exited here) so cli.ts's single top-level catch\n // is the only place that reports a failure and sets the exit code.\n throw new Error(`Docpress build failed: ${formatError(error)}`)\n }\n\n log(`\\n\\nDocpress build succeeded in ${formatDuration(Date.now() - start)}.`, 'success')\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createDir } from './functions.js'\n\n/**\n * Generates a file by copying content from source to destination\n *\n * @param src - Path to the source file\n * @param dest - Path to the destination file\n */\nexport function generateFile(src: string, dest: string) {\n const content = readFileSync(src, { encoding: 'utf8' })\n createDir(dirname(dest))\n writeFileSync(dest, content)\n}\n","import type { DefaultTheme, defineConfig } from 'vitepress'\nimport { deepMerge } from '../utils/functions.js'\nimport type { Page } from './prepare.js'\n\n/**\n * Default VitePress configuration\n */\nconst defaultVitepressConfig: ReturnType<typeof defineConfig> = {\n base: '/',\n lang: 'en-US',\n vite: {\n publicDir: '../public',\n },\n title: 'Home',\n description: 'Docpress',\n srcDir: './docs',\n cleanUrls: false,\n ignoreDeadLinks: 'localhostLinks',\n themeConfig: {\n outline: [2, 3],\n search: {\n provider: 'local',\n options: {\n translations: {\n button: {\n buttonText: 'Search...',\n buttonAriaLabel: 'Search',\n },\n modal: {\n backButtonTitle: 'erase search',\n displayDetails: 'show details',\n noResultsText: 'No results for : ',\n resetButtonTitle: 'cancel search',\n footer: {\n selectText: 'go to',\n navigateText: 'navigate in results',\n closeText: 'close',\n },\n },\n },\n },\n },\n },\n}\n\n/**\n * Generates VitePress configuration by merging default and custom configs\n *\n * @param sidebar - Sidebar configuration, either one flat list or one sidebar per route\n * @param nav - Navigation configuration\n * @param vitepressConfig - Optional custom VitePress configuration\n * @returns Merged VitePress configuration\n */\nexport function getVitepressConfig(sidebar: DefaultTheme.Sidebar, nav: Page[], vitepressConfig?: ReturnType<typeof defineConfig>): Partial<ReturnType<typeof defineConfig>> {\n const config = vitepressConfig\n ? deepMerge(defaultVitepressConfig, vitepressConfig, { themeConfig: { sidebar, nav } })\n : deepMerge(defaultVitepressConfig, { themeConfig: { sidebar, nav } })\n return config\n}\n","import type { DefaultTheme, defineConfig } from 'vitepress'\nimport type { GlobalOpts } from '../schemas/global.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport type { EnhancedRepository } from './fetch.js'\nimport type { getInfos } from './git.js'\nimport { appendFileSync, cpSync, existsSync, readdirSync, renameSync, writeFileSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { basename, dirname, parse, resolve } from 'node:path'\nimport YAML from 'yaml'\nimport { DOCPRESS_DIR, DOCS_DIR, FORKS_FILE, INDEX_FILE, TEMPLATE_THEME, VITEPRESS_CONFIG, VITEPRESS_THEME, VITEPRESS_USER_THEME } from '../utils/const.js'\nimport { createDir, extractFiles, formatError, getMdFiles, getUserInfos, getUserRepos, isFile, prettify, replaceInternalMdLinks, replaceReadmePath, replaceRelativePath } from '../utils/functions.js'\nimport { log } from '../utils/logger.js'\nimport { generateFile } from '../utils/templates.js'\nimport { getContributors } from './git.js'\nimport { getVitepressConfig } from './vitepress.js'\n\n/**\n * Represents a navigation page item with text and link\n */\nexport interface Page {\n text: string\n link: string\n}\n\n/**\n * Represents a sidebar project with nested items\n */\nexport interface SidebarProject {\n text: string\n collapsed?: boolean\n items: (Page | SidebarProject)[]\n}\n\n/**\n * Collapse behaviour applied to generated sidebar groups, mirroring Vitepress\n * semantics: `true` collapses groups by default, `false` expands them and\n * `null` makes them plain, non-collapsible sections\n */\nexport type SidebarCollapsed = boolean | null\n\n/**\n * Shape of the generated sidebar: `single` emits one flat list holding every\n * repository, `multi` emits one sidebar per repository keyed by its route so\n * each project only shows its own pages\n */\nexport type SidebarMode = 'single' | 'multi'\n\n/**\n * Options driving sidebar generation\n */\nexport interface SidebarOpts {\n mode?: SidebarMode\n collapsed?: SidebarCollapsed\n /** Words kept exactly as written in every generated label, home page titles included */\n acronyms?: string[]\n}\n\n/**\n * Builds the Vitepress `collapsed` property for a sidebar group, omitting it\n * entirely when groups should not be collapsible\n *\n * @param collapsed - Configured collapse behaviour\n * @returns An object holding the `collapsed` key, or an empty object\n */\nfunction collapsedProp(collapsed: SidebarCollapsed) {\n return collapsed === null ? {} : { collapsed }\n}\n\n/**\n * Represents a feature to display on the index page\n */\nexport interface Feature {\n title: string\n details: string\n link: string\n}\n\n/**\n * Represents the structure of the VitePress index page\n */\nexport interface Index {\n layout: string\n hero: {\n name: string\n tagline?: string\n }\n /** List of features to display */\n features: Feature[]\n}\n\n/**\n * Prepares the documentation structure for a user's repositories\n *\n * @param options - Options for documentation preparation\n * @param options.extraHeaderPages - Array of paths to additional header pages\n * @param options.extraPublicContent - Array of paths to additional public content\n * @param options.extraTheme - Array of paths to additional theme files\n * @param options.vitepressConfig - Path to the VitePress configuration file\n * @param options.forks - Flag to include forked repositories\n * @param options.gitProvider - Git provider used to retrieve data\n * @param options.lastUpdated - Flag to display each page's last Git commit date, computed by the fetch step\n * @param options.sidebarMode - Shape of the generated sidebar, one flat list or one sidebar per repository\n * @param options.sidebarCollapsed - Collapse behaviour applied to generated sidebar groups\n * @param options.acronyms - Words kept exactly as written in generated labels\n * @param options.token - Git provider token for API access\n * @param options.username - Git provider username to fetch repositories for\n * @param options.websiteTitle - Custom title for the documentation website\n * @param options.websiteTagline - Custom tagline for the documentation website\n */\nexport async function prepareDoc({ extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, acronyms, token, username, websiteTitle, websiteTagline }: Omit<PrepareOpts, 'usernames' | 'branch' | 'reposFilter'> & { username: PrepareOpts['usernames'][number] }) {\n // The forks page relies on matching contributors by login, which the GitLab API does not expose\n const forksEnabled = forks && gitProvider !== 'gitlab'\n if (forks && !forksEnabled) {\n log(` The forks page is not supported with the 'gitlab' provider, skipping.`, 'warn')\n }\n const user = getUserInfos(username)\n const repositories = getUserRepos(username)\n .reduce(({ internals, forks }: { internals: EnhancedRepository[], forks: EnhancedRepository[] }, cur) => {\n const { clone_url, private: privateRepo, fork, docpress } = cur\n if (clone_url && !privateRepo && !docpress.filtered) {\n if (!fork && docpress.includes.length) {\n return { internals: [...internals, cur], forks }\n } else if (fork) {\n return { internals, forks: [...forks, cur] }\n }\n }\n return { internals, forks }\n }, { internals: [], forks: [] })\n\n const websiteInfos = { title: websiteTitle, tagline: websiteTagline }\n const { index, sidebar } = transformDoc(repositories.internals, user, websiteInfos, { mode: sidebarMode, collapsed: sidebarCollapsed, acronyms })\n\n let finalSB\n let finalIndex\n if (existsSync(INDEX_FILE) && existsSync(VITEPRESS_CONFIG)) {\n const actualConfig = await parseVitepressConfig(VITEPRESS_CONFIG)\n const actualIndex = await parseVitepressIndex(INDEX_FILE)\n finalSB = mergeSidebars(actualConfig.themeConfig?.sidebar, sidebar)\n finalIndex = {\n ...index,\n features: [\n ...actualIndex.features,\n ...index.features,\n ].sort((a, b) => a.title.localeCompare(b.title)),\n }\n } else {\n finalSB = sidebar\n finalIndex = index\n }\n const nav: Page[] = []\n\n if (extraHeaderPages) {\n const pages = addExtraPages(extraHeaderPages)\n if (forksEnabled) {\n nav.push(...pages.filter(p => p.link !== '/forks'))\n } else {\n nav.push(...pages)\n }\n }\n if (extraPublicContent) {\n log(` Add extras Vitepress public folder content.`, 'info')\n addContent(extraPublicContent, resolve(DOCPRESS_DIR, 'public'))\n }\n if (extraTheme) {\n log(` Add extras Vitepress theme files.`, 'info')\n addContent(extraTheme, resolve(VITEPRESS_USER_THEME))\n }\n if (forksEnabled && username.length) {\n log(` Add fork page to display external contributions.`, 'info')\n await processForks(repositories.forks, username, token)\n nav.push({ text: 'Forks', link: '/forks' })\n }\n\n // Vitepress only renders the last updated date once its own \"lastUpdated\" flag is on;\n // default it to true when the docpress equivalent is enabled, without overriding an explicit user choice\n const finalVitepressConfig = lastUpdated ? { lastUpdated: true, ...vitepressConfig } : vitepressConfig\n const config = getVitepressConfig(finalSB, nav, finalVitepressConfig)\n\n generateVitepressFiles(config, finalIndex)\n}\n\n/**\n * Adds a source reference section to a markdown file\n *\n * @param repoUrl - URL of the GitHub repository\n * @param outputPath - Path to the markdown file where sources will be added\n */\nexport function addSources(repoUrl: string, outputPath: string) {\n const fileName = basename(outputPath)\n const title = prettify(fileName, { mode: 'lowercase', removeIdx: true }) === 'readme.md' ? '\\n## Sources' : '# Sources'\n\n const sourcesContent = `${title}\\n\\nTake a look at the [project sources](${repoUrl}).\\n`\n\n appendFileSync(outputPath, sourcesContent, 'utf8')\n}\n\n/**\n * Information about website title and tagline\n */\nexport interface WebsiteInfos {\n title?: string\n tagline?: string\n}\n\n/**\n * Generates the index page content for the documentation site\n *\n * @param features - Array of features to display on the homepage\n * @param user - User information retrieved from GitHub\n * @param websiteInfos - Custom title and tagline information\n * @returns An object with index page configuration\n */\nexport function generateIndex(features: Feature[], user: ReturnType<typeof getUserInfos>, websiteInfos: WebsiteInfos) {\n const { name, login, bio } = user\n const { title, tagline } = websiteInfos\n\n const hero = title\n ? { name: title, tagline }\n : { name: name ? `${name}'s projects` : `${login}'s projects`, tagline: bio ?? 'Robots are everywhere 🤖' }\n\n return {\n layout: 'home',\n hero,\n features,\n }\n}\n\n/**\n * Generates feature cards for the homepage\n *\n * @param repoName - Name of the repository\n * @param description - Description of the repository\n * @param features - Optional existing features to append to\n * @param routePrefix - Route prefix namespacing the repository in multi-user runs\n * @param acronyms - Words kept exactly as written in the title\n * @returns Array of feature objects for the homepage\n */\nexport function generateFeatures(repoName: string, description: string, features?: Feature[], routePrefix: string = '', acronyms: string[] = []) {\n const content = {\n title: prettify(repoName, { mode: 'capitalize', replaceDash: true, acronyms }),\n details: description,\n link: `/${routePrefix}${prettify(repoName, { removeDot: true })}/introduction`,\n }\n\n return features ? [...features, content] : [content]\n}\n\n/**\n * Generates a sidebar project entry for the documentation navigation\n *\n * @param repoName - Name of the repository\n * @param sidebarPages - Array of sidebar pages to include in this project\n * @param collapsed - Collapse behaviour applied to the generated group\n * @param acronyms - Words kept exactly as written in the group label\n * @returns A sidebar project configuration object\n */\nexport function generateSidebarProject(repoName: string, sidebarPages: (SidebarProject | Page)[], collapsed: SidebarCollapsed = true, acronyms: string[] = []) {\n return {\n text: prettify(repoName, { mode: 'capitalize', replaceDash: true, acronyms }),\n ...collapsedProp(collapsed),\n items: sidebarPages,\n }\n}\n\n/**\n * Generates sidebar page entries for the documentation navigation\n *\n * @param repoName - Name of the repository\n * @param fileName - Name of the file\n * @param sidebarPages - Optional existing sidebar pages to append to\n * @returns Array of page objects for the sidebar\n */\nexport function generateSidebarPages(repoName: string, fileName: string, sidebarPages?: Page[]) {\n const content = {\n text: fileName === 'introduction' ? 'Introduction' : prettify(fileName, { mode: 'capitalize', replaceDash: true }),\n link: `/${prettify(repoName, { removeDot: true })}/${fileName}`,\n }\n\n return sidebarPages ? [...sidebarPages, content] : [content]\n}\n\n/**\n * Generates sidebar items from a repository's file structure\n *\n * @param repository - Repository information\n * @param obj - Object representing the file tree structure\n * @param collapsed - Collapse behaviour applied to generated folder groups\n * @param acronyms - Words kept exactly as written in page and folder labels\n * @returns Array of sidebar items (projects and pages)\n */\nexport function generateSidebarItems(repository: EnhancedRepository, obj: any, collapsed: SidebarCollapsed = true, acronyms: string[] = []): (SidebarProject | Page)[] {\n return Object.entries(obj).flatMap(([key, value]): (SidebarProject | Page)[] => {\n if (key === '$') {\n if (Array.isArray(value)) {\n return value.map((element) => {\n const file = resolve(repository.docpress.projectPath, element)\n let filename = prettify(basename(file), { mode: 'lowercase', removeIdx: true })\n if (filename === 'readme.md') {\n filename = 'introduction.md'\n }\n if (filename !== basename(file)) {\n renameSync(file, resolve(dirname(file), filename))\n }\n\n return {\n text: parse(filename).name === 'introduction'\n ? 'Introduction'\n : prettify(filename, { mode: 'capitalize', replaceDash: true, removeExt: true, acronyms }),\n link: `/${repository.docpress.routePrefix ?? ''}${prettify(repository.name, { removeDot: true })}/${parse(filename).name}`,\n } as Page\n })\n }\n return []\n } else if (typeof value === 'object') {\n // Folders carry the ordering prefix for the same reason files do, and it is\n // stripped the same way. Without this a folder is ordered alphabetically and\n // nothing can change that, so a project whose sections have a reading order —\n // install before configure before reference — cannot express it at all, while\n // the files inside those sections can. The prefix also has to leave the path,\n // not just the label: the route is built from the folder name, so a group\n // reading 'Guide' whose pages live under '01-guide' would link into nothing.\n const folder = prettify(key, { removeIdx: true })\n const source = resolve(repository.docpress.projectPath, key)\n const renamed = resolve(repository.docpress.projectPath, folder)\n if (folder !== key) {\n renameSync(source, renamed)\n }\n\n return [{\n text: prettify(folder, { mode: 'capitalize', replaceDash: true, acronyms }),\n ...collapsedProp(collapsed),\n items: generateSidebarItems({\n ...repository,\n name: `${repository.name}/${folder}`,\n // Descend into the subfolder so file renames target the nested file and\n // not a same-named file at the repository root\n docpress: { ...repository.docpress, projectPath: renamed },\n }, value, collapsed, acronyms),\n } as SidebarProject]\n }\n\n return []\n })\n}\n\n/**\n * Builds a tree structure from an array of file paths\n *\n * @param files - Array of file paths to be organized into a tree\n * @returns A nested object representing the directory structure\n */\nexport function buildTree(files: string[]): any {\n return files.reduce((tree, file) => {\n const [first, ...rest] = file.split('/')\n if (!rest.length) {\n // eslint-disable-next-line dot-notation\n tree['$'] = [...(tree['$'] || []), first]\n } else {\n tree[first] = buildTree([\n ...(tree[first] ? flattenTree(tree[first], '') : []),\n rest.join('/'),\n ])\n }\n return tree\n }, {} as Record<string, any>)\n}\n\n/**\n * Flattens a nested tree structure into an array of file paths\n *\n * @param subtree - The tree object to flatten\n * @param prefix - Optional path prefix to prepend to each result\n * @returns An array of file paths\n */\nexport function flattenTree(subtree: any, prefix = ''): string[] {\n return Object.entries(subtree).flatMap(([key, value]) => {\n if (key === '$') {\n if (Array.isArray(value)) {\n return value.map((v: string) => (prefix ? `${prefix}/${v}` : v))\n } else {\n return []\n }\n }\n return flattenTree(value, prefix ? `${prefix}/${key}` : key)\n })\n}\n\n/**\n * Reorders sidebar items to ensure 'Sources' appears last\n *\n * @param arr - Array of sidebar items to reorder\n * @returns Reordered array with 'Sources' as the last item\n */\nexport function moveSourcesLast(arr: (SidebarProject | Page)[]) {\n if (!Array.isArray(arr)) {\n return arr\n }\n const sourcesIdx = arr.findIndex(item => item.text === 'Sources')\n if (sourcesIdx === -1) {\n return arr\n }\n const [sources] = arr.splice(sourcesIdx, 1)\n arr.push(sources)\n return arr\n}\n\n/**\n * Deepest sidebar level Vitepress renders. Its `VPSidebarGroup` mounts top level\n * items at depth 0 and `VPSidebarItem` only recurses while `depth < 5`, so the\n * children of a group sitting at this depth are silently dropped\n */\nconst MAX_SIDEBAR_DEPTH = 5\n\n/**\n * Finds sidebar groups nested so deeply that Vitepress will not render their\n * children, so the caller can warn instead of silently losing navigation\n *\n * @param items - Sidebar items to inspect\n * @param depth - Depth at which those items are rendered\n * @param trail - Group texts walked so far, used to build a readable path\n * @returns Paths of the groups whose children will not be displayed\n */\nexport function findHiddenSidebarPaths(items: (SidebarProject | Page)[], depth: number, trail: string[] = []): string[] {\n return items.flatMap((item) => {\n if (!('items' in item) || !item.items?.length) {\n return []\n }\n const path = [...trail, item.text]\n return depth >= MAX_SIDEBAR_DEPTH\n ? [path.join('/')]\n : findHiddenSidebarPaths(item.items, depth + 1, path)\n })\n}\n\n/**\n * Merges a previously generated sidebar with the one built by the current run,\n * used to accumulate repositories across usernames\n *\n * Flat sidebars are concatenated and sorted by group text, route-keyed sidebars\n * are merged by key with the current run winning. When the two shapes disagree,\n * the flat side is filed under the root key so nothing is silently dropped\n *\n * @param previous - Sidebar read back from the config written by a previous run\n * @param current - Sidebar generated by the current run\n * @returns The merged sidebar\n */\nexport function mergeSidebars(previous: DefaultTheme.Sidebar | undefined, current: DefaultTheme.Sidebar): DefaultTheme.Sidebar {\n if (!previous) {\n return current\n }\n if (Array.isArray(previous) && Array.isArray(current)) {\n return [...previous, ...current]\n .toSorted((a, b) => (a.text ?? '').localeCompare(b.text ?? ''))\n }\n const toMulti = (sidebar: DefaultTheme.Sidebar): DefaultTheme.SidebarMulti =>\n Array.isArray(sidebar) ? { '/': sidebar } : sidebar\n\n return sortSidebarRoutes({ ...toMulti(previous), ...toMulti(current) })\n}\n\n/**\n * Sorts the route keys of a multi sidebar so generated configurations stay\n * stable between runs regardless of repository processing order\n *\n * @param sidebarByRoute - Multi sidebar keyed by repository route\n * @returns The same sidebar with alphabetically ordered route keys\n */\nexport function sortSidebarRoutes(sidebarByRoute: DefaultTheme.SidebarMulti): DefaultTheme.SidebarMulti {\n return Object.fromEntries(\n Object.entries(sidebarByRoute).toSorted(([routeA], [routeB]) => routeA.localeCompare(routeB)),\n )\n}\n\n/**\n * Transforms repository data into documentation structure\n *\n * @param repositories - Array of enhanced repositories\n * @param user - User information retrieved from GitHub\n * @param websiteInfos - Custom title and tagline information\n * @param sidebarOpts - Options driving sidebar shape, collapse behaviour and label spelling\n * @returns Object containing sidebar and index page configurations\n */\nexport function transformDoc(repositories: EnhancedRepository[], user: ReturnType<typeof getUserInfos>, websiteInfos: WebsiteInfos, sidebarOpts: SidebarOpts = {}) {\n const { mode = 'single', collapsed = true, acronyms = [] } = sidebarOpts\n const features: Feature[] = []\n const sidebar: SidebarProject[] = []\n const sidebarByRoute: DefaultTheme.SidebarMulti = {}\n\n for (const repository of repositories) {\n log(` Replace urls for repository '${repository.name}'.`, 'info')\n getMdFiles([repository.docpress.projectPath]).forEach((file) => {\n log(` Processing file '${basename(file)}' for repository '${repository.name}'.`, 'debug')\n replaceRelativePath(file, repository.docpress.replace_url)\n\n if (basename(file).toLowerCase() === 'readme.md') {\n replaceReadmePath(file, repository.docpress.replace_url)\n }\n // Runs last so remaining relative links are rewritten to match the\n // renamed files (index prefix stripped, lowercased, readme -> introduction)\n replaceInternalMdLinks(file)\n })\n\n log(` Generate sidebar for repository '${repository.name}'.`, 'info')\n const projectFiles = readdirSync(repository.docpress.projectPath, { recursive: true })\n .filter((file) => {\n const filePath = resolve(repository.docpress.projectPath, file.toString())\n // Name check first, then a stat that tolerates files vanishing between\n // the directory scan and the check (e.g. leftover git lock files)\n return basename(filePath).endsWith('.md') && isFile(filePath)\n })\n .sort() as string[]\n\n log(` Add sources for repository '${repository.name}'.`, 'info')\n let sourceFile\n if (projectFiles.length > 1) {\n sourceFile = resolve(repository.docpress.projectPath, 'sources.md')\n projectFiles.push('sources.md')\n } else if (projectFiles.length === 1) {\n sourceFile = resolve(repository.docpress.projectPath, projectFiles[0])\n }\n if (sourceFile) {\n addSources(repository.html_url, sourceFile)\n } else {\n log(` No markdown files found for repository '${repository.name}', skipping sources.`, 'warn')\n }\n\n const projectTree = buildTree(projectFiles)\n const sidebarItems = moveSourcesLast(generateSidebarItems(repository, projectTree, collapsed, acronyms))\n\n // Single mode wraps each repository in its own group, which costs one level\n const hidden = findHiddenSidebarPaths(sidebarItems, mode === 'multi' ? 0 : 1)\n if (hidden.length) {\n log(` Sidebar for repository '${repository.name}' nests deeper than Vitepress renders, pages under ${hidden.map(path => `'${path}'`).join(', ')} will not appear in the sidebar.`, 'warn')\n }\n\n if (mode === 'multi') {\n // Vitepress picks the sidebar whose key is the longest prefix of the current\n // route, so keying on the repository route scopes it to that project alone\n sidebarByRoute[`/${repository.docpress.routePrefix ?? ''}${prettify(repository.name, { removeDot: true })}/`] = sidebarItems\n } else {\n sidebar.push(generateSidebarProject(prettify(repository.name, { removeDot: true }), sidebarItems, collapsed, acronyms))\n }\n features.push(...generateFeatures(prettify(repository.name, { removeDot: true }), repository.description || '', undefined, repository.docpress.routePrefix ?? '', acronyms))\n }\n\n log(` Generate index content.`, 'info')\n const index = generateIndex(features.toSorted((a, b) => a.title.localeCompare(b.title)), user, websiteInfos)\n return {\n sidebar: mode === 'multi'\n ? sortSidebarRoutes(sidebarByRoute)\n : sidebar.toSorted((a, b) => a.text.localeCompare(b.text)),\n index,\n }\n}\n\n/**\n * Adds extra pages to the documentation from specified paths\n *\n * @param paths - Array of paths to markdown files\n * @returns Array of navigation page objects\n */\nexport function addExtraPages(paths: string[]) {\n const files = getMdFiles(paths)\n const nav: Page[] = []\n\n log(` Add extras Vitepress headers pages.`, 'info')\n for (const file of files) {\n log(` Processing file '${file}'.`, 'debug')\n const src = resolve(process.cwd(), file)\n const dest = resolve(DOCS_DIR, prettify(basename(file), { mode: 'lowercase', removeIdx: true }))\n cpSync(src, dest)\n nav.push({\n text: prettify(parse(src).name, { replaceDash: true, removeIdx: true }),\n link: `/${prettify(parse(src).name, { removeIdx: true, mode: 'lowercase' })}`,\n })\n }\n return nav\n}\n\n/**\n * Adds content from specified paths to a target directory\n *\n * @param paths - String or array of paths to content\n * @param dir - Target directory to copy content to\n * @param fn - Optional callback function to execute for each file\n */\nexport function addContent(paths: string | string[], dir: string, fn?: () => void) {\n for (const path of Array.isArray(paths) ? paths : [paths]) {\n const absolutePath = resolve(process.cwd(), path)\n const files = extractFiles(absolutePath)\n\n for (const file of files) {\n const formattedFile = file.replace(absolutePath, '.')\n log(` Processing file '${formattedFile}' for entry '${path}'.`, 'debug')\n const src = resolve(process.cwd(), file)\n const dest = resolve(dir, formattedFile)\n if (fn) {\n fn()\n }\n cpSync(src, dest)\n }\n }\n}\n\n/**\n * Creates a fork page to display external contributions\n *\n * @param forks - Array of repositories and contribution counts\n */\nexport function addForkPage(forks: { repository: Awaited<ReturnType<typeof getInfos>>['repos'][number], contributions: number }[]) {\n const separator = '---\\n'\n const header = 'layout: fork-page\\nrepoList:\\n'\n const text = '\\n# External contributions\\n\\nThis gallery is a visual representation of the collaborative work done across a variety of open-source projects, each driven by a shared passion for innovation and community growth.\\n\\nEvery tile below represents a unique project where contributions have been made-ranging from code enhancements to documentation improvements. Each project includes a summary of its goals, features, and links to GitHub for direct access.\\n\\nThis page serves as both a portfolio of past work and a resource for revisiting projects that have made a meaningful impact.\\n'\n const frontmatter = forks.map(({ repository, contributions }) => {\n const { name, owner, html_url, description, stargazers_count } = repository\n return { name, owner: owner.login, html_url, description, stargazers_count, contributions }\n })\n log(` Generate forks page.`, 'info')\n writeFileSync(FORKS_FILE, separator + header + YAML.stringify(frontmatter) + separator + text)\n}\n\ntype Source = Required<Awaited<ReturnType<typeof getContributors>>['source']>\n\ntype AdaptedLicense = Omit<NonNullable<Source>['license'], 'spdx_id'> & { spdx_id?: string }\n\ntype AdaptedRepository = Omit<NonNullable<Source>, 'license'> & { license: AdaptedLicense }\n\n/**\n * Processes forked repositories to generate contribution information\n *\n * @param repositories - Array of enhanced repositories\n * @param username - GitHub username\n * @param token - Optional GitHub API token\n */\nexport async function processForks(repositories: EnhancedRepository[], username: GlobalOpts['usernames'][number], token?: GlobalOpts['token']) {\n const forks = await Promise.all(\n repositories.map(async (repository) => {\n const { source, contributors } = await getContributors({ repository, token })\n return {\n contributions: contributors?.find(contributor => contributor.login === username)?.contributions ?? 0,\n repository: source as AdaptedRepository,\n }\n }),\n ).then(f => f.filter(({ repository, contributions }) => !!repository && contributions))\n\n addForkPage(forks)\n}\n\n/**\n * Parses a VitePress configuration file\n *\n * @param path - Path to the VitePress config file\n * @returns The parsed configuration object\n */\nexport async function parseVitepressConfig(path: string): Promise<Partial<ReturnType<typeof defineConfig>>> {\n try {\n const { config } = await import(resolve(process.cwd(), path))\n return config ?? {}\n } catch (error) {\n log(` Unable to load existing Vitepress config '${path}', starting from an empty config. ${formatError(error)}`, 'warn')\n return {}\n }\n}\n\n/**\n * Parses a VitePress index file\n *\n * @param path - Path to the index file\n * @returns The parsed index object\n */\nexport async function parseVitepressIndex(path: string): Promise<Index> {\n const index = (await readFile(resolve(process.cwd(), path))).toString()\n return YAML.parse(index)\n}\n\n/**\n * Tail of the generated VitePress config. `config` stays plain JSON so a later run can\n * re-import it, while the default export adds what JSON cannot hold.\n *\n * Vue compiles every page as a template: VitePress keeps fenced blocks out of it with\n * `v-pre`, but not inline code, so a `${{ always() }}` from a GitHub Actions doc is\n * evaluated as an expression and breaks the build.\n */\nconst VITEPRESS_CONFIG_EXPORT = `\nfunction escapeInlineCode(md) {\n const render = md.renderer.rules.code_inline\n md.renderer.rules.code_inline = (tokens, idx, options, env, self) => {\n tokens[idx].attrSet('v-pre', '')\n return render(tokens, idx, options, env, self)\n }\n}\n\nexport default { ...config, markdown: { ...config.markdown, config: escapeInlineCode } }\n`\n\n/**\n * Generates VitePress configuration and index files\n *\n * @param vitepressConfig - VitePress configuration object\n * @param index - Index page configuration\n * @throws Error if no template theme files are found\n */\nexport function generateVitepressFiles(vitepressConfig: Partial<ReturnType<typeof defineConfig>>, index: Index) {\n const separator = '---\\n'\n createDir(dirname(VITEPRESS_CONFIG))\n\n log(` Generate Vitepress config.`, 'info')\n writeFileSync(VITEPRESS_CONFIG, `export const config = ${JSON.stringify(vitepressConfig, null, 2)}\\n${VITEPRESS_CONFIG_EXPORT}`)\n log(` Generate index file.`, 'info')\n writeFileSync(INDEX_FILE, separator + YAML.stringify(index))\n\n // Fail here (not during the later Vitepress build) so the error points at the\n // actual missing source instead of surfacing as an opaque Vite ENOENT\n const themeFiles = extractFiles(TEMPLATE_THEME)\n if (!themeFiles.length) {\n throw new Error(`No template theme files found at '${TEMPLATE_THEME}'. The Vitepress build cannot succeed without them.`)\n }\n\n log(` Add Docpress theme files.`, 'info')\n themeFiles.forEach((path) => {\n const relativePath = path.replace(`${TEMPLATE_THEME}/`, '')\n generateFile(path, resolve(VITEPRESS_THEME, relativePath))\n })\n}\n","import { dirname } from 'node:path'\nimport { createCommand, createOption } from 'commander'\nimport { configSchema } from '../schemas/global.js'\nimport { addOptions, explicitOptions, parseOptions } from '../utils/commands.js'\nimport { prepareDoc } from '../lib/prepare.js'\nimport type { PrepareOpts } from '../schemas/prepare.js'\nimport { log } from '../utils/logger.js'\nimport { createDir, formatDuration, formatError } from '../utils/functions.js'\nimport { VITEPRESS_CONFIG } from '../utils/const.js'\nimport { globalOpts } from './global.js'\n\n/**\n * Command name for the prepare operation\n */\nconst cmdName = 'prepare'\n\n/**\n * Command line options specific to the prepare command\n */\nexport const prepareOpts = [\n createOption('--acronyms <string>', configSchema.shape.acronyms.description),\n createOption('-c, --extra-public-content <string>', configSchema.shape.extraPublicContent.description),\n createOption('-f, --forks', configSchema.shape.forks.description),\n createOption('-p, --extra-header-pages <string>', configSchema.shape.extraHeaderPages.description),\n createOption('-t, --extra-theme <string>', configSchema.shape.extraTheme.description),\n createOption('--sidebar-mode <string>', configSchema.shape.sidebarMode.description),\n createOption('--sidebar-collapsed <string>', configSchema.shape.sidebarCollapsed.description),\n createOption('-v, --vitepress-config <string>', configSchema.shape.vitepressConfig.description),\n createOption('--website-title <string>', configSchema.shape.websiteTitle.description),\n createOption('--website-tagline <string>', configSchema.shape.websiteTagline.description),\n]\n\n/**\n * The prepare command definition for Commander\n */\nexport const prepareCmd = addOptions(createCommand(cmdName), [...prepareOpts, ...globalOpts])\n .description('Transform doc to the target vitepress format.')\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions(cmdName, explicitOptions(cmd, opts))\n await main(parsedOpts)\n })\n\n/**\n * Main function to execute the prepare command\n *\n * @param opts - Validated prepare options\n */\nexport async function main(opts: PrepareOpts) {\n const { extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, acronyms, token, usernames, websiteTitle, websiteTagline } = opts\n log(`\\n-> Start transform files to prepare Vitepress build.`, 'info')\n const start = Date.now()\n\n createDir(dirname(VITEPRESS_CONFIG), { clean: true })\n const failedUsers: string[] = []\n for (const username of usernames) {\n try {\n await prepareDoc({ extraHeaderPages, extraPublicContent, extraTheme, vitepressConfig, forks, gitProvider, lastUpdated, sidebarMode, sidebarCollapsed, acronyms, token, username, websiteTitle, websiteTagline })\n } catch (error) {\n failedUsers.push(username)\n log(` Failed to prepare documentation for '${username}': ${formatError(error)}`, 'error')\n }\n }\n\n if (failedUsers.length === usernames.length) {\n throw new Error(`Failed to prepare documentation for all requested username(s): ${failedUsers.join(', ')}.`)\n }\n\n const succeeded = usernames.length - failedUsers.length\n log(` Prepared documentation for ${succeeded}/${usernames.length} username(s) in ${formatDuration(Date.now() - start)}.`, failedUsers.length ? 'warn' : 'success')\n}\n","import { Command } from 'commander'\nimport pkg from '../package.json' with { type: 'json' }\nimport { fetchCmd, main as fetchFn, fetchOpts } from './commands/fetch.js'\nimport { buildCmd, main as buildFn } from './commands/build.js'\nimport { prepareCmd, main as prepareFn, prepareOpts } from './commands/prepare.js'\nimport { addOptions, explicitOptions, parseOptions } from './utils/commands.js'\nimport { globalOpts } from './commands/global.js'\nimport { log } from './utils/logger.js'\nimport { formatError } from './utils/functions.js'\n\n/**\n * Creates and configures the Command Line Interface for DocPress\n *\n * @returns A configured Commander program instance\n */\nexport function getProgram() {\n const pm = new Command()\n .name('docpress')\n .description('Build your doc website faster than light ⚡️⚡️⚡️')\n .version(`${pkg.version}`)\n .action(async (opts, cmd) => {\n const parsedOpts = parseOptions('global', explicitOptions(cmd, opts))\n await fetchFn(parsedOpts)\n await prepareFn(parsedOpts)\n await buildFn()\n })\n .configureHelp({\n sortSubcommands: true,\n sortOptions: true,\n showGlobalOptions: true,\n })\n .enablePositionalOptions()\n .passThroughOptions()\n .addCommand(fetchCmd)\n .addCommand(prepareCmd)\n .addCommand(buildCmd)\n\n addOptions(pm, [...fetchOpts, ...prepareOpts, ...globalOpts])\n\n return pm\n}\n\n/**\n * Entry point for the DocPress CLI\n * Initializes and runs the command parser\n */\nexport function main() {\n const pm = getProgram()\n pm.parseAsync(process.argv).catch((error) => {\n log(`\\n${formatError(error)}`, 'error')\n process.exit(1)\n })\n}\n"],"mappings":"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"}