From e88ded8c975501a09f3f306d2f2d7c732f20388f Mon Sep 17 00:00:00 2001 From: mohamed yahia Date: Sat, 25 Nov 2023 13:54:58 +0200 Subject: [PATCH 1/5] [ #60 , extract tasks ] --- __mocks__/content/index.mdx | 6 +++ src/lib/parseFile.ts | 37 +++++++++++++++++ src/tests/extractTasks.spec.ts | 72 ++++++++++++++++++++++++++++++++++ src/tests/parseFile.spec.ts | 10 +++++ src/tests/process.spec.ts | 14 +++++++ 5 files changed, 139 insertions(+) create mode 100644 src/tests/extractTasks.spec.ts diff --git a/__mocks__/content/index.mdx b/__mocks__/content/index.mdx index 56d9c80..fa8bb6b 100644 --- a/__mocks__/content/index.mdx +++ b/__mocks__/content/index.mdx @@ -6,3 +6,9 @@ tags: tag1, tag2, tag3 # Welcome [link](blog0.mdx) + +- [] uncompleted task 1 +- [ ] uncompleted task 2 + +- [x] completed task 1 +- [X] completed task 2 \ No newline at end of file diff --git a/src/lib/parseFile.ts b/src/lib/parseFile.ts index 3a26eee..fddc1f3 100644 --- a/src/lib/parseFile.ts +++ b/src/lib/parseFile.ts @@ -24,6 +24,9 @@ export function parseFile(source: string, options?: ParsingOptions) { // Links const links = extractWikiLinks(ast, options); + const tasks = extractTasks(ast); + metadata.tasks = tasks; + return { metadata, links, @@ -155,6 +158,40 @@ export const extractWikiLinks = (ast: Root, options?: ParsingOptions) => { return wikiLinks; }; +export interface Task { + description: string; + checked: boolean; +} + +export const extractTasks = (ast: Root) => { + const nodes = selectAll("*", ast); + const tasks: Task[] = []; + nodes.map((node: any) => { + if (node.type === "listItem") { + const description = recursivelyExtractText(node).trim(); + const checked = node.checked; + if (checked !== null) { + tasks.push({ + description, + checked, + }); + } + } + }); + + return tasks; +}; + +function recursivelyExtractText(node) { + if (node.value) { + return node.value; + } else if (node.children) { + return node.children.map(recursivelyExtractText).join(" "); + } else { + return ""; + } +} + // links = extractWikiLinks({ // source, // // TODO pass slug instead of file path as hrefs/srcs are sluggified too diff --git a/src/tests/extractTasks.spec.ts b/src/tests/extractTasks.spec.ts new file mode 100644 index 0000000..cc3de8e --- /dev/null +++ b/src/tests/extractTasks.spec.ts @@ -0,0 +1,72 @@ +import { extractTasks, processAST } from "../lib/parseFile"; + +const getTasksFromSource = (source: string) => { + const ast = processAST(source, {}); + const tasks = extractTasks(ast); + return tasks; +}; + +describe("extractTasks", () => { + test("should extract uncompleted tasks from body", () => { + const tasks = getTasksFromSource( + "- [] uncompleted task 1\n- [ ] uncompleted task 2" + ); + const expectedTasks = [ + { description: "uncompleted task 2", checked: false }, + ]; + expect(tasks).toEqual(expectedTasks); + }); + + test("should extract completed tasks from body", () => { + const tasks = getTasksFromSource( + "- [x] completed task 1\n- [X] completed task 2" + ); + const expectedTasks = [ + { description: "completed task 1", checked: true }, + { description: "completed task 2", checked: true }, + ]; + expect(tasks).toEqual(expectedTasks); + }); + + test("should handle mixed completed and uncompleted tasks", () => { + const tasks = getTasksFromSource( + "- [x] completed task\n- [ ] uncompleted task" + ); + const expectedTasks = [ + { description: "completed task", checked: true }, + { description: "uncompleted task", checked: false }, + ]; + expect(tasks).toEqual(expectedTasks); + }); + + test("should handle tasks with leading and trailing spaces", () => { + const tasks = getTasksFromSource( + "- [x] completed task \n- [ ] uncompleted task " + ); + const expectedTasks = [ + { description: "completed task", checked: true }, + { description: "uncompleted task", checked: false }, + ]; + expect(tasks).toEqual(expectedTasks); + }); + + test("should handle tasks with different checkbox formats", () => { + const tasks = getTasksFromSource( + "- [x] task 1\n- [X] task 2\n- [ ] task 3" + ); + const expectedTasks = [ + { description: "task 1", checked: true }, + { description: "task 2", checked: true }, + { description: "task 3", checked: false }, + ]; + expect(tasks).toEqual(expectedTasks); + }); + + test("should handle tasks with special characters", () => { + const tasks = getTasksFromSource("- [x] task with $pecial character$"); + const expectedTasks = [ + { description: "task with $pecial character$", checked: true }, + ]; + expect(tasks).toEqual(expectedTasks); + }); +}); diff --git a/src/tests/parseFile.spec.ts b/src/tests/parseFile.spec.ts index 482aaa0..fd77369 100644 --- a/src/tests/parseFile.spec.ts +++ b/src/tests/parseFile.spec.ts @@ -10,6 +10,8 @@ tags: a, b, c [[blog/Some Other Link]] [[blog/Some Other Link|Page Alias]] ![[Some Image.png]] +- [ ] uncompleted task +- [x] completed task `; describe("parseFile", () => { @@ -18,6 +20,10 @@ describe("parseFile", () => { title: "Hello World", authors: ["John Doe", "Jane Doe"], tags: ["a", "b", "c"], + tasks: [ + { description: "uncompleted task", checked: false }, + { description: "completed task", checked: true }, + ], }; const expectedLinks = [ { @@ -63,6 +69,10 @@ describe("parseFile", () => { title: "Hello World", authors: ["John Doe", "Jane Doe"], tags: ["a", "b", "c"], + tasks: [ + { description: "uncompleted task", checked: false }, + { description: "completed task", checked: true }, + ], }; const expectedLinks = [ { diff --git a/src/tests/process.spec.ts b/src/tests/process.spec.ts index 936195f..43fd910 100644 --- a/src/tests/process.spec.ts +++ b/src/tests/process.spec.ts @@ -21,6 +21,20 @@ describe("Can parse a file and get file info", () => { expect(fileInfo.metadata).toEqual({ title: "Homepage", tags: ["tag1", "tag2", "tag3"], + tasks: [ + { + checked: false, + description: "uncompleted task 2", + }, + { + checked: true, + description: "completed task 1", + }, + { + checked: true, + description: "completed task 2", + }, + ], }); expect(fileInfo.links).toEqual([ { From 78506e830cf09d4a3bed01106bb35ff60186c9ee Mon Sep 17 00:00:00 2001 From: mohamed yahia Date: Sat, 25 Nov 2023 14:02:02 +0200 Subject: [PATCH 2/5] fix linting --- src/lib/parseFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/parseFile.ts b/src/lib/parseFile.ts index fddc1f3..069ed05 100644 --- a/src/lib/parseFile.ts +++ b/src/lib/parseFile.ts @@ -182,7 +182,7 @@ export const extractTasks = (ast: Root) => { return tasks; }; -function recursivelyExtractText(node) { +function recursivelyExtractText(node: any) { if (node.value) { return node.value; } else if (node.children) { From a0a29dc8d8166fcd1ff190d380616355dd8f5b3a Mon Sep 17 00:00:00 2001 From: mohamed yahia Date: Sat, 25 Nov 2023 14:04:24 +0200 Subject: [PATCH 3/5] Add changeset --- .changeset/five-dots-speak.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/five-dots-speak.md diff --git a/.changeset/five-dots-speak.md b/.changeset/five-dots-speak.md new file mode 100644 index 0000000..f5b1dbf --- /dev/null +++ b/.changeset/five-dots-speak.md @@ -0,0 +1,6 @@ +--- +"mddb": minor +--- + +[ #60 , extract tasks ] +Add tasks extraction from files. e.g `- [ ] task` From be5f06ce7baf4f4082490604e95198a2f1492d4c Mon Sep 17 00:00:00 2001 From: mohamed yahia Date: Sat, 25 Nov 2023 14:08:28 +0200 Subject: [PATCH 4/5] Run format --- src/lib/databaseUtils.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib/databaseUtils.ts b/src/lib/databaseUtils.ts index 03316f4..5292ff6 100644 --- a/src/lib/databaseUtils.ts +++ b/src/lib/databaseUtils.ts @@ -1,11 +1,5 @@ import { Knex } from "knex"; -import { - MddbFile, - MddbTag, - MddbLink, - MddbFileTag, - File, -} from "./schema.js"; +import { MddbFile, MddbTag, MddbLink, MddbFileTag, File } from "./schema.js"; import path from "path"; import { WikiLink } from "./parseFile.js"; From e1923b3d6c4cfcad847fa3783a0191ac2a13e263 Mon Sep 17 00:00:00 2001 From: mohamed yahia Date: Mon, 27 Nov 2023 09:18:06 +0200 Subject: [PATCH 5/5] Exclude list items with an undefined checked property --- src/lib/parseFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/parseFile.ts b/src/lib/parseFile.ts index 069ed05..1883240 100644 --- a/src/lib/parseFile.ts +++ b/src/lib/parseFile.ts @@ -170,7 +170,7 @@ export const extractTasks = (ast: Root) => { if (node.type === "listItem") { const description = recursivelyExtractText(node).trim(); const checked = node.checked; - if (checked !== null) { + if (checked !== null && checked !== undefined) { tasks.push({ description, checked,