imdone-core 2.0.0 → 2.0.2
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/.dependency-cruiser.cjs +412 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/workflows/ci.yml +24 -0
- package/.imdoneignore +13 -0
- package/.mocharc.json +10 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +160 -0
- package/_index.md +7 -0
- package/_navbar.md +1 -0
- package/build.js +57 -0
- package/esm-migration.md +446 -0
- package/index.html +184 -0
- package/lib/FileProjectContext.js +13 -0
- package/lib/ProjectContext.js +43 -0
- package/lib/adapters/date-time.js +15 -0
- package/lib/adapters/environment.js +3 -0
- package/lib/adapters/file-gateway.js +154 -0
- package/lib/adapters/git-download.js +55 -0
- package/lib/adapters/index.js +9 -0
- package/lib/adapters/logger.js +136 -0
- package/lib/adapters/markdown.js +178 -0
- package/lib/adapters/parsers/DateLanguageParser.js +12 -0
- package/lib/adapters/parsers/content-transformer.js +116 -0
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +36 -0
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +126 -0
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +17 -0
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +106 -0
- package/lib/adapters/parsers/file/index.js +4 -0
- package/lib/adapters/parsers/function-parser.js +25 -0
- package/lib/adapters/parsers/index.js +5 -0
- package/lib/adapters/parsers/task/CardContentParser.js +367 -0
- package/lib/adapters/parsers/task/CheckStyleTaskParser.js +28 -0
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +27 -0
- package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/TaskParser.js +38 -0
- package/lib/adapters/parsers/task/index.js +6 -0
- package/lib/adapters/storage/config.js +44 -0
- package/lib/adapters/storage/index.js +2 -0
- package/lib/adapters/storage/tags.js +29 -0
- package/lib/adapters/yaml.js +9 -0
- package/lib/card.js +299 -0
- package/lib/checksum.js +22 -0
- package/lib/config.js +249 -0
- package/lib/constants.js +68 -0
- package/lib/context/ApplicationContext.js +35 -0
- package/lib/context/index.js +1 -0
- package/lib/default-settings.js +37 -0
- package/lib/domain/entities/View.js +23 -0
- package/lib/domain/entities/index.js +1 -0
- package/lib/domain/index.js +2 -0
- package/lib/domain/usecases/GetViewsUsecase.js +44 -0
- package/lib/domain/usecases/SaveViewUsecase.js +51 -0
- package/lib/domain/usecases/index.js +2 -0
- package/lib/file.js +1391 -0
- package/lib/get-tasks-to-modify.js +117 -0
- package/lib/languages.js +113 -0
- package/lib/list.js +56 -0
- package/lib/migrate-config.js +165 -0
- package/lib/mixins/index.js +2 -0
- package/lib/mixins/repo-fs-store.js +359 -0
- package/lib/mixins/repo-watched-fs-store.js +206 -0
- package/lib/plugins/archive-plugin.js +72 -0
- package/lib/plugins/default-board-actions-plugin.js +56 -0
- package/lib/plugins/default-board-properties-plugin.js +101 -0
- package/lib/plugins/default-plugins.js +7 -0
- package/lib/plugins/epic-plugin.js +177 -0
- package/lib/plugins/extension-plugin.js +94 -0
- package/lib/plugins/index.js +9 -0
- package/lib/plugins/persist-tags-plugin.js +14 -0
- package/lib/plugins/plugin-manager.js +458 -0
- package/lib/plugins/plugin-registry.js +57 -0
- package/lib/project-factory.js +42 -0
- package/lib/project.js +864 -0
- package/lib/repository.js +1514 -0
- package/lib/task.js +780 -0
- package/lib/tools.js +176 -0
- package/lib/usecases/actions.js +32 -0
- package/lib/usecases/get-imdone-url.js +6 -0
- package/lib/usecases/get-project-tags.js +7 -0
- package/lib/usecases/get-tasks-in-file.js +26 -0
- package/lib/usecases/index.js +4 -0
- package/package.json +2 -7
- package/scripts/delete-markdown-indexes.js +87 -0
- package/scripts/generate-index.js +35 -0
- package/scripts/index-markdown.js +55 -0
- package/tsconfig.json +15 -0
- package/types/FileProjectContext.d.ts +4 -0
- package/types/ProjectContext.d.ts +9 -0
- package/types/adapters/date-time.d.ts +2 -0
- package/types/adapters/environment.d.ts +1 -0
- package/types/adapters/file-gateway.d.ts +51 -0
- package/types/adapters/git-download.d.ts +1 -0
- package/types/adapters/index.d.ts +9 -0
- package/types/adapters/logger.d.ts +24 -0
- package/types/adapters/markdown.d.ts +13 -0
- package/types/adapters/parsers/DateLanguageParser.d.ts +1 -0
- package/types/adapters/parsers/content-transformer.d.ts +10 -0
- package/types/adapters/parsers/file/LocalFileParserFactory.d.ts +5 -0
- package/types/adapters/parsers/file/ReadStreamCodeParser.d.ts +13 -0
- package/types/adapters/parsers/file/ReadStreamMarkdownParser.d.ts +7 -0
- package/types/adapters/parsers/file/ReadStreamTaskParser.d.ts +19 -0
- package/types/adapters/parsers/file/index.d.ts +4 -0
- package/types/adapters/parsers/function-parser.d.ts +1 -0
- package/types/adapters/parsers/index.d.ts +5 -0
- package/types/adapters/parsers/task/CardContentParser.d.ts +45 -0
- package/types/adapters/parsers/task/CheckStyleTaskParser.d.ts +12 -0
- package/types/adapters/parsers/task/CodeStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/HashStyleTaskParser.d.ts +13 -0
- package/types/adapters/parsers/task/MarkdownStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/TaskParser.d.ts +7 -0
- package/types/adapters/parsers/task/index.d.ts +6 -0
- package/types/adapters/storage/config.d.ts +6 -0
- package/types/adapters/storage/index.d.ts +2 -0
- package/types/adapters/storage/tags.d.ts +3 -0
- package/types/adapters/yaml.d.ts +2 -0
- package/types/card.d.ts +166 -0
- package/types/checksum.d.ts +8 -0
- package/types/config.d.ts +61 -0
- package/types/constants.d.ts +46 -0
- package/types/context/ApplicationContext.d.ts +24 -0
- package/types/context/index.d.ts +1 -0
- package/types/default-settings.d.ts +2 -0
- package/types/domain/entities/FileProjectContext.d.ts +5 -0
- package/types/domain/entities/View.d.ts +17 -0
- package/types/domain/entities/index.d.ts +1 -0
- package/types/domain/index.d.ts +2 -0
- package/types/domain/usecases/GetViewsUsecase.d.ts +4 -0
- package/types/domain/usecases/SaveViewUsecase.d.ts +8 -0
- package/types/domain/usecases/index.d.ts +2 -0
- package/types/file.d.ts +344 -0
- package/types/get-tasks-to-modify.d.ts +3 -0
- package/types/index.d.ts +23 -0
- package/types/languages.d.ts +649 -0
- package/types/list.d.ts +30 -0
- package/types/migrate-config.d.ts +1 -0
- package/types/mixins/index.d.ts +2 -0
- package/types/mixins/repo-fs-store.d.ts +2 -0
- package/types/mixins/repo-watched-fs-store.d.ts +2 -0
- package/types/plugins/archive-plugin.d.ts +8 -0
- package/types/plugins/default-board-actions-plugin.d.ts +9 -0
- package/types/plugins/default-board-properties-plugin.d.ts +16 -0
- package/types/plugins/default-plugins.d.ts +6 -0
- package/types/plugins/epic-plugin.d.ts +19 -0
- package/types/plugins/extension-plugin.d.ts +15 -0
- package/types/plugins/index.d.ts +9 -0
- package/types/plugins/persist-tags-plugin.d.ts +4 -0
- package/types/plugins/plugin-manager.d.ts +68 -0
- package/types/plugins/plugin-registry.d.ts +1 -0
- package/types/project-factory.d.ts +14 -0
- package/types/project.d.ts +158 -0
- package/types/repository.d.ts +996 -0
- package/types/task.d.ts +202 -0
- package/types/tools.d.ts +29 -0
- package/types/usecases/actions.d.ts +7 -0
- package/types/usecases/get-imdone-url.d.ts +1 -0
- package/types/usecases/get-project-tags.d.ts +1 -0
- package/types/usecases/get-tasks-in-file.d.ts +4 -0
- package/types/usecases/index.d.ts +4 -0
- package/vitest.config.js +13 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import Plugin from 'imdone-api'
|
|
2
|
+
import { getIsoDateWithOffset } from '../adapters/date-time.js'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
const simpleTemplate = '\n<!--\ncreated:${timestamp}\n-->\n'
|
|
6
|
+
|
|
7
|
+
const codeSpan = (value) => '`' + value + '`';
|
|
8
|
+
const codeBlock = (value, language = '') => '```' + language + '\n' + value + '\n```';
|
|
9
|
+
|
|
10
|
+
function flattenObject(object) {
|
|
11
|
+
return Object.keys(object).reduce((acc, key) => {
|
|
12
|
+
return { ...acc, [`template_${key}`]: object[key] };
|
|
13
|
+
}, {});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default class DefaultBoardPropertiesPlugin extends Plugin {
|
|
17
|
+
constructor(project) {
|
|
18
|
+
super(project)
|
|
19
|
+
this.templates = {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async onBeforeBoardUpdate() {
|
|
23
|
+
this.templates = await this.getTemplates()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getBoardProperties() {
|
|
27
|
+
const templates = flattenObject(this.templates);
|
|
28
|
+
const clearFilterURL = this.getFilterURL('');
|
|
29
|
+
const getFilterURL = (filter) => this.getFilterURL(filter);
|
|
30
|
+
const getFilterLink = (filter) => `[Filter By: ${filter}](${this.getFilterURL(filter)})`;
|
|
31
|
+
// TODO Make board properties self documenting
|
|
32
|
+
// <!--
|
|
33
|
+
// #imdone-1.53.16
|
|
34
|
+
// #feature
|
|
35
|
+
// created:2025-02-14T16:54:22-05:00
|
|
36
|
+
// order:-185
|
|
37
|
+
// -->
|
|
38
|
+
// [Feature Description]
|
|
39
|
+
// ## :ballot_box_with_check: Tasks
|
|
40
|
+
// - [ ] Add tasks here
|
|
41
|
+
// ## :white_check_mark: DoD
|
|
42
|
+
// - [ ] Code complete (No Tech Debt)
|
|
43
|
+
// - [ ] Update tests
|
|
44
|
+
// - [ ] Automate what's working
|
|
45
|
+
// - [ ] Update tutorial project
|
|
46
|
+
// - [ ] Update documentation
|
|
47
|
+
// - [ ] Run like a new user and make the experience better
|
|
48
|
+
// - [ ] Make sure the first card is expanded by default or in view mode
|
|
49
|
+
// - [ ] Make sure global and default settings isn't modified when opened for the first time
|
|
50
|
+
return {
|
|
51
|
+
date: `${getIsoDateWithOffset(new Date()).substring(0, 10)}`,
|
|
52
|
+
timestamp: getIsoDateWithOffset(new Date()),
|
|
53
|
+
codeSpan,
|
|
54
|
+
codeBlock,
|
|
55
|
+
clearFilterURL,
|
|
56
|
+
getFilterURL,
|
|
57
|
+
getFilterLink,
|
|
58
|
+
...templates
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getFilterURL(filter) {
|
|
63
|
+
filter = filter.replace(/\(/g, '%28').replace(/\)/g, '%29');
|
|
64
|
+
return encodeURI(`imdone://${this.project.path}?filter=${filter}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async getTemplates() {
|
|
69
|
+
const { preparePathForWriting, readdir, readFile, stat, exists } = this.project.fileGateway;
|
|
70
|
+
const templateDir = path.join(this.project.configDir, 'templates');
|
|
71
|
+
const githubDir = path.join(this.project.path, '.github');
|
|
72
|
+
|
|
73
|
+
await preparePathForWriting(templateDir, true);
|
|
74
|
+
|
|
75
|
+
const readMarkdownFiles = async (dir, prefix = '') => {
|
|
76
|
+
const files = await readdir(dir);
|
|
77
|
+
const readFilePromises = files.map(async (file) => {
|
|
78
|
+
const filePath = path.join(dir, file);
|
|
79
|
+
const fileStat = await stat(filePath);
|
|
80
|
+
if (fileStat && fileStat.isDirectory()) {
|
|
81
|
+
return readMarkdownFiles(filePath, `${prefix}${file}_`);
|
|
82
|
+
} else if (file.endsWith('.md')) {
|
|
83
|
+
const data = await readFile(filePath, 'utf-8');
|
|
84
|
+
const templateName = `${prefix}${file.replace('.md', '').replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
85
|
+
return { [templateName]: data };
|
|
86
|
+
}
|
|
87
|
+
return {};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const allTemplates = await Promise.all(readFilePromises);
|
|
91
|
+
return allTemplates.reduce((acc, template) => ({ ...acc, ...template }), {});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const templatesFromTemplateDir = await readMarkdownFiles(templateDir);
|
|
95
|
+
const templatesFromGithubDir = await exists(githubDir)
|
|
96
|
+
? await readMarkdownFiles(githubDir, 'github_')
|
|
97
|
+
: {};
|
|
98
|
+
|
|
99
|
+
return { simple: simpleTemplate, ...templatesFromTemplateDir, ...templatesFromGithubDir };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as ArchivePlugin from './archive-plugin.js'
|
|
2
|
+
export * as DefaultBoardActionsPlugin from './default-board-actions-plugin.js'
|
|
3
|
+
export * as DefaultBoardPropertiesPlugin from './default-board-properties-plugin.js'
|
|
4
|
+
export * as EpicPlugin from './epic-plugin.js'
|
|
5
|
+
export * as PersistTagsPlugin from './persist-tags-plugin.js'
|
|
6
|
+
export * as ExtensionPlugin from './extension-plugin.js'
|
|
7
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Plugin from 'imdone-api'
|
|
2
|
+
import { logger } from '../adapters/logger.js'
|
|
3
|
+
|
|
4
|
+
function getIsEpicMeta(task) {
|
|
5
|
+
return task.allMeta && task.allMeta['is-epic'] && task.allMeta['is-epic'][0]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getEpicMeta(task) {
|
|
9
|
+
return task.allMeta && task.allMeta['epic'] && task.allMeta['epic'][0]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default class EpicPlugin extends Plugin {
|
|
13
|
+
constructor(project) {
|
|
14
|
+
super(project)
|
|
15
|
+
this.epics = {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async onBeforeBoardUpdate() {
|
|
19
|
+
this.epics = {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onBoardUpdate(lists) {
|
|
23
|
+
// this html is getting initialized before onBoardUpdate
|
|
24
|
+
logger.log(
|
|
25
|
+
`${this.project.name} epics found:`,
|
|
26
|
+
Object.keys(this.epics).length
|
|
27
|
+
)
|
|
28
|
+
logger.time(`${this.project.name} Epic updates`)
|
|
29
|
+
this.updateCardMD(lists)
|
|
30
|
+
logger.timeEnd(`${this.project.name} Epic updates`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
onTaskUpdate(task) {
|
|
34
|
+
const isEpicMeta = getIsEpicMeta(task)
|
|
35
|
+
const epicMeta = getEpicMeta(task)
|
|
36
|
+
if (!isEpicMeta && !epicMeta) return
|
|
37
|
+
|
|
38
|
+
if (isEpicMeta && epicMeta) {
|
|
39
|
+
delete task.allMeta.epic
|
|
40
|
+
task.allMeta['is-epic'] = [epicMeta]
|
|
41
|
+
this.addEpic(epicMeta, task)
|
|
42
|
+
} else if (epicMeta) {
|
|
43
|
+
this.addEpicItem(epicMeta, task)
|
|
44
|
+
} else if (isEpicMeta) {
|
|
45
|
+
this.addEpic(isEpicMeta, task)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
initEpic(name) {
|
|
50
|
+
if (!this.epics[name]) {
|
|
51
|
+
this.epics[name] = {
|
|
52
|
+
items: {},
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
addEpic(name, task) {
|
|
58
|
+
this.initEpic(name)
|
|
59
|
+
this.epics[name].epic = task
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
addEpicItem(name, task) {
|
|
63
|
+
const list = task.list
|
|
64
|
+
this.initEpic(name)
|
|
65
|
+
if (!this.getEpicItemsInList(name, list)) this.epics[name].items[list] = []
|
|
66
|
+
if (this.getEpicItemsInList(name, list).find((t) => t.id === task.id)) return
|
|
67
|
+
this.epics[name].items[list].push(task)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get epicNames() {
|
|
71
|
+
return Object.keys(this.epics)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getEpic(name) {
|
|
75
|
+
return this.epics[name].epic
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getEpicItems(name) {
|
|
79
|
+
return this.epics[name].items
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getEpicItemsInList(name, list) {
|
|
83
|
+
return this.epics[name].items[list]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
getEpicLists(name) {
|
|
88
|
+
return Object.keys(this.getEpicItems(name))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isListHidden(name, lists) {
|
|
92
|
+
return lists.find((list) => list.name === name).hidden
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
isListIgnored(name, lists) {
|
|
96
|
+
return lists.find((list) => list.name === name).ignore
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
updateCardMD(lists) {
|
|
100
|
+
this.epicNames.forEach((epicId) => {
|
|
101
|
+
const epic = this.getEpic(epicId)
|
|
102
|
+
const epicLists = this.getEpicLists(epicId)
|
|
103
|
+
if (!epic) return
|
|
104
|
+
if (epicLists.length === 0) return
|
|
105
|
+
if (
|
|
106
|
+
epic.list === this.project.doneList &&
|
|
107
|
+
(this.isListHidden(epic.list, lists) ||
|
|
108
|
+
this.isListIgnored(epic.list, lists))
|
|
109
|
+
) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const epicText = epic.text // removeMD(epic.text)
|
|
114
|
+
const epicFilter = encodeURIComponent(
|
|
115
|
+
`(allMeta.epic="${epicId}" or allMeta.is-epic="${epicId}")`
|
|
116
|
+
)
|
|
117
|
+
const epicFilePath = encodeURI(
|
|
118
|
+
`${epic.source.repoId}/${epic.source.path}`.replace(/\\/g, '/')
|
|
119
|
+
)
|
|
120
|
+
const epicUrl = `imdone://${epicFilePath}?line=${epic.line}&filter=${epicFilter}`
|
|
121
|
+
const epicLink = `**Epic:** [${epicText}](${epicUrl})\n\n`
|
|
122
|
+
|
|
123
|
+
// Add epic link to epic tasks
|
|
124
|
+
epicLists.forEach((list) => {
|
|
125
|
+
this.getEpicItemsInList(epicId, list).forEach((task) => {
|
|
126
|
+
task.interpretedContent = epicLink + task.interpretedContent
|
|
127
|
+
task.init()
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const taskList =
|
|
132
|
+
epicLists.length === 0
|
|
133
|
+
? '*None*\n'
|
|
134
|
+
: '\n<div class="imdone-epic-items">\n\n' +
|
|
135
|
+
this.getTaskListMD(lists, epicFilter, epicUrl, epicId) +
|
|
136
|
+
'\n\n</div>\n'
|
|
137
|
+
|
|
138
|
+
if (epic.allMeta.epic && epic.allMeta.epic.includes(epicId)) {
|
|
139
|
+
const index = epic.allMeta.epic.findIndex((value) => value === epicId)
|
|
140
|
+
epic.allMeta.epic.splice(index, 1)
|
|
141
|
+
}
|
|
142
|
+
epic.interpretedContent =
|
|
143
|
+
epic.interpretedContent +
|
|
144
|
+
`\n----\n## [Epic items](${epicUrl})\n${taskList}`
|
|
145
|
+
epic.init()
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getTaskListMD(lists, epicFilter, epicUrl, epicId) {
|
|
150
|
+
let content = ''
|
|
151
|
+
|
|
152
|
+
lists
|
|
153
|
+
.filter((list) => this.getEpicItemsInList(epicId, list.name))
|
|
154
|
+
.reverse()
|
|
155
|
+
.forEach((list) => {
|
|
156
|
+
const tasks = this.getEpicItemsInList(epicId, list.name)
|
|
157
|
+
const listUrl = epicUrl + encodeURIComponent(` and list=${list.name}`)
|
|
158
|
+
const listLink = `[${list.name}](${listUrl})`
|
|
159
|
+
content = content + '\n\n#### ' + listLink + '\n'
|
|
160
|
+
content += '<ul style="list-style: none;">'
|
|
161
|
+
content += tasks
|
|
162
|
+
.map((task) => {
|
|
163
|
+
const taskText = this.project.renderMarkdown(task.text, task.fullPath).replaceAll(/<p>|<\/p>/ig, '') // removeMD(task.text)
|
|
164
|
+
const checked =
|
|
165
|
+
task.list === this.project.doneList ? ' checked="true"' : ''
|
|
166
|
+
const taskFilePath = encodeURI(
|
|
167
|
+
`${task.source.repoId}/${task.source.path}`.replace(/\\/g, '/')
|
|
168
|
+
)
|
|
169
|
+
return `<li style="margin-left: 1rem; list-style: none;"><input type="checkbox" disabled="true"${checked}><a href="imdone://${taskFilePath}?line=${task.line}&filter=${epicFilter}" target="_blank">${taskText}</a></li>`
|
|
170
|
+
})
|
|
171
|
+
.join('\n')
|
|
172
|
+
content += '</ul>'
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return content
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import Plugin from 'imdone-api'
|
|
2
|
+
import _path from 'path'
|
|
3
|
+
import { exists } from '../adapters/file-gateway.js'
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { logger } from '../adapters/logger.js';
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const url = new URL(import.meta.url)
|
|
9
|
+
const __dirname = _path.dirname(url.pathname)
|
|
10
|
+
|
|
11
|
+
export default class ExtensionPlugin extends Plugin {
|
|
12
|
+
constructor(project) {
|
|
13
|
+
super(project)
|
|
14
|
+
logger.log('loading extensions')
|
|
15
|
+
this.configDir = _path.join(project.path, '.imdone')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async init() {
|
|
19
|
+
this.cardActionsFunction = await this.loadExtensionModule(
|
|
20
|
+
() => [],
|
|
21
|
+
'actions',
|
|
22
|
+
'card'
|
|
23
|
+
)
|
|
24
|
+
this.boardActionsFunction = await this.loadExtensionModule(
|
|
25
|
+
() => [],
|
|
26
|
+
'actions',
|
|
27
|
+
'board'
|
|
28
|
+
)
|
|
29
|
+
this.cardPropertiesFunction = await this.loadExtensionModule(
|
|
30
|
+
() => {
|
|
31
|
+
return {}
|
|
32
|
+
},
|
|
33
|
+
'properties',
|
|
34
|
+
'card'
|
|
35
|
+
)
|
|
36
|
+
this.boardPropertiesFunction = await this.loadExtensionModule(
|
|
37
|
+
() => {
|
|
38
|
+
return {}
|
|
39
|
+
},
|
|
40
|
+
'properties',
|
|
41
|
+
'board'
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
getCardProperties(task) {
|
|
45
|
+
return this.cardPropertiesFunction(task)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getBoardProperties() {
|
|
49
|
+
return await this.boardPropertiesFunction()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getCardActions(task) {
|
|
53
|
+
return this.cardActionsFunction(task)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getBoardActions() {
|
|
57
|
+
return this.boardActionsFunction().map(({ title, action, keys, icon }) => ({
|
|
58
|
+
title,
|
|
59
|
+
name: title,
|
|
60
|
+
action,
|
|
61
|
+
keys,
|
|
62
|
+
icon
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getConfigPath(relativePath) {
|
|
67
|
+
return _path.resolve(_path.join(this.configDir, ...relativePath) + '.js')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async loadExtensionModule(_default, ...path) {
|
|
71
|
+
let extension = _default
|
|
72
|
+
const extensionPath = this.getConfigPath(path)
|
|
73
|
+
try {
|
|
74
|
+
await exists(extensionPath)
|
|
75
|
+
delete require.cache[extensionPath]
|
|
76
|
+
extension = require(extensionPath)
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (e.code === 'ENOENT') {
|
|
79
|
+
logger.log('No extension found at:', extensionPath)
|
|
80
|
+
} else {
|
|
81
|
+
logger.warn('Error loading extension:', e.message)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof extension !== 'function') {
|
|
86
|
+
logger.warn(
|
|
87
|
+
`${extensionPath} does not export a function. Using default empty function. ${_default.toString()}`
|
|
88
|
+
)
|
|
89
|
+
extension = _default
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return extension
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './archive-plugin.js';
|
|
2
|
+
export * from './default-board-actions-plugin.js';
|
|
3
|
+
export * from './default-board-properties-plugin.js';
|
|
4
|
+
export * from './default-plugins.js';
|
|
5
|
+
export * from './epic-plugin.js';
|
|
6
|
+
export * from './extension-plugin.js';
|
|
7
|
+
export * from './persist-tags-plugin.js';
|
|
8
|
+
export * from './plugin-manager.js';
|
|
9
|
+
export * from './plugin-registry.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Plugin from 'imdone-api'
|
|
2
|
+
import { save } from '../adapters/storage/tags.js'
|
|
3
|
+
|
|
4
|
+
export default class PersistTagsPlugin extends Plugin {
|
|
5
|
+
|
|
6
|
+
constructor(project) {
|
|
7
|
+
super(project)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async onBoardUpdate() {
|
|
11
|
+
await save(this.project.allTags.sort(), this.project.path)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
}
|