Skip to content

Commit 5c3ec7b

Browse files
authored
Merge pull request #4198 from nfs0619/102
fix some bug slugs
2 parents 64f8ef6 + 2825fde commit 5c3ec7b

File tree

19 files changed

+194
-19
lines changed

19 files changed

+194
-19
lines changed

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"write-heading-ids": "docusaurus write-heading-ids",
2828
"typecheck": "tsc",
2929
"generatewiki": "node scripts/generateWiki.js",
30-
"generate-lang-map": "node scripts/generate-language-map.js"
30+
"generate-lang-map": "node scripts/generate-language-map.js",
31+
"normalize-frontmatter": "node scripts/normalize-frontmatter.js"
32+
3133
},
3234
"dependencies": {
3335
"@ant-design/icons": "^5.4.0",
@@ -72,8 +74,11 @@
7274
"baseline-browser-mapping": "^2.9.14",
7375
"concurrently": "^9.2.1",
7476
"cross-env": "^10.1.0",
77+
"fast-glob": "^3.3.3",
7578
"gh-pages": "^6.3.0",
7679
"glob": "^11.0.2",
80+
"gray-matter": "^4.0.3",
81+
"js-yaml": "^4.1.1",
7782
"typescript": "^4.7.4"
7883
},
7984
"browserslist": {

scripts/normalize-frontmatter.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env node
2+
/* scripts/normalize-frontmatter.js */
3+
const fs = require("fs");
4+
const path = require("path");
5+
const matter = require("gray-matter");
6+
const fg = require("fast-glob");
7+
const yaml = require("js-yaml");
8+
const { execSync } = require("child_process");
9+
10+
const ROOT = process.cwd();
11+
const BASE_URL = "https://wiki.seeedstudio.com";
12+
13+
const LANG_RULES = [
14+
{ dir: path.join("sites", "en", "docs"), prefix: "" },
15+
{ dir: path.join("sites", "zh-CN", "docs"), prefix: "/cn" },
16+
{ dir: path.join("sites", "ja", "docs"), prefix: "/ja" },
17+
{ dir: path.join("sites", "es", "docs"), prefix: "/es" },
18+
];
19+
20+
function detectLangPrefix(fileAbsPath) {
21+
const rel = path.relative(ROOT, fileAbsPath).split(path.sep).join("/");
22+
for (const r of LANG_RULES) {
23+
const dirNorm = r.dir.split(path.sep).join("/");
24+
if (rel.startsWith(dirNorm + "/") || rel === dirNorm) return r.prefix;
25+
}
26+
return null;
27+
}
28+
29+
function runGit(cmd) {
30+
try {
31+
return execSync(cmd, { cwd: ROOT, stdio: ["ignore", "pipe", "ignore"] })
32+
.toString("utf8")
33+
.trim();
34+
} catch {
35+
return "";
36+
}
37+
}
38+
39+
function getGitTimes(fileAbsPath) {
40+
// 最新提交时间
41+
const latest = runGit(`git log -1 --format=%aI -- "${fileAbsPath}"`);
42+
// 最早提交时间(文件首次出现)
43+
// --follow 对重命名更友好;--diff-filter=A 获取“新增该文件”的提交
44+
// 若历史中有重命名导致 A 取不到,则 fallback 到 reverse 的第一条
45+
let first = runGit(
46+
`git log --follow --diff-filter=A --format=%aI -1 -- "${fileAbsPath}"`
47+
);
48+
49+
if (!first) {
50+
const all = runGit(`git log --follow --format=%aI --reverse -- "${fileAbsPath}"`);
51+
first = all ? all.split(/\r?\n/)[0].trim() : "";
52+
}
53+
54+
return { createdAt: first, updatedAt: latest };
55+
}
56+
57+
function toYMD(iso) {
58+
if (!iso || typeof iso !== "string") return "";
59+
// git 的 %aI 格式是 ISO 8601,前 10 位就是 YYYY-MM-DD
60+
return iso.slice(0, 10);
61+
}
62+
63+
function normalizeSlug(slug) {
64+
if (!slug || typeof slug !== "string") return "";
65+
let s = slug.trim();
66+
if (!s.startsWith("/")) s = "/" + s;
67+
68+
// ✅ 空白(空格/tab/多空白)=> 下划线
69+
s = s.replace(/\s+/g, "_");
70+
71+
// ✅ 可选:避免出现多个连续下划线
72+
s = s.replace(/_+/g, "_");
73+
74+
return s;
75+
}
76+
77+
function buildUrl(prefix, slug) {
78+
const p = prefix ? (prefix.startsWith("/") ? prefix : "/" + prefix) : "";
79+
const s = normalizeSlug(slug);
80+
if (!s) return "";
81+
let u = `${BASE_URL}${p}${s}`;
82+
if (!u.endsWith("/")) u += "/";
83+
return u;
84+
}
85+
86+
function dumpFrontmatter(data) {
87+
// 不排序:尽量保留你原本 key 的相对习惯(js-yaml 默认不保证顺序,但多数情况下保持插入顺序)
88+
// noRefs: 避免出现 &a *a 之类引用
89+
return yaml.dump(data, {
90+
noRefs: true,
91+
lineWidth: 1000,
92+
}).trimEnd();
93+
}
94+
95+
function normalizeFile(fileAbsPath, { checkOnly = false } = {}) {
96+
const raw = fs.readFileSync(fileAbsPath, "utf8");
97+
const parsed = matter(raw);
98+
99+
const data = parsed.data || {};
100+
const content = parsed.content || "";
101+
102+
const prefix = detectLangPrefix(fileAbsPath);
103+
// 你给的语言目录里应该都能匹配上;匹配不上就跳过 url 生成
104+
const slug = normalizeSlug(data.slug);
105+
106+
const gitTimes = getGitTimes(fileAbsPath);
107+
108+
// createdAt:优先用已有值(如果你之前手工写过),否则用 git 最早提交时间
109+
// 如果已有值为空/无效,再补
110+
if (!data.createdAt && gitTimes.createdAt) data.createdAt = toYMD(gitTimes.createdAt);
111+
// updatedAt:总是更新为 git 最新提交时间(若取不到则不动)
112+
if (gitTimes.updatedAt) data.updatedAt = toYMD(gitTimes.updatedAt);
113+
114+
// url:只要能识别语言 + slug,就生成/更新
115+
if (prefix !== null && slug) {
116+
data.url = buildUrl(prefix, slug);
117+
// 同时把规范化后的 slug 写回(可选,但通常是你想要的)
118+
data.slug = slug;
119+
}
120+
121+
const newFm = dumpFrontmatter(data);
122+
const newRaw = `---\n${newFm}\n---\n${content.replace(/^\n+/, "")}`;
123+
124+
const changed = newRaw !== raw;
125+
126+
if (changed) {
127+
if (!checkOnly) fs.writeFileSync(fileAbsPath, newRaw, "utf8");
128+
}
129+
return { changed, fileAbsPath };
130+
}
131+
132+
async function main() {
133+
const args = process.argv.slice(2);
134+
const checkOnly = args.includes("--check");
135+
136+
const patterns = [
137+
"sites/en/docs/**/*.md",
138+
"sites/en/docs/**/*.mdx",
139+
"sites/zh-CN/docs/**/*.md",
140+
"sites/zh-CN/docs/**/*.mdx",
141+
"sites/ja/docs/**/*.md",
142+
"sites/ja/docs/**/*.mdx",
143+
"sites/es/docs/**/*.md",
144+
"sites/es/docs/**/*.mdx",
145+
];
146+
147+
const files = await fg(patterns, { absolute: true, dot: false });
148+
let changedCount = 0;
149+
150+
for (const f of files) {
151+
const r = normalizeFile(f, { checkOnly });
152+
if (r.changed) changedCount++;
153+
}
154+
155+
if (checkOnly) {
156+
if (changedCount > 0) {
157+
console.log(`[CHECK] ${changedCount} files would be modified.`);
158+
process.exitCode = 1;
159+
} else {
160+
console.log("[CHECK] All good. No changes needed.");
161+
}
162+
} else {
163+
console.log(`[DONE] Updated ${changedCount} files.`);
164+
}
165+
}
166+
167+
main().catch((e) => {
168+
console.error(e);
169+
process.exit(2);
170+
});

sites/en/docs/Edge/NVIDIA_Jetson/Application/Generative_AI/Real_Time_Subtitle_Recoder_on_Jetson.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ keywords:
66
- reComputer
77
- Jetson
88
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
9-
slug: /Real Time Subtitle Recoder on Nvidia Jetson
9+
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
1010
last_update:
1111
date: 02/23/2024
1212
author: Jiahao

sites/en/docs/Seeed_Elderly/Discrete_Product/Grove_Maker_Kit_for_Intel_Joule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Grove Maker Kit for Intel Joule
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /Grove Maker Kit for Intel Joule
7+
slug: /Grove_Maker_Kit_for_Intel_Joule
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

sites/en/docs/Seeed_Elderly/Discrete_Product/IoT_Fast_Prototyping_Kit_S5D9.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: IoT Fast Prototyping Kit S5D9
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /IoT_Fast_Prototyping_Kit S5D9
7+
slug: /IoT_Fast_Prototyping_Kit_S5D9
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

sites/en/docs/Seeed_Elderly/rePhone/Retro_Phone_Kit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Retro Phone Kit
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /Retro Phone Kit
7+
slug: /Retro_Phone_Kit
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

sites/es/docs/Edge/NVIDIA_Jetson/Application/Generative_AI/es_Real_Time_Subtitle_Recoder_on_Jetson.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ keywords:
66
- reComputer
77
- Jetson
88
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
9-
slug: /Real Time Subtitle Recoder on Nvidia Jetson
9+
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
1010
last_update:
1111
date: 02/23/2024
1212
author: Jiahao

sites/es/docs/Seeed_Elderly/Discrete_Product/es_Grove_Maker_Kit_for_Intel_Joule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Grove Maker Kit para Intel Joule
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /Grove Maker Kit for Intel Joule
7+
slug: /Grove_Maker_Kit_for_Intel_Joule
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

sites/es/docs/Seeed_Elderly/Discrete_Product/es_IoT_Fast_Prototyping_Kit_S5D9.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Kit de Prototipado Rápido IoT S5D9
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /IoT_Fast_Prototyping_Kit S5D9
7+
slug: /IoT_Fast_Prototyping_Kit_S5D9
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

sites/es/docs/Seeed_Elderly/rePhone/es_Retro_Phone_Kit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Kit de Teléfono Retro
44
keywords:
55
- Seeed_Elderly
66
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
7-
slug: /Retro Phone Kit
7+
slug: /Retro_Phone_Kit
88
last_update:
99
date: 1/13/2023
1010
author: shuxu hu

0 commit comments

Comments
 (0)