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.
Files changed (101) hide show
  1. package/.github/FUNDING.yml +13 -0
  2. package/.github/workflows/repo-lockdown.yml +24 -0
  3. package/.jsdoc.conf.json +7 -6
  4. package/LICENSE +1 -1
  5. package/README.md +50 -18
  6. package/class/app.js +378 -54
  7. package/class/bajo.js +228 -149
  8. package/class/base.js +106 -0
  9. package/class/helper/bajo.js +146 -90
  10. package/class/helper/{plugin.js → base.js} +96 -22
  11. package/class/{base → misc}/err.js +44 -14
  12. package/class/misc/log.js +255 -0
  13. package/class/misc/print.js +264 -0
  14. package/class/plugin.js +120 -68
  15. package/docs/App.html +23 -1
  16. package/docs/Bajo.html +2 -9
  17. package/docs/Base.html +3 -0
  18. package/docs/Err.html +3 -1
  19. package/docs/Log.html +5 -1
  20. package/docs/Plugin.html +1 -1
  21. package/docs/Print.html +1 -1
  22. package/docs/class_app.js.html +378 -56
  23. package/docs/class_bajo.js.html +230 -151
  24. package/docs/class_base.js.html +109 -0
  25. package/docs/class_helper_bajo.js.html +138 -87
  26. package/docs/class_helper_base.js.html +246 -0
  27. package/docs/class_misc_err.js.html +129 -0
  28. package/docs/class_misc_log.js.html +210 -0
  29. package/docs/class_misc_print.js.html +267 -0
  30. package/docs/class_plugin.js.html +122 -70
  31. package/docs/data/search.json +1 -1
  32. package/docs/global.html +4 -1
  33. package/docs/index.html +2 -2
  34. package/docs/index.js.html +35 -0
  35. package/docs/lib_current-loc.js.html +36 -0
  36. package/docs/lib_formats.js.html +8 -8
  37. package/docs/lib_import-module.js.html +59 -0
  38. package/docs/lib_log-levels.js.html +17 -7
  39. package/docs/lib_parse-args-argv.js.html +83 -0
  40. package/docs/lib_parse-env.js.html +53 -0
  41. package/docs/lib_resolve-path.js.html +3 -6
  42. package/docs/lib_shim.js.html +8 -3
  43. package/docs/module-Helper_Bajo.html +3 -0
  44. package/docs/module-Helper_Base.html +3 -0
  45. package/docs/module-Lib.html +15 -0
  46. package/docs/static/home.md +32 -0
  47. package/docs/static/logo-ecosystem.png +0 -0
  48. package/docs/static/logo.png +0 -0
  49. package/extend/bajo/intl/en-US.json +9 -2
  50. package/extend/bajo/intl/id.json +9 -2
  51. package/index.js +22 -2
  52. package/lib/current-loc.js +24 -2
  53. package/lib/formats.js +6 -6
  54. package/lib/import-module.js +29 -0
  55. package/lib/log-levels.js +15 -5
  56. package/lib/parse-args-argv.js +20 -12
  57. package/lib/parse-env.js +18 -7
  58. package/lib/resolve-path.js +1 -4
  59. package/lib/shim.js +6 -1
  60. package/package.json +4 -7
  61. package/wiki/CONFIG.md +36 -0
  62. package/wiki/CONTRIBUTING.md +5 -0
  63. package/wiki/DEV_GUIDE.md +3 -0
  64. package/wiki/ECOSYSTEM.md +93 -0
  65. package/wiki/GETTING-STARTED.md +356 -0
  66. package/wiki/USER-GUIDE.md +256 -0
  67. package/class/base/log.js +0 -205
  68. package/class/base/plugin.js +0 -168
  69. package/class/base/print.js +0 -272
  70. package/docs/BasePlugin.html +0 -5
  71. package/docs/class_base_err.js.html +0 -99
  72. package/docs/class_base_log.js.html +0 -208
  73. package/docs/class_base_plugin.js.html +0 -180
  74. package/docs/class_base_print.js.html +0 -275
  75. package/docs/class_helper_plugin.js.html +0 -172
  76. package/docs/lib_create-method.js.html +0 -42
  77. package/docs/module-class_helper_bajo.html +0 -3
  78. package/docs/module-class_helper_plugin.html +0 -3
  79. package/docs/module-lib_create-method.html +0 -3
  80. package/docs/module-lib_formats.html +0 -3
  81. package/docs/module-lib_log-levels.html +0 -3
  82. package/docs/module-lib_resolve-path.html +0 -3
  83. package/docs/module-lib_shim.html +0 -3
  84. package/docs/tutorial-contribution.html +0 -3
  85. package/docs/tutorial-ecosystem.html +0 -3
  86. package/docs/tutorial-getting-started.html +0 -13
  87. package/docs/tutorial-plugin-dev.html +0 -3
  88. package/docs/tutorial-user-guide.html +0 -3
  89. package/lib/create-method.js +0 -39
  90. package/lib/dayjs.js +0 -8
  91. package/lib/omitted-plugin-keys.js +0 -3
  92. package/lib/read-all-configs.js +0 -19
  93. package/misc-docs/.hook.md +0 -11
  94. package/misc-docs/bitcoin.jpeg +0 -0
  95. package/misc-docs/contribution.md +0 -20
  96. package/misc-docs/ecosystem.md +0 -94
  97. package/misc-docs/getting-started.md +0 -142
  98. package/misc-docs/plugin-dev.md +0 -0
  99. package/misc-docs/toc.json +0 -17
  100. package/misc-docs/user-guide.md +0 -1
  101. /package/docs/{bitcoin.jpeg → static/bitcoin.jpeg} +0 -0
@@ -0,0 +1,13 @@
1
+ github: ardhi
2
+ patreon: bajoframework
3
+ #open_collective: # Replace with a single Open Collective username
4
+ #ko_fi: # Replace with a single Ko-fi username
5
+ #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
6
+ #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
7
+ #liberapay: # Replace with a single Liberapay username
8
+ #issuehunt: # Replace with a single IssueHunt username
9
+ #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
10
+ #polar: # Replace with a single Polar username
11
+ #buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
12
+ #thanks_dev: # Replace with a single thanks.dev username
13
+ custom: ["https://www.paypal.com/ncp/payment/EWLERL7SCUU64"]
@@ -0,0 +1,24 @@
1
+ name: 'Repo Lockdown'
2
+
3
+ on:
4
+ #issues:
5
+ #types: opened
6
+ pull_request_target:
7
+ types: opened
8
+ #schedule:
9
+ #- cron: '* */6 * * *'
10
+
11
+ permissions:
12
+ #issues: write
13
+ pull-requests: write
14
+
15
+ jobs:
16
+ action:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: dessant/repo-lockdown@v4
20
+ with:
21
+ pr-comment: >
22
+ This repository does not accept pull requests,
23
+ see the README.md for details.
24
+ skip-closed-pr-comment: true
package/.jsdoc.conf.json CHANGED
@@ -6,21 +6,22 @@
6
6
  "verbose": true,
7
7
  "destination": "./docs",
8
8
  "template": "node_modules/clean-jsdoc-theme",
9
- "tutorials": "./misc-docs",
9
+ "readme": "./docs/static/home.md",
10
10
  "theme_opts": {
11
11
  "default_theme": "light",
12
12
  "display-module-header": true,
13
- "title": "Bajo Framework",
14
- "homepageTitle": "Bajo Framework",
13
+ "title": "Bajo API",
14
+ "homepageTitle": "Bajo API",
15
+ "sections": ["Classes", "Events", "Modules", "Global"],
15
16
  "menu": [{
16
- "title": "Bajo.app",
17
- "link": "https://bajo.app"
18
- }, {
19
17
  "title": "NPM",
20
18
  "link": "https://www.npmjs.com/package/bajo"
21
19
  }, {
22
20
  "title": "Github",
23
21
  "link": "https://github.com/ardhi/bajo"
22
+ }, {
23
+ "title": "Bajo",
24
+ "link": "https://bajo.app"
24
25
  }]
25
26
  }
26
27
  },
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Ardhi Lukianto
3
+ Copyright (c) 2023-2025 Ardhi Lukianto
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,18 +1,50 @@
1
- # bajo
2
-
3
- ![GitHub package.json version](https://img.shields.io/github/package-json/v/ardhi/bajo) ![NPM Version](https://img.shields.io/npm/v/bajo)
4
-
5
- > <br />**Attention**: I do NOT accept any pull request at the moment, thanks! ([Why?](misc-docs/contribution.md))<br /><br />
6
-
7
- ## Documentations
8
-
9
- - [Getting Started](misc-docs/getting-started.md)
10
- - [User Guide](misc-docs/user-guide.md)
11
- - [Plugin Development](misc-docs/plugin-dev.md)
12
- - [Ecosystem](misc-docs/ecosystem.md)
13
- - [Contribution](misc-docs/contribution.md)
14
- - [API](docs)
15
-
16
- ## License
17
-
18
- [MIT](LICENSE)
1
+ # bajo
2
+
3
+ The ultimate framework for whipping up a massive app in no time!
4
+
5
+ ![GitHub package.json version](https://img.shields.io/github/package-json/v/ardhi/bajo) ![NPM Version](https://img.shields.io/npm/v/bajo)
6
+
7
+ > <br />**Attention**: I do NOT accept any pull request at the moment, thanks! ([Why?](wiki/CONTRIBUTING.md))<br /><br />
8
+
9
+ <a href="https://bajo.app"><img src="docs/static/logo.png" width="300" alt="bajo"></a>
10
+
11
+ [Bajo](https://bajo.app) is one of a kind framework. Think of it as Lego blocks; you create your very own customized structure by attaching together only the needed blocks. This same principle for app building is now possible with Bajo: pick the right plugins, glue them together, and fire it up!
12
+
13
+ From a simple command-line app to a monster-sized, multi-page web app, Bajo will deliver your app in no time just by adding more plugins.
14
+
15
+ ## Documentations
16
+
17
+ - [Homepage](https://bajo.app/)
18
+ - [Getting Started](wiki/GETTING-STARTED.md)
19
+ - [User Guide](wiki/USER-GUIDE.md)
20
+ - [Developer Guide](wiki/DEV-GUIDE.md)
21
+ - [Config Object](wiki/CONFIG.md)
22
+ - [Ecosystem](wiki/ECOSYSTEM.md)
23
+ - [API](https://ardhi.github.io/bajo)
24
+ - [Contributing](wiki/CONTRIBUTING.md)
25
+
26
+ ## Hire Me
27
+
28
+ If you have a Bajo Framework-based project and need a professional service or assistance, please <a href="https://github.com/ardhi#professional-service">click here</a>. I'd be happy to work on it at a competitive price and with fast turnaround!
29
+
30
+ ## Support Me
31
+
32
+ Please support me using the channels below. Your donation will motivate me to work faster and more diligently on future development.
33
+
34
+ <a href="https://github.com/sponsors/ardhi">
35
+ <img src="https://img.shields.io/badge/Github-slategrey?style=flat&logo=github" height="50">
36
+ </a>
37
+ <a href="https://www.patreon.com/bajoframework">
38
+ <img src="https://img.shields.io/badge/Patreon-f2c3b2?style=flat&logo=patreon" height="50">
39
+ </a>
40
+ <a href="https://www.paypal.com/ncp/payment/EWLERL7SCUU64">
41
+ <img src="https://img.shields.io/badge/Paypal-blue?style=flat&logo=paypal" height="50">
42
+ </a>
43
+
44
+ <p>
45
+ <div><img alt="bc1qwtv78cwp9ef8hnqaw84fxg5856l0pggqe32g6f" src="docs/static/bitcoin.jpeg" width="150" height="150" /><br>Bitcoin</div>
46
+ </p>
47
+
48
+ ## License
49
+
50
+ [MIT](LICENSE)
package/class/app.js CHANGED
@@ -1,49 +1,239 @@
1
1
  import util from 'util'
2
2
  import lodash from 'lodash'
3
3
  import Bajo from './bajo.js'
4
+ import fastGlob from 'fast-glob'
5
+ import { sprintf } from 'sprintf-js'
6
+ import outmatch from 'outmatch'
7
+ import fs from 'fs-extra'
8
+ import aneka from 'aneka/index.js'
9
+ import Base from './base.js'
4
10
  import resolvePath from '../lib/resolve-path.js'
5
11
  import parseArgsArgv from '../lib/parse-args-argv.js'
6
12
  import parseEnv from '../lib/parse-env.js'
7
- import {
8
- buildBaseConfig,
9
- buildExtConfig,
10
- buildPlugins,
11
- collectConfigHandlers,
12
- bootOrder,
13
- bootPlugins,
14
- exitHandler,
15
- runAsApplet
16
- } from './helper/bajo.js'
17
-
18
- const { last } = lodash
13
+ import { runAsApplet } from './helper/bajo.js'
14
+ import dayjs from 'dayjs'
15
+ import utc from 'dayjs/plugin/utc.js'
16
+ import customParseFormat from 'dayjs/plugin/customParseFormat.js'
17
+ import localizedFormat from 'dayjs/plugin/localizedFormat.js'
18
+ import weekOfYear from 'dayjs/plugin/weekOfYear.js'
19
+
20
+ dayjs.extend(utc)
21
+ dayjs.extend(customParseFormat)
22
+ dayjs.extend(localizedFormat)
23
+ dayjs.extend(weekOfYear)
24
+
25
+ const { isPlainObject, get, reverse, map, isString, last, without, keys } = lodash
26
+ let unknownLangWarning = false
27
+
28
+ function outmatchNs (source, pattern) {
29
+ const { breakNsPath } = this.bajo
30
+ const [src, subSrc] = source.split(':')
31
+ if (!subSrc) return pattern === src
32
+ try {
33
+ const { fullNs, path } = breakNsPath(pattern)
34
+ const isMatch = outmatch(path)
35
+ return src === fullNs && isMatch(subSrc)
36
+ } catch (err) {
37
+ return false
38
+ }
39
+ }
19
40
 
20
41
  /**
21
- * App class. This is where everything starts, the boot process:
22
- *
23
- * 1. Parsing all arguments and environment values
24
- * 2. Create {@link Bajo|Bajo} instance
25
- * 3. Building {@link module:class/helper/bajo.buildBaseConfig|base config}
26
- * 4. {@link module:class/helper/bajo.buildPlugins|Building plugins}
27
- * 5. Collect all {@link module:class/helper/bajo.collectConfigHandlers|config handler}
28
- * 6. Building {@link module:class/helper/bajo.buildExtConfig|extra config}
29
- * 7. Setup {@link module:class/helper/bajo.bootOrder|boot order}
30
- * 8. {@link module:class/helper/bajo.bootPlugins|Boot loaded plugins}
31
- * 9. Attach {@link module:class/helper/bajo.exitHandler|exit handlers}
32
- * 10. Finish
33
- *
34
- * After boot process is completed, event ```bajo:afterBootComplete``` is emitted.
35
- *
36
- * If app mode is ```applet```, it runs your choosen applet instead.
42
+ * @typedef {Object} TAppEnv
43
+ * @property {string} dev=development
44
+ * @property {string} prod=production
45
+ * @see App
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} TAppLib
50
+ * @property {Object} _ - Access to {@link https://lodash.com|lodash}
51
+ * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
52
+ * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
53
+ * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
54
+ * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
55
+ * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
56
+ * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
57
+ * @see App
58
+ */
59
+ const lib = {
60
+ _: lodash,
61
+ fs,
62
+ fastGlob,
63
+ sprintf,
64
+ outmatch,
65
+ dayjs,
66
+ aneka
67
+ }
68
+
69
+ /**
70
+ * App class. This is the root. This is where all plugins call it home.
37
71
  *
38
72
  * @class
39
73
  */
40
74
  class App {
41
75
  /**
42
- * Class constructor
76
+ * Your main namespace. And yes, you suppose to NOT CHANGE this
43
77
  *
78
+ * @memberof App
79
+ * @constant {string}
80
+ * @default 'main'
81
+ */
82
+ static mainNs = 'main'
83
+
84
+ /**
85
+ * App environments
86
+ * @memberof App
87
+ * @constant {TAppEnv}
88
+ */
89
+ static envs = { dev: 'development', prod: 'production' }
90
+
91
+ /**
44
92
  * @param {string} cwd - Current working dirctory
45
93
  */
46
94
  constructor (cwd) {
95
+ /**
96
+ * Date/time when your app start
97
+ * @type {Date}
98
+ */
99
+ this.runAt = new Date()
100
+
101
+ /**
102
+ * Applets
103
+ *
104
+ * @type {Array}
105
+ */
106
+ this.applets = []
107
+
108
+ /**
109
+ * List of all loaded plugin's package names
110
+ *
111
+ * @type {Array}
112
+ */
113
+ this.pluginPkgs = []
114
+
115
+ /**
116
+ * @typedef {Object} TAppConfigHandler
117
+ * @property {string} ext - File extension
118
+ * @property {function} [readHandler] - Function to call for reading
119
+ * @property {function} [writeHandler] - Function to call for writing
120
+ * @see App#configHandlers
121
+ */
122
+
123
+ /**
124
+ * Config handlers.
125
+ *
126
+ * By default, there are two built-in handlers available: ```.js```
127
+ * and ```.json```. Use plugins to add more, e.g {@link https://github.com/ardhi/bajo-config|bajo-config}
128
+ * lets you to use ```.yaml/.yml``` and ```.toml```
129
+ *
130
+ * @type {TAppConfigHandler[]}
131
+ */
132
+ this.configHandlers = []
133
+
134
+ /**
135
+ * Gives you direct access to the most commonly used 3rd party library in a Bajo based app.
136
+ * No manual import necessary, always available, anywhere, anytime!
137
+ *
138
+ * Example:
139
+ * ```javascript
140
+ * const { camelCase, kebabCase } = this.app.lib._
141
+ * console.log(camelCase('Elit commodo sit et aliqua'))
142
+ * ```
143
+ *
144
+ * @type {TAppLib}
145
+ */
146
+ this.lib = lib
147
+ this.lib.outmatchNs = outmatchNs.bind(this)
148
+
149
+ /**
150
+ * Instance of system log
151
+ *
152
+ * @type {Log}
153
+ */
154
+ this.log = {}
155
+
156
+ /**
157
+ * All plugin's class definitions are saved here as key-value pairs with plugin name as its key.
158
+ * The special key ```base``` is for {@link Base}'s class so anytime you want to
159
+ * create your own plugin, just use something like this:
160
+ *
161
+ * ```javascript
162
+ * class MyPlugin extends this.app.pluginClass.base {
163
+ * ... your class
164
+ * }
165
+ */
166
+ this.pluginClass = { base: Base }
167
+
168
+ /**
169
+ * If app runs in applet mode, this will be the applet's name
170
+ *
171
+ * @type {string}
172
+ */
173
+ this.applet = undefined
174
+
175
+ /**
176
+ * Program arguments
177
+ *
178
+ * ```
179
+ * $ node index.js arg1 arg2
180
+ * ...
181
+ * console.log(this.args) // it should print: ['arg1', 'arg2']
182
+ * ```
183
+ *
184
+ * @type {string[]}
185
+ * @see module:Lib.parseArgsArgv
186
+ */
187
+ this.args = []
188
+
189
+ /**
190
+ * Program options.
191
+ *
192
+ * - Dash (```-```) breaks the string into object keys
193
+ * - While colon (```:```) is used as namespace separator. If no namespace found, it is saved under ```_``` key.
194
+ *
195
+ * Values are parsed automatically. See {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}
196
+ * for details.
197
+ *
198
+ * ```
199
+ * $ node index.js --my-name-first=John --my-name-last=Doe --my-birthDay=secret --nameSpace:path-subPath=true
200
+ * ...
201
+ * // {
202
+ * // _: {
203
+ * // my: {
204
+ * // name: { first: 'John', last: 'Doe' },
205
+ * // birthDay: 'secret'
206
+ * // }
207
+ * // },
208
+ * // nameSpace: { path: { subPath: true } }
209
+ * // }
210
+ * ```
211
+ *
212
+ * @type {Object}
213
+ * @see module:Lib.parseArgsArgv
214
+ */
215
+ this.argv = {}
216
+
217
+ /**
218
+ * Environment variables. Support dotenv (```.env```) file too!
219
+ *
220
+ * - Underscore (```_```) translates key to camel-cased one
221
+ * - Double underscores (```__```) breaks the key into object keys
222
+ * - While dot (```.```) is used as namespace separator. If no namespace found, it is saved under ```_``` key.
223
+ *
224
+ * Values are also parsed automatically using {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}.
225
+ *
226
+ * Example:
227
+ *
228
+ * - ```MY_KEY=secret``` → ```{ _: { myKey: 'secret' } }```
229
+ * - ```MY_KEY__SUB_KEY=supersecret``` → ```{ _: { myKey: { subKey: 'supersecret' } } }```
230
+ * - ```MY_NS.MY_NAME=John``` → ```{ myNs: { myName: 'John' } }```
231
+ *
232
+ * @type {Object}
233
+ * @see module:Lib.parseEnv
234
+ */
235
+ this.envVars = {}
236
+
47
237
  if (!cwd) cwd = process.cwd()
48
238
  const l = last(process.argv)
49
239
  if (l.startsWith('--cwd')) {
@@ -54,19 +244,35 @@ class App {
54
244
  process.env.APPDIR = this.dir
55
245
  }
56
246
 
247
+ get mainNs () {
248
+ return this.constructor.mainNs
249
+ }
250
+
251
+ /**
252
+ * Add and save plugin and it's class definition (if provided)
253
+ *
254
+ * @method
255
+ * @param {TPlugin} plugin - A valid bajo plugin
256
+ * @param {Object} [pluginClass] - Plugin's class definition
257
+ */
258
+ addPlugin = (plugin, pluginClass) => {
259
+ if (this[plugin.ns]) throw new Error(`Plugin '${plugin.ns}' added already`)
260
+ this[plugin.ns] = plugin
261
+ if (pluginClass) this.pluginClass[plugin.ns] = pluginClass
262
+ }
263
+
57
264
  /**
58
- * Add a plugin to the app
265
+ * Get all loaded plugin namesspaces
59
266
  *
60
267
  * @method
61
- * @param {Object} plugin - A valid bajo plugin
268
+ * @returns {string[]}
62
269
  */
63
- addPlugin = (plugin) => {
64
- if (this[plugin.name]) throw new Error(`Plugin '${plugin.name}' added already`)
65
- this[plugin.name] = plugin
270
+ getAllNs = () => {
271
+ return without(keys(this.pluginClass), 'base')
66
272
  }
67
273
 
68
274
  /**
69
- * Dumping variable on screen
275
+ * Dumping variable on screen. Like ```console.log``` but with max 10 depth.
70
276
  *
71
277
  * @method
72
278
  * @param {...any} args - any arguments passed will be displayed on screen. If the last argument is a boolean 'true', app will quit rightaway
@@ -75,38 +281,156 @@ class App {
75
281
  const terminate = last(args) === true
76
282
  if (terminate) args.pop()
77
283
  for (const arg of args) {
78
- const result = util.inspect(arg, false, null, true)
284
+ const result = util.inspect(arg, { depth: 10, colors: true })
79
285
  console.log(result)
80
286
  }
81
287
  if (terminate) process.kill(process.pid, 'SIGINT')
82
288
  }
83
289
 
84
290
  /**
85
- * Booting the app
291
+ * Boot process:
292
+ *
293
+ * - Parsing {@link module:Lib.parseArgsArgv|program arguments, options} and {@link module:Lib.parseEnv|environment values}
294
+ * - Create {@link Bajo|Bajo} instance & initialize it
295
+ * - {@link module:Helper/Bajo.runAsApplet|Run in applet mode} if ```-a``` or ```--applet``` is given
296
+ *
297
+ * After boot process is completed, event ```bajo:afterBootComplete``` is emitted.
298
+ *
299
+ * If app mode is ```applet```, it runs your choosen applet instead.
86
300
  *
87
301
  * @method
88
302
  * @async
303
+ * @returns {App}
304
+ * @fires bajo:afterBootComplete
89
305
  */
90
306
  boot = async () => {
91
- // argv/args
92
- const { args, argv } = await parseArgsArgv.call(this.app) ?? {}
93
- this.argv = argv
307
+ // argv/args/env
308
+ this.bajo = new Bajo(this)
309
+ const { argv, args } = await parseArgsArgv.call(this) ?? {}
94
310
  this.args = args
95
- this.env = parseEnv() ?? {}
96
-
97
- const bajo = new Bajo(this)
98
- await buildBaseConfig.call(bajo)
99
- await buildPlugins.call(bajo)
100
- await collectConfigHandlers.call(bajo)
101
- await buildExtConfig.call(bajo)
102
- await bootOrder.call(bajo)
103
- await bootPlugins.call(bajo)
104
- await exitHandler.call(bajo)
311
+ this.argv = argv
312
+ this.envVars = parseEnv.call(this)
313
+ this.applet = this.envVars._.applet ?? this.argv._.applet
314
+ await this.bajo.init()
105
315
  // boot complete
106
- const elapsed = new Date() - bajo.runAt
107
- bajo.log.info('bootCompleted%s', bajo.lib.aneka.secToHms(elapsed, true))
108
- await bajo.runHook('bajo:afterBootComplete')
109
- if (bajo.applet) await runAsApplet.call(bajo)
316
+ const elapsed = new Date() - this.runAt
317
+ this.bajo.log.debug('bootCompleted%s', this.lib.aneka.secToHms(elapsed, true))
318
+ /**
319
+ * Run after boot process is completed
320
+ *
321
+ * @global
322
+ * @event bajo:afterBootComplete
323
+ * @see {@tutorial hook}
324
+ * @see App#boot
325
+ */
326
+ await this.bajo.runHook('bajo:afterBootComplete')
327
+ if (this.applet) await runAsApplet.call(this.bajo)
328
+ return this
329
+ }
330
+
331
+ /**
332
+ * Terminate the app and back to console
333
+ *
334
+ * @method
335
+ * @param {string} [signal=SIGINT] - Signal to send
336
+ */
337
+ exit = (signal = 'SIGINT') => {
338
+ process.kill(process.pid, 'SIGINT')
339
+ }
340
+
341
+ /**
342
+ * Load internationalization & languages files for particular plugin
343
+ *
344
+ * @method
345
+ * @param {string} ns - Plugin name
346
+ */
347
+ loadIntl = (ns) => {
348
+ this[ns].intl = {}
349
+ for (const l of this.bajo.config.intl.supported) {
350
+ this[ns].intl[l] = {}
351
+ const path = `${this[ns].dir.pkg}/extend/bajo/intl/${l}.json`
352
+ if (!fs.existsSync(path)) continue
353
+ const trans = fs.readFileSync(path, 'utf8')
354
+ try {
355
+ this[ns].intl[l] = JSON.parse(trans)
356
+ } catch (err) {}
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Translate text and interpolate with given ```args```.
362
+ *
363
+ * There is a shortcut to this method attached on all plugins. You'll normally call that shorcut
364
+ * instead of this method, because it is bound to plugin's name already
365
+ *
366
+ * ```javascript
367
+ * ... within your main plugin
368
+ * const translated = this.app.t('main', 'My cute cat is %s', 'purring')
369
+ * // or
370
+ * const translated = this.t('My cute cat is %s', 'purring')
371
+ * ```
372
+ * @method
373
+ * @param {string} ns - Namespace
374
+ * @param {string} text - Text to translate
375
+ * @param {...any} params - Arguments
376
+ * @returns {string}
377
+ */
378
+ t = (ns, text, ...params) => {
379
+ if (!text) {
380
+ text = ns
381
+ ns = 'bajo'
382
+ }
383
+ const opts = last(params)
384
+ let lang = this.bajo.config.lang
385
+ if (isPlainObject(opts)) {
386
+ params.pop()
387
+ if (opts.lang) lang = opts.lang
388
+ }
389
+ const { fallback, supported } = this.bajo.config.intl
390
+ if (!unknownLangWarning && !supported.includes(lang)) {
391
+ unknownLangWarning = true
392
+ this.bajo.log.warn(`Unsupported language, fallback to '${fallback}'`)
393
+ }
394
+ const plugins = reverse(without([...this.getAllNs()], ns))
395
+ plugins.unshift(ns)
396
+ plugins.push('bajo')
397
+
398
+ let trans
399
+ for (const p of plugins) {
400
+ const store = get(this, `${p}.intl.${lang}`, {})
401
+ trans = get(store, text)
402
+ if (trans) break
403
+ }
404
+ if (!trans) {
405
+ for (const p of plugins) {
406
+ const store = get(this, `${p}.intl.${fallback}`, {})
407
+ trans = get(store, text)
408
+ if (trans) break
409
+ }
410
+ }
411
+ if (!trans) trans = text
412
+ const values = map(params, a => {
413
+ if (!isString(a)) return a
414
+ return a
415
+ })
416
+ return sprintf(trans, ...values)
417
+ }
418
+
419
+ /**
420
+ * Helper method to list all supported config formats
421
+ *
422
+ * @returns {string[]}
423
+ */
424
+ getConfigFormats = () => {
425
+ return map(this.configHandlers, 'ext')
426
+ }
427
+
428
+ startPlugin = (ns, ...args) => {
429
+ this[ns].start(...args)
430
+ }
431
+
432
+ stopPlugin = (ns, ...args) => {
433
+ this[ns].stop(...args)
110
434
  }
111
435
  }
112
436