imdone-core 1.47.1 → 1.48.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.
@@ -4,6 +4,7 @@ const path = require('path')
4
4
  const languages = require('../../../languages')
5
5
  const ReadStreamCodeParser = require('./ReadStreamCodeParser')
6
6
 
7
+
7
8
  function getLang(languages, filePath) {
8
9
  var lang = languages[path.extname(filePath)]
9
10
  return lang || { name: 'text', symbol: '' }
@@ -14,10 +15,6 @@ function getLanguages(config) {
14
15
  return { ...languages, ...configLanguages }
15
16
  }
16
17
 
17
- // DOING Use task line parsers for vs code extension and obsidian plugin
18
- // <!--
19
- // order:-10000030
20
- // -->
21
18
 
22
19
  module.exports.getFileParser = async function getFileParser (filePath, config, readStream) {
23
20
  if (!readStream) {
@@ -287,16 +287,26 @@ function getTaskContent ({
287
287
  getTextFileTaskContent({ config, content, beforeText, fileType })
288
288
  }
289
289
 
290
- function getRawTask ({tokenPrefix = DEFAULT_TOKEN_PREFIX, orderMeta, beforeText = '', hasColon = false, list, order= '', text, type}) {
290
+ function getRawTask ({
291
+ tokenPrefix = DEFAULT_TOKEN_PREFIX,
292
+ orderMeta = true,
293
+ beforeText = '',
294
+ hasColon = false,
295
+ list,
296
+ order = '',
297
+ text,
298
+ type = TASK_TYPES.HASHTAG
299
+ }) {
291
300
  if (type === TASK_TYPES.MARKDOWN) {
292
- return `${beforeText}[${text}](${tokenPrefix}${list}:${
301
+ return `${beforeText || ''}[${text}](${tokenPrefix}${list}:${
293
302
  orderMeta ? '' : order
294
303
  })`
304
+ } else {
305
+ const colon = hasColon || (isNumber(order) && !orderMeta) ? ':' : '';
306
+ return `${beforeText || ''}${tokenPrefix}${list}${colon}${
307
+ orderMeta ? '' : order
308
+ } ${text}`
295
309
  }
296
- const colon = hasColon || (isNumber(order) && !orderMeta) ? ':' : '';
297
- return `${beforeText}${tokenPrefix}${list}${colon}${
298
- orderMeta ? '' : order
299
- } ${text}`
300
310
  }
301
311
 
302
312
  function isNumber (value) {
@@ -0,0 +1,50 @@
1
+ const { readFile, writeFile, exists } = require('../file-gateway')
2
+ const { CONFIG_DIR, CONFIG_FILE_YML } = require('../../constants')
3
+ const { loadYAML, dumpYAML } = require('../yaml')
4
+ const findUp = require('find-up')
5
+ const _path = require('path')
6
+ const Config = require('../../config')
7
+
8
+ module.exports = {
9
+ load,
10
+ save,
11
+ loadForFilePath,
12
+ findImdonePath,
13
+ }
14
+
15
+ async function load(projectPath) {
16
+ const configPath = _path.join(projectPath, CONFIG_FILE_YML)
17
+ const configLike = await getYamlConfig(configPath)
18
+ return configLike && new Config(configLike)
19
+ }
20
+
21
+ async function save(config, projectPath) {
22
+ const configPath = _path.join(projectPath, CONFIG_FILE_YML)
23
+ await writeFile(configPath, dumpYAML(config))
24
+ }
25
+
26
+ async function loadForFilePath(filePath) {
27
+ const projectPath = await findImdonePath(filePath)
28
+ return load(projectPath)
29
+ }
30
+
31
+ async function getYamlConfig(filePath) {
32
+ if (!(await exists(filePath))) return
33
+ const configData = await readFile(filePath, 'utf-8')
34
+ let configLike = {}
35
+ try {
36
+ configLike = loadYAML(configData.toString())
37
+ if (configLike.exclude) delete configLike.exclude
38
+ if (configLike.watcher) delete configLike.watcher
39
+
40
+ } catch (e) {
41
+ /* noop */
42
+ }
43
+ return configLike
44
+ }
45
+
46
+ async function findImdonePath(cwd) {
47
+ const configDir = await findUp(CONFIG_DIR, { cwd, type: 'directory' })
48
+ return _path.dirname(configDir)
49
+ }
50
+
@@ -0,0 +1,30 @@
1
+ const { readFile, writeFile, exists } = require('../file-gateway')
2
+ const { CONFIG_DIR } = require('../../constants')
3
+ const { loadYAML, dumpYAML } = require('../yaml')
4
+ const _path = require('path')
5
+
6
+ module.exports = {
7
+ load,
8
+ save
9
+ }
10
+
11
+ async function load(projectPath) {
12
+ const tagsPath = getTagsPath(projectPath)
13
+ if (!(await exists(tagsPath))) return
14
+ const tagsData = await readFile(tagsPath, 'utf-8')
15
+ try {
16
+ const { tags } = loadYAML(tagsData)
17
+ return tags
18
+ } catch (e) {
19
+ console.error(e)
20
+ return
21
+ }
22
+ }
23
+
24
+ async function save(tags, projectPath) {
25
+ await writeFile(getTagsPath(projectPath), dumpYAML({ tags }))
26
+ }
27
+
28
+ function getTagsPath(projectPath) {
29
+ return _path.join(projectPath, CONFIG_DIR, 'tags.yml')
30
+ }
package/lib/card.js CHANGED
@@ -78,36 +78,10 @@ module.exports = function newCard(task, _project, dontParse) {
78
78
  : { ...computed }
79
79
  }
80
80
 
81
- getCardActions() {
82
- // TODO Stop allowing links/actions from card config
83
- // <!-- order:-80 -->
84
- const links = project.config.cardActions
85
- const allLinks = this.frontMatter.links
86
- ? [...links, ...this.frontMatter.links]
87
- : [...links]
88
- return allLinks.filter(({ display }) => {
89
- if (!display) return true
90
- try {
91
- return display.call({ ...this.data, ...this.desc })
92
- } catch (e) {
93
- console.error(e)
94
- }
95
- return false
96
- })
97
- }
98
-
99
81
  initActions() {
100
- this.links = this.getCardActions().map(
101
- ({ pack, icon, title, href }, index) => {
102
- href = this.formatContent(href).content
103
- title = this.formatContent(title).content
104
- const action = JSON.stringify({ index })
105
- return { pack, icon, title, href, action } // Use index instead of action
106
- }
107
- )
108
-
82
+ this.links = []
109
83
  if (!pluginManager) return
110
- this.links = [...this.links, ...pluginManager.getCardActions(this)]
84
+ this.links = [...pluginManager.getCardActions(this)]
111
85
  }
112
86
 
113
87
  get projectPath() {
package/lib/config.js CHANGED
@@ -123,17 +123,6 @@ class Config {
123
123
  return this.settings.views
124
124
  }
125
125
 
126
- get cardActions() {
127
- return [..._get(this, 'settings.cards.links', [])]
128
- }
129
-
130
- get boardActions() {
131
- const settingsActions = _get(this, 'settings.actions', []).map((action) => {
132
- return { name: action.name, action: action['function'] }
133
- })
134
- return [...settingsActions]
135
- }
136
-
137
126
  get appendNewCardsTo() {
138
127
  return _get(this, 'settings.appendNewCardsTo', false)
139
128
  }
@@ -223,27 +212,4 @@ Config.newDefaultConfig = (config = {}) => new Config(
223
212
  }
224
213
  )
225
214
 
226
- Config.findImdonePath = async (cwd) => {
227
- const configDir = await findUp(CONFIG_DIR, { cwd, type: 'directory' })
228
- return _path.dirname(configDir)
229
- }
230
-
231
- Config.getYamlConfig = async (filePath) => {
232
- if (!(await exists(filePath))) return
233
- const configData = await readFile(filePath, 'utf-8')
234
- let configLike = {}
235
- try {
236
- configLike = loadYAML(configData.toString())
237
- } catch (e) {
238
- /* noop */
239
- }
240
- return configLike
241
- }
242
-
243
- Config.load = async (imdonePath) => {
244
- const configPath = _path.join(imdonePath, CONFIG_FILE_YML)
245
- const configLike = await Config.getYamlConfig(configPath)
246
- return new Config(configLike)
247
- }
248
-
249
215
  module.exports = Config
package/lib/constants.js CHANGED
@@ -57,6 +57,8 @@ module.exports = {
57
57
  NOT_A_FILE: 'file must be of type File',
58
58
  CALLBACK_REQUIRED: 'Last paramter must be a callback function',
59
59
  NO_CONTENT: 'File has no content',
60
+ LIST_NOT_FOUND: 'List not found',
61
+ TASK_NOT_FOUND: 'Task not found',
60
62
  },
61
63
  DEFAULT_CONFIG: {
62
64
  keepEmptyPriority: false,
package/lib/file.js CHANGED
@@ -290,7 +290,7 @@ File.prototype.transformTask = function ({config, modify, task}) {
290
290
  }
291
291
 
292
292
  File.prototype.transformTasks = function (config, modify) {
293
- if (this.isCodeFile()) return
293
+ // if (this.isCodeFile()) return
294
294
  fastSort(this.getTasks())
295
295
  .desc(u => u.line)
296
296
  .forEach(task => this.transformTask({config, modify, task}))
@@ -270,9 +270,6 @@ function mixin(repo, fs = require('fs')) {
270
270
  let configLike = {}
271
271
  try {
272
272
  configLike = loadYAML(configData.toString())
273
- Repository.trimFunctionsInSettings(configLike)
274
- if (configLike.exclude) delete configLike.exclude
275
- if (configLike.watcher) delete configLike.watcher
276
273
  } catch (e) {
277
274
  /* noop */
278
275
  }
@@ -1,5 +1,6 @@
1
1
  const Plugin = require('imdone-api')
2
2
  const path = require('path')
3
+ const { getRawTask } = require('../adapters/parsers/task/CardContentParser')
3
4
 
4
5
  module.exports = class ArchivePlugin extends Plugin {
5
6
  constructor(project) {
@@ -14,24 +15,28 @@ module.exports = class ArchivePlugin extends Plugin {
14
15
  return this.project.fileGateway
15
16
  }
16
17
 
17
- onBeforeBoardUpdate() {
18
+ async onBeforeBoardUpdate() {
18
19
  const config = this.config
19
20
  if (!config || !config.archiveCompleted || this.archivingTasks) return
20
21
  this.archivingTasks = true
21
22
  const cards = this.project.getAllCards(`meta.archived != "true" and list = ${config.doneList}`)
22
23
  const messageUser = cards.length > 1
24
+
23
25
  if (!cards.length) {
24
26
  this.archivingTasks = false
25
27
  return
26
28
  } else {
27
29
  if (messageUser) this.project.snackBar({message: `Archiving ${cards.length} card(s)...`})
28
30
  }
31
+
29
32
  cards.forEach(card => this.archiveTask(card))
33
+
30
34
  if (messageUser) this.project.snackBar({message: ` Done archiving ${cards.length} card(s). Deleting original(s)...`})
31
- this.project.deleteTasks(cards).then(() => {
32
- this.archivingTasks = false
33
- if (messageUser) this.project.snackBar({message: `Done deleting original card(s).`})
34
- })
35
+ await this.project.deleteTasks(cards)
36
+
37
+ this.archivingTasks = false
38
+ if (messageUser) this.project.snackBar({message: `Done deleting original card(s).`})
39
+
35
40
  }
36
41
 
37
42
  archiveTask (task, config = this.config) {
@@ -57,8 +62,11 @@ module.exports = class ArchivePlugin extends Plugin {
57
62
  // #imdone-1.47.0
58
63
  // order:-110
59
64
  // -->
60
- const taskContent = `${task.beforeText}${config.tokenPrefix}${task.list} ${content}\n\n\n`
61
-
65
+ const { tokenPrefix } = config
66
+ const { beforeText, list } = task
67
+ const taskStart = getRawTask({tokenPrefix, beforeText, list, text: ''})
68
+ const taskContent = `${taskStart} ${content}\n\n\n`
69
+
62
70
  this.fileGateway.preparePathForWriting(newPath)
63
71
  this.fileGateway.appendFileSync(newPath, taskContent)
64
72
  }
@@ -0,0 +1,13 @@
1
+ const Plugin = require('imdone-api')
2
+ const { save } = require('../adapters/storage/tags.js')
3
+
4
+ module.exports = class PersistTagsPlugin extends Plugin {
5
+ constructor(project) {
6
+ super(project)
7
+ }
8
+
9
+ async onBoardUpdate() {
10
+ await save(this.project.allTags, this.project.path)
11
+ }
12
+
13
+ }
@@ -14,7 +14,8 @@ module.exports = class PluginManager extends Emitter {
14
14
  this.defaultPlugins = [
15
15
  './archive-plugin',
16
16
  './epic-plugin',
17
- './extension-plugin'
17
+ './extension-plugin',
18
+ './persist-tags-plugin',
18
19
  ]
19
20
  this.pluginsMap = {}
20
21
  this.pluginPath = _path.join(project.path, '.imdone', 'plugins')
@@ -234,13 +235,13 @@ module.exports = class PluginManager extends Emitter {
234
235
  )
235
236
  }
236
237
 
237
- onBoardUpdate(lists) {
238
+ async onBoardUpdate(lists) {
238
239
  if (!lists || lists.length == 0) return
239
- this.eachPlugin(({ pluginInstance }) => {
240
+ this.eachPluginAsync(async ({ pluginInstance }) => {
240
241
  // const timeLabel = `${this.getPluginName(pluginInstance)} onBoardUpdate time`
241
242
  // console.time(timeLabel)
242
243
  try {
243
- pluginInstance.onBoardUpdate(lists)
244
+ await pluginInstance.onBoardUpdate(lists)
244
245
  } catch (e) {
245
246
  this.pluginError('onBoardUpdate', pluginInstance, e)
246
247
  }
package/lib/project.js CHANGED
@@ -142,9 +142,9 @@ module.exports = class WorkerProject extends Project {
142
142
  files: this.files,
143
143
  totals,
144
144
  tags: this.getTags(),
145
- allMeta: this.repo.allMeta,
146
- allContexts: [...this.repo.allContexts],
147
- allTags: [...this.repo.allTags],
145
+ allMeta: this.allMeta,
146
+ allContexts: this.allContexts,
147
+ allTags: this.allTags,
148
148
  filter: this.filter,
149
149
  defaultFilter: this.defaultFilter,
150
150
  actions: this.boardActions,
@@ -164,6 +164,18 @@ module.exports = class WorkerProject extends Project {
164
164
  if (this.pluginManager) this.pluginManager.destroyPlugins()
165
165
  }
166
166
 
167
+ get allTags() {
168
+ return [...this.repo.allTags]
169
+ }
170
+
171
+ get allContexts() {
172
+ return [...this.repo.allContexts]
173
+ }
174
+
175
+ get allMeta() {
176
+ return this.repo.allMeta
177
+ }
178
+
167
179
  get lists() {
168
180
  return this.getLists({
169
181
  tasks: this.filteredCards,
@@ -219,8 +231,8 @@ module.exports = class WorkerProject extends Project {
219
231
  ...new Set(queryProps),
220
232
  ...[...this.repo.metaKeys].map(key => `allMeta.${key}`),
221
233
  ...[...this.repo.metaKeys].map(key => `meta.${key}`),
222
- ...this.repo.allTags,
223
- ...this.repo.allContexts,
234
+ ...this.allTags,
235
+ ...this.allContexts,
224
236
  ]
225
237
  }
226
238
 
@@ -295,12 +307,7 @@ module.exports = class WorkerProject extends Project {
295
307
  }
296
308
 
297
309
  get boardActions() {
298
- // TODO Stop allowing board actions from config
299
- // <!-- order:-90 -->
300
310
  return [
301
- ...this.config.boardActions.map((item, index) => {
302
- return { ...item, index }
303
- }),
304
311
  ...this.pluginManager.getBoardActions(),
305
312
  ]
306
313
  }
@@ -474,15 +481,7 @@ module.exports = class WorkerProject extends Project {
474
481
  return new Promise((resolve, reject) => {
475
482
  this.repo.addTaskToFile(filePath, list, content, (err, file, task) => {
476
483
  if (err) return reject(err)
477
-
478
- const newList = task.list
479
- const list = this.lists.find(l => l.name === newList)
480
- const newPos = this.config.isAddNewCardsToTop() ? 0 : list.tasks.length - 1
481
-
482
- this.repo.moveTask({ task, newList, newPos }, (err) => {
483
- if (err) return reject(err)
484
- resolve({ file, task })
485
- })
484
+ resolve({ file, task })
486
485
  })
487
486
  })
488
487
  }
package/lib/repository.js CHANGED
@@ -465,6 +465,7 @@ Repository.prototype.addFile = function (file, cb) {
465
465
  if (this.destroyed) return cb(new Error('destroyed'))
466
466
  var self = this
467
467
  this.fileOK(file, function (err, ok) {
468
+ if (err) return cb(err)
468
469
  if (ok) {
469
470
  var index = self.files.findIndex(({ path }) => path === file.path)
470
471
  if (index > -1) {
@@ -942,13 +943,15 @@ Repository.prototype.writeAndExtract = function (file, emit, cb) {
942
943
  this.writeFile(file, emit, (err, file) => {
943
944
  if (err) return cb(new Error('Unable to write file:' + file.path))
944
945
  this.extractTasks(file, (err, file) => {
945
- if (err)
946
+ if (err) {
946
947
  return cb(new Error('Unable to extract tasks for file:' + file.path))
948
+ }
949
+
947
950
  this.addFile(file, (err) => {
948
- if (err)
949
- return cb(
950
- new Error('Unable to add file after extracting tasks:' + file.path)
951
- )
951
+ if (err) {
952
+ err.message = `Unable to add file after extracting tasks: #{file.path}\n${err.message}`
953
+ return cb(err)
954
+ }
952
955
  cb(null, file)
953
956
  })
954
957
  })
@@ -1097,20 +1100,18 @@ Repository.prototype.appendTask = function({file, content, list}, cb) {
1097
1100
  let sep = fileContent.indexOf(crlf) > -1 ? crlf : String(eol.lf)
1098
1101
  if (fileContent.endsWith(sep) || fileIsEmpty) sep = ''
1099
1102
  const order = appContext().projectContext.getOrder(list)
1100
- const orderString = isNumber(order) ? order : ''
1101
- const description =
1102
- lines.length > 1 ? Task.padDescription(lines.slice(1), taskPrefix) : []
1103
-
1104
- const rawTask = getRawTask({tokenPrefix: config.tokenPrefix, orderMeta: config.orderMeta, list, order, text, type: taskSyntax})
1105
- const taskContent = config.orderMeta
1106
- ? this.getTaskContent({
1107
- description,
1108
- order,
1109
- text,
1110
- taskPrefix,
1111
- taskSyntax,
1112
- })
1113
- : description.join(eol.lf)
1103
+ const description = lines.length > 1 ? Task.padDescription(lines.slice(1), taskPrefix) : []
1104
+ const { orderMeta, tokenPrefix } = config
1105
+ const rawTask = getRawTask({tokenPrefix, orderMeta, list, order, text, type: taskSyntax})
1106
+ const taskContent = config.orderMeta
1107
+ ? this.getTaskContent({
1108
+ description,
1109
+ order,
1110
+ text,
1111
+ taskPrefix,
1112
+ taskSyntax,
1113
+ })
1114
+ : description.join(eol.lf)
1114
1115
 
1115
1116
  let appendContent = File.trimBlankLines(
1116
1117
  `${taskPrefix}${rawTask}${eol.lf}${taskContent}`
@@ -1674,55 +1675,4 @@ Repository.prototype.query = function (queryString) {
1674
1675
  return Repository.getTasksByList(this, result, true)
1675
1676
  }
1676
1677
 
1677
- function trimFunction(propValue) {
1678
- let func = null
1679
- if (propValue && _isFunction(propValue)) {
1680
- const functionMatch = propValue.toString().match(functionRegex())
1681
- const lines = eol.split(functionMatch[3])
1682
- const funcBody = lines.filter((line) => line.trim().length > 0).join(eol.lf)
1683
- try {
1684
- func = new Function(funcBody)
1685
- } catch (e) {
1686
- console.error(`Unable to parse function:`, propValue.toString())
1687
- console.error(e)
1688
- }
1689
- }
1690
- return func
1691
- }
1692
-
1693
- Repository.trimFunctionsInSettings = function (config) {
1694
- const computed = _get(config, 'settings.cards.computed')
1695
- if (_isObject(computed)) {
1696
- Object.keys(computed).forEach((key) => {
1697
- const value = computed[key]
1698
- const func = trimFunction(value)
1699
- if (func) config.settings.cards.computed[key] = func
1700
- })
1701
- }
1702
-
1703
- let links = _get(config, 'settings.cards.links')
1704
- if (Array.isArray(links)) {
1705
- links = links.map((link) => {
1706
- ;['action', 'display'].forEach((prop) => {
1707
- const propValue = link[prop]
1708
- const func = trimFunction(propValue)
1709
- if (func) link[prop] = func
1710
- })
1711
- return link
1712
- })
1713
- config.settings.cards.links = links
1714
- }
1715
-
1716
- let actions = _get(config, 'settings.actions')
1717
- if (Array.isArray(actions)) {
1718
- actions = actions.map((action) => {
1719
- const propValue = action.function
1720
- const func = trimFunction(propValue)
1721
- if (func) action.function = func
1722
- return action
1723
- })
1724
- config.settings.actions = actions
1725
- }
1726
- }
1727
-
1728
1678
  module.exports = Repository
@@ -0,0 +1,17 @@
1
+ const {
2
+ load,
3
+ save
4
+ } = require('../adapters/storage/tags.js')
5
+
6
+ const {
7
+ findImdonePath
8
+ } = require('../adapters/storage/config.js')
9
+
10
+ module.exports = {
11
+ getTags
12
+ }
13
+
14
+ async function getTags(filePath) {
15
+ const projectPath = await findImdonePath(filePath)
16
+ return load(projectPath)
17
+ }
@@ -1,20 +1,18 @@
1
- const Config = require('../config')
1
+ const { loadForFilePath } = require('../adapters/storage/config')
2
2
  const { getFileParser } = require('../adapters/parsers/file/LocalFileParserFactory')
3
3
  const { Readable } = require('stream');
4
4
 
5
- // DOING Use task line parsers for vs code extension and obsidian plugin
6
- // <!--
7
- // order:-10000030
8
- // -->
9
- export async function getTasks({filePath, content}) {
10
- const projectPath = await Config.findImdonePath(filePath)
11
- const config = await Config.load(projectPath)
5
+ module.exports = {
6
+ getTasks
7
+ }
8
+
9
+ async function getTasks({filePath, content}) {
10
+ const config = await loadForFilePath(filePath)
12
11
 
13
- // create a readstream from the content and pass it to getFileParser
14
12
  const readStream = new Readable({
15
13
  read() {
16
14
  this.push(content);
17
- this.push(null); // Signals the end of the stream
15
+ this.push(null);
18
16
  }
19
17
  });
20
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imdone-core",
3
- "version": "1.47.1",
3
+ "version": "1.48.0",
4
4
  "description": "imdone-core",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,10 +0,0 @@
1
- module.exports = class FileSystemConfigReader {
2
- constructor(location) {
3
- this.location = location
4
- }
5
-
6
- // BACKLOG:-880 ## Implement the read method
7
- // [${source.path}](${source.path}) #adapter #urgent
8
- // <!-- epic:"Release 2.0" -->
9
- async read() {}
10
- }