bajo 2.0.2 → 2.2.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.
- package/.github/FUNDING.yml +0 -0
- package/.github/workflows/repo-lockdown.yml +0 -0
- package/.jsdoc.conf.json +0 -0
- package/.mocharc.json +4 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/class/{misc → app}/log.js +73 -24
- package/class/app.js +65 -50
- package/class/bajo.js +43 -211
- package/class/base.js +25 -22
- package/class/helper/bajo.js +67 -60
- package/class/helper/base.js +34 -75
- package/class/{misc → plugin}/err.js +23 -18
- package/class/{misc → plugin}/print.js +7 -16
- package/class/plugin/tools.js +42 -0
- package/class/plugin.js +58 -54
- package/docs/App.html +0 -0
- package/docs/Bajo.html +0 -0
- package/docs/Base.html +0 -0
- package/docs/Err.html +0 -0
- package/docs/Log.html +0 -0
- package/docs/Plugin.html +0 -0
- package/docs/Print.html +0 -0
- package/docs/class_app.js.html +0 -0
- package/docs/class_bajo.js.html +0 -0
- package/docs/class_base.js.html +0 -0
- package/docs/class_helper_bajo.js.html +0 -0
- package/docs/class_helper_base.js.html +0 -0
- package/docs/class_misc_err.js.html +0 -0
- package/docs/class_misc_log.js.html +0 -0
- package/docs/class_misc_print.js.html +0 -0
- package/docs/class_plugin.js.html +0 -0
- package/docs/data/search.json +0 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/global.html +0 -0
- package/docs/index.html +0 -0
- package/docs/index.js.html +0 -0
- package/docs/lib_current-loc.js.html +0 -0
- package/docs/lib_formats.js.html +0 -0
- package/docs/lib_import-module.js.html +0 -0
- package/docs/lib_log-levels.js.html +0 -0
- package/docs/lib_parse-args-argv.js.html +0 -0
- package/docs/lib_parse-env.js.html +0 -0
- package/docs/lib_resolve-path.js.html +0 -0
- package/docs/lib_shim.js.html +0 -0
- package/docs/module-Helper_Bajo.html +0 -0
- package/docs/module-Helper_Base.html +0 -0
- package/docs/module-Lib.html +0 -0
- package/docs/scripts/core.js +476 -477
- package/docs/scripts/core.min.js +0 -0
- package/docs/scripts/resize.js +36 -36
- package/docs/scripts/search.js +105 -105
- package/docs/scripts/search.min.js +0 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
- package/docs/scripts/third-party/fuse.js +1 -1
- package/docs/scripts/third-party/hljs-line-num-original.js +282 -285
- package/docs/scripts/third-party/hljs-line-num.js +1 -1
- package/docs/scripts/third-party/hljs-original.js +1195 -1202
- package/docs/scripts/third-party/hljs.js +1 -1
- package/docs/scripts/third-party/popper.js +1 -1
- package/docs/scripts/third-party/tippy.js +1 -1
- package/docs/scripts/third-party/tocbot.js +508 -509
- package/docs/scripts/third-party/tocbot.min.js +0 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +0 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +0 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
- package/docs/styles/clean-jsdoc-theme-light.css +0 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
- package/docs/styles/clean-jsdoc-theme.min.css +0 -0
- package/extend/bajo/intl/en-US.json +11 -5
- package/extend/bajo/intl/id.json +11 -5
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +9 -1
- package/lib/find-deep.js +24 -0
- package/lib/formats.js +0 -0
- package/lib/freeze.js +16 -0
- package/lib/import-module.js +5 -3
- package/lib/index.js +6 -0
- package/lib/log-levels.js +0 -0
- package/package.json +5 -11
- package/test/base.test.js +108 -0
- package/wiki/CHANGES.md +63 -0
- package/wiki/CONFIG.md +7 -1
- package/wiki/CONTRIBUTING.md +0 -0
- package/wiki/DEV_GUIDE.md +0 -0
- package/wiki/ECOSYSTEM.md +0 -0
- package/wiki/GETTING-STARTED.md +1 -1
- package/wiki/USER-GUIDE.md +0 -0
- package/lib/current-loc.js +0 -33
- package/lib/parse-args-argv.js +0 -80
- package/lib/parse-env.js +0 -50
- package/lib/resolve-path.js +0 -24
- package/lib/shim.js +0 -37
package/class/helper/bajo.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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,13 +37,23 @@ 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
|
-
dateFormat: 'YYYY-MM-DDTHH:
|
|
46
|
-
|
|
45
|
+
dateFormat: 'YYYY-MM-DDTHH:mm:ss.SSS',
|
|
46
|
+
useUtc: false,
|
|
47
47
|
pretty: false,
|
|
48
48
|
applet: false,
|
|
49
|
-
traceHook: false
|
|
49
|
+
traceHook: false,
|
|
50
|
+
save: false,
|
|
51
|
+
rotation: {
|
|
52
|
+
cycle: 'none', // none, daily, weekly, monthly
|
|
53
|
+
compressOld: true,
|
|
54
|
+
byPlugin: false,
|
|
55
|
+
retain: 5
|
|
56
|
+
}
|
|
50
57
|
},
|
|
51
58
|
lang: Intl.DateTimeFormat().resolvedOptions().lang ?? 'en-US',
|
|
52
59
|
intl: {
|
|
@@ -74,7 +81,7 @@ const defConfig = {
|
|
|
74
81
|
const defMain = `async function factory (pkgName) {
|
|
75
82
|
const me = this
|
|
76
83
|
|
|
77
|
-
return class Main extends this.app.
|
|
84
|
+
return class Main extends this.app.baseClass.Base {
|
|
78
85
|
constructor () {
|
|
79
86
|
super(pkgName, me.app)
|
|
80
87
|
this.config = {}
|
|
@@ -108,23 +115,24 @@ export async function buildBaseConfig () {
|
|
|
108
115
|
this.config = defaultsDeep({}, this.app.envVars._, this.app.argv._)
|
|
109
116
|
set(this, 'dir.base', this.app.dir)
|
|
110
117
|
const path = currentLoc(import.meta).dir + '/../..'
|
|
111
|
-
set(this, 'dir.pkg',
|
|
118
|
+
set(this, 'dir.pkg', resolvePath(path))
|
|
119
|
+
if (get(this, 'config.dir.data')) set(this, 'dir.data', this.config.dir.data)
|
|
112
120
|
if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
|
|
113
|
-
this.dir.data =
|
|
114
|
-
if (!fs.existsSync(this.dir.data)) {
|
|
115
|
-
console.log('Data directory (%s) doesn\'t exist yet', this.dir.data)
|
|
116
|
-
process.exit(1)
|
|
117
|
-
}
|
|
121
|
+
this.dir.data = resolvePath(this.dir.data)
|
|
118
122
|
fs.ensureDirSync(`${this.dir.data}/config`)
|
|
119
123
|
if (!this.dir.tmp) {
|
|
120
|
-
this.dir.tmp = `${
|
|
124
|
+
this.dir.tmp = `${resolvePath(os.tmpdir())}/${this.ns}`
|
|
121
125
|
fs.ensureDirSync(this.dir.tmp)
|
|
122
126
|
}
|
|
127
|
+
this.pkg = await this.getPkgInfo()
|
|
123
128
|
// collect list of plugins
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
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
|
+
}
|
|
128
136
|
}
|
|
129
137
|
this.app.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.app.mainNs), p => {
|
|
130
138
|
return p[0] !== '#'
|
|
@@ -160,7 +168,10 @@ export async function buildPlugins () {
|
|
|
160
168
|
const { default: builder } = await import(resolvePath(factory, true))
|
|
161
169
|
const ClassDef = await builder.call(this, pkg)
|
|
162
170
|
const plugin = new ClassDef()
|
|
163
|
-
if (!(plugin instanceof this.app.
|
|
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', [])
|
|
164
175
|
this.app.addPlugin(plugin, ClassDef)
|
|
165
176
|
this.log.trace('- ' + pkg)
|
|
166
177
|
}
|
|
@@ -186,7 +197,6 @@ export async function collectConfigHandlers () {
|
|
|
186
197
|
if (isPlainObject(mod)) mod = [mod]
|
|
187
198
|
this.app.configHandlers = this.app.configHandlers.concat(mod)
|
|
188
199
|
}
|
|
189
|
-
this.app.log = new Log(this.app)
|
|
190
200
|
}
|
|
191
201
|
|
|
192
202
|
/**
|
|
@@ -200,9 +210,11 @@ export async function collectConfigHandlers () {
|
|
|
200
210
|
export async function buildExtConfig () {
|
|
201
211
|
// config merging
|
|
202
212
|
const { defaultsDeep } = this.app.lib.aneka
|
|
213
|
+
const { parseObject } = this.app.lib
|
|
214
|
+
|
|
203
215
|
let resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
|
|
204
|
-
resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env']), omitted)
|
|
205
|
-
const envs = this.app.
|
|
216
|
+
resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env', 'runtime']), omitted)
|
|
217
|
+
const envs = this.app.envs
|
|
206
218
|
this.config = defaultsDeep({}, this.config, resp, defConfig)
|
|
207
219
|
// language
|
|
208
220
|
this.config.lang = (this.config.lang ?? '').split('.')[0]
|
|
@@ -215,13 +227,16 @@ export async function buildExtConfig () {
|
|
|
215
227
|
if (!this.config.log.level) this.config.log.level = this.config.env === 'dev' ? 'debug' : 'info'
|
|
216
228
|
// misc
|
|
217
229
|
const obj = this.app.applet ? this.config : pick(this.config, keys(defConfig))
|
|
218
|
-
this.config =
|
|
230
|
+
this.config = parseObject(obj, { parseValue: true })
|
|
219
231
|
const exts = this.app.getConfigFormats()
|
|
220
232
|
if (this.app.applet) {
|
|
221
233
|
if (!this.app.pluginPkgs.includes('bajo-cli')) throw this.error('appletNeedsBajoCli')
|
|
222
234
|
if (!this.config.log.applet) this.config.log.level = 'silent'
|
|
223
235
|
this.config.exitHandler = false
|
|
224
236
|
}
|
|
237
|
+
if (this.config.runtime.noWarning) process.removeAllListeners('warning')
|
|
238
|
+
this.app.log = new Log(this.app)
|
|
239
|
+
this.log.trace('dataDir%s', this.dir.data)
|
|
225
240
|
this.log.debug('configHandlers%s', this.join(exts))
|
|
226
241
|
}
|
|
227
242
|
|
|
@@ -231,32 +246,26 @@ export async function buildExtConfig () {
|
|
|
231
246
|
* @async
|
|
232
247
|
*/
|
|
233
248
|
export async function bootOrder () {
|
|
249
|
+
const { freeze } = this.app.lib
|
|
250
|
+
const { isNumber } = this.app.lib._
|
|
234
251
|
this.log.debug('setupBootOrder')
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
norder[n] = Number(trim(await fs.readFile(`${dir}/.bootorder`, 'utf8')))
|
|
249
|
-
} 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)
|
|
250
264
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
|
|
254
|
-
result.push(item)
|
|
255
|
-
})
|
|
256
|
-
this.app.pluginPkgs = map(orderBy(result, ['v']), 'k')
|
|
257
|
-
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]))
|
|
258
267
|
// misc
|
|
259
|
-
|
|
268
|
+
freeze(this.config)
|
|
260
269
|
}
|
|
261
270
|
|
|
262
271
|
/**
|
|
@@ -265,9 +274,8 @@ export async function bootOrder () {
|
|
|
265
274
|
* 1. {@link module:Helper/Base.buildConfigs|build configs}
|
|
266
275
|
* 2. {@link module:Helper/Base.checkNameAliases|ensure names & aliases uniqueness}
|
|
267
276
|
* 3. {@link module:Helper/Base.checkDependencies|ensure dependencies are met}
|
|
268
|
-
* 4. {@link module:Helper/Base.
|
|
269
|
-
* 5. {@link module:Helper/Base.
|
|
270
|
-
* 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}
|
|
271
279
|
*
|
|
272
280
|
* @async
|
|
273
281
|
*/
|
|
@@ -275,7 +283,6 @@ export async function bootPlugins () {
|
|
|
275
283
|
await buildConfigs.call(this.app)
|
|
276
284
|
await checkNameAliases.call(this.app)
|
|
277
285
|
await checkDependencies.call(this.app)
|
|
278
|
-
await attachMethods.call(this.app)
|
|
279
286
|
await collectHooks.call(this.app)
|
|
280
287
|
await run.call(this.app)
|
|
281
288
|
}
|
|
@@ -291,11 +298,12 @@ export async function exitHandler () {
|
|
|
291
298
|
async function exit (signal) {
|
|
292
299
|
const { eachPlugins } = this
|
|
293
300
|
if (signal) this.log.warn('signalReceived%s', signal)
|
|
301
|
+
const me = this
|
|
294
302
|
await eachPlugins(async function ({ ns }) {
|
|
295
303
|
try {
|
|
296
304
|
await this.exit()
|
|
297
305
|
} catch (err) {}
|
|
298
|
-
|
|
306
|
+
me.log.trace('exited%s', this.ns)
|
|
299
307
|
})
|
|
300
308
|
this.log.debug('appShutdown')
|
|
301
309
|
process.exit(0)
|
|
@@ -351,8 +359,7 @@ export async function exitHandler () {
|
|
|
351
359
|
export async function runAsApplet () {
|
|
352
360
|
const { isString, map, find } = this.app.lib._
|
|
353
361
|
await this.eachPlugins(async function ({ file }) {
|
|
354
|
-
const { ns } = this
|
|
355
|
-
const { alias } = this.constructor
|
|
362
|
+
const { ns, alias } = this
|
|
356
363
|
this.app.applets.push({ ns, file, alias })
|
|
357
364
|
}, { glob: 'applet.js', prefix: 'bajoCli' })
|
|
358
365
|
|
|
@@ -380,7 +387,7 @@ export async function runAsApplet () {
|
|
|
380
387
|
* @see {@tutorial hook}
|
|
381
388
|
* @see module:Helper/Bajo.runAsApplet
|
|
382
389
|
*/
|
|
383
|
-
await this.runHook(`${
|
|
390
|
+
await this.runHook(`${applet.ns}:beforeAppletRun`, ...this.app.args)
|
|
384
391
|
await this.app.bajoCli.runApplet(applet, path, ...this.app.args)
|
|
385
392
|
/**
|
|
386
393
|
* Run after applet is run. ```[ns]``` is applet's namespace
|
|
@@ -391,5 +398,5 @@ export async function runAsApplet () {
|
|
|
391
398
|
* @see {@tutorial hook}
|
|
392
399
|
* @see module:Helper/Bajo.runAsApplet
|
|
393
400
|
*/
|
|
394
|
-
await this.runHook(`${
|
|
401
|
+
await this.runHook(`${applet.ns}:afterAppletRun`, ...this.app.args)
|
|
395
402
|
}
|
package/class/helper/base.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import semver from 'semver'
|
|
2
2
|
import lodash from 'lodash'
|
|
3
|
-
import Print from '../
|
|
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
|
-
|
|
89
|
-
const
|
|
90
|
-
const { alias } =
|
|
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,
|
|
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,
|
|
95
|
-
refs.push({ ns, alias,
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
const { dependencies } =
|
|
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',
|
|
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)], '
|
|
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',
|
|
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.
|
|
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 {
|
|
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
|
|
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 (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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.
|
|
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```
|
|
@@ -239,5 +196,7 @@ export async function run () {
|
|
|
239
196
|
* @see module:Helper/Base.run
|
|
240
197
|
*/
|
|
241
198
|
await runHook(`bajo:${camelCase(`after all ${method}`)}`)
|
|
199
|
+
if (me.bajo.config.log.level === 'trace') me.bajo.log.trace('loadedPlugins%s', join(map(me.bajo.app.pluginPkgs, b => camelCase(b))))
|
|
200
|
+
else me.bajo.log.debug('loadedPlugins%s', me.bajo.app.pluginPkgs.length)
|
|
242
201
|
}
|
|
243
202
|
}
|
|
@@ -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
|
|
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
|
|
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
|
|
4
|
-
|
|
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' ?
|
|
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
|