@md2do/core 0.2.3 → 0.4.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/CHANGELOG.md +36 -0
- package/coverage/coverage-final.json +6 -5
- package/coverage/index.html +52 -37
- package/coverage/lcov-report/index.html +52 -37
- package/coverage/lcov-report/src/filters/index.html +1 -1
- package/coverage/lcov-report/src/filters/index.ts.html +1 -1
- package/coverage/lcov-report/src/index.html +5 -5
- package/coverage/lcov-report/src/index.ts.html +13 -4
- package/coverage/lcov-report/src/parser/index.html +17 -17
- package/coverage/lcov-report/src/parser/index.ts.html +181 -13
- package/coverage/lcov-report/src/parser/patterns.ts.html +18 -9
- package/coverage/lcov-report/src/scanner/index.html +15 -15
- package/coverage/lcov-report/src/scanner/index.ts.html +83 -8
- package/coverage/lcov-report/src/sorting/index.html +1 -1
- package/coverage/lcov-report/src/sorting/index.ts.html +1 -1
- package/coverage/lcov-report/src/utils/dates.ts.html +169 -25
- package/coverage/lcov-report/src/utils/id.ts.html +1 -1
- package/coverage/lcov-report/src/utils/index.html +19 -19
- package/coverage/lcov-report/src/warnings/filter.ts.html +364 -0
- package/coverage/lcov-report/src/warnings/index.html +116 -0
- package/coverage/lcov-report/src/writer/index.html +1 -1
- package/coverage/lcov-report/src/writer/index.ts.html +1 -1
- package/coverage/lcov.info +760 -512
- package/coverage/src/filters/index.html +1 -1
- package/coverage/src/filters/index.ts.html +1 -1
- package/coverage/src/index.html +5 -5
- package/coverage/src/index.ts.html +13 -4
- package/coverage/src/parser/index.html +17 -17
- package/coverage/src/parser/index.ts.html +181 -13
- package/coverage/src/parser/patterns.ts.html +18 -9
- package/coverage/src/scanner/index.html +15 -15
- package/coverage/src/scanner/index.ts.html +83 -8
- package/coverage/src/sorting/index.html +1 -1
- package/coverage/src/sorting/index.ts.html +1 -1
- package/coverage/src/utils/dates.ts.html +169 -25
- package/coverage/src/utils/id.ts.html +1 -1
- package/coverage/src/utils/index.html +19 -19
- package/coverage/src/warnings/filter.ts.html +364 -0
- package/coverage/src/warnings/index.html +116 -0
- package/coverage/src/writer/index.html +1 -1
- package/coverage/src/writer/index.ts.html +1 -1
- package/dist/index.d.mts +103 -12
- package/dist/index.d.ts +103 -12
- package/dist/index.js +125 -11
- package/dist/index.mjs +125 -12
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/parser/index.ts +61 -5
- package/src/parser/patterns.ts +8 -5
- package/src/scanner/index.ts +25 -0
- package/src/types/index.ts +35 -2
- package/src/utils/dates.ts +58 -10
- package/src/warnings/filter.ts +93 -0
package/dist/index.d.mts
CHANGED
|
@@ -21,6 +21,9 @@ interface ParsingContext {
|
|
|
21
21
|
person?: string;
|
|
22
22
|
currentDate?: Date;
|
|
23
23
|
currentHeading?: string;
|
|
24
|
+
workdayStartTime?: string;
|
|
25
|
+
workdayEndTime?: string;
|
|
26
|
+
defaultDueTime?: 'start' | 'end';
|
|
24
27
|
}
|
|
25
28
|
interface TaskFilterCriteria {
|
|
26
29
|
assignee?: string | string[];
|
|
@@ -47,11 +50,20 @@ interface ScanResult {
|
|
|
47
50
|
parseErrors: number;
|
|
48
51
|
};
|
|
49
52
|
}
|
|
53
|
+
type WarningSeverity = 'info' | 'warning' | 'error';
|
|
54
|
+
type WarningCode = 'unsupported-bullet' | 'malformed-checkbox' | 'missing-space-after' | 'missing-space-before' | 'relative-date-no-context' | 'missing-due-date' | 'missing-completed-date' | 'duplicate-todoist-id' | 'file-read-error';
|
|
50
55
|
interface Warning {
|
|
51
56
|
file: string;
|
|
52
57
|
line: number;
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
column?: number;
|
|
59
|
+
severity: WarningSeverity;
|
|
60
|
+
source: 'md2do';
|
|
61
|
+
ruleId: WarningCode;
|
|
62
|
+
message: string;
|
|
63
|
+
text?: string;
|
|
64
|
+
url?: string;
|
|
65
|
+
/** @deprecated Use message instead */
|
|
66
|
+
reason?: string;
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
/**
|
|
@@ -113,10 +125,10 @@ declare function extractCompletedDate(text: string): Date | undefined;
|
|
|
113
125
|
* Extract due date from task text with context awareness
|
|
114
126
|
*
|
|
115
127
|
* Handles both absolute dates ([due: 2026-01-25]) and relative dates
|
|
116
|
-
* ([due: tomorrow]) which require context.
|
|
128
|
+
* ([due: tomorrow]) which require context. Supports optional time ([due: 2026-01-25 17:00]).
|
|
117
129
|
*
|
|
118
130
|
* @param text - Task text
|
|
119
|
-
* @param context - Parsing context (for relative dates)
|
|
131
|
+
* @param context - Parsing context (for relative dates and workday config)
|
|
120
132
|
* @returns Object with parsed date and optional warning
|
|
121
133
|
*/
|
|
122
134
|
declare function extractDueDate(text: string, context: ParsingContext): {
|
|
@@ -223,15 +235,17 @@ declare const PRIORITY_HIGH: RegExp;
|
|
|
223
235
|
*/
|
|
224
236
|
declare const PRIORITY_NORMAL: RegExp;
|
|
225
237
|
/**
|
|
226
|
-
* Matches absolute due date in ISO format [due: YYYY-MM-DD]
|
|
238
|
+
* Matches absolute due date in ISO format [due: YYYY-MM-DD] with optional time [due: YYYY-MM-DD HH:MM]
|
|
227
239
|
*
|
|
228
240
|
* Examples:
|
|
229
|
-
* "[due: 2026-01-25]" → "2026-01-25"
|
|
230
|
-
* "[due:2026-01-25]" → "2026-01-25"
|
|
231
|
-
* "[due:
|
|
241
|
+
* "[due: 2026-01-25]" → "2026-01-25", undefined
|
|
242
|
+
* "[due: 2026-01-25 17:00]" → "2026-01-25", "17:00"
|
|
243
|
+
* "[due: 2026-01-25 9:00]" → "2026-01-25", "9:00"
|
|
244
|
+
* "[due:2026-01-25]" → "2026-01-25", undefined (spaces optional)
|
|
232
245
|
*
|
|
233
246
|
* Groups:
|
|
234
247
|
* [1] - Date string in YYYY-MM-DD format
|
|
248
|
+
* [2] - Optional time string in H:MM or HH:MM format (24-hour)
|
|
235
249
|
*/
|
|
236
250
|
declare const DUE_DATE_ABSOLUTE: RegExp;
|
|
237
251
|
/**
|
|
@@ -391,9 +405,14 @@ declare class MarkdownScanner {
|
|
|
391
405
|
*
|
|
392
406
|
* @param filePath - Relative file path (for context extraction)
|
|
393
407
|
* @param content - File content as string
|
|
408
|
+
* @param options - Optional scanner options including workday config
|
|
394
409
|
* @returns Object containing tasks and warnings
|
|
395
410
|
*/
|
|
396
|
-
scanFile(filePath: string, content: string
|
|
411
|
+
scanFile(filePath: string, content: string, options?: {
|
|
412
|
+
workdayStartTime?: string;
|
|
413
|
+
workdayEndTime?: string;
|
|
414
|
+
defaultDueTime?: 'start' | 'end';
|
|
415
|
+
}): {
|
|
397
416
|
tasks: Task[];
|
|
398
417
|
warnings: Warning[];
|
|
399
418
|
};
|
|
@@ -812,8 +831,75 @@ declare namespace index {
|
|
|
812
831
|
export { type index_TaskComparator as TaskComparator, index_byAssignee as byAssignee, index_byCompletionStatus as byCompletionStatus, index_byCreatedDate as byCreatedDate, index_byDueDate as byDueDate, index_byFile as byFile, index_byPerson as byPerson, index_byPriority as byPriority, index_byProject as byProject, index_combineComparators as combineComparators, index_reverse as reverse };
|
|
813
832
|
}
|
|
814
833
|
|
|
834
|
+
interface WarningFilterConfig {
|
|
835
|
+
enabled?: boolean | undefined;
|
|
836
|
+
rules?: Record<string, 'error' | 'warn' | 'info' | 'off'> | undefined;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Filter warnings based on configuration rules
|
|
840
|
+
*
|
|
841
|
+
* This function applies warning configuration rules to filter out disabled warnings
|
|
842
|
+
* and optionally all warnings if globally disabled.
|
|
843
|
+
*
|
|
844
|
+
* @param warnings - Array of warnings to filter
|
|
845
|
+
* @param config - Warning configuration with enabled flag and rule overrides
|
|
846
|
+
* @returns Filtered array of warnings
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* const config = {
|
|
851
|
+
* enabled: true,
|
|
852
|
+
* rules: {
|
|
853
|
+
* 'missing-due-date': 'off',
|
|
854
|
+
* 'duplicate-todoist-id': 'error',
|
|
855
|
+
* },
|
|
856
|
+
* };
|
|
857
|
+
*
|
|
858
|
+
* const filtered = filterWarnings(allWarnings, config);
|
|
859
|
+
* // Returns warnings except those with ruleId 'missing-due-date'
|
|
860
|
+
* ```
|
|
861
|
+
*/
|
|
862
|
+
declare function filterWarnings(warnings: Warning[], config?: WarningFilterConfig): Warning[];
|
|
863
|
+
/**
|
|
864
|
+
* Group warnings by severity
|
|
865
|
+
*
|
|
866
|
+
* Useful for displaying warnings in order of importance or
|
|
867
|
+
* treating errors differently from warnings.
|
|
868
|
+
*
|
|
869
|
+
* @param warnings - Array of warnings to group
|
|
870
|
+
* @returns Object with warnings grouped by severity level
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```typescript
|
|
874
|
+
* const grouped = groupWarningsBySeverity(warnings);
|
|
875
|
+
* console.log(`${grouped.error.length} errors`);
|
|
876
|
+
* console.log(`${grouped.warning.length} warnings`);
|
|
877
|
+
* console.log(`${grouped.info.length} info messages`);
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
881
|
+
error: Warning[];
|
|
882
|
+
warning: Warning[];
|
|
883
|
+
info: Warning[];
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Parse a time string in HH:MM or H:MM format
|
|
888
|
+
*
|
|
889
|
+
* @param timeStr - Time string to parse (e.g., "17:00", "9:00")
|
|
890
|
+
* @returns Object with hours and minutes, or null if invalid
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* parseTime("17:00") // => { hours: 17, minutes: 0 }
|
|
894
|
+
* parseTime("9:30") // => { hours: 9, minutes: 30 }
|
|
895
|
+
* parseTime("25:00") // => null (invalid hour)
|
|
896
|
+
*/
|
|
897
|
+
declare function parseTime(timeStr: string): {
|
|
898
|
+
hours: number;
|
|
899
|
+
minutes: number;
|
|
900
|
+
} | null;
|
|
815
901
|
/**
|
|
816
|
-
* Parse an absolute date string in various formats
|
|
902
|
+
* Parse an absolute date string in various formats, with optional time
|
|
817
903
|
*
|
|
818
904
|
* Supported formats:
|
|
819
905
|
* - ISO: 2026-01-25
|
|
@@ -821,9 +907,14 @@ declare namespace index {
|
|
|
821
907
|
* - US full: 1/25/2026
|
|
822
908
|
*
|
|
823
909
|
* @param dateStr - Date string to parse
|
|
910
|
+
* @param timeStr - Optional time string in HH:MM or H:MM format
|
|
824
911
|
* @returns Parsed Date object or null if invalid
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* parseAbsoluteDate("2026-01-25") // => Date at midnight
|
|
915
|
+
* parseAbsoluteDate("2026-01-25", "17:00") // => Date at 5 PM
|
|
825
916
|
*/
|
|
826
|
-
declare function parseAbsoluteDate(dateStr: string): Date | null;
|
|
917
|
+
declare function parseAbsoluteDate(dateStr: string, timeStr?: string): Date | null;
|
|
827
918
|
/**
|
|
828
919
|
* Resolve a relative date keyword against a base date
|
|
829
920
|
*
|
|
@@ -876,4 +967,4 @@ declare function extractDateFromHeading(line: string): Date | null;
|
|
|
876
967
|
*/
|
|
877
968
|
declare function generateTaskId(file: string, line: number, text: string): string;
|
|
878
969
|
|
|
879
|
-
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, index$1 as filters, generateTaskId, parseAbsoluteDate, parseTask, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
|
970
|
+
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, parseTime, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ interface ParsingContext {
|
|
|
21
21
|
person?: string;
|
|
22
22
|
currentDate?: Date;
|
|
23
23
|
currentHeading?: string;
|
|
24
|
+
workdayStartTime?: string;
|
|
25
|
+
workdayEndTime?: string;
|
|
26
|
+
defaultDueTime?: 'start' | 'end';
|
|
24
27
|
}
|
|
25
28
|
interface TaskFilterCriteria {
|
|
26
29
|
assignee?: string | string[];
|
|
@@ -47,11 +50,20 @@ interface ScanResult {
|
|
|
47
50
|
parseErrors: number;
|
|
48
51
|
};
|
|
49
52
|
}
|
|
53
|
+
type WarningSeverity = 'info' | 'warning' | 'error';
|
|
54
|
+
type WarningCode = 'unsupported-bullet' | 'malformed-checkbox' | 'missing-space-after' | 'missing-space-before' | 'relative-date-no-context' | 'missing-due-date' | 'missing-completed-date' | 'duplicate-todoist-id' | 'file-read-error';
|
|
50
55
|
interface Warning {
|
|
51
56
|
file: string;
|
|
52
57
|
line: number;
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
column?: number;
|
|
59
|
+
severity: WarningSeverity;
|
|
60
|
+
source: 'md2do';
|
|
61
|
+
ruleId: WarningCode;
|
|
62
|
+
message: string;
|
|
63
|
+
text?: string;
|
|
64
|
+
url?: string;
|
|
65
|
+
/** @deprecated Use message instead */
|
|
66
|
+
reason?: string;
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
/**
|
|
@@ -113,10 +125,10 @@ declare function extractCompletedDate(text: string): Date | undefined;
|
|
|
113
125
|
* Extract due date from task text with context awareness
|
|
114
126
|
*
|
|
115
127
|
* Handles both absolute dates ([due: 2026-01-25]) and relative dates
|
|
116
|
-
* ([due: tomorrow]) which require context.
|
|
128
|
+
* ([due: tomorrow]) which require context. Supports optional time ([due: 2026-01-25 17:00]).
|
|
117
129
|
*
|
|
118
130
|
* @param text - Task text
|
|
119
|
-
* @param context - Parsing context (for relative dates)
|
|
131
|
+
* @param context - Parsing context (for relative dates and workday config)
|
|
120
132
|
* @returns Object with parsed date and optional warning
|
|
121
133
|
*/
|
|
122
134
|
declare function extractDueDate(text: string, context: ParsingContext): {
|
|
@@ -223,15 +235,17 @@ declare const PRIORITY_HIGH: RegExp;
|
|
|
223
235
|
*/
|
|
224
236
|
declare const PRIORITY_NORMAL: RegExp;
|
|
225
237
|
/**
|
|
226
|
-
* Matches absolute due date in ISO format [due: YYYY-MM-DD]
|
|
238
|
+
* Matches absolute due date in ISO format [due: YYYY-MM-DD] with optional time [due: YYYY-MM-DD HH:MM]
|
|
227
239
|
*
|
|
228
240
|
* Examples:
|
|
229
|
-
* "[due: 2026-01-25]" → "2026-01-25"
|
|
230
|
-
* "[due:2026-01-25]" → "2026-01-25"
|
|
231
|
-
* "[due:
|
|
241
|
+
* "[due: 2026-01-25]" → "2026-01-25", undefined
|
|
242
|
+
* "[due: 2026-01-25 17:00]" → "2026-01-25", "17:00"
|
|
243
|
+
* "[due: 2026-01-25 9:00]" → "2026-01-25", "9:00"
|
|
244
|
+
* "[due:2026-01-25]" → "2026-01-25", undefined (spaces optional)
|
|
232
245
|
*
|
|
233
246
|
* Groups:
|
|
234
247
|
* [1] - Date string in YYYY-MM-DD format
|
|
248
|
+
* [2] - Optional time string in H:MM or HH:MM format (24-hour)
|
|
235
249
|
*/
|
|
236
250
|
declare const DUE_DATE_ABSOLUTE: RegExp;
|
|
237
251
|
/**
|
|
@@ -391,9 +405,14 @@ declare class MarkdownScanner {
|
|
|
391
405
|
*
|
|
392
406
|
* @param filePath - Relative file path (for context extraction)
|
|
393
407
|
* @param content - File content as string
|
|
408
|
+
* @param options - Optional scanner options including workday config
|
|
394
409
|
* @returns Object containing tasks and warnings
|
|
395
410
|
*/
|
|
396
|
-
scanFile(filePath: string, content: string
|
|
411
|
+
scanFile(filePath: string, content: string, options?: {
|
|
412
|
+
workdayStartTime?: string;
|
|
413
|
+
workdayEndTime?: string;
|
|
414
|
+
defaultDueTime?: 'start' | 'end';
|
|
415
|
+
}): {
|
|
397
416
|
tasks: Task[];
|
|
398
417
|
warnings: Warning[];
|
|
399
418
|
};
|
|
@@ -812,8 +831,75 @@ declare namespace index {
|
|
|
812
831
|
export { type index_TaskComparator as TaskComparator, index_byAssignee as byAssignee, index_byCompletionStatus as byCompletionStatus, index_byCreatedDate as byCreatedDate, index_byDueDate as byDueDate, index_byFile as byFile, index_byPerson as byPerson, index_byPriority as byPriority, index_byProject as byProject, index_combineComparators as combineComparators, index_reverse as reverse };
|
|
813
832
|
}
|
|
814
833
|
|
|
834
|
+
interface WarningFilterConfig {
|
|
835
|
+
enabled?: boolean | undefined;
|
|
836
|
+
rules?: Record<string, 'error' | 'warn' | 'info' | 'off'> | undefined;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Filter warnings based on configuration rules
|
|
840
|
+
*
|
|
841
|
+
* This function applies warning configuration rules to filter out disabled warnings
|
|
842
|
+
* and optionally all warnings if globally disabled.
|
|
843
|
+
*
|
|
844
|
+
* @param warnings - Array of warnings to filter
|
|
845
|
+
* @param config - Warning configuration with enabled flag and rule overrides
|
|
846
|
+
* @returns Filtered array of warnings
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* const config = {
|
|
851
|
+
* enabled: true,
|
|
852
|
+
* rules: {
|
|
853
|
+
* 'missing-due-date': 'off',
|
|
854
|
+
* 'duplicate-todoist-id': 'error',
|
|
855
|
+
* },
|
|
856
|
+
* };
|
|
857
|
+
*
|
|
858
|
+
* const filtered = filterWarnings(allWarnings, config);
|
|
859
|
+
* // Returns warnings except those with ruleId 'missing-due-date'
|
|
860
|
+
* ```
|
|
861
|
+
*/
|
|
862
|
+
declare function filterWarnings(warnings: Warning[], config?: WarningFilterConfig): Warning[];
|
|
863
|
+
/**
|
|
864
|
+
* Group warnings by severity
|
|
865
|
+
*
|
|
866
|
+
* Useful for displaying warnings in order of importance or
|
|
867
|
+
* treating errors differently from warnings.
|
|
868
|
+
*
|
|
869
|
+
* @param warnings - Array of warnings to group
|
|
870
|
+
* @returns Object with warnings grouped by severity level
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```typescript
|
|
874
|
+
* const grouped = groupWarningsBySeverity(warnings);
|
|
875
|
+
* console.log(`${grouped.error.length} errors`);
|
|
876
|
+
* console.log(`${grouped.warning.length} warnings`);
|
|
877
|
+
* console.log(`${grouped.info.length} info messages`);
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
declare function groupWarningsBySeverity(warnings: Warning[]): {
|
|
881
|
+
error: Warning[];
|
|
882
|
+
warning: Warning[];
|
|
883
|
+
info: Warning[];
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Parse a time string in HH:MM or H:MM format
|
|
888
|
+
*
|
|
889
|
+
* @param timeStr - Time string to parse (e.g., "17:00", "9:00")
|
|
890
|
+
* @returns Object with hours and minutes, or null if invalid
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* parseTime("17:00") // => { hours: 17, minutes: 0 }
|
|
894
|
+
* parseTime("9:30") // => { hours: 9, minutes: 30 }
|
|
895
|
+
* parseTime("25:00") // => null (invalid hour)
|
|
896
|
+
*/
|
|
897
|
+
declare function parseTime(timeStr: string): {
|
|
898
|
+
hours: number;
|
|
899
|
+
minutes: number;
|
|
900
|
+
} | null;
|
|
815
901
|
/**
|
|
816
|
-
* Parse an absolute date string in various formats
|
|
902
|
+
* Parse an absolute date string in various formats, with optional time
|
|
817
903
|
*
|
|
818
904
|
* Supported formats:
|
|
819
905
|
* - ISO: 2026-01-25
|
|
@@ -821,9 +907,14 @@ declare namespace index {
|
|
|
821
907
|
* - US full: 1/25/2026
|
|
822
908
|
*
|
|
823
909
|
* @param dateStr - Date string to parse
|
|
910
|
+
* @param timeStr - Optional time string in HH:MM or H:MM format
|
|
824
911
|
* @returns Parsed Date object or null if invalid
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* parseAbsoluteDate("2026-01-25") // => Date at midnight
|
|
915
|
+
* parseAbsoluteDate("2026-01-25", "17:00") // => Date at 5 PM
|
|
825
916
|
*/
|
|
826
|
-
declare function parseAbsoluteDate(dateStr: string): Date | null;
|
|
917
|
+
declare function parseAbsoluteDate(dateStr: string, timeStr?: string): Date | null;
|
|
827
918
|
/**
|
|
828
919
|
* Resolve a relative date keyword against a base date
|
|
829
920
|
*
|
|
@@ -876,4 +967,4 @@ declare function extractDateFromHeading(line: string): Date | null;
|
|
|
876
967
|
*/
|
|
877
968
|
declare function generateTaskId(file: string, line: number, text: string): string;
|
|
878
969
|
|
|
879
|
-
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, index$1 as filters, generateTaskId, parseAbsoluteDate, parseTask, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
|
970
|
+
export { ASSIGNEE, COMPLETED_DATE, DUE_DATE_ABSOLUTE, DUE_DATE_RELATIVE, DUE_DATE_SHORT, HEADING_DATE_ISO, HEADING_DATE_NATURAL, HEADING_DATE_SLASH, MarkdownScanner, PATTERNS, PRIORITY_HIGH, PRIORITY_NORMAL, PRIORITY_URGENT, type ParsingContext, type Priority, type ScanResult, TAG, TASK_CHECKBOX, TODOIST_ID, type Task, type TaskFilterCriteria, type UpdateTaskOptions, type Warning, type WarningCode, type WarningFilterConfig, type WarningSeverity, type WriteTaskResult, addTask, cleanTaskText, extractAssignee, extractCompletedDate, extractDateFromHeading, extractDueDate, extractPersonFromFilename, extractPriority, extractProjectFromPath, extractTags, extractTodoistId, filterWarnings, index$1 as filters, generateTaskId, groupWarningsBySeverity, parseAbsoluteDate, parseTask, parseTime, resolveRelativeDate, index as sorting, updateTask, updateTasks };
|
package/dist/index.js
CHANGED
|
@@ -57,10 +57,13 @@ __export(index_exports, {
|
|
|
57
57
|
extractProjectFromPath: () => extractProjectFromPath,
|
|
58
58
|
extractTags: () => extractTags,
|
|
59
59
|
extractTodoistId: () => extractTodoistId,
|
|
60
|
+
filterWarnings: () => filterWarnings,
|
|
60
61
|
filters: () => filters_exports,
|
|
61
62
|
generateTaskId: () => generateTaskId,
|
|
63
|
+
groupWarningsBySeverity: () => groupWarningsBySeverity,
|
|
62
64
|
parseAbsoluteDate: () => parseAbsoluteDate,
|
|
63
65
|
parseTask: () => parseTask,
|
|
66
|
+
parseTime: () => parseTime,
|
|
64
67
|
resolveRelativeDate: () => resolveRelativeDate,
|
|
65
68
|
sorting: () => sorting_exports,
|
|
66
69
|
updateTask: () => updateTask,
|
|
@@ -74,7 +77,7 @@ var ASSIGNEE = /@([\w-]+)/;
|
|
|
74
77
|
var PRIORITY_URGENT = /!!!/;
|
|
75
78
|
var PRIORITY_HIGH = /!!/;
|
|
76
79
|
var PRIORITY_NORMAL = /(?<!!)!(?!!)/;
|
|
77
|
-
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})\s*\]/i;
|
|
80
|
+
var DUE_DATE_ABSOLUTE = /\[due:\s*(\d{4}-\d{2}-\d{2})(?:\s+(\d{1,2}:\d{2}))?\s*\]/i;
|
|
78
81
|
var DUE_DATE_RELATIVE = /\[due:\s*(tomorrow|today|next\s+week|next\s+month)\]/i;
|
|
79
82
|
var DUE_DATE_SHORT = /\[due:\s*(\d{1,2}\/\d{1,2}(?:\/\d{2,4})?)\]/i;
|
|
80
83
|
var TAG = /#([\w-]+)/g;
|
|
@@ -102,15 +105,34 @@ var PATTERNS = {
|
|
|
102
105
|
|
|
103
106
|
// src/utils/dates.ts
|
|
104
107
|
var import_date_fns = require("date-fns");
|
|
105
|
-
function
|
|
108
|
+
function parseTime(timeStr) {
|
|
109
|
+
const match = timeStr.match(/^(\d{1,2}):(\d{2})$/);
|
|
110
|
+
if (!match || !match[1] || !match[2]) return null;
|
|
111
|
+
const hours = parseInt(match[1], 10);
|
|
112
|
+
const minutes = parseInt(match[2], 10);
|
|
113
|
+
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return { hours, minutes };
|
|
117
|
+
}
|
|
118
|
+
function parseAbsoluteDate(dateStr, timeStr) {
|
|
106
119
|
const referenceDate = /* @__PURE__ */ new Date();
|
|
107
120
|
let date = (0, import_date_fns.parse)(dateStr, "yyyy-MM-dd", referenceDate);
|
|
108
|
-
if ((0, import_date_fns.isValid)(date))
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
if (!(0, import_date_fns.isValid)(date)) {
|
|
122
|
+
date = (0, import_date_fns.parse)(dateStr, "M/d/yy", referenceDate);
|
|
123
|
+
}
|
|
124
|
+
if (!(0, import_date_fns.isValid)(date)) {
|
|
125
|
+
date = (0, import_date_fns.parse)(dateStr, "M/d/yyyy", referenceDate);
|
|
126
|
+
}
|
|
127
|
+
if (!(0, import_date_fns.isValid)(date)) return null;
|
|
128
|
+
if (timeStr) {
|
|
129
|
+
const time = parseTime(timeStr);
|
|
130
|
+
if (time) {
|
|
131
|
+
date = (0, import_date_fns.setHours)(date, time.hours);
|
|
132
|
+
date = (0, import_date_fns.setMinutes)(date, time.minutes);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return date;
|
|
114
136
|
}
|
|
115
137
|
function resolveRelativeDate(relative, baseDate) {
|
|
116
138
|
const normalized = relative.toLowerCase().trim();
|
|
@@ -202,12 +224,29 @@ function extractCompletedDate(text) {
|
|
|
202
224
|
function extractDueDate(text, context) {
|
|
203
225
|
const absoluteMatch = text.match(PATTERNS.DUE_DATE_ABSOLUTE);
|
|
204
226
|
if (absoluteMatch?.[1]) {
|
|
205
|
-
const
|
|
227
|
+
const dateStr = absoluteMatch[1];
|
|
228
|
+
let timeStr = absoluteMatch[2];
|
|
229
|
+
if (!timeStr && context.defaultDueTime) {
|
|
230
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
231
|
+
timeStr = context.workdayStartTime;
|
|
232
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
233
|
+
timeStr = context.workdayEndTime;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const date = parseAbsoluteDate(dateStr, timeStr);
|
|
206
237
|
return { date: date ?? void 0 };
|
|
207
238
|
}
|
|
208
239
|
const shortMatch = text.match(PATTERNS.DUE_DATE_SHORT);
|
|
209
240
|
if (shortMatch?.[1]) {
|
|
210
|
-
|
|
241
|
+
let timeStr;
|
|
242
|
+
if (context.defaultDueTime) {
|
|
243
|
+
if (context.defaultDueTime === "start" && context.workdayStartTime) {
|
|
244
|
+
timeStr = context.workdayStartTime;
|
|
245
|
+
} else if (context.defaultDueTime === "end" && context.workdayEndTime) {
|
|
246
|
+
timeStr = context.workdayEndTime;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const date = parseAbsoluteDate(shortMatch[1], timeStr);
|
|
211
250
|
return { date: date ?? void 0 };
|
|
212
251
|
}
|
|
213
252
|
const relativeMatch = text.match(PATTERNS.DUE_DATE_RELATIVE);
|
|
@@ -216,11 +255,15 @@ function extractDueDate(text, context) {
|
|
|
216
255
|
return {
|
|
217
256
|
date: void 0,
|
|
218
257
|
warning: {
|
|
258
|
+
severity: "warning",
|
|
259
|
+
source: "md2do",
|
|
260
|
+
ruleId: "relative-date-no-context",
|
|
219
261
|
file: "",
|
|
220
262
|
// Will be filled in by caller
|
|
221
263
|
line: 0,
|
|
222
264
|
// Will be filled in by caller
|
|
223
265
|
text: text.trim(),
|
|
266
|
+
message: "Relative due date without context date from heading. Add a heading with a date above this task.",
|
|
224
267
|
reason: "Relative due date without context date from heading. Add a heading with a date above this task."
|
|
225
268
|
}
|
|
226
269
|
};
|
|
@@ -237,36 +280,52 @@ function parseTask(line, lineNumber, file, context) {
|
|
|
237
280
|
const warnings = [];
|
|
238
281
|
if (/^\s*[*+]\s+\[[ xX]\]/.test(line)) {
|
|
239
282
|
warnings.push({
|
|
283
|
+
severity: "warning",
|
|
284
|
+
source: "md2do",
|
|
285
|
+
ruleId: "unsupported-bullet",
|
|
240
286
|
file,
|
|
241
287
|
line: lineNumber,
|
|
242
288
|
text: line.trim(),
|
|
289
|
+
message: "Unsupported bullet marker (* or +). Use dash (-) for task lists.",
|
|
243
290
|
reason: "Unsupported bullet marker (* or +). Use dash (-) for task lists."
|
|
244
291
|
});
|
|
245
292
|
return { task: null, warnings };
|
|
246
293
|
}
|
|
247
294
|
if (/^\s*-\s+\[[xX]\s+\]/.test(line) || /^\s*-\s+\[\s+[xX]\]/.test(line)) {
|
|
248
295
|
warnings.push({
|
|
296
|
+
severity: "warning",
|
|
297
|
+
source: "md2do",
|
|
298
|
+
ruleId: "malformed-checkbox",
|
|
249
299
|
file,
|
|
250
300
|
line: lineNumber,
|
|
251
301
|
text: line.trim(),
|
|
302
|
+
message: "Malformed checkbox with extra spaces. Use [x] or [ ] without extra spaces.",
|
|
252
303
|
reason: "Malformed checkbox with extra spaces. Use [x] or [ ] without extra spaces."
|
|
253
304
|
});
|
|
254
305
|
return { task: null, warnings };
|
|
255
306
|
}
|
|
256
307
|
if (/^\s*-\s+\[[ xX]\][^\s]/.test(line)) {
|
|
257
308
|
warnings.push({
|
|
309
|
+
severity: "warning",
|
|
310
|
+
source: "md2do",
|
|
311
|
+
ruleId: "missing-space-after",
|
|
258
312
|
file,
|
|
259
313
|
line: lineNumber,
|
|
260
314
|
text: line.trim(),
|
|
315
|
+
message: 'Missing space after checkbox. Use "- [x] Task" format.',
|
|
261
316
|
reason: 'Missing space after checkbox. Use "- [x] Task" format.'
|
|
262
317
|
});
|
|
263
318
|
return { task: null, warnings };
|
|
264
319
|
}
|
|
265
320
|
if (/^\s*-\[[ xX]\]/.test(line)) {
|
|
266
321
|
warnings.push({
|
|
322
|
+
severity: "warning",
|
|
323
|
+
source: "md2do",
|
|
324
|
+
ruleId: "missing-space-before",
|
|
267
325
|
file,
|
|
268
326
|
line: lineNumber,
|
|
269
327
|
text: line.trim(),
|
|
328
|
+
message: 'Missing space before checkbox. Use "- [x] Task" format.',
|
|
270
329
|
reason: 'Missing space before checkbox. Use "- [x] Task" format.'
|
|
271
330
|
});
|
|
272
331
|
return { task: null, warnings };
|
|
@@ -292,17 +351,25 @@ function parseTask(line, lineNumber, file, context) {
|
|
|
292
351
|
}
|
|
293
352
|
if (!completed && !dueDateResult.date && !context.currentDate) {
|
|
294
353
|
warnings.push({
|
|
354
|
+
severity: "info",
|
|
355
|
+
source: "md2do",
|
|
356
|
+
ruleId: "missing-due-date",
|
|
295
357
|
file,
|
|
296
358
|
line: lineNumber,
|
|
297
359
|
text: fullText.trim(),
|
|
360
|
+
message: "Task has no due date. Add [due: YYYY-MM-DD] or place under a heading with a date.",
|
|
298
361
|
reason: "Task has no due date. Add [due: YYYY-MM-DD] or place under a heading with a date."
|
|
299
362
|
});
|
|
300
363
|
}
|
|
301
364
|
if (completed && !completedDate) {
|
|
302
365
|
warnings.push({
|
|
366
|
+
severity: "info",
|
|
367
|
+
source: "md2do",
|
|
368
|
+
ruleId: "missing-completed-date",
|
|
303
369
|
file,
|
|
304
370
|
line: lineNumber,
|
|
305
371
|
text: fullText.trim(),
|
|
372
|
+
message: "Completed task missing completion date. Add [completed: YYYY-MM-DD].",
|
|
306
373
|
reason: "Completed task missing completion date. Add [completed: YYYY-MM-DD]."
|
|
307
374
|
});
|
|
308
375
|
}
|
|
@@ -346,9 +413,10 @@ var MarkdownScanner = class {
|
|
|
346
413
|
*
|
|
347
414
|
* @param filePath - Relative file path (for context extraction)
|
|
348
415
|
* @param content - File content as string
|
|
416
|
+
* @param options - Optional scanner options including workday config
|
|
349
417
|
* @returns Object containing tasks and warnings
|
|
350
418
|
*/
|
|
351
|
-
scanFile(filePath, content) {
|
|
419
|
+
scanFile(filePath, content, options) {
|
|
352
420
|
const tasks = [];
|
|
353
421
|
const warnings = [];
|
|
354
422
|
const todoistIds = /* @__PURE__ */ new Map();
|
|
@@ -357,6 +425,15 @@ var MarkdownScanner = class {
|
|
|
357
425
|
if (project !== void 0) context.project = project;
|
|
358
426
|
const person = extractPersonFromFilename(filePath);
|
|
359
427
|
if (person !== void 0) context.person = person;
|
|
428
|
+
if (options?.workdayStartTime) {
|
|
429
|
+
context.workdayStartTime = options.workdayStartTime;
|
|
430
|
+
}
|
|
431
|
+
if (options?.workdayEndTime) {
|
|
432
|
+
context.workdayEndTime = options.workdayEndTime;
|
|
433
|
+
}
|
|
434
|
+
if (options?.defaultDueTime) {
|
|
435
|
+
context.defaultDueTime = options.defaultDueTime;
|
|
436
|
+
}
|
|
360
437
|
const lines = content.split("\n");
|
|
361
438
|
for (let i = 0; i < lines.length; i++) {
|
|
362
439
|
const line = lines[i];
|
|
@@ -375,9 +452,13 @@ var MarkdownScanner = class {
|
|
|
375
452
|
const existing = todoistIds.get(result.task.todoistId);
|
|
376
453
|
if (existing) {
|
|
377
454
|
warnings.push({
|
|
455
|
+
severity: "error",
|
|
456
|
+
source: "md2do",
|
|
457
|
+
ruleId: "duplicate-todoist-id",
|
|
378
458
|
file: filePath,
|
|
379
459
|
line: lineNumber,
|
|
380
460
|
text: result.task.text,
|
|
461
|
+
message: `Duplicate Todoist ID [todoist:${result.task.todoistId}]. Also found at ${existing.file}:${existing.line}.`,
|
|
381
462
|
reason: `Duplicate Todoist ID [todoist:${result.task.todoistId}]. Also found at ${existing.file}:${existing.line}.`
|
|
382
463
|
});
|
|
383
464
|
} else {
|
|
@@ -416,9 +497,13 @@ var MarkdownScanner = class {
|
|
|
416
497
|
const existing = todoistIds.get(task.todoistId);
|
|
417
498
|
if (existing && existing.file !== task.file) {
|
|
418
499
|
allWarnings.push({
|
|
500
|
+
severity: "error",
|
|
501
|
+
source: "md2do",
|
|
502
|
+
ruleId: "duplicate-todoist-id",
|
|
419
503
|
file: task.file,
|
|
420
504
|
line: task.line,
|
|
421
505
|
text: task.text,
|
|
506
|
+
message: `Duplicate Todoist ID [todoist:${task.todoistId}] across files. Also found at ${existing.file}:${existing.line}.`,
|
|
422
507
|
reason: `Duplicate Todoist ID [todoist:${task.todoistId}] across files. Also found at ${existing.file}:${existing.line}.`
|
|
423
508
|
});
|
|
424
509
|
} else if (!existing) {
|
|
@@ -867,6 +952,32 @@ function combineComparators(comparators) {
|
|
|
867
952
|
function reverse(comparator) {
|
|
868
953
|
return (a, b) => -comparator(a, b);
|
|
869
954
|
}
|
|
955
|
+
|
|
956
|
+
// src/warnings/filter.ts
|
|
957
|
+
function filterWarnings(warnings, config = {}) {
|
|
958
|
+
if (config.enabled === false) {
|
|
959
|
+
return [];
|
|
960
|
+
}
|
|
961
|
+
if (!config.rules) {
|
|
962
|
+
return warnings;
|
|
963
|
+
}
|
|
964
|
+
return warnings.filter((warning) => {
|
|
965
|
+
const level = config.rules?.[warning.ruleId];
|
|
966
|
+
if (level === void 0) {
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
return level !== "off";
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
function groupWarningsBySeverity(warnings) {
|
|
973
|
+
return warnings.reduce(
|
|
974
|
+
(acc, warning) => {
|
|
975
|
+
acc[warning.severity].push(warning);
|
|
976
|
+
return acc;
|
|
977
|
+
},
|
|
978
|
+
{ error: [], warning: [], info: [] }
|
|
979
|
+
);
|
|
980
|
+
}
|
|
870
981
|
// Annotate the CommonJS export names for ESM import in node:
|
|
871
982
|
0 && (module.exports = {
|
|
872
983
|
ASSIGNEE,
|
|
@@ -896,10 +1007,13 @@ function reverse(comparator) {
|
|
|
896
1007
|
extractProjectFromPath,
|
|
897
1008
|
extractTags,
|
|
898
1009
|
extractTodoistId,
|
|
1010
|
+
filterWarnings,
|
|
899
1011
|
filters,
|
|
900
1012
|
generateTaskId,
|
|
1013
|
+
groupWarningsBySeverity,
|
|
901
1014
|
parseAbsoluteDate,
|
|
902
1015
|
parseTask,
|
|
1016
|
+
parseTime,
|
|
903
1017
|
resolveRelativeDate,
|
|
904
1018
|
sorting,
|
|
905
1019
|
updateTask,
|