aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/plugins/updates.ts
blob: 4b383c8ed43d533501880f6a7d1699cf73b8dfa3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { execSync } from "node:child_process";
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";

/**
 * ホームページの「更新情報」をビルド時に生成する。
 *
 * git の履歴から「新しく公開したページ」だけを拾い(--diff-filter=A)、
 * そのページのフロントマターの title と URL を組みにして src/data/updates.json に書き出す。
 * 加筆や内部の修正は拾わないので、一覧が作業ログで埋まらない。
 *
 * 生成結果はコミットしておく(git の履歴がない環境でもビルドできるように)。
 */
export default function updatesIntegration() {
  return {
    name: "build-updates",
    hooks: {
      "astro:config:setup": () => {
        const items: { date: string; title: string; url: string; group: string }[] = [];
        try {
          const out = execSync(
            'git log --diff-filter=A --name-only --date=short --format="%x01%ad" -- src/content/docs',
            { encoding: "utf-8", maxBuffer: 64 * 1024 * 1024 },
          );
          // 新しく追加されたファイル → そのコミットの日付(git log は新しい順なので最初の出現が追加時)
          const added = new Map<string, string>();
          let date = "";
          for (const line of out.split("\n")) {
            if (line.startsWith("\u0001")) {
              date = line.slice(1).trim();
              continue;
            }
            const file = line.trim();
            if (!file.endsWith(".mdx") || file.endsWith("/index.mdx")) continue;
            if (!added.has(file)) added.set(file, date);
          }

          const reiwa = (dir: string) => {
            const n = Number(dir.replace(/^r(\d+)d$/, "$1"));
            return n === 1 ? "令和元年" : `令和${n}年`;
          };

          for (const [file, d] of added) {
            try {
              const src = readFileSync(file, "utf-8");
              // 下書き(draft: true)は本番に出ないので除外する
              const fm = src.match(/^---\r?\n([\s\S]*?)\r?\n---/);
              if (fm && /^draft:\s*true\s*$/m.test(fm[1])) continue;
              const m = src.match(/^title:\s*"?(.+?)"?\s*$/m);
              if (!m) continue;
              const slug = file
                .replace(/^src\/content\/docs\//, "")
                .replace(/\.mdx$/, "");
              const url = `/${slug}/`;
              let group = "";
              const mm = slug.match(/^ippan-situmon\/(r\d+d)\/(\d+)gatu\//);
              if (mm) group = `${reiwa(mm[1])}${mm[2]}月定例会`;
              items.push({ date: d, title: m[1], url, group });
            } catch {
              // 追加後に改名・削除されたファイルは飛ばす
            }
          }
          items.sort((a, b) => b.date.localeCompare(a.date));
        } catch (e) {
          console.warn("[updates] git から取得できませんでした:", (e as Error).message);
        }
        try {
          mkdirSync("src/data", { recursive: true });
          writeFileSync(
            "src/data/updates.json",
            JSON.stringify(items.slice(0, 40), null, 2) + "\n",
          );
          console.log(`[updates] ${items.length}件のうち直近${Math.min(items.length, 40)}件を書き出しました`);
        } catch (e) {
          console.warn("[updates] 書き出しに失敗しました:", (e as Error).message);
        }
      },
    },
  };
}