bajo 2.0.1 → 2.0.2
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 +376 -54
- package/class/bajo.js +228 -149
- package/class/base.js +106 -0
- package/class/helper/bajo.js +135 -84
- package/class/helper/{plugin.js → base.js} +96 -22
- package/class/{base → misc}/err.js +44 -14
- package/class/misc/log.js +212 -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 +8 -2
- package/extend/bajo/intl/id.json +8 -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 +30 -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,22 +28,23 @@ 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
|
+
timeTaken: false,
|
|
44
45
|
dateFormat: 'YYYY-MM-DDTHH:MM:ss.SSS[Z]',
|
|
45
|
-
|
|
46
|
+
localDate: false,
|
|
47
|
+
pretty: false,
|
|
46
48
|
applet: false,
|
|
47
49
|
traceHook: false
|
|
48
50
|
},
|
|
@@ -69,23 +71,41 @@ const defConfig = {
|
|
|
69
71
|
exitHandler: true
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
const defMain = `async function factory (pkgName) {
|
|
75
|
+
const me = this
|
|
76
|
+
|
|
77
|
+
return class Main extends this.app.pluginClass.base {
|
|
78
|
+
constructor () {
|
|
79
|
+
super(pkgName, me.app)
|
|
80
|
+
this.config = {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default factory
|
|
86
|
+
`
|
|
87
|
+
|
|
72
88
|
/**
|
|
73
|
-
*
|
|
89
|
+
* Internal helpers called by Bajo that only used once for bootstrapping. It should remains
|
|
90
|
+
* hidden and not to be imported by any program.
|
|
91
|
+
*
|
|
92
|
+
* @module Helper/Bajo
|
|
74
93
|
*/
|
|
75
94
|
|
|
76
95
|
/**
|
|
77
96
|
* Building bajo base config. Mostly dealing with directory setups:
|
|
78
97
|
* - determine base directory
|
|
79
|
-
* - check whether data directory is valid
|
|
98
|
+
* - check whether data directory is valid. If not exist, create one inside app dir
|
|
80
99
|
* - ensure data config directory is there
|
|
100
|
+
* - ensure tmp dir is there
|
|
101
|
+
* - read the list of plugins from ```.plugins``` file
|
|
81
102
|
*
|
|
82
103
|
* @async
|
|
83
104
|
*/
|
|
84
105
|
export async function buildBaseConfig () {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.config = defaultsDeep({}, this.app.
|
|
88
|
-
this.alias = this.name
|
|
106
|
+
// dirs
|
|
107
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
108
|
+
this.config = defaultsDeep({}, this.app.envVars._, this.app.argv._)
|
|
89
109
|
set(this, 'dir.base', this.app.dir)
|
|
90
110
|
const path = currentLoc(import.meta).dir + '/../..'
|
|
91
111
|
set(this, 'dir.pkg', this.resolvePath(path))
|
|
@@ -97,55 +117,54 @@ export async function buildBaseConfig () {
|
|
|
97
117
|
}
|
|
98
118
|
fs.ensureDirSync(`${this.dir.data}/config`)
|
|
99
119
|
if (!this.dir.tmp) {
|
|
100
|
-
this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.
|
|
120
|
+
this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.ns}`
|
|
101
121
|
fs.ensureDirSync(this.dir.tmp)
|
|
102
122
|
}
|
|
103
|
-
|
|
123
|
+
// collect list of plugins
|
|
124
|
+
let pluginPkgs = []
|
|
125
|
+
const pluginsFile = `${this.dir.data}/config/.plugins`
|
|
126
|
+
if (fs.existsSync(pluginsFile)) {
|
|
127
|
+
pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
|
|
128
|
+
}
|
|
129
|
+
this.app.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.app.mainNs), p => {
|
|
130
|
+
return p[0] !== '#'
|
|
131
|
+
}), p => {
|
|
132
|
+
return trim(p.split('#')[0])
|
|
133
|
+
})
|
|
134
|
+
this.app.pluginPkgs.push(this.app.mainNs)
|
|
104
135
|
}
|
|
105
136
|
|
|
106
137
|
/**
|
|
107
138
|
* Building all plugins:
|
|
108
|
-
* -
|
|
139
|
+
* - load from app's pluginPkgs
|
|
109
140
|
* - iterate through the list and build related plugins
|
|
141
|
+
* - making sure main plugin is there. If not, create from template
|
|
110
142
|
* - attach these plugins to the app instance
|
|
111
143
|
*
|
|
112
144
|
* @async
|
|
113
145
|
*/
|
|
114
146
|
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) {
|
|
147
|
+
this.log.trace('buildPluginsStart')
|
|
148
|
+
for (const pkg of this.app.pluginPkgs) {
|
|
128
149
|
const ns = camelCase(pkg)
|
|
129
150
|
let dir
|
|
130
151
|
if (ns === 'main') {
|
|
131
|
-
dir = `${this.dir.base}/${this.mainNs}`
|
|
152
|
+
dir = `${this.dir.base}/${this.app.mainNs}`
|
|
132
153
|
fs.ensureDirSync(dir)
|
|
133
|
-
fs.
|
|
154
|
+
if (!fs.existsSync(`${dir}/index.js`)) {
|
|
155
|
+
fs.writeFileSync(`${dir}/index.js`, defMain, 'utf8')
|
|
156
|
+
}
|
|
134
157
|
} else dir = this.getModuleDir(pkg)
|
|
135
|
-
let plugin
|
|
136
158
|
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)
|
|
159
|
+
if (!fs.existsSync(factory)) throw this.error('pluginPackageNotFound%s', pkg)
|
|
160
|
+
const { default: builder } = await import(resolvePath(factory, true))
|
|
161
|
+
const ClassDef = await builder.call(this, pkg)
|
|
162
|
+
const plugin = new ClassDef()
|
|
163
|
+
if (!(plugin instanceof this.app.pluginClass.base)) throw this.error('pluginPackageInvalid%s', pkg)
|
|
164
|
+
this.app.addPlugin(plugin, ClassDef)
|
|
165
|
+
this.log.trace('- ' + pkg)
|
|
147
166
|
}
|
|
148
|
-
this.
|
|
167
|
+
this.log.debug('buildPluginsComplete')
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
/**
|
|
@@ -154,7 +173,7 @@ export async function buildPlugins () {
|
|
|
154
173
|
* @async
|
|
155
174
|
*/
|
|
156
175
|
export async function collectConfigHandlers () {
|
|
157
|
-
for (const pkg of this.pluginPkgs) {
|
|
176
|
+
for (const pkg of this.app.pluginPkgs) {
|
|
158
177
|
let dir
|
|
159
178
|
try {
|
|
160
179
|
dir = this.getModuleDir(pkg)
|
|
@@ -165,8 +184,9 @@ export async function collectConfigHandlers () {
|
|
|
165
184
|
if (!mod) continue
|
|
166
185
|
if (isFunction(mod)) mod = await mod.call(this.app[camelCase(pkg)])
|
|
167
186
|
if (isPlainObject(mod)) mod = [mod]
|
|
168
|
-
this.configHandlers = this.configHandlers.concat(mod)
|
|
187
|
+
this.app.configHandlers = this.app.configHandlers.concat(mod)
|
|
169
188
|
}
|
|
189
|
+
this.app.log = new Log(this.app)
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
/**
|
|
@@ -179,26 +199,30 @@ export async function collectConfigHandlers () {
|
|
|
179
199
|
*/
|
|
180
200
|
export async function buildExtConfig () {
|
|
181
201
|
// config merging
|
|
182
|
-
const { defaultsDeep } = this.lib.aneka
|
|
183
|
-
let resp = await readAllConfigs
|
|
202
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
203
|
+
let resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
|
|
184
204
|
resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env']), omitted)
|
|
205
|
+
const envs = this.app.constructor.envs
|
|
185
206
|
this.config = defaultsDeep({}, this.config, resp, defConfig)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
207
|
+
// language
|
|
208
|
+
this.config.lang = (this.config.lang ?? '').split('.')[0]
|
|
209
|
+
this.app.loadIntl(this.ns)
|
|
210
|
+
this.print = new Print(this)
|
|
211
|
+
// environment
|
|
212
|
+
if (values(envs).includes(this.config.env)) this.config.env = this.app.lib.aneka.getKeyByValue(envs, this.config.env)
|
|
213
|
+
if (!keys(envs).includes(this.config.env)) throw this.error('unknownEnv%s%s', this.config.env, this.join(keys(envs), { lastSeparator: this.t('or') }))
|
|
214
|
+
process.env.NODE_ENV = envs[this.config.env]
|
|
190
215
|
if (!this.config.log.level) this.config.log.level = this.config.env === 'dev' ? 'debug' : 'info'
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
// misc
|
|
217
|
+
const obj = this.app.applet ? this.config : pick(this.config, keys(defConfig))
|
|
218
|
+
this.config = this.parseObject(obj, { parseValue: true })
|
|
219
|
+
const exts = this.app.getConfigFormats()
|
|
220
|
+
if (this.app.applet) {
|
|
221
|
+
if (!this.app.pluginPkgs.includes('bajo-cli')) throw this.error('appletNeedsBajoCli')
|
|
194
222
|
if (!this.config.log.applet) this.config.log.level = 'silent'
|
|
195
223
|
this.config.exitHandler = false
|
|
196
224
|
}
|
|
197
|
-
const exts = map(this.configHandlers, 'ext')
|
|
198
|
-
this.initPrint()
|
|
199
|
-
this.initLog()
|
|
200
225
|
this.log.debug('configHandlers%s', this.join(exts))
|
|
201
|
-
this.config = this.parseObject(this.config, { parseValue: true })
|
|
202
226
|
}
|
|
203
227
|
|
|
204
228
|
/**
|
|
@@ -208,17 +232,17 @@ export async function buildExtConfig () {
|
|
|
208
232
|
*/
|
|
209
233
|
export async function bootOrder () {
|
|
210
234
|
this.log.debug('setupBootOrder')
|
|
211
|
-
const order = reduce(this.pluginPkgs, (o, k, i) => {
|
|
235
|
+
const order = reduce(this.app.pluginPkgs, (o, k, i) => {
|
|
212
236
|
const key = map(k.split(':'), m => trim(m))
|
|
213
237
|
if (key[1] && !isNaN(Number(key[1]))) o[key[0]] = Number(key[1])
|
|
214
238
|
else o[key[0]] = 10000 + i
|
|
215
239
|
return o
|
|
216
240
|
}, {})
|
|
217
241
|
const norder = {}
|
|
218
|
-
for (let n of this.pluginPkgs) {
|
|
242
|
+
for (let n of this.app.pluginPkgs) {
|
|
219
243
|
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)
|
|
244
|
+
const dir = n === this.app.mainNs ? (`${this.dir.base}/${this.app.mainNs}`) : this.getModuleDir(n)
|
|
245
|
+
if (n !== this.app.mainNs && !fs.existsSync(dir)) throw this.error('packageNotFoundOrNotBajo%s', n)
|
|
222
246
|
norder[n] = NaN
|
|
223
247
|
try {
|
|
224
248
|
norder[n] = Number(trim(await fs.readFile(`${dir}/.bootorder`, 'utf8')))
|
|
@@ -229,20 +253,21 @@ export async function bootOrder () {
|
|
|
229
253
|
const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
|
|
230
254
|
result.push(item)
|
|
231
255
|
})
|
|
232
|
-
this.pluginPkgs = map(orderBy(result, ['v']), 'k')
|
|
233
|
-
this.log.
|
|
256
|
+
this.app.pluginPkgs = map(orderBy(result, ['v']), 'k')
|
|
257
|
+
this.log.debug('runInEnv%s', this.t(this.app.constructor.envs[this.config.env]))
|
|
234
258
|
// misc
|
|
235
259
|
this.freeze(this.config)
|
|
236
260
|
}
|
|
237
261
|
|
|
238
262
|
/**
|
|
239
263
|
* Iterate through all plugins loaded and do:
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
264
|
+
*
|
|
265
|
+
* 1. {@link module:Helper/Base.buildConfigs|build configs}
|
|
266
|
+
* 2. {@link module:Helper/Base.checkNameAliases|ensure names & aliases uniqueness}
|
|
267
|
+
* 3. {@link module:Helper/Base.checkDependencies|ensure dependencies are met}
|
|
268
|
+
* 4. {@link module:Helper/Base.attachMethods|build and attach dynamic methods}
|
|
269
|
+
* 5. {@link module:Helper/Base.collectHooks|collect hooks}
|
|
270
|
+
* 6. {@link module:Helper/Base.run|run plugins}
|
|
246
271
|
*
|
|
247
272
|
* @async
|
|
248
273
|
*/
|
|
@@ -265,10 +290,10 @@ export async function exitHandler () {
|
|
|
265
290
|
|
|
266
291
|
async function exit (signal) {
|
|
267
292
|
const { eachPlugins } = this
|
|
268
|
-
this.log.warn('signalReceived%s', signal)
|
|
269
|
-
await eachPlugins(async function () {
|
|
293
|
+
if (signal) this.log.warn('signalReceived%s', signal)
|
|
294
|
+
await eachPlugins(async function ({ ns }) {
|
|
270
295
|
try {
|
|
271
|
-
await this.
|
|
296
|
+
await this.exit()
|
|
272
297
|
} catch (err) {}
|
|
273
298
|
this.log.trace('exited')
|
|
274
299
|
})
|
|
@@ -284,6 +309,10 @@ export async function exitHandler () {
|
|
|
284
309
|
await exit.call(this, 'SIGTERM')
|
|
285
310
|
})
|
|
286
311
|
|
|
312
|
+
process.on('beforeExit', async () => {
|
|
313
|
+
await exit.call(this)
|
|
314
|
+
})
|
|
315
|
+
|
|
287
316
|
process.on('uncaughtException', (error, origin) => {
|
|
288
317
|
setTimeout(() => {
|
|
289
318
|
console.error(error)
|
|
@@ -316,29 +345,51 @@ export async function exitHandler () {
|
|
|
316
345
|
* If app is in ```applet``` mode, this little helper should take care plugin's applet boot process
|
|
317
346
|
*
|
|
318
347
|
* @async
|
|
348
|
+
* @fires {ns}:beforeAppletRun
|
|
349
|
+
* @fires {ns}:afterAppletRun
|
|
319
350
|
*/
|
|
320
351
|
export async function runAsApplet () {
|
|
321
|
-
const { isString, map, find } = this.lib._
|
|
352
|
+
const { isString, map, find } = this.app.lib._
|
|
322
353
|
await this.eachPlugins(async function ({ file }) {
|
|
323
|
-
const {
|
|
324
|
-
|
|
354
|
+
const { ns } = this
|
|
355
|
+
const { alias } = this.constructor
|
|
356
|
+
this.app.applets.push({ ns, file, alias })
|
|
325
357
|
}, { glob: 'applet.js', prefix: 'bajoCli' })
|
|
326
358
|
|
|
327
359
|
this.log.debug('appletModeActivated')
|
|
328
360
|
this.print.info('appRunningAsApplet')
|
|
329
|
-
if (this.applets.length === 0) this.print.fatal('noAppletLoaded')
|
|
330
|
-
let name = this.applet
|
|
331
|
-
if (!isString(
|
|
361
|
+
if (this.app.applets.length === 0) this.print.fatal('noAppletLoaded')
|
|
362
|
+
let name = this.app.applet
|
|
363
|
+
if (!isString(name)) {
|
|
332
364
|
const select = await this.importPkg('bajoCli:@inquirer/select')
|
|
333
365
|
name = await select({
|
|
334
|
-
message: this.
|
|
335
|
-
choices: map(this.applets, t => ({ value: t.ns }))
|
|
366
|
+
message: this.t('Please select:'),
|
|
367
|
+
choices: map(this.app.applets, t => ({ value: t.ns }))
|
|
336
368
|
})
|
|
337
369
|
}
|
|
338
370
|
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
|
-
|
|
371
|
+
const applet = find(this.app.applets, a => (a.ns === ns || a.alias === ns))
|
|
372
|
+
if (!applet) this.print.fatal('notFound%s%s', this.app.t('applet'), name)
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Run before applet is run. ```[ns]``` is applet's namespace
|
|
376
|
+
*
|
|
377
|
+
* @global
|
|
378
|
+
* @event {ns}:beforeAppletRun
|
|
379
|
+
* @param {...any} params
|
|
380
|
+
* @see {@tutorial hook}
|
|
381
|
+
* @see module:Helper/Bajo.runAsApplet
|
|
382
|
+
*/
|
|
383
|
+
await this.runHook(`${this.app[applet.ns]}:beforeAppletRun`, ...this.app.args)
|
|
342
384
|
await this.app.bajoCli.runApplet(applet, path, ...this.app.args)
|
|
343
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Run after applet is run. ```[ns]``` is applet's namespace
|
|
387
|
+
*
|
|
388
|
+
* @global
|
|
389
|
+
* @event {ns}:afterAppletRun
|
|
390
|
+
* @param {...any} params
|
|
391
|
+
* @see {@tutorial hook}
|
|
392
|
+
* @see module:Helper/Bajo.runAsApplet
|
|
393
|
+
*/
|
|
394
|
+
await this.runHook(`${this.app[applet.ns]}:afterAppletRun`, ...this.app.args)
|
|
344
395
|
}
|