imdone-core 2.0.0 → 2.0.1

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.
Files changed (160) hide show
  1. package/.dependency-cruiser.cjs +412 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  3. package/.github/workflows/ci.yml +24 -0
  4. package/.imdoneignore +13 -0
  5. package/.mocharc.json +10 -0
  6. package/.prettierrc +6 -0
  7. package/CHANGELOG.md +160 -0
  8. package/_index.md +7 -0
  9. package/_navbar.md +1 -0
  10. package/build.js +57 -0
  11. package/esm-migration.md +446 -0
  12. package/index.html +184 -0
  13. package/lib/FileProjectContext.js +13 -0
  14. package/lib/ProjectContext.js +43 -0
  15. package/lib/adapters/date-time.js +15 -0
  16. package/lib/adapters/environment.js +3 -0
  17. package/lib/adapters/file-gateway.js +154 -0
  18. package/lib/adapters/git-download.js +55 -0
  19. package/lib/adapters/index.js +9 -0
  20. package/lib/adapters/logger.js +136 -0
  21. package/lib/adapters/markdown.js +180 -0
  22. package/lib/adapters/parsers/DateLanguageParser.js +12 -0
  23. package/lib/adapters/parsers/content-transformer.js +116 -0
  24. package/lib/adapters/parsers/file/LocalFileParserFactory.js +36 -0
  25. package/lib/adapters/parsers/file/ReadStreamCodeParser.js +126 -0
  26. package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +17 -0
  27. package/lib/adapters/parsers/file/ReadStreamTaskParser.js +106 -0
  28. package/lib/adapters/parsers/file/index.js +4 -0
  29. package/lib/adapters/parsers/function-parser.js +25 -0
  30. package/lib/adapters/parsers/index.js +5 -0
  31. package/lib/adapters/parsers/task/CardContentParser.js +367 -0
  32. package/lib/adapters/parsers/task/CheckStyleTaskParser.js +28 -0
  33. package/lib/adapters/parsers/task/CodeStyleTaskParser.js +24 -0
  34. package/lib/adapters/parsers/task/HashStyleTaskParser.js +27 -0
  35. package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +24 -0
  36. package/lib/adapters/parsers/task/TaskParser.js +38 -0
  37. package/lib/adapters/parsers/task/index.js +6 -0
  38. package/lib/adapters/storage/config.js +44 -0
  39. package/lib/adapters/storage/index.js +2 -0
  40. package/lib/adapters/storage/tags.js +29 -0
  41. package/lib/adapters/yaml.js +9 -0
  42. package/lib/card.js +299 -0
  43. package/lib/checksum.js +22 -0
  44. package/lib/config.js +249 -0
  45. package/lib/constants.js +68 -0
  46. package/lib/context/ApplicationContext.js +35 -0
  47. package/lib/context/index.js +1 -0
  48. package/lib/default-settings.js +37 -0
  49. package/lib/domain/entities/View.js +23 -0
  50. package/lib/domain/entities/index.js +1 -0
  51. package/lib/domain/index.js +2 -0
  52. package/lib/domain/usecases/GetViewsUsecase.js +44 -0
  53. package/lib/domain/usecases/SaveViewUsecase.js +51 -0
  54. package/lib/domain/usecases/index.js +2 -0
  55. package/lib/file.js +1391 -0
  56. package/lib/get-tasks-to-modify.js +117 -0
  57. package/lib/languages.js +113 -0
  58. package/lib/list.js +56 -0
  59. package/lib/migrate-config.js +165 -0
  60. package/lib/mixins/index.js +2 -0
  61. package/lib/mixins/repo-fs-store.js +359 -0
  62. package/lib/mixins/repo-watched-fs-store.js +206 -0
  63. package/lib/plugins/archive-plugin.js +72 -0
  64. package/lib/plugins/default-board-actions-plugin.js +56 -0
  65. package/lib/plugins/default-board-properties-plugin.js +101 -0
  66. package/lib/plugins/default-plugins.js +7 -0
  67. package/lib/plugins/epic-plugin.js +177 -0
  68. package/lib/plugins/extension-plugin.js +94 -0
  69. package/lib/plugins/index.js +9 -0
  70. package/lib/plugins/persist-tags-plugin.js +14 -0
  71. package/lib/plugins/plugin-manager.js +458 -0
  72. package/lib/plugins/plugin-registry.js +57 -0
  73. package/lib/project-factory.js +42 -0
  74. package/lib/project.js +864 -0
  75. package/lib/repository.js +1514 -0
  76. package/lib/task.js +780 -0
  77. package/lib/tools.js +176 -0
  78. package/lib/usecases/actions.js +32 -0
  79. package/lib/usecases/get-imdone-url.js +6 -0
  80. package/lib/usecases/get-project-tags.js +7 -0
  81. package/lib/usecases/get-tasks-in-file.js +26 -0
  82. package/lib/usecases/index.js +4 -0
  83. package/package.json +2 -6
  84. package/scripts/delete-markdown-indexes.js +87 -0
  85. package/scripts/generate-index.js +35 -0
  86. package/scripts/index-markdown.js +55 -0
  87. package/tsconfig.json +15 -0
  88. package/types/FileProjectContext.d.ts +4 -0
  89. package/types/ProjectContext.d.ts +9 -0
  90. package/types/adapters/date-time.d.ts +2 -0
  91. package/types/adapters/environment.d.ts +1 -0
  92. package/types/adapters/file-gateway.d.ts +51 -0
  93. package/types/adapters/git-download.d.ts +1 -0
  94. package/types/adapters/index.d.ts +9 -0
  95. package/types/adapters/logger.d.ts +24 -0
  96. package/types/adapters/markdown.d.ts +13 -0
  97. package/types/adapters/parsers/DateLanguageParser.d.ts +1 -0
  98. package/types/adapters/parsers/content-transformer.d.ts +10 -0
  99. package/types/adapters/parsers/file/LocalFileParserFactory.d.ts +5 -0
  100. package/types/adapters/parsers/file/ReadStreamCodeParser.d.ts +13 -0
  101. package/types/adapters/parsers/file/ReadStreamMarkdownParser.d.ts +7 -0
  102. package/types/adapters/parsers/file/ReadStreamTaskParser.d.ts +19 -0
  103. package/types/adapters/parsers/file/index.d.ts +4 -0
  104. package/types/adapters/parsers/function-parser.d.ts +1 -0
  105. package/types/adapters/parsers/index.d.ts +5 -0
  106. package/types/adapters/parsers/task/CardContentParser.d.ts +45 -0
  107. package/types/adapters/parsers/task/CheckStyleTaskParser.d.ts +12 -0
  108. package/types/adapters/parsers/task/CodeStyleTaskParser.d.ts +14 -0
  109. package/types/adapters/parsers/task/HashStyleTaskParser.d.ts +13 -0
  110. package/types/adapters/parsers/task/MarkdownStyleTaskParser.d.ts +14 -0
  111. package/types/adapters/parsers/task/TaskParser.d.ts +7 -0
  112. package/types/adapters/parsers/task/index.d.ts +6 -0
  113. package/types/adapters/storage/config.d.ts +6 -0
  114. package/types/adapters/storage/index.d.ts +2 -0
  115. package/types/adapters/storage/tags.d.ts +3 -0
  116. package/types/adapters/yaml.d.ts +2 -0
  117. package/types/card.d.ts +166 -0
  118. package/types/checksum.d.ts +8 -0
  119. package/types/config.d.ts +61 -0
  120. package/types/constants.d.ts +46 -0
  121. package/types/context/ApplicationContext.d.ts +24 -0
  122. package/types/context/index.d.ts +1 -0
  123. package/types/default-settings.d.ts +2 -0
  124. package/types/domain/entities/FileProjectContext.d.ts +5 -0
  125. package/types/domain/entities/View.d.ts +17 -0
  126. package/types/domain/entities/index.d.ts +1 -0
  127. package/types/domain/index.d.ts +2 -0
  128. package/types/domain/usecases/GetViewsUsecase.d.ts +4 -0
  129. package/types/domain/usecases/SaveViewUsecase.d.ts +8 -0
  130. package/types/domain/usecases/index.d.ts +2 -0
  131. package/types/file.d.ts +344 -0
  132. package/types/get-tasks-to-modify.d.ts +3 -0
  133. package/types/index.d.ts +23 -0
  134. package/types/languages.d.ts +649 -0
  135. package/types/list.d.ts +30 -0
  136. package/types/migrate-config.d.ts +1 -0
  137. package/types/mixins/index.d.ts +2 -0
  138. package/types/mixins/repo-fs-store.d.ts +2 -0
  139. package/types/mixins/repo-watched-fs-store.d.ts +2 -0
  140. package/types/plugins/archive-plugin.d.ts +8 -0
  141. package/types/plugins/default-board-actions-plugin.d.ts +9 -0
  142. package/types/plugins/default-board-properties-plugin.d.ts +16 -0
  143. package/types/plugins/default-plugins.d.ts +6 -0
  144. package/types/plugins/epic-plugin.d.ts +19 -0
  145. package/types/plugins/extension-plugin.d.ts +15 -0
  146. package/types/plugins/index.d.ts +9 -0
  147. package/types/plugins/persist-tags-plugin.d.ts +4 -0
  148. package/types/plugins/plugin-manager.d.ts +68 -0
  149. package/types/plugins/plugin-registry.d.ts +1 -0
  150. package/types/project-factory.d.ts +14 -0
  151. package/types/project.d.ts +158 -0
  152. package/types/repository.d.ts +996 -0
  153. package/types/task.d.ts +202 -0
  154. package/types/tools.d.ts +29 -0
  155. package/types/usecases/actions.d.ts +7 -0
  156. package/types/usecases/get-imdone-url.d.ts +1 -0
  157. package/types/usecases/get-project-tags.d.ts +1 -0
  158. package/types/usecases/get-tasks-in-file.d.ts +4 -0
  159. package/types/usecases/index.d.ts +4 -0
  160. package/vitest.config.js +13 -0
@@ -0,0 +1,367 @@
1
+ import eol from 'eol'
2
+ import XRegExp from 'xregexp'
3
+
4
+ export const FILE_TYPES = {
5
+ MARKDOWN: 'markdown',
6
+ CODE: 'code',
7
+ }
8
+ export const TASK_TYPES = {
9
+ CODE: 'CODE',
10
+ HASHTAG: 'HASHTAG',
11
+ MARKDOWN: 'MARKDOWN',
12
+ }
13
+
14
+ const DEFAULT_TOKEN_PREFIX = '#'
15
+ const START_TAG = '<card>'
16
+ const END_TAG = '</card>'
17
+ const CODE_STYLE_PATTERN = '([A-Z]+[A-Z-_]+?)((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
18
+
19
+ export const LIST_NAME_PATTERN = '[\\p{L}0-9-_]{2,}' // [a-zA-Z-_]+?
20
+ export const LINK_STYLE_REGEX = new XRegExp(
21
+ `(\\[.*\\]\\s?)?(\\[(.+)\\]\\(#(${LIST_NAME_PATTERN})(:)(-?[\\d.]+(?:e-?\\d+)?)?\\))`,
22
+ 'gm'
23
+ )
24
+
25
+ export const CHECK_REGEX = /^(\s*- )\[([x ])\]/
26
+
27
+ export function getHashStyleRegex(tokenPrefix = DEFAULT_TOKEN_PREFIX, orderMeta = false) {
28
+ return orderMeta ?
29
+ new XRegExp(
30
+ `${tokenPrefix}(${LIST_NAME_PATTERN})[ \\t]+(.+)$`, 'gm'
31
+ ) :
32
+ new XRegExp(
33
+ `${tokenPrefix}(${LIST_NAME_PATTERN})(:(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$`,
34
+ 'gm'
35
+ )
36
+ }
37
+
38
+ export function getCheckedData(content) {
39
+ const taskRegex = new RegExp(CHECK_REGEX)
40
+ const result = taskRegex.exec(content)
41
+ return result
42
+ ? {
43
+ pad: result[1].length,
44
+ checked: result[2].trim() !== '',
45
+ }
46
+ : null
47
+ }
48
+
49
+ function getContentLines(content) {
50
+ let foundContent = false
51
+ return eol.split(content).filter((line) => {
52
+ const hasContent = line.trim()
53
+ if (!foundContent && hasContent) foundContent = true
54
+ return foundContent || hasContent
55
+ })
56
+ }
57
+
58
+ function isCustomCardTerminator(config, line) {
59
+ return config.customCardTerminator
60
+ && config.customCardTerminator.trim()
61
+ && line
62
+ && line.trim() === config.customCardTerminator
63
+ }
64
+
65
+ function isCheckBoxTask (config, line, beforeText) {
66
+ if (!config.isAddCheckBoxTasks()) return
67
+
68
+ const beforeTextCheckData = getCheckedData(beforeText)
69
+ if (!beforeTextCheckData) return
70
+
71
+ const lineCheckData = getCheckedData(line)
72
+ if (!lineCheckData) return
73
+
74
+ return lineCheckData.pad <= beforeTextCheckData.pad
75
+ }
76
+
77
+ export function hasTaskInText(config, text, isCodeFile) {
78
+ const hashStyleRegex = new XRegExp(getHashStyleRegex(config.tokenPrefix));
79
+ const linkStyleRegex = new XRegExp(LINK_STYLE_REGEX);
80
+
81
+ const inlineCodeRe = /`[^`]*`/g;
82
+
83
+ // Function to check if a match is within inline code
84
+ function isWithinInlineCode(index) {
85
+ let match;
86
+ while ((match = inlineCodeRe.exec(text)) !== null) {
87
+ const isInCode = index >= match.index && index < match.index + match[0].length
88
+ if (isInCode) return true;
89
+ }
90
+ return false;
91
+ }
92
+
93
+ // Check for code style tasks
94
+ if (isCodeFile && hasCodeStyleTask(config, text)) {
95
+ return true;
96
+ }
97
+
98
+ // Check for hash style tasks
99
+ let result;
100
+ if ((result = hashStyleRegex.exec(text)) !== null && !isWithinInlineCode(result.index)) {
101
+ return !!config.lists.find((list) => text.includes(`${config.tokenPrefix}${list.name}`));
102
+ }
103
+
104
+ // Check for link style tasks
105
+ if ((result = linkStyleRegex.exec(text)) !== null && !isWithinInlineCode(result.index)) {
106
+ return !!config.lists.find((list) => text.includes(`${config.tokenPrefix}${list.name}`));
107
+ }
108
+
109
+ return false;
110
+ }
111
+
112
+ export function isBeforeTextMarkdownList(beforeText) {
113
+ return /^\s*(\*|-|- \[[x| ]\]|\d+\.|[a-zA-Z]\.)\s$/.test(beforeText)
114
+ }
115
+
116
+ function isMarkdownListItem(line) {
117
+ return /^\s*(\*|-|- \[[x| ]\]|\d+\.|[a-zA-Z]\.)\s/.test(line)
118
+ }
119
+
120
+ export function padDescription(description = [], beforeText = '') {
121
+ if (!isBeforeTextMarkdownList(beforeText)) return description
122
+ return description.map((line) => {
123
+ return line.padStart(line.length + beforeText.length)
124
+ })
125
+ }
126
+
127
+ function isLineOutsideMarkdownList(line, beforeText) {
128
+ return isBeforeTextMarkdownList(beforeText) &&
129
+ isMarkdownListItem(line) &&
130
+ line.search(/\S/) <= beforeText.search(/\S/)
131
+ }
132
+
133
+ function hasCodeStyleTask(config, text) {
134
+ const codeStylePatternRe = new RegExp(CODE_STYLE_PATTERN);
135
+ let result = codeStylePatternRe.exec(text);
136
+
137
+ if (!result) return false;
138
+
139
+ // Check if the match is within inline code
140
+ const inlineCodeRe = /`[^`]*`/g;
141
+ let match;
142
+ while ((match = inlineCodeRe.exec(text)) !== null) {
143
+ if (result.index >= match.index && result.index < match.index + match[0].length) {
144
+ return false;
145
+ }
146
+ }
147
+
148
+ return config.includeList(result[1]);
149
+ }
150
+
151
+ function getCardTagsTaskContent ({
152
+ content,
153
+ fileType
154
+ }) {
155
+ const isCodeFile = fileType === FILE_TYPES.CODE
156
+ if (isCodeFile) return
157
+
158
+ const isMarkDownFile = fileType === FILE_TYPES.MARKDOWN
159
+ let lines = getContentLines(content)
160
+ const hasCardStartTag =
161
+ isMarkDownFile && lines.length > 1 && lines[1].trim() === START_TAG
162
+ if (!hasCardStartTag) return
163
+
164
+ let openBlockCommentTokens = 0
165
+ let closeBlockCommentTokens = 0
166
+ let hasCardEndTag = false
167
+ const rawTaskContentLines = []
168
+ const taskContentLines = []
169
+ for (let i = 1; i < lines.length; i++) {
170
+ let line = lines[i]
171
+ // Check for matching block comments in markdown files
172
+ if (isMarkDownFile && line.trim().indexOf('<!--') > -1)
173
+ openBlockCommentTokens++
174
+ if (isMarkDownFile && line.trim().indexOf('-->') > -1)
175
+ closeBlockCommentTokens++
176
+
177
+ rawTaskContentLines.push(line)
178
+
179
+ if (line.trim() === START_TAG) continue
180
+ if (line.trim() === END_TAG) {
181
+ hasCardEndTag = true
182
+ break
183
+ }
184
+ taskContentLines.push(line)
185
+ }
186
+
187
+ // Remove extra block comment tokens
188
+ if (
189
+ isMarkDownFile &&
190
+ rawTaskContentLines.length &&
191
+ rawTaskContentLines[rawTaskContentLines.length - 1].trim() === '-->' &&
192
+ openBlockCommentTokens !== closeBlockCommentTokens
193
+ )
194
+ rawTaskContentLines.pop()
195
+
196
+ const isWrappedWithCardTag = hasCardStartTag && hasCardEndTag
197
+
198
+ return {
199
+ rawTaskContentLines,
200
+ taskContentLines,
201
+ isWrappedWithCardTag,
202
+ }
203
+ }
204
+
205
+ // This finds the end of task
206
+ function getCodeFileTaskContent ({
207
+ config,
208
+ content,
209
+ inBlockComment,
210
+ fileType,
211
+ lang
212
+ }) {
213
+ const isCodeFile = fileType === FILE_TYPES.CODE
214
+ if (!isCodeFile) return
215
+
216
+ const rawTaskContentLines = []
217
+ let lines = getContentLines(content)
218
+ for (let i = 1; i < lines.length; i++) {
219
+ let line = lines[i]
220
+ // Another task ends a task
221
+ if (hasTaskInText(config, line, isCodeFile)) break
222
+ if (
223
+ !inBlockComment &&
224
+ (line.indexOf(lang.symbol) < 0 ||
225
+ line.trim().indexOf(lang.symbol) > 0 ||
226
+ line.trim() === lang.symbol)
227
+ )
228
+ break
229
+ if (lang.block && line.trim() === lang.block.end)
230
+ break
231
+ if (lang.block && line.trim() === lang.block.ignore)
232
+ break
233
+ if (line && lang && lang.block) line = line.replace(lang.block.end, '')
234
+
235
+ rawTaskContentLines.push(line)
236
+ }
237
+ return {
238
+ rawTaskContentLines,
239
+ taskContentLines: rawTaskContentLines,
240
+ }
241
+ }
242
+
243
+ function getTextFileTaskContent ({
244
+ config,
245
+ content,
246
+ beforeText,
247
+ fileType,
248
+ }) {
249
+ const isCodeFile = fileType === FILE_TYPES.CODE
250
+ if (isCodeFile) return
251
+
252
+ let lines = getContentLines(content)
253
+ const hasCardStartTag = lines.length > 1 && lines[1].trim() === START_TAG
254
+ if (hasCardStartTag) return
255
+
256
+ const isMarkDownFile = fileType === FILE_TYPES.MARKDOWN
257
+ const isTaskInList = isMarkDownFile && isBeforeTextMarkdownList(beforeText)
258
+ let openBlockCommentTokens = 0
259
+ let closeBlockCommentTokens = 0
260
+ let lastLineBlank = false
261
+ let endsWithDoubleBlank = false
262
+ let trailingBlankLines = 0
263
+ const rawTaskContentLines = []
264
+ const taskContentLines = []
265
+
266
+ let inCodeBlock = false
267
+
268
+ for (let i = 1; i < lines.length; i++) {
269
+ let line = lines[i]
270
+
271
+ // Check for markdown code blocks
272
+ if (isMarkDownFile && line.trim().startsWith('```')) {
273
+ inCodeBlock = !inCodeBlock
274
+ }
275
+
276
+ // Only break if the task is not in a markdown code block or code span
277
+ if (!inCodeBlock && hasTaskInText(config, line)) break
278
+ if (isCheckBoxTask(config, line, beforeText)) break
279
+ if (isTaskInList && isLineOutsideMarkdownList(line, beforeText)) break
280
+ if (isCustomCardTerminator(config, line)) break
281
+
282
+ rawTaskContentLines.push(line)
283
+ taskContentLines.push(`${line}`)
284
+
285
+ // Check for matching block comments in markdown files
286
+ if (isMarkDownFile && line.trim().indexOf('<!--') > -1)
287
+ openBlockCommentTokens++
288
+ if (isMarkDownFile && line.trim().indexOf('-->') > -1)
289
+ closeBlockCommentTokens++
290
+
291
+ const thisLineBlank = line.trim() === ''
292
+
293
+ if (thisLineBlank && lastLineBlank) {
294
+ endsWithDoubleBlank = true
295
+ break
296
+ }
297
+
298
+ lastLineBlank = thisLineBlank
299
+ }
300
+
301
+ if (endsWithDoubleBlank) trailingBlankLines = 2
302
+ else if (lastLineBlank) trailingBlankLines = 1
303
+
304
+ // Remove extra block comment tokens
305
+ if (
306
+ isMarkDownFile &&
307
+ rawTaskContentLines.length &&
308
+ rawTaskContentLines[rawTaskContentLines.length - 1].trim() === '-->' &&
309
+ openBlockCommentTokens !== closeBlockCommentTokens
310
+ ) {
311
+ rawTaskContentLines.pop()
312
+ taskContentLines.pop()
313
+ }
314
+
315
+ return {
316
+ rawTaskContentLines,
317
+ taskContentLines: trailingBlankLines
318
+ ? taskContentLines.slice(0, -trailingBlankLines)
319
+ : taskContentLines,
320
+ trailingBlankLines,
321
+ }
322
+ }
323
+
324
+ export function getTaskContent ({
325
+ config,
326
+ content,
327
+ inBlockComment,
328
+ beforeText,
329
+ fileType,
330
+ lang
331
+ }) {
332
+ return getCardTagsTaskContent({ content, fileType }) ||
333
+ getCodeFileTaskContent({ config, content, inBlockComment, fileType, lang }) ||
334
+ getTextFileTaskContent({ config, content, beforeText, fileType })
335
+ }
336
+
337
+ export function getRawTask ({
338
+ tokenPrefix = DEFAULT_TOKEN_PREFIX,
339
+ orderMeta = true,
340
+ beforeText = '',
341
+ hasColon = false,
342
+ list,
343
+ order = '',
344
+ text,
345
+ type = TASK_TYPES.HASHTAG
346
+ }) {
347
+ if (type === TASK_TYPES.MARKDOWN) {
348
+ return `${beforeText || ''}[${text}](${tokenPrefix}${list}:${
349
+ orderMeta ? '' : order
350
+ })`
351
+ } else {
352
+ const colon = hasColon || (isNumber(order) && !orderMeta) ? ':' : '';
353
+ return `${beforeText || ''}${tokenPrefix}${list}${colon}${
354
+ orderMeta ? '' : order
355
+ } ${text}`
356
+ }
357
+ }
358
+
359
+ export function isNumber(value) {
360
+ return !isNaN(value) && isFinite(Number(value));
361
+ }
362
+
363
+ export function toNumber(value) {
364
+ if (value == null || value == undefined) return undefined; // Convert null or undefined to undefined
365
+ const num = Number(value);
366
+ return isNaN(num) ? undefined : num; // Return 0 if conversion fails, otherwise return the number
367
+ }
@@ -0,0 +1,28 @@
1
+ import { TaskParser } from "./TaskParser.js"
2
+
3
+ export class CheckStyleTaskParser extends TaskParser {
4
+ constructor() {
5
+ super()
6
+ }
7
+
8
+ get pattern() {
9
+ return /^(\s*- \[([x ])\]\s)(.+$)/gm
10
+ }
11
+
12
+ parse(lineContent, line, matchResult, task) {
13
+ let [match, beforeText, checked, text] = matchResult
14
+ if (
15
+ (task && !task.beforeText) ||
16
+ (task && task.beforeText.length !== beforeText.length)
17
+ )
18
+ return
19
+ checked = checked.trim() === 'x'
20
+ return {
21
+ beforeText,
22
+ text,
23
+ line,
24
+ checked,
25
+ type: this.config.getNewCardSyntax(),
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ import { TaskParser } from './TaskParser.js'
2
+
3
+ export class CodeStyleTaskParser extends TaskParser {
4
+ constructor(config) {
5
+ super('CODE', config)
6
+ }
7
+
8
+ get pattern() {
9
+ return /^(.*?[ #]?)([a-zA-Z-_]+?)(:)(-?[\d.]+(?:e-?\d+)?)?[ \t]+(.+)$/gm
10
+ }
11
+
12
+ parse(lineContent, line, matchResult) {
13
+ const [match, beforeText, list, colon, order, text] = matchResult
14
+ return {
15
+ beforeText,
16
+ text,
17
+ order,
18
+ list,
19
+ line,
20
+ colon,
21
+ type: this.type,
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,27 @@
1
+ import { TaskParser } from './TaskParser.js'
2
+ import { getHashStyleRegex } from './CardContentParser.js'
3
+
4
+ export class HashStyleTaskParser extends TaskParser {
5
+ constructor(config) {
6
+ super('HASHTAG', config)
7
+ }
8
+
9
+ get pattern() {
10
+ const config = this.config
11
+ return getHashStyleRegex(config.tokenPrefix, config.orderMeta)
12
+ }
13
+
14
+ parse(lineContent, line, matchResult) {
15
+ const [rawTask, list, orderGroup, order, text] = matchResult
16
+
17
+ return {
18
+ // beforeText,
19
+ text,
20
+ order,
21
+ list,
22
+ line,
23
+ colon: orderGroup && orderGroup.startsWith(':'),
24
+ type: this.type,
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,24 @@
1
+ import { TaskParser } from './TaskParser.js'
2
+
3
+ export class MarkdownStyleTaskParser extends TaskParser {
4
+ constructor(config) {
5
+ super('MARKDOWN', config)
6
+ }
7
+
8
+ get pattern() {
9
+ return /^(.*)(\[(.+?)\]\(#([a-zA-Z-_]+?)(:)(-?[\d.]+(?:e-?\d+)?)?\))/gm
10
+ }
11
+
12
+ parse(lineContent, line, matchResult) {
13
+ const [match, beforeText, rawTask, text, list, colon, order] = matchResult
14
+ return {
15
+ beforeText,
16
+ text,
17
+ order,
18
+ list,
19
+ line,
20
+ colon,
21
+ type: this.type,
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,38 @@
1
+
2
+ class Task {
3
+ constructor({ beforeText, text, list, line, order, checked, colon, type }) {
4
+ this.contentLength = 0
5
+ this.description = []
6
+ this.beforeText = beforeText
7
+ this.text = text
8
+ this.list = list
9
+ this.line = line
10
+ this.order = order
11
+ this.checked = checked
12
+ this.colon = colon
13
+ this.type = type
14
+ }
15
+ }
16
+
17
+ export class TaskParser {
18
+ constructor(type, config) {
19
+ this.config = config
20
+ this.type = type
21
+ }
22
+
23
+ get pattern() {
24
+ throw new Error('Unimplemented')
25
+ }
26
+
27
+ parseLine(lineContent, line, task) {
28
+ const re = new RegExp(this.pattern)
29
+ const result = re.exec(lineContent)
30
+ const parseResult = result && this.parse(lineContent, line, result, task)
31
+
32
+ if (parseResult && this.config.lists && !this.config.listExists(parseResult.list)) {
33
+ return
34
+ }
35
+
36
+ return parseResult && new Task(parseResult)
37
+ }
38
+ }
@@ -0,0 +1,6 @@
1
+ export * from './CardContentParser.js';
2
+ export * from './CheckStyleTaskParser.js';
3
+ export * from './CodeStyleTaskParser.js';
4
+ export * from './HashStyleTaskParser.js';
5
+ export * from './MarkdownStyleTaskParser.js';
6
+ export * from './TaskParser.js';
@@ -0,0 +1,44 @@
1
+ import { readFile, writeFile, exists } from '../file-gateway.js'
2
+ import { constants } from '../../constants.js'
3
+ import { loadYAML, dumpYAML } from '../yaml.js'
4
+ import { findUp } from 'find-up-simple'
5
+ import _path from 'path'
6
+ import { Config } from '../../config.js'
7
+ import { logger } from '../logger.js'
8
+
9
+ const { CONFIG_DIR, CONFIG_FILE_YML } = constants
10
+ export async function load(projectPath, configPath = _path.join(projectPath, CONFIG_FILE_YML)) {
11
+ const configLike = await loadYamlConfig(configPath)
12
+ return configLike && new Config(configLike)
13
+ }
14
+
15
+ export async function save(config, projectPath) {
16
+ const configPath = _path.join(projectPath, CONFIG_FILE_YML)
17
+ await writeFile(configPath, dumpYAML(config))
18
+ }
19
+
20
+ export async function loadForFilePath(filePath) {
21
+ const projectPath = await findImdonePath(filePath)
22
+ return load(projectPath)
23
+ }
24
+
25
+ export async function loadYamlConfig(filePath) {
26
+ if (!(await exists(filePath))) return
27
+ const configData = await readFile(filePath, 'utf-8')
28
+ let configLike = {}
29
+ try {
30
+ configLike = loadYAML(configData.toString())
31
+ if (configLike.exclude) delete configLike.exclude
32
+ if (configLike.watcher) delete configLike.watcher
33
+
34
+ } catch (e) {
35
+ logger.error(`Error parsing config file: ${filePath}`)
36
+ }
37
+ return configLike
38
+ }
39
+
40
+ export async function findImdonePath(cwd) {
41
+ const configDir = await findUp(CONFIG_DIR, { cwd, type: 'directory' })
42
+ return _path.dirname(configDir)
43
+ }
44
+
@@ -0,0 +1,2 @@
1
+ export * from './config.js';
2
+ export * from './tags.js';
@@ -0,0 +1,29 @@
1
+ import { readFile, writeFile, exists } from '../file-gateway.js'
2
+ import { constants } from '../../constants.js'
3
+ const { CONFIG_DIR } = constants
4
+ import { loadYAML, dumpYAML } from '../yaml.js'
5
+ import _path from 'path'
6
+ import { logger } from '../logger.js'
7
+
8
+ export const TAGS_FILE_PATH = _path.join(CONFIG_DIR, 'tags.yml')
9
+
10
+ export async function load(projectPath) {
11
+ const tagsPath = getTagsPath(projectPath)
12
+ if (!(await exists(tagsPath))) return
13
+ const tagsData = await readFile(tagsPath, 'utf-8')
14
+ try {
15
+ const { tags } = loadYAML(tagsData)
16
+ return tags
17
+ } catch (e) {
18
+ logger.error(e)
19
+ return
20
+ }
21
+ }
22
+
23
+ export async function save(tags, projectPath) {
24
+ await writeFile(getTagsPath(projectPath), dumpYAML({ tags }))
25
+ }
26
+
27
+ function getTagsPath(projectPath) {
28
+ return _path.join(projectPath, TAGS_FILE_PATH)
29
+ }
@@ -0,0 +1,9 @@
1
+ import YAML from 'js-yaml'
2
+
3
+ export function dumpYAML(obj, opts) {
4
+ return YAML.dump(obj, opts)
5
+ }
6
+
7
+ export function loadYAML(str, opts) {
8
+ return YAML.load(str, opts)
9
+ }