docs: refresh taskmaster roadmap share
This commit is contained in:
106
taskmaster-share/scripts/refresh-share.mjs
Normal file
106
taskmaster-share/scripts/refresh-share.mjs
Normal file
@@ -0,0 +1,106 @@
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
|
||||
const root = new URL('../../', import.meta.url);
|
||||
const taskmasterPath = new URL('.taskmaster/tasks/tasks.json', root);
|
||||
const shareTasksPath = new URL('taskmaster-share/tasks.json', root);
|
||||
const indexPath = new URL('taskmaster-share/index.html', root);
|
||||
|
||||
const raw = JSON.parse(await readFile(taskmasterPath, 'utf8'));
|
||||
const data = raw.master ?? raw;
|
||||
await writeFile(shareTasksPath, `${JSON.stringify(data, null, 2)}\n`);
|
||||
|
||||
let html = await readFile(indexPath, 'utf8');
|
||||
const tasks = data.tasks ?? [];
|
||||
const subtasks = tasks.flatMap((task) => task.subtasks ?? []);
|
||||
const allItems = [...tasks, ...subtasks];
|
||||
const counts = {
|
||||
parent: tasks.length,
|
||||
subtask: subtasks.length,
|
||||
pending: allItems.filter((item) => item.status !== 'done').length,
|
||||
done: allItems.filter((item) => item.status === 'done').length,
|
||||
};
|
||||
|
||||
const escapeHtml = (value) =>
|
||||
String(value ?? '').replace(/[&<>"']/g, (char) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
})[char]);
|
||||
|
||||
const badge = (value, prefix = '') =>
|
||||
`<span class="badge ${prefix}${escapeHtml(value)}">${escapeHtml(value)}</span>`;
|
||||
|
||||
const formatDeps = (dependencies, parentId) => {
|
||||
if (!dependencies?.length) return '';
|
||||
const values = dependencies.map((dependency) =>
|
||||
String(dependency).includes('.') ? dependency : `${parentId}.${dependency}`
|
||||
);
|
||||
return `<p class="deps">Depends on: ${escapeHtml(values.join(', '))}</p>`;
|
||||
};
|
||||
|
||||
const renderSubtask = (task, subtask) => `
|
||||
<li class="subtask" data-status="${escapeHtml(subtask.status)}" data-priority="${escapeHtml(subtask.priority)}">
|
||||
<div class="subtask-top">
|
||||
<span class="sub-id">${escapeHtml(task.id)}.${escapeHtml(subtask.id)}</span>
|
||||
<strong>${escapeHtml(subtask.title)}</strong>
|
||||
${badge(subtask.status)}
|
||||
${badge(subtask.priority, 'priority-')}
|
||||
</div>
|
||||
<p>${escapeHtml(subtask.description || subtask.details || '')}</p>
|
||||
${formatDeps(subtask.dependencies, task.id)}
|
||||
</li>`;
|
||||
|
||||
const renderTask = (task) => `
|
||||
<article class="task" data-status="${escapeHtml(task.status)}" data-priority="${escapeHtml(task.priority)}">
|
||||
<div class="task-head">
|
||||
<div>
|
||||
<p class="eyebrow">Task ${escapeHtml(task.id)}</p>
|
||||
<h2>${escapeHtml(task.title)}</h2>
|
||||
</div>
|
||||
<div class="badges">${badge(task.status)}${badge(task.priority, 'priority-')}</div>
|
||||
</div>
|
||||
<p class="desc">${escapeHtml(task.description)}</p>
|
||||
<details>
|
||||
<summary>Details and test strategy</summary>
|
||||
<p>${escapeHtml(task.details)}</p>
|
||||
${task.testStrategy ? `<p><strong>Test strategy:</strong> ${escapeHtml(task.testStrategy)}</p>` : ''}
|
||||
${task.dependencies?.length ? `<p><strong>Depends on:</strong> ${escapeHtml(task.dependencies.join(', '))}</p>` : ''}
|
||||
</details>
|
||||
<h3>Subtasks (${(task.subtasks ?? []).length})</h3>
|
||||
<ul class="subtasks">${(task.subtasks ?? []).map((subtask) => renderSubtask(task, subtask)).join('')}
|
||||
</ul>
|
||||
</article>`;
|
||||
|
||||
const taskFive = tasks.find((task) => Number(task.id) === 5);
|
||||
if (!taskFive) {
|
||||
throw new Error('Task 5 is missing from Taskmaster data.');
|
||||
}
|
||||
|
||||
html = html
|
||||
.replace(/<div class="stat"><strong>\d+<\/strong><span>parent tasks<\/span><\/div>/, `<div class="stat"><strong>${counts.parent}</strong><span>parent tasks</span></div>`)
|
||||
.replace(/<div class="stat"><strong>\d+<\/strong><span>subtasks<\/span><\/div>/, `<div class="stat"><strong>${counts.subtask}</strong><span>subtasks</span></div>`)
|
||||
.replace(/<div class="stat"><strong>\d+<\/strong><span>pending items<\/span><\/div>/, `<div class="stat"><strong>${counts.pending}</strong><span>pending items</span></div>`)
|
||||
.replace(/<div class="stat"><strong>\d+<\/strong><span>done items<\/span><\/div>/, `<div class="stat"><strong>${counts.done}</strong><span>done items</span></div>`)
|
||||
.replace(
|
||||
'Track security remediation, payment architecture, refactor decisions, and completed documentation work from one place.',
|
||||
'Track security remediation, payment architecture, Telegram-native work, refactor decisions, and completed documentation work from one place.'
|
||||
);
|
||||
|
||||
const article = renderTask(taskFive);
|
||||
if (html.includes('Task 5</p>')) {
|
||||
html = html.replace(
|
||||
/\n <article class="task" data-status="pending" data-priority="high">\n <div class="task-head">\n <div>\n <p class="eyebrow">Task 5<\/p>[\s\S]*?\n <article class="task" data-status="done" data-priority="medium">/,
|
||||
`\n${article}\n <article class="task" data-status="done" data-priority="medium">`
|
||||
);
|
||||
} else {
|
||||
html = html.replace(
|
||||
'\n <article class="task" data-status="done" data-priority="medium">',
|
||||
`\n${article}\n <article class="task" data-status="done" data-priority="medium">`
|
||||
);
|
||||
}
|
||||
|
||||
await writeFile(indexPath, html);
|
||||
|
||||
console.log(`Updated share page: ${counts.parent} tasks, ${counts.subtask} subtasks.`);
|
||||
Reference in New Issue
Block a user