跳到主要内容

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:72url = ${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 不可变),用构建层 + 托管层两步绕过:

  1. 去门控:webpack loader scripts/strip-search-dev-gate-loader.cjssearchByWorker.js / EmptyTemplate.js 里的 process.env.NODE_ENV === "production" 字符串替换为 truedocusaurus.config.ts 新增 searchDevGate 插件,用 require.resolve 动态定位这两个文件(避免硬编码 .pnpm hash 路径),configureWebpack 加 module rule 精确 include 这两个文件挂该 loader。production build 时替换等价于原值,常开安全。
  2. 托管索引scripts/prepare-search-index.mjsbuild/search-index.json 复制到 static/search-index.json(mtime 比对跳过重复),dev server serve static/ 到根,worker 即可 fetch /search-index.jsonpnpm 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 同一手法。