ISSUE-046: docs-site 本地搜索在 dev server 不可用
- 日期:2026-06-10
- 状态:已修复(待浏览器最终确认)
- 类型:功能缺失 / 开发体验
- 影响范围:docs-site dev server (
pnpm start, :3000) 搜索功能
问题现象
在 pnpm start 的开发服务器上点搜索框,输入任意词只显示提示:
⚠️ The search index is only available when you run docusaurus build!
搜索完全不可用,只有 pnpm build + serve 的生产产物能搜。
调查过程
- [查代码]
@easyops-cn/docusaurus-search-local客户端三处硬门控process.env.NODE_ENV === "production":dist/client/client/theme/searchByWorker.js:3 处——dev 时 search worker 不启动、fetchIndexes空转、searchByWorker直接返回[]。dist/client/client/theme/SearchBar/EmptyTemplate.js:1 处——dev 时空结果模板渲染上述 warning。
- [查代码] worker 索引拉取
worker.js:72:url = ${baseUrl}${searchIndexUrl},searchIndexUrl来自插件生成的generated-constants.js,dev 模式值为search-index{dir}.json(无 searchContext 即search-index.json)。 - [查产物] 索引文件
search-index.json仅由插件 postBuild 生成于build/;dev 模式.docusaurus/下不存在——这是 dev 搜不到的数据缺口。 - [实验] 确认 dev 模式
generated-constants.js已生成且searchIndexUrl/language=["zh","en"]完备,仅缺索引文件本身。 - [查证业界] 所有 Docusaurus 本地搜索插件(easyops-cn / cmfcmf / gabrielcsapo)均 production-only,换插件无解;dev 实时可搜只有搜索服务器(Meilisearch/Typesense)路线,运维成本对单人内网站不划算。
根因分析
插件按"dev 不生成索引、故 dev 不提供搜索"设计,把 worker 启动 + 索引拉取 + 结果模板全部门控在 production 分支。dev 缺的是两样:① 门控放行 ② 索引文件可被 dev server 访问。
解决方案
不改插件源码(pnpm 不可变),用构建层 + 托管层两步绕过:
- 去门控:webpack loader
scripts/strip-search-dev-gate-loader.cjs把searchByWorker.js/EmptyTemplate.js里的process.env.NODE_ENV === "production"字符串替换为true。docusaurus.config.ts新增searchDevGate插件,用require.resolve动态定位这两个文件(避免硬编码 .pnpm hash 路径),configureWebpack 加 module rule 精确 include 这两个文件挂该 loader。production build 时替换等价于原值,常开安全。 - 托管索引:
scripts/prepare-search-index.mjs把build/search-index.json复制到static/search-index.json(mtime 比对跳过重复),dev server servestatic/到根,worker 即可 fetch/search-index.json。pnpm start链加入该脚本,.gitignore排除static/search-index.json(约 22MB)。
附带:撤销前一轮 ISSUE 之外的 3001 serve 方案(dev 能搜后端口冗余),start-dev.mjs 回归单 3000。
涉及文件:
- 新建
docs-site/scripts/strip-search-dev-gate-loader.cjs - 新建
docs-site/scripts/prepare-search-index.mjs docs-site/docusaurus.config.ts(searchDevGate 插件)docs-site/package.json(start 链 + prepare-search-index)docs-site/scripts/start-dev.mjs(撤 3001 serve).gitignore(static/search-index.json)
验证
自动验证(已通过,测试端口 3199):
- dev server 启动返回 200——webpack loader 未破坏编译。
/search-index.json返回 200 + 23MB JSON 数组——索引托管成功。/search/?q=ring搜索结果页返回 200。
手动验证(待浏览器确认):pnpm start 后在 :3000 搜索框输入词,应正常返回结果而非 warning。worker 在 dev 浏览器的实际启动+查询无法用 curl 验证,需肉眼确认。
局限 / 遗留
- 索引非实时:dev 不重建索引,搜到的是最近一次 build 的快照(每日 07:00 自动 build + 手动 build 刷新)。刚写未 build 的新文档搜不到。这是 Docusaurus 本地搜索架构的固有限制,非本方案可解。
- 耦合插件内部:loader 依赖
searchByWorker.js/EmptyTemplate.js的文件路径与门控写法,插件大版本升级可能失效——升级后需复查这两个文件的 NODE_ENV 门控是否仍是同样字符串。
Lessons Learned / Prevention
- 第三方插件的"环境门控"(dev/prod 行为差异)若挡了合理需求,构建层 loader 字符串替换比 fork/swizzle 更轻、更易随升级复查。
- 动态
require.resolve定位包内文件,避免硬编码 pnpm 虚拟 store 的 hash 路径——与themeCommonDedup同一手法。