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/vite.ts","../src/core/slugify.ts","../src/core/config-bridge.ts","../src/core/vault/index.ts","../src/utils/path.ts","../src/utils/url.ts","../src/core/vault/scan.ts","../src/core/vault/ignore.ts","../src/core/vault/frontmatter.ts","../src/core/vault/headings.ts","../src/core/asset-pipeline/dev-middleware.ts","../src/core/asset-pipeline/build-emit.ts"],"sourcesContent":["/**\n * Vite 插件入口。\n *\n * 职责:\n * - configResolved 时扫描 vault 建立 VaultIndex\n * - configureServer 注册 dev middleware(asset 流式响应,作为兜底)\n * - **resolveId**:拦截 markdown-it 渲染时产出的 /__ayn_asset__/ 占位 URL,\n * **直接返回 asset 的绝对文件路径**,后续交给 Vite 自带的资源管线处理:\n * - dev:Vite 自动按文件路径服务,URL 形如 /<srcDir 相对>/<asset 路径>\n * - build:Vite 自动 emit + 加 hash,URL 形如 /<assetsDir>/<name>-<hash>.<ext>\n * 这样我们就完全不用维护虚拟模块、不用写 load()、不会再撞 ?import 之类的边界\n * - handleHotUpdate:dev 增量更新\n * - config 钩子扩 server.fs.allow:用户的 vault 可能在项目根外,Vite 默认拒绝\n *\n * 设计:**完全不用 `\\0` 虚拟模块**。虚拟模块需要 Vite 在 URL 里把 id 编码成\n * `__x00__...`,加上 ?import 之类的 query,加上 URL 里 `/` 的歧义,导致 load()\n * 的 id 形态不稳定,要么找不到 asset 要么返回 HTML 把页面崩了(实测 v0.1\n * 收尾时撞过)。让 Vite 处理真实文件路径就完全没这一摊烦心事。\n */\n\nimport fs from 'node:fs'\nimport nodePath from 'node:path'\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type {\n AllYouNeedOptions,\n ResolvedOptions,\n VaultIndex,\n} from './core/types.js'\nimport { resolveOptions } from './core/config-bridge.js'\nimport { scanVault, updateFile, removeFile } from './core/vault/index.js'\nimport { createDevMiddleware } from './core/asset-pipeline/dev-middleware.js'\nimport { ASSET_PLACEHOLDER_PREFIX } from './core/asset-pipeline/build-emit.js'\nimport { toPosix } from './utils/path.js'\n\nexport interface VitePluginAllYouNeedReturn extends Plugin {\n __getOptions(): ResolvedOptions\n __getIndex(): VaultIndex | undefined\n}\n\n/** 剥掉 ?query 和 #hash —— Vite 会带各种后缀来 */\nfunction stripQueryAndHash(id: string): string {\n return id.split('?')[0]!.split('#')[0]!\n}\n\nexport function viteAllYouNeed(\n userOptions: AllYouNeedOptions = {},\n): VitePluginAllYouNeedReturn {\n let resolved: ResolvedOptions\n let index: VaultIndex | undefined\n let viteConfig: ResolvedConfig | undefined\n\n const plugin: VitePluginAllYouNeedReturn = {\n name: 'vitepress-allyouneed',\n\n /**\n * 在 Vite 配置定型前扩 server.fs.allow,让我们的 vault srcDir 可被 Vite\n * 服务(即便 srcDir 不在项目根下)。\n */\n config(_userViteConfig, _envCtx) {\n const srcDirOpt = userOptions.srcDir\n if (!srcDirOpt) return undefined\n const abs = toPosix(nodePath.resolve(srcDirOpt))\n return {\n server: {\n fs: {\n allow: [abs],\n },\n },\n }\n },\n\n configResolved(cfg) {\n viteConfig = cfg\n resolved = resolveOptions(userOptions, {\n srcDir: userOptions.srcDir ?? cfg.root,\n base: userOptions.base ?? cfg.base,\n cleanUrls: userOptions.cleanUrls,\n })\n try {\n index = scanVault(resolved)\n if (index.warnings.length > 0) {\n const top = index.warnings.slice(0, 10)\n for (const w of top) {\n cfg.logger.warn(`[vitepress-allyouneed] ${w.message}`)\n }\n if (index.warnings.length > top.length) {\n cfg.logger.warn(\n `[vitepress-allyouneed] (...还有 ${\n index.warnings.length - top.length\n } 条告警)`,\n )\n }\n }\n } catch (err) {\n cfg.logger.error(\n `[vitepress-allyouneed] vault 扫描失败: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n index = undefined\n }\n },\n\n configureServer(server) {\n if (!index) return\n const mw = createDevMiddleware(index, resolved)\n return () => {\n server.middlewares.use(mw)\n }\n },\n\n handleHotUpdate(ctx) {\n if (!index) return\n try {\n const stat = fs.statSync(ctx.file)\n if (stat.isFile()) {\n updateFile(index, ctx.file, resolved)\n } else if (!fs.existsSync(ctx.file)) {\n removeFile(index, ctx.file, resolved)\n }\n } catch {\n removeFile(index, ctx.file, resolved)\n }\n },\n\n /**\n * 拦截占位符 URL,**返回真实绝对文件路径**(POSIX 风格)。\n *\n * Vite 拿到文件路径会:\n * - dev:按文件系统服务,id 经 transform 后变成 `export default '<url>'`\n * - build:emit asset、Rollup 自动加 hash,导出最终 URL\n */\n resolveId(id) {\n if (!index) return null\n const stripped = stripQueryAndHash(id)\n const ph = ASSET_PLACEHOLDER_PREFIX // '/__ayn_asset__/'\n const phIdx = stripped.indexOf(ph)\n if (phIdx < 0) return null\n const encoded = stripped.slice(phIdx + ph.length)\n let relPath: string\n try {\n relPath = decodeURI(encoded)\n } catch {\n return null\n }\n const asset = index.assetsByRelativePath.get(relPath)\n if (!asset) return null\n // 把原 query(?import / ?inline / ?url 等)透传给下游,否则 Vite 会\n // 用错误的 transform 来处理\n const query = id.slice(stripped.length)\n return asset.absolutePath + query\n },\n\n /**\n * 给 vitepress.ts wrapper 用。\n */\n __getOptions() {\n return resolved\n },\n __getIndex() {\n return index\n },\n }\n\n // 防 unused\n void viteConfig\n\n return plugin\n}\n\nexport default viteAllYouNeed\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 * URL 拼接 / 编码工具。\n *\n * 关键设计:\n * - 站内 URL 永远是绝对路径 + base 前缀;\n * - 路径段做 URI 编码(空格、中文 → %xx),但保留 '/' 和 '#';\n * - 输出的 URL 仍是字符串,attr 写入时再做 HTML escape(escape.ts 负责)。\n */\n\n/**\n * 给一段路径做 URL 编码,但保留 '/' 和 '#'。\n *\n * `encodeURI` 默认就保留 '/'、'#'、'?' 等,但会保留 '%' —— 我们假设传入的是\n * 原始路径(未编码),所以直接 encodeURI 就行。\n */\nexport function encodePath(s: string): string {\n return encodeURI(s)\n}\n\n/**\n * 拼接 base + 路径段(单个或多个)+ 可选锚点,产出最终 URL。\n *\n * - 多余的 '/' 会被折叠;\n * - 空段被跳过;\n * - 始终以 base 开头(若 base 提供);\n * - cleanUrls=false 时调用方负责加 '.html' 后缀(我们这里不管)。\n */\nexport function buildUrl(\n base: string,\n pathSegments: string[],\n anchor?: string,\n): string {\n // 归一化 base:确保以 / 开头并以 / 结尾\n let normBase = base || '/'\n if (!normBase.startsWith('/')) normBase = '/' + normBase\n if (!normBase.endsWith('/')) normBase = normBase + '/'\n\n // 拼接路径段(过滤空、去段头尾的 /)\n const joined = pathSegments\n .map((s) => s.replace(/^\\/+|\\/+$/g, ''))\n .filter(Boolean)\n .join('/')\n\n let url = normBase + joined\n // 折叠相邻多 /\n url = url.replace(/\\/{2,}/g, '/')\n // URI 编码(空格、中文)\n url = encodePath(url)\n\n if (anchor) {\n // anchor 也要编码,但不再处理 /\n url += '#' + encodeURIComponent(anchor).replace(/%2F/g, '/')\n }\n return url\n}\n\n/**\n * 给路径加上 cleanUrls 决定的扩展名。\n *\n * cleanUrls=true:不加任何后缀(VitePress 生成 .html 但路由用无扩展)。\n * cleanUrls=false:加 '.html'。\n *\n * 调用方应该在 buildUrl 之前对 path 段调用本函数(或之后再追加 .html)。\n */\nexport function applyCleanUrls(path: string, cleanUrls: boolean): string {\n if (cleanUrls) return path\n if (/\\.html$/i.test(path)) return path\n // 末尾的 / 表示目录路由,index.html\n if (path.endsWith('/')) return path + 'index.html'\n return path + '.html'\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 * Heading 收集。\n *\n * 不引入完整 markdown-it 单纯为了收 heading —— 自己用正则扫,够快、零依赖。\n * 处理 ATX 风格(# / ## / ###),不处理 Setext(`===` / `---` 下划线风格,\n * Obsidian / VitePress 实际都极少用)。\n *\n * 识别 `{#custom-id}` 自定义 anchor 语法。\n */\n\nimport type { HeadingEntry } from '../types.js'\nimport { extractCustomId } from '../slugify.js'\n\nconst HEADING_RE = /^(#{1,6})\\s+(.+?)\\s*$/\nconst FENCE_RE = /^(`{3,}|~{3,})/\n\n/**\n * 从源文件正文(去掉 frontmatter 之后)收集 heading。\n *\n * @param content 正文\n * @param slugify 与 VitePress 一致的 slug 函数\n */\nexport function collectHeadings(\n content: string,\n slugify: (text: string) => string,\n): HeadingEntry[] {\n const lines = content.split(/\\r?\\n/)\n const out: HeadingEntry[] = []\n\n let inFence = false\n let fenceMarker = ''\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n\n // 跳过代码块内部\n const fenceMatch = line.match(FENCE_RE)\n if (fenceMatch) {\n if (!inFence) {\n inFence = true\n fenceMarker = fenceMatch[1]!\n } else if (line.startsWith(fenceMarker)) {\n inFence = false\n fenceMarker = ''\n }\n continue\n }\n if (inFence) continue\n\n const m = line.match(HEADING_RE)\n if (!m) continue\n\n const level = m[1]!.length\n const rawText = m[2]!\n const { text, customId } = extractCustomId(rawText)\n const slug = customId ?? slugify(text)\n\n out.push({ level, text, slug, line: i })\n }\n\n return out\n}\n","/**\n * Dev 模式中间件 —— 拦截未知 URL,从 vault asset 索引里找文件流式响应。\n *\n * 查找顺序:\n * 1. **按相对路径**(`/<base>/<relativePath>`)—— resolveId/load 路径下,\n * Vue 编译后的 `<img src>` 解析出来的 URL 走这条\n * 2. **按 basename** —— 用户直接在 markdown 用绝对 URL `/foo.png` 时兜底\n * 3. 都没命中 → next() 给 Vite 默认 404\n *\n * 零拷贝、零配置。改图片直接刷新就能看到。\n */\n\nimport fs from 'node:fs'\nimport nodePath from 'node:path'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type {\n VaultIndex,\n ResolvedOptions,\n AssetEntry,\n} from '../types.js'\nimport { basename } from '../../utils/path.js'\n\nexport type DevMiddleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void,\n) => void\n\nexport function createDevMiddleware(\n index: VaultIndex,\n options: ResolvedOptions,\n): DevMiddleware {\n return (req, res, next) => {\n if (!req.url) return next()\n\n const cleanPath = req.url.split('?')[0]!.split('#')[0]!\n\n // 把 base 前缀剥掉,得到站内绝对路径\n let inSiteUrl = cleanPath\n if (options.base !== '/' && cleanPath.startsWith(options.base)) {\n inSiteUrl = '/' + cleanPath.slice(options.base.length)\n }\n\n let decoded: string\n try {\n decoded = decodeURIComponent(inSiteUrl)\n } catch {\n return next()\n }\n\n // 必须含扩展名才尝试解析(避免误吞 /about 这种页面路由)\n if (!/\\.[a-zA-Z0-9]+$/.test(decoded)) return next()\n\n // 1) 先按相对路径查\n const relCandidate = decoded.replace(/^\\/+/, '')\n let asset: AssetEntry | undefined =\n index.assetsByRelativePath.get(relCandidate)\n\n // 2) 再按 basename 查\n if (!asset) {\n const bn = basename(decoded)\n const map = options.caseSensitive\n ? index.assetsByBasename\n : index.assetsByBasenameLower\n const key = options.caseSensitive ? bn : bn.toLowerCase()\n const candidates = map.get(key)\n if (candidates && candidates.length > 0) asset = candidates[0]\n }\n\n if (!asset) return next()\n\n let stat: fs.Stats\n try {\n stat = fs.statSync(asset.absolutePath)\n } catch {\n return next()\n }\n\n res.statusCode = 200\n res.setHeader('Content-Type', guessMime(asset.extension))\n res.setHeader('Content-Length', String(stat.size))\n res.setHeader('Cache-Control', 'no-cache')\n\n fs.createReadStream(asset.absolutePath)\n .on('error', () => next())\n .pipe(res)\n }\n}\n\n// 极简 MIME 映射,够 vault 里常见 asset 用。\nconst MIME: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n bmp: 'image/bmp',\n avif: 'image/avif',\n ico: 'image/x-icon',\n mp4: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n m4v: 'video/x-m4v',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n flac: 'audio/flac',\n pdf: 'application/pdf',\n canvas: 'application/json',\n excalidraw: 'application/json',\n}\n\nfunction guessMime(ext: string): string {\n return MIME[ext.toLowerCase()] ?? 'application/octet-stream'\n}\n\n// 防 unused\nexport { nodePath }\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"],"mappings":"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","names":["import_node_fs","import_node_path","mditVueSlugify","import_node_fs","import_node_path","nodePath","import_node_path","fs","nodePath","import_node_fs","import_node_path","nodePath","fs","picomatch","matter","nodePath","fs","import_node_fs","import_node_path","fs","nodePath","fs"]}
@@ -0,0 +1,30 @@
1
+ import { Plugin } from 'vite';
2
+ import { a as AllYouNeedOptions, f as ResolvedOptions, V as VaultIndex } from './types-CapUIPbW.cjs';
3
+
4
+ /**
5
+ * Vite 插件入口。
6
+ *
7
+ * 职责:
8
+ * - configResolved 时扫描 vault 建立 VaultIndex
9
+ * - configureServer 注册 dev middleware(asset 流式响应,作为兜底)
10
+ * - **resolveId**:拦截 markdown-it 渲染时产出的 /__ayn_asset__/ 占位 URL,
11
+ * **直接返回 asset 的绝对文件路径**,后续交给 Vite 自带的资源管线处理:
12
+ * - dev:Vite 自动按文件路径服务,URL 形如 /<srcDir 相对>/<asset 路径>
13
+ * - build:Vite 自动 emit + 加 hash,URL 形如 /<assetsDir>/<name>-<hash>.<ext>
14
+ * 这样我们就完全不用维护虚拟模块、不用写 load()、不会再撞 ?import 之类的边界
15
+ * - handleHotUpdate:dev 增量更新
16
+ * - config 钩子扩 server.fs.allow:用户的 vault 可能在项目根外,Vite 默认拒绝
17
+ *
18
+ * 设计:**完全不用 `\0` 虚拟模块**。虚拟模块需要 Vite 在 URL 里把 id 编码成
19
+ * `__x00__...`,加上 ?import 之类的 query,加上 URL 里 `/` 的歧义,导致 load()
20
+ * 的 id 形态不稳定,要么找不到 asset 要么返回 HTML 把页面崩了(实测 v0.1
21
+ * 收尾时撞过)。让 Vite 处理真实文件路径就完全没这一摊烦心事。
22
+ */
23
+
24
+ interface VitePluginAllYouNeedReturn extends Plugin {
25
+ __getOptions(): ResolvedOptions;
26
+ __getIndex(): VaultIndex | undefined;
27
+ }
28
+ declare function viteAllYouNeed(userOptions?: AllYouNeedOptions): VitePluginAllYouNeedReturn;
29
+
30
+ export { type VitePluginAllYouNeedReturn, viteAllYouNeed as default, viteAllYouNeed };
package/dist/vite.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { Plugin } from 'vite';
2
+ import { a as AllYouNeedOptions, f as ResolvedOptions, V as VaultIndex } from './types-CapUIPbW.js';
3
+
4
+ /**
5
+ * Vite 插件入口。
6
+ *
7
+ * 职责:
8
+ * - configResolved 时扫描 vault 建立 VaultIndex
9
+ * - configureServer 注册 dev middleware(asset 流式响应,作为兜底)
10
+ * - **resolveId**:拦截 markdown-it 渲染时产出的 /__ayn_asset__/ 占位 URL,
11
+ * **直接返回 asset 的绝对文件路径**,后续交给 Vite 自带的资源管线处理:
12
+ * - dev:Vite 自动按文件路径服务,URL 形如 /<srcDir 相对>/<asset 路径>
13
+ * - build:Vite 自动 emit + 加 hash,URL 形如 /<assetsDir>/<name>-<hash>.<ext>
14
+ * 这样我们就完全不用维护虚拟模块、不用写 load()、不会再撞 ?import 之类的边界
15
+ * - handleHotUpdate:dev 增量更新
16
+ * - config 钩子扩 server.fs.allow:用户的 vault 可能在项目根外,Vite 默认拒绝
17
+ *
18
+ * 设计:**完全不用 `\0` 虚拟模块**。虚拟模块需要 Vite 在 URL 里把 id 编码成
19
+ * `__x00__...`,加上 ?import 之类的 query,加上 URL 里 `/` 的歧义,导致 load()
20
+ * 的 id 形态不稳定,要么找不到 asset 要么返回 HTML 把页面崩了(实测 v0.1
21
+ * 收尾时撞过)。让 Vite 处理真实文件路径就完全没这一摊烦心事。
22
+ */
23
+
24
+ interface VitePluginAllYouNeedReturn extends Plugin {
25
+ __getOptions(): ResolvedOptions;
26
+ __getIndex(): VaultIndex | undefined;
27
+ }
28
+ declare function viteAllYouNeed(userOptions?: AllYouNeedOptions): VitePluginAllYouNeedReturn;
29
+
30
+ export { type VitePluginAllYouNeedReturn, viteAllYouNeed as default, viteAllYouNeed };