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.
Files changed (107) hide show
  1. package/.jsdoc.conf.json +38 -0
  2. package/README.md +57 -3
  3. package/boot/attach-helper.js +26 -0
  4. package/boot/boot-order.js +34 -0
  5. package/boot/build-config.js +94 -0
  6. package/boot/create-scope.js +36 -0
  7. package/boot/exit-handler.js +60 -0
  8. package/boot/helper/build-collections.js +40 -0
  9. package/boot/helper/call-helper-or-handler.js +15 -0
  10. package/boot/helper/current-loc.js +11 -0
  11. package/boot/helper/defaults-deep.js +14 -0
  12. package/boot/helper/dump.js +10 -0
  13. package/boot/helper/each-plugins.js +94 -0
  14. package/boot/helper/envs.js +14 -0
  15. package/boot/helper/error.js +47 -0
  16. package/boot/helper/fatal.js +12 -0
  17. package/boot/helper/generate-id.js +19 -0
  18. package/boot/helper/get-config.js +18 -0
  19. package/boot/helper/get-global-module-dir.js +27 -0
  20. package/boot/helper/get-helper.js +11 -0
  21. package/boot/helper/get-item-by-name.js +26 -0
  22. package/boot/helper/get-key-by-value.js +5 -0
  23. package/boot/helper/get-module-dir.js +22 -0
  24. package/boot/helper/get-plugin-name.js +39 -0
  25. package/boot/helper/get-plugin.js +7 -0
  26. package/boot/helper/import-module.js +25 -0
  27. package/boot/helper/import-pkg.js +67 -0
  28. package/boot/helper/index.js +36 -0
  29. package/boot/helper/is-empty-dir.js +9 -0
  30. package/boot/helper/is-log-in-range.js +10 -0
  31. package/boot/helper/is-set.js +5 -0
  32. package/boot/helper/is-valid-app.js +12 -0
  33. package/boot/helper/is-valid-plugin.js +12 -0
  34. package/boot/helper/log-levels.js +19 -0
  35. package/boot/helper/pascal-case.js +7 -0
  36. package/boot/helper/print.js +89 -0
  37. package/boot/helper/read-config.js +46 -0
  38. package/boot/helper/read-json.js +10 -0
  39. package/boot/helper/resolve-path.js +15 -0
  40. package/boot/helper/resolve-tpl-path.js +21 -0
  41. package/boot/helper/run-hook.js +46 -0
  42. package/boot/helper/save-as-download.js +18 -0
  43. package/boot/helper/white-space.js +3 -0
  44. package/boot/index.js +60 -0
  45. package/boot/lib/bora.js +97 -0
  46. package/boot/lib/build-helper.js +58 -0
  47. package/boot/lib/logger.js +75 -0
  48. package/boot/lib/omitted-plugin-keys.js +3 -0
  49. package/boot/lib/parse-args-argv.js +75 -0
  50. package/boot/lib/parse-env.js +36 -0
  51. package/boot/lib/shim.js +14 -0
  52. package/boot/plugins/attach-helper.js +20 -0
  53. package/boot/plugins/build-config.js +75 -0
  54. package/boot/plugins/check-clash.js +18 -0
  55. package/boot/plugins/check-dependency.js +37 -0
  56. package/boot/plugins/collect-config-handlers.js +25 -0
  57. package/boot/plugins/collect-exit-handlers.js +23 -0
  58. package/boot/plugins/collect-hooks.js +33 -0
  59. package/boot/plugins/extend-config.js +21 -0
  60. package/boot/plugins/index.js +28 -0
  61. package/boot/plugins/run.js +31 -0
  62. package/boot/run-tool.js +34 -0
  63. package/docs/boot_build-config.js.html +75 -0
  64. package/docs/boot_create-scope.js.html +25 -0
  65. package/docs/boot_index.js.html +43 -0
  66. package/docs/data/search.json +1 -0
  67. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  68. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  69. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  70. package/docs/helper_emit.js.html +18 -0
  71. package/docs/helper_envs.js.html +16 -0
  72. package/docs/helper_error.js.html +25 -0
  73. package/docs/helper_get-bajo.js.html +42 -0
  74. package/docs/helper_index.js.html +39 -0
  75. package/docs/helper_log-levels.js.html +20 -0
  76. package/docs/helper_set-hook.js.html +38 -0
  77. package/docs/helper_walk-bajos.js.html +51 -0
  78. package/docs/index.html +16 -0
  79. package/docs/module-boot.html +3 -0
  80. package/docs/module-boot_buildConfig.html +3 -0
  81. package/docs/module-boot_createScope.html +3 -0
  82. package/docs/module-helper.html +7 -0
  83. package/docs/module-helper_setHook.html +4 -0
  84. package/docs/module-helper_walkBajos.html +6 -0
  85. package/docs/scripts/core.js +655 -0
  86. package/docs/scripts/core.min.js +23 -0
  87. package/docs/scripts/resize.js +90 -0
  88. package/docs/scripts/search.js +265 -0
  89. package/docs/scripts/search.min.js +6 -0
  90. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  91. package/docs/scripts/third-party/fuse.js +9 -0
  92. package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
  93. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  94. package/docs/scripts/third-party/hljs-original.js +5171 -0
  95. package/docs/scripts/third-party/hljs.js +1 -0
  96. package/docs/scripts/third-party/popper.js +5 -0
  97. package/docs/scripts/third-party/tippy.js +1 -0
  98. package/docs/scripts/third-party/tocbot.js +672 -0
  99. package/docs/scripts/third-party/tocbot.min.js +1 -0
  100. package/docs/styles/clean-jsdoc-theme-base.css +975 -0
  101. package/docs/styles/clean-jsdoc-theme-dark.css +407 -0
  102. package/docs/styles/clean-jsdoc-theme-light.css +388 -0
  103. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  104. package/package.json +36 -4
  105. package/test/helper-error.js +25 -0
  106. package/test/helper-isSet.js +41 -0
  107. package/test/helper-pathResolve.js +28 -0
@@ -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
- # bajo
2
-
3
- In development. Stay tune!
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,10 @@
1
+ import util from 'util'
2
+
3
+ const dump = (...args) => {
4
+ for (const arg of args) {
5
+ const result = util.inspect(arg, false, null, true)
6
+ console.log(result)
7
+ }
8
+ }
9
+
10
+ export default dump
@@ -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,14 @@
1
+ /**
2
+ * Supported environment
3
+ *
4
+ * @memberof module:helper
5
+ * @type Object
6
+ * @instance
7
+ */
8
+
9
+ const envs = {
10
+ dev: 'development',
11
+ prod: 'production'
12
+ }
13
+
14
+ export default envs
@@ -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