imdone-core 1.53.7 → 1.53.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
@@ -1,6 +1,6 @@
1
1
  [![npm version](https://badge.fury.io/js/imdone-core.svg)](https://badge.fury.io/js/imdone-core)
2
2
  [![Downloads](https://img.shields.io/npm/dm/imdone-core.svg)](https://npmjs.org/package/imdone-core)
3
- [![Build Status](https://github.com/imdone/imdone-core/actions/workflows/ci.yml/badge.svg)
3
+ ![Build Status](https://github.com/imdone/imdone-core/actions/workflows/ci.yml/badge.svg)
4
4
 
5
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.
6
6
 
@@ -4,7 +4,20 @@ let fs = require('fs')
4
4
  const sanitize = require('sanitize-filename')
5
5
 
6
6
 
7
- module.exports = {
7
+ function mkdirpSync(path) {
8
+ console.warn('sync call')
9
+ return require('mkdirp').sync(path, { fs })
10
+ }
11
+
12
+ function statSync(path) {
13
+ console.warn('sync call')
14
+ try {
15
+ return fs.statSync(path)
16
+ } catch {
17
+ }
18
+ }
19
+
20
+ const gateway = {
8
21
  init(fileSystem = fs) {
9
22
  fs = fileSystem
10
23
  },
@@ -53,10 +66,7 @@ module.exports = {
53
66
  return fs.unlinkSync.apply({}, args)
54
67
  },
55
68
 
56
- mkdirpSync(path) {
57
- console.warn('sync call')
58
- return require('mkdirp').sync(path, { fs })
59
- },
69
+ mkdirpSync,
60
70
 
61
71
  async mkdir(path) {
62
72
  return fs.promises.mkdir(path)
@@ -66,13 +76,7 @@ module.exports = {
66
76
  return fs.promises.readFile(path, 'utf8')
67
77
  },
68
78
 
69
- statSync(path) {
70
- console.warn('sync call')
71
- try {
72
- return fs.statSync(path)
73
- } catch {
74
- }
75
- },
79
+ statSync,
76
80
 
77
81
  readdirSyncRecursive(path) {
78
82
  console.warn('sync call')
@@ -94,14 +98,14 @@ module.exports = {
94
98
  },
95
99
 
96
100
  preparePathForWriting(path) {
97
- let stat = this.statSync(path)
101
+ let stat = statSync(path)
98
102
  if (!stat) {
99
103
  let { dir, ext } = _path.parse(path)
100
104
  if (!ext) {
101
105
  dir = path
102
106
  }
103
- this.mkdirpSync(dir)
104
- stat = this.statSync(path)
107
+ mkdirpSync(dir)
108
+ stat = statSync(path)
105
109
  }
106
110
  return {
107
111
  isFile: stat && stat.isFile(),
@@ -109,3 +113,5 @@ module.exports = {
109
113
  }
110
114
  }
111
115
  }
116
+
117
+ module.exports = gateway
@@ -0,0 +1,29 @@
1
+ module.exports = {
2
+ getFunctionSignature
3
+ };
4
+
5
+ function getFunctionSignature(fn) {
6
+ if (typeof fn !== "function") {
7
+ throw new TypeError("Expected a function");
8
+ }
9
+
10
+ const fnString = fn.toString().trim();
11
+ let name = fn.name || "anonymous";
12
+ let params = "";
13
+
14
+ // Match function parameters
15
+ const paramMatch = fnString.match(/\(([^)]*)\)/);
16
+ if (paramMatch) {
17
+ params = paramMatch[1].trim();
18
+ } else {
19
+ // Handle arrow functions without parentheses
20
+ const arrowMatch = fnString.match(/^(?:\S+\s+)?(\w+)?\s*=?\s*(?:async\s*)?\(?([^)=]*)\)?\s*=>/);
21
+ if (arrowMatch) {
22
+ name = name || arrowMatch[1] || "anonymous";
23
+ params = arrowMatch[2].trim();
24
+ }
25
+ }
26
+
27
+ return `${name}(${params})`;
28
+ }
29
+
package/lib/card.js CHANGED
@@ -8,6 +8,7 @@ const _isEmpty = require('lodash.isempty')
8
8
  const _get = require('lodash.get')
9
9
  const eol = require('eol')
10
10
  const allEmoji = require('markdown-it-emoji/lib/data/full.json')
11
+ const { getFunctionSignature } = require('./adapters/parsers/function-parser')
11
12
 
12
13
  const { removeMD } = require('./adapters/markdown')
13
14
 
@@ -190,7 +191,7 @@ module.exports = function newCard(task, _project, dontParse) {
190
191
  const keys = Object.keys(data).map((key) => {
191
192
  const value = data[key]
192
193
  if (_isFunction(value)) {
193
- key = key + '()'
194
+ key = getFunctionSignature(value)
194
195
  }
195
196
  return key
196
197
  })
@@ -237,7 +238,8 @@ module.exports = function newCard(task, _project, dontParse) {
237
238
  }
238
239
 
239
240
  format(content, mustache) {
240
- const data = this.data
241
+ const projectData = project ? { ...project.data } : {}
242
+ const data = { ...this.data, ...projectData }
241
243
 
242
244
  const opts = { interpolate: /(?<![`\\])\${([\s\S]+?)}/g }
243
245
 
package/lib/config.js CHANGED
@@ -71,8 +71,10 @@ class Config {
71
71
  }
72
72
 
73
73
  getDoingList() {
74
- const lists = _cloneDeep(this.lists).reverse().filter((list) => !list.filter)
75
- const defaultValue = lists && lists.length ? lists[1].name : undefined
74
+ const lists = _cloneDeep(this.lists).filter((list) => !list.filter)
75
+ .reverse().map(list => list.name)
76
+ const defaultValue = lists ? lists.find(list => /doing|progress/i.test(list.name)) :
77
+ lists[1].name || '';
76
78
  return _get(this, 'settings.cards.doingList', defaultValue)
77
79
  }
78
80
 
package/lib/constants.js CHANGED
@@ -14,6 +14,7 @@ flow-typed
14
14
  .hs
15
15
  .npmignore
16
16
  .obsidian
17
+ .imdone/templates
17
18
  `
18
19
  var DEFAULT_IGNORE_EXTS = `
19
20
  *.jpg
package/lib/file.js CHANGED
@@ -1192,13 +1192,13 @@ File.prototype.getCodeCommentRegex = function () {
1192
1192
  // #BACKLOG:-460 Allow languages to have multiple block comment styles, like html gh:13 id:5
1193
1193
  var lang = this.getLang()
1194
1194
  var symbol = lang.symbol
1195
- var reString = escapeRegExp(symbol) + '[^{].*$'
1195
+ var reString = '(?<!["\'`])' + escapeRegExp(symbol) + '[^{].*$'
1196
1196
 
1197
1197
  if (lang.block) {
1198
1198
  var start = escapeRegExp(lang.block.start)
1199
1199
  var end = escapeRegExp(lang.block.end)
1200
1200
  //
1201
- reString = reString + '|' + start + '(.|[\\r\\n])*?' + end
1201
+ reString = reString + '|(?<!["\'`])' + start + '(.|[\\r\\n])*?' + end
1202
1202
  }
1203
1203
 
1204
1204
  return new RegExp(reString, 'gmi')
package/lib/list.js CHANGED
@@ -13,7 +13,7 @@ const uniqid = require('uniqid')
13
13
  * @param {} hidden
14
14
  * @return
15
15
  */
16
- function List({name, hidden, ignore, filter, id = null}) {
16
+ function List({ name, hidden, ignore, filter, id }) {
17
17
  this.name = name;
18
18
  this.hidden = hidden || false
19
19
  this.ignore = ignore || false
@@ -51,7 +51,8 @@ module.exports = function (repo, defaultSettings = defaultSettingsObject) {
51
51
  }
52
52
 
53
53
  if (config.settings.cards.defaultList === undefined) {
54
- config.settings.cards.defaultList = lists ? lists[0] : ''
54
+ config.settings.cards.defaultList = lists ? lists.find(list => /todo/i.test(list)) :
55
+ lists[0] || '';
55
56
  }
56
57
 
57
58
  // archiveCompleted
@@ -107,11 +108,13 @@ module.exports = function (repo, defaultSettings = defaultSettingsObject) {
107
108
  }
108
109
  // Set up default doneList
109
110
  if (config.settings.cards.doneList === undefined) {
110
- config.settings.cards.doneList = lists ? lists[lists.length - 1] : ''
111
+ config.settings.cards.doneList = lists ? lists.find(list => /done/i.test(list)) :
112
+ lists[lists.length - 1] || '';
111
113
  }
112
114
  // Set up default doingList
113
115
  if (config.settings.cards.doingList === undefined) {
114
- config.settings.cards.doingList = lists ? lists[lists.length - 1] : ''
116
+ config.settings.cards.doingList = lists ? lists.find(list => /doing|progress/i.test(list)) :
117
+ lists[lists.length - 2] || '';
115
118
  }
116
119
  // Add name
117
120
  if (config.settings.name === undefined) {
@@ -0,0 +1,54 @@
1
+ const Plugin = require('imdone-api')
2
+ const {getIsoDateWithOffset} = require('../adapters/date-time')
3
+ const path = require('path')
4
+ const simpleTemplate = '\n<!--\ncreated:${timestamp}\n-->\n'
5
+
6
+ module.exports = class DefaultBoardPropertiesPlugin extends Plugin {
7
+ constructor(project) {
8
+ super(project)
9
+ this.templates = {}
10
+ }
11
+
12
+ async onBeforeBoardUpdate() {
13
+ this.templates = await this.getTemplates()
14
+ }
15
+
16
+ getBoardProperties() {
17
+ const self = this;
18
+
19
+ return {
20
+ date: `${getIsoDateWithOffset(new Date()).substring(0, 10)}`,
21
+ timestamp: getIsoDateWithOffset(new Date()),
22
+ clearFilterURL: self.getFilterURL(''),
23
+ templates: this.templates,
24
+ getFilterURL: (filter) => self.getFilterURL(filter),
25
+ getFilterLink: (filter) => `[Filter By: ${filter}](${self.getFilterURL(filter)})`
26
+ }
27
+ }
28
+
29
+ getFilterURL(filter) {
30
+ return `imdone://${this.project.path}?filter=${filter}`
31
+ }
32
+
33
+ async getTemplates() {
34
+ const { preparePathForWriting, readdir, readFile } = this.project.fileGateway
35
+ const templateDir = path.join(this.project.configDir, 'templates');
36
+ preparePathForWriting(templateDir);
37
+ const files = await readdir(templateDir);
38
+ const readFilePromises = files.map((file) => {
39
+ return new Promise(async (resolve) => {
40
+ const data = await readFile(path.join(templateDir, file), 'utf-8');
41
+ const templateName = file.replace('.md', '').replace(/[^a-zA-Z0-9]/g, '_');
42
+ resolve({ [templateName]: data });
43
+ });
44
+ });
45
+
46
+ const allTemplates = await Promise.all(readFilePromises);
47
+
48
+ const templates = allTemplates.reduce((acc, template) => {
49
+ return { ...acc, ...template };
50
+ }, {});
51
+
52
+ return { simple: simpleTemplate, ...templates };
53
+ }
54
+ }
@@ -14,7 +14,7 @@ module.exports = class EpicPlugin extends Plugin {
14
14
  this.epics = {}
15
15
  }
16
16
 
17
- onBeforeBoardUpdate() {
17
+ async onBeforeBoardUpdate() {
18
18
  this.epics = {}
19
19
  }
20
20
 
@@ -25,10 +25,21 @@ module.exports = class ExtensionPlugin extends Plugin {
25
25
  'properties',
26
26
  'card'
27
27
  )
28
+ this.boardPropertiesFunction = this.loadExtensionModule(
29
+ () => {
30
+ return {}
31
+ },
32
+ 'properties',
33
+ 'board'
34
+ )
28
35
  }
29
36
 
30
37
  getCardProperties(task) {
31
- return this.cardPropertiesFunction(task, this.project)
38
+ return this.cardPropertiesFunction(task)
39
+ }
40
+
41
+ getBoardProperties() {
42
+ return this.boardPropertiesFunction()
32
43
  }
33
44
 
34
45
  getCardActions(task) {
@@ -13,7 +13,7 @@ module.exports = class PluginManager extends Emitter {
13
13
  this.project = project
14
14
  this.defaultPlugins = [
15
15
  './persist-tags-plugin',
16
- './default-card-properties-plugin',
16
+ './default-board-properties-plugin',
17
17
  './default-board-actions-plugin',
18
18
  './archive-plugin',
19
19
  './epic-plugin',
@@ -229,7 +229,7 @@ module.exports = class PluginManager extends Emitter {
229
229
  }
230
230
 
231
231
  pluginError(method, pluginInstance, error) {
232
- console.error(
232
+ console.warn(
233
233
  `Plugin: ${this.getPluginName(
234
234
  pluginInstance
235
235
  )} threw an error on ${method}: `,
@@ -239,7 +239,7 @@ module.exports = class PluginManager extends Emitter {
239
239
 
240
240
  async onBoardUpdate(lists) {
241
241
  if (!lists || lists.length == 0) return
242
- this.eachPluginAsync(async ({ pluginInstance }) => {
242
+ await this.eachPluginAsync(async ({ pluginInstance }) => {
243
243
  // const timeLabel = `${this.getPluginName(pluginInstance)} onBoardUpdate time`
244
244
  // console.time(timeLabel)
245
245
  try {
@@ -252,10 +252,10 @@ module.exports = class PluginManager extends Emitter {
252
252
  return lists
253
253
  }
254
254
 
255
- onBeforeBoardUpdate() {
256
- this.eachPlugin(({ pluginInstance }) => {
255
+ async onBeforeBoardUpdate() {
256
+ await this.eachPluginAsync(async ({ pluginInstance }) => {
257
257
  try {
258
- pluginInstance.onBeforeBoardUpdate()
258
+ await pluginInstance.onBeforeBoardUpdate()
259
259
  } catch (e) {
260
260
  this.pluginError('onBeforeBoardUpdate', pluginInstance, e)
261
261
  }
@@ -320,6 +320,19 @@ module.exports = class PluginManager extends Emitter {
320
320
  return cardProps
321
321
  }
322
322
 
323
+ getBoardProperties() {
324
+ let boardProps = {}
325
+ this.eachPlugin(({ pluginInstance }) => {
326
+ try {
327
+ const pluginProps = pluginInstance.getBoardProperties ? pluginInstance.getBoardProperties() : {}
328
+ boardProps = { ...boardProps, ...pluginProps }
329
+ } catch (e) {
330
+ this.pluginError('getBoardProperties', pluginInstance, e)
331
+ }
332
+ })
333
+ return boardProps
334
+ }
335
+
323
336
  getCardActions(task) {
324
337
  let cardLinks = []
325
338
  this.eachPlugin(({ pluginInstance }) => {
package/lib/project.js CHANGED
@@ -3,7 +3,7 @@ const PluginManager = require('./plugins/plugin-manager')
3
3
  const Repository = require('./repository')
4
4
  const _cloneDeep = require('lodash.clonedeep')
5
5
  const newCard = require('./card')
6
- const { JOURNAL_TYPE } = require('./constants')
6
+ const { JOURNAL_TYPE, CONFIG_DIR } = require('./constants')
7
7
  const _path = require('path')
8
8
  const moment = require('moment')
9
9
  const { getIsoDateWithOffset } = require('./adapters/date-time')
@@ -11,11 +11,13 @@ const fileGateway = require('./adapters/file-gateway')
11
11
  const matter = require('gray-matter')
12
12
  const _isObject = require('lodash.isobject')
13
13
  const _isString = require('lodash.isstring')
14
+ const _isFunction = require('lodash.isfunction')
14
15
  const exec = require('child_process').exec
15
16
  const fastSort = require('fast-sort/dist/sort.js')
16
17
  const Task = require('./task')
17
18
  const eol = require('eol')
18
19
  const { renderMarkdown, extractWikilinkTopics } = require('./adapters/markdown')
20
+ const { getFunctionSignature } = require('./adapters/parsers/function-parser')
19
21
 
20
22
  function calculateTotals(lists) {
21
23
  const totals = {}
@@ -97,6 +99,28 @@ module.exports = class WorkerProject extends Project {
97
99
  return { path: this.path }
98
100
  }
99
101
 
102
+ getListsForImdoneJSON(totals) {
103
+ return this.allLists.map((list) => {
104
+ list = { ...list }
105
+ list.tasks = list.tasks.map((card, index) => {
106
+ card.interpretedContent = ''
107
+ card.totals = totals
108
+ card.index = index
109
+ return card
110
+ })
111
+ return list
112
+ })
113
+ }
114
+
115
+ getInitializedCards(allLists, totals) {
116
+ return allLists
117
+ .map((list) => list.tasks)
118
+ .flat()
119
+ .map(
120
+ (card) => card.init(totals)
121
+ )
122
+ }
123
+
100
124
  async toImdoneJSON() {
101
125
  this.pluginManager.initDevMode()
102
126
  console.time('toJSON time')
@@ -106,26 +130,17 @@ module.exports = class WorkerProject extends Project {
106
130
  populateFiltered: true,
107
131
  })
108
132
  )
109
- let allLists = this.allLists
110
- allLists = allLists.map((list) => {
111
- list.tasks = list.tasks.map((card, index) => {
112
- card.interpretedContent = ''
113
- card.totals = totals
114
- card.index = index
115
- return card
116
- })
117
- return list
118
- })
133
+ const allLists = this.getListsForImdoneJSON(totals)
119
134
 
120
135
  console.time('plugin onBeforeBoardUpdate')
121
- this.pluginManager.onBeforeBoardUpdate()
136
+ await this.pluginManager.onBeforeBoardUpdate()
122
137
  console.timeEnd('plugin onBeforeBoardUpdate')
123
138
 
139
+ const data = this.data = this.pluginManager.getBoardProperties()
140
+ const dataKeys = this.dataKays = this.getDataKeys(data)
141
+
124
142
  console.time('card init')
125
- const cards = allLists
126
- .map((list) => list.tasks)
127
- .flat()
128
- .map((card) => card.init(totals))
143
+ const cards = this.getInitializedCards(allLists, totals)
129
144
  console.timeEnd('card init')
130
145
 
131
146
  console.time('plugin onBoardUpdate')
@@ -140,6 +155,7 @@ module.exports = class WorkerProject extends Project {
140
155
  tasks: this.getCards(this.filter, cards),
141
156
  populateFiltered: true,
142
157
  })
158
+
143
159
  console.timeEnd('toJSON time')
144
160
  return {
145
161
  path: this.path,
@@ -156,6 +172,8 @@ module.exports = class WorkerProject extends Project {
156
172
  defaultFilter: this.defaultFilter,
157
173
  actions: this.boardActions,
158
174
  plugins: this.pluginManager.getPlugins(),
175
+ data,
176
+ dataKeys,
159
177
  queryProps: this.getQueryProps(cards),
160
178
  }
161
179
  }
@@ -171,6 +189,10 @@ module.exports = class WorkerProject extends Project {
171
189
  if (this.pluginManager) this.pluginManager.destroyPlugins()
172
190
  }
173
191
 
192
+ get configDir() {
193
+ return _path.join(this.path, CONFIG_DIR)
194
+ }
195
+
174
196
  get allTopics() {
175
197
  return [...this.repo.allTopics]
176
198
  }
@@ -209,6 +231,23 @@ module.exports = class WorkerProject extends Project {
209
231
  return this._updatedAt
210
232
  }
211
233
 
234
+ getDataKeys(data) {
235
+ const keys = Object.keys(data).map((key) => {
236
+ const value = data[key]
237
+ if (_isFunction(value)) {
238
+ key = getFunctionSignature(value)
239
+ }
240
+ return key
241
+ })
242
+ keys.forEach((key) => {
243
+ const value = data[key]
244
+ if (_isObject(value) && !Array.isArray(value)) {
245
+ keys.push(...Object.keys(value).map((k) => `${key}.${k}`))
246
+ }
247
+ })
248
+ return keys
249
+ }
250
+
212
251
  removeList(list) {
213
252
  this.repo.removeList(list)
214
253
  }
@@ -218,18 +257,18 @@ module.exports = class WorkerProject extends Project {
218
257
  return Repository.getTasksByList(this.repo, tasks, true, populateFiltered)
219
258
  }
220
259
 
221
- getTaskQueryProps(task) {
260
+ getTaskQueryProps(props) {
222
261
  const queryProps = []
223
- if (task) {
262
+ if (props) {
224
263
  // add all properties of the task to the queryProps array
225
- Object.keys(task).forEach((key) => {
264
+ Object.keys(props).forEach((key) => {
226
265
  if (key === 'data') return
227
266
  if (queryProps.includes(key)) return
228
267
 
229
268
  queryProps.push(key)
230
269
 
231
- if (_isObject(task[key])) {
232
- Object.keys(task[key]).forEach((subKey) => {
270
+ if (_isObject(props[key])) {
271
+ Object.keys(props[key]).forEach((subKey) => {
233
272
  if (!queryProps.includes(`${key}.${subKey}`))
234
273
  queryProps.push(`${key}.${subKey}`)
235
274
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imdone-core",
3
- "version": "1.53.7",
3
+ "version": "1.53.9",
4
4
  "description": "imdone-core",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -57,7 +57,7 @@
57
57
  "find-up": "^5.0.0",
58
58
  "gray-matter": "^4.0.2",
59
59
  "ignore": "^5.2.0",
60
- "imdone-api": "^1.52.2",
60
+ "imdone-api": "^1.53.0",
61
61
  "isbinaryfile": "^3.0.2",
62
62
  "js-yaml": "^3.14.1",
63
63
  "json-fns": "^1.0.0",
@@ -1,25 +0,0 @@
1
- const Plugin = require('imdone-api')
2
- const {getIsoDateWithOffset} = require('../adapters/date-time')
3
-
4
- module.exports = class DefaultCardPropertiesPlugin extends Plugin {
5
- constructor(project) {
6
- super(project)
7
- }
8
-
9
- getCardProperties() {
10
- const self = this;
11
-
12
- return {
13
- date: `${getIsoDateWithOffset(new Date()).substring(0, 10)}`,
14
- timestamp: getIsoDateWithOffset(new Date()),
15
- clearFilterURL: self.getFilterURL(''),
16
- getFilterURL: (filter) => self.getFilterURL(filter),
17
- getFilterLink: (filter) => `[Filter By: ${filter}](${self.getFilterURL(filter)})`
18
- }
19
- }
20
-
21
- getFilterURL(filter) {
22
- return `imdone://${this.project.path}?filter=${filter}`
23
- }
24
-
25
- }