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.
- 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 +180 -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 -6
- 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,458 @@
|
|
|
1
|
+
import Emitter from 'events'
|
|
2
|
+
import Plugin from 'imdone-api'
|
|
3
|
+
import _path from 'path'
|
|
4
|
+
import { downloadPlugin } from '../adapters/git-download.js'
|
|
5
|
+
import rimraf from 'rimraf'
|
|
6
|
+
import chokidar from 'chokidar'
|
|
7
|
+
import debounce from 'lodash.debounce'
|
|
8
|
+
import { exists, mkdir, readdir } from '../adapters/file-gateway.js'
|
|
9
|
+
import { appContext } from '../context/ApplicationContext.js'
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
|
|
12
|
+
import PersistTagsPlugin from './persist-tags-plugin.js'
|
|
13
|
+
import DefaultBoardPropertiesPlugin from './default-board-properties-plugin.js'
|
|
14
|
+
import DefaultBoardActionsPlugin from './default-board-actions-plugin.js'
|
|
15
|
+
import ArchivePlugin from './archive-plugin.js'
|
|
16
|
+
import EpicPlugin from './epic-plugin.js'
|
|
17
|
+
import ExtensionPlugin from './extension-plugin.js'
|
|
18
|
+
import { logger } from '../adapters/logger.js'
|
|
19
|
+
|
|
20
|
+
const require = createRequire(import.meta.url);
|
|
21
|
+
const url = new URL(import.meta.url)
|
|
22
|
+
const __dirname = _path.dirname(url.pathname)
|
|
23
|
+
|
|
24
|
+
export class PluginManager extends Emitter {
|
|
25
|
+
constructor(project) {
|
|
26
|
+
super()
|
|
27
|
+
this.project = project
|
|
28
|
+
this.defaultPlugins = [
|
|
29
|
+
PersistTagsPlugin,
|
|
30
|
+
DefaultBoardPropertiesPlugin,
|
|
31
|
+
DefaultBoardActionsPlugin,
|
|
32
|
+
ArchivePlugin,
|
|
33
|
+
EpicPlugin,
|
|
34
|
+
ExtensionPlugin,
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
this.pluginsMap = {}
|
|
38
|
+
this.pluginPath = _path.join(project.path, '.imdone', 'plugins')
|
|
39
|
+
this.onDevChange = debounce(this.onDevChange.bind(this), 1000)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async startDevMode() {
|
|
43
|
+
if (this.project && this.project.config.devMode && !this.watcher) {
|
|
44
|
+
if (!(await exists(this.pluginPath)))
|
|
45
|
+
await mkdir(this.pluginPath)
|
|
46
|
+
this.watcher = chokidar(this.pluginPath, {
|
|
47
|
+
ignored(path) {
|
|
48
|
+
return /node_modules/.test(path)
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
this.watcher.on('change', (path, root, stat) => {
|
|
52
|
+
this.onDevChange()
|
|
53
|
+
})
|
|
54
|
+
this.watcher.on('add', (path, root, stat) => {
|
|
55
|
+
this.onDevChange()
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
stopDevMode() {
|
|
61
|
+
this.watcher && this.watcher.close()
|
|
62
|
+
this.watcher = null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
initDevMode() {
|
|
66
|
+
if (!this.project.config.devMode) this.stopDevMode()
|
|
67
|
+
this.startDevMode()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async onDevChange() {
|
|
71
|
+
try {
|
|
72
|
+
await this.reloadPlugins()
|
|
73
|
+
this.emit('plugins-reloaded')
|
|
74
|
+
} catch {
|
|
75
|
+
logger.error('Error reloading plugins.', err)
|
|
76
|
+
throw err
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async reloadPlugins() {
|
|
81
|
+
this.destroyPlugins()
|
|
82
|
+
await this.loadPlugins()
|
|
83
|
+
await this.startDevMode()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async uninstallPlugin(pluginName) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
logger.log('Preparing to uninstall plugin:', pluginName)
|
|
89
|
+
const pluginClassName = Object.keys(this.pluginsMap).find((key) => {
|
|
90
|
+
return this.getPlugin(key).info.name === pluginName
|
|
91
|
+
})
|
|
92
|
+
if (!pluginClassName) {
|
|
93
|
+
return reject(new Error('Unable to find plugin:' + pluginName))
|
|
94
|
+
}
|
|
95
|
+
const { info } = this.getPlugin(pluginClassName)
|
|
96
|
+
const { path } = info
|
|
97
|
+
// const pkg = { version, name }
|
|
98
|
+
logger.log('Uninstalling:', info)
|
|
99
|
+
|
|
100
|
+
rimraf(path, (err) => {
|
|
101
|
+
if (err) return reject(err)
|
|
102
|
+
delete this.pluginsMap[pluginClassName]
|
|
103
|
+
this.emit('plugin-uninstalled', pluginName)
|
|
104
|
+
resolve()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async installPlugin({ name, version }) {
|
|
110
|
+
if (!(await exists(this.pluginPath)))
|
|
111
|
+
await mkdir(this.pluginPath)
|
|
112
|
+
const installPath = _path.join(this.pluginPath, name)
|
|
113
|
+
await downloadPlugin(version, installPath)
|
|
114
|
+
logger.log(`Done installing ${name}`)
|
|
115
|
+
await this.loadPlugin(installPath)
|
|
116
|
+
this.emit('plugin-installed', name)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async loadPlugins() {
|
|
120
|
+
for (const PluginClass of this.defaultPlugins) {
|
|
121
|
+
await this.createPlugin(PluginClass)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await this.loadInstalledPlugins()
|
|
125
|
+
await this.loadPluginsNotInstalled()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async loadInstalledPlugins() {
|
|
129
|
+
const path = await exists(this.pluginPath)
|
|
130
|
+
if (!path) {
|
|
131
|
+
await mkdir(this.pluginPath)
|
|
132
|
+
}
|
|
133
|
+
const paths = await readdir(this.pluginPath, { withFileTypes: true })
|
|
134
|
+
const pluginPaths = paths
|
|
135
|
+
.filter(
|
|
136
|
+
(entry) =>
|
|
137
|
+
entry.name !== 'node_modules' &&
|
|
138
|
+
(entry.isDirectory() || entry.isSymbolicLink())
|
|
139
|
+
)
|
|
140
|
+
.map((entry) => _path.join(this.pluginPath, entry.name))
|
|
141
|
+
|
|
142
|
+
for (const path of pluginPaths) {
|
|
143
|
+
await this.loadPlugin(path)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async loadPluginsNotInstalled() {
|
|
148
|
+
const availablePlugins = await appContext().pluginRegistry.getAvailablePlugins()
|
|
149
|
+
const configPluginNames = Object.keys(this.project.config.plugins)
|
|
150
|
+
if (!configPluginNames) return
|
|
151
|
+
const installedPluginNames = Object.keys(this.pluginsMap)
|
|
152
|
+
const pluginsNotInstalled = configPluginNames.filter(name => !installedPluginNames.includes(name))
|
|
153
|
+
for (const pluginName of pluginsNotInstalled) {
|
|
154
|
+
const plugin = availablePlugins.find(p => p.name === pluginName)
|
|
155
|
+
if (plugin && plugin.name) await this.installPlugin(plugin)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async loadPlugin(path) {
|
|
160
|
+
logger.log('Loading plugin: ', path)
|
|
161
|
+
const fullPath = path.endsWith('.js')
|
|
162
|
+
? _path.resolve(path)
|
|
163
|
+
: _path.join(_path.resolve(path), 'bundle.js')
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
// eslint-disable-next-line
|
|
167
|
+
delete require.cache[require.resolve(fullPath)]
|
|
168
|
+
// eslint-disable-next-line
|
|
169
|
+
const pluginClass = await import(_path.resolve(path))
|
|
170
|
+
const pluginInstance = await this.createPlugin(pluginClass.default, path)
|
|
171
|
+
return pluginInstance
|
|
172
|
+
} catch (e) {
|
|
173
|
+
logger.error(`Error loading plugin at: ${path}`, e)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getPackageInfo(path) {
|
|
178
|
+
if (!path) return {}
|
|
179
|
+
let info = { path }
|
|
180
|
+
try {
|
|
181
|
+
info = {...info, ...require(`${path}/package.json`) }
|
|
182
|
+
delete info.dependencies
|
|
183
|
+
delete info.devDependencies
|
|
184
|
+
delete info.scripts
|
|
185
|
+
delete info.main
|
|
186
|
+
} catch (e) {
|
|
187
|
+
logger.info('No info on plugin:', path)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async createPlugin(pluginClass, path = undefined) {
|
|
192
|
+
if (!this.isPlugin(pluginClass))
|
|
193
|
+
throw new Error(`${pluginClass.name} is not a plugin`)
|
|
194
|
+
const name = pluginClass.name.toString()
|
|
195
|
+
const pluginInstance = new pluginClass(this.project)
|
|
196
|
+
let info = {
|
|
197
|
+
name,
|
|
198
|
+
...this.getPackageInfo(path),
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
pluginInstance.getSettings = () => {
|
|
202
|
+
return this.getPluginSettings(name)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.pluginsMap[name] = {
|
|
206
|
+
pluginInstance,
|
|
207
|
+
pluginClass,
|
|
208
|
+
info,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (pluginInstance.init) await pluginInstance.init()
|
|
212
|
+
|
|
213
|
+
return pluginInstance
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
destroyPlugins() {
|
|
217
|
+
this.stopDevMode()
|
|
218
|
+
this.eachPlugin(({ pluginInstance }) => {
|
|
219
|
+
try {
|
|
220
|
+
pluginInstance.destroy()
|
|
221
|
+
} catch (e) {
|
|
222
|
+
this.pluginError('destroy', pluginInstance, e)
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
isPlugin(pluginClass) {
|
|
228
|
+
// const pluginClassName = Plugin.name.toString()
|
|
229
|
+
// return pluginClass.proname.toString() === pluginClassName
|
|
230
|
+
const pluginClassPrototypeName = Object.getPrototypeOf(pluginClass.prototype).constructor.name
|
|
231
|
+
const pluginClassName = Plugin.name.toString()
|
|
232
|
+
logger.log('pluginClassPrototypeName', pluginClassPrototypeName)
|
|
233
|
+
logger.log('pluginClassName', pluginClassName)
|
|
234
|
+
return pluginClassPrototypeName === pluginClassName
|
|
235
|
+
|
|
236
|
+
// return Object.getPrototypeOf(pluginClass.prototype) === Plugin.prototype
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
eachPlugin(cb) {
|
|
240
|
+
Object.keys(this.pluginsMap).forEach((key) => {
|
|
241
|
+
cb(this.getPlugin(key))
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async eachPluginAsync(cb) {
|
|
246
|
+
for (const key of Object.keys(this.pluginsMap)) {
|
|
247
|
+
await cb(this.getPlugin(key))
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getPlugins() {
|
|
252
|
+
return Object.keys(this.pluginsMap).map((key) => {
|
|
253
|
+
const { info, pluginInstance } = this.getPlugin(key)
|
|
254
|
+
const schema = pluginInstance.getSettingsSchema()
|
|
255
|
+
if (schema) {
|
|
256
|
+
schema.id = info.name
|
|
257
|
+
schema.title = `${info.name} settings`
|
|
258
|
+
}
|
|
259
|
+
return { ...info, schema }
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
disablePlugin(name) {
|
|
264
|
+
delete this.pluginsMap[name]
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
getPluginName(pluginInstance) {
|
|
268
|
+
return Object.keys(this.pluginsMap).find(
|
|
269
|
+
(pluginName) =>
|
|
270
|
+
this.getPlugin(pluginName).pluginInstance === pluginInstance
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getPluginInstance(name) {
|
|
275
|
+
const plugin = this.getPlugin(name)
|
|
276
|
+
if (!plugin) throw new Error(`Plugin ${name} not found`)
|
|
277
|
+
return plugin.pluginInstance
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
getPlugin(name) {
|
|
281
|
+
return this.pluginsMap[name]
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
getPluginSettings(name) {
|
|
285
|
+
return this.project.config.plugins[name] || {}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
pluginError(method, pluginInstance, error) {
|
|
289
|
+
logger.warn(
|
|
290
|
+
`Plugin: ${this.getPluginName(
|
|
291
|
+
pluginInstance
|
|
292
|
+
)} threw an error on ${method}: `,
|
|
293
|
+
error
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async onBoardUpdate(lists) {
|
|
298
|
+
if (!lists || lists.length == 0) return
|
|
299
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
300
|
+
// const timeLabel = `${this.getPluginName(pluginInstance)} onBoardUpdate time`
|
|
301
|
+
// logger.time(timeLabel)
|
|
302
|
+
try {
|
|
303
|
+
await pluginInstance.onBoardUpdate(lists)
|
|
304
|
+
} catch (e) {
|
|
305
|
+
this.pluginError('onBoardUpdate', pluginInstance, e)
|
|
306
|
+
}
|
|
307
|
+
// logger.timeEnd(timeLabel)
|
|
308
|
+
})
|
|
309
|
+
return lists
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async onBeforeBoardUpdate() {
|
|
313
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
314
|
+
try {
|
|
315
|
+
await pluginInstance.onBeforeBoardUpdate()
|
|
316
|
+
} catch (e) {
|
|
317
|
+
this.pluginError('onBeforeBoardUpdate', pluginInstance, e)
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
onTaskUpdate(task) {
|
|
323
|
+
this.eachPlugin(({ pluginInstance }) => {
|
|
324
|
+
try {
|
|
325
|
+
pluginInstance.onTaskUpdate(task)
|
|
326
|
+
} catch (e) {
|
|
327
|
+
this.pluginError('onTaskUpdate', pluginInstance, e)
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async onTaskFound(task) {
|
|
333
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
334
|
+
try {
|
|
335
|
+
if (pluginInstance.onTaskFound) await pluginInstance.onTaskFound(task)
|
|
336
|
+
} catch (e) {
|
|
337
|
+
this.pluginError('onTaskFound', pluginInstance, e)
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async onBeforeAddTask({path, list, content, tags, contexts, meta, useCardTemplate}) {
|
|
343
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
344
|
+
try {
|
|
345
|
+
const pluginMods = await pluginInstance.onBeforeAddTask({path, list, content, tags, contexts, meta, useCardTemplate})
|
|
346
|
+
path = pluginMods.path
|
|
347
|
+
content = pluginMods.content
|
|
348
|
+
tags = pluginMods.tags
|
|
349
|
+
contexts = pluginMods.contexts
|
|
350
|
+
meta = pluginMods.meta
|
|
351
|
+
} catch (e) {
|
|
352
|
+
this.pluginError('onBeforeAddTask', pluginInstance, e)
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
return {path, content, tags, contexts, meta}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async onAfterDeleteTask(task) {
|
|
359
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
360
|
+
try {
|
|
361
|
+
await pluginInstance.onAfterDeleteTask(task)
|
|
362
|
+
} catch (e) {
|
|
363
|
+
this.pluginError('onAfterDeleteTask', pluginInstance, e)
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
getCardProperties(props) {
|
|
369
|
+
let cardProps = {}
|
|
370
|
+
this.eachPlugin(({ pluginInstance }) => {
|
|
371
|
+
try {
|
|
372
|
+
cardProps = { ...cardProps, ...pluginInstance.getCardProperties(props) }
|
|
373
|
+
} catch (e) {
|
|
374
|
+
this.pluginError('getCardProperties', pluginInstance, e)
|
|
375
|
+
}
|
|
376
|
+
})
|
|
377
|
+
return cardProps
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
async getBoardProperties() {
|
|
382
|
+
let boardProps = {}
|
|
383
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
384
|
+
try {
|
|
385
|
+
const pluginProps = pluginInstance.getBoardProperties ? await pluginInstance.getBoardProperties() : {}
|
|
386
|
+
boardProps = { ...boardProps, ...pluginProps }
|
|
387
|
+
} catch (e) {
|
|
388
|
+
this.pluginError('getBoardProperties', pluginInstance, e)
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
return boardProps
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
getCardActions(task) {
|
|
395
|
+
let cardLinks = []
|
|
396
|
+
this.eachPlugin(({ pluginInstance }) => {
|
|
397
|
+
try {
|
|
398
|
+
cardLinks = [
|
|
399
|
+
...cardLinks,
|
|
400
|
+
...pluginInstance.getCardActions(task).map((link, index) => {
|
|
401
|
+
return {
|
|
402
|
+
...link,
|
|
403
|
+
action: { plugin: this.getPluginName(pluginInstance), index },
|
|
404
|
+
}
|
|
405
|
+
}),
|
|
406
|
+
]
|
|
407
|
+
} catch (e) {
|
|
408
|
+
this.pluginError('getCardActions', pluginInstance, e)
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
return cardLinks
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
getBoardActions() {
|
|
415
|
+
let actions = []
|
|
416
|
+
this.eachPlugin(({ pluginInstance }) => {
|
|
417
|
+
try {
|
|
418
|
+
actions = [
|
|
419
|
+
...actions,
|
|
420
|
+
...pluginInstance.getBoardActions().map((item, index) => {
|
|
421
|
+
if (item.title) item.name = item.title
|
|
422
|
+
return {
|
|
423
|
+
...item,
|
|
424
|
+
plugin: this.getPluginName(pluginInstance),
|
|
425
|
+
index,
|
|
426
|
+
}
|
|
427
|
+
}),
|
|
428
|
+
]
|
|
429
|
+
} catch (e) {
|
|
430
|
+
this.pluginError('getBoardActions', pluginInstance, e)
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
return actions
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
performCardAction(action, task) {
|
|
437
|
+
const plugin = this.getPluginInstance(action.plugin)
|
|
438
|
+
try {
|
|
439
|
+
return plugin.getCardActions(task)[action.index].action()
|
|
440
|
+
} catch (e) {
|
|
441
|
+
this.pluginError('getCardActions', plugin, e)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async performBoardAction(action, task) {
|
|
446
|
+
const title = action.title || action.name
|
|
447
|
+
const plugin = this.getPluginInstance(action.plugin)
|
|
448
|
+
try {
|
|
449
|
+
return action.index
|
|
450
|
+
? await plugin.getBoardActions()[action.index].action(task)
|
|
451
|
+
: await plugin.getBoardActions().find(a => a.title === title).action(task)
|
|
452
|
+
} catch (e) {
|
|
453
|
+
|
|
454
|
+
this.pluginError('getBoardActions', plugin, e)
|
|
455
|
+
throw new Error(`Error performing action ${title} on plugin ${plugin.name}: ${e.message}`)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import https from 'https'
|
|
2
|
+
import {
|
|
3
|
+
renderMarkdown,
|
|
4
|
+
} from '../adapters/markdown.js'
|
|
5
|
+
|
|
6
|
+
export async function getAvailablePlugins() {
|
|
7
|
+
const response = await fetch('https://raw.githubusercontent.com/imdone/imdone-plugins/main/index.json')
|
|
8
|
+
const plugins = response.body
|
|
9
|
+
for (const plugin of plugins) {
|
|
10
|
+
plugin.content = renderMarkdown(await getReadmeContent(plugin))
|
|
11
|
+
}
|
|
12
|
+
return plugins
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function getReadmeContent({version}) {
|
|
16
|
+
const response = await fetch(`https://api.github.com/repos/${version}/readme`)
|
|
17
|
+
const { content } = response?.body
|
|
18
|
+
if (!content) return ''
|
|
19
|
+
const buffer = Buffer.from(content, 'base64');
|
|
20
|
+
return buffer.toString('utf-8');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function fetch(url, opts = { method: 'GET', headers: {} }) {
|
|
24
|
+
const { method, headers, body, debug = () => { } } = opts
|
|
25
|
+
if (body) headers['Content-Length'] = body.length
|
|
26
|
+
headers['Cache-Control'] = 'no-cache'; // Add Cache-Control header
|
|
27
|
+
url = new URL(url)
|
|
28
|
+
const port = url.protocol === 'https:' ? 443 : 80
|
|
29
|
+
const options = {
|
|
30
|
+
hostname: url.hostname,
|
|
31
|
+
port: port,
|
|
32
|
+
path: url.pathname,
|
|
33
|
+
method,
|
|
34
|
+
headers: {
|
|
35
|
+
'User-Agent': 'imdone',
|
|
36
|
+
...headers,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
debug("Fetch request:", JSON.stringify({ method, url, body }))
|
|
41
|
+
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
let result = []
|
|
44
|
+
|
|
45
|
+
const req = https.request(options, (res) => {
|
|
46
|
+
res.on('data', (chunk) => result.push(chunk))
|
|
47
|
+
res.on('end', () => {
|
|
48
|
+
const data = Buffer.concat(result).toString()
|
|
49
|
+
debug("Fetch response:", JSON.stringify({ data, status: res.statusCode }))
|
|
50
|
+
resolve({ body: data && JSON.parse(data), status: res.statusCode })
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
req.on('error', reject)
|
|
54
|
+
if (body) req.write(body)
|
|
55
|
+
req.end()
|
|
56
|
+
})
|
|
57
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Repository } from './repository.js'
|
|
2
|
+
import { watchedFileSystemStoreMixin } from './mixins/repo-watched-fs-store.js'
|
|
3
|
+
import { fileSystemStoreMixin } from './mixins/repo-fs-store.js'
|
|
4
|
+
import { ProjectContext } from './ProjectContext.js'
|
|
5
|
+
import { WorkerProject } from './project.js'
|
|
6
|
+
import { appContext as context } from './context/ApplicationContext.js'
|
|
7
|
+
import _path from 'node:path'
|
|
8
|
+
|
|
9
|
+
export function createFileSystemProject({
|
|
10
|
+
path,
|
|
11
|
+
config,
|
|
12
|
+
repo = fileSystemStoreMixin(new Repository(_path.resolve(path), config)),
|
|
13
|
+
loadInstalledPlugins,
|
|
14
|
+
loadPluginsNotInstalled
|
|
15
|
+
}) {
|
|
16
|
+
context().repo = repo
|
|
17
|
+
context().projectContext = new ProjectContext(repo)
|
|
18
|
+
context().project = new WorkerProject(repo)
|
|
19
|
+
if (loadInstalledPlugins) {
|
|
20
|
+
context().project.pluginManager.loadInstalledPlugins = loadInstalledPlugins
|
|
21
|
+
}
|
|
22
|
+
if (loadPluginsNotInstalled) {
|
|
23
|
+
context().project.pluginManager.loadPluginsNotInstalled = loadPluginsNotInstalled
|
|
24
|
+
}
|
|
25
|
+
return context().project
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createWatchedFileSystemProject({
|
|
29
|
+
path,
|
|
30
|
+
config,
|
|
31
|
+
repo = watchedFileSystemStoreMixin(new Repository(_path.resolve(path), config)),
|
|
32
|
+
loadInstalledPlugins,
|
|
33
|
+
loadPluginsNotInstalled
|
|
34
|
+
}) {
|
|
35
|
+
return createFileSystemProject({
|
|
36
|
+
path,
|
|
37
|
+
config,
|
|
38
|
+
repo,
|
|
39
|
+
loadInstalledPlugins,
|
|
40
|
+
loadPluginsNotInstalled
|
|
41
|
+
})
|
|
42
|
+
}
|