tospudo 1.0.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/LICENSE +15 -0
- package/README.md +78 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +207 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -0
- package/dist/output.d.ts +3 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +40 -0
- package/dist/output.js.map +1 -0
- package/dist/parser.d.ts +9 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +93 -0
- package/dist/parser.js.map +1 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +40 -0
- package/dist/scanner.js.map +1 -0
- package/dist/todo-md.d.ts +29 -0
- package/dist/todo-md.d.ts.map +1 -0
- package/dist/todo-md.js +147 -0
- package/dist/todo-md.js.map +1 -0
- package/package.json +52 -0
- package/schema.json +29 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hayrdenhald
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# 🍠 Tospudo
|
|
2
|
+
|
|
3
|
+
A tool to manage TODOs in your project.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D tospudo
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Add it to your `package.json` scripts:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"scripts": {
|
|
16
|
+
"todo": "tospudo"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm todo
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Tospudo scans all files from the current directory, respecting `.gitignore`.
|
|
28
|
+
|
|
29
|
+
It reports every `TODO` and `FIXME` comment found in code, plus unchecked items (`- [ ]`) in a `TODO.md` file.
|
|
30
|
+
|
|
31
|
+
## CLI options
|
|
32
|
+
|
|
33
|
+
- `--max <number>` — exit with code 1 if the TODO count exceeds this number (useful in CI)
|
|
34
|
+
- `--help`
|
|
35
|
+
- `--version`
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
Configuration is optional. Use a `tospudo.config.json` file:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"$schema": "./node_modules/tospudo/schema.json",
|
|
44
|
+
"ignore": ["src/generated/**"],
|
|
45
|
+
"max": 20
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `$schema` field enables autocomplete and inline documentation for all options in VS Code and other editors that support JSON Schema.
|
|
50
|
+
|
|
51
|
+
Or add a `tospudo` key to `package.json`:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"tospudo": {
|
|
56
|
+
"ignore": ["src/generated/**"],
|
|
57
|
+
"max": 20
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Options:**
|
|
63
|
+
|
|
64
|
+
- `ignore` — glob patterns to exclude from scanning, in addition to `.gitignore` rules
|
|
65
|
+
- `max` — exit with code 1 if TODO count exceeds this threshold; useful in CI. Can also be set with the `--max` CLI flag
|
|
66
|
+
- `maxLength` — truncate TODO text in scan output to this many characters (default: `80`); does not affect `TODO.md` content
|
|
67
|
+
- `sectionEmojis` — show an emoji prefix in `TODO.md` section headings, e.g. `🐛 fix` (default: `true`)
|
|
68
|
+
|
|
69
|
+
<br>
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
<br>
|
|
74
|
+
<br>
|
|
75
|
+
<br>
|
|
76
|
+
<div align="center">
|
|
77
|
+
<img src="logo.svg" width="200px">
|
|
78
|
+
</div>
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import cac from "cac";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
import pkg from "../package.json" with { type: "json" };
|
|
9
|
+
import { loadConfig } from "./config.js";
|
|
10
|
+
import { printResults } from "./output.js";
|
|
11
|
+
import { scanFiles } from "./scanner.js";
|
|
12
|
+
import { ANY_ITEM_REGEX, appendTodo, CHECKED_REGEX, completeLine, deleteLine, hasSections, migrateToSections, parseAllItems, parseAllItemsWithStatus, parseCompletedLines, parseLines, pruneEmptySections, SECTIONS, uncompleteLine, } from "./todo-md.js";
|
|
13
|
+
const TODO_PATH = join(process.cwd(), "TODO.md");
|
|
14
|
+
function readTodoMd() {
|
|
15
|
+
return readFile(TODO_PATH, "utf8");
|
|
16
|
+
}
|
|
17
|
+
async function runScan(options) {
|
|
18
|
+
const config = await loadConfig();
|
|
19
|
+
const max = options?.max !== undefined ? Number(options.max) : config?.max;
|
|
20
|
+
const todos = await scanFiles(config?.ignore ?? []);
|
|
21
|
+
printResults(todos, max, config?.maxLength);
|
|
22
|
+
if (max !== undefined && todos.length > max) {
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function runList() {
|
|
27
|
+
if (!existsSync(TODO_PATH)) {
|
|
28
|
+
p.cancel("No TODO.md found in current directory.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const content = await readTodoMd();
|
|
32
|
+
const items = parseAllItemsWithStatus(content);
|
|
33
|
+
if (items.length === 0) {
|
|
34
|
+
console.log(pc.green("No TODOs found in TODO.md."));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(pc.bold(`\nTODO.md (${items.length} item${items.length === 1 ? "" : "s"}):`));
|
|
38
|
+
for (const line of content.split("\n")) {
|
|
39
|
+
const sectionMatch = /^## (.+)$/.exec(line);
|
|
40
|
+
const itemMatch = ANY_ITEM_REGEX.exec(line);
|
|
41
|
+
if (sectionMatch) {
|
|
42
|
+
console.log(pc.bold(`\n ${sectionMatch[1]}`));
|
|
43
|
+
}
|
|
44
|
+
else if (itemMatch) {
|
|
45
|
+
const checked = CHECKED_REGEX.test(line);
|
|
46
|
+
const checkbox = checked ? pc.green("[x]") : pc.dim("[ ]");
|
|
47
|
+
const text = checked ? pc.dim(itemMatch[1].trim()) : itemMatch[1].trim();
|
|
48
|
+
console.log(` ${checkbox} ${text}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
console.log("");
|
|
52
|
+
}
|
|
53
|
+
const config = await loadConfig();
|
|
54
|
+
const allTodos = await scanFiles(config?.ignore ?? []);
|
|
55
|
+
const codeTodos = allTodos.filter((t) => t.file !== "TODO.md" && !t.file.endsWith("/TODO.md"));
|
|
56
|
+
if (codeTodos.length > 0) {
|
|
57
|
+
const n = codeTodos.length;
|
|
58
|
+
console.log(pc.dim(` ... ${items.length === 0 ? "but" : "and"} ${n} TODO${n === 1 ? "" : "s"} spread around the rest of the codebase.`));
|
|
59
|
+
console.log("");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function parseSectionPrefix(text) {
|
|
63
|
+
const match = /^(\w+):\s*(.+)$/.exec(text);
|
|
64
|
+
if (!match)
|
|
65
|
+
return null;
|
|
66
|
+
const candidate = match[1];
|
|
67
|
+
if (!SECTIONS.includes(candidate))
|
|
68
|
+
return null;
|
|
69
|
+
return { section: candidate, todoText: match[2].trim() };
|
|
70
|
+
}
|
|
71
|
+
async function runAdd(text) {
|
|
72
|
+
const config = await loadConfig();
|
|
73
|
+
const emoji = config?.sectionEmojis !== false;
|
|
74
|
+
let existing = existsSync(TODO_PATH) ? await readTodoMd() : "";
|
|
75
|
+
if (existing.length > 0 && !hasSections(existing)) {
|
|
76
|
+
existing = migrateToSections(existing, emoji);
|
|
77
|
+
}
|
|
78
|
+
let todoText = text?.trim();
|
|
79
|
+
if (!todoText) {
|
|
80
|
+
const input = await p.text({ message: "What needs to be done?" });
|
|
81
|
+
if (p.isCancel(input))
|
|
82
|
+
process.exit(0);
|
|
83
|
+
todoText = input.trim();
|
|
84
|
+
if (!todoText) {
|
|
85
|
+
p.cancel("TODO text cannot be empty.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const parsed = parseSectionPrefix(todoText);
|
|
90
|
+
let section;
|
|
91
|
+
if (parsed) {
|
|
92
|
+
section = parsed.section;
|
|
93
|
+
todoText = parsed.todoText;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const selected = await p.select({
|
|
97
|
+
message: "Which section?",
|
|
98
|
+
options: SECTIONS.map((s) => ({ value: s, label: s })),
|
|
99
|
+
});
|
|
100
|
+
if (p.isCancel(selected))
|
|
101
|
+
process.exit(0);
|
|
102
|
+
section = selected;
|
|
103
|
+
}
|
|
104
|
+
await writeFile(TODO_PATH, appendTodo(existing, todoText, section, emoji), "utf8");
|
|
105
|
+
p.outro(`Added: ${todoText}`);
|
|
106
|
+
}
|
|
107
|
+
async function runCheck() {
|
|
108
|
+
if (!existsSync(TODO_PATH)) {
|
|
109
|
+
p.cancel("No TODO.md found in current directory.");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const content = await readTodoMd();
|
|
113
|
+
const items = parseLines(content);
|
|
114
|
+
if (items.length === 0) {
|
|
115
|
+
p.cancel("No unchecked TODOs found.");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const selected = await p.autocomplete({
|
|
119
|
+
message: "Select a TODO to check off:",
|
|
120
|
+
options: items.map((item) => ({ label: item.text, value: item.index })),
|
|
121
|
+
});
|
|
122
|
+
if (p.isCancel(selected))
|
|
123
|
+
process.exit(0);
|
|
124
|
+
await writeFile(TODO_PATH, completeLine(content, selected), "utf8");
|
|
125
|
+
p.outro(`Checked: ${items.find((i) => i.index === selected)?.text}`);
|
|
126
|
+
}
|
|
127
|
+
async function runUncheck() {
|
|
128
|
+
if (!existsSync(TODO_PATH)) {
|
|
129
|
+
p.cancel("No TODO.md found in current directory.");
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
const content = await readTodoMd();
|
|
133
|
+
const items = parseCompletedLines(content);
|
|
134
|
+
if (items.length === 0) {
|
|
135
|
+
p.cancel("No checked TODOs found.");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
const selected = await p.autocomplete({
|
|
139
|
+
message: "Select a TODO to uncheck:",
|
|
140
|
+
options: items.map((item) => ({ label: item.text, value: item.index })),
|
|
141
|
+
});
|
|
142
|
+
if (p.isCancel(selected))
|
|
143
|
+
process.exit(0);
|
|
144
|
+
await writeFile(TODO_PATH, uncompleteLine(content, selected), "utf8");
|
|
145
|
+
p.outro(`Unchecked: ${items.find((i) => i.index === selected)?.text}`);
|
|
146
|
+
}
|
|
147
|
+
async function runRemove() {
|
|
148
|
+
if (!existsSync(TODO_PATH)) {
|
|
149
|
+
p.cancel("No TODO.md found in current directory.");
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const content = await readTodoMd();
|
|
153
|
+
const items = parseAllItems(content);
|
|
154
|
+
if (items.length === 0) {
|
|
155
|
+
p.cancel("No TODOs found.");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const selected = await p.autocomplete({
|
|
159
|
+
message: "Select a TODO to remove:",
|
|
160
|
+
options: items.map((item) => ({ label: item.text, value: item.index })),
|
|
161
|
+
});
|
|
162
|
+
if (p.isCancel(selected))
|
|
163
|
+
process.exit(0);
|
|
164
|
+
await writeFile(TODO_PATH, pruneEmptySections(deleteLine(content, selected)), "utf8");
|
|
165
|
+
p.outro(`Removed: ${items.find((i) => i.index === selected)?.text}`);
|
|
166
|
+
}
|
|
167
|
+
const cli = cac("tospudo");
|
|
168
|
+
cli
|
|
169
|
+
.command("scan", "Scan for TODOs across the codebase")
|
|
170
|
+
.option("--max <number>", "Maximum number of TODOs before failing")
|
|
171
|
+
.action(runScan);
|
|
172
|
+
cli.command("list", "List TODOs in TODO.md").action(runList);
|
|
173
|
+
cli.command("add [text]", "Add a new TODO to TODO.md").action(runAdd);
|
|
174
|
+
cli.command("check", "Check off a TODO").action(runCheck);
|
|
175
|
+
cli.command("uncheck", "Uncheck a completed TODO").action(runUncheck);
|
|
176
|
+
cli.command("remove", "Remove a TODO").action(runRemove);
|
|
177
|
+
cli.command("", "Interactive menu").action(async () => {
|
|
178
|
+
const action = await p.select({
|
|
179
|
+
message: "What would you like to do?",
|
|
180
|
+
options: [
|
|
181
|
+
{ value: "scan", label: "Scan codebase for TODOs" },
|
|
182
|
+
{ value: "list", label: "List TODOs in TODO.md" },
|
|
183
|
+
{ value: "add", label: "Add a TODO to TODO.md" },
|
|
184
|
+
{ value: "check", label: "Check off a TODO" },
|
|
185
|
+
{ value: "uncheck", label: "Uncheck a TODO" },
|
|
186
|
+
{ value: "remove", label: "Remove a TODO" },
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
if (p.isCancel(action))
|
|
190
|
+
process.exit(0);
|
|
191
|
+
if (action === "scan")
|
|
192
|
+
await runScan();
|
|
193
|
+
if (action === "list")
|
|
194
|
+
await runList();
|
|
195
|
+
if (action === "add")
|
|
196
|
+
await runAdd();
|
|
197
|
+
if (action === "check")
|
|
198
|
+
await runCheck();
|
|
199
|
+
if (action === "uncheck")
|
|
200
|
+
await runUncheck();
|
|
201
|
+
if (action === "remove")
|
|
202
|
+
await runRemove();
|
|
203
|
+
});
|
|
204
|
+
cli.help();
|
|
205
|
+
cli.version(pkg.version);
|
|
206
|
+
cli.parse();
|
|
207
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
ignore: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
4
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
sectionEmojis: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
type TospudoConfig = z.infer<typeof ConfigSchema>;
|
|
9
|
+
export declare function loadConfig(): Promise<TospudoConfig | null>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,YAAY;;;;;iBAKvB,CAAC;AAEH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAWlD,wBAAsB,UAAU,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAQhE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const ConfigSchema = z.object({
|
|
4
|
+
ignore: z.array(z.string()).optional(),
|
|
5
|
+
max: z.number().int().positive().optional(),
|
|
6
|
+
maxLength: z.number().int().positive().optional(),
|
|
7
|
+
sectionEmojis: z.boolean().optional(),
|
|
8
|
+
});
|
|
9
|
+
const explorer = cosmiconfig("tospudo", {
|
|
10
|
+
searchPlaces: [
|
|
11
|
+
"package.json",
|
|
12
|
+
"tospudo.config.json",
|
|
13
|
+
"tospudo.config.yaml",
|
|
14
|
+
"tospudo.config.yml",
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
export async function loadConfig() {
|
|
18
|
+
const result = await explorer.search();
|
|
19
|
+
if (!result || result.isEmpty)
|
|
20
|
+
return null;
|
|
21
|
+
const parsed = ConfigSchema.safeParse(result.config);
|
|
22
|
+
if (!parsed.success) {
|
|
23
|
+
throw new Error(`Invalid tospudo config: ${parsed.error.message}`);
|
|
24
|
+
}
|
|
25
|
+
return parsed.data;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC3C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAIH,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,EAAE;IACtC,YAAY,EAAE;QACZ,cAAc;QACd,qBAAqB;QACrB,qBAAqB;QACrB,oBAAoB;KACrB;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;IACvC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAe5C,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAgCtF"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
const DEFAULT_MAX_LENGTH = 80;
|
|
3
|
+
function formatLocation(item) {
|
|
4
|
+
if (item.line !== undefined) {
|
|
5
|
+
return `${item.file}:${item.line}`;
|
|
6
|
+
}
|
|
7
|
+
return item.file;
|
|
8
|
+
}
|
|
9
|
+
function truncate(text, limit) {
|
|
10
|
+
return text.length > limit ? `${text.slice(0, limit)} …` : text;
|
|
11
|
+
}
|
|
12
|
+
export function printResults(todos, max, maxLength) {
|
|
13
|
+
const limit = maxLength ?? DEFAULT_MAX_LENGTH;
|
|
14
|
+
const count = todos.length;
|
|
15
|
+
if (count === 0) {
|
|
16
|
+
console.log(pc.green("No TODOs found."));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.log(pc.bold(`Found ${count} TODO${count === 1 ? "" : "s"}:\n`));
|
|
20
|
+
const maxTextLength = Math.max(...todos.map((t) => truncate(t.text, limit).length + (t.hasMore ? 2 : 0)));
|
|
21
|
+
for (const item of todos) {
|
|
22
|
+
const location = formatLocation(item);
|
|
23
|
+
const display = truncate(item.text, limit);
|
|
24
|
+
const more = item.hasMore ? pc.dim(" …") : "";
|
|
25
|
+
const textWidth = display.length + (item.hasMore ? 2 : 0);
|
|
26
|
+
const padding = " ".repeat(Math.max(0, maxTextLength - textWidth + 4));
|
|
27
|
+
console.log(` ${display}${more}${padding}${pc.cyan(location)}`);
|
|
28
|
+
}
|
|
29
|
+
console.log("");
|
|
30
|
+
}
|
|
31
|
+
if (max !== undefined) {
|
|
32
|
+
if (count > max) {
|
|
33
|
+
console.log(pc.red(`Threshold: ${max} — EXCEEDED (${count}/${max}) ✗`));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(pc.green(`Threshold: ${max} — OK ✓`));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,SAAS,cAAc,CAAC,IAAc;IACpC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa;IAC3C,OAAO,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAE,GAAY,EAAE,SAAkB;IAC9E,MAAM,KAAK,GAAG,SAAS,IAAI,kBAAkB,CAAC;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAE3B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;QAExE,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAC5B,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC1E,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,cAAc,GAAG,gBAAgB,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;QAC1E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface TodoItem {
|
|
2
|
+
file: string;
|
|
3
|
+
line?: number;
|
|
4
|
+
text: string;
|
|
5
|
+
hasMore?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseFileForTodos(content: string, file: string): TodoItem[];
|
|
8
|
+
export declare function parseTodoMd(content: string, file: string): TodoItem[];
|
|
9
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA+DD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA+B3E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAerE"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const TODO_REGEX = /(?:(?:TODO|FIXME):|^\s*\*?\s*(?:TODO|FIXME)\b)/im;
|
|
2
|
+
const CHECKBOX_REGEX = /^- \[ \] (.+)$/;
|
|
3
|
+
function extractCommentContent(line, inBlockComment, inHtmlComment) {
|
|
4
|
+
let result = "";
|
|
5
|
+
let pos = 0;
|
|
6
|
+
while (pos < line.length) {
|
|
7
|
+
if (inHtmlComment) {
|
|
8
|
+
const close = line.indexOf("-->", pos);
|
|
9
|
+
if (close === -1) {
|
|
10
|
+
result += line.slice(pos);
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
result += line.slice(pos, close);
|
|
14
|
+
inHtmlComment = false;
|
|
15
|
+
pos = close + 3;
|
|
16
|
+
}
|
|
17
|
+
else if (inBlockComment) {
|
|
18
|
+
const close = line.indexOf("*/", pos);
|
|
19
|
+
if (close === -1) {
|
|
20
|
+
result += line.slice(pos);
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
result += line.slice(pos, close);
|
|
24
|
+
inBlockComment = false;
|
|
25
|
+
pos = close + 2;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const slashSlash = line.indexOf("//", pos);
|
|
29
|
+
const slashStar = line.indexOf("/*", pos);
|
|
30
|
+
const htmlOpen = line.indexOf("<!--", pos);
|
|
31
|
+
const candidates = [
|
|
32
|
+
{ idx: slashSlash, type: "line" },
|
|
33
|
+
{ idx: slashStar, type: "block" },
|
|
34
|
+
{ idx: htmlOpen, type: "html" },
|
|
35
|
+
]
|
|
36
|
+
.filter((c) => c.idx !== -1)
|
|
37
|
+
.sort((a, b) => a.idx - b.idx);
|
|
38
|
+
const next = candidates[0];
|
|
39
|
+
if (!next)
|
|
40
|
+
break;
|
|
41
|
+
if (next.type === "line") {
|
|
42
|
+
result += line.slice(next.idx + 2);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (next.type === "block") {
|
|
46
|
+
inBlockComment = true;
|
|
47
|
+
pos = next.idx + 2;
|
|
48
|
+
}
|
|
49
|
+
if (next.type === "html") {
|
|
50
|
+
inHtmlComment = true;
|
|
51
|
+
pos = next.idx + 4;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { commentContent: result, inBlockComment, inHtmlComment };
|
|
56
|
+
}
|
|
57
|
+
export function parseFileForTodos(content, file) {
|
|
58
|
+
const lines = content.split("\n");
|
|
59
|
+
const results = [];
|
|
60
|
+
let inBlockComment = false;
|
|
61
|
+
let inHtmlComment = false;
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
const { commentContent, inBlockComment: nextBlock, inHtmlComment: nextHtml, } = extractCommentContent(lines[i], inBlockComment, inHtmlComment);
|
|
64
|
+
inBlockComment = nextBlock;
|
|
65
|
+
inHtmlComment = nextHtml;
|
|
66
|
+
if (!TODO_REGEX.test(commentContent))
|
|
67
|
+
continue;
|
|
68
|
+
// NOTE: Find where the keyword starts to reconstruct "TODO: ..." from the keyword onwards
|
|
69
|
+
const keywordMatch = /(?:TODO|FIXME)/i.exec(lines[i]);
|
|
70
|
+
const raw = keywordMatch ? lines[i].slice(keywordMatch.index).trim() : lines[i].trim();
|
|
71
|
+
const text = raw.replace(/\s*(?:\*\/|-->)\s*$/, "").trim();
|
|
72
|
+
const nextLine = lines[i + 1];
|
|
73
|
+
const hasMore = nextLine !== undefined &&
|
|
74
|
+
extractCommentContent(nextLine, inBlockComment, inHtmlComment).commentContent.trim() !== "";
|
|
75
|
+
results.push({ file, line: i + 1, text, hasMore });
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
export function parseTodoMd(content, file) {
|
|
80
|
+
const lines = content.split("\n");
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const match = CHECKBOX_REGEX.exec(line.trim());
|
|
84
|
+
if (match) {
|
|
85
|
+
results.push({
|
|
86
|
+
file,
|
|
87
|
+
text: match[1].trim(),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAa5C,wBAAsB,SAAS,CAAC,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CA6B9E"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import fg from "fast-glob";
|
|
5
|
+
import ignore from "ignore";
|
|
6
|
+
import { parseFileForTodos, parseTodoMd } from "./parser.js";
|
|
7
|
+
async function loadGitignore(cwd) {
|
|
8
|
+
const ig = ignore();
|
|
9
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
10
|
+
if (existsSync(gitignorePath)) {
|
|
11
|
+
const content = await readFile(gitignorePath, "utf8");
|
|
12
|
+
ig.add(content);
|
|
13
|
+
}
|
|
14
|
+
return ig;
|
|
15
|
+
}
|
|
16
|
+
export async function scanFiles(userIgnore = []) {
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
const ig = await loadGitignore(cwd);
|
|
19
|
+
const globIgnore = ["node_modules/**", "**/node_modules/**", ".git/**", ...userIgnore];
|
|
20
|
+
const files = await fg("**/*", {
|
|
21
|
+
cwd,
|
|
22
|
+
dot: true,
|
|
23
|
+
ignore: globIgnore,
|
|
24
|
+
onlyFiles: true,
|
|
25
|
+
});
|
|
26
|
+
const filteredFiles = files.filter((f) => !ig.ignores(f));
|
|
27
|
+
const allTodos = [];
|
|
28
|
+
for (const file of filteredFiles) {
|
|
29
|
+
const absolutePath = join(cwd, file);
|
|
30
|
+
const content = await readFile(absolutePath, "utf8");
|
|
31
|
+
if (file === "TODO.md" || file.endsWith("/TODO.md")) {
|
|
32
|
+
allTodos.push(...parseTodoMd(content, file));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
allTodos.push(...parseFileForTodos(content, file));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return allTodos;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACtD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,aAAuB,EAAE;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IAEpC,MAAM,UAAU,GAAG,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,CAAC;IAEvF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE;QAC7B,GAAG;QACH,GAAG,EAAE,IAAI;QACT,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAe,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAErD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACpD,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const CHECKED_REGEX: RegExp;
|
|
2
|
+
export declare const ANY_ITEM_REGEX: RegExp;
|
|
3
|
+
export declare const SECTIONS: readonly ["fix", "feature", "refactor", "test", "chore", "ci", "style", "build", "perf"];
|
|
4
|
+
export type Section = (typeof SECTIONS)[number];
|
|
5
|
+
export declare function hasSections(content: string): boolean;
|
|
6
|
+
export declare function migrateToSections(content: string, emoji?: boolean): string;
|
|
7
|
+
export declare function parseLines(content: string): {
|
|
8
|
+
index: number;
|
|
9
|
+
text: string;
|
|
10
|
+
}[];
|
|
11
|
+
export declare function parseCompletedLines(content: string): {
|
|
12
|
+
index: number;
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
export declare function parseAllItems(content: string): {
|
|
16
|
+
index: number;
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
export declare function parseAllItemsWithStatus(content: string): {
|
|
20
|
+
index: number;
|
|
21
|
+
text: string;
|
|
22
|
+
checked: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
export declare function uncompleteLine(content: string, index: number): string;
|
|
25
|
+
export declare function completeLine(content: string, index: number): string;
|
|
26
|
+
export declare function deleteLine(content: string, index: number): string;
|
|
27
|
+
export declare function pruneEmptySections(content: string): string;
|
|
28
|
+
export declare function appendTodo(content: string, text: string, section?: Section, emoji?: boolean): string;
|
|
29
|
+
//# sourceMappingURL=todo-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-md.d.ts","sourceRoot":"","sources":["../src/todo-md.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,QAAoB,CAAC;AAC/C,eAAO,MAAM,cAAc,QAAoB,CAAC;AAEhD,eAAO,MAAM,QAAQ,0FAUX,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAuBhD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,UAAO,GAAG,MAAM,CAKvE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAK7E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAKtF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAKhF;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAMrD;AAQD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,UAAO,GAAG,MAAM,CAmCjG"}
|
package/dist/todo-md.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const UNCHECKED_REGEX = /^- \[ \] (.+)$/;
|
|
2
|
+
export const CHECKED_REGEX = /^- \[x\] (.+)$/i;
|
|
3
|
+
export const ANY_ITEM_REGEX = /^- \[.?\] (.+)$/;
|
|
4
|
+
export const SECTIONS = [
|
|
5
|
+
"fix",
|
|
6
|
+
"feature",
|
|
7
|
+
"refactor",
|
|
8
|
+
"test",
|
|
9
|
+
"chore",
|
|
10
|
+
"ci",
|
|
11
|
+
"style",
|
|
12
|
+
"build",
|
|
13
|
+
"perf",
|
|
14
|
+
];
|
|
15
|
+
const SECTION_EMOJIS = {
|
|
16
|
+
fix: "🐛",
|
|
17
|
+
feature: "✨",
|
|
18
|
+
refactor: "♻️",
|
|
19
|
+
test: "🧪",
|
|
20
|
+
chore: "🧹",
|
|
21
|
+
ci: "🚀",
|
|
22
|
+
style: "💅",
|
|
23
|
+
build: "📦",
|
|
24
|
+
perf: "⚡",
|
|
25
|
+
};
|
|
26
|
+
function sectionHeading(section, emoji) {
|
|
27
|
+
return emoji ? `## ${SECTION_EMOJIS[section]} ${section}` : `## ${section}`;
|
|
28
|
+
}
|
|
29
|
+
/** Matches a section heading line for a given section name, with or without emoji. */
|
|
30
|
+
function matchesSectionHeading(line, section) {
|
|
31
|
+
return line === `## ${section}` || (line.startsWith(`## `) && line.endsWith(` ${section}`));
|
|
32
|
+
}
|
|
33
|
+
export function hasSections(content) {
|
|
34
|
+
return content.split("\n").some((line) => /^## \S/.test(line));
|
|
35
|
+
}
|
|
36
|
+
export function migrateToSections(content, emoji = true) {
|
|
37
|
+
const lines = content.split("\n");
|
|
38
|
+
const checkboxLines = lines.filter((line) => ANY_ITEM_REGEX.test(line));
|
|
39
|
+
if (checkboxLines.length === 0)
|
|
40
|
+
return content;
|
|
41
|
+
return `${sectionHeading("fix", emoji)}\n\n${checkboxLines.join("\n")}\n`;
|
|
42
|
+
}
|
|
43
|
+
export function parseLines(content) {
|
|
44
|
+
return content.split("\n").flatMap((line, index) => {
|
|
45
|
+
const match = UNCHECKED_REGEX.exec(line);
|
|
46
|
+
return match ? [{ index, text: match[1].trim() }] : [];
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function parseCompletedLines(content) {
|
|
50
|
+
return content.split("\n").flatMap((line, index) => {
|
|
51
|
+
const match = CHECKED_REGEX.exec(line);
|
|
52
|
+
return match ? [{ index, text: match[1].trim() }] : [];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function parseAllItems(content) {
|
|
56
|
+
return content.split("\n").flatMap((line, index) => {
|
|
57
|
+
const match = ANY_ITEM_REGEX.exec(line);
|
|
58
|
+
return match ? [{ index, text: match[1].trim() }] : [];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function parseAllItemsWithStatus(content) {
|
|
62
|
+
return content.split("\n").flatMap((line, index) => {
|
|
63
|
+
const match = ANY_ITEM_REGEX.exec(line);
|
|
64
|
+
if (!match)
|
|
65
|
+
return [];
|
|
66
|
+
return [{ index, text: match[1].trim(), checked: CHECKED_REGEX.test(line) }];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function assertValidIndex(index, length) {
|
|
70
|
+
if (index < 0 || index >= length) {
|
|
71
|
+
throw new RangeError(`Line index ${index} is out of bounds (file has ${length} lines)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function uncompleteLine(content, index) {
|
|
75
|
+
const lines = content.split("\n");
|
|
76
|
+
assertValidIndex(index, lines.length);
|
|
77
|
+
lines[index] = lines[index].replace(/^- \[x\] /i, "- [ ] ");
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
80
|
+
export function completeLine(content, index) {
|
|
81
|
+
const lines = content.split("\n");
|
|
82
|
+
assertValidIndex(index, lines.length);
|
|
83
|
+
lines[index] = lines[index].replace("- [ ]", "- [x]");
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
export function deleteLine(content, index) {
|
|
87
|
+
const lines = content.split("\n");
|
|
88
|
+
assertValidIndex(index, lines.length);
|
|
89
|
+
lines.splice(index, 1);
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
export function pruneEmptySections(content) {
|
|
93
|
+
const lines = content.split("\n");
|
|
94
|
+
const keep = new Array(lines.length).fill(true);
|
|
95
|
+
for (let i = 0; i < lines.length; i++) {
|
|
96
|
+
if (!/^## \S/.test(lines[i]))
|
|
97
|
+
continue;
|
|
98
|
+
// Find the end of this section (next ## heading or EOF)
|
|
99
|
+
let nextSection = lines.length;
|
|
100
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
101
|
+
if (/^## \S/.test(lines[j])) {
|
|
102
|
+
nextSection = j;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Check if section has any checkbox items
|
|
107
|
+
const hasItems = lines.slice(i + 1, nextSection).some((l) => ANY_ITEM_REGEX.test(l));
|
|
108
|
+
if (!hasItems) {
|
|
109
|
+
// Remove the header and any blank lines immediately after it up to the next section
|
|
110
|
+
for (let j = i; j < nextSection; j++) {
|
|
111
|
+
keep[j] = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const result = lines.filter((_, i) => keep[i]).join("\n");
|
|
116
|
+
// Collapse 3+ consecutive blank lines into 2
|
|
117
|
+
return result.replace(/\n{3,}/g, "\n\n");
|
|
118
|
+
}
|
|
119
|
+
export function appendTodo(content, text, section, emoji = true) {
|
|
120
|
+
if (!section) {
|
|
121
|
+
const trimmed = content.trimEnd();
|
|
122
|
+
const base = trimmed.length > 0 ? `${trimmed}\n` : "";
|
|
123
|
+
return `${base}- [ ] ${text}\n`;
|
|
124
|
+
}
|
|
125
|
+
const lines = content.split("\n");
|
|
126
|
+
const sectionStartIndex = lines.findIndex((line) => matchesSectionHeading(line, section));
|
|
127
|
+
if (sectionStartIndex === -1) {
|
|
128
|
+
const trimmed = content.trimEnd();
|
|
129
|
+
const base = trimmed.length > 0 ? `${trimmed}\n` : "";
|
|
130
|
+
return `${base}\n${sectionHeading(section, emoji)}\n\n- [ ] ${text}\n`;
|
|
131
|
+
}
|
|
132
|
+
const nextSectionIndex = lines.findIndex((line, i) => i > sectionStartIndex && /^## \S/.test(line));
|
|
133
|
+
const blockEnd = nextSectionIndex === -1 ? lines.length : nextSectionIndex;
|
|
134
|
+
let lastItemIndex = -1;
|
|
135
|
+
for (let i = sectionStartIndex + 1; i < blockEnd; i++) {
|
|
136
|
+
if (/^- \[/.test(lines[i]))
|
|
137
|
+
lastItemIndex = i;
|
|
138
|
+
}
|
|
139
|
+
const insertAfter = lastItemIndex !== -1
|
|
140
|
+
? lastItemIndex
|
|
141
|
+
: lines[sectionStartIndex + 1] === ""
|
|
142
|
+
? sectionStartIndex + 1
|
|
143
|
+
: sectionStartIndex;
|
|
144
|
+
lines.splice(insertAfter + 1, 0, `- [ ] ${text}`);
|
|
145
|
+
return lines.join("\n");
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=todo-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-md.js","sourceRoot":"","sources":["../src/todo-md.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tospudo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tool to manage TODOs in your project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tospudo": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"schema.json"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"todo"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/hayrdenhald/tospudo.git"
|
|
19
|
+
},
|
|
20
|
+
"author": "hayrdenhald",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.0.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@clack/prompts": "1.2.0",
|
|
27
|
+
"cac": "7.0.0",
|
|
28
|
+
"cosmiconfig": "9.0.1",
|
|
29
|
+
"fast-glob": "3.3.3",
|
|
30
|
+
"ignore": "7.0.5",
|
|
31
|
+
"picocolors": "1.1.1",
|
|
32
|
+
"zod": "4.3.6"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@biomejs/biome": "2.4.10",
|
|
36
|
+
"@types/node": "25.5.0",
|
|
37
|
+
"knip": "6.2.0",
|
|
38
|
+
"typescript": "6.0.2",
|
|
39
|
+
"vitest": "4.1.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"build": "pnpm run clean && tsc --project tsconfig.build.json",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"lint": "biome check .",
|
|
46
|
+
"format": "biome format --write .",
|
|
47
|
+
"knip": "knip",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:unit": "vitest run tests/unit",
|
|
50
|
+
"test:meta": "vitest run tests/meta"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/schema.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "TospudoConfig",
|
|
4
|
+
"description": "Configuration for tospudo.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": true,
|
|
7
|
+
"allowedProperties": ["$schema"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"ignore": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"items": { "type": "string" },
|
|
12
|
+
"description": "Glob patterns to exclude from scanning, in addition to .gitignore rules."
|
|
13
|
+
},
|
|
14
|
+
"max": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"minimum": 1,
|
|
17
|
+
"description": "Exit with code 1 if TODO count exceeds this threshold. Useful in CI. Can also be set with the --max CLI flag."
|
|
18
|
+
},
|
|
19
|
+
"maxLength": {
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"minimum": 1,
|
|
22
|
+
"description": "Truncate TODO text in scan output to this many characters. Defaults to 80. Does not affect TODO.md content."
|
|
23
|
+
},
|
|
24
|
+
"sectionEmojis": {
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"description": "Show an emoji prefix in TODO.md section headings (e.g. \"🐛 fix\"). Defaults to true."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|