bajo 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsdoc.conf.json +38 -0
- package/README.md +57 -3
- package/boot/attach-helper.js +26 -0
- package/boot/boot-order.js +34 -0
- package/boot/build-config.js +94 -0
- package/boot/create-scope.js +36 -0
- package/boot/exit-handler.js +60 -0
- package/boot/helper/build-collections.js +40 -0
- package/boot/helper/call-helper-or-handler.js +15 -0
- package/boot/helper/current-loc.js +11 -0
- package/boot/helper/defaults-deep.js +14 -0
- package/boot/helper/dump.js +10 -0
- package/boot/helper/each-plugins.js +94 -0
- package/boot/helper/envs.js +14 -0
- package/boot/helper/error.js +47 -0
- package/boot/helper/fatal.js +12 -0
- package/boot/helper/generate-id.js +19 -0
- package/boot/helper/get-config.js +18 -0
- package/boot/helper/get-global-module-dir.js +27 -0
- package/boot/helper/get-helper.js +11 -0
- package/boot/helper/get-item-by-name.js +26 -0
- package/boot/helper/get-key-by-value.js +5 -0
- package/boot/helper/get-module-dir.js +22 -0
- package/boot/helper/get-plugin-name.js +39 -0
- package/boot/helper/get-plugin.js +7 -0
- package/boot/helper/import-module.js +25 -0
- package/boot/helper/import-pkg.js +67 -0
- package/boot/helper/index.js +36 -0
- package/boot/helper/is-empty-dir.js +9 -0
- package/boot/helper/is-log-in-range.js +10 -0
- package/boot/helper/is-set.js +5 -0
- package/boot/helper/is-valid-app.js +12 -0
- package/boot/helper/is-valid-plugin.js +12 -0
- package/boot/helper/log-levels.js +19 -0
- package/boot/helper/pascal-case.js +7 -0
- package/boot/helper/print.js +89 -0
- package/boot/helper/read-config.js +46 -0
- package/boot/helper/read-json.js +10 -0
- package/boot/helper/resolve-path.js +15 -0
- package/boot/helper/resolve-tpl-path.js +21 -0
- package/boot/helper/run-hook.js +46 -0
- package/boot/helper/save-as-download.js +18 -0
- package/boot/helper/white-space.js +3 -0
- package/boot/index.js +60 -0
- package/boot/lib/bora.js +97 -0
- package/boot/lib/build-helper.js +58 -0
- package/boot/lib/logger.js +75 -0
- package/boot/lib/omitted-plugin-keys.js +3 -0
- package/boot/lib/parse-args-argv.js +75 -0
- package/boot/lib/parse-env.js +36 -0
- package/boot/lib/shim.js +14 -0
- package/boot/plugins/attach-helper.js +20 -0
- package/boot/plugins/build-config.js +75 -0
- package/boot/plugins/check-clash.js +18 -0
- package/boot/plugins/check-dependency.js +37 -0
- package/boot/plugins/collect-config-handlers.js +25 -0
- package/boot/plugins/collect-exit-handlers.js +23 -0
- package/boot/plugins/collect-hooks.js +33 -0
- package/boot/plugins/extend-config.js +21 -0
- package/boot/plugins/index.js +28 -0
- package/boot/plugins/run.js +31 -0
- package/boot/run-tool.js +34 -0
- package/docs/boot_build-config.js.html +75 -0
- package/docs/boot_create-scope.js.html +25 -0
- package/docs/boot_index.js.html +43 -0
- package/docs/data/search.json +1 -0
- 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 +18 -0
- package/docs/helper_envs.js.html +16 -0
- package/docs/helper_error.js.html +25 -0
- package/docs/helper_get-bajo.js.html +42 -0
- package/docs/helper_index.js.html +39 -0
- package/docs/helper_log-levels.js.html +20 -0
- package/docs/helper_set-hook.js.html +38 -0
- package/docs/helper_walk-bajos.js.html +51 -0
- package/docs/index.html +16 -0
- package/docs/module-boot.html +3 -0
- package/docs/module-boot_buildConfig.html +3 -0
- package/docs/module-boot_createScope.html +3 -0
- package/docs/module-helper.html +7 -0
- package/docs/module-helper_setHook.html +4 -0
- package/docs/module-helper_walkBajos.html +6 -0
- package/docs/scripts/core.js +655 -0
- package/docs/scripts/core.min.js +23 -0
- package/docs/scripts/resize.js +90 -0
- package/docs/scripts/search.js +265 -0
- package/docs/scripts/search.min.js +6 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/scripts/third-party/fuse.js +9 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/scripts/third-party/hljs-original.js +5171 -0
- package/docs/scripts/third-party/hljs.js +1 -0
- package/docs/scripts/third-party/popper.js +5 -0
- package/docs/scripts/third-party/tippy.js +1 -0
- package/docs/scripts/third-party/tocbot.js +672 -0
- package/docs/scripts/third-party/tocbot.min.js +1 -0
- package/docs/styles/clean-jsdoc-theme-base.css +975 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +407 -0
- package/docs/styles/clean-jsdoc-theme-light.css +388 -0
- package/docs/styles/clean-jsdoc-theme.min.css +1 -0
- package/package.json +36 -4
- package/test/helper-error.js +25 -0
- package/test/helper-isSet.js +41 -0
- package/test/helper-pathResolve.js +28 -0
package/.jsdoc.conf.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugins": ["plugins/markdown"],
|
|
3
|
+
"opts": {
|
|
4
|
+
"encoding": "utf8",
|
|
5
|
+
"readme": "./README.md",
|
|
6
|
+
"recurse": true,
|
|
7
|
+
"verbose": true,
|
|
8
|
+
"destination": "./docs",
|
|
9
|
+
"template": "node_modules/clean-jsdoc-theme",
|
|
10
|
+
"theme_opts": {
|
|
11
|
+
"default_theme": "dark",
|
|
12
|
+
"display-module-header": true,
|
|
13
|
+
"title": "Bajo Framework",
|
|
14
|
+
"homepageTitle": "Bajo Framework",
|
|
15
|
+
"menu": [{
|
|
16
|
+
"title": "NPM",
|
|
17
|
+
"link": "https://www.npmjs.com/package/bajo"
|
|
18
|
+
}, {
|
|
19
|
+
"title": "Github",
|
|
20
|
+
"link": "https://github.com/ardhi/bajo"
|
|
21
|
+
}]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"source": {
|
|
25
|
+
"include": ["."],
|
|
26
|
+
"includePattern": ".+\\.js(doc|x)?$",
|
|
27
|
+
"exclude": ["node_modules", "doc", "lib", "package-lock.json"]
|
|
28
|
+
},
|
|
29
|
+
"markdown": {
|
|
30
|
+
"hardwrap": false,
|
|
31
|
+
"idInHeadings": true
|
|
32
|
+
},
|
|
33
|
+
"sourceType": "module",
|
|
34
|
+
"templates": {
|
|
35
|
+
"cleverLinks": false,
|
|
36
|
+
"monospaceLinks": false
|
|
37
|
+
}
|
|
38
|
+
}
|
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# Bajo Framework
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
> Note: you must have a proper install of ```node```, ```npm``` and/or ```yarn``` before
|
|
6
|
+
|
|
7
|
+
Open your terminal and type:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install bajo
|
|
11
|
+
# or
|
|
12
|
+
yarn add bajo
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Fire up!
|
|
16
|
+
|
|
17
|
+
Goto your project directory, create the ```index.js``` file and put this in it:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const bajo = require('bajo')
|
|
21
|
+
bajo.boot()
|
|
22
|
+
.then(scope => {
|
|
23
|
+
})
|
|
24
|
+
.catch(err => {
|
|
25
|
+
console.trace(err)
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Bajo application **ALWAYS** need a data directory to put configuration files, etc. This
|
|
30
|
+
cloud be located inside or outside your project directory.
|
|
31
|
+
|
|
32
|
+
Lets assume you're going to put your data directory inside your project directory. So please
|
|
33
|
+
create a new directory called ```data``` first. After that, just type in your terminal:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
node index.js --dir-data=data
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or you could use ```dotenv``` by creating ```.env``` file in the same directory as ```index.js```, and put this inside:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
DIR_DATA = .\data
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Now you can omit calling node with arguments, you just need to type:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
node index.js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Documentation
|
|
52
|
+
|
|
53
|
+
[API](https://ardhi.github.io/bajo)
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
[MIT](LICENSE) License
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import buildHelper from './lib/build-helper.js'
|
|
2
|
+
import logger from './lib/logger.js'
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import utc from 'dayjs/plugin/utc.js'
|
|
6
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
|
|
7
|
+
import deepFreeze from 'deep-freeze-strict'
|
|
8
|
+
import currentLoc from './helper/current-loc.js'
|
|
9
|
+
|
|
10
|
+
dayjs.extend(utc)
|
|
11
|
+
dayjs.extend(customParseFormat)
|
|
12
|
+
|
|
13
|
+
export default async function () {
|
|
14
|
+
this.bajo.helper = await buildHelper.call(this, `${currentLoc(import.meta).dir}/helper`)
|
|
15
|
+
this.bajo.helper.freeze = (o, shallow) => {
|
|
16
|
+
if (shallow) Object.freeze(o)
|
|
17
|
+
else deepFreeze(o)
|
|
18
|
+
}
|
|
19
|
+
this.bajo.helper.log = logger.call(this)
|
|
20
|
+
this.bajo.helper.dayjs = dayjs
|
|
21
|
+
this.bajo.helper.freeze(this.bajo.helper, true)
|
|
22
|
+
// last cleanup
|
|
23
|
+
if (!fs.existsSync(this.bajo.config.dir.data)) {
|
|
24
|
+
this.bajo.helper.log.warn('Data directory \'%s\' is not set yet!', this.bajo.config.dir.data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { reduce, map, isNaN, trim, forOwn, orderBy } from 'lodash-es'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
import getModuleDir from './helper/get-module-dir.js'
|
|
4
|
+
|
|
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) => {
|
|
10
|
+
const key = map(k.split(':'), m => trim(m))
|
|
11
|
+
if (key[1] && !isNaN(Number(key[1]))) o[key[0]] = Number(key[1])
|
|
12
|
+
else o[key[0]] = 10000 + i
|
|
13
|
+
return o
|
|
14
|
+
}, {})
|
|
15
|
+
const norder = {}
|
|
16
|
+
for (let n of config.plugins) {
|
|
17
|
+
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)
|
|
20
|
+
norder[n] = NaN
|
|
21
|
+
try {
|
|
22
|
+
norder[n] = Number(trim(await fs.readFile(`${dir}/bajo/.bootorder`, 'utf8')))
|
|
23
|
+
} catch (err) {}
|
|
24
|
+
}
|
|
25
|
+
const result = []
|
|
26
|
+
forOwn(order, (v, k) => {
|
|
27
|
+
const item = { k, v: isNaN(norder[k]) ? v : norder[k] }
|
|
28
|
+
result.push(item)
|
|
29
|
+
})
|
|
30
|
+
config.plugins = map(orderBy(result, ['v']), 'k')
|
|
31
|
+
log.info('Run in \'%s\' environment', envs[config.env])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default bootOrder
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module boot/buildConfig
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import os from 'os'
|
|
6
|
+
import fs from 'fs-extra'
|
|
7
|
+
import { get, set, pick, values, keys, uniq, without, filter, map, isEmpty, trim } from 'lodash-es'
|
|
8
|
+
import omitDeep from 'omit-deep'
|
|
9
|
+
import resolvePath from './helper/resolve-path.js'
|
|
10
|
+
import readConfig from './helper/read-config.js'
|
|
11
|
+
import getKeyByValue from './helper/get-key-by-value.js'
|
|
12
|
+
import envs from './helper/envs.js'
|
|
13
|
+
import defaultsDeep from './helper/defaults-deep.js'
|
|
14
|
+
import parseArgsArgv from './lib/parse-args-argv.js'
|
|
15
|
+
import parseEnv from './lib/parse-env.js'
|
|
16
|
+
import error from './helper/error.js'
|
|
17
|
+
|
|
18
|
+
const configFilePick = ['log', 'plugins', 'env', 'run']
|
|
19
|
+
const configFileOmit = ['tool', 'spawn', 'cwd']
|
|
20
|
+
|
|
21
|
+
const defConfig = {
|
|
22
|
+
dir: {},
|
|
23
|
+
log: {
|
|
24
|
+
dateFormat: 'YYYY-MM-DDTHH:MM:ss.SSS[Z]',
|
|
25
|
+
report: [],
|
|
26
|
+
tool: false
|
|
27
|
+
},
|
|
28
|
+
lang: Intl.DateTimeFormat().resolvedOptions().lang,
|
|
29
|
+
plugins: ['app'],
|
|
30
|
+
env: 'dev',
|
|
31
|
+
tool: false,
|
|
32
|
+
exitHandler: true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Building configuration object. Read configurtion file from app data directory, program
|
|
37
|
+
* arguments and envoronment variables with following priority: ```Env > Args > Config file >
|
|
38
|
+
* defaults config```
|
|
39
|
+
*
|
|
40
|
+
* If data directory is provided and doesn't exist yet, it will be automatically created.
|
|
41
|
+
*
|
|
42
|
+
* Config file must be located in: ```<data dir>/config/bajo.<format>```, and support either
|
|
43
|
+
* ```.json``` or ```.js``` format. JS format must be a nodejs module that wrap an async
|
|
44
|
+
* function and return an object
|
|
45
|
+
*
|
|
46
|
+
* If app run tool, by default log is in silent. To activate, set log.tool to true
|
|
47
|
+
*
|
|
48
|
+
* By default, if 'app' is a bajo app, it is always the last plugins to boot. To overide this
|
|
49
|
+
* behavior, you should configure it at plugin level (i.e. using .bootorder file)
|
|
50
|
+
*
|
|
51
|
+
* @instance
|
|
52
|
+
* @async
|
|
53
|
+
* @throws Will throw if data directory is not provided
|
|
54
|
+
*
|
|
55
|
+
* @returns {Object} config
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
async function buildConfig (cwd) {
|
|
59
|
+
const { args, argv } = await parseArgsArgv()
|
|
60
|
+
const env = parseEnv()
|
|
61
|
+
const envArgv = defaultsDeep({}, env.root, argv.root)
|
|
62
|
+
// directories
|
|
63
|
+
set(envArgv, 'dir.base', cwd)
|
|
64
|
+
if (!get(envArgv, 'dir.data')) set(envArgv, 'dir.data', `${envArgv.dir.base}/data`)
|
|
65
|
+
envArgv.dir.data = resolvePath(envArgv.dir.data)
|
|
66
|
+
if (!envArgv.dir.tmp) {
|
|
67
|
+
envArgv.dir.tmp = resolvePath(os.tmpdir()) + '/bajo'
|
|
68
|
+
fs.ensureDirSync(envArgv.dir.tmp)
|
|
69
|
+
}
|
|
70
|
+
// config merging
|
|
71
|
+
let resp = await readConfig.call(this, `${envArgv.dir.data}/config/bajo.*`, { ignoreError: true })
|
|
72
|
+
resp = omitDeep(pick(resp, configFilePick), configFileOmit)
|
|
73
|
+
const config = defaultsDeep({}, envArgv, resp, defConfig)
|
|
74
|
+
// force init
|
|
75
|
+
config.args = args
|
|
76
|
+
config.env = config.env.toLowerCase()
|
|
77
|
+
if (values(envs).includes(config.env)) config.env = getKeyByValue(envs, config.env)
|
|
78
|
+
if (!keys(envs).includes(config.env)) config.env = 'dev'
|
|
79
|
+
process.env.NODE_ENV = envs[config.env]
|
|
80
|
+
if (!config.log.level) config.log.level = config.env === 'dev' ? 'debug' : 'info'
|
|
81
|
+
if (config.silent) config.log.level = 'silent'
|
|
82
|
+
// sanitize plugins
|
|
83
|
+
config.plugins = without(config.plugins, 'app')
|
|
84
|
+
if (fs.existsSync(`${config.dir.base}/app/bajo`)) config.plugins.push('app')
|
|
85
|
+
config.plugins = filter(uniq(map(config.plugins, b => trim(b))), b => !isEmpty(b))
|
|
86
|
+
if (config.tool) {
|
|
87
|
+
if (!config.plugins.includes('bajo-cli')) throw error('Sidetool needs to have \'bajo-cli\' package loaded first')
|
|
88
|
+
if (!config.log.tool) config.log.level = 'silent'
|
|
89
|
+
config.exitHandler = false
|
|
90
|
+
}
|
|
91
|
+
this.bajo.config = config
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default buildConfig
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module boot/createScope
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { isFunction } from 'lodash-es'
|
|
6
|
+
import readJson from './helper/read-json.js'
|
|
7
|
+
import importModule from './helper/import-module.js'
|
|
8
|
+
|
|
9
|
+
async function defHandler (file) {
|
|
10
|
+
let mod = await importModule(file)
|
|
11
|
+
if (isFunction(mod)) mod = await mod.call(this)
|
|
12
|
+
return mod
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const configHandlers = [
|
|
16
|
+
{ ext: '.js', handler: defHandler },
|
|
17
|
+
{ ext: '.mjs', handler: defHandler },
|
|
18
|
+
{ ext: '.json', handler: readJson }
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const bajo = {
|
|
22
|
+
runAt: new Date(),
|
|
23
|
+
configHandlers
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @instance
|
|
28
|
+
* @async
|
|
29
|
+
* @returns {Object} scope
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
function createScope () {
|
|
33
|
+
return { bajo }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default createScope
|
|
@@ -0,0 +1,60 @@
|
|
|
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]
|
|
6
|
+
if (!handler) return undefined
|
|
7
|
+
await handler.call(this)
|
|
8
|
+
log.debug('Exited: %s', plugin)
|
|
9
|
+
})
|
|
10
|
+
log.debug('Program shutdown')
|
|
11
|
+
process.exit(0)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function exitHandler () {
|
|
15
|
+
const { log, getConfig } = this.bajo.helper
|
|
16
|
+
const config = getConfig()
|
|
17
|
+
if (!config.exitHandler) return
|
|
18
|
+
|
|
19
|
+
log.debug('Exit handlings')
|
|
20
|
+
|
|
21
|
+
process.on('SIGINT', async () => {
|
|
22
|
+
await exit.call(this, 'SIGINT')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
process.on('SIGTERM', async () => {
|
|
26
|
+
await exit.call(this, 'SIGTERM')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
process.on('uncaughtException', (error, origin) => {
|
|
30
|
+
const { getConfig } = this.bajo.helper
|
|
31
|
+
const config = getConfig()
|
|
32
|
+
if (config.log.report.includes('sys:uncaughtException')) log.fatal({ origin }, '%s', error.message)
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
console.error(error)
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}, 50)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
40
|
+
const stackFile = reason.stack.split('\n')[1]
|
|
41
|
+
let file
|
|
42
|
+
const info = stackFile.match(/\((.*)\)/) // file is in (<file>)
|
|
43
|
+
if (info) file = info[1]
|
|
44
|
+
else if (stackFile.startsWith(' at ')) file = stackFile.slice(7) // file is stackFile itself
|
|
45
|
+
if (!file) return
|
|
46
|
+
const parts = file.split(':')
|
|
47
|
+
const column = parseInt(parts[parts.length - 1])
|
|
48
|
+
const line = parseInt(parts[parts.length - 2])
|
|
49
|
+
parts.pop()
|
|
50
|
+
parts.pop()
|
|
51
|
+
file = parts.join(':')
|
|
52
|
+
log.error({ file, line, column }, '%s', reason.message)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
process.on('warning', warning => {
|
|
56
|
+
log.error('%s', warning.message)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default exitHandler
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { filter, isArray, each, pullAt, camelCase } from 'lodash-es'
|
|
2
|
+
|
|
3
|
+
async function buildCollections ({ name, handler, dupChecks, container = 'connections' } = {}) {
|
|
4
|
+
const { getConfig, getPluginName, fatal, runHook } = this.bajo.helper
|
|
5
|
+
if (!name) name = getPluginName(4)
|
|
6
|
+
const options = getConfig(name, { full: true })
|
|
7
|
+
if (!options[container]) return []
|
|
8
|
+
if (!isArray(options[container])) options[container] = [options[container]]
|
|
9
|
+
options[container] = options[container] ?? []
|
|
10
|
+
await runHook(`${name}:${camelCase(`before build ${container}`)}`)
|
|
11
|
+
const deleted = []
|
|
12
|
+
for (const index in options[container]) {
|
|
13
|
+
const item = options[container][index]
|
|
14
|
+
const result = await handler.call(this, { item, index, options })
|
|
15
|
+
if (result) options[container][index] = result
|
|
16
|
+
else if (result === false) deleted.push(index)
|
|
17
|
+
}
|
|
18
|
+
if (deleted.length > 0) pullAt(options[container], deleted)
|
|
19
|
+
|
|
20
|
+
// check for duplicity
|
|
21
|
+
each(options[container], c => {
|
|
22
|
+
const checker = {}
|
|
23
|
+
each(dupChecks, d => {
|
|
24
|
+
checker[d] = c[d]
|
|
25
|
+
})
|
|
26
|
+
const match = filter(options[container], checker)
|
|
27
|
+
if (match.length > 1) fatal('One or more %s shared the same \'%s\'', container, dupChecks.join(', '), { code: 'BAJOMQTT_CONNECTION_NOT_UNIQUE' })
|
|
28
|
+
})
|
|
29
|
+
/*
|
|
30
|
+
each(dupChecks, item => {
|
|
31
|
+
const items = map(options[container], item)
|
|
32
|
+
const uItems = uniq(items)
|
|
33
|
+
if (items.length !== uItems.length) fatal('One or more %s shared the same \'%s\'', container, item, { code: 'BAJOMQTT_CONNECTION_NOT_UNIQUE' })
|
|
34
|
+
})
|
|
35
|
+
*/
|
|
36
|
+
await runHook(`${name}:${camelCase(`after build ${container}`)}`)
|
|
37
|
+
return options[container]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default buildCollections
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isString, isFunction } from 'lodash-es'
|
|
2
|
+
import getHelper from './get-helper.js'
|
|
3
|
+
|
|
4
|
+
async function callHelperOrHandler (nameOrFn, ...args) {
|
|
5
|
+
let result
|
|
6
|
+
if (isString(nameOrFn)) {
|
|
7
|
+
const helper = getHelper.call(this, nameOrFn)
|
|
8
|
+
if (isFunction(helper)) result = await helper(...args)
|
|
9
|
+
} else if (isFunction(nameOrFn)) {
|
|
10
|
+
result = await nameOrFn.call(this, ...args)
|
|
11
|
+
}
|
|
12
|
+
return result
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default callHelperOrHandler
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import resolvePath from './resolve-path.js'
|
|
3
|
+
import { fileURLToPath } from 'url'
|
|
4
|
+
|
|
5
|
+
const currentLoc = (meta) => {
|
|
6
|
+
const file = resolvePath(fileURLToPath(meta.url))
|
|
7
|
+
const dir = path.dirname(file)
|
|
8
|
+
return { dir, file, __dirname: dir, __filename: file }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default currentLoc
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mergeWith, isArray } from 'lodash-es'
|
|
2
|
+
// taken from https://github.com/nodeutils/defaults-deep/blob/master/lib/index.js
|
|
3
|
+
|
|
4
|
+
const defaultsDeep = (...args) => {
|
|
5
|
+
const output = {}
|
|
6
|
+
args.reverse().forEach(function (item) {
|
|
7
|
+
mergeWith(output, item, function (objectValue, sourceValue) {
|
|
8
|
+
return isArray(sourceValue) ? sourceValue : undefined
|
|
9
|
+
})
|
|
10
|
+
})
|
|
11
|
+
return output
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default defaultsDeep
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { camelCase, isString, omit, isPlainObject, slice } from 'lodash-es'
|
|
2
|
+
import fastGlob from 'fast-glob'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import omittedPluginKeys from '../lib/omitted-plugin-keys.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module helper/eachPlugins
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Callback function that will be executed while walking through all Bajos
|
|
12
|
+
*
|
|
13
|
+
* @callback handlerFn
|
|
14
|
+
* @async
|
|
15
|
+
* @param {Object} argument - Provides information about current Bajo
|
|
16
|
+
* @param {string} argument.name - Bajo's name
|
|
17
|
+
* @param {string} argument.pkgName - Bajo's package name
|
|
18
|
+
* @param {Object} argument.cfg - Bajo's config object
|
|
19
|
+
* @returns {Object|boolean|undefined}
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Walk through all plugins and execute the callback handler
|
|
24
|
+
*
|
|
25
|
+
* @instance
|
|
26
|
+
* @async
|
|
27
|
+
* @param {function} handlerFn - [The callback]{@link module:helper/eachPlugins~handlerFn}
|
|
28
|
+
* @param {Object} [options] - Optional parameter
|
|
29
|
+
* @param {string} [options.key=name] - Key of Bajo's config object that will be used as the key of returned object
|
|
30
|
+
* @returns {Object} Results from callback execution through all Bajos
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const { eachPlugins } = this.bajo.helper
|
|
34
|
+
* await eachPlugins(async function ({ name }) => {
|
|
35
|
+
* console.log(name)
|
|
36
|
+
* })
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
async function eachPlugins (handler, { key = 'name', glob, ns } = {}) {
|
|
40
|
+
const { getConfig, getPluginName } = this.bajo.helper
|
|
41
|
+
const config = getConfig()
|
|
42
|
+
const result = {}
|
|
43
|
+
ns = ns ?? getPluginName(4)
|
|
44
|
+
for (const pkg of config.plugins) {
|
|
45
|
+
const plugin = camelCase(pkg)
|
|
46
|
+
let cfg = getConfig(plugin, { full: true })
|
|
47
|
+
const { alias, dir, dependencies } = cfg
|
|
48
|
+
cfg = omit(cfg, omittedPluginKeys)
|
|
49
|
+
let r
|
|
50
|
+
if (glob) {
|
|
51
|
+
const base = `${dir}/${ns}`
|
|
52
|
+
let pattern
|
|
53
|
+
let opts
|
|
54
|
+
if (isPlainObject(glob) && glob.pattern) pattern = glob.pattern
|
|
55
|
+
else {
|
|
56
|
+
if (isString(glob)) pattern = [glob]
|
|
57
|
+
for (const i in pattern) {
|
|
58
|
+
pattern[i] = `${base}/${pattern[i]}`
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const files = await fastGlob(pattern, opts)
|
|
62
|
+
for (const f of files) {
|
|
63
|
+
const rel = f.replace(base, '')
|
|
64
|
+
const b = path.basename(rel, path.extname(rel))
|
|
65
|
+
const relDir = path.dirname(rel)
|
|
66
|
+
const relDirBase = `${relDir}/${b}`
|
|
67
|
+
const relName = slice(relDirBase.split('/'), 2).join('/')
|
|
68
|
+
const fileInfo = {
|
|
69
|
+
rel,
|
|
70
|
+
relDir,
|
|
71
|
+
relDirBase,
|
|
72
|
+
base: b,
|
|
73
|
+
name: camelCase(`${relName}`),
|
|
74
|
+
nameWithPlugin: camelCase(`${plugin} ${relName}`)
|
|
75
|
+
}
|
|
76
|
+
const resp = await handler.call(this, { plugin, pkg, cfg, alias, file: f, dir: base, dependencies, fileInfo })
|
|
77
|
+
if (resp === false) break
|
|
78
|
+
else if (resp === undefined) continue
|
|
79
|
+
else {
|
|
80
|
+
result[plugin] = result[plugin] ?? {}
|
|
81
|
+
result[plugin][f] = resp
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
r = await handler.call(this, { plugin, pkg, cfg, dir, alias, dependencies })
|
|
86
|
+
if (r === false) break
|
|
87
|
+
else if (r === undefined) continue
|
|
88
|
+
else result[plugin] = r
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default eachPlugins
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { last, isPlainObject } from 'lodash-es'
|
|
2
|
+
import print from './print.js'
|
|
3
|
+
import getPluginName from './get-plugin-name.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* It's a shortcut to create an instance of Error with message and optional parameter
|
|
7
|
+
* in a single line
|
|
8
|
+
*
|
|
9
|
+
* @memberof helper
|
|
10
|
+
* @type Object
|
|
11
|
+
* @instance
|
|
12
|
+
* @param {string} msg - String that will be used as error message
|
|
13
|
+
* @param {options} [options] - Optional parameter
|
|
14
|
+
* @param {string} [options.code] - Error code
|
|
15
|
+
* @returns {error} Instance of Error
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
Error.stackTraceLimit = 15
|
|
19
|
+
|
|
20
|
+
function error (msg = 'Internal server error', ...args) {
|
|
21
|
+
let payload = last(args)
|
|
22
|
+
let ns
|
|
23
|
+
if (isPlainObject(payload)) {
|
|
24
|
+
payload = args.pop()
|
|
25
|
+
ns = payload.ns
|
|
26
|
+
}
|
|
27
|
+
if (!ns) ns = getPluginName.call(this, 3)
|
|
28
|
+
const message = print._format.call(this, ns, msg, ...args)
|
|
29
|
+
let err
|
|
30
|
+
if (isPlainObject(payload) && payload.class) err = payload.class(message)
|
|
31
|
+
else err = Error(message)
|
|
32
|
+
const stacks = err.stack.split('\n')
|
|
33
|
+
stacks.splice(1, 1) // this file
|
|
34
|
+
if (stacks[1].includes('/helper/fatal.js')) stacks.splice(1, 1) // if it goes to fatal.js
|
|
35
|
+
stacks.splice(1, 1) // for buildHelper.js
|
|
36
|
+
err.stack = stacks.join('\n')
|
|
37
|
+
if (isPlainObject(payload)) {
|
|
38
|
+
delete payload.class
|
|
39
|
+
delete payload.ns
|
|
40
|
+
for (const key in payload) {
|
|
41
|
+
err[key] = payload[key]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return err
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default error
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import error from './error.js'
|
|
2
|
+
import getPluginName from './get-plugin-name.js'
|
|
3
|
+
|
|
4
|
+
function fatal (...args) {
|
|
5
|
+
const ns = getPluginName.call(this, 3)
|
|
6
|
+
args.push({ ns })
|
|
7
|
+
const err = error(...args)
|
|
8
|
+
console.error(err)
|
|
9
|
+
process.exit(1)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default fatal
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { cloneDeep, isPlainObject } from 'lodash-es'
|
|
2
|
+
import { customAlphabet } from 'nanoid'
|
|
3
|
+
|
|
4
|
+
const generateId = ({ pattern, length = 21, returnInstance } = {}) => {
|
|
5
|
+
let opts = {}
|
|
6
|
+
if (isPlainObject(pattern)) {
|
|
7
|
+
opts = cloneDeep(pattern)
|
|
8
|
+
returnInstance = opts.returnInstance
|
|
9
|
+
length = opts.length
|
|
10
|
+
pattern = opts.pattern
|
|
11
|
+
}
|
|
12
|
+
pattern = pattern ?? 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
13
|
+
if (opts.lowerCase) pattern = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
|
14
|
+
else if (opts.upperCase) pattern = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
15
|
+
const nid = customAlphabet(pattern, length)
|
|
16
|
+
return returnInstance ? nid : nid()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default generateId
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isEmpty, isPlainObject, cloneDeep, omit, find } from 'lodash-es'
|
|
2
|
+
import omittedPluginKeys from '../lib/omitted-plugin-keys.js'
|
|
3
|
+
|
|
4
|
+
function getConfig (name, { full, clone } = {}) {
|
|
5
|
+
if (name === 'bajo' || isEmpty(name)) return this.bajo.config
|
|
6
|
+
if (this[name] && isPlainObject(this[name].config) && this[name].config.name === name) {
|
|
7
|
+
const cfg = clone ? cloneDeep(this[name].config) : this[name].config
|
|
8
|
+
return full ? cfg : omit(cfg, omittedPluginKeys)
|
|
9
|
+
}
|
|
10
|
+
const ref = find(this.bajo.pluginRefs ?? [], { alias: name })
|
|
11
|
+
if (ref) {
|
|
12
|
+
const cfg = clone ? cloneDeep(this[ref.name].config) : this[ref.name].config
|
|
13
|
+
return full ? cfg : omit(cfg, omittedPluginKeys)
|
|
14
|
+
}
|
|
15
|
+
return {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default getConfig
|