bajo 2.0.1 → 2.1.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 +13 -0
- package/.github/workflows/repo-lockdown.yml +24 -0
- package/.jsdoc.conf.json +7 -6
- package/LICENSE +1 -1
- package/README.md +50 -18
- package/class/app.js +378 -54
- package/class/bajo.js +228 -149
- package/class/base.js +106 -0
- package/class/helper/bajo.js +146 -90
- package/class/helper/{plugin.js → base.js} +96 -22
- package/class/{base → misc}/err.js +44 -14
- package/class/misc/log.js +255 -0
- package/class/misc/print.js +264 -0
- package/class/plugin.js +120 -68
- package/docs/App.html +23 -1
- package/docs/Bajo.html +2 -9
- package/docs/Base.html +3 -0
- package/docs/Err.html +3 -1
- package/docs/Log.html +5 -1
- package/docs/Plugin.html +1 -1
- package/docs/Print.html +1 -1
- package/docs/class_app.js.html +378 -56
- package/docs/class_bajo.js.html +230 -151
- package/docs/class_base.js.html +109 -0
- package/docs/class_helper_bajo.js.html +138 -87
- package/docs/class_helper_base.js.html +246 -0
- package/docs/class_misc_err.js.html +129 -0
- package/docs/class_misc_log.js.html +210 -0
- package/docs/class_misc_print.js.html +267 -0
- package/docs/class_plugin.js.html +122 -70
- package/docs/data/search.json +1 -1
- package/docs/global.html +4 -1
- package/docs/index.html +2 -2
- package/docs/index.js.html +35 -0
- package/docs/lib_current-loc.js.html +36 -0
- package/docs/lib_formats.js.html +8 -8
- package/docs/lib_import-module.js.html +59 -0
- package/docs/lib_log-levels.js.html +17 -7
- package/docs/lib_parse-args-argv.js.html +83 -0
- package/docs/lib_parse-env.js.html +53 -0
- package/docs/lib_resolve-path.js.html +3 -6
- package/docs/lib_shim.js.html +8 -3
- package/docs/module-Helper_Bajo.html +3 -0
- package/docs/module-Helper_Base.html +3 -0
- package/docs/module-Lib.html +15 -0
- package/docs/static/home.md +32 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/extend/bajo/intl/en-US.json +9 -2
- package/extend/bajo/intl/id.json +9 -2
- package/index.js +22 -2
- package/lib/current-loc.js +24 -2
- package/lib/formats.js +6 -6
- package/lib/import-module.js +29 -0
- package/lib/log-levels.js +15 -5
- package/lib/parse-args-argv.js +20 -12
- package/lib/parse-env.js +18 -7
- package/lib/resolve-path.js +1 -4
- package/lib/shim.js +6 -1
- package/package.json +4 -7
- package/wiki/CONFIG.md +36 -0
- package/wiki/CONTRIBUTING.md +5 -0
- package/wiki/DEV_GUIDE.md +3 -0
- package/wiki/ECOSYSTEM.md +93 -0
- package/wiki/GETTING-STARTED.md +356 -0
- package/wiki/USER-GUIDE.md +256 -0
- package/class/base/log.js +0 -205
- package/class/base/plugin.js +0 -168
- package/class/base/print.js +0 -272
- package/docs/BasePlugin.html +0 -5
- package/docs/class_base_err.js.html +0 -99
- package/docs/class_base_log.js.html +0 -208
- package/docs/class_base_plugin.js.html +0 -180
- package/docs/class_base_print.js.html +0 -275
- package/docs/class_helper_plugin.js.html +0 -172
- package/docs/lib_create-method.js.html +0 -42
- package/docs/module-class_helper_bajo.html +0 -3
- package/docs/module-class_helper_plugin.html +0 -3
- package/docs/module-lib_create-method.html +0 -3
- package/docs/module-lib_formats.html +0 -3
- package/docs/module-lib_log-levels.html +0 -3
- package/docs/module-lib_resolve-path.html +0 -3
- package/docs/module-lib_shim.html +0 -3
- package/docs/tutorial-contribution.html +0 -3
- package/docs/tutorial-ecosystem.html +0 -3
- package/docs/tutorial-getting-started.html +0 -13
- package/docs/tutorial-plugin-dev.html +0 -3
- package/docs/tutorial-user-guide.html +0 -3
- package/lib/create-method.js +0 -39
- package/lib/dayjs.js +0 -8
- package/lib/omitted-plugin-keys.js +0 -3
- package/lib/read-all-configs.js +0 -19
- package/misc-docs/.hook.md +0 -11
- package/misc-docs/bitcoin.jpeg +0 -0
- package/misc-docs/contribution.md +0 -20
- package/misc-docs/ecosystem.md +0 -94
- package/misc-docs/getting-started.md +0 -142
- package/misc-docs/plugin-dev.md +0 -0
- package/misc-docs/toc.json +0 -17
- package/misc-docs/user-guide.md +0 -1
- /package/docs/{bitcoin.jpeg → static/bitcoin.jpeg} +0 -0
package/class/base.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Plugin from './plugin.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This is the class that your own plugin suppose to extend. Don't use {@link Plugin} directly
|
|
5
|
+
* unless you know what you're doing.
|
|
6
|
+
*
|
|
7
|
+
* @class
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class Base extends Plugin {
|
|
11
|
+
/**
|
|
12
|
+
* Dependencies to other plugins. Enter all plugin's package name your plugin dependent from.
|
|
13
|
+
*
|
|
14
|
+
* Semver is also supported.
|
|
15
|
+
*
|
|
16
|
+
* @constant {string[]}
|
|
17
|
+
* @memberof Base
|
|
18
|
+
*/
|
|
19
|
+
static dependencies = []
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} pkgName - Package name (the one you use in package.json)
|
|
23
|
+
* @param {Object} app - App instance reference. Usefull to call app method inside a plugin
|
|
24
|
+
*/
|
|
25
|
+
constructor (pkgName, app) {
|
|
26
|
+
super(pkgName, app)
|
|
27
|
+
this.state = {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load config from file in data directory, program arguments and environment variables. Level of importance:
|
|
32
|
+
* ```Env Variables > Program Arguments > Config File```
|
|
33
|
+
*
|
|
34
|
+
* @method
|
|
35
|
+
* @async
|
|
36
|
+
*/
|
|
37
|
+
loadConfig = async () => {
|
|
38
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
39
|
+
const { get, kebabCase, keys, pick } = this.app.lib._
|
|
40
|
+
const { log, readJson, parseObject, getModuleDir, readAllConfigs } = this.app.bajo
|
|
41
|
+
const defKeys = keys(this.config)
|
|
42
|
+
log.trace('- %s', this.ns)
|
|
43
|
+
const dir = this.ns === this.app.mainNs ? (`${this.app.bajo.dir.base}/${this.app.mainNs}`) : getModuleDir(this.pkgName)
|
|
44
|
+
let cfg = await readAllConfigs(`${dir}/config`)
|
|
45
|
+
this.constructor.alias = this.alias ?? (this.pkgName.slice(0, 5) === 'bajo-' ? this.pkgName.slice(5).toLowerCase() : this.ns.toLowerCase())
|
|
46
|
+
this.constructor.alias = kebabCase(this.alias)
|
|
47
|
+
|
|
48
|
+
this.dir = {
|
|
49
|
+
pkg: dir,
|
|
50
|
+
data: `${this.app.bajo.dir.data}/plugins/${this.ns}`
|
|
51
|
+
}
|
|
52
|
+
const file = `${dir + (this.ns === this.app.mainNs ? '/..' : '')}/package.json`
|
|
53
|
+
const pkgJson = await readJson(file)
|
|
54
|
+
this.pkg = pick(pkgJson,
|
|
55
|
+
['name', 'version', 'description', 'author', 'license', 'homepage'])
|
|
56
|
+
if (this.ns === this.app.mainNs) {
|
|
57
|
+
this.constructor.alias = this.app.mainNs
|
|
58
|
+
this.title = this.title ?? this.alias
|
|
59
|
+
}
|
|
60
|
+
// merge with config from datadir
|
|
61
|
+
try {
|
|
62
|
+
const altCfg = await readAllConfigs(`${this.app.bajo.dir.data}/config/${this.ns}`)
|
|
63
|
+
cfg = defaultsDeep({}, altCfg, cfg)
|
|
64
|
+
} catch (err) {}
|
|
65
|
+
const cfgEnv = get(this, `app.env.${this.ns}`, {})
|
|
66
|
+
const cfgArgv = get(this, `app.argv.${this.ns}`, {})
|
|
67
|
+
const envArgv = defaultsDeep({}, cfgEnv, cfgArgv)
|
|
68
|
+
cfg = pick(defaultsDeep({}, envArgv ?? {}, cfg ?? {}, this.config ?? {}), defKeys)
|
|
69
|
+
this.title = this.title ?? cfg.title ?? this.alias
|
|
70
|
+
this.config = parseObject(cfg, { parseValue: true })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* After config is read, plugin will be initialized. You can still change your config here,
|
|
75
|
+
* because after plugin is initialized, config will be deep frozen.
|
|
76
|
+
*
|
|
77
|
+
* @method
|
|
78
|
+
* @async
|
|
79
|
+
*/
|
|
80
|
+
init = async () => {
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* This method will be called after plugin's init
|
|
85
|
+
*
|
|
86
|
+
* @method
|
|
87
|
+
* @async
|
|
88
|
+
*/
|
|
89
|
+
start = async () => {
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
stop = async () => {
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Upon app termination, this method will be called first. Mostly useful for system cleanup,
|
|
97
|
+
* delete temporary files, freeing resources etc.
|
|
98
|
+
*
|
|
99
|
+
* @method
|
|
100
|
+
* @async
|
|
101
|
+
*/
|
|
102
|
+
exit = async () => {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default Base
|
package/class/helper/bajo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import readAllConfigs from '../../lib/read-all-configs.js'
|
|
2
1
|
import currentLoc from '../../lib/current-loc.js'
|
|
3
2
|
import resolvePath from '../../lib/resolve-path.js'
|
|
3
|
+
import Print from '../misc/print.js'
|
|
4
|
+
import Log from '../misc/log.js'
|
|
4
5
|
import omitDeep from 'omit-deep'
|
|
5
6
|
import os from 'os'
|
|
6
7
|
import fs from 'fs-extra'
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
attachMethods,
|
|
13
14
|
collectHooks,
|
|
14
15
|
run
|
|
15
|
-
} from './
|
|
16
|
+
} from './base.js'
|
|
16
17
|
|
|
17
18
|
const {
|
|
18
19
|
reduce,
|
|
@@ -27,24 +28,32 @@ const {
|
|
|
27
28
|
keys,
|
|
28
29
|
set,
|
|
29
30
|
get,
|
|
30
|
-
isString,
|
|
31
31
|
filter,
|
|
32
32
|
trim,
|
|
33
33
|
without,
|
|
34
34
|
uniq,
|
|
35
35
|
camelCase,
|
|
36
|
-
isEmpty
|
|
37
|
-
omit
|
|
36
|
+
isEmpty
|
|
38
37
|
} = lodash
|
|
39
38
|
|
|
40
39
|
const omitted = ['spawn', 'cwd', 'name', 'alias', 'applet', 'a', 'plugins']
|
|
41
40
|
|
|
42
41
|
const defConfig = {
|
|
42
|
+
env: 'dev',
|
|
43
43
|
log: {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
timeTaken: false,
|
|
45
|
+
dateFormat: 'YYYY-MM-DDTHH:mm:ss.SSS',
|
|
46
|
+
useUtc: false,
|
|
47
|
+
pretty: false,
|
|
46
48
|
applet: false,
|
|
47
|
-
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
|
+
}
|
|
48
57
|
},
|
|
49
58
|
lang: Intl.DateTimeFormat().resolvedOptions().lang ?? 'en-US',
|
|
50
59
|
intl: {
|
|
@@ -69,83 +78,97 @@ const defConfig = {
|
|
|
69
78
|
exitHandler: true
|
|
70
79
|
}
|
|
71
80
|
|
|
81
|
+
const defMain = `async function factory (pkgName) {
|
|
82
|
+
const me = this
|
|
83
|
+
|
|
84
|
+
return class Main extends this.app.pluginClass.base {
|
|
85
|
+
constructor () {
|
|
86
|
+
super(pkgName, me.app)
|
|
87
|
+
this.config = {}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default factory
|
|
93
|
+
`
|
|
94
|
+
|
|
72
95
|
/**
|
|
73
|
-
*
|
|
96
|
+
* Internal helpers called by Bajo that only used once for bootstrapping. It should remains
|
|
97
|
+
* hidden and not to be imported by any program.
|
|
98
|
+
*
|
|
99
|
+
* @module Helper/Bajo
|
|
74
100
|
*/
|
|
75
101
|
|
|
76
102
|
/**
|
|
77
103
|
* Building bajo base config. Mostly dealing with directory setups:
|
|
78
104
|
* - determine base directory
|
|
79
|
-
* - check whether data directory is valid
|
|
105
|
+
* - check whether data directory is valid. If not exist, create one inside app dir
|
|
80
106
|
* - ensure data config directory is there
|
|
107
|
+
* - ensure tmp dir is there
|
|
108
|
+
* - read the list of plugins from ```.plugins``` file
|
|
81
109
|
*
|
|
82
110
|
* @async
|
|
83
111
|
*/
|
|
84
112
|
export async function buildBaseConfig () {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.config = defaultsDeep({}, this.app.
|
|
88
|
-
this.alias = this.name
|
|
113
|
+
// dirs
|
|
114
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
115
|
+
this.config = defaultsDeep({}, this.app.envVars._, this.app.argv._)
|
|
89
116
|
set(this, 'dir.base', this.app.dir)
|
|
90
117
|
const path = currentLoc(import.meta).dir + '/../..'
|
|
91
118
|
set(this, 'dir.pkg', this.resolvePath(path))
|
|
119
|
+
if (get(this, 'config.dir.data')) set(this, 'dir.data', this.config.dir.data)
|
|
92
120
|
if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
|
|
93
121
|
this.dir.data = this.resolvePath(this.dir.data)
|
|
94
|
-
if (!fs.existsSync(this.dir.data)) {
|
|
95
|
-
console.log('Data directory (%s) doesn\'t exist yet', this.dir.data)
|
|
96
|
-
process.exit(1)
|
|
97
|
-
}
|
|
98
122
|
fs.ensureDirSync(`${this.dir.data}/config`)
|
|
99
123
|
if (!this.dir.tmp) {
|
|
100
|
-
this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.
|
|
124
|
+
this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.ns}`
|
|
101
125
|
fs.ensureDirSync(this.dir.tmp)
|
|
102
126
|
}
|
|
103
|
-
|
|
127
|
+
// 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)))
|
|
132
|
+
}
|
|
133
|
+
this.app.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.app.mainNs), p => {
|
|
134
|
+
return p[0] !== '#'
|
|
135
|
+
}), p => {
|
|
136
|
+
return trim(p.split('#')[0])
|
|
137
|
+
})
|
|
138
|
+
this.app.pluginPkgs.push(this.app.mainNs)
|
|
104
139
|
}
|
|
105
140
|
|
|
106
141
|
/**
|
|
107
142
|
* Building all plugins:
|
|
108
|
-
* -
|
|
143
|
+
* - load from app's pluginPkgs
|
|
109
144
|
* - iterate through the list and build related plugins
|
|
145
|
+
* - making sure main plugin is there. If not, create from template
|
|
110
146
|
* - attach these plugins to the app instance
|
|
111
147
|
*
|
|
112
148
|
* @async
|
|
113
149
|
*/
|
|
114
150
|
export async function buildPlugins () {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const pluginsFile = `${this.dir.data}/config/.plugins`
|
|
118
|
-
if (fs.existsSync(pluginsFile)) {
|
|
119
|
-
pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
|
|
120
|
-
}
|
|
121
|
-
this.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.mainNs), p => {
|
|
122
|
-
return p[0] !== '#'
|
|
123
|
-
}), p => {
|
|
124
|
-
return trim(p.split('#')[0])
|
|
125
|
-
})
|
|
126
|
-
this.pluginPkgs.push(this.mainNs)
|
|
127
|
-
for (const pkg of this.pluginPkgs) {
|
|
151
|
+
this.log.trace('buildPluginsStart')
|
|
152
|
+
for (const pkg of this.app.pluginPkgs) {
|
|
128
153
|
const ns = camelCase(pkg)
|
|
129
154
|
let dir
|
|
130
155
|
if (ns === 'main') {
|
|
131
|
-
dir = `${this.dir.base}/${this.mainNs}`
|
|
156
|
+
dir = `${this.dir.base}/${this.app.mainNs}`
|
|
132
157
|
fs.ensureDirSync(dir)
|
|
133
|
-
fs.
|
|
158
|
+
if (!fs.existsSync(`${dir}/index.js`)) {
|
|
159
|
+
fs.writeFileSync(`${dir}/index.js`, defMain, 'utf8')
|
|
160
|
+
}
|
|
134
161
|
} else dir = this.getModuleDir(pkg)
|
|
135
|
-
let plugin
|
|
136
162
|
const factory = `${dir}/index.js`
|
|
137
|
-
if (fs.existsSync(factory))
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
this.pluginNames.push(plugin.name)
|
|
146
|
-
this.app.addPlugin(plugin)
|
|
163
|
+
if (!fs.existsSync(factory)) throw this.error('pluginPackageNotFound%s', pkg)
|
|
164
|
+
const { default: builder } = await import(resolvePath(factory, true))
|
|
165
|
+
const ClassDef = await builder.call(this, pkg)
|
|
166
|
+
const plugin = new ClassDef()
|
|
167
|
+
if (!(plugin instanceof this.app.pluginClass.base)) throw this.error('pluginPackageInvalid%s', pkg)
|
|
168
|
+
this.app.addPlugin(plugin, ClassDef)
|
|
169
|
+
this.log.trace('- ' + pkg)
|
|
147
170
|
}
|
|
148
|
-
this.
|
|
171
|
+
this.log.debug('buildPluginsComplete')
|
|
149
172
|
}
|
|
150
173
|
|
|
151
174
|
/**
|
|
@@ -154,7 +177,7 @@ export async function buildPlugins () {
|
|
|
154
177
|
* @async
|
|
155
178
|
*/
|
|
156
179
|
export async function collectConfigHandlers () {
|
|
157
|
-
for (const pkg of this.pluginPkgs) {
|
|
180
|
+
for (const pkg of this.app.pluginPkgs) {
|
|
158
181
|
let dir
|
|
159
182
|
try {
|
|
160
183
|
dir = this.getModuleDir(pkg)
|
|
@@ -165,8 +188,9 @@ export async function collectConfigHandlers () {
|
|
|
165
188
|
if (!mod) continue
|
|
166
189
|
if (isFunction(mod)) mod = await mod.call(this.app[camelCase(pkg)])
|
|
167
190
|
if (isPlainObject(mod)) mod = [mod]
|
|
168
|
-
this.configHandlers = this.configHandlers.concat(mod)
|
|
191
|
+
this.app.configHandlers = this.app.configHandlers.concat(mod)
|
|
169
192
|
}
|
|
193
|
+
this.app.log = new Log(this.app)
|
|
170
194
|
}
|
|
171
195
|
|
|
172
196
|
/**
|
|
@@ -179,26 +203,31 @@ export async function collectConfigHandlers () {
|
|
|
179
203
|
*/
|
|
180
204
|
export async function buildExtConfig () {
|
|
181
205
|
// config merging
|
|
182
|
-
const { defaultsDeep } = this.lib.aneka
|
|
183
|
-
let resp = await readAllConfigs
|
|
206
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
207
|
+
let resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
|
|
184
208
|
resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env']), omitted)
|
|
209
|
+
const envs = this.app.constructor.envs
|
|
185
210
|
this.config = defaultsDeep({}, this.config, resp, defConfig)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
211
|
+
// language
|
|
212
|
+
this.config.lang = (this.config.lang ?? '').split('.')[0]
|
|
213
|
+
this.app.loadIntl(this.ns)
|
|
214
|
+
this.print = new Print(this)
|
|
215
|
+
// environment
|
|
216
|
+
if (values(envs).includes(this.config.env)) this.config.env = this.app.lib.aneka.getKeyByValue(envs, this.config.env)
|
|
217
|
+
if (!keys(envs).includes(this.config.env)) throw this.error('unknownEnv%s%s', this.config.env, this.join(keys(envs), { lastSeparator: this.t('or') }))
|
|
218
|
+
process.env.NODE_ENV = envs[this.config.env]
|
|
190
219
|
if (!this.config.log.level) this.config.log.level = this.config.env === 'dev' ? 'debug' : 'info'
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
220
|
+
// misc
|
|
221
|
+
const obj = this.app.applet ? this.config : pick(this.config, keys(defConfig))
|
|
222
|
+
this.config = this.parseObject(obj, { parseValue: true })
|
|
223
|
+
const exts = this.app.getConfigFormats()
|
|
224
|
+
if (this.app.applet) {
|
|
225
|
+
if (!this.app.pluginPkgs.includes('bajo-cli')) throw this.error('appletNeedsBajoCli')
|
|
194
226
|
if (!this.config.log.applet) this.config.log.level = 'silent'
|
|
195
227
|
this.config.exitHandler = false
|
|
196
228
|
}
|
|
197
|
-
|
|
198
|
-
this.initPrint()
|
|
199
|
-
this.initLog()
|
|
229
|
+
this.log.trace('dataDir%s', this.dir.data)
|
|
200
230
|
this.log.debug('configHandlers%s', this.join(exts))
|
|
201
|
-
this.config = this.parseObject(this.config, { parseValue: true })
|
|
202
231
|
}
|
|
203
232
|
|
|
204
233
|
/**
|
|
@@ -208,17 +237,17 @@ export async function buildExtConfig () {
|
|
|
208
237
|
*/
|
|
209
238
|
export async function bootOrder () {
|
|
210
239
|
this.log.debug('setupBootOrder')
|
|
211
|
-
const order = reduce(this.pluginPkgs, (o, k, i) => {
|
|
240
|
+
const order = reduce(this.app.pluginPkgs, (o, k, i) => {
|
|
212
241
|
const key = map(k.split(':'), m => trim(m))
|
|
213
242
|
if (key[1] && !isNaN(Number(key[1]))) o[key[0]] = Number(key[1])
|
|
214
243
|
else o[key[0]] = 10000 + i
|
|
215
244
|
return o
|
|
216
245
|
}, {})
|
|
217
246
|
const norder = {}
|
|
218
|
-
for (let n of this.pluginPkgs) {
|
|
247
|
+
for (let n of this.app.pluginPkgs) {
|
|
219
248
|
n = map(n.split(':'), m => trim(m))[0]
|
|
220
|
-
const dir = n === this.mainNs ? (`${this.dir.base}/${this.mainNs}`) : this.getModuleDir(n)
|
|
221
|
-
if (n !== this.mainNs && !fs.existsSync(dir)) throw this.error('packageNotFoundOrNotBajo%s', n)
|
|
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)
|
|
222
251
|
norder[n] = NaN
|
|
223
252
|
try {
|
|
224
253
|
norder[n] = Number(trim(await fs.readFile(`${dir}/.bootorder`, 'utf8')))
|
|
@@ -229,20 +258,21 @@ export async function bootOrder () {
|
|
|
229
258
|
const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
|
|
230
259
|
result.push(item)
|
|
231
260
|
})
|
|
232
|
-
this.pluginPkgs = map(orderBy(result, ['v']), 'k')
|
|
233
|
-
this.log.
|
|
261
|
+
this.app.pluginPkgs = map(orderBy(result, ['v']), 'k')
|
|
262
|
+
this.log.debug('runInEnv%s', this.t(this.app.constructor.envs[this.config.env]))
|
|
234
263
|
// misc
|
|
235
264
|
this.freeze(this.config)
|
|
236
265
|
}
|
|
237
266
|
|
|
238
267
|
/**
|
|
239
268
|
* Iterate through all plugins loaded and do:
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
269
|
+
*
|
|
270
|
+
* 1. {@link module:Helper/Base.buildConfigs|build configs}
|
|
271
|
+
* 2. {@link module:Helper/Base.checkNameAliases|ensure names & aliases uniqueness}
|
|
272
|
+
* 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}
|
|
246
276
|
*
|
|
247
277
|
* @async
|
|
248
278
|
*/
|
|
@@ -265,10 +295,10 @@ export async function exitHandler () {
|
|
|
265
295
|
|
|
266
296
|
async function exit (signal) {
|
|
267
297
|
const { eachPlugins } = this
|
|
268
|
-
this.log.warn('signalReceived%s', signal)
|
|
269
|
-
await eachPlugins(async function () {
|
|
298
|
+
if (signal) this.log.warn('signalReceived%s', signal)
|
|
299
|
+
await eachPlugins(async function ({ ns }) {
|
|
270
300
|
try {
|
|
271
|
-
await this.
|
|
301
|
+
await this.exit()
|
|
272
302
|
} catch (err) {}
|
|
273
303
|
this.log.trace('exited')
|
|
274
304
|
})
|
|
@@ -284,6 +314,10 @@ export async function exitHandler () {
|
|
|
284
314
|
await exit.call(this, 'SIGTERM')
|
|
285
315
|
})
|
|
286
316
|
|
|
317
|
+
process.on('beforeExit', async () => {
|
|
318
|
+
await exit.call(this)
|
|
319
|
+
})
|
|
320
|
+
|
|
287
321
|
process.on('uncaughtException', (error, origin) => {
|
|
288
322
|
setTimeout(() => {
|
|
289
323
|
console.error(error)
|
|
@@ -316,29 +350,51 @@ export async function exitHandler () {
|
|
|
316
350
|
* If app is in ```applet``` mode, this little helper should take care plugin's applet boot process
|
|
317
351
|
*
|
|
318
352
|
* @async
|
|
353
|
+
* @fires {ns}:beforeAppletRun
|
|
354
|
+
* @fires {ns}:afterAppletRun
|
|
319
355
|
*/
|
|
320
356
|
export async function runAsApplet () {
|
|
321
|
-
const { isString, map, find } = this.lib._
|
|
357
|
+
const { isString, map, find } = this.app.lib._
|
|
322
358
|
await this.eachPlugins(async function ({ file }) {
|
|
323
|
-
const {
|
|
324
|
-
|
|
359
|
+
const { ns } = this
|
|
360
|
+
const { alias } = this.constructor
|
|
361
|
+
this.app.applets.push({ ns, file, alias })
|
|
325
362
|
}, { glob: 'applet.js', prefix: 'bajoCli' })
|
|
326
363
|
|
|
327
364
|
this.log.debug('appletModeActivated')
|
|
328
365
|
this.print.info('appRunningAsApplet')
|
|
329
|
-
if (this.applets.length === 0) this.print.fatal('noAppletLoaded')
|
|
330
|
-
let name = this.applet
|
|
331
|
-
if (!isString(
|
|
366
|
+
if (this.app.applets.length === 0) this.print.fatal('noAppletLoaded')
|
|
367
|
+
let name = this.app.applet
|
|
368
|
+
if (!isString(name)) {
|
|
332
369
|
const select = await this.importPkg('bajoCli:@inquirer/select')
|
|
333
370
|
name = await select({
|
|
334
|
-
message: this.
|
|
335
|
-
choices: map(this.applets, t => ({ value: t.ns }))
|
|
371
|
+
message: this.t('Please select:'),
|
|
372
|
+
choices: map(this.app.applets, t => ({ value: t.ns }))
|
|
336
373
|
})
|
|
337
374
|
}
|
|
338
375
|
const [ns, path] = name.split(':')
|
|
339
|
-
const applet = find(this.applets, a => (a.ns === ns || a.alias === ns))
|
|
340
|
-
if (!applet) this.print.fatal('notFound%s%s', this.
|
|
341
|
-
|
|
376
|
+
const applet = find(this.app.applets, a => (a.ns === ns || a.alias === ns))
|
|
377
|
+
if (!applet) this.print.fatal('notFound%s%s', this.app.t('applet'), name)
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Run before applet is run. ```[ns]``` is applet's namespace
|
|
381
|
+
*
|
|
382
|
+
* @global
|
|
383
|
+
* @event {ns}:beforeAppletRun
|
|
384
|
+
* @param {...any} params
|
|
385
|
+
* @see {@tutorial hook}
|
|
386
|
+
* @see module:Helper/Bajo.runAsApplet
|
|
387
|
+
*/
|
|
388
|
+
await this.runHook(`${this.app[applet.ns]}:beforeAppletRun`, ...this.app.args)
|
|
342
389
|
await this.app.bajoCli.runApplet(applet, path, ...this.app.args)
|
|
343
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Run after applet is run. ```[ns]``` is applet's namespace
|
|
392
|
+
*
|
|
393
|
+
* @global
|
|
394
|
+
* @event {ns}:afterAppletRun
|
|
395
|
+
* @param {...any} params
|
|
396
|
+
* @see {@tutorial hook}
|
|
397
|
+
* @see module:Helper/Bajo.runAsApplet
|
|
398
|
+
*/
|
|
399
|
+
await this.runHook(`${this.app[applet.ns]}:afterAppletRun`, ...this.app.args)
|
|
344
400
|
}
|