fix(seo): leetcode 中文 slug 旧 URL 301 到拼音新路径#318
Conversation
GSC 报 41 条 /docs/CommunityShare/Leetcode/<中文文件名> 的 404。根因: lib/source.ts 里的 transformer 会把 leetcode 目录下含中文的文件名转成拼音 slug,但 next.config.mjs 的 wildcard 只做前缀替换没做 slug 拼音化,跳过去 slug 还是中文导致 404。 改动: - scripts/generate-leetcode-slug-map.mjs 构建时扫目录,输出 generated/leetcode-slug-map.json「中文 stem → 拼音 stem」字面映射 - proxy.ts 新增 redirectLeetcodeIfNeeded:Edge 端 O(1) 查表单跳 301 - next.config.mjs 删 leetcode wildcard,改由 middleware 处理避免二跳 - prebuild 挂钩生成脚本,保证 JSON 永远同步最新文件列表 - 补 dev_docs/leetcode_slug_redirect.md 讲清方案
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
该 PR 旨在解决 Google Search Console 中历史 Leetcode 文档旧链接(含中文文件名 slug)在 301 后仍 404 的问题,通过在 Edge 侧将旧中文 slug 单跳重定向到拼音化后的新路径,改善 SEO 抓取与用户访问体验。
Changes:
- 新增构建脚本扫描 Leetcode 目录,生成
中文 stem → 拼音 stem的 JSON 映射表 - 在 Edge 侧(proxy/middleware)基于映射表对旧路径/中文 slug 做单跳 301 到新路径
- 移除
next.config.mjs中 Leetcode wildcard redirect,避免“前缀替换但 slug 未拼音化”的二跳/404
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/generate-leetcode-slug-map.mjs | 构建时生成中文文件名到拼音 slug 的字面映射 JSON |
| generated/leetcode-slug-map.json | 新增映射表产物,供 Edge 查表重定向 |
| proxy.ts | 新增 Leetcode 路径/中文 slug 的查表 301 重定向逻辑 |
| next.config.mjs | 删除 Leetcode wildcard redirect,改由 Edge 逻辑处理 |
| package.json | prebuild 增加生成映射表脚本,确保构建产物最新 |
| dev_docs/leetcode_slug_redirect.md | 记录方案、覆盖范围与验证方式 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * 与 lib/source.ts 中 convertSlugToPinyin 保持同步。 | ||
| * 入参:单个 slug 片段(一般是文件名 stem)。 | ||
| * 无中文直接原样返回;有中文则按拼音 + 非字母数字清洗 + 连字符拼接。 | ||
| */ | ||
| function convertSlugToPinyin(text) { | ||
| const decodedText = decodeURIComponent(text); | ||
| if (!/[\u4e00-\u9fa5]/.test(decodedText)) return text; | ||
| return pinyin(decodedText, { | ||
| toneType: "none", | ||
| type: "array", | ||
| nonZh: "consecutive", | ||
| }) | ||
| .map((t) => t.toLowerCase().replace(/[^a-z0-9]/g, "")) | ||
| .filter(Boolean) | ||
| .join("-"); | ||
| } |
There was a problem hiding this comment.
已修:抽到 lib/leetcode-slug.ts,lib/source.ts 的 transformer 和 scripts/generate-leetcode-slug-map.mts 都 import 同一份 convertSlugToPinyin。脚本改成 .mts 走 Node 24 原生 TS 支持,避免 tsx v4.21 把 TS 转成 CJS 丢 named export 的问题。commit 25a076e
| rawSlug = rest; | ||
| } | ||
|
|
||
| const mapped = SLUG_MAP[rawSlug]; |
There was a problem hiding this comment.
已修:实测这确实是真 bug,不是理论风险。用 plain object 时 SLUG_MAP["__proto__"] 返回 [object Object]、SLUG_MAP["constructor"] 返回 Object 构造函数(function Object() { [native code] }),会 301 到诡异 URL。改成 new Map(Object.entries(...)),Map 天然隔离原型链。实测 /docs/CommunityShare/Leetcode/__proto__ / constructor / hasOwnProperty 现在都按原样落地到不存在的新路径(正常 404),不会命中原型。commit 25a076e
| export function proxy(req: NextRequest) { | ||
| // Leetcode 老 URL / 中文 slug 优先做 301,避免后续 locale 逻辑给 404 页种 cookie | ||
| const leetcodeRedirect = redirectLeetcodeIfNeeded(req); | ||
| if (leetcodeRedirect) return leetcodeRedirect; |
There was a problem hiding this comment.
这个是 Copilot 过时了——Next 16 把 middleware.ts 官方改名为 proxy.ts,导出符号也从 middleware 改成 proxy。仓库里 commit 7e079c3 "fix: 修复警告, 现在已经用Proxy代替Middleware了" 就是做这次迁移。
实测验证(production build + next start):
curl -I "http://localhost:3999/docs/CommunityShare/Leetcode/%5B146%5DLRU%20%E7%BC%93%E5%AD%98_translated"
# → 301 Location: /docs/career/interview-prep/leetcode/146lru-huan-cun-translated
Build 输出里也有 ƒ Proxy (Middleware) 那行,说明它被识别为 middleware。
1. 抽 convertSlugToPinyin 到 lib/leetcode-slug.ts,lib/source.ts 和生成脚本 共用同一实现,消除双点维护(Copilot CR comment #1) 2. proxy.ts 里把 SLUG_MAP 从 plain object 换成 Map,实测 __proto__/constructor 等原型链 key 会被错误命中(拿到 [object Object] / Object 构造函数), Map 天然隔离原型链(Copilot CR comment #2) 3. 脚本从 .mjs 改为 .mts 以便 import TypeScript 共享模块,用 Node 24 的 原生 TS 支持执行,prebuild 走 node 不走 tsx(tsx v4.21 在 Node 24 下 会把 TS 转成 CJS 导致命名 ESM 导出丢失)
背景
GSC 报 involutionhell.com 41 条 404,集中在
/docs/CommunityShare/Leetcode/<中文文件名>这类路径。根因
lib/source.ts里的 transformer 会把app/docs/career/interview-prep/leetcode/下含中文的文件名转成拼音 slug(例:
2309兼具大小写的最好英文字母_translated.md对外是
/2309-jian-ju-...-translated)。但
next.config.mjs的 wildcard/docs/CommunityShare/Leetcode/:path*→/docs/career/interview-prep/leetcode/:path*只做前缀替换,不做 slug 拼音化。所以旧 URL 301 跳过去之后 slug 还是中文,目标页依然 404。
改动
scripts/generate-leetcode-slug-map.mjs构建时扫目录,输出generated/leetcode-slug-map.json字面映射(当前 32 条)proxy.ts新增redirectLeetcodeIfNeeded,Edge 端 O(1) 查表单跳 301next.config.mjs删掉 leetcode wildcard,改由 middleware 处理避免二跳package.jsonprebuild 挂钩生成脚本dev_docs/leetcode_slug_redirect.md讲方案选型理由
[]/ 中文在 Next.js 路由匹配不稳本地验证
抽查 5 条 GSC 报告的 404 URL,全部单跳 301 到正确拼音页(200)。
Test plan
pnpm typecheck通过pnpm lint通过(无新增警告)pnpm build通过,middleware bundle 正常