bajo 0.3.9 → 1.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 (162) hide show
  1. package/README.md +127 -26
  2. package/bajoI18N/resource/id.json +31 -0
  3. package/boot/class/app.js +69 -0
  4. package/boot/class/bajo-core/helper/attach-method.js +30 -0
  5. package/boot/{boot-order.js → class/bajo-core/helper/boot-order.js} +10 -10
  6. package/boot/class/bajo-core/helper/boot-plugins.js +19 -0
  7. package/boot/class/bajo-core/helper/build-config.js +61 -0
  8. package/boot/class/bajo-core/helper/build-plugins.js +26 -0
  9. package/boot/class/bajo-core/helper/collect-config-handlers.js +21 -0
  10. package/boot/{exit-handler.js → class/bajo-core/helper/exit-handler.js} +10 -16
  11. package/boot/class/bajo-core/helper/run-as-applet.js +26 -0
  12. package/boot/class/bajo-core/method/arrange-array.js +18 -0
  13. package/boot/class/bajo-core/method/break-ns-path-from-file.js +24 -0
  14. package/boot/class/bajo-core/method/break-ns-path.js +29 -0
  15. package/boot/class/bajo-core/method/build-collections.js +46 -0
  16. package/boot/class/bajo-core/method/call-handler.js +29 -0
  17. package/boot/{helper → class/bajo-core/method}/defaults-deep.js +1 -1
  18. package/boot/class/bajo-core/method/each-plugins.js +55 -0
  19. package/boot/class/bajo-core/method/envs.js +7 -0
  20. package/boot/{helper → class/bajo-core/method}/generate-id.js +4 -0
  21. package/boot/{helper → class/bajo-core/method}/get-global-module-dir.js +3 -4
  22. package/boot/class/bajo-core/method/get-method.js +10 -0
  23. package/boot/{helper → class/bajo-core/method}/get-module-dir.js +3 -4
  24. package/boot/{helper → class/bajo-core/method}/get-plugin-data-dir.js +2 -3
  25. package/boot/class/bajo-core/method/get-plugin-file.js +18 -0
  26. package/boot/class/bajo-core/method/get-plugin.js +23 -0
  27. package/boot/{helper → class/bajo-core/method}/import-module.js +4 -10
  28. package/boot/{helper → class/bajo-core/method}/import-pkg.js +9 -3
  29. package/boot/class/bajo-core/method/includes.js +13 -0
  30. package/boot/class/bajo-core/method/is-class.js +7 -0
  31. package/boot/{helper → class/bajo-core/method}/is-log-in-range.js +1 -1
  32. package/boot/{helper → class/bajo-core/method}/is-valid-app.js +2 -2
  33. package/boot/class/bajo-core/method/join.js +15 -0
  34. package/boot/class/bajo-core/method/log-levels.js +9 -0
  35. package/boot/class/bajo-core/method/num-unit.js +9 -0
  36. package/boot/{helper → class/bajo-core/method}/parse-object.js +24 -8
  37. package/boot/class/bajo-core/method/read-config.js +48 -0
  38. package/boot/{helper → class/bajo-core/method}/read-json.js +2 -2
  39. package/boot/class/bajo-core/method/run-hook.js +26 -0
  40. package/boot/{helper → class/bajo-core/method}/save-as-download.js +2 -2
  41. package/boot/class/bajo-core/method/slice-string.js +13 -0
  42. package/boot/class/bajo-core/method/titleize.js +23 -0
  43. package/boot/class/bajo-core.js +30 -0
  44. package/boot/class/bajo-plugin/helper/attach-method.js +14 -0
  45. package/boot/class/bajo-plugin/helper/build-config.js +10 -0
  46. package/boot/class/bajo-plugin/helper/check-clash.js +16 -0
  47. package/boot/class/bajo-plugin/helper/check-dependency.js +37 -0
  48. package/boot/class/bajo-plugin/helper/collect-exit-handlers.js +14 -0
  49. package/boot/class/bajo-plugin/helper/collect-hooks.js +29 -0
  50. package/boot/class/bajo-plugin/helper/run.js +20 -0
  51. package/boot/class/bajo-plugin.js +78 -0
  52. package/boot/class/error.js +59 -0
  53. package/boot/class/log.js +67 -0
  54. package/boot/class/plugin.js +51 -0
  55. package/boot/class/print.js +103 -0
  56. package/boot/index.js +4 -51
  57. package/boot/lib/create-method.js +30 -0
  58. package/boot/{helper → lib}/current-loc.js +1 -1
  59. package/boot/lib/omitted-plugin-keys.js +1 -1
  60. package/boot/lib/parse-args-argv.js +8 -11
  61. package/boot/lib/parse-env.js +7 -6
  62. package/boot/lib/read-all-configs.js +36 -0
  63. package/boot/lib/translate.js +18 -0
  64. package/docs/ecosystem.md +55 -0
  65. package/docs/hook.md +11 -0
  66. package/package.json +55 -56
  67. package/test/{helper-isSet.js → method/isSet.js} +6 -4
  68. package/waibuStatic/virtual.json +7 -0
  69. package/boot/attach-helper.js +0 -40
  70. package/boot/build-config.js +0 -99
  71. package/boot/create-scope.js +0 -36
  72. package/boot/helper/break-ns-path.js +0 -20
  73. package/boot/helper/build-collections.js +0 -44
  74. package/boot/helper/build-name.js +0 -17
  75. package/boot/helper/call-helper-or-handler.js +0 -15
  76. package/boot/helper/dump.js +0 -14
  77. package/boot/helper/each-plugins.js +0 -106
  78. package/boot/helper/envs.js +0 -14
  79. package/boot/helper/error.js +0 -77
  80. package/boot/helper/fatal.js +0 -13
  81. package/boot/helper/get-config.js +0 -19
  82. package/boot/helper/get-helper.js +0 -12
  83. package/boot/helper/get-item-by-name.js +0 -26
  84. package/boot/helper/get-plugin-file.js +0 -16
  85. package/boot/helper/get-plugin-name.js +0 -39
  86. package/boot/helper/get-plugin.js +0 -14
  87. package/boot/helper/join.js +0 -11
  88. package/boot/helper/log-levels.js +0 -19
  89. package/boot/helper/read-config.js +0 -48
  90. package/boot/helper/resolve-tpl-path.js +0 -15
  91. package/boot/helper/run-hook.js +0 -49
  92. package/boot/helper/spinner.js +0 -9
  93. package/boot/helper/start-plugin.js +0 -7
  94. package/boot/helper/titleize.js +0 -14
  95. package/boot/lib/build-helper.js +0 -60
  96. package/boot/lib/logger.js +0 -76
  97. package/boot/lib/print.js +0 -138
  98. package/boot/plugins/attach-helper.js +0 -20
  99. package/boot/plugins/build-config.js +0 -68
  100. package/boot/plugins/check-clash.js +0 -18
  101. package/boot/plugins/check-dependency.js +0 -37
  102. package/boot/plugins/collect-config-handlers.js +0 -22
  103. package/boot/plugins/collect-exit-handlers.js +0 -18
  104. package/boot/plugins/collect-hooks.js +0 -34
  105. package/boot/plugins/extend-config.js +0 -21
  106. package/boot/plugins/index.js +0 -27
  107. package/boot/plugins/run.js +0 -26
  108. package/boot/run-tool.js +0 -41
  109. package/docs/boot_build-config.js.html +0 -75
  110. package/docs/boot_create-scope.js.html +0 -25
  111. package/docs/boot_index.js.html +0 -43
  112. package/docs/data/search.json +0 -1
  113. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  114. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  115. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  116. package/docs/helper_emit.js.html +0 -18
  117. package/docs/helper_envs.js.html +0 -16
  118. package/docs/helper_error.js.html +0 -25
  119. package/docs/helper_get-bajo.js.html +0 -42
  120. package/docs/helper_index.js.html +0 -39
  121. package/docs/helper_log-levels.js.html +0 -20
  122. package/docs/helper_set-hook.js.html +0 -38
  123. package/docs/helper_walk-bajos.js.html +0 -51
  124. package/docs/index.html +0 -16
  125. package/docs/module-boot.html +0 -3
  126. package/docs/module-boot_buildConfig.html +0 -3
  127. package/docs/module-boot_createScope.html +0 -3
  128. package/docs/module-helper.html +0 -7
  129. package/docs/module-helper_setHook.html +0 -4
  130. package/docs/module-helper_walkBajos.html +0 -6
  131. package/docs/scripts/core.js +0 -655
  132. package/docs/scripts/core.min.js +0 -23
  133. package/docs/scripts/resize.js +0 -90
  134. package/docs/scripts/search.js +0 -265
  135. package/docs/scripts/search.min.js +0 -6
  136. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
  137. package/docs/scripts/third-party/fuse.js +0 -9
  138. package/docs/scripts/third-party/hljs-line-num-original.js +0 -369
  139. package/docs/scripts/third-party/hljs-line-num.js +0 -1
  140. package/docs/scripts/third-party/hljs-original.js +0 -5171
  141. package/docs/scripts/third-party/hljs.js +0 -1
  142. package/docs/scripts/third-party/popper.js +0 -5
  143. package/docs/scripts/third-party/tippy.js +0 -1
  144. package/docs/scripts/third-party/tocbot.js +0 -672
  145. package/docs/scripts/third-party/tocbot.min.js +0 -1
  146. package/docs/styles/clean-jsdoc-theme-base.css +0 -975
  147. package/docs/styles/clean-jsdoc-theme-dark.css +0 -407
  148. package/docs/styles/clean-jsdoc-theme-light.css +0 -388
  149. package/docs/styles/clean-jsdoc-theme.min.css +0 -1
  150. package/test/helper-error.js +0 -25
  151. package/test/helper-pathResolve.js +0 -28
  152. /package/boot/{helper → class/bajo-core/method}/get-key-by-value.js +0 -0
  153. /package/boot/{helper → class/bajo-core/method}/is-empty-dir.js +0 -0
  154. /package/boot/{helper → class/bajo-core/method}/is-set.js +0 -0
  155. /package/boot/{helper → class/bajo-core/method}/is-valid-plugin.js +0 -0
  156. /package/boot/{helper → class/bajo-core/method}/paginate.js +0 -0
  157. /package/boot/{helper → class/bajo-core/method}/pascal-case.js +0 -0
  158. /package/boot/{helper → class/bajo-core/method}/pick.js +0 -0
  159. /package/boot/{helper → class/bajo-core/method}/resolve-path.js +0 -0
  160. /package/boot/{helper → class/bajo-core/method}/round.js +0 -0
  161. /package/boot/{helper → class/bajo-core/method}/sec-to-hms.js +0 -0
  162. /package/boot/{helper → class/bajo-core/method}/white-space.js +0 -0
package/README.md CHANGED
@@ -1,57 +1,158 @@
1
- # Bajo Framework
1
+ # bajo
2
2
 
3
- ## Installation
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!<br /><br />
6
+
7
+ ## Overview
4
8
 
5
- > Note: you must have a proper install of ```node```, ```npm``` and/or ```yarn``` before
9
+ Before we go any further, below are some terminologies I use throughout these documentations:
10
+
11
+ - ```<bajo-base-dir>```: project directory is where you write all your codes
12
+ - ```<bajo-data-dir>```: data directory, defaults to ```<bajo-base-dir>/data``` if not specifically stated
13
+ - ```<bajo-tmp-dir>```: temp directory, defaults to OS temporary directory
14
+ - ```<package>```: plugin package name as normally showed on npm listing
15
+ - ```<plugin>```: plugin name, which is camel cased version of package name
16
+
17
+ ## Installation
6
18
 
7
19
  Open your terminal and type:
8
20
 
9
- ```
10
- npm install bajo
11
- # or
12
- yarn add bajo
21
+ ```bash
22
+ $ npm install bajo
13
23
  ```
14
24
 
15
25
  ## Fire up!
16
26
 
17
- Goto your project directory, create the ```index.js``` file and put this in it:
27
+ Create a new empty directory, this will be your project directory or your ```<bajo-base-dir>```. Now goto your newly created directory, and type:
28
+
29
+ ```bash
30
+ $ npm init
31
+ ```
32
+
33
+ You'll be asked to name your project etc. **IMPORTANT**: don't forget to mark your project as ES6 project by setting ```type``` key in your ```package.json``` to ```module```.
34
+
35
+ After completing those steps, move on the the next one: crating bajo bootstrap.
36
+
37
+ Inside your ```<bajo-base-dir>```, create the ```index.js``` file and put these lines below:
18
38
 
19
39
  ```js
20
- const bajo = require('bajo')
21
- bajo.boot()
22
- .then(scope => {
23
- })
24
- .catch(err => {
25
- console.trace(err)
26
- })
40
+ import bajo from 'bajo'
41
+ await bajo.default()
27
42
  ```
28
43
 
29
- Bajo application **ALWAYS** need a data directory to put configuration files, etc. This
30
- cloud be located inside or outside your project directory.
44
+ A bajo app **ALWAYS** needs a data directory to put configuration files, etc. This
45
+ could be located inside or outside your ```<bajo-base-dir>```.
31
46
 
32
- Lets assume you're going to put your data directory inside your project directory. So please
47
+ Lets assume you're going to put your data directory inside your ```<bajo-base-dir>```. So please
33
48
  create a new directory called ```data``` first. After that, just type in your terminal:
34
49
 
35
- ```
36
- node index.js --dir-data=data
50
+ ```bash
51
+ $ node index.js --dir-data=data
37
52
  ```
38
53
 
39
- Or you could use ```dotenv``` by creating ```.env``` file in the same directory as ```index.js```, and put this inside:
54
+ Or you could utilize ```dotenv``` by creating ```.env``` file in the same directory as ```index.js```, and put this inside:
40
55
 
41
56
  ```
42
- DIR_DATA = .\data
57
+ DIR_DATA = ./data
43
58
  ```
44
59
 
45
60
  Now you can omit calling node with arguments, you just need to type:
46
61
 
62
+ ```bash
63
+ $ node index.js
64
+ ```
65
+
66
+ ## Configuration
67
+
68
+ ### General rules
69
+
70
+ - All configuration files must be placed in ```<bajo-data-dir>/config/``` subfolder
71
+ - Config files should be named after its plugin name
72
+ - File format should be in ```.json``` or ```.js``` format
73
+ - If ```.js``` file is used, it should be in ES6 format and should export either plain javascript object or a function (sync or async both supported)
74
+ - If it returns a function, this function will be called within its plugin scope and should return a plain js object
75
+ - Other formats (```.yml```, ```.yaml``` and ```.toml```) can also be used by installing [bajoConfig](https://github.com/ardhi/bajo-config) plugin
76
+ - Order of precedence: ```.js``` > ```.json``` > ```.yml``` > ```.yaml``` > ```.toml```
77
+
78
+ ### Main configuration File
79
+
80
+ It should be named ```bajo.json``` with following keys:
81
+
82
+ | Key | Type | Required | Default | Description |
83
+ | --- | ---- | -------- | ------- | ----------- |
84
+ | ```env``` | ```string``` | no | ```dev``` | App environment: ```dev``` or ```prod``` |
85
+ | ```log``` | ```object``` | no || Logger setting |
86
+ | &nbsp;&nbsp;```dateFormat``` | ```string``` | no | ```YYYY-MM-DDTHH:MM:ss.SSS[Z]```| Date format accoding to [dayjs](https://github.com/iamkun/dayjs) |
87
+ | &nbsp;&nbsp;```tool``` | ```boolean``` | no | ```false``` | Set to ```true``` if you want to show log even in [tool mode](#tool-mode) |
88
+ | &nbsp;&nbsp;```level``` | ```string``` | no || Set one of these: ```trace```, ```debug```, ```info```, ```warn```, ```error```, ```fatal``` and ```silent```. If it isn't set, it will auto selected based on environment |
89
+ | ```lang``` | ```string``` | no || Language to use. If not set, it will be auto detected |
90
+ | ```exitHandler``` | ```boolean``` | no | ```true``` | Set to ```false``` if you want your app **NOT** to exit gracefully |
91
+
92
+ ### Installed Plugins
93
+
94
+ Plugins are what make Bajo Framework so great and flexible: they extends app features!
95
+
96
+ To use plugins:
97
+
98
+ 1. Install with ```npm install <package>```
99
+ 2. Optionally create ```<bajo-data-dir>/config/<plugin>.json``` to customize plugin settings
100
+ 3. Open/create ```<bajo-data-dir>/config/.plugins``` and put ```<package>``` in it, line by line
101
+
102
+ Example below will load ```bajoConfig```, ```bajoLogger``` and ```bajoMqtt```:
103
+
47
104
  ```
48
- node index.js
105
+ bajo-config
106
+ bajo-logger
107
+ bajo-mqtt
49
108
  ```
50
109
 
51
- ## Documentation
110
+ If you later decide to NOT load one or more plugins from your app, you just need to remove those from ```.plugins``` file and restart your app.
111
+
112
+ > **Warning**: please do not confuse between ```<package>``` and ```<plugin>```. Plugin package is the name of JS package listed on npm, while plugin name is the name of a plugin - a camel cased version of plugin package
113
+
114
+ ### Configuration Overrides
115
+
116
+ You can override ANY settings on ANY configuration files with dotenv variables and program's argument switches easily.
117
+
118
+ Order of importance: dotenv variable > args switches > config files
119
+
120
+ #### dotenv
121
+
122
+ - Create/open ```<bajo-base-dir>/.env```
123
+ - Use ```__``` (double underscores) as the replacement of the dot in object
124
+ - ```DIR__DATA```: Set ```<bajo-data-dir>``` data directory
125
+ - ```DIR__TMP```: Set ```<bajo-tmp-dir>``` temp directory
126
+ - For every key in ```bajo.json```, use its snake cased, upper cased version, e.g:
127
+ - ```env``` => ```ENV```
128
+ - ```log.dateFormat``` => ```LOG__DATE_FORMAT```
129
+ - ```exitHandler``` => ```EXIT_HANDLER```
130
+ - To override plugins config, prepend every key in plugin config with snake cased, upper cased version of the plugin name followed by a dot, e.g:
131
+ - ```key``` in ```myPlugin``` => ```MY_PLUGIN.KEY```
132
+ - ```key.subKey.subSubKey``` in ```myPlugin``` => ```MY_PLUGIN.KEY__SUB_KEY__SUB_SUB_KEY```
133
+
134
+ #### Program argument switches
135
+ - Execute with switches, e.g: ```node index.js --xxx=one --yyy=two```
136
+ - Every switches must be prefixed with ```--```
137
+ - Use ```-``` as the replacement of the dot in object
138
+ - ```--dir-data```: Set ```<bajo-data-dir>``` data directory
139
+ - ```--dir-tmp```: Set ```<bajo-tmp-dir>``` temp directory
140
+ - For every key in ```bajo.json```, add prefix ```--``` e.g:
141
+ - ```env``` => ```--env=prod```
142
+ - ```log.dateFormat``` => ```--log-dateFormat=xxx```
143
+ - ```exitHandler``` => ```--exitHandler```
144
+ - To override plugins config, prepend every key in plugin config with the plugin name followed by a ```:```, e.g:
145
+ - ```key``` in ```myPlugin``` => ```--myPlugin:key```
146
+ - ```key.subKey.subSubKey``` in ```myPlugin``` => ```--myPlugin:key-subKey-subSubKey```
147
+
148
+ ## More Documentations
52
149
 
53
- [API](https://ardhi.github.io/bajo)
150
+ - [Tutorial](docs/tutorial.md)
151
+ - [User Guide](docs/user-guide.md)
152
+ - [Plugin Development](docs/plugin-dev.md)
153
+ - [API](docs/api.md)
154
+ - [Ecosystem](docs/ecosystem.md)
54
155
 
55
156
  ## License
56
157
 
57
- [MIT](LICENSE) License
158
+ [MIT](LICENSE)
@@ -0,0 +1,31 @@
1
+ {
2
+ "[%s] '%s' signal received": "[%s] Diterima signal '%s'",
3
+ "[%s] Exited: %s": "[%s] Keluar: %s",
4
+ "[%s] Program shutdown": "[%s] Program berakhir",
5
+ "[%s] Exit handlings": "[%s] Pengaturan keluar",
6
+ "[%s] Boot process completed in %s": "[%s] Proses boot komplit dalam waktu %s",
7
+ "[%s] Run tool": "[%s] Jalankan perkakas",
8
+ "[%s] Loaded plugins: %s": "[%s] Plugin termuat: %s",
9
+ "[%s] Unloaded 'single' plugins: %s": "[%s] 'Single' plugin tak termuat: %s",
10
+ "[%s] No %s found": "[%s] Tidak ditemukan %s",
11
+ "[%s] Init plugin...": "[%s] Init plugin...",
12
+ "[%s] Start plugin...": "[%s] Jalankan plugin...",
13
+ "[%s] Collecting %s": "[%s] Mengumpulkan %s",
14
+ "[%s] %s collected: %d": "[%s] Total %s terkumpul: %d",
15
+ "- All -": "- Semua -",
16
+ "Tool mode activated": "Mode perkakas diaktifkan",
17
+ "App is running in tool mode...": "Aplikasi berjalan dalam mode perkakas",
18
+ "No tool loaded. Aborted!": "Tidak ada perkakas yang termuat. Batalkan!",
19
+ "Please select:": "Silahkan pilih:",
20
+ "Tool '%s' not found. Aborted!": "perkakas '%s' tidak ditemukan. Batalkan!",
21
+ "Please select a method:": "Silahkan pilih metode:",
22
+ "Unknown method '%s'": "Metode '%s' tidak dikenal",
23
+ "and": "dan",
24
+ "or": "atau",
25
+ "Yes": "Ya",
26
+ "No": "Tidak",
27
+ "[%s] Exited": "[%s] Telah keluar",
28
+ "[%s] App shutdown": "[%s] Aplikasi dimatikan",
29
+ "[%s] Collect %s": "",
30
+ "[%s] %s support is disabled": ""
31
+ }
@@ -0,0 +1,69 @@
1
+ import util from 'util'
2
+ import { last } from 'lodash-es'
3
+ import BajoCore from './bajo-core.js'
4
+ import resolvePath from './bajo-core/method/resolve-path.js'
5
+ import parseArgsArgv from '../lib/parse-args-argv.js'
6
+ import parseEnv from '../lib/parse-env.js'
7
+ import buildPlugins from './bajo-core/helper/build-plugins.js'
8
+ import { buildBaseConfig, buildExtConfig } from './bajo-core/helper/build-config.js'
9
+ import collectConfigHandlers from './bajo-core/helper/collect-config-handlers.js'
10
+ import attachMethod from './bajo-core/helper/attach-method.js'
11
+ import bootOrder from './bajo-core/helper/boot-order.js'
12
+ import bootPlugins from './bajo-core/helper/boot-plugins.js'
13
+ import exitHandler from './bajo-core/helper/exit-handler.js'
14
+ import runAsApplet from './bajo-core/helper/run-as-applet.js'
15
+
16
+ class App {
17
+ constructor (cwd) {
18
+ if (!cwd) cwd = process.cwd()
19
+ const l = last(process.argv)
20
+ if (l.startsWith('--cwd')) {
21
+ const parts = l.split('=')
22
+ cwd = parts[1]
23
+ }
24
+ cwd = resolvePath(cwd)
25
+ process.env.BAJOCWD = cwd
26
+ this.cwd = cwd
27
+ }
28
+
29
+ addPlugin (plugin) {
30
+ if (this[plugin.name]) throw new Error(`Plugin '${plugin.name}' added already`)
31
+ this[plugin.name] = plugin
32
+ plugin.initPrint()
33
+ }
34
+
35
+ dump (...args) {
36
+ const terminate = last(args) === true
37
+ if (terminate) args.pop()
38
+ for (const arg of args) {
39
+ const result = util.inspect(arg, false, null, true)
40
+ console.log(result)
41
+ }
42
+ if (terminate) process.kill(process.pid, 'SIGINT')
43
+ }
44
+
45
+ async boot () {
46
+ // argv/args
47
+ const { args, argv } = await parseArgsArgv.call(this.app) ?? {}
48
+ this.argv = argv
49
+ this.args = args
50
+ this.env = parseEnv() ?? {}
51
+
52
+ const bajo = new BajoCore(this)
53
+ await buildBaseConfig.call(bajo)
54
+ await buildPlugins.call(bajo)
55
+ await collectConfigHandlers.call(bajo)
56
+ await buildExtConfig.call(bajo)
57
+ await attachMethod.call(bajo)
58
+ await bootOrder.call(bajo)
59
+ await bootPlugins.call(bajo)
60
+ await exitHandler.call(bajo)
61
+ // boot complete
62
+ await bajo.runHook('bajo:bootComplete')
63
+ const elapsed = new Date() - bajo.runAt
64
+ bajo.log.info('Boot process completed in %s', bajo.secToHms(elapsed, true))
65
+ if (bajo.applet) await runAsApplet.call(bajo)
66
+ }
67
+ }
68
+
69
+ export default App
@@ -0,0 +1,30 @@
1
+ import createMethod from '../../../lib/create-method.js'
2
+ import deepFreeze from 'deep-freeze-strict'
3
+ import currentLoc from '../../../lib/current-loc.js'
4
+ import fs from 'fs-extra'
5
+ import fastGlob from 'fast-glob'
6
+ import { sprintf } from 'sprintf-js'
7
+ import outmatch from 'outmatch'
8
+
9
+ export default async function () {
10
+ await createMethod.call(this, `${currentLoc(import.meta).dir}/../method`)
11
+ this.freeze = (o, shallow) => {
12
+ if (shallow) Object.freeze(o)
13
+ else deepFreeze(o)
14
+ }
15
+ this.setImmediate = function () {
16
+ return new Promise((resolve) => {
17
+ setImmediate(() => resolve())
18
+ })
19
+ }
20
+ // commonly used libraries
21
+ this.lib._ = await import('lodash-es')
22
+ this.lib.fs = fs
23
+ this.lib.fastGlob = fastGlob
24
+ this.lib.sprintf = sprintf
25
+ this.lib.outmatch = outmatch
26
+ // last cleanup
27
+ if (!fs.existsSync(this.dir.data)) {
28
+ this.log.warn('Data directory \'%s\' doesn\'t exists!', this.dir.data)
29
+ }
30
+ }
@@ -1,22 +1,20 @@
1
1
  import { reduce, map, isNaN, trim, forOwn, orderBy } from 'lodash-es'
2
2
  import fs from 'fs-extra'
3
- import getModuleDir from './helper/get-module-dir.js'
3
+ import getModuleDir from '../method/get-module-dir.js'
4
4
 
5
5
  async function bootOrder () {
6
- const { log, envs, error } = this.bajo.helper
7
- log.debug('Setup boot order')
8
- const config = this.bajo.config
9
- const order = reduce(config.plugins, (o, k, i) => {
6
+ this.log.debug('Setup boot order')
7
+ const order = reduce(this.pluginPkgs, (o, k, i) => {
10
8
  const key = map(k.split(':'), m => trim(m))
11
9
  if (key[1] && !isNaN(Number(key[1]))) o[key[0]] = Number(key[1])
12
10
  else o[key[0]] = 10000 + i
13
11
  return o
14
12
  }, {})
15
13
  const norder = {}
16
- for (let n of config.plugins) {
14
+ for (let n of this.pluginPkgs) {
17
15
  n = map(n.split(':'), m => trim(m))[0]
18
- const dir = n === 'app' ? (config.dir.base + '/app') : getModuleDir(n)
19
- if (n !== 'app' && !fs.existsSync(`${dir}/bajo`)) throw error('Package \'%s\' not found or isn\'t a valid Bajo package', n)
16
+ const dir = n === this.mainNs ? (`${this.dir.base}/${this.mainNs}`) : getModuleDir(n)
17
+ if (n !== this.mainNs && !fs.existsSync(`${dir}/bajo`)) throw this.error('Package \'%s\' not found or isn\'t a valid Bajo package', n)
20
18
  norder[n] = NaN
21
19
  try {
22
20
  norder[n] = Number(trim(await fs.readFile(`${dir}/bajo/.bootorder`, 'utf8')))
@@ -27,8 +25,10 @@ async function bootOrder () {
27
25
  const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
28
26
  result.push(item)
29
27
  })
30
- config.plugins = map(orderBy(result, ['v']), 'k')
31
- log.info('Run in \'%s\' environment', envs[config.env])
28
+ this.pluginPkgs = map(orderBy(result, ['v']), 'k')
29
+ this.log.info('Run in \'%s\' environment', this.envs[this.config.env])
30
+ // misc
31
+ this.freeze(this.config)
32
32
  }
33
33
 
34
34
  export default bootOrder
@@ -0,0 +1,19 @@
1
+ import buildConfig from '../../bajo-plugin/helper/build-config.js'
2
+ import checkDependency from '../../bajo-plugin/helper/check-dependency.js'
3
+ import checkClash from '../../bajo-plugin/helper/check-clash.js'
4
+ import attachMethod from '../../bajo-plugin/helper/attach-method.js'
5
+ import collectHooks from '../../bajo-plugin/helper/collect-hooks.js'
6
+ import run from '../../bajo-plugin/helper/run.js'
7
+ import collectExitHandlers from '../../bajo-plugin/helper/collect-exit-handlers.js'
8
+
9
+ async function bootPlugins () {
10
+ await buildConfig.call(this.app)
11
+ await checkClash.call(this.app)
12
+ await checkDependency.call(this.app)
13
+ await attachMethod.call(this.app)
14
+ await collectHooks.call(this.app)
15
+ await collectExitHandlers.call(this.app)
16
+ await run.call(this.app)
17
+ }
18
+
19
+ export default bootPlugins
@@ -0,0 +1,61 @@
1
+ import readAllConfigs from '../../../lib/read-all-configs.js'
2
+ import defaultsDeep from '../method/defaults-deep.js'
3
+ import getKeyByValue from '../method/get-key-by-value.js'
4
+ import resolvePath from '../method/resolve-path.js'
5
+ import currentLoc from '../../../lib/current-loc.js'
6
+ import envs from '../method/envs.js'
7
+ import join from '../method/join.js'
8
+ import omitDeep from 'omit-deep'
9
+ import os from 'os'
10
+ import fs from 'fs-extra'
11
+
12
+ import { map, pick, values, keys, set, get } from 'lodash-es'
13
+
14
+ const omitted = ['spawn', 'cwd', 'name', 'alias', 'applet', 'a', 'plugins']
15
+
16
+ const defConfig = {
17
+ log: {
18
+ dateFormat: 'YYYY-MM-DDTHH:MM:ss.SSS[Z]',
19
+ applet: false,
20
+ traceHook: false
21
+ },
22
+ lang: Intl.DateTimeFormat().resolvedOptions().lang ?? 'en-US',
23
+ exitHandler: true
24
+ }
25
+
26
+ export async function buildBaseConfig () {
27
+ this.applet = this.app.argv._.applet
28
+ this.config = defaultsDeep({}, this.app.env._, this.app.argv._)
29
+ this.alias = this.name
30
+ set(this, 'dir.base', this.app.cwd)
31
+ const path = currentLoc(import.meta).dir + '/../../../..'
32
+ set(this, 'dir.pkg', resolvePath(path))
33
+ if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
34
+ this.dir.data = resolvePath(this.dir.data)
35
+ if (!this.dir.tmp) {
36
+ this.dir.tmp = `${resolvePath(os.tmpdir())}/${this.name}`
37
+ fs.ensureDirSync(this.dir.tmp)
38
+ }
39
+ this.app.addPlugin(this)
40
+ }
41
+
42
+ export async function buildExtConfig () {
43
+ // config merging
44
+ let resp = await readAllConfigs.call(this.app, `${this.dir.data}/config/${this.name}`)
45
+ resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env']), omitted)
46
+ this.config = defaultsDeep({}, resp, this.config, defConfig)
47
+ this.config.env = (this.config.env ?? 'dev').toLowerCase()
48
+ if (values(envs).includes(this.config.env)) this.config.env = getKeyByValue(envs, this.config.env)
49
+ if (!keys(envs).includes(this.config.env)) throw new Error(`Unknown environment '${this.config.env}'. Supported: ${join(keys(envs))}`)
50
+ process.env.NODE_ENV = envs[this.config.env]
51
+ if (!this.config.log.level) this.config.log.level = this.config.env === 'dev' ? 'debug' : 'info'
52
+ if (this.config.silent) this.config.log.level = 'silent'
53
+ if (this.applet) {
54
+ if (!this.pluginPkgs.includes('bajo-cli')) throw new Error('Applet needs to have \'bajo-cli\' loaded first')
55
+ if (!this.config.log.applet) this.config.log.level = 'silent'
56
+ this.config.exitHandler = false
57
+ }
58
+ const exts = map(this.configHandlers, 'ext')
59
+ this.log.init()
60
+ this.log.debug('Config handlers: %s', join(exts))
61
+ }
@@ -0,0 +1,26 @@
1
+ import { isString, filter, map, trim, without, uniq, camelCase, isEmpty } from 'lodash-es'
2
+ import fs from 'fs-extra'
3
+ import getModuleDir from '../method/get-module-dir.js'
4
+ import BajoPlugin from '../../bajo-plugin.js'
5
+
6
+ async function buildPlugins () {
7
+ let pluginPkgs = this.config.plugins ?? []
8
+ if (isString(pluginPkgs)) pluginPkgs = [pluginPkgs]
9
+ const pluginsFile = `${this.dir.data}/config/.plugins`
10
+ if (fs.existsSync(pluginsFile)) {
11
+ pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
12
+ }
13
+ this.pluginPkgs = without(uniq(pluginPkgs), this.mainNs)
14
+ this.pluginPkgs.push(this.mainNs)
15
+ for (const pkg of this.pluginPkgs) {
16
+ const ns = camelCase(pkg)
17
+ const dir = ns === this.mainNs ? (`${this.dir.base}/${this.mainNs}`) : getModuleDir.call(this, pkg)
18
+ if (ns !== this.mainNs && !fs.existsSync(`${dir}/${this.name}`)) throw new Error(`Package '${pkg}' isn't a valid Bajo package`)
19
+ const plugin = new BajoPlugin(pkg, this.app)
20
+ this.pluginNames.push(plugin.name)
21
+ this.app.addPlugin(plugin)
22
+ }
23
+ delete this.config.plugins
24
+ }
25
+
26
+ export default buildPlugins
@@ -0,0 +1,21 @@
1
+ import getModuleDir from '../method/get-module-dir.js'
2
+ import importModule from '../method/import-module.js'
3
+ import { isFunction, isPlainObject, camelCase } from 'lodash-es'
4
+
5
+ async function collectConfigHandlers () {
6
+ for (const pkg of this.pluginPkgs) {
7
+ let dir
8
+ try {
9
+ dir = getModuleDir.call(this, pkg)
10
+ } catch (err) {}
11
+ if (!dir) continue
12
+ const file = `${dir}/bajo/config-handlers.js`
13
+ let mod = await importModule.call(this, file)
14
+ if (!mod) continue
15
+ if (isFunction(mod)) mod = await mod.call(this.app[camelCase(pkg)])
16
+ if (isPlainObject(mod)) mod = [mod]
17
+ this.configHandlers = this.configHandlers.concat(mod)
18
+ }
19
+ }
20
+
21
+ export default collectConfigHandlers
@@ -1,24 +1,20 @@
1
1
  async function exit (signal) {
2
- const { eachPlugins, log } = this.bajo.helper
3
- log.warn('\'%s\' signal received', signal)
4
- await eachPlugins(async function ({ plugin }) {
5
- const handler = this.bajo.exitHandler[plugin]
2
+ const { eachPlugins } = this
3
+ this.log.warn('\'%s\' signal received', signal)
4
+ await eachPlugins(async function ({ ns }) {
5
+ const handler = this.exitHandler
6
6
  if (!handler) return undefined
7
7
  try {
8
8
  await handler.call(this)
9
9
  } catch (err) {}
10
- log.debug('Exited: %s', plugin)
10
+ this.log.debug('Exited')
11
11
  })
12
- log.debug('Program shutdown')
12
+ this.log.debug('App shutdown')
13
13
  process.exit(0)
14
14
  }
15
15
 
16
16
  async function exitHandler () {
17
- const { log, getConfig } = this.bajo.helper
18
- const config = getConfig()
19
- if (!config.exitHandler) return
20
-
21
- log.debug('Exit handlings')
17
+ if (!this.config.exitHandler) return
22
18
 
23
19
  process.on('SIGINT', async () => {
24
20
  await exit.call(this, 'SIGINT')
@@ -29,9 +25,7 @@ async function exitHandler () {
29
25
  })
30
26
 
31
27
  process.on('uncaughtException', (error, origin) => {
32
- const { getConfig } = this.bajo.helper
33
- const config = getConfig()
34
- if (config.log.report.includes('sys:uncaughtException')) log.fatal({ origin }, '%s', error.message)
28
+ if (this.config.log.report.includes('sys:uncaughtException')) this.log.fatal({ origin }, '%s', error.message)
35
29
  setTimeout(() => {
36
30
  console.error(error)
37
31
  process.exit(1)
@@ -51,11 +45,11 @@ async function exitHandler () {
51
45
  parts.pop()
52
46
  parts.pop()
53
47
  file = parts.join(':')
54
- log.error({ file, line, column }, '%s', reason.message)
48
+ this.log.error({ file, line, column }, '%s', reason.message)
55
49
  })
56
50
 
57
51
  process.on('warning', warning => {
58
- log.error('%s', warning.message)
52
+ this.log.error('%s', warning.message)
59
53
  })
60
54
  }
61
55
 
@@ -0,0 +1,26 @@
1
+ async function runAsApplet () {
2
+ const { isString, map, find } = this.lib._
3
+ await this.eachPlugins(async function ({ file, ns, alias }) {
4
+ this.app.bajo.applets.push({ ns, file, alias })
5
+ }, { glob: 'applet.js', prefix: 'bajoCli' })
6
+
7
+ this.log.debug('Applet mode activated')
8
+ this.print.info('App is running as applet...')
9
+ if (this.applets.length === 0) this.print.fatal('No applets loaded. Aborted!')
10
+ let name = this.applet
11
+ if (!isString(this.applet)) {
12
+ const select = await this.importPkg('bajoCli:@inquirer/select')
13
+ name = await select({
14
+ message: this.print.write('Please select:'),
15
+ choices: map(this.applets, t => ({ value: t.ns }))
16
+ })
17
+ }
18
+ const [ns, path] = name.split(':')
19
+ const applet = find(this.applets, a => (a.ns === ns || a.alias === ns))
20
+ if (!applet) this.print.fatal('Applet \'%s\' not found. Aborted!', name)
21
+ await this.runHook(`${this.app[applet.ns]}:beforeAppletRun`)
22
+ await this.app.bajoCli.runApplet(applet, path, ...this.app.args)
23
+ await this.runHook(`${this.app[applet.ns]}:afterAppletRun`)
24
+ }
25
+
26
+ export default runAsApplet
@@ -0,0 +1,18 @@
1
+ import { filter, trim } from 'lodash-es'
2
+
3
+ function arrangeArray (inputs, trimItem = true) {
4
+ const first = []
5
+ const last = []
6
+
7
+ const items = filter(inputs, item => {
8
+ if (trimItem) item = trim(item)
9
+ if (item[0] === '^') first.push(item.slice(1))
10
+ else if (item[0] === '$') last.push(item.slice(1))
11
+ return !['^', '$'].includes(item[0])
12
+ })
13
+ items.unshift(...first)
14
+ items.push(...last)
15
+ return items
16
+ }
17
+
18
+ export default arrangeArray
@@ -0,0 +1,24 @@
1
+ import _path from 'path'
2
+ import { map, camelCase } from 'lodash-es'
3
+
4
+ function breakNsPathFromFile ({ file, dir, baseNs, suffix = '', getType } = {}) {
5
+ let item = file.replace(dir + suffix, '')
6
+ let type
7
+ if (getType) {
8
+ const items = item.split('/')
9
+ type = items.shift()
10
+ item = items.join('/')
11
+ }
12
+ item = item.slice(0, item.length - _path.extname(item).length)
13
+ let [name, path] = item.split('@')
14
+ if (!path) {
15
+ path = name
16
+ name = baseNs
17
+ }
18
+ path = camelCase(path)
19
+ const names = map(name.split('.'), n => camelCase(n))
20
+ const [ns, subNs] = names
21
+ return { ns, subNs, path, fullNs: names.join('.'), type }
22
+ }
23
+
24
+ export default breakNsPathFromFile
@@ -0,0 +1,29 @@
1
+ import { isEmpty } from 'lodash-es'
2
+ import querystring from 'querystring'
3
+
4
+ function breakNsPath (item = '', defaultNs = 'bajo', checkNs = true) {
5
+ let [ns, ...path] = item.split(':')
6
+ let subNs
7
+ let subSubNs
8
+ path = path.join(':')
9
+ if (path.startsWith('//')) return { ns: undefined, path: item } // for: http:// etc
10
+ if (isEmpty(path)) {
11
+ path = ns
12
+ ns = defaultNs
13
+ }
14
+ [ns, subNs, subSubNs] = ns.split('.')
15
+ if (checkNs) {
16
+ if (!this.app[ns]) {
17
+ const plugin = this.getPlugin(ns)
18
+ if (plugin) ns = plugin.name
19
+ }
20
+ if (!this.app[ns]) throw this.error('Unknown plugin \'%s\' or plugin isn\'t loaded yet')
21
+ }
22
+ const fullPath = path
23
+ let qs
24
+ [path, qs] = path.split('?')
25
+ qs = querystring.parse(qs) ?? {}
26
+ return { ns, path, subNs, subSubNs, qs, fullPath }
27
+ }
28
+
29
+ export default breakNsPath