aizuchi 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * task frontmatter の 1 フィールドだけを byte 保全で書き換える純関数。
3
+ * setRelatedDocsInRaw (related-docs.ts) と同じ行差し替え方式を採用する。
4
+ *
5
+ * - setDependsOnInRaw: frontmatter の depends_on 配列だけを置換/追加する
6
+ * - setPriorityInRaw: frontmatter の priority スカラだけを置換/追加する
7
+ *
8
+ * 本文・他フィールド・改行コード・コメントは完全に保全する。
9
+ * frontmatter が見つからない raw はそのまま返す。
10
+ */
11
+ // frontmatter 境界: 先頭の `---` 行 (行末空白を許容、parseTaskFile / setRelatedDocsInRaw と同じ規約)
12
+ const FM_OPEN = /^---[ \t]*\r?\n/;
13
+ // 閉じ `---` 行 (行末空白許容)
14
+ const FM_CLOSE = /^---[ \t]*$/;
15
+ // BOM は literal 禁止 (LEARN#13: invisible unicode は \uXXXX escape で書く)
16
+ const BOM = '\uFEFF';
17
+ // ---------------------------------------------------------------------------
18
+ // 内部ユーティリティ
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * raw から BOM を剥がし、処理後に復元するラッパー。
22
+ * setRelatedDocsInRaw と同じ規約 (R13-1)。
23
+ */
24
+ function withBomHandling(raw, fn) {
25
+ const hasBom = raw.startsWith(BOM);
26
+ const body = hasBom ? raw.slice(1) : raw;
27
+ const result = fn(body);
28
+ return hasBom ? BOM + result : result;
29
+ }
30
+ /**
31
+ * frontmatter 内の指定 key の行 (flow 1 行 or block 形式 key+続く`- `行) を除去し、
32
+ * フロントマターの行配列 (開き`---`を除く、閉じ`---`を除く範囲) を返す。
33
+ * また閉じ `---` のインデックスと全行配列も返す。
34
+ */
35
+ function parseFrontmatterLines(raw) {
36
+ if (!FM_OPEN.test(raw))
37
+ return null;
38
+ const lines = raw.split('\n');
39
+ let closeIdx = -1;
40
+ for (let i = 1; i < lines.length; i++) {
41
+ if (FM_CLOSE.test(lines[i].replace(/\r$/, ''))) {
42
+ closeIdx = i;
43
+ break;
44
+ }
45
+ }
46
+ if (closeIdx === -1)
47
+ return null;
48
+ return { lines, closeIdx };
49
+ }
50
+ /**
51
+ * frontmatter 内の指定 key の行 (flow or block) を除去した行配列を返す。
52
+ * kept は開き `---` の次から closeIdx の手前までの内容 (閉じ `---` は含まない)。
53
+ */
54
+ function removeKeyLines(lines, closeIdx, key) {
55
+ const keyPattern = new RegExp(`^${key}\\s*:`);
56
+ const kept = [];
57
+ let i = 1;
58
+ while (i < closeIdx) {
59
+ const line = lines[i].replace(/\r$/, '');
60
+ if (keyPattern.test(line)) {
61
+ i++;
62
+ // block 形式: 続く `- ` 行も取り除く (ゼロインデントも対象。setRelatedDocsInRaw R13-4 と同方式)
63
+ while (i < closeIdx && /^\s*-\s/.test(lines[i].replace(/\r$/, '')))
64
+ i++;
65
+ continue;
66
+ }
67
+ kept.push(lines[i]);
68
+ i++;
69
+ }
70
+ return kept;
71
+ }
72
+ /**
73
+ * `kept` 行配列にフィールドを挿入し、raw 全体を再組み立てして返す。
74
+ * 挿入位置: created: 行の直前、なければ末尾 (setRelatedDocsInRaw と同じ規約)。
75
+ * newLine が null の場合は挿入しない (フィールド除去のみ)。
76
+ */
77
+ function rebuildRaw(lines, closeIdx, kept, newLine) {
78
+ if (newLine !== null) {
79
+ const createdIdx = kept.findIndex((l) => /^created\s*:/.test(l.replace(/\r$/, '')));
80
+ if (createdIdx !== -1) {
81
+ kept.splice(createdIdx, 0, newLine);
82
+ }
83
+ else {
84
+ kept.push(newLine);
85
+ }
86
+ }
87
+ const head = [lines[0], ...kept].join('\n');
88
+ const tail = lines.slice(closeIdx).join('\n');
89
+ return `${head}\n${tail}`;
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // 公開 API
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * raw (frontmatter + 本文) の depends_on 行を ids で差し替えた raw を返す。
96
+ * - ids が空: depends_on 行 (flow 1 行 or block 形式) を除去する
97
+ * - ids が非空: JSON.stringify 済み flow 形式 1 行
98
+ * (`depends_on: ["TASK-001", "TASK-002"]`) に置換/挿入する。
99
+ * setRelatedDocsInRaw と同じ表現スタイルに統一する (仕様「配列表現は existing に合わせる」)。
100
+ * - frontmatter が無い raw はそのまま返す
101
+ */
102
+ export function setDependsOnInRaw(raw, ids) {
103
+ return withBomHandling(raw, (body) => setDependsOnInner(body, ids));
104
+ }
105
+ function setDependsOnInner(raw, ids) {
106
+ const parsed = parseFrontmatterLines(raw);
107
+ if (!parsed)
108
+ return raw;
109
+ const { lines, closeIdx } = parsed;
110
+ const kept = removeKeyLines(lines, closeIdx, 'depends_on');
111
+ // setRelatedDocsInRaw と同じ JSON.stringify flow 形式 (YAML double-quoted として常に valid)
112
+ const newLine = ids.length > 0 ? `depends_on: [${ids.map((id) => JSON.stringify(id)).join(', ')}]` : null;
113
+ return rebuildRaw(lines, closeIdx, kept, newLine);
114
+ }
115
+ /**
116
+ * raw (frontmatter + 本文) の priority 行を priority で差し替えた raw を返す。
117
+ * - フィールドが無ければ created: 行の直前 (無ければ閉じ `---` の直前) に追加する
118
+ * - フィールドが既にあれば値だけ置換する
119
+ * - frontmatter が無い raw はそのまま返す
120
+ */
121
+ export function setPriorityInRaw(raw, priority) {
122
+ return withBomHandling(raw, (body) => setPriorityInner(body, priority));
123
+ }
124
+ function setPriorityInner(raw, priority) {
125
+ const parsed = parseFrontmatterLines(raw);
126
+ if (!parsed)
127
+ return raw;
128
+ const { lines, closeIdx } = parsed;
129
+ const kept = removeKeyLines(lines, closeIdx, 'priority');
130
+ const newLine = `priority: ${priority}`;
131
+ return rebuildRaw(lines, closeIdx, kept, newLine);
132
+ }