imdone-core 1.54.8 → 1.54.9

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/README.md CHANGED
@@ -199,8 +199,9 @@ License
199
199
 
200
200
  <!-- # #NOTE Notes
201
201
 
202
- **Development Package Version {{version}}**
203
- **Published Version**
202
+ {{branch}} :deciduous_tree: :hammer_and_wrench: Current Branch
203
+ {{version}} :package: :hammer_and_wrench: Code Version
204
+ {{publishedVersion}} :package: :passenger_ship: Published Version
204
205
 
205
206
  [Focus]({{getFilterURL('list = DOING or tags = focus')}})
206
207
  [Plan]({{getFilterURL(' ')}})
@@ -3,6 +3,14 @@ const _path = require('path')
3
3
  let fs = require('fs')
4
4
  const sanitize = require('sanitize-filename')
5
5
 
6
+ async function exists(path) {
7
+ try {
8
+ await fs.promises.access(path)
9
+ return path
10
+ } catch {
11
+ return false
12
+ }
13
+ }
6
14
 
7
15
  function mkdirpSync(path) {
8
16
  console.warn('sync call')
@@ -29,14 +37,7 @@ const gateway = {
29
37
  return fs.existsSync.apply({}, args)
30
38
  },
31
39
 
32
- async exists(path) {
33
- try {
34
- await fs.promises.access(path)
35
- return path
36
- } catch {
37
- return false
38
- }
39
- },
40
+ exists,
40
41
 
41
42
  async stat(path, opts) {
42
43
  try {
@@ -14,6 +14,7 @@ class ApplicationContext {
14
14
  this.projectReader = projectReader;
15
15
  this.projectPresenter = projectPresenter;
16
16
  this.innerConfig = Config.newDefaultConfig()
17
+ this.pluginRegistry = require('../plugins/plugin-registry')
17
18
  }
18
19
 
19
20
  set config(config) {
@@ -1,11 +1,12 @@
1
1
  const Emitter = require('events')
2
2
  const Plugin = require('imdone-api')
3
- const fs = require('fs')
4
3
  const _path = require('path')
5
4
  const downloadRepo = require('../adapters/git-download')
6
5
  const rimraf = require('rimraf')
7
6
  const sane = require('sane')
8
7
  const debounce = require('lodash.debounce')
8
+ const { exists, mkdir, readdir } = require('../adapters/file-gateway')
9
+ const appContext = require('../context/ApplicationContext')
9
10
 
10
11
  module.exports = class PluginManager extends Emitter {
11
12
  constructor(project) {
@@ -26,8 +27,8 @@ module.exports = class PluginManager extends Emitter {
26
27
 
27
28
  async startDevMode() {
28
29
  if (this.project && this.project.config.devMode && !this.watcher) {
29
- if (!(await this.exists(this.pluginPath)))
30
- await fs.promises.mkdir(this.pluginPath)
30
+ if (!(await exists(this.pluginPath)))
31
+ await mkdir(this.pluginPath)
31
32
  this.watcher = sane(this.pluginPath, {
32
33
  ignored(path) {
33
34
  return /node_modules/.test(path)
@@ -92,8 +93,8 @@ module.exports = class PluginManager extends Emitter {
92
93
  }
93
94
 
94
95
  async installPlugin({ name, version }) {
95
- if (!(await this.exists(this.pluginPath)))
96
- await fs.promises.mkdir(this.pluginPath)
96
+ if (!(await exists(this.pluginPath)))
97
+ await mkdir(this.pluginPath)
97
98
  const installPath = _path.join(this.pluginPath, name)
98
99
  await downloadRepo(version, installPath)
99
100
  console.log(`Done installing ${name}`)
@@ -106,9 +107,16 @@ module.exports = class PluginManager extends Emitter {
106
107
  await this.loadPlugin(pluginPath)
107
108
  }
108
109
 
109
- const path = await this.exists(this.pluginPath)
110
- if (!path) return
111
- const paths = await fs.promises.readdir(this.pluginPath, { withFileTypes: true })
110
+ await this.loadInstalledPlugins()
111
+ await this.loadPluginsNotInstalled()
112
+ }
113
+
114
+ async loadInstalledPlugins() {
115
+ const path = await exists(this.pluginPath)
116
+ if (!path) {
117
+ await mkdir(this.pluginPath)
118
+ }
119
+ const paths = await readdir(this.pluginPath, { withFileTypes: true })
112
120
  const pluginPaths = paths
113
121
  .filter(
114
122
  (entry) =>
@@ -120,9 +128,20 @@ module.exports = class PluginManager extends Emitter {
120
128
  for (const path of pluginPaths) {
121
129
  await this.loadPlugin(path)
122
130
  }
123
-
124
131
  }
125
132
 
133
+ async loadPluginsNotInstalled() {
134
+ const availablePlugins = await appContext().pluginRegistry.getAvailablePlugins()
135
+ const configPluginNames = Object.keys(this.project.config.plugins)
136
+ if (!configPluginNames) return
137
+ const installedPluginNames = Object.keys(this.pluginsMap)
138
+ const pluginsNotInstalled = configPluginNames.filter(name => !installedPluginNames.includes(name))
139
+ for (const pluginName of pluginsNotInstalled) {
140
+ const plugin = availablePlugins.find(p => p.name === pluginName)
141
+ await this.installPlugin(plugin)
142
+ }
143
+ }
144
+
126
145
  async loadPlugin(path) {
127
146
  console.log('Loading plugin: ', path)
128
147
  try {
@@ -380,15 +399,6 @@ module.exports = class PluginManager extends Emitter {
380
399
  return actions
381
400
  }
382
401
 
383
- async exists(path) {
384
- try {
385
- await fs.promises.access(path)
386
- return path
387
- } catch {
388
- return false
389
- }
390
- }
391
-
392
402
  performCardAction(action, task) {
393
403
  const plugin = this.getPluginInstance(action.plugin)
394
404
  try {
@@ -0,0 +1,61 @@
1
+ const {
2
+ renderMarkdown,
3
+ } = require('../adapters/markdown')
4
+
5
+ module.exports = {
6
+
7
+ getAvailablePlugins: async function () {
8
+ const response = await fetch('https://raw.githubusercontent.com/imdone/imdone-plugins/main/index.json')
9
+ const plugins = response.body
10
+ for (const plugin of plugins) {
11
+ plugin.content = renderMarkdown(await getReadmeContent(plugin))
12
+ }
13
+ return plugins
14
+ },
15
+
16
+ }
17
+
18
+ async function getReadmeContent({version}) {
19
+ const response = await fetch(`https://api.github.com/repos/${version}/readme`)
20
+ const { content } = response.body
21
+ const buffer = Buffer.from(content, 'base64');
22
+ return buffer.toString('utf-8');
23
+ }
24
+
25
+ const https = require('https')
26
+
27
+ async function fetch(url, opts = { method: 'GET', headers: {} }) {
28
+ const { method, headers, body, debug = () => { } } = opts
29
+ if (body) headers['Content-Length'] = body.length
30
+ headers['Cache-Control'] = 'no-cache'; // Add Cache-Control header
31
+ url = new URL(url)
32
+ const port = url.protocol === 'https:' ? 443 : 80
33
+ const options = {
34
+ hostname: url.hostname,
35
+ port: port,
36
+ path: url.pathname,
37
+ method,
38
+ headers: {
39
+ 'User-Agent': 'imdone',
40
+ ...headers,
41
+ },
42
+ }
43
+
44
+ debug("Fetch request:", JSON.stringify({ method, url, body }))
45
+
46
+ return new Promise((resolve, reject) => {
47
+ let result = []
48
+
49
+ const req = https.request(options, (res) => {
50
+ res.on('data', (chunk) => result.push(chunk))
51
+ res.on('end', () => {
52
+ const data = Buffer.concat(result).toString()
53
+ debug("Fetch response:", JSON.stringify({ data, status: res.statusCode }))
54
+ resolve({ body: data && JSON.parse(data), status: res.statusCode })
55
+ })
56
+ })
57
+ req.on('error', reject)
58
+ if (body) req.write(body)
59
+ req.end()
60
+ })
61
+ }
@@ -5,16 +5,40 @@ const ProjectContext = require('./ProjectContext')
5
5
  const Project = require('./project')
6
6
  const context = require('./context/ApplicationContext')
7
7
 
8
- function createFileSystemProject({path, config, repo = fsStore(new Repository(path, config))}) {
8
+ function createFileSystemProject({
9
+ path,
10
+ config,
11
+ repo = fsStore(new Repository(path, config)),
12
+ loadInstalledPlugins,
13
+ loadPluginsNotInstalled
14
+ }) {
9
15
  context().repo = repo
10
16
  context().projectContext = new ProjectContext(repo)
11
17
  context().project = new Project(repo)
18
+ if (loadInstalledPlugins) {
19
+ context().project.pluginManager.loadInstalledPlugins = loadInstalledPlugins
20
+ }
21
+ if (loadPluginsNotInstalled) {
22
+ context().project.pluginManager.loadPluginsNotInstalled = loadPluginsNotInstalled
23
+ }
12
24
  return context().project
13
25
  }
14
26
 
15
27
  module.exports = {
16
- createWatchedFileSystemProject: function({path, config, repo = watchedFsStore(new Repository(path, config))}) {
17
- return createFileSystemProject({path, config, repo})
28
+ createWatchedFileSystemProject: function({
29
+ path,
30
+ config,
31
+ repo = watchedFsStore(new Repository(path, config)),
32
+ loadInstalledPlugins,
33
+ loadPluginsNotInstalled
34
+ }) {
35
+ return createFileSystemProject({
36
+ path,
37
+ config,
38
+ repo,
39
+ loadInstalledPlugins,
40
+ loadPluginsNotInstalled
41
+ })
18
42
  },
19
43
 
20
44
  createFileSystemProject
package/lib/project.js CHANGED
@@ -54,13 +54,100 @@ module.exports = class WorkerProject extends Project {
54
54
  this._updatedAt = undefined
55
55
  this.data = {}
56
56
  this.dataKeys = []
57
+ this.repo.project = this
58
+ this.pluginManager = new PluginManager(this)
59
+ }
60
+
61
+ get configDir() {
62
+ return _path.join(this.path, CONFIG_DIR)
63
+ }
64
+
65
+ get allTopics() {
66
+ return [...this.repo.allTopics]
67
+ }
68
+
69
+ get allTags() {
70
+ return [...this.repo.allTags]
71
+ }
72
+
73
+ get allContexts() {
74
+ return [...this.repo.allContexts]
75
+ }
76
+
77
+ get allMeta() {
78
+ return this.repo.allMeta
79
+ }
80
+
81
+ get lists() {
82
+ return this.getLists({
83
+ tasks: this.filteredCards,
84
+ })
85
+ }
86
+
87
+ get allLists() {
88
+ return this.getLists()
89
+ }
90
+
91
+ get filteredCards() {
92
+ return this.getCards(this.filter)
93
+ }
94
+
95
+ get isoDateWithOffset() {
96
+ return getIsoDateWithOffset()
97
+ }
98
+
99
+ get updatedAt() {
100
+ return this._updatedAt
101
+ }
102
+
103
+ get files() {
104
+ return this.repo.getFilePaths()
105
+ }
106
+
107
+ get config() {
108
+ return this.repo.config
109
+ }
110
+
111
+ get defaultFilter() {
112
+ return this.config && this.config.defaultFilter
113
+ }
114
+
115
+ set defaultFilter(filter) {
116
+ this.config.defaultFilter = filter
117
+ }
118
+
119
+ get totals() {
120
+ return calculateTotals(this.lists)
121
+ }
122
+
123
+ get path() {
124
+ return this.repo.path
125
+ }
126
+
127
+ get name() {
128
+ return this.repo.getDisplayName()
129
+ }
130
+
131
+ get doneList() {
132
+ return this.config.getDoneList()
133
+ }
134
+
135
+ get boardActions() {
136
+ return [
137
+ ...this.pluginManager.getBoardActions(),
138
+ ]
139
+ }
140
+
141
+ get filter() {
142
+ return this.innerFilter
143
+ }
144
+
145
+ set filter(filter) {
146
+ this.innerFilter = filter
57
147
  }
58
148
 
59
149
  async init(cb) {
60
150
  const promise = new Promise(async (resolve, reject) => {
61
- this.repo.project = this
62
-
63
- this.pluginManager = new PluginManager(this)
64
151
  this.pluginManager.on('plugin-installed', () => this.emitUpdate())
65
152
  this.pluginManager.on('plugin-uninstalled', () => this.emitUpdate())
66
153
  this.pluginManager.on('plugins-reloaded', () => this.emitUpdate())
@@ -226,48 +313,6 @@ module.exports = class WorkerProject extends Project {
226
313
  if (this.pluginManager) this.pluginManager.destroyPlugins()
227
314
  }
228
315
 
229
- get configDir() {
230
- return _path.join(this.path, CONFIG_DIR)
231
- }
232
-
233
- get allTopics() {
234
- return [...this.repo.allTopics]
235
- }
236
-
237
- get allTags() {
238
- return [...this.repo.allTags]
239
- }
240
-
241
- get allContexts() {
242
- return [...this.repo.allContexts]
243
- }
244
-
245
- get allMeta() {
246
- return this.repo.allMeta
247
- }
248
-
249
- get lists() {
250
- return this.getLists({
251
- tasks: this.filteredCards,
252
- })
253
- }
254
-
255
- get allLists() {
256
- return this.getLists()
257
- }
258
-
259
- get filteredCards() {
260
- return this.getCards(this.filter)
261
- }
262
-
263
- get isoDateWithOffset() {
264
- return getIsoDateWithOffset()
265
- }
266
-
267
- get updatedAt() {
268
- return this._updatedAt
269
- }
270
-
271
316
  getDataKeys(data) {
272
317
  const keys = Object.keys(data).map((key) => {
273
318
  const value = data[key]
@@ -372,52 +417,6 @@ module.exports = class WorkerProject extends Project {
372
417
  return result
373
418
  }
374
419
 
375
- get files() {
376
- return this.repo.getFilePaths()
377
- }
378
-
379
- get config() {
380
- return this.repo.config
381
- }
382
-
383
- get defaultFilter() {
384
- return this.config && this.config.defaultFilter
385
- }
386
-
387
- set defaultFilter(filter) {
388
- this.config.defaultFilter = filter
389
- }
390
-
391
- get totals() {
392
- return calculateTotals(this.lists)
393
- }
394
-
395
- get path() {
396
- return this.repo.path
397
- }
398
-
399
- get name() {
400
- return this.repo.getDisplayName()
401
- }
402
-
403
- get doneList() {
404
- return this.config.getDoneList()
405
- }
406
-
407
- get filter() {
408
- return this.innerFilter
409
- }
410
-
411
- get boardActions() {
412
- return [
413
- ...this.pluginManager.getBoardActions(),
414
- ]
415
- }
416
-
417
- set filter(filter) {
418
- this.innerFilter = filter
419
- }
420
-
421
420
  async addMetadata(task, key, value) {
422
421
  if (task.hasMetaData(key, value)) return
423
422
  if (!task.allMeta[key]) task.allMeta[key] = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imdone-core",
3
- "version": "1.54.8",
3
+ "version": "1.54.9",
4
4
  "description": "imdone-core",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -118,7 +118,7 @@
118
118
  "fs-extra": "^11.1.0",
119
119
  "jshint": "^2.9.4",
120
120
  "json-stringify-safe": "~5.0.0",
121
- "mocha": "^9.2.2",
121
+ "mocha": "^11.1.0",
122
122
  "mocha-each": "^2.0.1",
123
123
  "nyc": "^15.1.0",
124
124
  "prettier": "^2.5.1",