vitepress-allyouneed 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markdown-it.ts","../src/core/slugify.ts","../src/core/config-bridge.ts","../src/core/vault/index.ts","../src/utils/path.ts","../src/core/vault/scan.ts","../src/core/vault/ignore.ts","../src/core/vault/frontmatter.ts","../src/core/resolver.ts","../src/modules/wikilinks/render.ts","../src/utils/escape.ts","../src/core/asset-pipeline/build-emit.ts","../src/modules/embeds/image.ts","../src/modules/embeds/transclusion.ts","../src/modules/wikilinks/rule.ts","../src/modules/wikilinks/index.ts","../src/modules/embeds/block-rule.ts","../src/modules/embeds/index.ts"],"sourcesContent":["/**\n * markdown-it 入口。\n *\n * 高级用法:用户自己创建 markdown-it 实例,然后 md.use(allYouNeed, options)。\n * 此模式下需要由调用方负责提供 VaultIndex(通过 markdown-it env)。\n *\n * 用法示例:\n *\n * ```ts\n * import allYouNeed from 'vitepress-allyouneed/markdown-it'\n * import { scanVault, resolveOptions } from 'vitepress-allyouneed'\n *\n * const options = resolveOptions({ srcDir: '/path/to/vault' })\n * const index = scanVault(options)\n *\n * md.use(allYouNeed, options)\n *\n * // render 时把 index 注入 env\n * const html = md.render(markdown, { index, options })\n * ```\n */\n\nimport type MarkdownIt from 'markdown-it'\nimport type { ResolvedOptions, AllYouNeedOptions } from './core/types.js'\nimport { resolveOptions } from './core/config-bridge.js'\nimport { registerWikilinks } from './modules/wikilinks/index.js'\nimport {\n registerEmbedsOnly,\n registerEmbedBlockRule,\n} from './modules/embeds/index.js'\n\n/**\n * markdown-it 插件函数。\n *\n * @param md markdown-it 实例\n * @param options AllYouNeedOptions 或已 resolve 过的 ResolvedOptions\n */\nfunction allYouNeedMarkdownIt(\n md: MarkdownIt,\n options?: AllYouNeedOptions | ResolvedOptions,\n): void {\n const resolved: ResolvedOptions =\n options && isResolved(options)\n ? options\n : resolveOptions(options as AllYouNeedOptions | undefined)\n\n const { wikilinks: wlOn, embeds: emOn } = resolved.modules\n\n if (wlOn && emOn) {\n registerWikilinks(md, 'both')\n registerEmbedBlockRule(md)\n } else if (wlOn) {\n registerWikilinks(md, 'wikilinks-only')\n // embeds 关掉时不注册 block rule\n } else if (emOn) {\n registerEmbedsOnly(md) // 内含 inline + block\n }\n}\n\nfunction isResolved(\n o: AllYouNeedOptions | ResolvedOptions,\n): o is ResolvedOptions {\n // ResolvedOptions 有 'slugify' + 'modules' + 'srcDir'(都是必填)\n return (\n typeof (o as ResolvedOptions).slugify === 'function' &&\n typeof (o as ResolvedOptions).srcDir === 'string' &&\n typeof (o as ResolvedOptions).modules === 'object' &&\n (o as ResolvedOptions).modules !== null\n )\n}\n\nexport default allYouNeedMarkdownIt\nexport { allYouNeedMarkdownIt }\n","/**\n * 锚点 slugifier。\n *\n * 默认用 @mdit-vue/shared 的 slugify —— 这是 VitePress 内部 markdown-it-anchor\n * 默认使用的实现,保证我们算出的 slug 和 anchor ID 100% 对得上(包括中文)。\n *\n * 用户在 VitePress 里覆写了 markdown.anchor.slugify 时,ConfigBridge 会把\n * 用户的函数读出来传给 ResolvedOptions.slugify,Resolver 直接用,无需走这里。\n */\n\nimport { slugify as mditVueSlugify } from '@mdit-vue/shared'\n\n/**\n * 默认 slugifier。\n */\nexport function defaultSlugify(text: string): string {\n return mditVueSlugify(text)\n}\n\n/**\n * 处理 `## 标题 {#custom-id}` 这种自定义 anchor 语法,\n * 返回 `{ text: '标题', slug: 'custom-id' | undefined }`。\n *\n * markdown-it-anchor 默认启用此扩展(`permalink.linkInsideHeader` 等无关),\n * 我们的 heading 收集器要做相同识别,否则 [[Page#标题]] 会按 slugify('标题')\n * 算,而 markdown-it-anchor 实际用了 'custom-id',结果不匹配。\n */\nconst CUSTOM_ID_RE = /\\s*\\{#([^}\\s]+)\\}\\s*$/\n\nexport function extractCustomId(headingText: string): {\n text: string\n customId: string | undefined\n} {\n const m = headingText.match(CUSTOM_ID_RE)\n if (!m) return { text: headingText, customId: undefined }\n return {\n text: headingText.replace(CUSTOM_ID_RE, ''),\n customId: m[1],\n }\n}\n","/**\n * VitePress / Vite 配置桥接。\n *\n * 负责把外部配置(VitePress site config、Vite 的 ResolvedConfig)和用户传入\n * 的插件选项合并,产出一份 ResolvedOptions —— 所有字段都填值,后续模块只看这份。\n */\n\nimport type {\n AllYouNeedOptions,\n ResolvedOptions,\n PageLinkAttrs,\n ImageEmbedAttrs,\n} from './types.js'\nimport { defaultSlugify } from './slugify.js'\n\nconst DEFAULT_ASSET_EXTENSIONS = [\n // 位图\n 'bmp',\n 'gif',\n 'jpeg',\n 'jpg',\n 'png',\n 'svg',\n 'webp',\n 'avif',\n 'ico',\n // 视频\n 'mp4',\n 'webm',\n 'mov',\n 'm4v',\n // 音频\n 'mp3',\n 'wav',\n 'ogg',\n 'm4a',\n 'flac',\n // 文档\n 'pdf',\n // Obsidian 专属\n 'canvas',\n 'excalidraw',\n]\n\nconst DEFAULT_IMAGE_EXTENSIONS = [\n 'bmp',\n 'gif',\n 'jpeg',\n 'jpg',\n 'png',\n 'svg',\n 'webp',\n 'avif',\n 'ico',\n]\n\n/**\n * 合并默认值与用户配置,产出 ResolvedOptions。\n *\n * @param user 用户传入(可能为空对象)\n * @param ctx 外部上下文(VitePress / Vite 决议出的值)\n */\nexport function resolveOptions(\n user: AllYouNeedOptions = {},\n ctx: {\n srcDir?: string\n base?: string\n cleanUrls?: boolean\n /** VitePress markdown.anchor.slugify 若被用户覆写 */\n externalSlugify?: (text: string) => string\n } = {},\n): ResolvedOptions {\n const srcDir = user.srcDir ?? ctx.srcDir ?? process.cwd()\n let base = user.base ?? ctx.base ?? '/'\n if (!base.startsWith('/')) base = '/' + base\n if (!base.endsWith('/')) base = base + '/'\n\n const cleanUrls = user.cleanUrls ?? ctx.cleanUrls ?? false\n const slugify = user.slugify ?? ctx.externalSlugify ?? defaultSlugify\n\n const wikilinksUser = user.wikilinks ?? {}\n const embedsUser = user.embeds ?? {}\n const scanUser = user.scan ?? {}\n const assetsUser = user.assets ?? {}\n const modulesUser = user.modules ?? {}\n\n const wikilinksHtmlAttrs: PageLinkAttrs = wikilinksUser.htmlAttributes ?? {}\n const embedsHtmlAttrs: ImageEmbedAttrs = embedsUser.htmlAttributes ?? {}\n\n return {\n srcDir,\n base,\n cleanUrls,\n caseSensitive: user.caseSensitive ?? false,\n deadLink: user.deadLink ?? 'warn',\n onConflict: user.onConflict ?? 'shortest',\n onAliasConflict: user.onAliasConflict ?? 'first',\n\n scan: {\n include: scanUser.include ?? ['**/*.md', '**/*.markdown'],\n exclude: scanUser.exclude ?? [],\n followSymlinks: scanUser.followSymlinks ?? false,\n respectGitignore: scanUser.respectGitignore ?? true,\n assetExtensions: scanUser.assetExtensions ?? DEFAULT_ASSET_EXTENSIONS,\n },\n\n assets: {\n mode: assetsUser.mode ?? 'auto',\n preserveAssetPaths: assetsUser.preserveAssetPaths ?? false,\n outputDir: assetsUser.outputDir ?? '_assets',\n },\n\n wikilinks: {\n postProcessLinkTarget:\n wikilinksUser.postProcessLinkTarget ?? ((t: string) => t.trim()),\n postProcessLinkLabel:\n wikilinksUser.postProcessLinkLabel ?? ((l: string) => l.trim()),\n allowLinkLabelFormatting:\n wikilinksUser.allowLinkLabelFormatting ?? false,\n linkText: wikilinksUser.linkText ?? 'basename',\n htmlAttributes: wikilinksHtmlAttrs,\n },\n\n embeds: {\n imageFileExt: embedsUser.imageFileExt ?? DEFAULT_IMAGE_EXTENSIONS,\n defaultAltText: embedsUser.defaultAltText ?? false,\n postProcessImageTarget:\n embedsUser.postProcessImageTarget ?? ((t: string) => t.trim()),\n postProcessAltText:\n embedsUser.postProcessAltText ?? ((a: string) => a.trim()),\n uriSuffix: embedsUser.uriSuffix ?? '',\n transclusionMaxDepth: embedsUser.transclusionMaxDepth ?? 8,\n htmlAttributes: embedsHtmlAttrs,\n },\n\n modules: {\n wikilinks: modulesUser.wikilinks ?? true,\n embeds: modulesUser.embeds ?? true,\n },\n\n slugify,\n }\n}\n","/**\n * VaultScanner —— 把 srcDir 扫成一份完整的 VaultIndex。\n *\n * 设计目标:\n * - 一次扫描产出所有衍生索引,后续模块只查不扫;\n * - 同步实现(简单可靠,性能足够);\n * - 增量更新(updateFile / removeFile)供 dev 模式 HMR 用。\n */\n\nimport fs from 'node:fs'\nimport nodePath from 'node:path'\nimport type {\n VaultIndex,\n FileEntry,\n AssetEntry,\n ResolvedOptions,\n ScanWarning,\n} from '../types.js'\nimport {\n toPosix,\n basename,\n extname,\n relative as relativePath,\n pathDepth,\n} from '../../utils/path.js'\nimport { buildUrl, applyCleanUrls } from '../../utils/url.js'\nimport { walk } from './scan.js'\nimport { buildIgnorer } from './ignore.js'\nimport {\n parseFrontmatter,\n normalizeAliases,\n normalizeTags,\n} from './frontmatter.js'\nimport { collectHeadings } from './headings.js'\n\nconst MD_EXTENSIONS = new Set(['md', 'markdown'])\n\n/**\n * 创建一份空 VaultIndex(供测试或没有 srcDir 时使用)。\n */\nexport function createEmptyIndex(\n srcDir = '',\n base = '/',\n cleanUrls = false,\n): VaultIndex {\n return {\n files: new Map(),\n assets: new Map(),\n byBasename: new Map(),\n byBasenameLower: new Map(),\n byAlias: new Map(),\n byRelativePath: new Map(),\n byUrl: new Map(),\n assetsByBasename: new Map(),\n assetsByBasenameLower: new Map(),\n assetsByRelativePath: new Map(),\n tags: new Map(),\n backlinks: new Map(),\n headings: new Map(),\n srcDir,\n base,\n cleanUrls,\n scannedAt: Date.now(),\n warnings: [],\n }\n}\n\n/**\n * 扫描 srcDir 并构建 VaultIndex。\n */\nexport function scanVault(options: ResolvedOptions): VaultIndex {\n const srcDir = toPosix(nodePath.resolve(options.srcDir))\n const index = createEmptyIndex(srcDir, options.base, options.cleanUrls)\n\n const isIgnored = buildIgnorer(\n srcDir,\n options.scan.exclude,\n options.scan.respectGitignore,\n )\n\n const assetExtSet = new Set(\n options.scan.assetExtensions.map((e) => e.toLowerCase()),\n )\n\n const entries = walk(srcDir, isIgnored, options.scan.followSymlinks)\n\n for (const ent of entries) {\n const ext = ent.extension\n if (MD_EXTENSIONS.has(ext)) {\n ingestMarkdown(index, ent.absolutePath, ent.size, ent.mtime, options)\n } else if (assetExtSet.has(ext)) {\n ingestAsset(index, ent.absolutePath, ent.size, ent.mtime, ext)\n }\n // 其它扩展静默跳过(.json、.ts 等)\n }\n\n index.scannedAt = Date.now()\n return index\n}\n\n/**\n * 把一个 .md 文件读入索引。\n */\nfunction ingestMarkdown(\n index: VaultIndex,\n absPath: string,\n size: number,\n mtime: number,\n options: ResolvedOptions,\n): void {\n let raw: string\n try {\n raw = fs.readFileSync(absPath, 'utf8')\n } catch (err) {\n index.warnings.push({\n kind: 'unreadable-file',\n message: `无法读取文件: ${absPath} (${\n err instanceof Error ? err.message : String(err)\n })`,\n affected: [absPath],\n })\n return\n }\n\n const { data, content, error } = parseFrontmatter(raw)\n if (error) {\n index.warnings.push({\n kind: 'invalid-frontmatter',\n message: `frontmatter 解析失败 (${absPath}): ${error}`,\n affected: [absPath],\n })\n }\n\n const aliases = normalizeAliases(data.aliases)\n const tags = normalizeTags(data.tags)\n const headings = collectHeadings(content, options.slugify)\n const rel = relativePath(index.srcDir, absPath)\n const base = basename(absPath, true)\n const ext = extname(absPath)\n const url = computeUrl(rel, options)\n\n const entry: FileEntry = {\n absolutePath: absPath,\n relativePath: rel,\n basename: base,\n extension: ext,\n url,\n frontmatter: data,\n aliases,\n tags,\n headings,\n mtime,\n size,\n content,\n }\n\n registerFileEntry(index, entry, options)\n}\n\n/**\n * 把一个 asset 文件加入索引。\n */\nfunction ingestAsset(\n index: VaultIndex,\n absPath: string,\n size: number,\n mtime: number,\n ext: string,\n): void {\n const rel = relativePath(index.srcDir, absPath)\n const base = basename(absPath)\n\n const entry: AssetEntry = {\n absolutePath: absPath,\n relativePath: rel,\n basename: base,\n extension: ext,\n mtime,\n size,\n referencedBy: new Set(),\n }\n\n index.assets.set(absPath, entry)\n index.assetsByRelativePath.set(rel, entry)\n pushToArrayMap(index.assetsByBasename, base, entry)\n pushToArrayMap(index.assetsByBasenameLower, base.toLowerCase(), entry)\n}\n\n/**\n * 把 FileEntry 写进所有查找索引。\n */\nfunction registerFileEntry(\n index: VaultIndex,\n entry: FileEntry,\n options: ResolvedOptions,\n): void {\n index.files.set(entry.absolutePath, entry)\n index.byRelativePath.set(entry.relativePath, entry)\n\n // URL 冲突:多文件指向同一 URL(常见原因:index.md 和 README.md 并存,\n // 它们都路由到 '/'。VitePress 会让其中一个 404)\n const existingAtUrl = index.byUrl.get(entry.url)\n if (existingAtUrl && existingAtUrl.absolutePath !== entry.absolutePath) {\n index.warnings.push({\n kind: 'unknown',\n message:\n `URL 冲突:文件 \"${entry.relativePath}\" 和 \"${existingAtUrl.relativePath}\" ` +\n `都路由到 \"${entry.url}\"。VitePress 会让其中一个 404。` +\n `建议在 .vitepress/config 加 srcExclude: ['${entry.relativePath}'](或另一个)。`,\n affected: [existingAtUrl.absolutePath, entry.absolutePath],\n })\n }\n index.byUrl.set(entry.url, entry)\n index.headings.set(entry.absolutePath, entry.headings)\n\n pushToArrayMap(index.byBasename, entry.basename, entry)\n pushToArrayMap(index.byBasenameLower, entry.basename.toLowerCase(), entry)\n\n for (const alias of entry.aliases) {\n const key = options.caseSensitive ? alias : alias.toLowerCase()\n if (index.byAlias.has(key)) {\n index.warnings.push({\n kind: 'duplicate-alias',\n message: `alias \"${alias}\" 同时被多个文件声明,按 onAliasConflict='${options.onAliasConflict}' 处理`,\n affected: [index.byAlias.get(key)!.absolutePath, entry.absolutePath],\n })\n if (options.onAliasConflict === 'first') continue\n // 'error' 由调用方在 build 时检查 warnings 决定是否抛\n }\n index.byAlias.set(key, entry)\n }\n\n for (const tag of entry.tags) {\n pushToArrayMap(index.tags, tag, entry)\n }\n}\n\n/**\n * 从 relativePath 计算 VitePress URL。\n *\n * - index.md / README.md → 目录根(`/` 或 `/dir/`)\n * (VitePress 默认把这两个文件都路由到目录根,所以同一目录下若两者并存\n * 会冲突,scanVault 末尾会有 URL 冲突告警)\n * - foo.md → /dir/foo (或 /dir/foo.html when !cleanUrls)\n */\nfunction computeUrl(rel: string, options: ResolvedOptions): string {\n const noExt = rel.replace(/\\.(md|markdown)$/i, '')\n // index.md / README.md 特殊处理:URL 为父目录\n const isIndex = /(^|\\/)(index|README)$/i.test(noExt)\n const pathPart = isIndex ? noExt.replace(/(^|\\/)(index|README)$/i, '$1') : noExt\n\n // 路径段切片,各段 buildUrl 时再编码\n const segments = pathPart.split('/').filter(Boolean)\n if (segments.length === 0) {\n // 根 index.md\n return options.base\n }\n\n // 应用 cleanUrls(若 false,末尾加 .html)\n const last = segments[segments.length - 1]!\n if (!isIndex) {\n segments[segments.length - 1] = applyCleanUrls(last, options.cleanUrls)\n } else if (!options.cleanUrls) {\n // isIndex 且非 cleanUrls,URL 是 /dir/index.html\n segments.push('index.html')\n }\n\n return buildUrl(options.base, segments)\n}\n\n/** Map<K, V[]> push 工具 */\nfunction pushToArrayMap<K, V>(m: Map<K, V[]>, k: K, v: V): void {\n const arr = m.get(k)\n if (arr) arr.push(v)\n else m.set(k, [v])\n}\n\n/**\n * 增量更新:单个文件改了。\n * 把旧 entry 从所有索引清掉,再走 ingest。\n */\nexport function updateFile(\n index: VaultIndex,\n absPath: string,\n options: ResolvedOptions,\n): void {\n const posix = toPosix(absPath)\n removeFile(index, posix, options)\n let stat: fs.Stats\n try {\n stat = fs.statSync(posix)\n } catch {\n return\n }\n const ext = extname(posix)\n if (MD_EXTENSIONS.has(ext)) {\n ingestMarkdown(index, posix, stat.size, stat.mtimeMs, options)\n } else if (\n new Set(options.scan.assetExtensions.map((e) => e.toLowerCase())).has(ext)\n ) {\n ingestAsset(index, posix, stat.size, stat.mtimeMs, ext)\n }\n}\n\n/**\n * 增量更新:单个文件被删。\n */\nexport function removeFile(\n index: VaultIndex,\n absPath: string,\n options: ResolvedOptions,\n): void {\n const posix = toPosix(absPath)\n\n // 试 file\n const file = index.files.get(posix)\n if (file) {\n index.files.delete(posix)\n index.byRelativePath.delete(file.relativePath)\n index.byUrl.delete(file.url)\n index.headings.delete(posix)\n removeFromArrayMap(index.byBasename, file.basename, file)\n removeFromArrayMap(\n index.byBasenameLower,\n file.basename.toLowerCase(),\n file,\n )\n for (const alias of file.aliases) {\n const key = options.caseSensitive ? alias : alias.toLowerCase()\n if (index.byAlias.get(key) === file) index.byAlias.delete(key)\n }\n for (const tag of file.tags) {\n removeFromArrayMap(index.tags, tag, file)\n }\n return\n }\n\n // 试 asset\n const asset = index.assets.get(posix)\n if (asset) {\n index.assets.delete(posix)\n index.assetsByRelativePath.delete(asset.relativePath)\n removeFromArrayMap(index.assetsByBasename, asset.basename, asset)\n removeFromArrayMap(\n index.assetsByBasenameLower,\n asset.basename.toLowerCase(),\n asset,\n )\n }\n}\n\nfunction removeFromArrayMap<K, V>(m: Map<K, V[]>, k: K, v: V): void {\n const arr = m.get(k)\n if (!arr) return\n const idx = arr.indexOf(v)\n if (idx >= 0) arr.splice(idx, 1)\n if (arr.length === 0) m.delete(k)\n}\n\n/**\n * 多条目按\"路径深度浅 → 路径字典序\"排序的 helper,供 Resolver 'shortest' 用。\n */\nexport function sortByShortestPath<T extends { relativePath: string }>(\n items: T[],\n): T[] {\n return [...items].sort((a, b) => {\n const da = pathDepth(a.relativePath)\n const db = pathDepth(b.relativePath)\n if (da !== db) return da - db\n return a.relativePath.localeCompare(b.relativePath)\n })\n}\n\n","/**\n * 跨平台 POSIX 路径工具。\n *\n * 设计原则:\n * - 内部所有路径都用 POSIX 风格(正斜杠);进出 OS 边界才转换。\n * - 我们的索引 key 永远是 POSIX,不依赖运行平台。\n */\n\nimport nodePath from 'node:path'\n\n/** 把任意路径转成 POSIX 风格(替换 \\\\ 为 /)*/\nexport function toPosix(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\n/** 计算相对路径,POSIX 风格 */\nexport function relative(from: string, to: string): string {\n return toPosix(nodePath.relative(from, to))\n}\n\n/** POSIX join */\nexport function posixJoin(...parts: string[]): string {\n return parts\n .filter((p) => p && p.length > 0)\n .map((p, i) => {\n let s = toPosix(p)\n if (i > 0) s = s.replace(/^\\/+/, '')\n if (i < parts.length - 1) s = s.replace(/\\/+$/, '')\n return s\n })\n .join('/')\n}\n\n/** 去掉尾部 .md / .markdown(若有);其它扩展名保留 */\nexport function stripMarkdownExt(target: string): string {\n return target.replace(/\\.(md|markdown)$/i, '')\n}\n\n/** 取 POSIX 风格 basename,可选去扩展 */\nexport function basename(p: string, stripExt = false): string {\n const idx = p.lastIndexOf('/')\n const file = idx === -1 ? p : p.slice(idx + 1)\n if (!stripExt) return file\n const dot = file.lastIndexOf('.')\n return dot <= 0 ? file : file.slice(0, dot)\n}\n\n/** 取扩展名(不含点,小写);无扩展返回 '' */\nexport function extname(p: string): string {\n const file = basename(p)\n const dot = file.lastIndexOf('.')\n if (dot <= 0) return ''\n return file.slice(dot + 1).toLowerCase()\n}\n\n/** 把 path 用 / 拆开,过滤空段 */\nexport function splitPath(p: string): string[] {\n return toPosix(p).split('/').filter(Boolean)\n}\n\n/** 路径深度(段数),用于 onConflict: 'shortest' */\nexport function pathDepth(relPath: string): number {\n return splitPath(relPath).length\n}\n\n/** 把绝对路径正规化为 POSIX */\nexport function normalizeAbs(p: string): string {\n return toPosix(nodePath.resolve(p))\n}\n","/**\n * 文件系统遍历。\n *\n * 同步遍历 srcDir,产出绝对路径列表。性能预算:\n * - 10k 文件 / 千级目录,< 1s。\n * - 同步 fs(我们就一份索引,build/dev 启动时一次性建,简单可靠)。\n */\n\nimport fs from 'node:fs'\nimport nodePath from 'node:path'\nimport { toPosix, extname } from '../../utils/path.js'\n\nexport interface WalkEntry {\n absolutePath: string // POSIX 风格\n size: number\n mtime: number\n extension: string\n}\n\n/**\n * 递归遍历 srcDir,返回所有非忽略的文件条目。\n *\n * @param srcDir 绝对路径\n * @param isIgnored 判定函数\n * @param followSymlinks 是否跟随符号链接(默认 false)\n */\nexport function walk(\n srcDir: string,\n isIgnored: (absPath: string) => boolean,\n followSymlinks: boolean,\n): WalkEntry[] {\n const out: WalkEntry[] = []\n const seenInodes = new Set<string>() // 防符号链接环\n\n function visit(dir: string): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n\n for (const ent of entries) {\n const full = nodePath.join(dir, ent.name)\n const posix = toPosix(full)\n if (isIgnored(posix)) continue\n\n let isDir = ent.isDirectory()\n let isFile = ent.isFile()\n\n if (ent.isSymbolicLink()) {\n if (!followSymlinks) continue\n try {\n const stat = fs.statSync(full)\n isDir = stat.isDirectory()\n isFile = stat.isFile()\n } catch {\n continue\n }\n }\n\n if (isDir) {\n if (followSymlinks) {\n try {\n const stat = fs.statSync(full)\n const key = `${stat.dev}:${stat.ino}`\n if (seenInodes.has(key)) continue\n seenInodes.add(key)\n } catch {\n // 取不到 stat 就跳过\n continue\n }\n }\n visit(full)\n } else if (isFile) {\n try {\n const stat = fs.statSync(full)\n out.push({\n absolutePath: posix,\n size: stat.size,\n mtime: stat.mtimeMs,\n extension: extname(posix),\n })\n } catch {\n // 读不到 stat 就跳过\n }\n }\n }\n }\n\n visit(srcDir)\n return out\n}\n","/**\n * 文件忽略规则。\n *\n * 默认忽略列表针对\"VitePress 文档站 + Obsidian vault\"的典型布局,\n * 用户可通过 ResolvedOptions.scan.exclude 追加。\n */\n\nimport fs from 'node:fs'\nimport nodePath from 'node:path'\nimport picomatch from 'picomatch'\nimport { relative, toPosix } from '../../utils/path.js'\n\n/** 始终忽略,不可关闭 */\nexport const HARD_IGNORE_DIRS = new Set([\n 'node_modules',\n '.git',\n '.svn',\n '.hg',\n '.obsidian',\n '.trash',\n '.vitepress',\n '.next',\n '.nuxt',\n '.cache',\n '.idea',\n '.vscode',\n 'dist',\n 'build',\n])\n\n/**\n * 构造一个 (absolutePath) => boolean 的\"是否忽略\"判定函数。\n *\n * @param srcDir 扫描根目录(绝对路径)\n * @param userExclude 用户额外的 glob 列表\n * @param respectGitignore 是否读 srcDir/.gitignore\n */\nexport function buildIgnorer(\n srcDir: string,\n userExclude: string[],\n respectGitignore: boolean,\n): (absPath: string) => boolean {\n const patterns: string[] = [...userExclude]\n if (respectGitignore) {\n const gitignore = nodePath.join(srcDir, '.gitignore')\n try {\n const content = fs.readFileSync(gitignore, 'utf8')\n for (const line of content.split(/\\r?\\n/)) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) continue\n // .gitignore 规则不完全等价于 picomatch 的 glob,\n // 但对常见 'dist/'、'*.log' 这类够用了。\n patterns.push(trimmed.endsWith('/') ? trimmed + '**' : trimmed)\n }\n } catch {\n // .gitignore 不存在或不可读 —— 静默跳过\n }\n }\n\n const matchers = patterns.map((p) =>\n picomatch(p, { dot: true, nocase: false }),\n )\n\n return (absPath: string): boolean => {\n const rel = toPosix(relative(srcDir, absPath))\n if (!rel || rel.startsWith('..')) return true\n\n // 硬忽略:任意路径段命中即忽略\n for (const seg of rel.split('/')) {\n if (HARD_IGNORE_DIRS.has(seg)) return true\n }\n\n // 用户/gitignore 规则\n for (const m of matchers) {\n if (m(rel)) return true\n }\n return false\n }\n}\n","/**\n * frontmatter 解析。\n *\n * 用 gray-matter,Obsidian 和 Astro 都用它,YAML 兼容性好。\n * 解析失败时返回空 frontmatter + 原文,落 warning。\n */\n\nimport matter from 'gray-matter'\n\nexport interface ParsedFrontmatter {\n /** 解析后的 frontmatter 对象,失败时为空对象 */\n data: Record<string, unknown>\n /** 去掉 frontmatter 之后的正文 */\n content: string\n /** 解析错误信息,成功为 undefined */\n error?: string\n}\n\nexport function parseFrontmatter(raw: string): ParsedFrontmatter {\n try {\n const { data, content } = matter(raw)\n return { data: (data ?? {}) as Record<string, unknown>, content }\n } catch (err) {\n return {\n data: {},\n content: raw,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * 把 frontmatter.aliases 归一化为字符串数组。支持:\n * - undefined / null → []\n * - string → [string]\n * - string[] → 原样过滤空字符串\n * - 其它 → []\n */\nexport function normalizeAliases(raw: unknown): string[] {\n if (raw == null) return []\n if (typeof raw === 'string') return raw.trim() ? [raw.trim()] : []\n if (Array.isArray(raw)) {\n return raw\n .filter((v): v is string => typeof v === 'string')\n .map((s) => s.trim())\n .filter(Boolean)\n }\n return []\n}\n\n/**\n * 把 frontmatter.tags 归一化为字符串数组(同上规则)。\n */\nexport function normalizeTags(raw: unknown): string[] {\n if (raw == null) return []\n if (typeof raw === 'string') {\n return raw\n .split(/[,\\s]+/)\n .map((s) => s.trim().replace(/^#/, ''))\n .filter(Boolean)\n }\n if (Array.isArray(raw)) {\n return raw\n .filter((v): v is string => typeof v === 'string')\n .map((s) => s.trim().replace(/^#/, ''))\n .filter(Boolean)\n }\n return []\n}\n","/**\n * Resolver —— 把 wikilink target 解析成最终 URL。\n *\n * 解析顺序(对应 PLAN §5):\n * 1. trim、剥 .md/.markdown\n * 2. 拆 #heading\n * 3. 含 '/' → 按 byRelativePath 查\n * 4. 不含 '/' → byAlias → byBasename(冲突按 onConflict)\n * 5. heading 匹配 → 加 anchor;否则标记半死链\n */\n\nimport type {\n VaultIndex,\n ResolvedOptions,\n ResolveResult,\n FileEntry,\n} from './types.js'\nimport { sortByShortestPath } from './vault/index.js'\nimport { stripMarkdownExt, toPosix, basename } from '../utils/path.js'\n\n/**\n * 解析一个 wikilink target(已经从 [[]] / ![[]] 中取出的 raw 字符串,不含 pipe 部分)。\n *\n * @param rawTarget 例如 \"notes/a\" / \"a\" / \"a#heading\" / \"中文笔记\"\n * @param index vault 索引\n * @param options 已解析配置\n * @param kind 'page' | 'image' | 'transclusion';v0.1 image 走单独路径,这里\n * 只处理 'page' / 'transclusion'\n */\nexport function resolveWikilink(\n rawTarget: string,\n index: VaultIndex,\n options: ResolvedOptions,\n kind: 'page' | 'transclusion' = 'page',\n): ResolveResult {\n // 1. 归一化:反斜杠 → 正斜杠、剥 markdown 扩展、trim\n let target = toPosix(rawTarget).trim()\n // 2. 拆 #heading\n const hashIdx = target.indexOf('#')\n let headingPart = ''\n if (hashIdx >= 0) {\n headingPart = target.slice(hashIdx + 1).trim()\n target = target.slice(0, hashIdx).trim()\n }\n // 剥 .md / .markdown\n target = stripMarkdownExt(target)\n\n // 3-4. 查 entry\n const entry = lookupEntry(target, index, options)\n\n if (!entry) {\n return {\n url: buildDeadUrl(rawTarget, options),\n defaultLabel: defaultLabel(target, headingPart, undefined, options),\n isDead: true,\n hasUnmatchedAnchor: false,\n kind,\n }\n }\n\n // 5. heading 匹配\n let url = entry.url\n let hasUnmatchedAnchor = false\n if (headingPart) {\n const heading = entry.headings.find(\n (h) =>\n h.text === headingPart ||\n h.slug === headingPart ||\n h.slug === options.slugify(headingPart),\n )\n if (heading) {\n url = entry.url + '#' + heading.slug\n } else {\n hasUnmatchedAnchor = true\n url = entry.url + '#' + encodeURIComponent(headingPart)\n }\n }\n\n return {\n url,\n defaultLabel: defaultLabel(target, headingPart, entry, options),\n isDead: false,\n hasUnmatchedAnchor,\n target: entry,\n kind,\n }\n}\n\n/**\n * 查目标 entry:含 '/' 按 byRelativePath,否则 byAlias → byBasename(冲突按策略)。\n */\nfunction lookupEntry(\n target: string,\n index: VaultIndex,\n options: ResolvedOptions,\n): FileEntry | undefined {\n if (!target) return undefined\n\n // 含 '/':按路径查\n if (target.includes('/')) {\n // 用户写 'notes/a' → 找 'notes/a.md' 或 'notes/a.markdown'\n const variants = [\n target,\n target + '.md',\n target + '.markdown',\n target + '/index.md',\n target + '/index.markdown',\n ]\n for (const v of variants) {\n const e = index.byRelativePath.get(v)\n if (e) return e\n }\n return undefined\n }\n\n // 不含 '/':先 alias\n const aliasKey = options.caseSensitive ? target : target.toLowerCase()\n const aliased = index.byAlias.get(aliasKey)\n if (aliased) return aliased\n\n // 再 basename\n const bnMap = options.caseSensitive\n ? index.byBasename\n : index.byBasenameLower\n const bnKey = options.caseSensitive ? target : target.toLowerCase()\n const candidates = bnMap.get(bnKey)\n if (!candidates || candidates.length === 0) return undefined\n if (candidates.length === 1) return candidates[0]!\n\n // 多个 → onConflict\n switch (options.onConflict) {\n case 'shortest': {\n const sorted = sortByShortestPath(candidates)\n return sorted[0]!\n }\n case 'first':\n return candidates[0]!\n case 'error':\n // build 时由调用方根据 deadLink 决定;这里返回 undefined → 当作死链\n return undefined\n }\n}\n\n/**\n * 死链 URL —— 给一个尽量接近用户意图的 href,便于用户点开诊断。\n * 不抛错,渲染时附 wikilink--dead class。\n */\nfunction buildDeadUrl(rawTarget: string, options: ResolvedOptions): string {\n // 把 raw 字符串原样编码进 URL,带个 sentinel hash 让用户一眼能看出\n const safe = encodeURIComponent(stripMarkdownExt(rawTarget).split('#')[0]!)\n return options.base + safe\n}\n\n/**\n * 计算默认 label。\n * - 用户传了 alias 优先,这里只算\"没有 alias 时\" fallback。\n * - linkText='basename' / 'fullPath' / 自定义函数\n * - 带 heading 时:basename > heading\n */\nfunction defaultLabel(\n target: string,\n headingPart: string,\n entry: FileEntry | undefined,\n options: ResolvedOptions,\n): string {\n const lt = options.wikilinks.linkText\n let base: string\n if (typeof lt === 'function') {\n if (entry) {\n base = lt(entry, target)\n } else {\n // 死链时拿不到 entry,降级到 raw target 的 basename\n base = basename(target)\n }\n } else if (lt === 'fullPath') {\n base = entry ? entry.relativePath.replace(/\\.(md|markdown)$/i, '') : target\n } else {\n // 'basename'\n base = entry ? entry.basename : basename(target)\n }\n if (headingPart) {\n // Obsidian 风格 \"basename > 章节\"\n return `${base} > ${headingPart}`\n }\n return base\n}\n\n/**\n * 解析 image embed target → AssetEntry。\n * 与 resolveWikilink 类似,但走 assets 索引。\n */\nexport function resolveAsset(\n rawTarget: string,\n index: VaultIndex,\n options: ResolvedOptions,\n): {\n asset: import('./types.js').AssetEntry | undefined\n rawBasename: string\n} {\n const target = toPosix(rawTarget).trim()\n // 含 '/':按 assetsByRelativePath 查\n if (target.includes('/')) {\n return {\n asset: index.assetsByRelativePath.get(target),\n rawBasename: basename(target),\n }\n }\n const bn = options.caseSensitive ? target : target.toLowerCase()\n const map = options.caseSensitive\n ? index.assetsByBasename\n : index.assetsByBasenameLower\n const candidates = map.get(bn)\n if (!candidates || candidates.length === 0) {\n return { asset: undefined, rawBasename: target }\n }\n if (candidates.length === 1) {\n return { asset: candidates[0], rawBasename: target }\n }\n // 多个 asset 同名 → 也走 onConflict\n switch (options.onConflict) {\n case 'shortest': {\n const sorted = sortByShortestPath(candidates)\n return { asset: sorted[0], rawBasename: target }\n }\n case 'first':\n return { asset: candidates[0], rawBasename: target }\n case 'error':\n return { asset: undefined, rawBasename: target }\n }\n}\n","/**\n * wikilinks 渲染辅助 —— 把解析结果落成 markdown-it tokens。\n */\n\nimport type StateInline from 'markdown-it/lib/rules_inline/state_inline.mjs'\nimport type {\n ResolveResult,\n AllYouNeedEnv,\n PageLinkAttrs,\n PageLinkAttrsContext,\n} from '../../core/types.js'\n\n/**\n * 渲染正常 wikilink(`[[Page]]`、`[[Page|alias]]`、`[[Page#heading]]`)。\n */\nexport function renderPageLink(\n state: StateInline,\n result: ResolveResult,\n label: string,\n env: AllYouNeedEnv,\n): boolean {\n const open = state.push('link_open', 'a', 1)\n const classes = ['wikilink']\n if (result.hasUnmatchedAnchor) classes.push('wikilink--unmatched-anchor')\n\n const baseAttrs: Record<string, string> = {\n href: result.url,\n class: classes.join(' '),\n 'data-wikilink-target': result.target\n ? result.target.relativePath\n : '',\n }\n\n const extra = resolveExtraAttrs(env.options.wikilinks.htmlAttributes, {\n originalHref: result.url,\n label,\n target: result.target,\n isDead: result.isDead,\n hasUnmatchedAnchor: result.hasUnmatchedAnchor,\n })\n\n applyAttrs(open, baseAttrs, extra)\n\n // label 渲染:默认走 text token(安全);allowLinkLabelFormatting=true 时\n // 走 inline 解析,但带递归保护。\n emitLabel(state, label, env)\n\n state.push('link_close', 'a', -1)\n return true\n}\n\n/**\n * 渲染死链:href 还是用原 raw 编码后的值,加 wikilink--dead class,\n * label 仍是 user-supplied 或 fallback,不抛错。\n */\nexport function renderDeadLink(\n state: StateInline,\n url: string,\n label: string,\n rawTarget: string,\n env: AllYouNeedEnv,\n): boolean {\n const open = state.push('link_open', 'a', 1)\n const baseAttrs: Record<string, string> = {\n href: url,\n class: 'wikilink wikilink--dead',\n 'data-wikilink-target': rawTarget,\n title: `死链:找不到 [[${rawTarget}]]`,\n }\n const extra = resolveExtraAttrs(env.options.wikilinks.htmlAttributes, {\n originalHref: url,\n label,\n target: undefined,\n isDead: true,\n hasUnmatchedAnchor: false,\n })\n applyAttrs(open, baseAttrs, extra)\n\n emitLabel(state, label, env)\n\n state.push('link_close', 'a', -1)\n return true\n}\n\n/**\n * 推 label 文本 token(或 inline 解析的结果)。\n */\nfunction emitLabel(\n state: StateInline,\n label: string,\n env: AllYouNeedEnv,\n): void {\n if (env.options.wikilinks.allowLinkLabelFormatting) {\n // 递归保护:env 上挂一个深度计数,超过 3 直接回退到 text\n const depth = (env as unknown as { _labelDepth?: number })._labelDepth ?? 0\n if (depth < 3) {\n ;(env as unknown as { _labelDepth?: number })._labelDepth = depth + 1\n const md = state.md\n const html = md.renderInline(label, env)\n const token = state.push('html_inline', '', 0)\n token.content = html\n ;(env as unknown as { _labelDepth?: number })._labelDepth = depth\n return\n }\n }\n const t = state.push('text', '', 0)\n t.content = label\n}\n\n/**\n * 合并用户 htmlAttributes(函数或对象)。\n */\nfunction resolveExtraAttrs(\n htmlAttrs: PageLinkAttrs,\n ctx: PageLinkAttrsContext,\n): Record<string, string> {\n if (typeof htmlAttrs === 'function') return htmlAttrs(ctx)\n return htmlAttrs ?? {}\n}\n\n/**\n * 把 base + extra 应用到 token,extra 中重复的 key 覆盖 base\n * (例外:class 做合并而非覆盖)。\n */\nfunction applyAttrs(\n token: { attrSet(k: string, v: string): void },\n base: Record<string, string>,\n extra: Record<string, string>,\n): void {\n const merged: Record<string, string> = { ...base }\n for (const [k, v] of Object.entries(extra)) {\n if (k === 'class' && merged.class) {\n merged.class = merged.class + ' ' + v\n } else {\n merged[k] = v\n }\n }\n for (const [k, v] of Object.entries(merged)) {\n token.attrSet(k, v)\n }\n}\n","/**\n * HTML escape。\n *\n * 这里不依赖 markdown-it 的 md.utils.escapeHtml,因为我们的工具可能\n * 在 markdown-it 之外被调用(例如 build-emit 写 HTML 占位符)。\n */\nconst HTML_ESCAPE_MAP: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n}\n\nconst HTML_ESCAPE_RE = /[&<>\"']/g\n\nexport function escapeHtml(s: string): string {\n return s.replace(HTML_ESCAPE_RE, (c) => HTML_ESCAPE_MAP[c]!)\n}\n","/**\n * Asset 占位符 URL + dev 公开 URL helpers。\n *\n * 工作流(v0.1):\n * 1. markdown-it 渲染 ![[image.png]] 时,image.ts 调 buildPlaceholderUrl 出\n * `<img src=\"/__ayn_asset__/<encoded-relPath>\">`,并把 AssetEntry 登记。\n * 2. Vite 编译 .md→.vue 时 Vue compiler 把 `<img src>` 转成 import;Vite 的\n * import-analysis 走 resolveId → 我们插件在 src/vite.ts 拦截这个 URL。\n * 3. dev:resolveId 返回虚拟模块,load 导出 `buildPublicUrl(asset)` —— URL\n * 点回 vault asset(由 dev-middleware 流式响应)。\n * 4. build:load 调 `this.emitFile` 把文件丢给 Rollup,导出\n * `import.meta.ROLLUP_FILE_URL_<id>` —— Rollup 自动算 hash 并替换。\n *\n * 没有 transformIndexHtml/后期字符串替换的路径 —— 全部走标准 Vite 资源管线。\n */\n\nimport type { AssetEntry, ResolvedOptions } from '../types.js'\nimport { basename } from '../../utils/path.js'\n\n/** 占位符 URL 前缀,不会出现在合法 URL 里 */\nexport const ASSET_PLACEHOLDER_PREFIX = '/__ayn_asset__/'\n\n/**\n * 给 asset 构造占位符 URL,markdown-it 渲染阶段使用。\n * 形如 `/__ayn_asset__/<encoded relativePath>`(base 前置)。\n *\n * **用 encodeURI 而不是 encodeURIComponent** —— 我们希望保留 `/` 不被编码成 `%2F`。\n * 原因:Vite 在内部把 URL 路径中的 `%2F` 解码回 `/`,导致 resolveId/load 收到的 id\n * 时而带 `%2F` 时而带 `/`,匹配 byRelativePath 不稳定。统一用 encodeURI 让路径段\n * 保留 `/`,resolveId/load 拿到的 id 总是 `/` 形式,行为稳定。\n */\nexport function buildPlaceholderUrl(\n asset: AssetEntry,\n options: ResolvedOptions,\n): string {\n const id = encodeURI(asset.relativePath)\n return options.base + ASSET_PLACEHOLDER_PREFIX.slice(1) + id\n}\n\n/**\n * dev 模式下 asset 的最终公开 URL。\n *\n * - preserveAssetPaths=false(默认):URL = base + basename\n * - preserveAssetPaths=true:URL = base + relativePath\n *\n * 这俩 dev-middleware 都能服务(basename + relativePath 双查找)。\n */\nexport function buildPublicUrl(\n asset: AssetEntry,\n options: ResolvedOptions,\n): string {\n const path = options.assets.preserveAssetPaths\n ? asset.relativePath\n : basename(asset.absolutePath)\n return options.base + encodeURI(path)\n}\n","/**\n * image embed:`![[image.png]]`、`![[image.png|alt]]`、\n * `![[image.png|300]]`、`![[image.png|x200]]`、`![[image.png|300x200]]`、\n * `![[image.png|alt|300x200]]`。\n *\n * 实现说明:\n * - 渲染为 `<img>` HTML 字符串(`renderImageHtml`)。\n * - inline 场景:外层用 `html_inline` token 包(图片本身是 inline 元素,\n * 和段落文本混排 OK)。\n * - block 场景:外层用 `html_block` token 包(整行只有 ![[image]] 时,\n * 避免被多余 `<p>` 包裹)。\n * - **不用 markdown-it 的 'image' token**:默认渲染器假设 attrs 数组里一定\n * 有 'alt' 项,若没设会 `attrs[-1] = ...` 崩溃。\n */\n\nimport type StateInline from 'markdown-it/lib/rules_inline/state_inline.mjs'\nimport type {\n AllYouNeedEnv,\n ImageEmbedAttrs,\n ImageEmbedAttrsContext,\n} from '../../core/types.js'\nimport { resolveAsset } from '../../core/resolver.js'\nimport { basename } from '../../utils/path.js'\nimport { escapeHtml } from '../../utils/escape.js'\nimport {\n buildPlaceholderUrl,\n buildPublicUrl,\n} from '../../core/asset-pipeline/build-emit.js'\n\n// ── 公共渲染:返回 <img> 标签字符串 ───────────────────────────────\n\n/**\n * 把 ![[image|...]] 渲染为完整的 `<img>` HTML 字符串。\n * 不向 markdown-it state 推 token;调用方决定用 html_inline 还是 html_block。\n */\nexport function renderImageHtml(\n rawTarget: string,\n aliasParts: string[],\n env: AllYouNeedEnv,\n): string {\n const { index, options } = env\n\n const { altText, dim } = parseAltAndDim(aliasParts)\n\n const processedTarget = options.embeds.postProcessImageTarget(rawTarget)\n const { asset } = resolveAsset(processedTarget, index, options)\n\n let src: string\n if (asset) {\n asset.referencedBy.add(env.currentPath ?? '<unknown>')\n env.referencedAssets?.add(asset)\n src = buildPlaceholderUrl(asset, options) + options.embeds.uriSuffix\n } else {\n src =\n options.base +\n encodeURIComponent(basename(processedTarget)) +\n options.embeds.uriSuffix\n }\n\n const finalAlt = determineAlt(altText, processedTarget, options)\n\n const attrs: Record<string, string> = {\n src,\n alt: finalAlt ?? '',\n }\n if (dim.width !== undefined) attrs.width = String(dim.width)\n if (dim.height !== undefined) attrs.height = String(dim.height)\n\n const extra = resolveExtra(options.embeds.htmlAttributes, {\n originalHref: src,\n altText: finalAlt,\n dimensions: dim.raw,\n embedType: 'image',\n })\n for (const [k, v] of Object.entries(extra)) {\n if (k === 'class' && attrs.class) {\n attrs.class = attrs.class + ' ' + v\n } else {\n attrs[k] = v\n }\n }\n\n return (\n '<img ' +\n Object.entries(attrs)\n .map(([k, v]) => `${escapeAttrName(k)}=\"${escapeHtml(v)}\"`)\n .join(' ') +\n ' />'\n )\n}\n\n// ── inline 入口(从 wikilinks rule 调用)──────────────────────────\n\n/**\n * 处理 inline 上下文中的 ![[image|...]]:推 html_inline。\n */\nexport function handleImageEmbed(\n state: StateInline,\n rawTarget: string,\n aliasParts: string[],\n env: AllYouNeedEnv,\n): boolean {\n const html = renderImageHtml(rawTarget, aliasParts, env)\n const token = state.push('html_inline', '', 0)\n token.content = html\n return true\n}\n\n// ── 解析尺寸 + alt ───────────────────────────────────────────────\n\ninterface ParsedDim {\n width?: number\n height?: number\n raw: string\n}\n\nfunction parseAltAndDim(parts: string[]): {\n altText: string\n dim: ParsedDim\n} {\n if (parts.length === 0) return { altText: '', dim: { raw: '' } }\n const last = parts[parts.length - 1]!\n const parsedLast = tryParseDimension(last)\n if (parsedLast) {\n const alt = parts.slice(0, -1).join('|').trim()\n return { altText: alt, dim: { ...parsedLast, raw: last } }\n }\n return { altText: parts.join('|').trim(), dim: { raw: '' } }\n}\n\nfunction tryParseDimension(\n s: string,\n): { width?: number; height?: number } | undefined {\n const trimmed = s.trim().toLowerCase()\n if (!trimmed) return undefined\n\n if (trimmed.includes('x')) {\n const [w, h] = trimmed.split('x')\n const wOk = w === '' || /^\\d+$/.test(w!)\n const hOk = h === '' || /^\\d+$/.test(h!)\n if (!wOk || !hOk) return undefined\n if (w === '' && h === '') return undefined\n return {\n width: w === '' ? undefined : Number(w),\n height: h === '' ? undefined : Number(h),\n }\n }\n if (/^\\d+$/.test(trimmed)) {\n return { width: Number(trimmed) }\n }\n return undefined\n}\n\nfunction determineAlt(\n rawAlt: string,\n target: string,\n options: AllYouNeedEnv['options'],\n): string | undefined {\n if (rawAlt && rawAlt !== '') {\n return options.embeds.postProcessAltText(rawAlt)\n }\n const def = options.embeds.defaultAltText\n if (def === false) return undefined\n if (def === true) {\n const bn = basename(target)\n const dot = bn.lastIndexOf('.')\n const noExt = dot > 0 ? bn.slice(0, dot) : bn\n return options.embeds.postProcessAltText(noExt)\n }\n if (typeof def === 'string') {\n return def === '' ? '' : options.embeds.postProcessAltText(def)\n }\n return options.embeds.postProcessAltText(basename(target))\n}\n\nfunction resolveExtra(\n attrs: ImageEmbedAttrs,\n ctx: ImageEmbedAttrsContext,\n): Record<string, string> {\n if (typeof attrs === 'function') return attrs(ctx)\n return attrs ?? {}\n}\n\nfunction escapeAttrName(k: string): string {\n return k.replace(/[^a-zA-Z0-9_-]/g, '_')\n}\n\n// 防 unused\nexport { buildPublicUrl }\n","/**\n * 笔记 transclusion:`![[note]]`、`![[note#heading]]`。\n *\n * **transclusion 始终是 block-level**:它内部会含 `<h1>` / `<div>` / `<pre>`\n * 等 block 元素,放进 `<p>` 里会产生不合法 HTML。\n *\n * 两种入口:\n * - block 入口(从 embed block-rule 调用)→ 推 html_block,无 <p> 包裹。\n * - inline 入口(从 wikilinks rule 调用)→ 不真做 transclusion,降级为\n * 带告警的 `<a>` 链接 + warn 日志(原因同上)。\n *\n * 内部 helper renderTransclusionHtml 返回完整 `<div class=\"transclusion\">...</div>`\n * 字符串,供 block 入口使用。\n */\n\nimport type StateInline from 'markdown-it/lib/rules_inline/state_inline.mjs'\nimport type MarkdownIt from 'markdown-it'\nimport type { AllYouNeedEnv, FileEntry } from '../../core/types.js'\nimport { resolveWikilink } from '../../core/resolver.js'\nimport { escapeHtml } from '../../utils/escape.js'\n\ninterface TransclusionCacheEntry {\n html: string\n}\n\n/**\n * 公共渲染:返回完整 `<div class=\"transclusion\">…</div>` HTML 字符串。\n * 失败/循环/超深时返回带告警 class 的 div。\n */\nexport function renderTransclusionHtml(\n md: MarkdownIt,\n rawTarget: string,\n aliasParts: string[],\n env: AllYouNeedEnv,\n): string {\n const { index, options } = env\n const result = resolveWikilink(rawTarget, index, options, 'transclusion')\n\n if (result.isDead || !result.target) {\n return `<div class=\"transclusion transclusion--dead\" data-target=\"${escapeHtml(\n rawTarget,\n )}\">⚠️ 找不到笔记 <code>${escapeHtml(rawTarget)}</code></div>`\n }\n\n const target = result.target\n\n // 注册 backlink\n const arr = index.backlinks.get(target.absolutePath) ?? []\n if (env.currentPath) {\n arr.push({\n fromPath: env.currentPath,\n fromUrl: index.files.get(env.currentPath)?.url ?? '',\n context: '',\n isEmbed: true,\n line: -1,\n })\n }\n index.backlinks.set(target.absolutePath, arr)\n\n const stack = env.transclusionStack ?? []\n if (stack.includes(target.absolutePath)) {\n return `<div class=\"transclusion transclusion--cycle\" data-target=\"${escapeHtml(\n rawTarget,\n )}\">⚠️ 循环引用:<code>${escapeHtml(\n rawTarget,\n )}</code> 已在 transclusion 链上</div>`\n }\n\n const depth = env.transclusionDepth ?? 0\n if (depth >= options.embeds.transclusionMaxDepth) {\n return `<div class=\"transclusion transclusion--too-deep\" data-target=\"${escapeHtml(\n rawTarget,\n )}\">⚠️ transclusion 嵌套过深(&gt; ${options.embeds.transclusionMaxDepth})</div>`\n }\n\n const headingPart = extractHeading(rawTarget)\n const fragment = headingPart\n ? sliceByHeading(target, headingPart, options.slugify)\n : target.content\n\n if (fragment == null) {\n return `<div class=\"transclusion transclusion--unmatched-anchor\" data-target=\"${escapeHtml(\n rawTarget,\n )}\">⚠️ 找不到章节 <code>#${escapeHtml(\n headingPart,\n )}</code></div>`\n }\n\n const cacheKey = `${target.absolutePath}::${headingPart ?? ''}`\n const cache = getCache(env)\n let inner: string\n const cached = cache.get(cacheKey)\n if (cached) {\n inner = cached.html\n } else {\n // ⚠️ 关键:childEnv **只显式继承**我们这个插件需要共享的字段。\n // 不能用 { ...env, ... } 浅拷贝,否则 env.frontmatter / env.__data /\n // env.headers / env.excerpt 等 VitePress per-page 字段会被引用共享。\n // 递归 md.render 时,@mdit-vue/plugin-frontmatter 等插件会就地重置\n // 这些字段(因为内联的 fragment 没有自己的 frontmatter),从而 **覆盖\n // 掉外层页面的 frontmatter** —— VitePress 后续读不到 pageData → 路由\n // 数据缺失 → 首页 404。这是 v0.1 收尾期最坑的一个 bug,见 transclusion 测试。\n const childEnv: AllYouNeedEnv = {\n index: env.index,\n options: env.options,\n currentPath: target.absolutePath,\n transclusionStack: [...stack, target.absolutePath],\n transclusionDepth: depth + 1,\n // 跨递归共享:asset 引用集合(build 时要 emit 所有被引用 asset)\n referencedAssets: env.referencedAssets,\n }\n // 跨递归共享:transclusion 渲染缓存(同一笔记被多处 embed 时复用)\n ;(childEnv as unknown as Record<string, unknown>)._transclusionCache = (\n env as unknown as Record<string, unknown>\n )._transclusionCache\n\n inner = md.render(fragment, childEnv)\n cache.set(cacheKey, { html: inner })\n }\n\n const sourceUrl = headingPart\n ? `${target.url}#${escapeHtml(options.slugify(headingPart))}`\n : target.url\n\n // aliasParts 当前作为 wrapper 的可见 caption,在 v0.1 仅做 data 属性留痕\n const aliasData = aliasParts.length\n ? ` data-caption=\"${escapeHtml(aliasParts.join('|'))}\"`\n : ''\n\n return `<div class=\"transclusion\" data-source=\"${escapeHtml(\n target.relativePath,\n )}\" data-source-url=\"${escapeHtml(sourceUrl)}\"${aliasData}>${inner}</div>`\n}\n\n/**\n * inline 上下文中的 ![[note]] —— 不做真 transclusion,降级为带告警的链接。\n * 原因:transclusion 内含 block 元素(<h1>/<div> 等),嵌进 <p> 会产生不合法 HTML。\n */\nexport function handleTransclusion(\n state: StateInline,\n rawTarget: string,\n aliasParts: string[],\n env: AllYouNeedEnv,\n): boolean {\n if (env.options.deadLink !== 'silent') {\n console.warn(\n `vitepress-allyouneed: ![[${rawTarget}]] 在段落中无法 transclude(会产生不合法 HTML),已降级为链接。请单独放一行。`,\n )\n }\n const { index, options } = env\n const result = resolveWikilink(rawTarget, index, options, 'page')\n const url = result.url\n const label = aliasParts.length\n ? aliasParts.join('|').trim()\n : result.defaultLabel\n\n const html =\n `<a class=\"wikilink wikilink--inline-transclusion-degraded\" ` +\n `href=\"${escapeHtml(url)}\" ` +\n `data-wikilink-target=\"${escapeHtml(rawTarget)}\" ` +\n `title=\"行内 transclusion 已降级,见控制台\">` +\n `${escapeHtml(label)}</a>`\n\n const token = state.push('html_inline', '', 0)\n token.content = html\n return true\n}\n\nfunction extractHeading(raw: string): string {\n const hashIdx = raw.indexOf('#')\n if (hashIdx < 0) return ''\n return raw.slice(hashIdx + 1).trim()\n}\n\nfunction sliceByHeading(\n target: FileEntry,\n headingPart: string,\n slugify: (s: string) => string,\n): string | undefined {\n const matched = target.headings.find(\n (h) =>\n h.text === headingPart ||\n h.slug === headingPart ||\n h.slug === slugify(headingPart),\n )\n if (!matched) return undefined\n\n const lines = target.content.split(/\\r?\\n/)\n const startLine = matched.line + 1\n let endLine = lines.length\n for (let i = startLine; i < lines.length; i++) {\n const l = lines[i]!\n const m = l.match(/^(#{1,6})\\s+/)\n if (m && m[1]!.length <= matched.level) {\n endLine = i\n break\n }\n }\n return lines.slice(startLine, endLine).join('\\n').trim()\n}\n\nfunction getCache(env: AllYouNeedEnv): Map<string, TransclusionCacheEntry> {\n const e = env as unknown as {\n _transclusionCache?: Map<string, TransclusionCacheEntry>\n }\n if (!e._transclusionCache) e._transclusionCache = new Map()\n return e._transclusionCache\n}\n","/**\n * wikilinks inline rule:识别 [[...]] 与 ![[...]]。\n *\n * 这条规则在 wikilinks/embeds 都启用时被共用,内部按 isEmbed 分派。\n * 注册顺序:`md.inline.ruler.before('link', 'allyouneed_wikilinks', rule)`。\n */\n\nimport type StateInline from 'markdown-it/lib/rules_inline/state_inline.mjs'\nimport type {\n AllYouNeedEnv,\n ResolvedOptions,\n VaultIndex,\n} from '../../core/types.js'\nimport { resolveWikilink } from '../../core/resolver.js'\nimport {\n renderPageLink,\n renderDeadLink,\n} from './render.js'\nimport { handleImageEmbed } from '../embeds/image.js'\nimport { handleTransclusion } from '../embeds/transclusion.js'\n\n/**\n * 构造 inline rule。\n *\n * @param md markdown-it 实例,仅用于把 env 取出来\n * @param scope 控制此规则处理哪些前缀:'both' / 'wikilinks-only' / 'embeds-only'\n */\nexport function makeWikilinkRule(\n scope: 'both' | 'wikilinks-only' | 'embeds-only',\n): (state: StateInline, silent: boolean) => boolean {\n return function wikilinkRule(state, silent) {\n const src = state.src\n const start = state.pos\n const max = state.posMax\n\n let isEmbed = false\n let inner: string\n\n // 探测 ![[ 或 [[\n if (\n src.charCodeAt(start) === 0x21 /* ! */ &&\n src.charCodeAt(start + 1) === 0x5b /* [ */ &&\n src.charCodeAt(start + 2) === 0x5b /* [ */\n ) {\n if (scope === 'wikilinks-only') return false\n isEmbed = true\n } else if (\n src.charCodeAt(start) === 0x5b /* [ */ &&\n src.charCodeAt(start + 1) === 0x5b /* [ */\n ) {\n if (scope === 'embeds-only') return false\n } else {\n return false\n }\n\n const innerStart = start + (isEmbed ? 3 : 2)\n const closeIdx = src.indexOf(']]', innerStart)\n if (closeIdx < 0 || closeIdx >= max) return false\n\n inner = src.slice(innerStart, closeIdx)\n if (inner.includes('\\n')) return false // 不跨行\n if (!inner.trim()) return false\n\n // 按 markdown-it 约定:silent 模式找到匹配返回 true\n if (silent) return true\n\n // 提取 env\n const env = state.env as AllYouNeedEnv\n if (!env || !env.index || !env.options) {\n // 没注入索引 → 视为不识别,把控制权交回 markdown-it 默认链\n return false\n }\n\n state.pos = closeIdx + 2 // 推进游标\n\n const parts = inner.split('|').map((p) => p.trim())\n const rawTarget = parts[0]!\n const aliasParts = parts.slice(1)\n\n if (isEmbed) {\n // 判断是图片还是 transclusion\n const ext = extractExt(rawTarget)\n const isImage =\n ext && env.options.embeds.imageFileExt.includes(ext.toLowerCase())\n if (isImage) {\n return handleImageEmbed(state, rawTarget, aliasParts, env)\n }\n return handleTransclusion(state, rawTarget, aliasParts, env)\n }\n\n // 普通 [[wikilink]]\n return emitPageLink(state, rawTarget, aliasParts, env)\n }\n}\n\n/**\n * 取一个 path 的扩展名(小写,无点),无扩展名返回 ''。\n */\nfunction extractExt(target: string): string {\n // 拆 #heading 之前先剥\n const cleaned = target.split('#')[0]!\n const dot = cleaned.lastIndexOf('.')\n if (dot <= 0) return ''\n return cleaned.slice(dot + 1).toLowerCase()\n}\n\n/**\n * 渲染一个 [[wikilink]]:解析 target → URL → push tokens。\n */\nfunction emitPageLink(\n state: StateInline,\n rawTarget: string,\n aliasParts: string[],\n env: AllYouNeedEnv,\n): boolean {\n const { index, options } = env\n const userAlias = aliasParts.length > 0 ? aliasParts.join('|').trim() : ''\n const processed = options.wikilinks.postProcessLinkTarget(rawTarget)\n const result = resolveWikilink(processed, index, options, 'page')\n\n const label = userAlias\n ? options.wikilinks.postProcessLinkLabel(userAlias)\n : result.defaultLabel\n\n // 登记 backlink(用于 graph 模块)\n registerBacklink(env, result.target?.absolutePath, false)\n\n if (result.isDead) {\n handleDeadLink(env, rawTarget)\n return renderDeadLink(state, result.url, label, rawTarget, env)\n }\n return renderPageLink(state, result, label, env)\n}\n\n/** 登记反向链接(target 是被链接的页面)*/\nfunction registerBacklink(\n env: AllYouNeedEnv,\n targetPath: string | undefined,\n isEmbed: boolean,\n): void {\n if (!targetPath || !env.currentPath) return\n const arr = env.index.backlinks.get(targetPath) ?? []\n arr.push({\n fromPath: env.currentPath,\n fromUrl: env.index.files.get(env.currentPath)?.url ?? '',\n context: '',\n isEmbed,\n line: -1,\n })\n env.index.backlinks.set(targetPath, arr)\n}\n\n/** 死链 → 按 deadLink 策略输出告警 */\nfunction handleDeadLink(env: AllYouNeedEnv, rawTarget: string): void {\n const { options } = env\n const msg = `vitepress-allyouneed: 死链 [[${rawTarget}]]${\n env.currentPath ? ` (in ${env.currentPath})` : ''\n }`\n if (options.deadLink === 'silent') return\n if (options.deadLink === 'warn') {\n console.warn(msg)\n return\n }\n // 'error':仍然渲染,但把错误推到 index.warnings 让 build 失败\n env.index.warnings.push({\n kind: 'unknown',\n message: msg,\n affected: env.currentPath ? [env.currentPath] : [],\n })\n}\n\n// 抑制 unused 警告\nexport type { VaultIndex, ResolvedOptions }\n","/**\n * wikilinks 模块入口。\n *\n * register(md, scope) 把规则装进 markdown-it 实例。scope 决定本规则\n * 同时还要不要处理 ![[]];当 wikilinks 模块单独启用、embeds 模块关闭时\n * 走 'wikilinks-only',反之亦然。两者都开就 'both'(同一个规则,内部分派)。\n */\n\nimport type MarkdownIt from 'markdown-it'\nimport { makeWikilinkRule } from './rule.js'\n\nexport function registerWikilinks(\n md: MarkdownIt,\n scope: 'both' | 'wikilinks-only' | 'embeds-only',\n): void {\n md.inline.ruler.before(\n 'link',\n 'allyouneed_wikilinks',\n makeWikilinkRule(scope),\n )\n}\n","/**\n * Block rule:整行只有 `![[...]]` 时按 block-level 处理,推 `html_block` token。\n *\n * 这条规则**必须**存在,否则 markdown-it 会把 `![[note]]` 当成段落里的 inline\n * 内容,渲染出 `<p><div class=\"transclusion\">...</div></p>` —— 不合法 HTML\n * (`<div>` 不能在 `<p>` 里),Vue/VitePress 会爆 hydration 警告。\n *\n * 配合 wikilinks/rule.ts 的 inline 规则:\n * - 单独一行的 ![[...]] → 本规则吃掉,html_block\n * - 段落中混合的 ![[...]] → inline 规则处理,image 走 html_inline,\n * transclusion 降级为链接\n */\n\nimport type StateBlock from 'markdown-it/lib/rules_block/state_block.mjs'\nimport type MarkdownIt from 'markdown-it'\nimport type { AllYouNeedEnv } from '../../core/types.js'\nimport { renderImageHtml } from './image.js'\nimport { renderTransclusionHtml } from './transclusion.js'\n\nconst LINE_RE = /^!\\[\\[([^\\n\\]]+)\\]\\]\\s*$/\n\nexport function registerEmbedBlockRule(md: MarkdownIt): void {\n md.block.ruler.before(\n 'paragraph',\n 'allyouneed_embed_block',\n makeRule(md),\n { alt: ['paragraph'] },\n )\n}\n\nfunction makeRule(md: MarkdownIt) {\n return function embedBlockRule(\n state: StateBlock,\n startLine: number,\n _endLine: number,\n silent: boolean,\n ): boolean {\n const start = state.bMarks[startLine]! + state.tShift[startLine]!\n const max = state.eMarks[startLine]!\n const lineText = state.src.slice(start, max)\n\n const m = lineText.match(LINE_RE)\n if (!m) return false\n if (silent) return true\n\n const env = state.env as AllYouNeedEnv\n if (!env || !env.index || !env.options) return false\n\n const inner = m[1]!\n const parts = inner.split('|').map((p) => p.trim())\n const rawTarget = parts[0]!\n const aliasParts = parts.slice(1)\n\n const ext = extractExt(rawTarget)\n const isImage =\n !!ext && env.options.embeds.imageFileExt.includes(ext.toLowerCase())\n\n let html: string\n if (isImage) {\n html = renderImageHtml(rawTarget, aliasParts, env)\n } else {\n html = renderTransclusionHtml(md, rawTarget, aliasParts, env)\n }\n\n const token = state.push('html_block', '', 0)\n token.content = html + '\\n'\n token.map = [startLine, startLine + 1]\n\n state.line = startLine + 1\n return true\n }\n}\n\nfunction extractExt(target: string): string {\n const cleaned = target.split('#')[0]!\n const dot = cleaned.lastIndexOf('.')\n if (dot <= 0) return ''\n return cleaned.slice(dot + 1).toLowerCase()\n}\n","/**\n * embeds 模块入口。\n *\n * embeds 模块有两条规则:\n * 1. **block rule**(`registerEmbedBlockRule`):整行只有 ![[...]] 时按 block-level\n * 处理,推 `html_block`。**必须有,否则 transclusion 会产生 `<div>` in `<p>` 不合法 HTML**。\n * 2. **inline rule**(共用 wikilinks 模块的 makeWikilinkRule):处理段落内混合的\n * ![[...]]。image 走 inline image,transclusion 降级为告警链接。\n *\n * markdown-it.ts 在两种情况下分别调用:\n * - wikilinks + embeds 都开:wikilinks/index.ts 注册 inline 'both';本文件注册 block。\n * - 只开 embeds:本文件 registerEmbedsOnly 注册 inline 'embeds-only';同时注册 block。\n */\n\nimport type MarkdownIt from 'markdown-it'\nimport { makeWikilinkRule } from '../wikilinks/rule.js'\nimport { registerEmbedBlockRule } from './block-rule.js'\n\nexport function registerEmbedsOnly(md: MarkdownIt): void {\n md.inline.ruler.before(\n 'link',\n 'allyouneed_embeds',\n makeWikilinkRule('embeds-only'),\n )\n registerEmbedBlockRule(md)\n}\n\nexport { registerEmbedBlockRule }\n"],"mappings":"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","names":["mditVueSlugify","import_node_fs","import_node_path","import_node_path","import_node_fs","import_node_path","extractExt"]}
@@ -0,0 +1,34 @@
1
+ import MarkdownIt from 'markdown-it';
2
+ import { a as AllYouNeedOptions, f as ResolvedOptions } from './types-CapUIPbW.cjs';
3
+
4
+ /**
5
+ * markdown-it 入口。
6
+ *
7
+ * 高级用法:用户自己创建 markdown-it 实例,然后 md.use(allYouNeed, options)。
8
+ * 此模式下需要由调用方负责提供 VaultIndex(通过 markdown-it env)。
9
+ *
10
+ * 用法示例:
11
+ *
12
+ * ```ts
13
+ * import allYouNeed from 'vitepress-allyouneed/markdown-it'
14
+ * import { scanVault, resolveOptions } from 'vitepress-allyouneed'
15
+ *
16
+ * const options = resolveOptions({ srcDir: '/path/to/vault' })
17
+ * const index = scanVault(options)
18
+ *
19
+ * md.use(allYouNeed, options)
20
+ *
21
+ * // render 时把 index 注入 env
22
+ * const html = md.render(markdown, { index, options })
23
+ * ```
24
+ */
25
+
26
+ /**
27
+ * markdown-it 插件函数。
28
+ *
29
+ * @param md markdown-it 实例
30
+ * @param options AllYouNeedOptions 或已 resolve 过的 ResolvedOptions
31
+ */
32
+ declare function allYouNeedMarkdownIt(md: MarkdownIt, options?: AllYouNeedOptions | ResolvedOptions): void;
33
+
34
+ export { allYouNeedMarkdownIt, allYouNeedMarkdownIt as default };
@@ -0,0 +1,34 @@
1
+ import MarkdownIt from 'markdown-it';
2
+ import { a as AllYouNeedOptions, f as ResolvedOptions } from './types-CapUIPbW.js';
3
+
4
+ /**
5
+ * markdown-it 入口。
6
+ *
7
+ * 高级用法:用户自己创建 markdown-it 实例,然后 md.use(allYouNeed, options)。
8
+ * 此模式下需要由调用方负责提供 VaultIndex(通过 markdown-it env)。
9
+ *
10
+ * 用法示例:
11
+ *
12
+ * ```ts
13
+ * import allYouNeed from 'vitepress-allyouneed/markdown-it'
14
+ * import { scanVault, resolveOptions } from 'vitepress-allyouneed'
15
+ *
16
+ * const options = resolveOptions({ srcDir: '/path/to/vault' })
17
+ * const index = scanVault(options)
18
+ *
19
+ * md.use(allYouNeed, options)
20
+ *
21
+ * // render 时把 index 注入 env
22
+ * const html = md.render(markdown, { index, options })
23
+ * ```
24
+ */
25
+
26
+ /**
27
+ * markdown-it 插件函数。
28
+ *
29
+ * @param md markdown-it 实例
30
+ * @param options AllYouNeedOptions 或已 resolve 过的 ResolvedOptions
31
+ */
32
+ declare function allYouNeedMarkdownIt(md: MarkdownIt, options?: AllYouNeedOptions | ResolvedOptions): void;
33
+
34
+ export { allYouNeedMarkdownIt, allYouNeedMarkdownIt as default };