bajo 0.3.10 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -26
- package/bajoI18N/resource/id.json +31 -0
- package/boot/class/app.js +69 -0
- package/boot/class/bajo-core/helper/attach-method.js +30 -0
- package/boot/{boot-order.js → class/bajo-core/helper/boot-order.js} +10 -10
- package/boot/class/bajo-core/helper/boot-plugins.js +19 -0
- package/boot/class/bajo-core/helper/build-config.js +61 -0
- package/boot/class/bajo-core/helper/build-plugins.js +26 -0
- package/boot/class/bajo-core/helper/collect-config-handlers.js +21 -0
- package/boot/{exit-handler.js → class/bajo-core/helper/exit-handler.js} +10 -16
- package/boot/class/bajo-core/helper/run-as-applet.js +26 -0
- package/boot/class/bajo-core/method/arrange-array.js +18 -0
- package/boot/class/bajo-core/method/break-ns-path-from-file.js +24 -0
- package/boot/class/bajo-core/method/break-ns-path.js +29 -0
- package/boot/class/bajo-core/method/build-collections.js +46 -0
- package/boot/class/bajo-core/method/call-handler.js +29 -0
- package/boot/{helper → class/bajo-core/method}/defaults-deep.js +1 -1
- package/boot/class/bajo-core/method/each-plugins.js +55 -0
- package/boot/class/bajo-core/method/envs.js +7 -0
- package/boot/{helper → class/bajo-core/method}/generate-id.js +4 -0
- package/boot/{helper → class/bajo-core/method}/get-global-module-dir.js +5 -6
- package/boot/class/bajo-core/method/get-method.js +10 -0
- package/boot/{helper → class/bajo-core/method}/get-module-dir.js +3 -4
- package/boot/{helper → class/bajo-core/method}/get-plugin-data-dir.js +2 -3
- package/boot/class/bajo-core/method/get-plugin-file.js +18 -0
- package/boot/class/bajo-core/method/get-plugin.js +23 -0
- package/boot/{helper → class/bajo-core/method}/import-module.js +4 -10
- package/boot/{helper → class/bajo-core/method}/import-pkg.js +9 -3
- package/boot/class/bajo-core/method/includes.js +13 -0
- package/boot/class/bajo-core/method/is-class.js +7 -0
- package/boot/{helper → class/bajo-core/method}/is-log-in-range.js +1 -1
- package/boot/{helper → class/bajo-core/method}/is-valid-app.js +2 -2
- package/boot/class/bajo-core/method/join.js +15 -0
- package/boot/class/bajo-core/method/log-levels.js +9 -0
- package/boot/class/bajo-core/method/num-unit.js +9 -0
- package/boot/{helper → class/bajo-core/method}/parse-object.js +24 -8
- package/boot/class/bajo-core/method/read-config.js +48 -0
- package/boot/{helper → class/bajo-core/method}/read-json.js +2 -2
- package/boot/class/bajo-core/method/run-hook.js +26 -0
- package/boot/{helper → class/bajo-core/method}/save-as-download.js +2 -2
- package/boot/class/bajo-core/method/slice-string.js +13 -0
- package/boot/class/bajo-core/method/titleize.js +23 -0
- package/boot/class/bajo-core.js +30 -0
- package/boot/class/bajo-plugin/helper/attach-method.js +14 -0
- package/boot/class/bajo-plugin/helper/build-config.js +10 -0
- package/boot/class/bajo-plugin/helper/check-clash.js +16 -0
- package/boot/class/bajo-plugin/helper/check-dependency.js +37 -0
- package/boot/class/bajo-plugin/helper/collect-exit-handlers.js +14 -0
- package/boot/class/bajo-plugin/helper/collect-hooks.js +29 -0
- package/boot/class/bajo-plugin/helper/run.js +20 -0
- package/boot/class/bajo-plugin.js +78 -0
- package/boot/class/error.js +59 -0
- package/boot/class/log.js +67 -0
- package/boot/class/plugin.js +51 -0
- package/boot/class/print.js +103 -0
- package/boot/index.js +4 -51
- package/boot/lib/create-method.js +30 -0
- package/boot/{helper → lib}/current-loc.js +1 -1
- package/boot/lib/get-global-module-path.js +189 -0
- package/boot/lib/omitted-plugin-keys.js +1 -1
- package/boot/lib/parse-args-argv.js +8 -11
- package/boot/lib/parse-env.js +7 -6
- package/boot/lib/read-all-configs.js +36 -0
- package/boot/lib/translate.js +18 -0
- package/docs/ecosystem.md +55 -0
- package/docs/hook.md +11 -0
- package/package.json +54 -56
- package/test/{helper-isSet.js → method/isSet.js} +6 -4
- package/waibuStatic/virtual.json +7 -0
- package/boot/attach-helper.js +0 -40
- package/boot/build-config.js +0 -99
- package/boot/create-scope.js +0 -36
- package/boot/helper/break-ns-path.js +0 -20
- package/boot/helper/build-collections.js +0 -43
- package/boot/helper/build-name.js +0 -17
- package/boot/helper/call-helper-or-handler.js +0 -15
- package/boot/helper/dump.js +0 -14
- package/boot/helper/each-plugins.js +0 -106
- package/boot/helper/envs.js +0 -14
- package/boot/helper/error.js +0 -77
- package/boot/helper/fatal.js +0 -13
- package/boot/helper/get-config.js +0 -19
- package/boot/helper/get-helper.js +0 -12
- package/boot/helper/get-item-by-name.js +0 -26
- package/boot/helper/get-plugin-file.js +0 -16
- package/boot/helper/get-plugin-name.js +0 -39
- package/boot/helper/get-plugin.js +0 -14
- package/boot/helper/join.js +0 -11
- package/boot/helper/log-levels.js +0 -19
- package/boot/helper/read-config.js +0 -48
- package/boot/helper/resolve-tpl-path.js +0 -15
- package/boot/helper/run-hook.js +0 -49
- package/boot/helper/spinner.js +0 -9
- package/boot/helper/start-plugin.js +0 -7
- package/boot/helper/titleize.js +0 -14
- package/boot/lib/build-helper.js +0 -60
- package/boot/lib/logger.js +0 -76
- package/boot/lib/print.js +0 -138
- package/boot/plugins/attach-helper.js +0 -20
- package/boot/plugins/build-config.js +0 -68
- package/boot/plugins/check-clash.js +0 -18
- package/boot/plugins/check-dependency.js +0 -37
- package/boot/plugins/collect-config-handlers.js +0 -22
- package/boot/plugins/collect-exit-handlers.js +0 -18
- package/boot/plugins/collect-hooks.js +0 -34
- package/boot/plugins/extend-config.js +0 -21
- package/boot/plugins/index.js +0 -27
- package/boot/plugins/run.js +0 -26
- package/boot/run-tool.js +0 -41
- package/docs/boot_build-config.js.html +0 -75
- package/docs/boot_create-scope.js.html +0 -25
- package/docs/boot_index.js.html +0 -43
- package/docs/data/search.json +0 -1
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/helper_emit.js.html +0 -18
- package/docs/helper_envs.js.html +0 -16
- package/docs/helper_error.js.html +0 -25
- package/docs/helper_get-bajo.js.html +0 -42
- package/docs/helper_index.js.html +0 -39
- package/docs/helper_log-levels.js.html +0 -20
- package/docs/helper_set-hook.js.html +0 -38
- package/docs/helper_walk-bajos.js.html +0 -51
- package/docs/index.html +0 -16
- package/docs/module-boot.html +0 -3
- package/docs/module-boot_buildConfig.html +0 -3
- package/docs/module-boot_createScope.html +0 -3
- package/docs/module-helper.html +0 -7
- package/docs/module-helper_setHook.html +0 -4
- package/docs/module-helper_walkBajos.html +0 -6
- package/docs/scripts/core.js +0 -655
- package/docs/scripts/core.min.js +0 -23
- package/docs/scripts/resize.js +0 -90
- package/docs/scripts/search.js +0 -265
- package/docs/scripts/search.min.js +0 -6
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
- package/docs/scripts/third-party/fuse.js +0 -9
- package/docs/scripts/third-party/hljs-line-num-original.js +0 -369
- package/docs/scripts/third-party/hljs-line-num.js +0 -1
- package/docs/scripts/third-party/hljs-original.js +0 -5171
- package/docs/scripts/third-party/hljs.js +0 -1
- package/docs/scripts/third-party/popper.js +0 -5
- package/docs/scripts/third-party/tippy.js +0 -1
- package/docs/scripts/third-party/tocbot.js +0 -672
- package/docs/scripts/third-party/tocbot.min.js +0 -1
- package/docs/styles/clean-jsdoc-theme-base.css +0 -975
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -407
- package/docs/styles/clean-jsdoc-theme-light.css +0 -388
- package/docs/styles/clean-jsdoc-theme.min.css +0 -1
- package/test/helper-error.js +0 -25
- package/test/helper-pathResolve.js +0 -28
- /package/boot/{helper → class/bajo-core/method}/get-key-by-value.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/is-empty-dir.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/is-set.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/is-valid-plugin.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/paginate.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/pascal-case.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/pick.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/resolve-path.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/round.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/sec-to-hms.js +0 -0
- /package/boot/{helper → class/bajo-core/method}/white-space.js +0 -0
package/README.md
CHANGED
|
@@ -1,57 +1,158 @@
|
|
|
1
|
-
#
|
|
1
|
+
# bajo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
 
|
|
4
|
+
|
|
5
|
+
> <br />**Attention**: I do NOT accept any pull request at the moment, thanks!<br /><br />
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
4
8
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
bajo.
|
|
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
|
-
|
|
30
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
+
| ```dateFormat``` | ```string``` | no | ```YYYY-MM-DDTHH:MM:ss.SSS[Z]```| Date format accoding to [dayjs](https://github.com/iamkun/dayjs) |
|
|
87
|
+
| ```tool``` | ```boolean``` | no | ```false``` | Set to ```true``` if you want to show log even in [tool mode](#tool-mode) |
|
|
88
|
+
| ```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
|
-
|
|
105
|
+
bajo-config
|
|
106
|
+
bajo-logger
|
|
107
|
+
bajo-mqtt
|
|
49
108
|
```
|
|
50
109
|
|
|
51
|
-
|
|
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
|
-
[
|
|
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)
|
|
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 '
|
|
3
|
+
import getModuleDir from '../method/get-module-dir.js'
|
|
4
4
|
|
|
5
5
|
async function bootOrder () {
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
14
|
+
for (let n of this.pluginPkgs) {
|
|
17
15
|
n = map(n.split(':'), m => trim(m))[0]
|
|
18
|
-
const dir = n ===
|
|
19
|
-
if (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
|
-
|
|
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
|
|
3
|
-
log.warn('\'%s\' signal received', signal)
|
|
4
|
-
await eachPlugins(async function ({
|
|
5
|
-
const handler = this.
|
|
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
|
|
10
|
+
this.log.debug('Exited')
|
|
11
11
|
})
|
|
12
|
-
log.debug('
|
|
12
|
+
this.log.debug('App shutdown')
|
|
13
13
|
process.exit(0)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
async function exitHandler () {
|
|
17
|
-
|
|
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
|
-
|
|
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
|