src/content/ 下的六个软链,是 content.config.ts 自己证明不需要的东西。
现状
src/content.config.ts 里有两种 base 写法,并存:
// 直接读 vault,没有软链
site: glob ( { pattern : 'site.md' , base : findContentRoot ( ) ?? './.content' } )
translations: glob ( { pattern : '**/*.{zh,en}.md' , base : findContentRoot ( ) ?? './.content' } )
// 必须先有软链
people: glob ( { ..., base : './src/content/people' } )
projects: glob ( { ..., base : './src/content/projects' } )
library: glob ( { ..., base : './src/content/library' } )
research: glob ( { ..., base : './src/content/library/研究' } )
resources: glob ( { ..., base : './src/content/library/资源' } )
publicationHighlights: glob ( { ..., base : './src/content/library/文献/精选' } )
translations 集合会遍历整个 vault 找 *.en.md,它用绝对路径就够了。
同一个 vault、同一个 glob() loader,前两行不需要软链,后六行需要——差别不在
Astro,只在这六行 base 怎么写的。
这六个软链的代价
scripts/setup-content.mjs 里一个 320 行的闭包 linkCollection (L364–683,
在一个 370 行的 setupContentCollections 里面)。它要处理:软链已存在、
软链指向旧路径、目标是真目录、Windows/Vercel 上 symlink 不可用要退化成
fs.cpSync、删除失败要重试三次并 sleep 递增退避、复制后要再验证一次。
这段代码做递归删除 ,没有任何测试 ,而 eslint.config.js 自己的注释
就把「path resolution / recursive deletes」列为本仓最脆弱的代码。
.gitignore 要专门写三行 把 src/ 里的三个路径排除掉:
src/content/people
src/content/projects
src/content/library
eslint.config.js 要专门 ignore 同样三个路径 ,注释写着「linting them would
walk content that this repo does not own」。
src/content/ 这一层同时装了生成物和源码 :三个 gitignore 的软链,和
src/content/loaders/(745 行真源码,有测试)并排。ls src/content/ 看不出
哪个是哪个。
collection → vault 目录的映射又多了一份。 setup-content.mjs 已经
import { BUNDLE_COLLECTION_DIRS } from '../src/utils/contentLayout.js',
却在 L686–694 硬编码:
await linkCollection ( 'people' , '通讯录' ) ;
await linkCollection ( 'projects' , '图书馆/项目' ) ;
await linkCollection ( 'library' , '图书馆' ) ;
而 contentLayout.js 里就有 COLLECTION_VAULT_PATHS = { people: '通讯录', projects: '图书馆/项目', library: '图书馆', … }。vault 里改个目录名,
这三行不会报错,只会安静地少建一个软链、少一整个 collection。
(i18n 收口留下的重复与死代码:OVERLAY_META、sidecar 目录、isFallback、resolveLocalized #42 数出四份映射,这是第五份;research/resources 的 base 穿过 library
软链再拼一次子目录,是第六份。)
建议
把六个 base 改成从 vault 根算出来的绝对路径,和 site / translations 一样:
const root = findContentRoot ( ) ?? './.content' ;
people: glob ( { ..., base : path . join ( root , COLLECTION_VAULT_PATHS . people ) } )
这样一次拿掉:linkCollection 的 320 行、三行 .gitignore、三行 eslint ignore、
以及第五、第六份目录映射。setup-content.mjs 只剩下它真正该干的事——把内容仓
clone 或软链到 .content/,同步 attachments。src/content/ 只剩 loaders/,
是纯源码。
需要先验证的一点:astro sync 生成的 collection 类型是否依赖 base 在 src/ 下。
site 和 translations 已经不在 src/ 下且类型正常,所以大概率不依赖,但值得
在动手前跑一次确认。
src/content/下的六个软链,是content.config.ts自己证明不需要的东西。现状
src/content.config.ts里有两种 base 写法,并存:translations集合会遍历整个 vault 找*.en.md,它用绝对路径就够了。同一个 vault、同一个
glob()loader,前两行不需要软链,后六行需要——差别不在Astro,只在这六行 base 怎么写的。
这六个软链的代价
scripts/setup-content.mjs里一个 320 行的闭包linkCollection(L364–683,在一个 370 行的
setupContentCollections里面)。它要处理:软链已存在、软链指向旧路径、目标是真目录、Windows/Vercel 上 symlink 不可用要退化成
fs.cpSync、删除失败要重试三次并 sleep 递增退避、复制后要再验证一次。这段代码做递归删除,没有任何测试,而
eslint.config.js自己的注释就把「path resolution / recursive deletes」列为本仓最脆弱的代码。
.gitignore要专门写三行把src/里的三个路径排除掉:eslint.config.js要专门 ignore 同样三个路径,注释写着「linting them wouldwalk content that this repo does not own」。
src/content/这一层同时装了生成物和源码:三个 gitignore 的软链,和src/content/loaders/(745 行真源码,有测试)并排。ls src/content/看不出哪个是哪个。
collection → vault 目录的映射又多了一份。
setup-content.mjs已经import { BUNDLE_COLLECTION_DIRS } from '../src/utils/contentLayout.js',却在 L686–694 硬编码:
而
contentLayout.js里就有COLLECTION_VAULT_PATHS = { people: '通讯录', projects: '图书馆/项目', library: '图书馆', … }。vault 里改个目录名,这三行不会报错,只会安静地少建一个软链、少一整个 collection。
(i18n 收口留下的重复与死代码:OVERLAY_META、sidecar 目录、isFallback、resolveLocalized #42 数出四份映射,这是第五份;
research/resources的 base 穿过 library软链再拼一次子目录,是第六份。)
建议
把六个 base 改成从 vault 根算出来的绝对路径,和
site/translations一样:这样一次拿掉:
linkCollection的 320 行、三行.gitignore、三行 eslint ignore、以及第五、第六份目录映射。
setup-content.mjs只剩下它真正该干的事——把内容仓clone 或软链到
.content/,同步 attachments。src/content/只剩loaders/,是纯源码。
需要先验证的一点:
astro sync生成的 collection 类型是否依赖 base 在src/下。site和translations已经不在src/下且类型正常,所以大概率不依赖,但值得在动手前跑一次确认。