@backlog-md/core 0.2.2 → 0.3.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.
- package/dist/core/Core.d.ts +130 -1
- package/dist/core/Core.d.ts.map +1 -1
- package/dist/core/Core.js +523 -2
- package/dist/core/Core.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/markdown/index.d.ts +56 -1
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +174 -0
- package/dist/markdown/index.js.map +1 -1
- package/dist/types/index.d.ts +52 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/milestones.d.ts +65 -0
- package/dist/utils/milestones.d.ts.map +1 -0
- package/dist/utils/milestones.js +191 -0
- package/dist/utils/milestones.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone utilities for @backlog-md/core
|
|
3
|
+
*
|
|
4
|
+
* Functions for grouping tasks by milestone and calculating progress.
|
|
5
|
+
*/
|
|
6
|
+
/** Key used for tasks without a milestone */
|
|
7
|
+
const NO_MILESTONE_KEY = "__none";
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a milestone name by trimming whitespace
|
|
10
|
+
*/
|
|
11
|
+
export function normalizeMilestoneName(name) {
|
|
12
|
+
return name.trim();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get a lowercase key for milestone comparison
|
|
16
|
+
*/
|
|
17
|
+
export function milestoneKey(name) {
|
|
18
|
+
return normalizeMilestoneName(name ?? "").toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if a status represents a "done" state
|
|
22
|
+
*/
|
|
23
|
+
export function isDoneStatus(status) {
|
|
24
|
+
const normalized = (status ?? "").toLowerCase();
|
|
25
|
+
return normalized.includes("done") || normalized.includes("complete");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the display label for a milestone
|
|
29
|
+
* Uses the milestone entity title if available, otherwise returns the ID
|
|
30
|
+
*/
|
|
31
|
+
export function getMilestoneLabel(milestoneId, milestoneEntities) {
|
|
32
|
+
if (!milestoneId) {
|
|
33
|
+
return "Tasks without milestone";
|
|
34
|
+
}
|
|
35
|
+
const entity = milestoneEntities.find((m) => milestoneKey(m.id) === milestoneKey(milestoneId));
|
|
36
|
+
return entity?.title || milestoneId;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Collect all unique milestone IDs from tasks and milestone entities
|
|
40
|
+
*/
|
|
41
|
+
export function collectMilestoneIds(tasks, milestoneEntities) {
|
|
42
|
+
const merged = [];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const addMilestone = (value) => {
|
|
45
|
+
const normalized = normalizeMilestoneName(value);
|
|
46
|
+
if (!normalized)
|
|
47
|
+
return;
|
|
48
|
+
const key = milestoneKey(normalized);
|
|
49
|
+
if (seen.has(key))
|
|
50
|
+
return;
|
|
51
|
+
seen.add(key);
|
|
52
|
+
merged.push(normalized);
|
|
53
|
+
};
|
|
54
|
+
// Add milestone entities first (they have priority for ordering)
|
|
55
|
+
for (const entity of milestoneEntities) {
|
|
56
|
+
addMilestone(entity.id);
|
|
57
|
+
}
|
|
58
|
+
// Then add any milestones from tasks that aren't in entities
|
|
59
|
+
for (const task of tasks) {
|
|
60
|
+
addMilestone(task.milestone ?? "");
|
|
61
|
+
}
|
|
62
|
+
return merged;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Collect milestones from tasks and config (legacy support)
|
|
66
|
+
*/
|
|
67
|
+
export function collectMilestones(tasks, configMilestones) {
|
|
68
|
+
const merged = [];
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
const addMilestone = (value) => {
|
|
71
|
+
const normalized = normalizeMilestoneName(value);
|
|
72
|
+
if (!normalized)
|
|
73
|
+
return;
|
|
74
|
+
const key = milestoneKey(normalized);
|
|
75
|
+
if (seen.has(key))
|
|
76
|
+
return;
|
|
77
|
+
seen.add(key);
|
|
78
|
+
merged.push(normalized);
|
|
79
|
+
};
|
|
80
|
+
for (const m of configMilestones) {
|
|
81
|
+
addMilestone(m);
|
|
82
|
+
}
|
|
83
|
+
for (const task of tasks) {
|
|
84
|
+
addMilestone(task.milestone ?? "");
|
|
85
|
+
}
|
|
86
|
+
return merged;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create a milestone bucket for a given milestone
|
|
90
|
+
*/
|
|
91
|
+
function createBucket(milestoneId, tasks, statuses, milestoneEntities, isNoMilestone) {
|
|
92
|
+
const bucketMilestoneKey = milestoneKey(milestoneId);
|
|
93
|
+
const bucketTasks = tasks.filter((task) => {
|
|
94
|
+
const taskMilestoneKey = milestoneKey(task.milestone);
|
|
95
|
+
return bucketMilestoneKey
|
|
96
|
+
? taskMilestoneKey === bucketMilestoneKey
|
|
97
|
+
: !taskMilestoneKey;
|
|
98
|
+
});
|
|
99
|
+
const counts = {};
|
|
100
|
+
for (const status of statuses) {
|
|
101
|
+
counts[status] = 0;
|
|
102
|
+
}
|
|
103
|
+
for (const task of bucketTasks) {
|
|
104
|
+
const status = task.status ?? "";
|
|
105
|
+
counts[status] = (counts[status] ?? 0) + 1;
|
|
106
|
+
}
|
|
107
|
+
const doneCount = bucketTasks.filter((t) => isDoneStatus(t.status)).length;
|
|
108
|
+
const progress = bucketTasks.length > 0
|
|
109
|
+
? Math.round((doneCount / bucketTasks.length) * 100)
|
|
110
|
+
: 0;
|
|
111
|
+
const key = bucketMilestoneKey ? bucketMilestoneKey : NO_MILESTONE_KEY;
|
|
112
|
+
const label = getMilestoneLabel(milestoneId, milestoneEntities);
|
|
113
|
+
return {
|
|
114
|
+
key,
|
|
115
|
+
label,
|
|
116
|
+
milestone: milestoneId,
|
|
117
|
+
isNoMilestone,
|
|
118
|
+
tasks: bucketTasks,
|
|
119
|
+
statusCounts: counts,
|
|
120
|
+
total: bucketTasks.length,
|
|
121
|
+
doneCount,
|
|
122
|
+
progress,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Build milestone buckets from tasks and milestone entities
|
|
127
|
+
*
|
|
128
|
+
* @param tasks - All tasks to group
|
|
129
|
+
* @param milestoneEntities - Milestone entities (for titles)
|
|
130
|
+
* @param statuses - Configured statuses (for status counts)
|
|
131
|
+
* @returns Array of milestone buckets
|
|
132
|
+
*/
|
|
133
|
+
export function buildMilestoneBuckets(tasks, milestoneEntities, statuses) {
|
|
134
|
+
const allMilestoneIds = collectMilestoneIds(tasks, milestoneEntities);
|
|
135
|
+
const buckets = [
|
|
136
|
+
// "No milestone" bucket first
|
|
137
|
+
createBucket(undefined, tasks, statuses, milestoneEntities, true),
|
|
138
|
+
// Then each milestone bucket
|
|
139
|
+
...allMilestoneIds.map((m) => createBucket(m, tasks, statuses, milestoneEntities, false)),
|
|
140
|
+
];
|
|
141
|
+
return buckets;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build milestone buckets using config milestone strings (legacy support)
|
|
145
|
+
*
|
|
146
|
+
* @deprecated Use buildMilestoneBuckets with Milestone entities instead
|
|
147
|
+
*/
|
|
148
|
+
export function buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses) {
|
|
149
|
+
// Convert config milestone strings to minimal Milestone entities
|
|
150
|
+
const milestoneEntities = configMilestones.map((id) => ({
|
|
151
|
+
id,
|
|
152
|
+
title: id,
|
|
153
|
+
description: "",
|
|
154
|
+
rawContent: "",
|
|
155
|
+
tasks: [],
|
|
156
|
+
}));
|
|
157
|
+
return buildMilestoneBuckets(tasks, milestoneEntities, statuses);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Build a complete milestone summary
|
|
161
|
+
*
|
|
162
|
+
* @param tasks - All tasks
|
|
163
|
+
* @param milestoneEntities - Milestone entities
|
|
164
|
+
* @param statuses - Configured statuses
|
|
165
|
+
* @returns MilestoneSummary with milestones and buckets
|
|
166
|
+
*/
|
|
167
|
+
export function buildMilestoneSummary(tasks, milestoneEntities, statuses) {
|
|
168
|
+
const milestones = collectMilestoneIds(tasks, milestoneEntities);
|
|
169
|
+
const buckets = buildMilestoneBuckets(tasks, milestoneEntities, statuses);
|
|
170
|
+
return {
|
|
171
|
+
milestones,
|
|
172
|
+
buckets,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Group tasks by milestone (simple version using config milestones)
|
|
177
|
+
*
|
|
178
|
+
* @param tasks - Tasks to group
|
|
179
|
+
* @param configMilestones - Milestones from config
|
|
180
|
+
* @param statuses - Configured statuses
|
|
181
|
+
* @returns MilestoneSummary
|
|
182
|
+
*/
|
|
183
|
+
export function groupTasksByMilestone(tasks, configMilestones, statuses) {
|
|
184
|
+
const milestones = collectMilestones(tasks, configMilestones);
|
|
185
|
+
const buckets = buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses);
|
|
186
|
+
return {
|
|
187
|
+
milestones,
|
|
188
|
+
buckets,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=milestones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"milestones.js","sourceRoot":"","sources":["../../src/utils/milestones.ts"],"names":[],"mappings":"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"}
|