bajo 1.2.9 → 2.0.1

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 (166) hide show
  1. package/.jsdoc.conf.json +6 -3
  2. package/README.md +8 -148
  3. package/class/app.js +113 -0
  4. package/{boot/class/bajo-core.js → class/bajo.js} +448 -35
  5. package/{boot/class/error.js → class/base/err.js} +29 -3
  6. package/class/base/log.js +205 -0
  7. package/class/base/plugin.js +168 -0
  8. package/class/base/print.js +272 -0
  9. package/class/helper/bajo.js +344 -0
  10. package/class/helper/plugin.js +169 -0
  11. package/{boot/class/bajo-plugin.js → class/plugin.js} +60 -3
  12. package/docs/App.html +3 -0
  13. package/docs/Bajo.html +15 -0
  14. package/docs/BasePlugin.html +5 -0
  15. package/docs/Err.html +3 -0
  16. package/docs/Log.html +3 -0
  17. package/docs/Plugin.html +3 -0
  18. package/docs/Print.html +3 -0
  19. package/docs/bitcoin.jpeg +0 -0
  20. package/docs/class_app.js.html +116 -0
  21. package/docs/class_bajo.js.html +1100 -0
  22. package/docs/class_base_err.js.html +99 -0
  23. package/docs/class_base_log.js.html +208 -0
  24. package/docs/class_base_plugin.js.html +180 -0
  25. package/docs/class_base_print.js.html +275 -0
  26. package/docs/class_helper_bajo.js.html +347 -0
  27. package/docs/class_helper_plugin.js.html +172 -0
  28. package/docs/class_plugin.js.html +121 -0
  29. package/docs/data/search.json +1 -0
  30. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  31. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  32. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  33. package/docs/global.html +3 -0
  34. package/docs/index.html +3 -0
  35. package/docs/lib_create-method.js.html +42 -0
  36. package/docs/lib_formats.js.html +68 -0
  37. package/docs/lib_log-levels.js.html +28 -0
  38. package/docs/lib_resolve-path.js.html +30 -0
  39. package/docs/lib_shim.js.html +35 -0
  40. package/docs/module-class_helper_bajo.html +3 -0
  41. package/docs/module-class_helper_plugin.html +3 -0
  42. package/docs/module-lib_create-method.html +3 -0
  43. package/docs/module-lib_formats.html +3 -0
  44. package/docs/module-lib_log-levels.html +3 -0
  45. package/docs/module-lib_resolve-path.html +3 -0
  46. package/docs/module-lib_shim.html +3 -0
  47. package/docs/scripts/core.js +726 -0
  48. package/docs/scripts/core.min.js +23 -0
  49. package/docs/scripts/resize.js +90 -0
  50. package/docs/scripts/search.js +265 -0
  51. package/docs/scripts/search.min.js +6 -0
  52. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  53. package/docs/scripts/third-party/fuse.js +9 -0
  54. package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
  55. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  56. package/docs/scripts/third-party/hljs-original.js +5171 -0
  57. package/docs/scripts/third-party/hljs.js +1 -0
  58. package/docs/scripts/third-party/popper.js +5 -0
  59. package/docs/scripts/third-party/tippy.js +1 -0
  60. package/docs/scripts/third-party/tocbot.js +672 -0
  61. package/docs/scripts/third-party/tocbot.min.js +1 -0
  62. package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
  63. package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
  64. package/docs/styles/clean-jsdoc-theme-light.css +482 -0
  65. package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  66. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  67. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  68. package/docs/tutorial-contribution.html +3 -0
  69. package/docs/tutorial-ecosystem.html +3 -0
  70. package/docs/tutorial-getting-started.html +13 -0
  71. package/docs/tutorial-plugin-dev.html +3 -0
  72. package/docs/tutorial-user-guide.html +3 -0
  73. package/lib/create-method.js +39 -0
  74. package/{boot/lib → lib}/formats.js +28 -0
  75. package/{boot/lib → lib}/log-levels.js +16 -0
  76. package/{boot/lib → lib}/parse-args-argv.js +1 -1
  77. package/{boot/lib → lib}/resolve-path.js +12 -0
  78. package/{boot/lib → lib}/shim.js +10 -0
  79. package/misc-docs/bitcoin.jpeg +0 -0
  80. package/misc-docs/contribution.md +20 -0
  81. package/{docs → misc-docs}/ecosystem.md +41 -12
  82. package/misc-docs/getting-started.md +142 -0
  83. package/misc-docs/plugin-dev.md +0 -0
  84. package/misc-docs/toc.json +17 -0
  85. package/misc-docs/user-guide.md +1 -0
  86. package/package.json +14 -13
  87. package/bajoBook/book/doc/.metadata.json +0 -28
  88. package/bajoBook/book/doc/How-to-Make-a-Paper-Boat-564x400@2x.jpg +0 -0
  89. package/bajoBook/book/doc/pages/guides/definition.md +0 -7
  90. package/bajoBook/book/doc/pages/guides/intro.md +0 -3
  91. package/bajoBook/book/doc/pages/guides/setup.md +0 -22
  92. package/bajoBook/book/reference/.metadata.json +0 -152
  93. package/bajoBook/book/reference/concept-leadership-business-with-paper-boats.jpg +0 -0
  94. package/bajoBook/book/reference/pages/configuration/configuration-file.md +0 -52
  95. package/bajoBook/book/reference/pages/helper/break-ns-path.md +0 -24
  96. package/bajoBook/book/reference/pages/helper/build-collections.md +0 -19
  97. package/bajoBook/book/reference/pages/helper/call-helper-or-handler.md +0 -35
  98. package/bajoBook/book/reference/pages/helper/current-loc.md +0 -28
  99. package/bajoBook/book/reference/pages/helper/dayjs.md +0 -13
  100. package/bajoBook/book/reference/pages/helper/defaults-deep.md +0 -29
  101. package/bajoBook/book/reference/pages/helper/dump.md +0 -32
  102. package/bajoBook/book/reference/pages/helper/each-plugins.md +0 -24
  103. package/bajoBook/book/reference/pages/helper/envs.md +0 -11
  104. package/bajoBook/book/reference/pages/helper/error.md +0 -29
  105. package/bajoBook/book/reference/pages/helper/fatal.md +0 -18
  106. package/bajoBook/book/reference/pages/helper/freeze.md +0 -13
  107. package/bajoBook/book/reference/pages/helper/generate-id.md +0 -36
  108. package/bajoBook/book/reference/pages/helper/get-config.md +0 -27
  109. package/bajoBook/book/reference/pages/helper/get-global-module-dir.md +0 -13
  110. package/bajoBook/book/reference/pages/helper/get-helper.md +0 -13
  111. package/bajoBook/book/reference/pages/helper/get-item-by-name.md +0 -13
  112. package/bajoBook/book/reference/pages/helper/get-key-by-value.md +0 -13
  113. package/bajoBook/book/reference/pages/helper/get-module-dir.md +0 -13
  114. package/bajoBook/book/reference/pages/helper/get-plugin-data-dir.md +0 -13
  115. package/bajoBook/book/reference/pages/helper/get-plugin-name.md +0 -13
  116. package/bajoBook/book/reference/pages/helper/get-plugin.md +0 -13
  117. package/bajoBook/book/reference/pages/helper/import-module.md +0 -13
  118. package/bajoBook/book/reference/pages/helper/import-pkg.md +0 -13
  119. package/bajoBook/book/reference/pages/helper/is-empty-dir.md +0 -13
  120. package/bajoBook/book/reference/pages/helper/is-log-in-range.md +0 -13
  121. package/bajoBook/book/reference/pages/helper/is-set.md +0 -13
  122. package/bajoBook/book/reference/pages/helper/is-valid-app.md +0 -13
  123. package/bajoBook/book/reference/pages/helper/is-valid-plugin.md +0 -13
  124. package/bajoBook/book/reference/pages/helper/log-levels.md +0 -13
  125. package/bajoBook/book/reference/pages/helper/log.md +0 -13
  126. package/bajoBook/book/reference/pages/helper/paginate.md +0 -13
  127. package/bajoBook/book/reference/pages/helper/pascal-case.md +0 -13
  128. package/bajoBook/book/reference/pages/helper/print.md +0 -13
  129. package/bajoBook/book/reference/pages/helper/read-config.md +0 -13
  130. package/bajoBook/book/reference/pages/helper/read-json.md +0 -13
  131. package/bajoBook/book/reference/pages/helper/resolve-path.md +0 -13
  132. package/bajoBook/book/reference/pages/helper/resolve-tpl-path.md +0 -13
  133. package/bajoBook/book/reference/pages/helper/run-hook.md +0 -13
  134. package/bajoBook/book/reference/pages/helper/save-as-download.md +0 -13
  135. package/bajoBook/book/reference/pages/helper/titleize.md +0 -13
  136. package/bajoBook/book/reference/pages/helper/white-space.md +0 -13
  137. package/boot/class/app.js +0 -67
  138. package/boot/class/bajo-core/boot-order.js +0 -35
  139. package/boot/class/bajo-core/boot-plugins.js +0 -17
  140. package/boot/class/bajo-core/build-config.js +0 -86
  141. package/boot/class/bajo-core/build-plugins.js +0 -44
  142. package/boot/class/bajo-core/collect-config-handlers.js +0 -20
  143. package/boot/class/bajo-core/exit-handler.js +0 -53
  144. package/boot/class/bajo-core/run-as-applet.js +0 -26
  145. package/boot/class/bajo-plugin/attach-method.js +0 -14
  146. package/boot/class/bajo-plugin/build-config.js +0 -15
  147. package/boot/class/bajo-plugin/check-clash.js +0 -18
  148. package/boot/class/bajo-plugin/check-dependency.js +0 -39
  149. package/boot/class/bajo-plugin/collect-hooks.js +0 -31
  150. package/boot/class/bajo-plugin/run.js +0 -23
  151. package/boot/class/log.js +0 -96
  152. package/boot/class/plugin.js +0 -79
  153. package/boot/class/print.js +0 -153
  154. package/boot/lib/create-method.js +0 -33
  155. package/test/method/isSet.js +0 -43
  156. /package/{bajo → extend/bajo}/intl/en-US.json +0 -0
  157. /package/{bajo → extend/bajo}/intl/id.json +0 -0
  158. /package/{waibuStatic → extend/waibuStatic}/virtual.json +0 -0
  159. /package/{boot/index.js → index.js} +0 -0
  160. /package/{boot/lib → lib}/current-loc.js +0 -0
  161. /package/{boot/lib → lib}/dayjs.js +0 -0
  162. /package/{boot/lib → lib}/import-module.js +0 -0
  163. /package/{boot/lib → lib}/omitted-plugin-keys.js +0 -0
  164. /package/{boot/lib → lib}/parse-env.js +0 -0
  165. /package/{boot/lib → lib}/read-all-configs.js +0 -0
  166. /package/{docs/hook.md → misc-docs/.hook.md} +0 -0
@@ -3,7 +3,16 @@ const { isPlainObject, each, isArray, get, isEmpty, merge } = lodash
3
3
 
4
4
  Error.stackTraceLimit = 15
5
5
 
6
- class BajoError {
6
+ /**
7
+ * Bajo error class, a thin wrapper of node's Error object. Use this name instead of Error
8
+ * because, of course, Error is a reserved keyword in node.
9
+ */
10
+ class Err {
11
+ /**
12
+ * @param {Object} plugin - Plugin instance
13
+ * @param {string} msg - Error message
14
+ * @param {...any} [args] - Variables to interpolate with error message. Payload object can be pushed as the very last argument
15
+ */
7
16
  constructor (plugin, msg, ...args) {
8
17
  this.plugin = plugin
9
18
  this.payload = args.length > 0 && isPlainObject(args[args.length - 1]) ? args[args.length - 1] : {}
@@ -12,6 +21,12 @@ class BajoError {
12
21
  this.write()
13
22
  }
14
23
 
24
+ /**
25
+ * Create the error object
26
+ *
27
+ * @method
28
+ * @returns {Object} Error object
29
+ */
15
30
  write = () => {
16
31
  let err
17
32
  if (this.payload.class) err = this.payload.class(this.message)
@@ -36,17 +51,28 @@ class BajoError {
36
51
  return err
37
52
  }
38
53
 
54
+ /**
55
+ * Print error object on screen and terminate app process
56
+ *
57
+ * @method
58
+ */
39
59
  fatal = () => {
40
60
  const err = this.write()
41
61
  console.error(err)
42
62
  process.kill(process.pid, 'SIGINT')
43
63
  }
44
64
 
65
+ /**
66
+ * Pretty format error details
67
+ *
68
+ * @method
69
+ * @param {Object} value - Value to format
70
+ * @returns {Object}
71
+ */
45
72
  formatErrorDetails = (value) => {
46
73
  const { isString } = this.plugin.app.bajo.lib._
47
74
  const result = {}
48
75
  const me = this
49
- this.plugin.app.dump(value)
50
76
  each(value, (v, i) => {
51
77
  const print = me.plugin.print
52
78
  if (isString(v)) v = { error: v }
@@ -67,4 +93,4 @@ class BajoError {
67
93
  }
68
94
  }
69
95
 
70
- export default BajoError
96
+ export default Err
@@ -0,0 +1,205 @@
1
+ import os from 'os'
2
+ import lodash from 'lodash'
3
+ import dayjs from 'dayjs'
4
+ import logLevels from '../../lib/log-levels.js'
5
+ import chalk from 'chalk'
6
+
7
+ const { isEmpty, without, merge } = lodash
8
+
9
+ export function isIgnored (level) {
10
+ const { filter, isArray } = this.lib._
11
+ let ignore = this.app.bajo.config.log.ignore ?? []
12
+ if (!isArray(ignore)) ignore = [ignore]
13
+ const items = filter(ignore, i => {
14
+ const [ns, lvl] = i.split(':')
15
+ if (lvl) return ns === this.name && lvl === level
16
+ return ns === this.name
17
+ })
18
+ return items.length > 0
19
+ }
20
+
21
+ /**
22
+ * A thin logger system.
23
+ *
24
+ * @class
25
+ */
26
+ class Log {
27
+ /**
28
+ * @param {Object} plugin - Plugin instance
29
+ */
30
+ constructor (plugin) {
31
+ this.plugin = plugin
32
+ this.format = 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'
33
+ }
34
+
35
+ /**
36
+ * Initialize logger. Auto detect to use different logger via Bajo's config file
37
+ *
38
+ * @method
39
+ */
40
+ init = () => {
41
+ this.bajoLog = this.plugin.app.bajo.config.log.logger ?? 'bajoLogger'
42
+ }
43
+
44
+ /**
45
+ * Interpolate and translate text via plugin's print engine. Check Print class
46
+ * for more information
47
+ *
48
+ * @method
49
+ * @param {string} text - Text pattern to use
50
+ * @param {...any} [args] - Variables to interpolate with text pattern above
51
+ * @returns {string} Interpolated & translated text
52
+ */
53
+ write = (text, ...args) => {
54
+ return this.plugin.print.write(text, ...args)
55
+ }
56
+
57
+ /**
58
+ * Do we use external logger or Bajo's built-in one?
59
+ *
60
+ * @method
61
+ * @returns {boolean}
62
+ */
63
+ isExtLogger = () => {
64
+ return !!(this.plugin.app[this.bajoLog] && this.plugin.app[this.bajoLog].logger)
65
+ }
66
+
67
+ /**
68
+ * Is provided level being ignored by config?
69
+ *
70
+ * @method
71
+ * @param {string} level - Log level
72
+ * @returns {boolean}
73
+ */
74
+ isIgnored = level => {
75
+ return isIgnored.call(this.plugin, level)
76
+ }
77
+
78
+ /**
79
+ * Create child logger
80
+ *
81
+ * @method
82
+ * @returns {Object} Child logger instance
83
+ */
84
+ child = () => {
85
+ if (this.isExtLogger()) return this.plugin.app[this.bajoLog].logger.child()
86
+ return this.plugin.app
87
+ }
88
+
89
+ /**
90
+ * Display & format message according to one of these rules:
91
+ * 1. ```level``` ```text``` ```var 1``` ```var 2``` ```...var n``` - Translate ```text``` and interpolate with ```vars``` for level ```level```
92
+ * 2. ```level``` ```data``` ```text``` ```var 1``` ```var 2``` ```...var n``` - As above, and append stringified ```data```
93
+ * 3. ```level``` ```error``` - Format as **error**. If current log level is _trace_, dump the error object on screen
94
+ *
95
+ * @method
96
+ * @param {string} level - Log level to use
97
+ * @param {...any} params - See format above
98
+ */
99
+ formatMsg = (level, ...params) => {
100
+ if (this.plugin.app.bajo.config.log.level === 'silent') return
101
+ if (!this.plugin.app.bajo.isLogInRange(level)) return
102
+ const plain = this.plugin.app.bajo.config.log.plain
103
+ let [data, msg, ...args] = params
104
+ if (typeof data === 'string') {
105
+ args.unshift(msg)
106
+ msg = data
107
+ data = null
108
+ }
109
+ args = without(args, undefined)
110
+ if (data instanceof Error) {
111
+ msg = 'error%s'
112
+ args = [data.message]
113
+ }
114
+ msg = this.write(msg, ...args)
115
+ if (this.plugin.app[this.bajoLog] && this.plugin.app[this.bajoLog].logger) {
116
+ this.plugin.app[this.bajoLog].logger[level](data, `[${this.plugin.name}] ${msg}`, ...args)
117
+ } else {
118
+ let text
119
+ const dt = new Date()
120
+ if (this.plugin.app.bajo.config.env === 'prod') {
121
+ const json = { level: logLevels[level].number, time: dt.valueOf(), pid: process.pid, hostname: os.hostname() }
122
+ if (!isEmpty(data)) merge(json, data)
123
+ merge(json, { msg: `[${this.plugin.name}] ${msg}` })
124
+ text = JSON.stringify(json)
125
+ } else {
126
+ const date = dayjs(dt).utc(true).format(this.format)
127
+ const tdate = plain ? `[${date}]` : chalk.cyan(date)
128
+ const tlevel = plain ? level.toUpperCase() : chalk[logLevels[level].color](level.toUpperCase())
129
+ const tplugin = plain ? `[${this.plugin.name}]` : chalk.bgBlue(`${this.plugin.name}`)
130
+ text = `${tdate} ${tlevel}: ${tplugin} ${msg}`
131
+ if (!isEmpty(data)) text += '\n' + JSON.stringify(data)
132
+ }
133
+ if (!this.isIgnored(level)) {
134
+ console.log(text)
135
+ if (data instanceof Error && level === 'trace') console.error(data)
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Display & format message as ```trace``` level. See {@link Log#formatMsg|formatMsg} for details
142
+ *
143
+ * @method
144
+ * @param {...any} params
145
+ */
146
+ trace = (...params) => {
147
+ this.formatMsg('trace', ...params)
148
+ }
149
+
150
+ /**
151
+ * Display & format message as ```debug``` level. See {@link Log#formatMsg|formatMsg} for details
152
+ *
153
+ * @method
154
+ * @param {...any} params
155
+ */
156
+ debug = (...params) => {
157
+ this.formatMsg('debug', ...params)
158
+ }
159
+
160
+ /**
161
+ * Display & format message as ```info``` level. See {@link Log#formatMsg|formatMsg} for details
162
+ *
163
+ * @method
164
+ * @param {...any} params
165
+ */
166
+ info = (...params) => {
167
+ this.formatMsg('info', ...params)
168
+ }
169
+
170
+ /**
171
+ * Display & format message as ```warn``` level. See {@link Log#formatMsg|formatMsg} for details
172
+ *
173
+ * @method
174
+ * @param {...any} params
175
+ */
176
+ warn = (...params) => {
177
+ this.formatMsg('warn', ...params)
178
+ }
179
+
180
+ /**
181
+ * Display & format message as ```error``` level. See {@link Log#formatMsg|formatMsg} for details
182
+ *
183
+ * @method
184
+ * @param {...any} params
185
+ */
186
+ error = (...params) => {
187
+ this.formatMsg('error', ...params)
188
+ }
189
+
190
+ /**
191
+ * Display & format message as ```fatal``` level. See {@link Log#formatMsg|formatMsg} for details
192
+ *
193
+ * @method
194
+ * @param {...any} params
195
+ */
196
+ fatal = (...params) => {
197
+ this.formatMsg('fatal', ...params)
198
+ }
199
+
200
+ silent = (...params) => {
201
+ this.formatMsg('silent', ...params)
202
+ }
203
+ }
204
+
205
+ export default Log
@@ -0,0 +1,168 @@
1
+ import lodash from 'lodash'
2
+ import omittedPluginKeys from '../../lib/omitted-plugin-keys.js'
3
+ import Log from './log.js'
4
+ import Print from './print.js'
5
+ import Err from './err.js'
6
+ import fastGlob from 'fast-glob'
7
+ import { sprintf } from 'sprintf-js'
8
+ import outmatch from 'outmatch'
9
+ import dayjs from '../../lib/dayjs.js'
10
+ import fs from 'fs-extra'
11
+ import aneka from 'aneka/index.js'
12
+
13
+ function outmatchNs (source, pattern) {
14
+ const { breakNsPath } = this.app.bajo
15
+ const [src, subSrc] = source.split(':')
16
+ if (!subSrc) return pattern === src
17
+ try {
18
+ const { fullNs, path } = breakNsPath(pattern)
19
+ const isMatch = outmatch(path)
20
+ return src === fullNs && isMatch(subSrc)
21
+ } catch (err) {
22
+ return false
23
+ }
24
+ }
25
+
26
+ const lib = {
27
+ _: lodash,
28
+ fs,
29
+ fastGlob,
30
+ sprintf,
31
+ outmatch,
32
+ dayjs,
33
+ aneka
34
+ }
35
+
36
+ const { get, isEmpty, cloneDeep, omit, isPlainObject, camelCase } = lodash
37
+
38
+ /**
39
+ * This is the base class of bajo's plugin system.
40
+ *
41
+ * @class
42
+ */
43
+ class BasePlugin {
44
+ /**
45
+ * @param {string} pkgName - Package name (the one you use in package.json)
46
+ * @param {Object} app - App instance reference. Usefull to call app method inside a plugin
47
+ */
48
+ constructor (pkgName, app) {
49
+ /**
50
+ * Package name, the one from package.json
51
+ * @type {string}
52
+ */
53
+ this.pkgName = pkgName
54
+
55
+ /**
56
+ * Plugin name. Simply the camel cased version of plugin's package name
57
+ *
58
+ * @type {string}
59
+ */
60
+ this.name = camelCase(pkgName)
61
+
62
+ /**
63
+ * Reference to app instance
64
+ *
65
+ * @type {Object}
66
+ */
67
+ this.app = app
68
+
69
+ /**
70
+ * Config object
71
+ *
72
+ * @type {Object}
73
+ */
74
+ this.config = {}
75
+
76
+ /**
77
+ * Property to give you direct access to the most commonly used 3rd party library in a bajo based app.
78
+ * No manual import necessary, always available, everywhere, anytime!
79
+ *
80
+ * Example:
81
+ * ```javascript
82
+ * const { camelCase, kebabCase } = this.lib._
83
+ * console.log(camelCase('Elit commodo sit et aliqua'))
84
+ * ```
85
+ *
86
+ * @type {Object}
87
+ * @property {Object} _ - Access to {@link https://lodash.com|lodash}
88
+ * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
89
+ * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
90
+ * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
91
+ * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
92
+ * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
93
+ * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
94
+ */
95
+ this.lib = lib
96
+ this.lib.outmatchNs = outmatchNs.bind(this)
97
+ }
98
+
99
+ /**
100
+ * Get plugin's config value
101
+ *
102
+ * @method
103
+ * @param {string} [path] - dot separated config path (think of lodash's 'get'). If not provided, the full config will be given
104
+ * @param {Object} [options={}] - Options
105
+ * @param {any} [options.defValue={}] - Default value to use if returned object is undefined
106
+ * @param {string[]} [options.omit=[]] - Omit these keys from returned object
107
+ * @param {boolean} [options.noClone=false] - Set true to NOT clone returned object
108
+ * @returns {Object} Returned object. If no path provided, the whole config object is returned
109
+ */
110
+ getConfig = (path, options = {}) => {
111
+ let obj = isEmpty(path) ? this.config : get(this.config, path, options.defValue ?? {})
112
+ options.omit = options.omit ?? omittedPluginKeys
113
+ if (isPlainObject(obj) && !isEmpty(options.omit)) obj = omit(obj, options.omit)
114
+ if (!options.noClone) obj = cloneDeep(obj)
115
+ return obj
116
+ }
117
+
118
+ /**
119
+ * Initialize log. Please refer to {@link Log} class for more info
120
+ *
121
+ * @method
122
+ */
123
+ initLog = () => {
124
+ this.log = new Log(this)
125
+ this.log.init()
126
+ }
127
+
128
+ /**
129
+ * Initialize print engine. Please refer to {@link Print} class for more info
130
+ *
131
+ * @method
132
+ * @param {Object} [options] - Print options
133
+ */
134
+ initPrint = (options) => {
135
+ this.print = new Print(this, options)
136
+ this.print.init()
137
+ }
138
+
139
+ /**
140
+ * Create an instance of {@link Err} object
141
+ *
142
+ * @method
143
+ * @param {msg} msg - Error message
144
+ * @param {...any} [args] - Argument variables you might want to add to the error object
145
+ * @returns {Object} Err instance
146
+ */
147
+ error = (msg, ...args) => {
148
+ if (!this.print) return new Error(msg, ...args)
149
+ const error = new Err(this, msg, ...args)
150
+ return error.write()
151
+ }
152
+
153
+ /**
154
+ * Create an instance of Err object, display it on screen and then force
155
+ * terminate the app process
156
+ *
157
+ * @method
158
+ * @param {msg} msg - Error message
159
+ * @param {...any} [args] - Argument variables you might want to add to the error object
160
+ */
161
+ fatal = (msg, ...args) => {
162
+ if (!this.print) return new Error(msg, ...args)
163
+ const error = new Err(this, msg, ...args)
164
+ error.fatal()
165
+ }
166
+ }
167
+
168
+ export default BasePlugin
@@ -0,0 +1,272 @@
1
+ import ora from 'ora'
2
+ import lodash from 'lodash'
3
+ import fs from 'fs-extra'
4
+ import Sprintf from 'sprintf-js'
5
+ const { sprintf } = Sprintf
6
+ let unknownLangWarning = false
7
+
8
+ const { isString, last, isPlainObject, get, without, reverse, map } = lodash
9
+
10
+ /**
11
+ * Print class. Use sprintf to interpolate pattern and variable. Support
12
+ * many methods to display things on screen including {@link https://github.com/sindresorhus/ora|ora} based spinner.
13
+ *
14
+ * It also serve as the foundation of Bajo's I18n lightweight system.
15
+ *
16
+ * @class
17
+ */
18
+ class Print {
19
+ /**
20
+ * Class constructor
21
+ *
22
+ * @param {Object} plugin - Plugin instance
23
+ * @param {Object} [opts={}] - Options to pass to {@link https://github.com/sindresorhus/ora|ora}
24
+ */
25
+ constructor (plugin, opts = {}) {
26
+ this.opts = opts
27
+ this.plugin = plugin
28
+ this.startTime = this.plugin.app.bajo.lib.dayjs()
29
+ this.setOpts()
30
+ this.ora = ora(this.opts)
31
+ this.intl = {}
32
+ }
33
+
34
+ /**
35
+ * Initialize print engine and read plugin's translation files
36
+ *
37
+ * @method
38
+ */
39
+ init = () => {
40
+ for (const l of this.plugin.app.bajo.config.intl.supported) {
41
+ this.intl[l] = {}
42
+ const path = `${this.plugin.dir.pkg}/extend/bajo/intl/${l}.json`
43
+ if (!fs.existsSync(path)) continue
44
+ const trans = fs.readFileSync(path, 'utf8')
45
+ try {
46
+ this.intl[l] = JSON.parse(trans)
47
+ } catch (err) {}
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Interpolate and translate text according to the chosen language
53
+ *
54
+ * @method
55
+ * @param {string} text - Text pattern to translate. See {@link https://github.com/alexei/sprintf.js|sprintf} for all supported token & format
56
+ * @param {...any} [args] - Variables to interpolate with text pattern above. If the last argument is an object, it will be use to override default translation option. Example: to force language to 'id', pass the last argument as "{ lang: 'id' }"
57
+ * @returns {string} Interpolated & translated text
58
+ */
59
+ write = (text, ...args) => {
60
+ const opts = last(args)
61
+ let lang = this.plugin.app.bajo.config.lang
62
+ if (isPlainObject(opts)) {
63
+ args.pop()
64
+ if (opts.lang) lang = opts.lang
65
+ }
66
+ const { fallback, supported } = this.plugin.app.bajo.config.intl
67
+ if (!unknownLangWarning && !supported.includes(lang)) {
68
+ unknownLangWarning = true
69
+ this.plugin.app.bajo.log.warn('unsupportedLangFallbackTo%s', fallback)
70
+ }
71
+ const plugins = reverse(without([...this.plugin.app.bajo.pluginNames], this.plugin.name))
72
+ plugins.unshift(this.plugin.name)
73
+ plugins.push('bajo')
74
+
75
+ let trans
76
+ for (const p of plugins) {
77
+ const root = get(this, `plugin.app.${p}.print.intl.${lang}`, {})
78
+ trans = get(root, text)
79
+ if (trans) break
80
+ }
81
+ if (!trans) {
82
+ for (const p of plugins) {
83
+ const root = get(this, `plugin.app.${p}.print.intl.${fallback}`, {})
84
+ trans = get(root, text)
85
+ if (trans) break
86
+ }
87
+ }
88
+ if (!trans) trans = text
89
+ const params = map(args, a => {
90
+ if (!isString(a)) return a
91
+ return a
92
+ })
93
+ return sprintf(trans, ...params)
94
+ }
95
+
96
+ /**
97
+ * Set spinner options
98
+ *
99
+ * @method
100
+ * @param {any[]} [args=[]] - Array of options. If the last argument is an object, it will be used to override ora options
101
+ */
102
+ setOpts = (args = []) => {
103
+ const config = this.plugin.app.bajo.config
104
+ let opts = {}
105
+ if (isPlainObject(args.slice(-1)[0])) opts = args.pop()
106
+ this.opts.isSilent = !!(config.silent || this.opts.isSilent)
107
+ this.opts = this.plugin.lib.aneka.defaultsDeep(opts, this.opts)
108
+ }
109
+
110
+ /**
111
+ * Set spinner text
112
+ *
113
+ * @method
114
+ * @param {string} text - Text to use
115
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
116
+ * @returns {Object} Return itself, usefull to chain methods
117
+ */
118
+ setText = (text, ...args) => {
119
+ text = this.write(text, ...args)
120
+ this.setOpts(args)
121
+ const prefixes = []
122
+ const texts = []
123
+ if (this.opts.showDatetime) prefixes.push('[' + this.plugin.app.bajo.lib.dayjs().toISOString() + ']')
124
+ if (this.opts.showCounter) texts.push('[' + this.getElapsed() + ']')
125
+ if (prefixes.length > 0) this.ora.prefixText = this.ora.prefixText + prefixes.join(' ')
126
+ if (texts.length > 0) text = texts.join(' ') + ' ' + text
127
+ this.ora.text = text
128
+ return this
129
+ }
130
+
131
+ /**
132
+ * Get elapsed time since print instance is created
133
+ *
134
+ * @method
135
+ * @param {string} [unit=hms] - Unit's time. Put 'hms' (default) to get hour, minute, second format or of any format supported by {@link https://day.js.org/docs/en/display/difference|dayjs}
136
+ * @returns {string} Elapsed time since start
137
+ */
138
+ getElapsed = (unit = 'hms') => {
139
+ const u = unit === 'hms' ? 'second' : unit
140
+ const elapsed = this.plugin.lib.dayjs().diff(this.startTime, u)
141
+ return unit === 'hms' ? this.plugin.lib.aneka.secToHms(elapsed) : elapsed
142
+ }
143
+
144
+ /**
145
+ * Start the spinner
146
+ *
147
+ * @method
148
+ * @param {string} text - Text to use
149
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
150
+ * @returns {Object} Return itself, usefull to chain methods
151
+ */
152
+ start = (text, ...args) => {
153
+ this.setOpts(args)
154
+ this.setText(text, ...args)
155
+ this.ora.start()
156
+ return this
157
+ }
158
+
159
+ /**
160
+ * Stop the spinner
161
+ *
162
+ * @method
163
+ * @returns {Object} Return itself, usefull to chain methods
164
+ */
165
+ stop = () => {
166
+ this.ora.stop()
167
+ return this
168
+ }
169
+
170
+ /**
171
+ * Print success message, prefixed with a check icon
172
+ *
173
+ * @method
174
+ * @param {string} text - Text to use
175
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
176
+ * @returns {Object} Return itself, usefull to chain methods
177
+ */
178
+ succeed = (text, ...args) => {
179
+ this.setText(text, ...args)
180
+ this.ora.succeed()
181
+ return this
182
+ }
183
+
184
+ /**
185
+ * Print failed message, prefixed with a cross icon
186
+ *
187
+ * @method
188
+ * @param {string} text - Text to use
189
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
190
+ * @returns {Object} Return itself, usefull to chain methods
191
+ */
192
+ fail = (text, ...args) => {
193
+ this.setText(text, ...args)
194
+ this.ora.fail()
195
+ return this
196
+ }
197
+
198
+ /**
199
+ * Print warning message, prefixed with a warn icon
200
+ *
201
+ * @method
202
+ * @param {string} text - Text to use
203
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
204
+ * @returns {Object} Return itself, usefull to chain methods
205
+ */
206
+ warn = (text, ...args) => {
207
+ this.setText(text, ...args)
208
+ this.ora.warn()
209
+ return this
210
+ }
211
+
212
+ /**
213
+ * Print failed message, prefixed with an info icon
214
+ *
215
+ * @method
216
+ * @param {string} text - Text to use
217
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
218
+ * @returns {Object} Return itself, usefull to chain methods
219
+ */
220
+ info = (text, ...args) => {
221
+ this.setText(text, ...args)
222
+ this.ora.info()
223
+ return this
224
+ }
225
+
226
+ /**
227
+ * Clear spinner text
228
+ *
229
+ * @method
230
+ * @returns {Object} Return itself, usefull to chain methods
231
+ */
232
+ clear = () => {
233
+ this.ora.clear()
234
+ return this
235
+ }
236
+
237
+ /**
238
+ * Force render spinner
239
+ *
240
+ * @method
241
+ * @returns {Object} Return itself, usefull to chain methods
242
+ */
243
+ render = () => {
244
+ this.ora.render()
245
+ return this
246
+ }
247
+
248
+ /**
249
+ * Print failed message, prefixed with a cross icon and terminate the app process
250
+ *
251
+ * @method
252
+ * @param {string} text - Text to use
253
+ * @param {...any} [args] - Any variable to interpolate text. If the last argument is an object, it will be used to override ora options
254
+ */
255
+ fatal = (text, ...args) => {
256
+ this.setText(text, ...args)
257
+ this.ora.fail()
258
+ process.kill(process.pid, 'SIGINT')
259
+ }
260
+
261
+ /**
262
+ * Create a new spinner
263
+ *
264
+ * @method
265
+ * @returns {Object} Return new instance
266
+ */
267
+ spinner = () => {
268
+ return new Print(this.plugin)
269
+ }
270
+ }
271
+
272
+ export default Print