-
Notifications
You must be signed in to change notification settings - Fork 11
fix: BLOCKMENTIONSDATED handles comma dates and 'first of' phrases (#54) #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MONTH_DAY_REGEX = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /^(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\s+\d{1,2}(?:st|nd|rd|th)?$/i; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const YEAR_REGEX = /^\d{4}$/; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Re-joins "Month Day, Year" date tokens that were split on commas. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * BLOCKMENTIONSDATED signature: (limit, title, startDate, endDate, sort, format, ...search) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Positions 0-1 (limit, title) are passed through unchanged. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Starting at position 2, up to 2 date tokens are coalesced (startDate + endDate). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The `mergedDates < 2` guard stops coalescing after both date slots are filled. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const coalesceBlockMentionsDatedDates = (args: string[]) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const merged = args.slice(0, 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let i = 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mergedDates = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (i < args.length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const current = args[i] || ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const next = args[i + 1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mergedDates < 2 && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typeof next === "string" && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONTH_DAY_REGEX.test(current.trim()) && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| YEAR_REGEX.test(next.trim()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| merged.push(`${current.trimEnd()}, ${next.trimStart()}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| i += 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mergedDates += 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| merged.push(current); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| i += 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return merged; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const splitSmartBlockArgs = (cmd: string, afterColon: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let commandStack = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let pageRefStack = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const args = [] as string[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let i = 0; i < afterColon.length; i += 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cur = afterColon[i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prev = afterColon[i - 1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const next = afterColon[i + 1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cur === "%" && prev === "<") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commandStack += 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (cur === "%" && next === ">" && commandStack) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commandStack -= 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (cur === "[" && next === "[") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pageRefStack += 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (cur === "]" && prev === "]" && pageRefStack) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pageRefStack -= 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+53
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The Concrete trace for
This only triggers on malformed input, but the fix is straightforward — only modify 🛡️ Proposed fix if (cur === "%" && prev === "<") {
commandStack += 1;
} else if (cur === "%" && next === ">" && commandStack) {
commandStack -= 1;
- } else if (cur === "[" && next === "[") {
- pageRefStack += 1;
- } else if (cur === "]" && prev === "]" && pageRefStack) {
- pageRefStack -= 1;
+ } else if (!commandStack) {
+ if (cur === "[" && next === "[") {
+ pageRefStack += 1;
+ } else if (cur === "]" && prev === "]" && pageRefStack) {
+ pageRefStack -= 1;
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cur === "," && !commandStack && !pageRefStack && prev !== "\\") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args.push(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+54
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leading comma silently drops the first argument slot.
For BLOCKMENTIONSDATED this shifts every positional arg left by one ( 🛡️ Proposed fix if (cur === "," && !commandStack && !pageRefStack && prev !== "\\") {
+ if (!args.length) args.push(""); // ensure first arg slot exists
args.push("");
continue;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (cur === "\\" && next === ",") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const current = args[args.length - 1] || ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!args.length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args.push(cur); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args[args.length - 1] = `${current}${cur}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cmd.toUpperCase() === "BLOCKMENTIONSDATED" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? coalesceBlockMentionsDatedDates(args) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : args; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default splitSmartBlockArgs; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { test, expect } from "@playwright/test"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import splitSmartBlockArgs from "../src/utils/splitSmartBlockArgs"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("splits nested smartblock commands as a single argument", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs("ANYCOMMAND", "one,<%RANDOMNUMBER:1,10%>,two") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["one", "<%RANDOMNUMBER:1,10%>", "two"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("preserves commas in daily note page references", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BLOCKMENTIONSDATED", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "10,DONE,[[January 24th, 2023]],[[January 1st, 2023]]" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["10", "DONE", "[[January 24th, 2023]]", "[[January 1st, 2023]]"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("coalesces month-day-year date tokens for BLOCKMENTIONSDATED", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BLOCKMENTIONSDATED", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "10,DONE,February 1, 2023,February 24, 2023,DESC" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["10", "DONE", "February 1, 2023", "February 24, 2023", "DESC"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("handles unclosed [[ gracefully by treating rest as single arg", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs("BLOCKMENTIONSDATED", "10,DONE,[[January 24th") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["10", "DONE", "[[January 24th"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("preserves commas inside nested page references", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BLOCKMENTIONSDATED", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "10,DONE,[[January 24th, 2023]],today" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["10", "DONE", "[[January 24th, 2023]]", "today"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("coalesces at most two date tokens for BLOCKMENTIONSDATED", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "BLOCKMENTIONSDATED", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "10,DONE,January 1, 2023,February 1, 2023,March 1, 2023" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "10", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "DONE", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "January 1, 2023", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "February 1, 2023", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "March 1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| " 2023", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+57
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Third-date coalescence limit behavior is correctly documented, but the " 2023" leading space deserves a comment. The test asserts 💡 Suggested comment ).toEqual([
"10",
"DONE",
"January 1, 2023",
"February 1, 2023",
- "March 1",
- " 2023",
+ "March 1", // mergedDates === 2, so 3rd date is not coalesced...
+ " 2023", // ...and the raw " 2023" token retains its leading space
]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("does not coalesce date tokens for non-BLOCKMENTIONSDATED commands", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs("ANYCOMMAND", "January 1, 2023") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["January 1", " 2023"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("unclosed [[ in non-BLOCKMENTIONSDATED treats remaining as single arg", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs("ANYCOMMAND", "one,[[unclosed,two,three") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["one", "[[unclosed,two,three"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("balanced [[ ]] followed by normal args splits correctly", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSmartBlockArgs("ANYCOMMAND", "[[page ref]],normal,arg") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toEqual(["[[page ref]]", "normal", "arg"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Missing edge-case test coverage. The suite covers the happy paths well, but several boundary conditions are absent:
Would you like me to draft the missing test cases? 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
date-fns startOfDay null behavior JavaScript💡 Result:
In date-fns,
startOfDaycallstoDatefirst, then doessetHours(0,0,0,0). (app.unpkg.com)What happens if you pass
null?It depends on the date-fns major version:
date-fns v2 (and the v2-era design):
nullis treated as not a valid date, sotoDate(null)returnsInvalid Date, and thereforestartOfDay(null)also yields an invalid date value. (blog.date-fns.org)date-fns v4.1.0 (current “latest” on unpkg at the time of this lookup):
toDate(null)ends up doingnew Date(null)viaconstructFrom(...), which produces a valid Date at Unix epoch (timestamp 0), sostartOfDay(null)returns the start of 1970-01-01 in the local timezone. (app.unpkg.com)Practical takeaway
Don’t rely on
nullcoercion—guard it yourself (or validate withisValid) before callingstartOfDay, because the runtime behavior has differed across major versions. (app.unpkg.com)Citations:
🌐 Web query:
chrono-node "start of this month" natural language parsing💡 Result:
chrono-nodedoesn’t have a built-in concept like “start of this month” (i.e., normalize to day 1 @ 00:00), but it’s designed to be extended: you can clone a parser (e.g.chrono.casual.clone()) and push a custom parser (or a refiner) to recognize that phrase and return the components you want. (npmjs.com)Option A: Add a custom parser for “start of this month”
This follows chrono’s documented extension pattern (clone +
parsers.push(...)). (npmjs.com)Option B: Parse “this month”, then post-process
If you’re okay with post-processing, parse something like
"this month"and then set the returnedDateto the 1st at midnight in your own code (using JSnew Date(y, m, 1)), rather than teaching chrono the phrase. (Chrono exposesparseDate/parseandParsedComponents.date()to get aDateyou can adjust.) (npmjs.com)Citations:
🏁 Script executed:
Repository: RoamJS/smartblocks
Length of output: 619
🏁 Script executed:
grep -n "const parseNlpDate" src/utils/core.ts -A 10Repository: RoamJS/smartblocks
Length of output: 44
🏁 Script executed:
Repository: RoamJS/smartblocks
Length of output: 44
🏁 Script executed:
rg "parseNlpDate" src/utils/core.ts -A 5 -B 2Repository: RoamJS/smartblocks
Length of output: 5975
🏁 Script executed:
Repository: RoamJS/smartblocks
Length of output: 74
🏁 Script executed:
rg "date-fns" package.jsonRepository: RoamJS/smartblocks
Length of output: 44
🏁 Script executed:
rg "addNlpDateParser" src/utils/core.tsRepository: RoamJS/smartblocks
Length of output: 100
🏁 Script executed:
Repository: RoamJS/smartblocks
Length of output: 835
🏁 Script executed:
rg "addNlpDateParser" src/utils/core.ts -A 20 -B 2Repository: RoamJS/smartblocks
Length of output: 1824
🏁 Script executed:
Repository: RoamJS/smartblocks
Length of output: 3272
parseBlockMentionsDatedArgreturnsnullwhen date parsing fails, and the "first of" → "start of" normalization is unsupported.1. Missing "start of" parser: Line 137 replaces
"first of" → "start of"(e.g., "first of this month" becomes "start of this month"), but chrono-node has no built-in parser for"start of"phrases. The custom parser added viaaddNlpDateParseronly handles theDBOM/DEOMformat, not natural language "start of" expressions. This means the normalized phrase will fail to parse, andparseNlpDatewill returnnull.2. Null propagation into
startOfDay/endOfDay: WhenpageTitleToDateandparseNlpDateboth fail,parseBlockMentionsDatedArgreturnsnull. The guards at lines 1233 and 1237 check ifstartArgandendArgare truthy, but they do not guard the return value ofparseBlockMentionsDatedArg. Anullresult propagates directly intostartOfDay(null)andendOfDay(null). In date-fns v4,new Date(null)coerces to Unix epoch (1970-01-01), which will silently filter results to that date instead of failing loudly.Fixes:
parseBlockMentionsDatedArg(startArg, referenceDate) || new Date()before passing tostartOfDay/endOfDay.🤖 Prompt for AI Agents