imdone-core 2.1.11 → 2.3.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 +5 -0
- package/README.md +2 -2
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +1 -1
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +185 -80
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +79 -4
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +20 -2
- package/lib/adapters/parsers/task/CheckStyleTaskParser.js +3 -2
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +1 -1
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +1 -1
- package/lib/adapters/parsers/task/TaskParser.js +7 -2
- package/lib/file.js +2 -2
- package/lib/index.js +1 -0
- package/lib/migrate-config.js +4 -3
- package/lib/plugins/extension-plugin.js +2 -1
- package/lib/plugins/plugin-manager.js +4 -3
- package/lib/repository.js +153 -24
- package/lib/utils/index.js +1 -0
- package/lib/utils/module-import.js +31 -0
- package/package.json +3 -3
- package/scripts/generate-index.js +11 -1
- package/scripts/prepublish-only.js +30 -0
- package/types/adapters/parsers/file/ReadStreamCodeParser.d.ts +2 -4
- package/types/adapters/parsers/file/ReadStreamMarkdownParser.d.ts +11 -0
- package/types/adapters/parsers/file/ReadStreamTaskParser.d.ts +5 -1
- package/types/adapters/parsers/task/CheckStyleTaskParser.d.ts +2 -1
- package/types/adapters/parsers/task/TaskParser.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/utils/index.d.ts +1 -0
- package/types/utils/module-import.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## 2.1.11
|
|
2
|
+
|
|
3
|
+
- Load extension and installed plugin modules through file URL import specifiers, so Windows users no longer see `ERR_UNSUPPORTED_ESM_URL_SCHEME` when project paths start with drive letters like `C:\`
|
|
4
|
+
- Keep generated library indexes from exporting tests, fixtures, hidden folders, or invalid namespace names, so build and type generation stay green when test fixtures include realistic `.imdone` directories
|
|
5
|
+
|
|
1
6
|
## 2.1.5
|
|
2
7
|
|
|
3
8
|
- Create GitHub releases automatically from the npm package version when changes merge to `master`.
|
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
[](https://npmjs.org/package/imdone-core)
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Imdone is text based kanban processor with a simple syntax that uses [comment tags like TODO and FIXME](https://en.wikipedia.org/wiki/Comment_%28computer_programming%29#Tags) and [todo.txt format](https://github.com/todotxt/todo.txt#todotxt-format). This model allows the user to create and modify tasks using the keyboard and automatically establishes a link between their tasks and work. Get [imdone](https://imdone.io) or use the cli to see your projects board and this library in action.
|
|
5
|
+
Imdone is text based kanban processor with a simple syntax that uses [comment tags like TODO and FIXME](https://en.wikipedia.org/wiki/Comment_%28computer_programming%29#Tags) and [todo.txt format](https://github.com/todotxt/todo.txt#todotxt-format). This model allows the user to create and modify tasks using the keyboard and automatically establishes a link between their tasks and work. Get [imdone kanban](https://imdone.io/docs/imdone-kanban/) or use the [cli](https://imdone.io/docs/imdone-cli/) to see your projects board and this library in action.
|
|
6
6
|
|
|
7
|
-

|
|
7
|
+

|
|
8
8
|
|
|
9
9
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
10
10
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { ReadStreamTaskParser } from './ReadStreamTaskParser.js'
|
|
3
|
-
import { CodeStyleTaskParser } from '../task/CodeStyleTaskParser.js'
|
|
4
3
|
import { languages } from '../../../languages.js'
|
|
5
|
-
import
|
|
6
|
-
import eol from 'eol'
|
|
7
|
-
import { Readable } from 'node:stream'
|
|
4
|
+
import { CodeStyleTaskParser } from '../task/CodeStyleTaskParser.js'
|
|
8
5
|
|
|
9
6
|
// TODO Move to readstream parsing for code to improve performance
|
|
10
7
|
// <!--
|
|
@@ -18,109 +15,217 @@ export class ReadStreamCodeParser extends ReadStreamTaskParser {
|
|
|
18
15
|
this.lists = lists
|
|
19
16
|
this.filePath = filePath
|
|
20
17
|
this.lang = lang
|
|
21
|
-
this.taskParsers = [new CodeStyleTaskParser()]
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
async init() {
|
|
25
|
-
const code = await streamToString(this.readStream)
|
|
26
21
|
// TODO Getting tokens from lists should be done higher up in the controller
|
|
27
22
|
// <!--
|
|
28
23
|
// order:-10
|
|
29
24
|
// -->
|
|
30
|
-
const tokens = this.lists
|
|
31
|
-
|
|
25
|
+
const tokens = this.lists
|
|
26
|
+
.map((list) => {
|
|
27
|
+
if (typeof list === 'string') return list
|
|
28
|
+
if (!list || typeof list !== 'object') return undefined
|
|
29
|
+
return list.name
|
|
30
|
+
})
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
this.cards = await this.parseCards(tokens)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
async readTask() {
|
|
35
36
|
return this.cards.shift()
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
parseCards(
|
|
39
|
-
const comments = this.parseComments(
|
|
40
|
-
const cards = []
|
|
41
|
-
|
|
39
|
+
async parseCards(tokens = ['TODO']) {
|
|
40
|
+
const comments = await this.parseComments()
|
|
41
|
+
const cards = []
|
|
42
|
+
const listNames = tokens.map((token) =>
|
|
43
|
+
typeof token === 'string' ? token : token?.name
|
|
44
|
+
).filter(Boolean)
|
|
45
|
+
const parser = new CodeStyleTaskParser({
|
|
46
|
+
includeList: (list) => listNames.includes(list),
|
|
47
|
+
listExists: (list) => listNames.includes(list),
|
|
48
|
+
lists: listNames.map((name) => ({ name })),
|
|
49
|
+
})
|
|
50
|
+
|
|
42
51
|
Object.keys(comments).forEach((comment) => {
|
|
43
|
-
const {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
const {
|
|
53
|
+
begin,
|
|
54
|
+
end,
|
|
55
|
+
content,
|
|
56
|
+
inBlockComment,
|
|
57
|
+
singleLineBlockComment,
|
|
58
|
+
} = comments[comment]
|
|
59
|
+
|
|
60
|
+
const lines = content.split('\n')
|
|
61
|
+
let activeTask = null
|
|
62
|
+
let taskLines = []
|
|
63
|
+
|
|
64
|
+
const flushActiveTask = (lineNo) => {
|
|
65
|
+
if (!activeTask) return
|
|
66
|
+
cards.push({
|
|
67
|
+
...activeTask,
|
|
68
|
+
content: `${taskLines.join('\n')}\n`,
|
|
69
|
+
line: activeTask.line,
|
|
70
|
+
lastLine: lineNo,
|
|
71
|
+
inBlockComment,
|
|
72
|
+
singleLineBlockComment,
|
|
73
|
+
})
|
|
74
|
+
activeTask = null
|
|
75
|
+
taskLines = []
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lines.forEach((line, index) => {
|
|
79
|
+
const lineNo = begin + index
|
|
80
|
+
const parsedTask = parser.parseLine(line, lineNo)
|
|
81
|
+
|
|
82
|
+
if (parsedTask) {
|
|
83
|
+
flushActiveTask(lineNo - 1)
|
|
84
|
+
activeTask = parsedTask
|
|
85
|
+
taskLines = [line]
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (activeTask) {
|
|
90
|
+
taskLines.push(line)
|
|
61
91
|
}
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
flushActiveTask(end)
|
|
95
|
+
})
|
|
96
|
+
return cards
|
|
65
97
|
}
|
|
66
98
|
|
|
67
|
-
parseComments(
|
|
68
|
-
const options = getLanguageOptions(this.filePath);
|
|
69
|
-
const comments =
|
|
70
|
-
|
|
99
|
+
async parseComments() {
|
|
100
|
+
const options = getLanguageOptions(this.filePath, this.lang);
|
|
101
|
+
const comments = [];
|
|
102
|
+
let lineNo = 0;
|
|
103
|
+
let singleLineComment;
|
|
104
|
+
let blockComment;
|
|
105
|
+
|
|
106
|
+
const finishSingleLineComment = () => {
|
|
107
|
+
if (!singleLineComment) return
|
|
108
|
+
comments.push(singleLineComment)
|
|
109
|
+
singleLineComment = undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for await (const line of this.readInterface) {
|
|
113
|
+
lineNo++
|
|
114
|
+
|
|
115
|
+
if (blockComment) {
|
|
116
|
+
appendBlockCommentLine(blockComment, line, options)
|
|
117
|
+
if (line.includes(options.block.end)) {
|
|
118
|
+
blockComment.end = lineNo
|
|
119
|
+
comments.push(blockComment)
|
|
120
|
+
blockComment = undefined
|
|
121
|
+
}
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const blockStartIndex = getBlockStartIndex(line, options)
|
|
126
|
+
const singleLineIndex = getSingleLineCommentIndex(line, options)
|
|
127
|
+
const blockStartsFirst = blockStartIndex > -1 && (singleLineIndex === -1 || blockStartIndex < singleLineIndex)
|
|
128
|
+
|
|
129
|
+
if (blockStartsFirst) {
|
|
130
|
+
finishSingleLineComment()
|
|
131
|
+
blockComment = {
|
|
132
|
+
begin: lineNo,
|
|
133
|
+
end: lineNo,
|
|
134
|
+
contentLines: [],
|
|
135
|
+
inBlockComment: true,
|
|
136
|
+
}
|
|
137
|
+
appendBlockCommentLine(blockComment, line, options)
|
|
138
|
+
if (line.includes(options.block.end)) {
|
|
139
|
+
comments.push(blockComment)
|
|
140
|
+
blockComment = undefined
|
|
141
|
+
}
|
|
142
|
+
} else if (singleLineIndex > -1) {
|
|
143
|
+
const content = line.slice(singleLineIndex + options.singleLineComment.length)
|
|
144
|
+
if (!singleLineComment || singleLineComment.end !== lineNo - 1) {
|
|
145
|
+
finishSingleLineComment()
|
|
146
|
+
singleLineComment = {
|
|
147
|
+
begin: lineNo,
|
|
148
|
+
end: lineNo,
|
|
149
|
+
contentLines: [],
|
|
150
|
+
inBlockComment: false,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
singleLineComment.end = lineNo
|
|
154
|
+
singleLineComment.contentLines.push(trimCommentLine(content))
|
|
155
|
+
} else {
|
|
156
|
+
finishSingleLineComment()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
finishSingleLineComment()
|
|
161
|
+
if (blockComment) comments.push(blockComment)
|
|
162
|
+
|
|
163
|
+
return comments.reduce((acc, comment, index) => {
|
|
164
|
+
acc[index] = {
|
|
165
|
+
begin: comment.begin,
|
|
166
|
+
end: comment.end,
|
|
167
|
+
content: formatCommentContent(comment.contentLines),
|
|
168
|
+
inBlockComment: comment.inBlockComment,
|
|
169
|
+
singleLineBlockComment: comment.inBlockComment && comment.begin === comment.end,
|
|
170
|
+
}
|
|
171
|
+
return acc
|
|
172
|
+
}, {})
|
|
71
173
|
}
|
|
72
174
|
|
|
73
175
|
}
|
|
74
176
|
|
|
177
|
+
function getBlockStartIndex(line, options) {
|
|
178
|
+
if (!options.block.start || !options.block.end) return -1
|
|
179
|
+
return line.indexOf(options.block.start)
|
|
180
|
+
}
|
|
75
181
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
name,
|
|
83
|
-
symbol,
|
|
84
|
-
block,
|
|
85
|
-
} = language ||
|
|
86
|
-
{
|
|
87
|
-
name: 'text',
|
|
88
|
-
symbol: '',
|
|
89
|
-
block: {
|
|
90
|
-
start: undefined,
|
|
91
|
-
end: undefined,
|
|
92
|
-
ignore: undefined,
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
// Use lang to get comment patterns
|
|
96
|
-
|
|
97
|
-
const {
|
|
98
|
-
start,
|
|
99
|
-
ignore: middle,
|
|
100
|
-
end
|
|
101
|
-
} = block || {};
|
|
102
|
-
|
|
103
|
-
return language && {
|
|
104
|
-
pattern: {
|
|
105
|
-
name,
|
|
106
|
-
nameMatchers: extension && [extension] || [],
|
|
107
|
-
singleLineComment: symbol && [{ start: symbol }] || [],
|
|
108
|
-
multiLineComment: start && end && [{ start, middle, end }] || [],
|
|
109
|
-
},
|
|
110
|
-
};
|
|
182
|
+
function getSingleLineCommentIndex(line, options) {
|
|
183
|
+
if (!options.singleLineComment) return -1
|
|
184
|
+
return line.indexOf(options.singleLineComment)
|
|
111
185
|
}
|
|
112
186
|
|
|
113
|
-
|
|
114
|
-
const
|
|
187
|
+
function appendBlockCommentLine(comment, line, options) {
|
|
188
|
+
const startIndex = line.indexOf(options.block.start)
|
|
189
|
+
const contentStart = startIndex > -1 ? startIndex + options.block.start.length : 0
|
|
190
|
+
const endIndex = line.indexOf(options.block.end, contentStart)
|
|
191
|
+
const contentEnd = endIndex > -1 ? endIndex : line.length
|
|
192
|
+
const content = line.slice(contentStart, contentEnd)
|
|
193
|
+
|
|
194
|
+
comment.contentLines.push(trimCommentLine(content, options.block.ignore))
|
|
195
|
+
}
|
|
115
196
|
|
|
116
|
-
|
|
117
|
-
|
|
197
|
+
function trimCommentLine(line, ignoreToken) {
|
|
198
|
+
let trimmed = line.trimStart()
|
|
199
|
+
if (ignoreToken && trimmed.startsWith(ignoreToken)) {
|
|
200
|
+
trimmed = trimmed.slice(ignoreToken.length).trimStart()
|
|
118
201
|
}
|
|
202
|
+
return trimmed.trimEnd()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function formatCommentContent(lines) {
|
|
206
|
+
const trimmedLines = trimBlankCommentLines(lines)
|
|
207
|
+
if (!trimmedLines.length) return ''
|
|
208
|
+
return `${trimmedLines.join('\n')}\n`
|
|
209
|
+
}
|
|
119
210
|
|
|
120
|
-
|
|
211
|
+
function trimBlankCommentLines(lines) {
|
|
212
|
+
const trimmedLines = [...lines]
|
|
213
|
+
while (trimmedLines[0] === '') trimmedLines.shift()
|
|
214
|
+
while (trimmedLines[trimmedLines.length - 1] === '') trimmedLines.pop()
|
|
215
|
+
return trimmedLines
|
|
121
216
|
}
|
|
122
217
|
|
|
123
|
-
function
|
|
124
|
-
const
|
|
125
|
-
|
|
218
|
+
function getLanguageOptions(filePath, lang) {
|
|
219
|
+
const extension = path.extname(filePath)
|
|
220
|
+
const language = lang || languages[extension]
|
|
221
|
+
const block = language?.block || {}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
singleLineComment: language?.symbol || '',
|
|
225
|
+
block: {
|
|
226
|
+
start: block.start,
|
|
227
|
+
ignore: block.ignore,
|
|
228
|
+
end: block.end,
|
|
229
|
+
},
|
|
230
|
+
}
|
|
126
231
|
}
|
|
@@ -1,17 +1,92 @@
|
|
|
1
1
|
import { ReadStreamTaskParser } from './ReadStreamTaskParser.js'
|
|
2
2
|
import { HashStyleTaskParser } from '../task/HashStyleTaskParser.js'
|
|
3
3
|
import { MarkdownStyleTaskParser } from '../task/MarkdownStyleTaskParser.js'
|
|
4
|
-
|
|
4
|
+
import { CheckStyleTaskParser } from '../task/CheckStyleTaskParser.js'
|
|
5
|
+
import { loadYAML } from '../../yaml.js'
|
|
6
|
+
|
|
7
|
+
const FRONTMATTER_DELIMITER = '---'
|
|
8
|
+
const CODE_FENCE_PATTERN = /^\s*(```|~~~)/
|
|
9
|
+
const INLINE_CODE_SPAN_PATTERN = /`[^`]*`/g
|
|
5
10
|
|
|
6
11
|
export class ReadStreamMarkdownParser extends ReadStreamTaskParser {
|
|
7
12
|
constructor(readStream, config) {
|
|
8
13
|
super(readStream, config)
|
|
14
|
+
this.frontmatterLines = []
|
|
15
|
+
this.collectingFrontmatter = false
|
|
16
|
+
this.frontmatterClosed = false
|
|
17
|
+
this.ignoredByFrontmatter = false
|
|
18
|
+
this.currentLineIsFrontmatter = false
|
|
19
|
+
this.currentLineIsCodeFence = false
|
|
20
|
+
this.inCodeFence = false
|
|
9
21
|
this.taskParsers = [
|
|
10
22
|
new HashStyleTaskParser(config),
|
|
11
23
|
new MarkdownStyleTaskParser(config),
|
|
12
24
|
]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
if (config.isAddCheckBoxTasks()) {
|
|
26
|
+
this.taskParsers.push(new CheckStyleTaskParser(config))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onLineRead(line) {
|
|
31
|
+
this.updateFrontmatterState(line)
|
|
32
|
+
this.updateCodeFenceState(line)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
shouldParseTaskLine() {
|
|
36
|
+
return !this.ignoredByFrontmatter &&
|
|
37
|
+
!this.currentLineIsFrontmatter &&
|
|
38
|
+
!this.currentLineIsCodeFence
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getLineForTaskParsing(line) {
|
|
42
|
+
return line.replace(INLINE_CODE_SPAN_PATTERN, (match) => ' '.repeat(match.length))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
updateFrontmatterState(line) {
|
|
46
|
+
const trimmed = line.trim()
|
|
47
|
+
this.currentLineIsFrontmatter = false
|
|
48
|
+
|
|
49
|
+
if (this.lineNo === 1 && isFrontmatterDelimiter(trimmed)) {
|
|
50
|
+
this.collectingFrontmatter = true
|
|
51
|
+
this.currentLineIsFrontmatter = true
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!this.collectingFrontmatter) return
|
|
56
|
+
|
|
57
|
+
this.currentLineIsFrontmatter = true
|
|
58
|
+
|
|
59
|
+
if (isFrontmatterDelimiter(trimmed)) {
|
|
60
|
+
this.collectingFrontmatter = false
|
|
61
|
+
this.frontmatterClosed = true
|
|
62
|
+
this.updateIgnoredByFrontmatter()
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.frontmatterLines.push(line)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
updateIgnoredByFrontmatter() {
|
|
70
|
+
if (!this.frontmatterClosed) return
|
|
71
|
+
const data = parseFrontmatter(this.frontmatterLines)
|
|
72
|
+
this.ignoredByFrontmatter = !!(data.imdone_ignore || data['kanban-plugin'])
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
updateCodeFenceState(line) {
|
|
76
|
+
const isFence = CODE_FENCE_PATTERN.test(line)
|
|
77
|
+
this.currentLineIsCodeFence = this.inCodeFence || isFence
|
|
78
|
+
if (isFence) this.inCodeFence = !this.inCodeFence
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isFrontmatterDelimiter(trimmedLine) {
|
|
83
|
+
return trimmedLine === FRONTMATTER_DELIMITER
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseFrontmatter(lines) {
|
|
87
|
+
try {
|
|
88
|
+
return loadYAML(lines.join('\n')) || {}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {}
|
|
16
91
|
}
|
|
17
92
|
}
|
|
@@ -16,11 +16,13 @@ export class ReadStreamTaskParser {
|
|
|
16
16
|
crlfDelay: Infinity,
|
|
17
17
|
console: false,
|
|
18
18
|
})
|
|
19
|
+
this.lineIterator = this.readInterface[Symbol.asyncIterator]()
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async readTask() {
|
|
22
23
|
let taskOrStreamDone = false
|
|
23
24
|
this.task = this.nextTask || null
|
|
25
|
+
this.nextTask = null
|
|
24
26
|
this.blankLines = 0
|
|
25
27
|
|
|
26
28
|
try {
|
|
@@ -29,6 +31,7 @@ export class ReadStreamTaskParser {
|
|
|
29
31
|
let { value, done } = this.line
|
|
30
32
|
if (this.line && !done) {
|
|
31
33
|
this.lineNo++
|
|
34
|
+
this.onLineRead(value)
|
|
32
35
|
const endOfTask = this.isEndOfTask(value)
|
|
33
36
|
if (this.task && endOfTask) {
|
|
34
37
|
taskOrStreamDone = true
|
|
@@ -51,7 +54,19 @@ export class ReadStreamTaskParser {
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
async readLine() {
|
|
54
|
-
return this.
|
|
57
|
+
return this.lineIterator.next()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onLineRead() {
|
|
61
|
+
// Hook for parsers that need line-aware state.
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
shouldParseTaskLine() {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getLineForTaskParsing(line) {
|
|
69
|
+
return line
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
isEndOfTask(line) {
|
|
@@ -76,9 +91,12 @@ export class ReadStreamTaskParser {
|
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
getTaskFromLine(line) {
|
|
94
|
+
if (!this.shouldParseTaskLine(line)) return
|
|
95
|
+
const originalLine = line
|
|
96
|
+
line = this.getLineForTaskParsing(line)
|
|
79
97
|
let task
|
|
80
98
|
this.taskParsers.find((taskParser) => {
|
|
81
|
-
task = taskParser.parseLine(line, this.lineNo, this.task)
|
|
99
|
+
task = taskParser.parseLine(line, this.lineNo, this.task, originalLine)
|
|
82
100
|
return task
|
|
83
101
|
})
|
|
84
102
|
return task
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TaskParser } from "./TaskParser.js"
|
|
2
2
|
|
|
3
3
|
export class CheckStyleTaskParser extends TaskParser {
|
|
4
|
-
constructor() {
|
|
5
|
-
super()
|
|
4
|
+
constructor(config) {
|
|
5
|
+
super('CHECKBOX', config)
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
get pattern() {
|
|
@@ -20,6 +20,7 @@ export class CheckStyleTaskParser extends TaskParser {
|
|
|
20
20
|
return {
|
|
21
21
|
beforeText,
|
|
22
22
|
text,
|
|
23
|
+
list: checked ? this.config.getDoneList() : this.config.getDefaultList(),
|
|
23
24
|
line,
|
|
24
25
|
checked,
|
|
25
26
|
type: this.config.getNewCardSyntax(),
|
|
@@ -6,7 +6,7 @@ export class CodeStyleTaskParser extends TaskParser {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
get pattern() {
|
|
9
|
-
return /^(.*?[ #]?)([
|
|
9
|
+
return /^(.*?[ #]?)([A-Z]+[A-Z-_]+?)(:)?(-?[\d.]+(?:e-?\d+)?)?[ \t]+(.+)$/gm
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
parse(lineContent, line, matchResult) {
|
|
@@ -8,7 +8,7 @@ export class HashStyleTaskParser extends TaskParser {
|
|
|
8
8
|
|
|
9
9
|
get pattern() {
|
|
10
10
|
const config = this.config
|
|
11
|
-
return getHashStyleRegex(config.tokenPrefix
|
|
11
|
+
return getHashStyleRegex(config.tokenPrefix)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
parse(lineContent, line, matchResult) {
|
|
@@ -24,10 +24,15 @@ export class TaskParser {
|
|
|
24
24
|
throw new Error('Unimplemented')
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
parseLine(lineContent, line, task) {
|
|
27
|
+
parseLine(lineContent, line, task, originalLineContent = lineContent) {
|
|
28
28
|
const re = new RegExp(this.pattern)
|
|
29
29
|
const result = re.exec(lineContent)
|
|
30
|
-
|
|
30
|
+
if (!result) return
|
|
31
|
+
|
|
32
|
+
const originalResult = originalLineContent === lineContent
|
|
33
|
+
? result
|
|
34
|
+
: new RegExp(this.pattern).exec(originalLineContent.slice(result.index)) || result
|
|
35
|
+
const parseResult = this.parse(originalLineContent, line, originalResult, task)
|
|
31
36
|
|
|
32
37
|
if (parseResult && this.config.lists && !this.config.listExists(parseResult.list)) {
|
|
33
38
|
return
|
package/lib/file.js
CHANGED
|
@@ -852,7 +852,7 @@ export class File extends Emitter {
|
|
|
852
852
|
} else text = task.text
|
|
853
853
|
if (/[a-z]/.test(task.list)) task.type = Task.Types.HASHTAG
|
|
854
854
|
const tokenPrefix = task.type === Task.Types.HASHTAG ? config.tokenPrefix : ''
|
|
855
|
-
var taskContent = getRawTask({tokenPrefix, orderMeta: config.orderMeta, list: task.list, order: task.order, text, type: task.type})
|
|
855
|
+
var taskContent = getRawTask({tokenPrefix, orderMeta: config.orderMeta, hasColon: task.hasColon, list: task.list, order: task.order, text, type: task.type})
|
|
856
856
|
task.line = this.getLineNumber(index)
|
|
857
857
|
task.id = getTaskId(self.getPath(), task.line, task.text)
|
|
858
858
|
this.setContent(beforeContent + taskContent + afterContent)
|
|
@@ -1508,4 +1508,4 @@ export class File extends Emitter {
|
|
|
1508
1508
|
isResultInMarkdownCode (positions, index) {
|
|
1509
1509
|
return positions.some(pos => index >= pos.start && index < pos.end);
|
|
1510
1510
|
};
|
|
1511
|
-
}
|
|
1511
|
+
}
|
package/lib/index.js
CHANGED
package/lib/migrate-config.js
CHANGED
|
@@ -149,13 +149,14 @@ export function buildMigrateConfig (repo, defaultSettings = _defaultSettings) {
|
|
|
149
149
|
config.settings.kudosProbability = 0.33
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
const
|
|
152
|
+
const defaultSettingsCopy = cloneDeep(defaultSettings)
|
|
153
|
+
const defaultCardsSettings = cloneDeep(defaultSettingsCopy.cards)
|
|
153
154
|
const cardsSettings = cloneDeep(config.settings.cards)
|
|
154
155
|
|
|
155
|
-
delete
|
|
156
|
+
delete defaultSettingsCopy.cards
|
|
156
157
|
delete config.settings.cards
|
|
157
158
|
|
|
158
|
-
config.settings = { ...
|
|
159
|
+
config.settings = { ...defaultSettingsCopy, ...config.settings }
|
|
159
160
|
config.settings.cards = {
|
|
160
161
|
...defaultCardsSettings,
|
|
161
162
|
...cardsSettings,
|
|
@@ -2,6 +2,7 @@ import Plugin from 'imdone-api'
|
|
|
2
2
|
import _path from 'path'
|
|
3
3
|
import { exists } from '../adapters/file-gateway.js'
|
|
4
4
|
import { logger } from '../adapters/logger.js';
|
|
5
|
+
import { toFileImportSpecifier } from '../utils/module-import.js'
|
|
5
6
|
|
|
6
7
|
export default class ExtensionPlugin extends Plugin {
|
|
7
8
|
constructor(project) {
|
|
@@ -71,7 +72,7 @@ export default class ExtensionPlugin extends Plugin {
|
|
|
71
72
|
const extensionPath = this.getExtensionPath(path)
|
|
72
73
|
try {
|
|
73
74
|
await exists(extensionPath)
|
|
74
|
-
const importedModule = await import(extensionPath)
|
|
75
|
+
const importedModule = await import(toFileImportSpecifier(extensionPath))
|
|
75
76
|
|
|
76
77
|
// Check if it's a module object with a default export
|
|
77
78
|
if (importedModule && typeof importedModule === 'object' && 'default' in importedModule) {
|
|
@@ -15,6 +15,7 @@ import ArchivePlugin from './archive-plugin.js'
|
|
|
15
15
|
import EpicPlugin from './epic-plugin.js'
|
|
16
16
|
import ExtensionPlugin from './extension-plugin.js'
|
|
17
17
|
import { logger } from '../adapters/logger.js'
|
|
18
|
+
import { toFileImportSpecifier } from '../utils/module-import.js'
|
|
18
19
|
// import { URL } from 'node:url'
|
|
19
20
|
|
|
20
21
|
// const require = createRequire(new URL(import.meta.url));
|
|
@@ -159,7 +160,7 @@ export class PluginManager extends Emitter {
|
|
|
159
160
|
// eslint-disable-next-line
|
|
160
161
|
delete require.cache[require.resolve(fullPath)]
|
|
161
162
|
// eslint-disable-next-line
|
|
162
|
-
const pluginClass = await import(fullPath)
|
|
163
|
+
const pluginClass = await import(toFileImportSpecifier(fullPath))
|
|
163
164
|
const pluginInstance = await this.createPlugin(pluginClass.default, path)
|
|
164
165
|
return pluginInstance
|
|
165
166
|
} catch (e) {
|
|
@@ -170,7 +171,7 @@ export class PluginManager extends Emitter {
|
|
|
170
171
|
if (packageInfo.default.main) {
|
|
171
172
|
fullPath = _path.join(_path.resolve(path), packageInfo.default.main)
|
|
172
173
|
logger.log(`Trying package.json main: ${fullPath}`)
|
|
173
|
-
const pluginClass = await import(fullPath)
|
|
174
|
+
const pluginClass = await import(toFileImportSpecifier(fullPath))
|
|
174
175
|
const pluginInstance = await this.createPlugin(pluginClass.default, path)
|
|
175
176
|
return pluginInstance
|
|
176
177
|
}
|
|
@@ -186,7 +187,7 @@ export class PluginManager extends Emitter {
|
|
|
186
187
|
if (!path) return {}
|
|
187
188
|
let info = { path }
|
|
188
189
|
const packagePath = _path.join(path, 'package.json')
|
|
189
|
-
const packageInfo = await import(packagePath, {with: { type: 'json' } })
|
|
190
|
+
const packageInfo = await import(toFileImportSpecifier(packagePath), {with: { type: 'json' } })
|
|
190
191
|
try {
|
|
191
192
|
info = {...info, ...packageInfo }
|
|
192
193
|
delete info.dependencies
|
package/lib/repository.js
CHANGED
|
@@ -37,8 +37,140 @@ import { appContext } from './context/ApplicationContext.js'
|
|
|
37
37
|
import { computeChecksum } from './checksum.js'
|
|
38
38
|
import { getTasksToModify } from './get-tasks-to-modify.js'
|
|
39
39
|
import { logger } from './adapters/logger.js'
|
|
40
|
+
import { getFileParser } from './adapters/parsers/file/LocalFileParserFactory.js'
|
|
41
|
+
import { CodeStyleTaskParser } from './adapters/parsers/task/CodeStyleTaskParser.js'
|
|
40
42
|
const { ERRORS, ASYNC_LIMIT, DEFAULT_FILE_PATTERN} = constants
|
|
41
43
|
const DEFAULT_SORT = [{ asc: u => isNumber(u.order) ? u.order : Infinity }, { asc: u => u.text }]
|
|
44
|
+
const STREAM_FALLBACK_REASONS = {
|
|
45
|
+
BLOCK_COMMENT_WRITE_BACK_CONTEXT_UNSUPPORTED: 'block-comment-write-back-context-unsupported',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function readStreamTasks(filePath, config) {
|
|
49
|
+
const parser = await getFileParser(filePath, config)
|
|
50
|
+
const tasks = []
|
|
51
|
+
let task
|
|
52
|
+
try {
|
|
53
|
+
while ((task = await parser.readTask())) {
|
|
54
|
+
tasks.push(task)
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
parser.close()
|
|
58
|
+
}
|
|
59
|
+
return tasks
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseCodeStreamTask(streamTask, config) {
|
|
63
|
+
if (!streamTask.content) return
|
|
64
|
+
const firstTaskLine = streamTask.content
|
|
65
|
+
.split(/\r?\n/)
|
|
66
|
+
.find((line) => line.trim())
|
|
67
|
+
if (!firstTaskLine) return
|
|
68
|
+
|
|
69
|
+
const parser = new CodeStyleTaskParser({
|
|
70
|
+
...config,
|
|
71
|
+
includeList: (list) => config.includeList(list),
|
|
72
|
+
listExists: (list) => config.includeList(list),
|
|
73
|
+
})
|
|
74
|
+
const parsed = parser.parseLine(firstTaskLine, streamTask.line)
|
|
75
|
+
if (!parsed) return
|
|
76
|
+
|
|
77
|
+
parsed.lastLine = streamTask.lastLine
|
|
78
|
+
return parsed
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toFileTask({ file, streamTask, config }) {
|
|
82
|
+
const parsedTask = streamTask.text
|
|
83
|
+
? streamTask
|
|
84
|
+
: parseCodeStreamTask(streamTask, config)
|
|
85
|
+
if (!parsedTask) return
|
|
86
|
+
|
|
87
|
+
const lineContent = file.getContentLines()[parsedTask.line - 1] || ''
|
|
88
|
+
const type = parsedTask.type || Task.Types.CODE
|
|
89
|
+
const taskStartOnLine = getTaskStartOnLine({ lineContent, parsedTask, type, config })
|
|
90
|
+
const linePos = file.getLinePos(parsedTask.line)
|
|
91
|
+
const tokenPrefix = type === Task.Types.HASHTAG ? config.tokenPrefix : ''
|
|
92
|
+
const rawTask = parsedTask.rawTask || getOriginalRawTask({
|
|
93
|
+
lineContent,
|
|
94
|
+
parsedTask,
|
|
95
|
+
taskStartOnLine,
|
|
96
|
+
type,
|
|
97
|
+
tokenPrefix,
|
|
98
|
+
config,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
return file.extractTaskWithDescription({
|
|
102
|
+
taskStartOnLine,
|
|
103
|
+
rawTask,
|
|
104
|
+
config,
|
|
105
|
+
list: parsedTask.list,
|
|
106
|
+
text: parsedTask.text,
|
|
107
|
+
order: parsedTask.order,
|
|
108
|
+
line: parsedTask.line,
|
|
109
|
+
type,
|
|
110
|
+
hasColon: !!parsedTask.colon,
|
|
111
|
+
content: file.getContent().substring(linePos),
|
|
112
|
+
pos: linePos + taskStartOnLine,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getTaskStartOnLine({ lineContent, parsedTask, type, config }) {
|
|
117
|
+
if (parsedTask.beforeText) return parsedTask.beforeText.length
|
|
118
|
+
if (type === Task.Types.CODE) {
|
|
119
|
+
return Math.max(0, lineContent.indexOf(parsedTask.list))
|
|
120
|
+
}
|
|
121
|
+
if (type === Task.Types.HASHTAG) {
|
|
122
|
+
const taskTokenIndex = lineContent.indexOf(`${config.tokenPrefix}${parsedTask.list}`)
|
|
123
|
+
if (taskTokenIndex > -1) return taskTokenIndex
|
|
124
|
+
}
|
|
125
|
+
return 0
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getOriginalRawTask({
|
|
129
|
+
lineContent,
|
|
130
|
+
parsedTask,
|
|
131
|
+
taskStartOnLine,
|
|
132
|
+
type,
|
|
133
|
+
tokenPrefix,
|
|
134
|
+
config,
|
|
135
|
+
}) {
|
|
136
|
+
if (type !== Task.Types.CODE) {
|
|
137
|
+
return lineContent.slice(taskStartOnLine)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return getRawTask({
|
|
141
|
+
tokenPrefix,
|
|
142
|
+
orderMeta: config.orderMeta,
|
|
143
|
+
beforeText: parsedTask.beforeText,
|
|
144
|
+
hasColon: !!parsedTask.colon,
|
|
145
|
+
list: parsedTask.list,
|
|
146
|
+
order: parsedTask.order,
|
|
147
|
+
text: parsedTask.text,
|
|
148
|
+
type,
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function attachStreamTasks({ file, streamTasks, config }) {
|
|
153
|
+
file.tasks = []
|
|
154
|
+
streamTasks.forEach((streamTask) => {
|
|
155
|
+
const task = toFileTask({ file, streamTask, config })
|
|
156
|
+
if (!task) return
|
|
157
|
+
file.addTask(task)
|
|
158
|
+
file.emit('task.found', task)
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getStreamWriteBackFallbackReason(streamTasks) {
|
|
163
|
+
if (streamTasks.some((streamTask) => streamTask.inBlockComment)) {
|
|
164
|
+
return STREAM_FALLBACK_REASONS.BLOCK_COMMENT_WRITE_BACK_CONTEXT_UNSUPPORTED
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function emitParserDiagnostic(repo, file, diagnostic) {
|
|
169
|
+
repo.emit('file.parser', {
|
|
170
|
+
path: file.path,
|
|
171
|
+
...diagnostic,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
42
174
|
|
|
43
175
|
function getTasksByList (
|
|
44
176
|
repo,
|
|
@@ -768,41 +900,38 @@ export class Repository extends Emitter {
|
|
|
768
900
|
|
|
769
901
|
file.on('task.found', this.taskFoundListener)
|
|
770
902
|
file.on('task.modified', this.taskModifiedListener)
|
|
771
|
-
const fileContent = file.content
|
|
772
|
-
|
|
773
|
-
logger.debug(`[INSTRUMENT] About to call extractAndTransformTasks, content length: ${fileContent?.length || 0}`)
|
|
774
|
-
file.extractAndTransformTasks(this.getConfig())
|
|
775
903
|
|
|
776
|
-
const
|
|
777
|
-
|
|
904
|
+
const streamTasks = await readStreamTasks(this.getFullPath(file), this.getConfig())
|
|
905
|
+
const fallbackReason = getStreamWriteBackFallbackReason(streamTasks)
|
|
906
|
+
if (fallbackReason) {
|
|
907
|
+
file.extractAndTransformTasks(this.getConfig())
|
|
908
|
+
emitParserDiagnostic(this, file, {
|
|
909
|
+
mode: 'legacy-fallback',
|
|
910
|
+
reason: fallbackReason,
|
|
911
|
+
})
|
|
778
912
|
|
|
779
|
-
if (!file.isModified() || fileContent === file.content) {
|
|
780
913
|
this.resetFile(file)
|
|
781
|
-
const
|
|
782
|
-
logger.debug(`[INSTRUMENT] extractTasks END (
|
|
914
|
+
const fallbackTotalTime = Date.now() - extractStartTime
|
|
915
|
+
logger.debug(`[INSTRUMENT] extractTasks END (legacy-fallback) - total ${fallbackTotalTime}ms`)
|
|
783
916
|
return file
|
|
784
917
|
}
|
|
785
918
|
|
|
786
|
-
|
|
787
|
-
file
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
919
|
+
const fileContent = file.content
|
|
920
|
+
attachStreamTasks({ file, streamTasks, config: this.getConfig() })
|
|
921
|
+
file.transformTasks(this.getConfig())
|
|
922
|
+
emitParserDiagnostic(this, file, {
|
|
923
|
+
mode: 'stream',
|
|
924
|
+
})
|
|
791
925
|
|
|
792
|
-
if (file.
|
|
793
|
-
logger.debug(
|
|
926
|
+
if (file.isModified() && fileContent !== file.content) {
|
|
927
|
+
logger.debug('[INSTRUMENT] About to writeFile (stream re-extract flow)')
|
|
794
928
|
await this.writeFile(file)
|
|
795
|
-
|
|
929
|
+
file.extractTasks(this.getConfig())
|
|
796
930
|
}
|
|
797
931
|
|
|
798
932
|
this.resetFile(file)
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
if (totalTime > 1000) {
|
|
802
|
-
logger.warn(`[INSTRUMENT] ⚠️ SLOW extractTasks: ${file.path} took ${totalTime}ms`)
|
|
803
|
-
}
|
|
804
|
-
logger.debug(`[INSTRUMENT] extractTasks END - total ${totalTime}ms`)
|
|
805
|
-
|
|
933
|
+
const streamTotalTime = Date.now() - extractStartTime
|
|
934
|
+
logger.debug(`[INSTRUMENT] extractTasks END (stream) - total ${streamTotalTime}ms`)
|
|
806
935
|
return file
|
|
807
936
|
}
|
|
808
937
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './module-import.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url'
|
|
2
|
+
|
|
3
|
+
const WINDOWS_DRIVE_PATH = /^[a-zA-Z]:[\\/]/
|
|
4
|
+
const WINDOWS_UNC_PATH = /^\\\\[^\\]+\\[^\\]+/
|
|
5
|
+
|
|
6
|
+
export function toFileImportSpecifier(filePath) {
|
|
7
|
+
const value = String(filePath)
|
|
8
|
+
if (/^(file|data|node):/i.test(value)) return value
|
|
9
|
+
if (WINDOWS_DRIVE_PATH.test(value)) return windowsDrivePathToFileURL(value)
|
|
10
|
+
if (WINDOWS_UNC_PATH.test(value)) return windowsUncPathToFileURL(value)
|
|
11
|
+
|
|
12
|
+
return pathToFileURL(filePath).href
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function windowsDrivePathToFileURL(filePath) {
|
|
16
|
+
const normalizedPath = filePath.replace(/\\/g, '/')
|
|
17
|
+
return `file:///${encodePathSegments(normalizedPath)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function windowsUncPathToFileURL(filePath) {
|
|
21
|
+
const normalizedPath = filePath.replace(/\\/g, '/').replace(/^\/+/, '')
|
|
22
|
+
const [host, ...pathSegments] = normalizedPath.split('/')
|
|
23
|
+
return `file://${host}/${pathSegments.map(encodeURIComponent).join('/')}`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function encodePathSegments(filePath) {
|
|
27
|
+
return filePath
|
|
28
|
+
.split('/')
|
|
29
|
+
.map(segment => /^[a-zA-Z]:$/.test(segment) ? segment : encodeURIComponent(segment))
|
|
30
|
+
.join('/')
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imdone-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "imdone-core",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"types": "types/index.d.ts",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"bin": {
|
|
13
|
-
"imdone-
|
|
13
|
+
"imdone-core": "lib/cli.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"prepublishOnly": "
|
|
16
|
+
"prepublishOnly": "node scripts/prepublish-only.js",
|
|
17
17
|
"build": "npm run pre-commit && npm run coverage",
|
|
18
18
|
"build:docs": "doctoc --github --notitle README.md",
|
|
19
19
|
"build:docs:index": "node scripts/index-markdown.js",
|
|
@@ -2,6 +2,16 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
4
|
const LIB_DIR = './lib';
|
|
5
|
+
const VALID_NAMESPACE_EXPORT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
6
|
+
|
|
7
|
+
function isIndexableDirectory(entry) {
|
|
8
|
+
return (
|
|
9
|
+
entry.isDirectory() &&
|
|
10
|
+
!['__tests__', '__fixtures__'].includes(entry.name) &&
|
|
11
|
+
!entry.name.startsWith('.') &&
|
|
12
|
+
VALID_NAMESPACE_EXPORT.test(entry.name)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
5
15
|
|
|
6
16
|
// Recursively generates index.js files
|
|
7
17
|
function generateIndexes(dir) {
|
|
@@ -12,7 +22,7 @@ function generateIndexes(dir) {
|
|
|
12
22
|
.map(entry => `export * from './${entry.name}';`);
|
|
13
23
|
|
|
14
24
|
const subdirs = entries
|
|
15
|
-
.filter(
|
|
25
|
+
.filter(isIndexableDirectory)
|
|
16
26
|
.map(entry => entry.name);
|
|
17
27
|
|
|
18
28
|
// Generate index.js in each subdirectory
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
const TRUE_VALUES = new Set(['1', 'true', 'yes', 'on'])
|
|
4
|
+
|
|
5
|
+
function isTruthy(value) {
|
|
6
|
+
return TRUE_VALUES.has(String(value || '').toLowerCase())
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function shouldSkipPrepublishOnly() {
|
|
10
|
+
return isTruthy(process.env.NO_PREPUBLISH)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (shouldSkipPrepublishOnly()) {
|
|
14
|
+
console.log('Skipping prepublishOnly because skip prepublish override is enabled.')
|
|
15
|
+
process.exit(0)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const build = spawn('npm', ['run', 'build'], {
|
|
19
|
+
stdio: 'inherit',
|
|
20
|
+
shell: process.platform === 'win32',
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
build.on('exit', (code) => {
|
|
24
|
+
process.exit(code ?? 1)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
build.on('error', (error) => {
|
|
28
|
+
console.error(`Unable to run prepublishOnly build: ${error.message}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
})
|
|
@@ -3,11 +3,9 @@ export class ReadStreamCodeParser extends ReadStreamTaskParser {
|
|
|
3
3
|
lists: any;
|
|
4
4
|
filePath: any;
|
|
5
5
|
lang: any;
|
|
6
|
-
taskParsers: CodeStyleTaskParser[];
|
|
7
6
|
init(): Promise<void>;
|
|
8
7
|
cards: any[] | undefined;
|
|
9
|
-
parseCards(
|
|
10
|
-
parseComments(
|
|
8
|
+
parseCards(tokens?: string[]): Promise<any[]>;
|
|
9
|
+
parseComments(): Promise<{}>;
|
|
11
10
|
}
|
|
12
11
|
import { ReadStreamTaskParser } from './ReadStreamTaskParser.js';
|
|
13
|
-
import { CodeStyleTaskParser } from '../task/CodeStyleTaskParser.js';
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
export class ReadStreamMarkdownParser extends ReadStreamTaskParser {
|
|
2
2
|
constructor(readStream: any, config: any);
|
|
3
|
+
frontmatterLines: any[];
|
|
4
|
+
collectingFrontmatter: boolean;
|
|
5
|
+
frontmatterClosed: boolean;
|
|
6
|
+
ignoredByFrontmatter: boolean;
|
|
7
|
+
currentLineIsFrontmatter: boolean;
|
|
8
|
+
currentLineIsCodeFence: boolean;
|
|
9
|
+
inCodeFence: boolean;
|
|
3
10
|
taskParsers: (HashStyleTaskParser | MarkdownStyleTaskParser)[];
|
|
11
|
+
onLineRead(line: any): void;
|
|
12
|
+
updateFrontmatterState(line: any): void;
|
|
13
|
+
updateIgnoredByFrontmatter(): void;
|
|
14
|
+
updateCodeFenceState(line: any): void;
|
|
4
15
|
}
|
|
5
16
|
import { ReadStreamTaskParser } from './ReadStreamTaskParser.js';
|
|
6
17
|
import { HashStyleTaskParser } from '../task/HashStyleTaskParser.js';
|
|
@@ -7,10 +7,14 @@ export class ReadStreamTaskParser {
|
|
|
7
7
|
lineNo: number;
|
|
8
8
|
blankLines: number;
|
|
9
9
|
readInterface: ReadLine.Interface;
|
|
10
|
+
lineIterator: NodeJS.AsyncIterator<string, any, any>;
|
|
10
11
|
readTask(): Promise<any>;
|
|
12
|
+
nextTask: any;
|
|
11
13
|
readLine(): Promise<IteratorResult<string, any>>;
|
|
14
|
+
onLineRead(): void;
|
|
15
|
+
shouldParseTaskLine(): boolean;
|
|
16
|
+
getLineForTaskParsing(line: any): any;
|
|
12
17
|
isEndOfTask(line: any): boolean;
|
|
13
|
-
nextTask: any;
|
|
14
18
|
getTaskFromLine(line: any): undefined;
|
|
15
19
|
addLineToTask(line: any): void;
|
|
16
20
|
trimLinesToEndTask(): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export class CheckStyleTaskParser extends TaskParser {
|
|
2
|
-
constructor();
|
|
2
|
+
constructor(config: any);
|
|
3
3
|
get pattern(): RegExp;
|
|
4
4
|
parse(lineContent: any, line: any, matchResult: any, task: any): {
|
|
5
5
|
beforeText: any;
|
|
6
6
|
text: any;
|
|
7
|
+
list: any;
|
|
7
8
|
line: any;
|
|
8
9
|
checked: any;
|
|
9
10
|
type: any;
|
package/types/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./module-import.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function toFileImportSpecifier(filePath: any): string;
|