bajo 2.22.0 → 2.24.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/.jsdoc.conf.json +2 -1
- package/class/app.js +220 -107
- package/class/bajo.js +258 -177
- package/class/base.js +85 -24
- package/class/cache.js +8 -16
- package/class/err.js +31 -10
- package/class/log.js +46 -38
- package/class/plugin.js +53 -30
- package/class/print.js +18 -15
- package/class/tools.js +6 -5
- package/docs/App.html +51 -1
- package/docs/Bajo.html +1 -1
- package/docs/Base.html +34 -1
- package/docs/Cache.html +1 -1
- package/docs/Err.html +2 -2
- package/docs/Log.html +2 -2
- package/docs/Main.html +3 -0
- package/docs/Plugin.html +1 -1
- package/docs/Print.html +1 -1
- package/docs/Tools.html +1 -1
- package/docs/class_app.js.html +222 -109
- package/docs/class_bajo.js.html +260 -179
- package/docs/class_base.js.html +87 -26
- package/docs/class_cache.js.html +10 -18
- package/docs/class_err.js.html +33 -12
- package/docs/class_log.js.html +48 -40
- package/docs/class_plugin.js.html +55 -32
- package/docs/class_print.js.html +20 -17
- package/docs/class_tools.js.html +8 -7
- package/docs/data/search.json +1 -1
- package/docs/extend_bajo_hook.js.html +289 -0
- package/docs/global.html +22 -1
- package/docs/index.html +1 -1
- package/docs/index.js.html +53 -16
- package/docs/lib_applet-hook.js.html +328 -0
- package/docs/lib_config.js.html +167 -0
- package/docs/lib_helper.js.html +186 -192
- package/docs/module-Applet.html +3 -0
- package/docs/module-Helper-Main.html +3 -0
- package/docs/module-Helper.html +1 -8
- package/docs/module-Hook.html +1 -1
- package/docs/tutorial-01_WELCOME.html +3 -0
- package/docs/tutorial-02_GETTING_STARTED.html +125 -0
- package/docs/tutorial-03_USER_GUIDE.html +54 -0
- package/docs/tutorial-04_DEV_GUIDE.html +178 -0
- package/docs/tutorial-05_ECOSYSTEM.html +3 -0
- package/extend/bajo/intl/en-US.json +2 -1
- package/extend/bajo/intl/id.json +2 -1
- package/index.js +51 -14
- package/lib/applet-hook.js +325 -0
- package/lib/config.js +164 -0
- package/lib/helper.js +184 -190
- package/package.json +6 -2
- package/test/00-global-hooks.test.js +44 -0
- package/test/e2e/_run.js +19 -0
- package/test/e2e/e2e-applet-process.test.js +66 -0
- package/test/e2e/e2e-boot-process.test.js +62 -0
- package/test/e2e/e2e-hooks-process.test.js +68 -0
- package/test/integration/_setup.js +55 -0
- package/test/integration/aspect-01-boot-instance.test.js +24 -0
- package/test/integration/aspect-02-plugin-loading.test.js +24 -0
- package/test/integration/aspect-03-config-merge.test.js +27 -0
- package/test/integration/aspect-04-intl-loading.test.js +29 -0
- package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
- package/test/integration/aspect-06-log-file-save.test.js +28 -0
- package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
- package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
- package/test/integration/aspect-09-config-formats.test.js +26 -0
- package/test/integration/aspect-10-hooks.test.js +38 -0
- package/test/unit/app.test.js +115 -0
- package/test/unit/applet-hook.test.js +10 -0
- package/test/unit/bajo.test.js +151 -0
- package/test/unit/base.test.js +90 -0
- package/test/unit/cache.test.js +89 -0
- package/test/unit/config.test.js +14 -0
- package/test/unit/err.test.js +57 -0
- package/test/unit/helper.test.js +116 -0
- package/test/unit/index.test.js +40 -0
- package/test/unit/log.test.js +84 -0
- package/test/unit/plugin.test.js +95 -0
- package/test/unit/print.test.js +72 -0
- package/test/unit/tools.test.js +29 -0
- package/wiki/CHANGES.md +9 -0
- package/wiki/tutorials/01_WELCOME.md +18 -0
- package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
- package/wiki/tutorials/03_USER_GUIDE.md +262 -0
- package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
- package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
- package/wiki/tutorials/tutorials.json +17 -0
- package/lib/hook.js +0 -228
- package/test/app.test.js +0 -183
- package/test/bajo.test.js +0 -124
- package/test/base.test.js +0 -75
- package/test/cache.test.js +0 -94
- package/test/e2e.test.js +0 -137
- package/test/err.test.js +0 -73
- package/test/helper.test.js +0 -39
- package/test/import-module.test.js +0 -138
- package/test/integration.test.js +0 -218
- package/test/log.test.js +0 -119
- package/test/plugin.test.js +0 -116
- package/test/print.test.js +0 -100
- package/test/tools.test.js +0 -38
- package/wiki/CONFIG.md +0 -36
- package/wiki/DEV_GUIDE.md +0 -3
- package/wiki/ECOSYSTEM.md +0 -93
- package/wiki/GETTING-STARTED.md +0 -356
- package/wiki/USER-GUIDE.md +0 -256
package/.jsdoc.conf.json
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
"verbose": true,
|
|
7
7
|
"destination": "./docs",
|
|
8
8
|
"template": "node_modules/clean-jsdoc-theme",
|
|
9
|
+
"tutorials": "./wiki/tutorials",
|
|
9
10
|
"readme": "./docs/static/home.md",
|
|
10
11
|
"theme_opts": {
|
|
11
12
|
"default_theme": "light",
|
|
12
13
|
"display-module-header": true,
|
|
13
14
|
"title": "Bajo API",
|
|
14
15
|
"homepageTitle": "Bajo API",
|
|
15
|
-
"sections": ["Classes", "Events", "Modules", "Externals", "Global"],
|
|
16
|
+
"sections": ["Tutorials", "Classes", "Events", "Modules", "Externals", "Global"],
|
|
16
17
|
"menu": [{
|
|
17
18
|
"title": "NPM",
|
|
18
19
|
"link": "https://www.npmjs.com/package/bajo"
|
package/class/app.js
CHANGED
|
@@ -31,100 +31,186 @@ function getCallerFilename () {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
+
* Supported environments.
|
|
35
|
+
*
|
|
36
|
+
* Environment is one of the most important aspect of a Bajo app. It is used to determine how your app should behave
|
|
37
|
+
* in different environments. It allows you to have different configurations and settings,
|
|
38
|
+
* and as a plugin developer, you can also use it to determine how your plugin should behave in different environments.
|
|
39
|
+
*
|
|
34
40
|
* @typedef {Object} TEnv
|
|
35
41
|
* @memberof App
|
|
36
42
|
* @property {string} dev=development
|
|
43
|
+
* @property {string} stag=staging
|
|
37
44
|
* @property {string} prod=production
|
|
45
|
+
* @property {string} test=testing
|
|
38
46
|
*/
|
|
39
47
|
|
|
40
48
|
/**
|
|
49
|
+
* Your read handler must follow this structure. It is used to read and parse config file in a particular format.
|
|
50
|
+
*
|
|
41
51
|
* @callback readHandler
|
|
42
52
|
* @memberof App
|
|
43
|
-
* @
|
|
44
|
-
* @param {
|
|
53
|
+
* @async
|
|
54
|
+
* @param {string} text - Text to be parsed or file path to be read if `options.readFromFile` is `true`
|
|
55
|
+
* @param {object|boolean} [options={}] - Options object. If a boolean is provided, it will be treated as `options.readFromFile`
|
|
56
|
+
* @param {boolean} [options.readFromFile=false] - If `true`, `text` is treated as a file path
|
|
57
|
+
* @param {boolean} [options.throwNotFound=false] - If `true`, throw exception if file is not found
|
|
58
|
+
* @param {object} [options.parserOpts={}] - Options to be passed to the parser
|
|
45
59
|
* @returns {object} Parsed object
|
|
46
60
|
*/
|
|
47
61
|
|
|
48
62
|
/**
|
|
63
|
+
* Your write handler must follow this structure. It is used to write config file in a particular format.
|
|
64
|
+
*
|
|
49
65
|
* @callback writeHandler
|
|
66
|
+
* @async
|
|
50
67
|
* @memberof App
|
|
51
68
|
* @param {Object} data - Data to be stringified
|
|
52
|
-
* @param {Object} [options={}] - Options
|
|
53
|
-
* @param {
|
|
54
|
-
* @param {
|
|
69
|
+
* @param {Object|string} [options={}] - Options object. If a string is provided, it will be treated as `options.writeToFile`
|
|
70
|
+
* @param {string} [options.writeToFile] - If not empty, write result to this file path instead of returning it as string
|
|
71
|
+
* @param {object} [options.parserOpts={}] - Options to be passed to the parser
|
|
55
72
|
* @returns {string} Stringified result
|
|
56
73
|
*/
|
|
57
74
|
|
|
58
75
|
/**
|
|
76
|
+
* Config handler definition. Your own handler must follow this structure.
|
|
77
|
+
*
|
|
59
78
|
* @typedef {Object} TConfigHandler
|
|
60
79
|
* @memberof App
|
|
61
|
-
* @property {string} ns -
|
|
62
|
-
* @property {string} ext -
|
|
63
|
-
* @property {App.readHandler} [readHandler] -
|
|
64
|
-
* @property {App.writeHandler} [writeHandler] -
|
|
80
|
+
* @property {string} ns - Owner plugin namespace
|
|
81
|
+
* @property {string} ext - Supported file extension
|
|
82
|
+
* @property {App.readHandler} [readHandler] - Async function to call for reading
|
|
83
|
+
* @property {App.writeHandler} [writeHandler] - Async function to call for writing
|
|
84
|
+
* @see {@link App#configHandlers}
|
|
65
85
|
*/
|
|
66
86
|
|
|
67
87
|
/**
|
|
88
|
+
* Options object passed to {@link App} constructor. By default, you don't need to pass any options,
|
|
89
|
+
* unless you want to manually override the default behavior.
|
|
68
90
|
* @typedef {Object} TOptions
|
|
69
91
|
* @memberof App
|
|
70
92
|
* @property {string} [cwd] - Set current working directory. Defaults to the script directory
|
|
71
|
-
* @property {string[]} [plugins] - Array of plugins to load. If provided, it override the list in `package.json` and `.plugins` file
|
|
72
|
-
* @property {Object} [config] - Plugin
|
|
93
|
+
* @property {string[]} [plugins] - Array of plugins **package names** to load. If provided, it override the list in `package.json` and `.plugins` file
|
|
94
|
+
* @property {Object} [config] - Plugin config objects, with plugin name as keys and their respective config objects as values. If provided, plugin configs will no longer be read from its config files
|
|
95
|
+
* @property {module:Hook.THook[]} [hooks] - Array of hooks to be added to the app
|
|
96
|
+
* @example
|
|
97
|
+
* // If you feel adventurous and decide to manually boot your app without the default boot module, here we go,,,
|
|
98
|
+
* import App from 'bajo/class/app.js'
|
|
99
|
+
*
|
|
100
|
+
* const options = {
|
|
101
|
+
* plugins: ['my-plugin', 'my-other-plugin'],
|
|
102
|
+
* config: {
|
|
103
|
+
* myPlugin: {
|
|
104
|
+
* // plugin config here
|
|
105
|
+
* },
|
|
106
|
+
* myOtherPlugin: {
|
|
107
|
+
* // plugin config here
|
|
108
|
+
* }
|
|
109
|
+
* },
|
|
110
|
+
* hooks: [{
|
|
111
|
+
* name: 'myPlugin:myHook',
|
|
112
|
+
* handler: async function (arg1, arg2) {
|
|
113
|
+
* // do something with arg1 and arg2
|
|
114
|
+
* }
|
|
115
|
+
* }]
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* const app = new App(options)
|
|
73
120
|
*/
|
|
74
121
|
|
|
75
122
|
/**
|
|
76
123
|
* App class. This is the root. This is where all plugins call it home.
|
|
77
124
|
*
|
|
125
|
+
* This class should not be instantiated directly (see example in {@link App.TOptions} if you
|
|
126
|
+
* feel adventurous). Instead, use the default {@link boot|boot} script.
|
|
127
|
+
*
|
|
128
|
+
* Plugins (including special plugins {@link Main} and {@link Bajo}) are loaded and attached as
|
|
129
|
+
* property members of this class. They can be accessed using their name/namespace.
|
|
130
|
+
*
|
|
131
|
+
* In every plugin there is a reference to this class instance, so they can call each other easily,
|
|
132
|
+
* e.g. in your plugin method, you would call other plugin's method like this `this.app.otherPlugin.method()`.
|
|
133
|
+
*
|
|
134
|
+
* A typical Bajo app should follow the following structure:
|
|
135
|
+
* ```
|
|
136
|
+
* my-app
|
|
137
|
+
* |-- package.json
|
|
138
|
+
* |-- index.js
|
|
139
|
+
* |-- main
|
|
140
|
+
* | |-- extend
|
|
141
|
+
* | | |-- bajo
|
|
142
|
+
* | | | |-- hook
|
|
143
|
+
* | | | | | |-- myPlugin:beforeAction.js
|
|
144
|
+
* | | | | | |-- myOtherPlugin:afterAction.js
|
|
145
|
+
* | | | |-- intl
|
|
146
|
+
* | |-- index.js
|
|
147
|
+
* |-- data
|
|
148
|
+
* | |-- config
|
|
149
|
+
* | | |-- .plugins
|
|
150
|
+
* | | |-- bajo.yml
|
|
151
|
+
* | | |-- myPlugin.yml
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
78
154
|
* @class
|
|
79
155
|
*/
|
|
80
156
|
class App {
|
|
81
157
|
/**
|
|
82
|
-
* Constructor.
|
|
158
|
+
* Constructor. See {@link App.TOptions} if you want to write custom boot process for your app without using the default boot module.
|
|
83
159
|
* @param {App.TOptions} [options={}] - Options object.
|
|
84
160
|
*/
|
|
85
161
|
constructor (options = {}) {
|
|
86
162
|
/**
|
|
87
|
-
* Copy of provided options.
|
|
163
|
+
* Copy of the provided options. See {@link App.TOptions} for details.
|
|
88
164
|
* @type {App.TOptions}
|
|
89
165
|
*/
|
|
90
166
|
this.options = options
|
|
91
167
|
|
|
92
168
|
/**
|
|
93
|
-
*
|
|
169
|
+
* App's main plugin namespace (`main`). Read-only
|
|
94
170
|
* @type {string}
|
|
95
171
|
*/
|
|
96
172
|
this.mainNs = 'main'
|
|
97
173
|
|
|
98
174
|
/**
|
|
99
|
-
*
|
|
175
|
+
* Supported environments. Read-only
|
|
100
176
|
* @type {App.TEnv}
|
|
101
177
|
*/
|
|
102
|
-
this.envs = { dev: 'development', prod: 'production' }
|
|
178
|
+
this.envs = { dev: 'development', prod: 'production', stag: 'staging', test: 'testing' }
|
|
103
179
|
|
|
104
180
|
/**
|
|
105
|
-
* Date/time when your app start.
|
|
181
|
+
* Date/time when your app start. You can use this e.g. to calculate how long your app has been running. Read-only
|
|
106
182
|
* @type {Date}
|
|
107
183
|
*/
|
|
108
184
|
this.runAt = new Date()
|
|
109
185
|
|
|
110
186
|
/**
|
|
111
|
-
* Applets container.
|
|
187
|
+
* Applets container. Usefull to get all available applets in your app. See {@link module:Applet|applet} for details.
|
|
112
188
|
* @type {Array}
|
|
113
189
|
*/
|
|
114
190
|
this.applets = []
|
|
115
191
|
|
|
116
192
|
/**
|
|
117
|
-
*
|
|
193
|
+
* Original plugin's **package names** (not to be confused with plugin namespaces) container. Ypu can use this
|
|
194
|
+
* information to get the real package name of a plugin.
|
|
195
|
+
*
|
|
196
|
+
* > **Reminder**: In Bajo, plugin name/namespace is the camel-cased version of its package name.
|
|
197
|
+
* E.g. `my-plugin` → `myPlugin`.
|
|
198
|
+
*
|
|
118
199
|
* @type {string[]}
|
|
119
200
|
*/
|
|
120
201
|
this.pluginPkgs = options.plugins ?? []
|
|
121
202
|
|
|
122
203
|
/**
|
|
123
|
-
* Config handlers.
|
|
204
|
+
* Config handlers container. This is where all config handlers are stored. Each handler
|
|
205
|
+
* is responsible to read and write config file in a particular format. Bajo uses this heavily to read/write config
|
|
206
|
+
* files and/or anything that needs to be parsed/stringified from string to object and vice versa.
|
|
124
207
|
*
|
|
125
208
|
* By default, there are three built-in handlers: `.js`, `.json`
|
|
126
209
|
* and `.yml/.yaml`. Use plugins to add more, e.g {@link https://github.com/ardhi/bajo-config|bajo-config}
|
|
127
210
|
* lets you to use `.toml`.
|
|
211
|
+
*
|
|
212
|
+
* > *Note*: `.js` is for reading only, it cannot be used to write config file.
|
|
213
|
+
|
|
128
214
|
* @type {Array<App.TConfigHandler>}
|
|
129
215
|
*/
|
|
130
216
|
this.configHandlers = []
|
|
@@ -143,55 +229,66 @@ class App {
|
|
|
143
229
|
this.lib.parseObject = this.lib.parseObject.bind(this)
|
|
144
230
|
|
|
145
231
|
/**
|
|
146
|
-
* Instance of system log.
|
|
147
|
-
*
|
|
232
|
+
* Instance of the system log. This will automatically be instantiated early during boot process. See {@link Log} for details.
|
|
148
233
|
* @type {Log}
|
|
149
234
|
*/
|
|
150
235
|
this.log = {}
|
|
151
236
|
|
|
152
237
|
/**
|
|
153
|
-
* All plugin's base
|
|
154
|
-
*
|
|
155
|
-
*
|
|
238
|
+
* All plugin's base classes are saved here for reference so that you can extend them in your own plugins.
|
|
239
|
+
*
|
|
240
|
+
* Two basic classes from Bajo package are also provided: {@link Base} and {@link Tools}. These are the most
|
|
241
|
+
* used classes in Bajo plugin development. You can extend them to create your own plugin's base class:
|
|
242
|
+
*
|
|
243
|
+
* ```javascript
|
|
244
|
+
* // factory function to create your own plugin's base class
|
|
245
|
+
* async function () {
|
|
246
|
+
* const { Base } = this.app.baseClass
|
|
247
|
+
* class MyBase extends Base {
|
|
248
|
+
* constructor(pkgName, app) {
|
|
249
|
+
* super(pkgName, app)
|
|
250
|
+
* this.myProperty = 'myValue' // your class property
|
|
251
|
+
* }
|
|
156
252
|
*
|
|
157
|
-
*
|
|
158
|
-
* class
|
|
159
|
-
*
|
|
253
|
+
* async myMethod() {
|
|
254
|
+
* // your class method
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
160
257
|
* }
|
|
258
|
+
* ```
|
|
161
259
|
*
|
|
162
260
|
* @type {Object}
|
|
163
261
|
*/
|
|
164
262
|
this.baseClass = { Base, Tools }
|
|
165
263
|
|
|
166
264
|
/**
|
|
167
|
-
* If app runs in applet mode, this will be the applet's name.
|
|
265
|
+
* If app runs in **applet** mode, this will be the current applet's name. Otherwise, it will be `undefined`. See {@link module:Applet|applet} for details.
|
|
168
266
|
* @type {string}
|
|
169
267
|
*/
|
|
170
268
|
this.applet = undefined
|
|
171
269
|
|
|
172
270
|
/**
|
|
173
271
|
* Parsed program arguments.
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
272
|
+
*
|
|
273
|
+
* Example:
|
|
274
|
+
* ```js
|
|
177
275
|
* $ node index.js arg1 arg2
|
|
178
276
|
* ...
|
|
179
277
|
* console.log(this.args) // it should print: ['arg1', 'arg2']
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @type {string[]}
|
|
180
281
|
*/
|
|
181
282
|
this.args = []
|
|
182
283
|
|
|
183
284
|
/**
|
|
184
|
-
* Parsed program options
|
|
185
|
-
*
|
|
285
|
+
* Parsed program options:
|
|
186
286
|
* - Dash (`-`) breaks the string into object keys
|
|
187
|
-
* - While colon (`:`) is used as namespace separator. If no namespace found, it is saved under `_
|
|
287
|
+
* - While colon (`:`) is used as namespace - object separator. If no such namespace found, it is saved under `_`.
|
|
188
288
|
*
|
|
189
|
-
*
|
|
190
|
-
* for details.
|
|
289
|
+
* Parsed object values are normalized to its primitives using {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}
|
|
191
290
|
*
|
|
192
|
-
*
|
|
193
|
-
* @see module:Helper.parseArgsArgv
|
|
194
|
-
* @example
|
|
291
|
+
* ```js
|
|
195
292
|
* $ node index.js --my-name-first=John --my-name-last=Doe --my-birthDay=secret --nameSpace:path-subPath=true
|
|
196
293
|
* ...
|
|
197
294
|
* // {
|
|
@@ -203,37 +300,33 @@ class App {
|
|
|
203
300
|
* // },
|
|
204
301
|
* // nameSpace: { path: { subPath: true } }
|
|
205
302
|
* // }
|
|
303
|
+
*
|
|
304
|
+
* @type {Object}
|
|
206
305
|
*/
|
|
207
306
|
this.argv = {}
|
|
208
307
|
|
|
209
308
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* -
|
|
213
|
-
* -
|
|
214
|
-
* - While dot (`.`) is used as namespace separator. If no namespace found, it is saved under `_` key.
|
|
215
|
-
*
|
|
216
|
-
* Values are also parsed automatically using {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}.
|
|
309
|
+
* Parsed environment variables. Support dotenv (`.env`) file too!
|
|
310
|
+
* - Underscore (`_`) between keys merge those keys into a single camel-cased key
|
|
311
|
+
* - Double underscores (`__`) between keys breaks the key into nested objects
|
|
312
|
+
* - Meanwhile a dot (`.`) between keys breaks the key into nested, name-spaced objects
|
|
217
313
|
*
|
|
218
|
-
*
|
|
314
|
+
* Parsed object values are normalized to its primitives using {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}
|
|
219
315
|
*
|
|
316
|
+
* Example:
|
|
220
317
|
* - `MY_KEY=secret` → `{ _: { myKey: 'secret' } }`
|
|
221
318
|
* - `MY_KEY__SUB_KEY=supersecret` → `{ _: { myKey: { subKey: 'supersecret' } } }`
|
|
222
319
|
* - `MY_NS.MY_NAME=John` → `{ myNs: { myName: 'John' } }`
|
|
223
320
|
*
|
|
321
|
+
* > **Tips**: During boot process, Bajo will add `APPDIR` environment variable which points to your app's root directory.
|
|
322
|
+
*
|
|
224
323
|
* @type {Object}
|
|
225
|
-
* @see module:Helper.parseEnv
|
|
226
324
|
*/
|
|
227
325
|
this.envVars = {}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Placeholder for boxen that will get imported from `bajoCli` later during boot process.
|
|
231
|
-
* @type {Object}
|
|
232
|
-
*/
|
|
233
326
|
this.boxen = null
|
|
234
327
|
|
|
235
328
|
/**
|
|
236
|
-
*
|
|
329
|
+
* Internal cache instance. This is used to store temporary data in memory for faster access.
|
|
237
330
|
* @type {Cache}
|
|
238
331
|
*/
|
|
239
332
|
this.cache = new Cache(this)
|
|
@@ -249,11 +342,12 @@ class App {
|
|
|
249
342
|
}
|
|
250
343
|
|
|
251
344
|
/**
|
|
252
|
-
* Add and save plugin and
|
|
345
|
+
* Add and save plugin and its base class definition (if provided) to the app instance. You can then reference
|
|
346
|
+
* the plugin using its namespace, e.g. `this.myPlugin` or `this['myPlugin']`.
|
|
253
347
|
*
|
|
254
348
|
* @method
|
|
255
|
-
* @param {
|
|
256
|
-
* @param {
|
|
349
|
+
* @param {Base} plugin - A valid Bajo plugin.
|
|
350
|
+
* @param {class} [baseClass] - Base class definition.
|
|
257
351
|
*/
|
|
258
352
|
addPlugin = (plugin, baseClass) => {
|
|
259
353
|
if (this[plugin.ns]) throw new Error(`Plugin '${plugin.ns}' added already`)
|
|
@@ -262,7 +356,7 @@ class App {
|
|
|
262
356
|
}
|
|
263
357
|
|
|
264
358
|
/**
|
|
265
|
-
* Get all loaded plugin namespaces
|
|
359
|
+
* Get all loaded **plugin namespaces**.
|
|
266
360
|
*
|
|
267
361
|
* @method
|
|
268
362
|
* @returns {string[]}
|
|
@@ -272,34 +366,36 @@ class App {
|
|
|
272
366
|
}
|
|
273
367
|
|
|
274
368
|
/**
|
|
275
|
-
* Get loaded plugins.
|
|
369
|
+
* Get loaded plugins by their namespaces. If no namespace is provided, it will return all loaded plugins.
|
|
276
370
|
*
|
|
277
371
|
* @method
|
|
278
|
-
* @param {string[]} [nss] - Array of namespaces. If empty, it returns all loaded plugins
|
|
279
|
-
* @
|
|
372
|
+
* @param {string[]} [nss] - Array of namespaces. If empty, it returns all loaded plugins
|
|
373
|
+
* @param {boolean} [nameOnly=false] - If `true`, it will return only the plugin namespaces instead of the plugin instances
|
|
374
|
+
* @returns {Array<Base>} Array of plugin instances
|
|
280
375
|
*/
|
|
281
|
-
getPlugins = (nss) => {
|
|
376
|
+
getPlugins = (nss, nameOnly = false) => {
|
|
282
377
|
const allNs = nss ?? this.getAllNs()
|
|
283
|
-
return allNs.map(ns => this[ns])
|
|
378
|
+
return allNs.map(ns => nameOnly ? ns : this[ns])
|
|
284
379
|
}
|
|
285
380
|
|
|
286
381
|
/**
|
|
287
|
-
* Get all
|
|
382
|
+
* Get all loaded plugins. Alias to {@link App#getPlugins|getPlugins()} with no namespace provided.
|
|
288
383
|
*
|
|
289
384
|
* @method
|
|
290
|
-
* @returns {
|
|
385
|
+
* @returns {Array<Base>} Array of all loaded plugin instances
|
|
291
386
|
*/
|
|
292
387
|
getAllPlugins = () => {
|
|
293
388
|
return this.getPlugins()
|
|
294
389
|
}
|
|
295
390
|
|
|
296
391
|
/**
|
|
297
|
-
* Get plugin by its namespace.
|
|
392
|
+
* Get plugin by its namespace or alias. If the plugin is not loaded, it will throw an error
|
|
393
|
+
* unless `silent` is set to `true`.
|
|
298
394
|
*
|
|
299
395
|
* @method
|
|
300
|
-
* @param {string} name -
|
|
301
|
-
* @param {boolean} [silent] - If `true`, silently return undefined even on error
|
|
302
|
-
* @returns {
|
|
396
|
+
* @param {string} name - Namespace or alias.
|
|
397
|
+
* @param {boolean} [silent] - If `true`, silently return undefined even on error
|
|
398
|
+
* @returns {Base} Plugin object.
|
|
303
399
|
*/
|
|
304
400
|
getPlugin = (name, silent) => {
|
|
305
401
|
if (!this[name]) {
|
|
@@ -313,7 +409,7 @@ class App {
|
|
|
313
409
|
}
|
|
314
410
|
}
|
|
315
411
|
if (!plugin) {
|
|
316
|
-
if (silent) return
|
|
412
|
+
if (silent) return undefined
|
|
317
413
|
throw this.bajo.error('pluginWithNameAliasNotLoaded%s', name)
|
|
318
414
|
}
|
|
319
415
|
name = plugin.ns
|
|
@@ -322,11 +418,12 @@ class App {
|
|
|
322
418
|
}
|
|
323
419
|
|
|
324
420
|
/**
|
|
325
|
-
* Get plugin data directory
|
|
421
|
+
* Get plugin data directory. If the directory does not exist, it will be created automatically
|
|
422
|
+
* unless `ensureDir` is set to `false`.
|
|
326
423
|
*
|
|
327
424
|
* @method
|
|
328
|
-
* @param {string} name -
|
|
329
|
-
* @param {boolean} [ensureDir=true] - Set `true` (default) to ensure directory
|
|
425
|
+
* @param {string} name - Namespace or alias.
|
|
426
|
+
* @param {boolean} [ensureDir=true] - Set `true` (default) to ensure directory exists
|
|
330
427
|
* @returns {string}
|
|
331
428
|
*/
|
|
332
429
|
getPluginDataDir = (name, ensureDir = true) => {
|
|
@@ -339,14 +436,13 @@ class App {
|
|
|
339
436
|
|
|
340
437
|
/**
|
|
341
438
|
* Resolve file path from:
|
|
342
|
-
*
|
|
343
439
|
* - local/absolute file
|
|
344
|
-
* -
|
|
345
|
-
* - file under node_modules
|
|
440
|
+
* - {@link Bajo.TNsPathPairs} e.g. `myPlugin:/path/to/file.txt`
|
|
441
|
+
* - file under `node_modules`, e.g. `myPlugin:node_modules/some-package/file.txt`
|
|
346
442
|
*
|
|
347
443
|
* @method
|
|
348
|
-
* @param {string} file - File path, see above for supported types
|
|
349
|
-
* @returns {string} Resolved file path
|
|
444
|
+
* @param {string} file - File path, see above for supported types
|
|
445
|
+
* @returns {string} Resolved file path
|
|
350
446
|
*/
|
|
351
447
|
getPluginFile = (file) => {
|
|
352
448
|
const { currentLoc } = this.lib.aneka
|
|
@@ -372,13 +468,13 @@ class App {
|
|
|
372
468
|
* Dumping variable on screen. Like `console.log` with configurable options. Useful for quick debugging and testing. You can also use it to dump variables in production without worrying about performance because it is using Bajo's built-in cache to store the result of util's inspect, so it will only be processed once for each unique variable.
|
|
373
469
|
*
|
|
374
470
|
* Any argument passed to this method will be displayed on screen.
|
|
375
|
-
* If the last argument is a
|
|
471
|
+
* If the last argument is a string `\q` or `.q`, app will quit rightaway after dumping.
|
|
376
472
|
*
|
|
377
473
|
* If you have `bajoCli` plugin installed, variables will be displayed in a nice box using `boxen` package.
|
|
378
474
|
* Otherwise, it will fallback to `console.log` with util's inspect result.
|
|
379
475
|
*
|
|
380
476
|
* To have more control on how the variable is displayed, you can set options in Bajo's config under `dump` key.
|
|
381
|
-
* See {@link Bajo
|
|
477
|
+
* See {@link Bajo.TConfig} for details.
|
|
382
478
|
*
|
|
383
479
|
* @method
|
|
384
480
|
* @param {...any} args - Variables to dump.
|
|
@@ -386,8 +482,7 @@ class App {
|
|
|
386
482
|
dump = (...args) => {
|
|
387
483
|
let caller = getCallerFilename()
|
|
388
484
|
caller = caller ? fileURLToPath(caller) : 'Unavailable'
|
|
389
|
-
const
|
|
390
|
-
const terminate = isPlainObject(opts) && opts.abort
|
|
485
|
+
const terminate = ['\\q', '.q'].includes(last(args))
|
|
391
486
|
if (terminate) args.pop()
|
|
392
487
|
const value = args.length === 1 ? args[0] : args
|
|
393
488
|
const options = { ...this.bajo.config.dump }
|
|
@@ -404,13 +499,15 @@ class App {
|
|
|
404
499
|
}
|
|
405
500
|
|
|
406
501
|
/**
|
|
407
|
-
* Run application
|
|
502
|
+
* Run application and begin the boot process. This method is called automatically by {@link boot|boot} script.
|
|
408
503
|
*
|
|
409
|
-
*
|
|
410
|
-
* -
|
|
411
|
-
* - {@link
|
|
504
|
+
* Boot process includes:
|
|
505
|
+
* - parsing environment values, program arguments, and options
|
|
506
|
+
* - create {@link Bajo|Bajo} instance & initialize it
|
|
507
|
+
* - load all plugins, their hooks, their configs and any other necessary services & resources
|
|
508
|
+
* - {@link Bajo.runAsApplet|run in applet mode} if `-a` or `--applet` is given
|
|
412
509
|
*
|
|
413
|
-
* After boot process is completed,
|
|
510
|
+
* After boot process is completed, hook `bajo:afterBoot` is triggered.
|
|
414
511
|
*
|
|
415
512
|
* If app mode is `applet`, it runs your choosen applet instead.
|
|
416
513
|
*
|
|
@@ -441,10 +538,10 @@ class App {
|
|
|
441
538
|
this.args = args
|
|
442
539
|
this.argv = parseObject(argv, { parseValue: true })
|
|
443
540
|
this.envVars = parseObject(parseEnv(), { parseValue: true })
|
|
444
|
-
if (get(this, 'envVars._.env') === '[object Object]') set(this, 'envVars._.env', 'dev')
|
|
541
|
+
if (get(this, 'envVars._.env') === '[object Object]') set(this, 'envVars._.env', 'dev') // hack for some env vars that are not parsed correctly
|
|
445
542
|
this.applet = this.envVars._.applet ?? this.argv._.applet
|
|
446
543
|
await this.bajo.runHook('bajo:beforeBoot')
|
|
447
|
-
await this.bajo.
|
|
544
|
+
await this.bajo.bootApp()
|
|
448
545
|
if (this.bajoCli) this.boxen = await this.bajo.importPkg('bajoCli:boxen')
|
|
449
546
|
// cache
|
|
450
547
|
this.cache.purge()
|
|
@@ -458,21 +555,25 @@ class App {
|
|
|
458
555
|
}
|
|
459
556
|
|
|
460
557
|
/**
|
|
461
|
-
* Terminate the app and back to console.
|
|
558
|
+
* Terminate the app forcefully and back to console.
|
|
462
559
|
*
|
|
463
560
|
* @method
|
|
464
|
-
* @param {string|boolean} [signal=SIGINT] - Signal to send. Set to `true` to terminate immediately without sending any signal
|
|
561
|
+
* @param {string|boolean} [signal=SIGINT] - Signal to send. Set to `true` to terminate immediately without sending any signal
|
|
562
|
+
* @param {string} [reason] - Reason to be printed on console before exiting
|
|
465
563
|
*/
|
|
466
|
-
exit = (signal = 'SIGINT') => {
|
|
564
|
+
exit = (signal = 'SIGINT', reason) => {
|
|
565
|
+
if (reason) console.error(reason)
|
|
467
566
|
if (signal === true) process.exit('1')
|
|
468
567
|
process.kill(process.pid, signal)
|
|
469
568
|
}
|
|
470
569
|
|
|
471
570
|
/**
|
|
472
|
-
* Load internationalization & languages files for particular plugin.
|
|
571
|
+
* Load internationalization & languages files for particular plugin by its namespace.
|
|
572
|
+
* It will load all supported languages defined in {@link Bajo.TConfig|`config.intl.supported`}
|
|
573
|
+
* and save them in `this[ns].intl` object.
|
|
473
574
|
*
|
|
474
575
|
* @method
|
|
475
|
-
* @param {string} ns - Plugin
|
|
576
|
+
* @param {string} ns - Plugin namespace
|
|
476
577
|
*/
|
|
477
578
|
loadIntl = (ns) => {
|
|
478
579
|
const { fs } = this.lib
|
|
@@ -536,28 +637,34 @@ class App {
|
|
|
536
637
|
}
|
|
537
638
|
|
|
538
639
|
/**
|
|
539
|
-
* Translate text
|
|
640
|
+
* Translate text to the current language.
|
|
641
|
+
*
|
|
642
|
+
* It will search for the translation in the plugin's `intl` object first, interpolating the parameters if found,
|
|
643
|
+
* then fallback to `bajo` plugin's `intl` object if not found.
|
|
540
644
|
*
|
|
541
645
|
* If the last parameter is an object with `lang` key, it will use that language instead of the default one.
|
|
542
646
|
*
|
|
543
647
|
* There is a shortcut to this method attached on all plugins. You'll normally call that shorcut
|
|
544
|
-
* instead of this method, because
|
|
648
|
+
* instead of this method, because that shortcut is bound to plugin's namespace already
|
|
545
649
|
*
|
|
546
|
-
*
|
|
547
|
-
* @param {string} ns - Namespace
|
|
548
|
-
* @param {string} text - Text to translate
|
|
549
|
-
* @param {...any} params - Arguments
|
|
550
|
-
* @returns {string}
|
|
551
|
-
* @example
|
|
650
|
+
* ```js
|
|
552
651
|
* ... within your main plugin
|
|
553
652
|
* const translated = this.app.t('main', 'My cute cat is %s', 'purring')
|
|
554
653
|
* // or within your plugin
|
|
555
654
|
* const translated = this.t('My cute cat is %s', 'purring')
|
|
655
|
+
* ```
|
|
656
|
+
*
|
|
657
|
+
* @method
|
|
658
|
+
* @param {string} ns - Namespace
|
|
659
|
+
* @param {string} text - Text to translate
|
|
660
|
+
* @param {...*} params - Parameters to interpolate into the translation text
|
|
661
|
+
* @returns {string}
|
|
556
662
|
*/
|
|
557
663
|
t = (ns, text, ...params) => {
|
|
558
664
|
const { formatText, isSet } = this.lib.aneka
|
|
559
|
-
const { isArray, last } = this.lib._
|
|
665
|
+
const { isArray, last, get } = this.lib._
|
|
560
666
|
const { join } = this.bajo
|
|
667
|
+
if (!get(this, 'bajo.config.intl')) return formatText(text, ...params)
|
|
561
668
|
let { text: newText, trans, params: args } = this._prepTrans(ns, text, params)
|
|
562
669
|
if (!isSet(trans)) trans = newText
|
|
563
670
|
const lang = isPlainObject(last(args)) ? last(args).lang : undefined
|
|
@@ -582,7 +689,8 @@ class App {
|
|
|
582
689
|
}
|
|
583
690
|
|
|
584
691
|
/**
|
|
585
|
-
*
|
|
692
|
+
* Method to list all supported config formats, that is, all file extensions that are supported
|
|
693
|
+
* by the app's config handlers.
|
|
586
694
|
*
|
|
587
695
|
* @method
|
|
588
696
|
* @param {boolean} [noDot] - If `true`, it will return the list without dot prefix
|
|
@@ -594,7 +702,7 @@ class App {
|
|
|
594
702
|
}
|
|
595
703
|
|
|
596
704
|
/**
|
|
597
|
-
* Start a plugin.
|
|
705
|
+
* Start a plugin by its namespace. It will call the plugin's `start` method with the provided arguments.
|
|
598
706
|
*
|
|
599
707
|
* @method
|
|
600
708
|
* @param {string} ns - Plugin namespace.
|
|
@@ -605,8 +713,13 @@ class App {
|
|
|
605
713
|
}
|
|
606
714
|
|
|
607
715
|
/**
|
|
608
|
-
* Stop a plugin.
|
|
716
|
+
* Stop a plugin by its namespace. It will call the plugin's `stop` method with the provided arguments.
|
|
717
|
+
*
|
|
718
|
+
* Reserved for future use.
|
|
609
719
|
*
|
|
720
|
+
* > **Note**: Basically it is not a good idea to stop a plugin because other plugins
|
|
721
|
+
* might be dependent on it. But if we could find a way to safely stop a plugin without breaking things,
|
|
722
|
+
* this will be a cool feature because it allows dynamic management of plugins without restarting the application.
|
|
610
723
|
* @method
|
|
611
724
|
* @param {string} ns - Plugin namespace.
|
|
612
725
|
* @param {...any} args - Arguments to pass to the plugin's stop method
|