Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"generatewiki": "node scripts/generateWiki.js",
"generate-lang-map": "node scripts/generate-language-map.js"
"generate-lang-map": "node scripts/generate-language-map.js",
"normalize-frontmatter": "node scripts/normalize-frontmatter.js"

},
"dependencies": {
"@ant-design/icons": "^5.4.0",
Expand Down Expand Up @@ -72,8 +74,11 @@
"baseline-browser-mapping": "^2.9.14",
"concurrently": "^9.2.1",
"cross-env": "^10.1.0",
"fast-glob": "^3.3.3",
"gh-pages": "^6.3.0",
"glob": "^11.0.2",
"gray-matter": "^4.0.3",
"js-yaml": "^4.1.1",
"typescript": "^4.7.4"
},
"browserslist": {
Expand Down
170 changes: 170 additions & 0 deletions scripts/normalize-frontmatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/usr/bin/env node
/* scripts/normalize-frontmatter.js */
const fs = require("fs");
const path = require("path");
const matter = require("gray-matter");
const fg = require("fast-glob");
const yaml = require("js-yaml");
const { execSync } = require("child_process");

const ROOT = process.cwd();
const BASE_URL = "https://wiki.seeedstudio.com";

const LANG_RULES = [
{ dir: path.join("sites", "en", "docs"), prefix: "" },
{ dir: path.join("sites", "zh-CN", "docs"), prefix: "/cn" },
{ dir: path.join("sites", "ja", "docs"), prefix: "/ja" },
{ dir: path.join("sites", "es", "docs"), prefix: "/es" },
];

function detectLangPrefix(fileAbsPath) {
const rel = path.relative(ROOT, fileAbsPath).split(path.sep).join("/");
for (const r of LANG_RULES) {
const dirNorm = r.dir.split(path.sep).join("/");
if (rel.startsWith(dirNorm + "/") || rel === dirNorm) return r.prefix;
}
return null;
}

function runGit(cmd) {
try {
return execSync(cmd, { cwd: ROOT, stdio: ["ignore", "pipe", "ignore"] })
.toString("utf8")
.trim();
} catch {
return "";
}
}

function getGitTimes(fileAbsPath) {
// 最新提交时间
const latest = runGit(`git log -1 --format=%aI -- "${fileAbsPath}"`);
// 最早提交时间(文件首次出现)
// --follow 对重命名更友好;--diff-filter=A 获取“新增该文件”的提交
// 若历史中有重命名导致 A 取不到,则 fallback 到 reverse 的第一条
let first = runGit(
`git log --follow --diff-filter=A --format=%aI -1 -- "${fileAbsPath}"`
);

if (!first) {
const all = runGit(`git log --follow --format=%aI --reverse -- "${fileAbsPath}"`);
first = all ? all.split(/\r?\n/)[0].trim() : "";
}

return { createdAt: first, updatedAt: latest };
}

function toYMD(iso) {
if (!iso || typeof iso !== "string") return "";
// git 的 %aI 格式是 ISO 8601,前 10 位就是 YYYY-MM-DD
return iso.slice(0, 10);
}

function normalizeSlug(slug) {
if (!slug || typeof slug !== "string") return "";
let s = slug.trim();
if (!s.startsWith("/")) s = "/" + s;

// ✅ 空白(空格/tab/多空白)=> 下划线
s = s.replace(/\s+/g, "_");

// ✅ 可选:避免出现多个连续下划线
s = s.replace(/_+/g, "_");

return s;
}

function buildUrl(prefix, slug) {
const p = prefix ? (prefix.startsWith("/") ? prefix : "/" + prefix) : "";
const s = normalizeSlug(slug);
if (!s) return "";
let u = `${BASE_URL}${p}${s}`;
if (!u.endsWith("/")) u += "/";
return u;
}

function dumpFrontmatter(data) {
// 不排序:尽量保留你原本 key 的相对习惯(js-yaml 默认不保证顺序,但多数情况下保持插入顺序)
// noRefs: 避免出现 &a *a 之类引用
return yaml.dump(data, {
noRefs: true,
lineWidth: 1000,
}).trimEnd();
}

function normalizeFile(fileAbsPath, { checkOnly = false } = {}) {
const raw = fs.readFileSync(fileAbsPath, "utf8");
const parsed = matter(raw);

const data = parsed.data || {};
const content = parsed.content || "";

const prefix = detectLangPrefix(fileAbsPath);
// 你给的语言目录里应该都能匹配上;匹配不上就跳过 url 生成
const slug = normalizeSlug(data.slug);

const gitTimes = getGitTimes(fileAbsPath);

// createdAt:优先用已有值(如果你之前手工写过),否则用 git 最早提交时间
// 如果已有值为空/无效,再补
if (!data.createdAt && gitTimes.createdAt) data.createdAt = toYMD(gitTimes.createdAt);
// updatedAt:总是更新为 git 最新提交时间(若取不到则不动)
if (gitTimes.updatedAt) data.updatedAt = toYMD(gitTimes.updatedAt);

// url:只要能识别语言 + slug,就生成/更新
if (prefix !== null && slug) {
data.url = buildUrl(prefix, slug);
// 同时把规范化后的 slug 写回(可选,但通常是你想要的)
data.slug = slug;
}

const newFm = dumpFrontmatter(data);
const newRaw = `---\n${newFm}\n---\n${content.replace(/^\n+/, "")}`;

const changed = newRaw !== raw;

if (changed) {
if (!checkOnly) fs.writeFileSync(fileAbsPath, newRaw, "utf8");
}
return { changed, fileAbsPath };
}

async function main() {
const args = process.argv.slice(2);
const checkOnly = args.includes("--check");

const patterns = [
"sites/en/docs/**/*.md",
"sites/en/docs/**/*.mdx",
"sites/zh-CN/docs/**/*.md",
"sites/zh-CN/docs/**/*.mdx",
"sites/ja/docs/**/*.md",
"sites/ja/docs/**/*.mdx",
"sites/es/docs/**/*.md",
"sites/es/docs/**/*.mdx",
];

const files = await fg(patterns, { absolute: true, dot: false });
let changedCount = 0;

for (const f of files) {
const r = normalizeFile(f, { checkOnly });
if (r.changed) changedCount++;
}

if (checkOnly) {
if (changedCount > 0) {
console.log(`[CHECK] ${changedCount} files would be modified.`);
process.exitCode = 1;
} else {
console.log("[CHECK] All good. No changes needed.");
}
} else {
console.log(`[DONE] Updated ${changedCount} files.`);
}
}

main().catch((e) => {
console.error(e);
process.exit(2);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords:
- reComputer
- Jetson
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Real Time Subtitle Recoder on Nvidia Jetson
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
last_update:
date: 02/23/2024
author: Jiahao
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Grove Maker Kit for Intel Joule
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Grove Maker Kit for Intel Joule
slug: /Grove_Maker_Kit_for_Intel_Joule
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: IoT Fast Prototyping Kit S5D9
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /IoT_Fast_Prototyping_Kit S5D9
slug: /IoT_Fast_Prototyping_Kit_S5D9
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
2 changes: 1 addition & 1 deletion sites/en/docs/Seeed_Elderly/rePhone/Retro_Phone_Kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Retro Phone Kit
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Retro Phone Kit
slug: /Retro_Phone_Kit
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords:
- reComputer
- Jetson
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Real Time Subtitle Recoder on Nvidia Jetson
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
last_update:
date: 02/23/2024
author: Jiahao
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Grove Maker Kit para Intel Joule
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Grove Maker Kit for Intel Joule
slug: /Grove_Maker_Kit_for_Intel_Joule
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Kit de Prototipado Rápido IoT S5D9
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /IoT_Fast_Prototyping_Kit S5D9
slug: /IoT_Fast_Prototyping_Kit_S5D9
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
2 changes: 1 addition & 1 deletion sites/es/docs/Seeed_Elderly/rePhone/es_Retro_Phone_Kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Kit de Teléfono Retro
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Retro Phone Kit
slug: /Retro_Phone_Kit
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords:
- reComputer
- Jetson
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Real Time Subtitle Recoder on Nvidia Jetson
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
last_update:
date: 02/23/2024
author: Jiahao
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Intel Joule用Grove Maker Kit
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Grove Maker Kit for Intel Joule
slug: /Grove_Maker_Kit_for_Intel_Joule
last_update:
date: 05/15/2025
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: IoT高速プロトタイピングキット S5D9
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /IoT_Fast_Prototyping_Kit S5D9
slug: /IoT_Fast_Prototyping_Kit_S5D9
last_update:
date: 05/15/2025
author: shuxu hu
Expand Down
2 changes: 1 addition & 1 deletion sites/ja/docs/Seeed_Elderly/rePhone/ja_Retro_Phone_Kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: レトロフォンキット
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Retro Phone Kit
slug: /Retro_Phone_Kit
last_update:
date: 05/15/2025
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords:
- reComputer
- Jetson
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Real Time Subtitle Recoder on Nvidia Jetson
slug: /Real_Time_Subtitle_Recoder_on_Nvidia_Jetson
last_update:
date: 02/23/2024
author: Jiahao
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Grove 制造者套件适用于 Intel Joule
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Grove Maker Kit for Intel Joule
slug: /Grove_Maker_Kit_for_Intel_Joule
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: IoT快速原型开发套件S5D9
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /IoT_Fast_Prototyping_Kit S5D9
slug: /IoT_Fast_Prototyping_Kit_S5D9
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: 复古电话套件
keywords:
- Seeed_Elderly
image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png
slug: /Retro Phone Kit
slug: /Retro_Phone_Kit
last_update:
date: 1/13/2023
author: shuxu hu
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5460,7 +5460,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==

fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0:
fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
Expand Down Expand Up @@ -6634,7 +6634,7 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"

js-yaml@^4.1.0:
js-yaml@^4.1.0, js-yaml@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
Expand Down
Loading