bajo 2.1.0 → 2.2.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.
Files changed (99) hide show
  1. package/.github/FUNDING.yml +0 -0
  2. package/.github/workflows/repo-lockdown.yml +0 -0
  3. package/.jsdoc.conf.json +0 -0
  4. package/.mocharc.json +4 -0
  5. package/LICENSE +0 -0
  6. package/README.md +0 -0
  7. package/class/{misc → app}/log.js +8 -2
  8. package/class/app.js +63 -50
  9. package/class/bajo.js +43 -211
  10. package/class/base.js +25 -22
  11. package/class/helper/bajo.js +55 -53
  12. package/class/helper/base.js +37 -75
  13. package/class/{misc → plugin}/err.js +23 -18
  14. package/class/{misc → plugin}/print.js +7 -16
  15. package/class/plugin/tools.js +42 -0
  16. package/class/plugin.js +58 -54
  17. package/docs/App.html +0 -0
  18. package/docs/Bajo.html +0 -0
  19. package/docs/Base.html +0 -0
  20. package/docs/Err.html +0 -0
  21. package/docs/Log.html +0 -0
  22. package/docs/Plugin.html +0 -0
  23. package/docs/Print.html +0 -0
  24. package/docs/class_app.js.html +0 -0
  25. package/docs/class_bajo.js.html +0 -0
  26. package/docs/class_base.js.html +0 -0
  27. package/docs/class_helper_bajo.js.html +0 -0
  28. package/docs/class_helper_base.js.html +0 -0
  29. package/docs/class_misc_err.js.html +0 -0
  30. package/docs/class_misc_log.js.html +0 -0
  31. package/docs/class_misc_print.js.html +0 -0
  32. package/docs/class_plugin.js.html +0 -0
  33. package/docs/data/search.json +0 -0
  34. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  35. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  36. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  37. package/docs/global.html +0 -0
  38. package/docs/index.html +0 -0
  39. package/docs/index.js.html +0 -0
  40. package/docs/lib_current-loc.js.html +0 -0
  41. package/docs/lib_formats.js.html +0 -0
  42. package/docs/lib_import-module.js.html +0 -0
  43. package/docs/lib_log-levels.js.html +0 -0
  44. package/docs/lib_parse-args-argv.js.html +0 -0
  45. package/docs/lib_parse-env.js.html +0 -0
  46. package/docs/lib_resolve-path.js.html +0 -0
  47. package/docs/lib_shim.js.html +0 -0
  48. package/docs/module-Helper_Bajo.html +0 -0
  49. package/docs/module-Helper_Base.html +0 -0
  50. package/docs/module-Lib.html +0 -0
  51. package/docs/scripts/core.js +476 -477
  52. package/docs/scripts/core.min.js +0 -0
  53. package/docs/scripts/resize.js +36 -36
  54. package/docs/scripts/search.js +105 -105
  55. package/docs/scripts/search.min.js +0 -0
  56. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
  57. package/docs/scripts/third-party/fuse.js +1 -1
  58. package/docs/scripts/third-party/hljs-line-num-original.js +282 -285
  59. package/docs/scripts/third-party/hljs-line-num.js +1 -1
  60. package/docs/scripts/third-party/hljs-original.js +1195 -1202
  61. package/docs/scripts/third-party/hljs.js +1 -1
  62. package/docs/scripts/third-party/popper.js +1 -1
  63. package/docs/scripts/third-party/tippy.js +1 -1
  64. package/docs/scripts/third-party/tocbot.js +508 -509
  65. package/docs/scripts/third-party/tocbot.min.js +0 -0
  66. package/docs/static/bitcoin.jpeg +0 -0
  67. package/docs/static/home.md +0 -0
  68. package/docs/static/logo-ecosystem.png +0 -0
  69. package/docs/static/logo.png +0 -0
  70. package/docs/styles/clean-jsdoc-theme-base.css +0 -0
  71. package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
  72. package/docs/styles/clean-jsdoc-theme-light.css +0 -0
  73. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  74. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  75. package/docs/styles/clean-jsdoc-theme.min.css +0 -0
  76. package/extend/bajo/intl/en-US.json +10 -5
  77. package/extend/bajo/intl/id.json +10 -5
  78. package/extend/waibuStatic/virtual.json +0 -0
  79. package/index.js +9 -1
  80. package/lib/find-deep.js +24 -0
  81. package/lib/formats.js +0 -0
  82. package/lib/freeze.js +16 -0
  83. package/lib/import-module.js +5 -3
  84. package/lib/index.js +6 -0
  85. package/lib/log-levels.js +0 -0
  86. package/package.json +5 -11
  87. package/test/base.test.js +108 -0
  88. package/wiki/CHANGES.md +68 -0
  89. package/wiki/CONFIG.md +0 -0
  90. package/wiki/CONTRIBUTING.md +0 -0
  91. package/wiki/DEV_GUIDE.md +0 -0
  92. package/wiki/ECOSYSTEM.md +0 -0
  93. package/wiki/GETTING-STARTED.md +1 -1
  94. package/wiki/USER-GUIDE.md +0 -0
  95. package/lib/current-loc.js +0 -33
  96. package/lib/parse-args-argv.js +0 -80
  97. package/lib/parse-env.js +0 -50
  98. package/lib/resolve-path.js +0 -24
  99. package/lib/shim.js +0 -37
@@ -1,7 +1,5 @@
1
- import currentLoc from '../../lib/current-loc.js'
2
- import resolvePath from '../../lib/resolve-path.js'
3
- import Print from '../misc/print.js'
4
- import Log from '../misc/log.js'
1
+ import Print from '../plugin/print.js'
2
+ import Log from '../app/log.js'
5
3
  import omitDeep from 'omit-deep'
6
4
  import os from 'os'
7
5
  import fs from 'fs-extra'
@@ -10,15 +8,14 @@ import {
10
8
  buildConfigs,
11
9
  checkDependencies,
12
10
  checkNameAliases,
13
- attachMethods,
14
11
  collectHooks,
15
12
  run
16
13
  } from './base.js'
14
+ import aneka from 'aneka'
15
+
16
+ const { currentLoc, resolvePath } = aneka
17
17
 
18
18
  const {
19
- reduce,
20
- isNaN,
21
- forOwn,
22
19
  orderBy,
23
20
  isFunction,
24
21
  isPlainObject,
@@ -40,6 +37,9 @@ const omitted = ['spawn', 'cwd', 'name', 'alias', 'applet', 'a', 'plugins']
40
37
 
41
38
  const defConfig = {
42
39
  env: 'dev',
40
+ runtime: {
41
+ noWarning: false
42
+ },
43
43
  log: {
44
44
  timeTaken: false,
45
45
  dateFormat: 'YYYY-MM-DDTHH:mm:ss.SSS',
@@ -81,7 +81,7 @@ const defConfig = {
81
81
  const defMain = `async function factory (pkgName) {
82
82
  const me = this
83
83
 
84
- return class Main extends this.app.pluginClass.base {
84
+ return class Main extends this.app.baseClass.Base {
85
85
  constructor () {
86
86
  super(pkgName, me.app)
87
87
  this.config = {}
@@ -115,20 +115,24 @@ export async function buildBaseConfig () {
115
115
  this.config = defaultsDeep({}, this.app.envVars._, this.app.argv._)
116
116
  set(this, 'dir.base', this.app.dir)
117
117
  const path = currentLoc(import.meta).dir + '/../..'
118
- set(this, 'dir.pkg', this.resolvePath(path))
118
+ set(this, 'dir.pkg', resolvePath(path))
119
119
  if (get(this, 'config.dir.data')) set(this, 'dir.data', this.config.dir.data)
120
120
  if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
121
- this.dir.data = this.resolvePath(this.dir.data)
121
+ this.dir.data = resolvePath(this.dir.data)
122
122
  fs.ensureDirSync(`${this.dir.data}/config`)
123
123
  if (!this.dir.tmp) {
124
- this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.ns}`
124
+ this.dir.tmp = `${resolvePath(os.tmpdir())}/${this.ns}`
125
125
  fs.ensureDirSync(this.dir.tmp)
126
126
  }
127
+ this.pkg = await this.getPkgInfo()
127
128
  // collect list of plugins
128
- let pluginPkgs = []
129
- const pluginsFile = `${this.dir.data}/config/.plugins`
130
- if (fs.existsSync(pluginsFile)) {
131
- pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
129
+ const mainPkg = await this.getPkgInfo(this.app.dir)
130
+ let pluginPkgs = get(mainPkg, 'bajo.plugins', [])
131
+ if (isEmpty(pluginPkgs)) {
132
+ const pluginsFile = `${this.dir.data}/config/.plugins`
133
+ if (fs.existsSync(pluginsFile)) {
134
+ pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
135
+ }
132
136
  }
133
137
  this.app.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.app.mainNs), p => {
134
138
  return p[0] !== '#'
@@ -164,7 +168,10 @@ export async function buildPlugins () {
164
168
  const { default: builder } = await import(resolvePath(factory, true))
165
169
  const ClassDef = await builder.call(this, pkg)
166
170
  const plugin = new ClassDef()
167
- if (!(plugin instanceof this.app.pluginClass.base)) throw this.error('pluginPackageInvalid%s', pkg)
171
+ if (!(plugin instanceof this.app.baseClass.Base)) throw this.error('pluginPackageInvalid%s', pkg)
172
+ plugin.pkg = plugin.getPkgInfo(ns === 'main' ? this.dir.base : dir)
173
+ plugin.alias = ns === 'main' ? this.app.mainNs : get(plugin.pkg, 'bajo.alias', (pkg.slice(0, 5) === 'bajo-' ? pkg.slice(5) : ns).toLowerCase())
174
+ plugin.dependencies = get(plugin.pkg, 'bajo.dependencies', [])
168
175
  this.app.addPlugin(plugin, ClassDef)
169
176
  this.log.trace('- ' + pkg)
170
177
  }
@@ -190,7 +197,6 @@ export async function collectConfigHandlers () {
190
197
  if (isPlainObject(mod)) mod = [mod]
191
198
  this.app.configHandlers = this.app.configHandlers.concat(mod)
192
199
  }
193
- this.app.log = new Log(this.app)
194
200
  }
195
201
 
196
202
  /**
@@ -204,9 +210,11 @@ export async function collectConfigHandlers () {
204
210
  export async function buildExtConfig () {
205
211
  // config merging
206
212
  const { defaultsDeep } = this.app.lib.aneka
213
+ const { parseObject } = this.app.lib
214
+
207
215
  let resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
208
- resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env']), omitted)
209
- const envs = this.app.constructor.envs
216
+ resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env', 'runtime']), omitted)
217
+ const envs = this.app.envs
210
218
  this.config = defaultsDeep({}, this.config, resp, defConfig)
211
219
  // language
212
220
  this.config.lang = (this.config.lang ?? '').split('.')[0]
@@ -219,13 +227,15 @@ export async function buildExtConfig () {
219
227
  if (!this.config.log.level) this.config.log.level = this.config.env === 'dev' ? 'debug' : 'info'
220
228
  // misc
221
229
  const obj = this.app.applet ? this.config : pick(this.config, keys(defConfig))
222
- this.config = this.parseObject(obj, { parseValue: true })
230
+ this.config = parseObject(obj, { parseValue: true })
223
231
  const exts = this.app.getConfigFormats()
224
232
  if (this.app.applet) {
225
233
  if (!this.app.pluginPkgs.includes('bajo-cli')) throw this.error('appletNeedsBajoCli')
226
234
  if (!this.config.log.applet) this.config.log.level = 'silent'
227
235
  this.config.exitHandler = false
228
236
  }
237
+ if (this.config.runtime.noWarning) process.removeAllListeners('warning')
238
+ this.app.log = new Log(this.app)
229
239
  this.log.trace('dataDir%s', this.dir.data)
230
240
  this.log.debug('configHandlers%s', this.join(exts))
231
241
  }
@@ -236,32 +246,26 @@ export async function buildExtConfig () {
236
246
  * @async
237
247
  */
238
248
  export async function bootOrder () {
249
+ const { freeze } = this.app.lib
250
+ const { isNumber } = this.app.lib._
239
251
  this.log.debug('setupBootOrder')
240
- const order = reduce(this.app.pluginPkgs, (o, k, i) => {
241
- const key = map(k.split(':'), m => trim(m))
242
- if (key[1] && !isNaN(Number(key[1]))) o[key[0]] = Number(key[1])
243
- else o[key[0]] = 10000 + i
244
- return o
245
- }, {})
246
- const norder = {}
247
- for (let n of this.app.pluginPkgs) {
248
- n = map(n.split(':'), m => trim(m))[0]
249
- const dir = n === this.app.mainNs ? (`${this.dir.base}/${this.app.mainNs}`) : this.getModuleDir(n)
250
- if (n !== this.app.mainNs && !fs.existsSync(dir)) throw this.error('packageNotFoundOrNotBajo%s', n)
251
- norder[n] = NaN
252
- try {
253
- norder[n] = Number(trim(await fs.readFile(`${dir}/.bootorder`, 'utf8')))
254
- } catch (err) {}
252
+ let counter = 1000
253
+ const orders = []
254
+ for (const pkg of this.app.pluginPkgs) {
255
+ const item = { pkg }
256
+ const ns = camelCase(pkg)
257
+ const order = get(this.app[ns], 'pkg.bajo.bootorder')
258
+ if (isNumber(order)) item.val = order
259
+ else {
260
+ item.val = counter
261
+ counter++
262
+ }
263
+ orders.push(item)
255
264
  }
256
- const result = []
257
- forOwn(order, (v, k) => {
258
- const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
259
- result.push(item)
260
- })
261
- this.app.pluginPkgs = map(orderBy(result, ['v']), 'k')
262
- this.log.debug('runInEnv%s', this.t(this.app.constructor.envs[this.config.env]))
265
+ this.app.pluginPkgs = map(orderBy(orders, ['val']), 'pkg')
266
+ this.log.debug('runInEnv%s', this.t(this.app.envs[this.config.env]))
263
267
  // misc
264
- this.freeze(this.config)
268
+ freeze(this.config)
265
269
  }
266
270
 
267
271
  /**
@@ -270,9 +274,8 @@ export async function bootOrder () {
270
274
  * 1. {@link module:Helper/Base.buildConfigs|build configs}
271
275
  * 2. {@link module:Helper/Base.checkNameAliases|ensure names & aliases uniqueness}
272
276
  * 3. {@link module:Helper/Base.checkDependencies|ensure dependencies are met}
273
- * 4. {@link module:Helper/Base.attachMethods|build and attach dynamic methods}
274
- * 5. {@link module:Helper/Base.collectHooks|collect hooks}
275
- * 6. {@link module:Helper/Base.run|run plugins}
277
+ * 4. {@link module:Helper/Base.collectHooks|collect hooks}
278
+ * 5. {@link module:Helper/Base.run|run plugins}
276
279
  *
277
280
  * @async
278
281
  */
@@ -280,7 +283,6 @@ export async function bootPlugins () {
280
283
  await buildConfigs.call(this.app)
281
284
  await checkNameAliases.call(this.app)
282
285
  await checkDependencies.call(this.app)
283
- await attachMethods.call(this.app)
284
286
  await collectHooks.call(this.app)
285
287
  await run.call(this.app)
286
288
  }
@@ -296,11 +298,12 @@ export async function exitHandler () {
296
298
  async function exit (signal) {
297
299
  const { eachPlugins } = this
298
300
  if (signal) this.log.warn('signalReceived%s', signal)
301
+ const me = this
299
302
  await eachPlugins(async function ({ ns }) {
300
303
  try {
301
304
  await this.exit()
302
305
  } catch (err) {}
303
- this.log.trace('exited')
306
+ me.log.trace('exited%s', this.ns)
304
307
  })
305
308
  this.log.debug('appShutdown')
306
309
  process.exit(0)
@@ -356,8 +359,7 @@ export async function exitHandler () {
356
359
  export async function runAsApplet () {
357
360
  const { isString, map, find } = this.app.lib._
358
361
  await this.eachPlugins(async function ({ file }) {
359
- const { ns } = this
360
- const { alias } = this.constructor
362
+ const { ns, alias } = this
361
363
  this.app.applets.push({ ns, file, alias })
362
364
  }, { glob: 'applet.js', prefix: 'bajoCli' })
363
365
 
@@ -385,7 +387,7 @@ export async function runAsApplet () {
385
387
  * @see {@tutorial hook}
386
388
  * @see module:Helper/Bajo.runAsApplet
387
389
  */
388
- await this.runHook(`${this.app[applet.ns]}:beforeAppletRun`, ...this.app.args)
390
+ await this.runHook(`${applet.ns}:beforeAppletRun`, ...this.app.args)
389
391
  await this.app.bajoCli.runApplet(applet, path, ...this.app.args)
390
392
  /**
391
393
  * Run after applet is run. ```[ns]``` is applet's namespace
@@ -396,5 +398,5 @@ export async function runAsApplet () {
396
398
  * @see {@tutorial hook}
397
399
  * @see module:Helper/Bajo.runAsApplet
398
400
  */
399
- await this.runHook(`${this.app[applet.ns]}:afterAppletRun`, ...this.app.args)
401
+ await this.runHook(`${applet.ns}:afterAppletRun`, ...this.app.args)
400
402
  }
@@ -1,11 +1,8 @@
1
1
  import semver from 'semver'
2
2
  import lodash from 'lodash'
3
- import Print from '../misc/print.js'
4
- import path from 'path'
5
- import resolvePath from '../../lib/resolve-path.js'
3
+ import Print from '../plugin/print.js'
6
4
 
7
5
  const {
8
- isFunction,
9
6
  merge,
10
7
  forOwn,
11
8
  groupBy,
@@ -27,41 +24,6 @@ const {
27
24
  * @module Helper/Base
28
25
  */
29
26
 
30
- /**
31
- * Scan plugins ```method``` directories, and turn + attach its found files as methods dynamically.
32
- *
33
- * @async
34
- */
35
- export async function attachMethods () {
36
- const { fastGlob } = this.lib
37
-
38
- async function createMethod (dir) {
39
- dir = resolvePath(dir)
40
- const files = await fastGlob([`!${dir}/**/_*.{js,json}`, `${dir}/**/*.{js,json}`])
41
- for (const f of files) {
42
- const ext = path.extname(f)
43
- const base = f.replace(dir, '').slice(0, -ext.length)
44
- const name = camelCase(base)
45
- let mod
46
- if (ext === '.json') mod = this.app.bajo.readJson(f)
47
- else mod = await this.app.bajo.importModule(f)
48
- if (isFunction(mod)) mod = mod.bind(this)
49
- this[name] = mod
50
- }
51
- return files.length
52
- }
53
-
54
- const { eachPlugins } = this.bajo
55
- const me = this // the app
56
- me.bajo.log.debug('attachMethods')
57
- await eachPlugins(async function () {
58
- const { ns, pkgName } = this
59
- const dir = ns === me.mainNs ? (`${me.bajo.dir.base}/${me.mainNs}`) : me.bajo.getModuleDir(pkgName)
60
- const num = await createMethod.call(me[ns], `${dir}/method`, pkgName)
61
- me.bajo.log.trace('- %s (%d)', ns, num)
62
- })
63
- }
64
-
65
27
  /**
66
28
  * Build configurations
67
29
  *
@@ -82,18 +44,17 @@ export async function buildConfigs () {
82
44
  * @async
83
45
  */
84
46
  export async function checkNameAliases () {
85
- const { eachPlugins } = this.bajo
86
47
  this.bajo.log.debug('checkAliasNameClash')
87
48
  const refs = []
88
- await eachPlugins(async function () {
89
- const { ns, pkgName } = this
90
- const { alias } = this.constructor
49
+ for (const pkg of this.bajo.app.pluginPkgs) {
50
+ const plugin = this.bajo.app[camelCase(pkg)]
51
+ const { ns, alias } = plugin
91
52
  let item = find(refs, { ns })
92
- if (item) throw this.error('pluginNameClash%s%s%s%s', ns, pkgName, item.ns, item.pkgName, { code: 'BAJO_NAME_CLASH' })
53
+ if (item) throw this.error('pluginNameClash%s%s%s%s', ns, pkg, item.ns, item.pkg, { code: 'BAJO_NAME_CLASH' })
93
54
  item = find(refs, { alias })
94
- if (item) throw this.error('pluginNameClash%s%s%s%s', alias, pkgName, item.alias, item.pkgName, { code: 'BAJO_ALIAS_CLASH' })
95
- refs.push({ ns, alias, pkgName })
96
- })
55
+ if (item) throw this.error('pluginNameClash%s%s%s%s', alias, pkg, item.alias, item.pkg, { code: 'BAJO_ALIAS_CLASH' })
56
+ refs.push({ ns, alias, pkg })
57
+ }
97
58
  }
98
59
 
99
60
  /**
@@ -102,11 +63,12 @@ export async function checkNameAliases () {
102
63
  * @async
103
64
  */
104
65
  export async function checkDependencies () {
105
- async function runner () {
106
- const { ns, pkgName } = this
107
- const { join } = this.app.bajo
108
- this.app.bajo.log.trace('- %s', ns)
109
- const { dependencies } = this.app.pluginClass[this.ns]
66
+ const { join } = this.bajo
67
+ this.bajo.log.debug('checkDeps')
68
+ for (const pkg of this.bajo.app.pluginPkgs) {
69
+ const plugin = this.bajo.app[camelCase(pkg)]
70
+ const { ns, dependencies } = plugin
71
+ this.bajo.log.trace('- %s', ns)
110
72
  const odep = reduce(dependencies, (o, k) => {
111
73
  const item = map(k.split('@'), m => trim(m))
112
74
  if (k[0] === '@') o['@' + item[1]] = item[2]
@@ -115,25 +77,19 @@ export async function checkDependencies () {
115
77
  }, {})
116
78
  const deps = keys(odep)
117
79
  if (deps.length > 0) {
118
- if (intersection(this.app.pluginPkgs, deps).length !== deps.length) {
119
- throw this.error('dependencyUnfulfilled%s%s', pkgName, join(deps), { code: 'BAJO_DEPENDENCY' })
80
+ if (intersection(this.bajo.app.pluginPkgs, deps).length !== deps.length) {
81
+ throw this.error('dependencyUnfulfilled%s%s', pkg, join(deps), { code: 'BAJO_DEPENDENCY' })
120
82
  }
121
83
  each(deps, d => {
122
84
  if (!odep[d]) return
123
- const ver = get(this.app[camelCase(d)], 'config.pkg.version')
85
+ const ver = get(this.bajo.app[camelCase(d)], 'pkg.version')
124
86
  if (!ver) return
125
87
  if (!semver.satisfies(ver, odep[d])) {
126
- throw this.error('semverCheckFailed%s%s', pkgName, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
88
+ throw this.error('semverCheckFailed%s%s', pkg, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
127
89
  }
128
90
  })
129
91
  }
130
92
  }
131
-
132
- const { eachPlugins } = this.bajo
133
- this.bajo.log.debug('checkDeps')
134
- await eachPlugins(async function () {
135
- await runner.call(this)
136
- })
137
93
  }
138
94
 
139
95
  /**
@@ -146,25 +102,26 @@ export async function collectHooks () {
146
102
  const { eachPlugins, runHook, isLogInRange, importModule, breakNsPathFromFile } = this.bajo
147
103
  const me = this
148
104
  me.bajo.hooks = this.bajo.hooks ?? []
149
- me.bajo.log.debug('collectHooks')
105
+ me.bajo.log.trace('collecting%s', this.t('hooks'))
150
106
  // collects
151
107
  await eachPlugins(async function ({ dir, file }) {
152
- const { ns } = this
153
- const { fullNs, path } = breakNsPathFromFile({ file, dir, baseNs: ns, suffix: '/hook/' })
108
+ const { ns: baseNs } = this
109
+ const { ns, subNs, path } = breakNsPathFromFile({ file, dir, baseNs, suffix: '/hook/' })
154
110
  const mod = await importModule(file, { asHandler: true })
155
111
  if (!mod) return undefined
156
- merge(mod, { ns: fullNs, path, src: ns })
112
+ merge(mod, { ns, subNs, path, src: baseNs })
157
113
  me.bajo.hooks.push(mod)
158
114
  }, { glob: 'hook/**/*.js', prefix: me.bajo.ns })
159
115
  // for log trace purpose only
160
- if (!isLogInRange('trace')) return
161
- const items = groupBy(me.bajo.hooks, 'ns')
162
- forOwn(items, (v, k) => {
163
- const hooks = groupBy(v, 'path')
164
- forOwn(hooks, (v1, k1) => {
165
- me.bajo.log.trace('- %s:%s (%d)', k, k1, v1.length)
116
+ if (isLogInRange('trace')) {
117
+ const items = groupBy(me.bajo.hooks, item => item.ns + (item.subNs ? `.${item.subNs}` : ''))
118
+ forOwn(items, (v, k) => {
119
+ const hooks = groupBy(v, 'path')
120
+ forOwn(hooks, (v1, k1) => {
121
+ me.bajo.log.trace('- %s:%s (%d)', k, k1, v1.length)
122
+ })
166
123
  })
167
- })
124
+ }
168
125
 
169
126
  /**
170
127
  * Run after hooks are collected
@@ -176,6 +133,7 @@ export async function collectHooks () {
176
133
  * @see module:Helper/Base.collectHooks
177
134
  */
178
135
  await runHook('bajo:afterCollectHooks', this.bajo.hooks)
136
+ me.bajo.log.debug('collected%s%d', this.t('hooks'), me.bajo.hooks.length)
179
137
  }
180
138
 
181
139
  /**
@@ -190,10 +148,9 @@ export async function collectHooks () {
190
148
  export async function run () {
191
149
  const me = this
192
150
  const { runHook, eachPlugins, join } = me.bajo
193
- const { freeze } = me.bajo
151
+ const { freeze } = me.lib
194
152
  const methods = ['init']
195
153
  if (!me.applet) methods.push('start')
196
- me.bajo.log.debug('loadedPlugins%s', join(map(me.bajo.app.pluginPkgs, b => camelCase(b))))
197
154
  for (const method of methods) {
198
155
  /**
199
156
  * Run before all ```{method}``` executed. Accepted ```{method}```: ```Init``` or ```Start```
@@ -240,4 +197,9 @@ export async function run () {
240
197
  */
241
198
  await runHook(`bajo:${camelCase(`after all ${method}`)}`)
242
199
  }
200
+ if (me.bajo.config.log.level === 'trace') {
201
+ let text = join(map(me.bajo.app.pluginPkgs, b => camelCase(b)))
202
+ text += ` (${me.bajo.app.pluginPkgs.length})`
203
+ me.bajo.log.trace('loadedPlugins%s', text)
204
+ } else me.bajo.log.debug('loadedPlugins%s', me.bajo.app.pluginPkgs.length)
243
205
  }
@@ -1,4 +1,6 @@
1
1
  import lodash from 'lodash'
2
+ import Tools from './tools.js'
3
+
2
4
  const { isPlainObject, each, isArray, get, isEmpty, merge } = lodash
3
5
 
4
6
  Error.stackTraceLimit = 15
@@ -14,24 +16,14 @@ Error.stackTraceLimit = 15
14
16
  * if (notfound) throw this.error('Sorry, item is nowhere to be found!')
15
17
  * ```
16
18
  */
17
- class Err {
19
+ class Err extends Tools {
18
20
  /**
19
21
  * @param {Plugin} plugin - Plugin instance
20
22
  * @param {string} msg - Error message
21
23
  * @param {...any} [args] - Variables to interpolate with error message. Payload object can be pushed at the very last argument
22
24
  */
23
25
  constructor (plugin, msg, ...args) {
24
- /**
25
- * Attached plugin
26
- * @type {Plugin}
27
- */
28
- this.plugin = plugin
29
-
30
- /**
31
- * The app instance
32
- * @type {App}
33
- */
34
- this.app = plugin.app
26
+ super(plugin)
35
27
 
36
28
  /**
37
29
  * Error payload extracted from the last arguments
@@ -50,7 +42,6 @@ class Err {
50
42
  * @type {string}
51
43
  */
52
44
  this.message = this.payload.noTrans ? msg : this.plugin.t(msg, ...args)
53
- this.write()
54
45
  }
55
46
 
56
47
  /**
@@ -69,10 +60,12 @@ class Err {
69
60
  stacks.splice(1, 1)
70
61
  err.stack = stacks.join('\n')
71
62
  const values = {}
63
+ let detailsMessage
72
64
  for (const key in this.payload) {
73
65
  const value = this.payload[key]
74
66
  if (key === 'details' && isArray(value)) {
75
- const result = this.formatErrorDetails(value)
67
+ const { result, detailsMessage: dm } = this.formatErrorDetails(value)
68
+ if (!isEmpty(dm)) detailsMessage = dm
76
69
  if (result) merge(values, result)
77
70
  }
78
71
  err[key] = value
@@ -80,6 +73,7 @@ class Err {
80
73
  if (!isEmpty(values)) err.values = values
81
74
  err.ns = this.plugin.ns
82
75
  err.orgMessage = this.orgMessage
76
+ if (detailsMessage) err.detailsMessage = detailsMessage
83
77
  return err
84
78
  }
85
79
 
@@ -91,7 +85,7 @@ class Err {
91
85
  fatal = () => {
92
86
  const err = this.write()
93
87
  console.error(err)
94
- this.app.exit()
88
+ this.app.exit(true)
95
89
  }
96
90
 
97
91
  /**
@@ -102,9 +96,10 @@ class Err {
102
96
  * @returns {Object}
103
97
  */
104
98
  formatErrorDetails = (value) => {
105
- const { isString } = this.app.lib._
99
+ const { isString, last } = this.app.lib._
106
100
  const result = {}
107
101
  const me = this
102
+ const detailsMessage = []
108
103
  each(value, (v, i) => {
109
104
  if (isString(v)) v = { error: v }
110
105
  if (!v.context) return undefined
@@ -112,14 +107,24 @@ class Err {
112
107
  if (v.type === 'any.only') v.context.ref = get(v, 'context.valids', []).join(', ')
113
108
  const field = get(v, 'context.key')
114
109
  const val = get(v, 'context.value')
110
+ let error = me.plugin.t(`validation.${v.type}`, v.context ?? {}, {})
111
+ if (error.includes(' ref:')) {
112
+ const item = last(error.split(' '))
113
+ const [, rfield] = item.split(':')
114
+ error = error.replace(item, `'${me.plugin.t('field.' + rfield)}'`)
115
+ }
115
116
  value[i] = {
116
117
  field,
117
- error: me.plugin.t(`validation.${v.type}`, v.context ?? {}, {}),
118
+ error,
118
119
  value: val,
119
120
  ext: { type: v.type, context: v.context }
120
121
  }
122
+ detailsMessage.push(me.plugin.t('fieldError%s%s', field, value[i].error))
121
123
  })
122
- return result
124
+ return {
125
+ result,
126
+ detailsMessage: detailsMessage.length > 0 ? (me.plugin.t('error') + ': ' + detailsMessage.join(', ')) : ''
127
+ }
123
128
  }
124
129
  }
125
130
 
@@ -1,7 +1,8 @@
1
1
  import ora from 'ora'
2
2
  import lodash from 'lodash'
3
- import aneka from 'aneka'
4
- const { defaultsDeep } = aneka
3
+ import defaultsDeep from 'aneka/src/defaults-deep.js'
4
+ import secToHms from 'aneka/src/sec-to-hms.js'
5
+ import Tools from './tools.js'
5
6
 
6
7
  const { isPlainObject } = lodash
7
8
 
@@ -22,29 +23,19 @@ const { isPlainObject } = lodash
22
23
  *
23
24
  * @class
24
25
  */
25
- class Print {
26
+ class Print extends Tools {
26
27
  /**
27
28
  * @param {Plugin} plugin - Plugin instance
28
29
  * @param {TPrintOptions} [options={}] - Options object
29
30
  */
30
31
  constructor (plugin, options = {}) {
32
+ super(plugin)
33
+
31
34
  /**
32
35
  * Options object
33
36
  * @type {TPrintOptions}
34
37
  */
35
38
  this.options = options
36
-
37
- /**
38
- * Attached plugin
39
- * @type {Plugin}
40
- */
41
- this.plugin = plugin
42
-
43
- /**
44
- * The app instance
45
- * @type {App}
46
- */
47
- this.app = plugin.app
48
39
  if (this.app.applet) {
49
40
  if (this.app.bajo.config.counter) this.options.showCounter = true
50
41
  if (this.app.bajo.config.datetime) this.options.showDatetime = true
@@ -122,7 +113,7 @@ class Print {
122
113
  getElapsed = (unit = 'hms') => {
123
114
  const u = unit === 'hms' ? 'second' : unit
124
115
  const elapsed = this.app.lib.dayjs().diff(this.startTime, u)
125
- return unit === 'hms' ? this.app.lib.aneka.secToHms(elapsed) : elapsed
116
+ return unit === 'hms' ? secToHms(elapsed) : elapsed
126
117
  }
127
118
 
128
119
  /**
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Base tools class
3
+ *
4
+ * @class
5
+ */
6
+
7
+ class Tools {
8
+ constructor (plugin) {
9
+ /**
10
+ * Attached plugin
11
+ * @type {Plugin}
12
+ */
13
+ this.plugin = plugin
14
+
15
+ /**
16
+ * The app instance
17
+ * @type {App}
18
+ */
19
+ this.app = plugin.app
20
+ }
21
+
22
+ /**
23
+ * Force bind methods to self (```this```)
24
+ *
25
+ * @param {string[]} names - Method's names
26
+ */
27
+ selfBind (names) {
28
+ for (const name of names) {
29
+ this[name] = this[name].bind(this)
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Dispose internal references
35
+ */
36
+ dispose () {
37
+ this.app = null
38
+ this.plugin = null
39
+ }
40
+ }
41
+
42
+ export default Tools