bajo 2.2.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/class/app.js CHANGED
@@ -1,43 +1,14 @@
1
1
  import util from 'util'
2
- import lodash from 'lodash'
3
2
  import Bajo from './bajo.js'
4
- import fastGlob from 'fast-glob'
5
- import { sprintf } from 'sprintf-js'
6
- import outmatch from 'outmatch'
7
- import fs from 'fs-extra'
8
- import aneka from 'aneka/index.js'
9
3
  import Base from './base.js'
10
- import freeze from '../lib/freeze.js'
11
4
  import { runAsApplet } from './helper/bajo.js'
12
5
  import Tools from './plugin/tools.js'
13
- import dayjs from 'dayjs'
14
- import utc from 'dayjs/plugin/utc.js'
15
- import customParseFormat from 'dayjs/plugin/customParseFormat.js'
16
- import localizedFormat from 'dayjs/plugin/localizedFormat.js'
17
- import weekOfYear from 'dayjs/plugin/weekOfYear.js'
18
- import findDeep from '../lib/find-deep.js'
19
-
20
- dayjs.extend(utc)
21
- dayjs.extend(customParseFormat)
22
- dayjs.extend(localizedFormat)
23
- dayjs.extend(weekOfYear)
24
-
25
- const { camelCase, isPlainObject, get, reverse, map, last, without } = lodash
26
- const { pascalCase } = aneka
27
- let unknownLangWarning = false
28
6
 
29
- function outmatchNs (source, pattern) {
30
- const { breakNsPath } = this.bajo
31
- const [src, subSrc] = source.split(':')
32
- if (!subSrc) return pattern === src
33
- try {
34
- const { fullNs, path } = breakNsPath(pattern)
35
- const isMatch = outmatch(path)
36
- return src === fullNs && isMatch(subSrc)
37
- } catch (err) {
38
- return false
39
- }
40
- }
7
+ import { outmatchNs, parseObject, lib } from './helper/app.js'
8
+
9
+ const { camelCase, isPlainObject, get, reverse, map, last, without } = lib._
10
+ const { pascalCase } = lib.aneka
11
+ let unknownLangWarning = false
41
12
 
42
13
  /**
43
14
  * @typedef {Object} TAppEnv
@@ -46,31 +17,6 @@ function outmatchNs (source, pattern) {
46
17
  * @see App
47
18
  */
48
19
 
49
- /**
50
- * @typedef {Object} TAppLib
51
- * @property {Object} _ - Access to {@link https://lodash.com|lodash}
52
- * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
53
- * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
54
- * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
55
- * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
56
- * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
57
- * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
58
- * @property {Object} freeze
59
- * @property {Object} findDeep
60
- * @see App
61
- */
62
- const lib = {
63
- _: lodash,
64
- fs,
65
- fastGlob,
66
- sprintf,
67
- outmatch,
68
- dayjs,
69
- aneka,
70
- freeze,
71
- findDeep
72
- }
73
-
74
20
  /**
75
21
  * App class. This is the root. This is where all plugins call it home.
76
22
  *
@@ -78,9 +24,19 @@ const lib = {
78
24
  */
79
25
  class App {
80
26
  /**
81
- * @param {string} cwd - Current working dirctory
27
+ * @param {Object} [options] - App options
28
+ * @param {string} [options.cwd] - Set current working directory. Defaults to the script directory
29
+ * @param {string[]} [options.plugins] - Array of plugins to load. If provided, it override the list in ```package.json``` and ```.plugins``` file
30
+ * @param {Object} [options.config] - Plugin's config object. If provided, plugin configs will no longer be read from its config files
82
31
  */
83
- constructor (cwd) {
32
+ constructor (options = {}) {
33
+ /**
34
+ * Copy of provided options
35
+ *
36
+ * @type {Object}
37
+ */
38
+ this.options = options
39
+
84
40
  /**
85
41
  * Your main namespace. And yes, you suppose to NOT CHANGE this
86
42
  *
@@ -115,7 +71,7 @@ class App {
115
71
  *
116
72
  * @type {Array}
117
73
  */
118
- this.pluginPkgs = []
74
+ this.pluginPkgs = options.plugins ?? []
119
75
 
120
76
  /**
121
77
  * @typedef {Object} TAppConfigHandler
@@ -150,20 +106,7 @@ class App {
150
106
  */
151
107
  this.lib = lib
152
108
  this.lib.outmatchNs = outmatchNs.bind(this)
153
- this.lib.parseObject = (obj, options = {}) => {
154
- const me = this
155
- const { ns = 'bajo', lang } = options
156
- options.translator = {
157
- lang,
158
- prefix: 't:',
159
- handler: val => {
160
- const [text, ...args] = val.split('|')
161
- args.push({ lang })
162
- return me[ns].t(text, ...args)
163
- }
164
- }
165
- return aneka.parseObject(obj, options)
166
- }
109
+ this.lib.parseObject = parseObject.bind(this)
167
110
 
168
111
  /**
169
112
  * Instance of system log
@@ -253,13 +196,13 @@ class App {
253
196
  */
254
197
  this.envVars = {}
255
198
 
256
- if (!cwd) cwd = process.cwd()
199
+ if (!options.cwd) options.cwd = process.cwd()
257
200
  const l = last(process.argv)
258
201
  if (l.startsWith('--cwd')) {
259
202
  const parts = l.split('=')
260
- cwd = parts[1]
203
+ options.cwd = parts[1]
261
204
  }
262
- this.dir = aneka.resolvePath(cwd)
205
+ this.dir = this.lib.aneka.resolvePath(options.cwd)
263
206
  process.env.APPDIR = this.dir
264
207
  }
265
208
 
@@ -320,17 +263,20 @@ class App {
320
263
  * @fires bajo:afterBootCompleted
321
264
  */
322
265
  boot = async () => {
323
- // argv/args/env
324
266
  this.bajo = new Bajo(this)
325
- const { argv, args } = await aneka.parseArgsArgv() ?? {}
267
+ // argv/args/env
268
+ const { parseArgsArgv, parseEnv, secToHms } = this.lib.aneka
269
+ const { parseObject } = this.lib
270
+ const { argv, args } = await parseArgsArgv({ cwd: this.options.cwd })
271
+
326
272
  this.args = args
327
- this.argv = aneka.parseObject(argv, { parseValue: true })
328
- this.envVars = aneka.parseObject(aneka.parseEnv(), { parseValue: true })
273
+ this.argv = parseObject(argv, { parseValue: true })
274
+ this.envVars = parseObject(parseEnv(), { parseValue: true })
329
275
  this.applet = this.envVars._.applet ?? this.argv._.applet
330
276
  await this.bajo.init()
331
277
  // boot complete
332
278
  const elapsed = new Date() - this.runAt
333
- this.bajo.log.debug('bootCompleted%s', aneka.secToHms(elapsed, true))
279
+ this.bajo.log.debug('bootCompleted%s', secToHms(elapsed, true))
334
280
  /**
335
281
  * Run after boot process is completed
336
282
  *
@@ -362,6 +308,8 @@ class App {
362
308
  * @param {string} ns - Plugin name
363
309
  */
364
310
  loadIntl = (ns) => {
311
+ const { fs } = this.lib
312
+
365
313
  this[ns].intl = {}
366
314
  for (const l of this.bajo.config.intl.supported) {
367
315
  this[ns].intl[l] = {}
@@ -393,6 +341,8 @@ class App {
393
341
  * @returns {string}
394
342
  */
395
343
  t = (ns, text, ...params) => {
344
+ const { formatText } = this.lib.aneka
345
+
396
346
  if (!text) {
397
347
  text = ns
398
348
  ns = 'bajo'
@@ -426,7 +376,7 @@ class App {
426
376
  }
427
377
  }
428
378
  if (!trans) trans = text
429
- return aneka.formatText(trans, ...params)
379
+ return formatText(trans, ...params)
430
380
  }
431
381
 
432
382
  /**
package/class/base.js CHANGED
@@ -38,11 +38,12 @@ class Base extends Plugin {
38
38
  */
39
39
  loadConfig = async () => {
40
40
  const { defaultsDeep } = this.app.lib.aneka
41
- const { get, keys, pick } = this.app.lib._
41
+ const { get, keys, pick, isEmpty } = this.app.lib._
42
42
  const { log, getModuleDir, readAllConfigs } = this.app.bajo
43
43
  const { parseObject } = this.app.lib
44
44
 
45
- const defKeys = keys(this.config)
45
+ const defKeys = keys(this.config).concat(this.app.getAllNs())
46
+ defKeys.push('title')
46
47
  log.trace('- %s', this.ns)
47
48
  const dir = this.ns === this.app.mainNs ? (`${this.app.bajo.dir.base}/${this.app.mainNs}`) : getModuleDir(this.pkgName)
48
49
  let cfg = await readAllConfigs(`${dir}/config`)
@@ -53,14 +54,14 @@ class Base extends Plugin {
53
54
  }
54
55
  // merge with config from datadir
55
56
  try {
56
- const altCfg = await readAllConfigs(`${this.app.bajo.dir.data}/config/${this.ns}`)
57
+ let altCfg = get(this, `app.options.config.${this.ns}`, {})
58
+ if (isEmpty(altCfg)) altCfg = await readAllConfigs(`${this.app.bajo.dir.data}/config/${this.ns}`)
57
59
  cfg = defaultsDeep({}, altCfg, cfg)
58
60
  } catch (err) {}
59
61
  const cfgEnv = get(this, `app.env.${this.ns}`, {})
60
62
  const cfgArgv = get(this, `app.argv.${this.ns}`, {})
61
63
  const envArgv = defaultsDeep({}, cfgEnv, cfgArgv)
62
64
  cfg = pick(defaultsDeep({}, envArgv ?? {}, cfg ?? {}, this.config ?? {}), defKeys)
63
- this.title = this.title ?? cfg.title ?? this.alias
64
65
  this.config = parseObject(cfg, { parseValue: true })
65
66
  }
66
67
 
@@ -0,0 +1,73 @@
1
+ import aneka from 'aneka/index.js'
2
+ import outmatch from 'outmatch'
3
+ import lodash from 'lodash'
4
+ import fs from 'fs-extra'
5
+ import fastGlob from 'fast-glob'
6
+ import { sprintf } from 'sprintf-js'
7
+ import dayjs from 'dayjs'
8
+ import utc from 'dayjs/plugin/utc.js'
9
+ import customParseFormat from 'dayjs/plugin/customParseFormat.js'
10
+ import localizedFormat from 'dayjs/plugin/localizedFormat.js'
11
+ import weekOfYear from 'dayjs/plugin/weekOfYear.js'
12
+ import freeze from '../../lib/freeze.js'
13
+ import findDeep from '../../lib/find-deep.js'
14
+ import omitDeep from 'omit-deep'
15
+
16
+ dayjs.extend(utc)
17
+ dayjs.extend(customParseFormat)
18
+ dayjs.extend(localizedFormat)
19
+ dayjs.extend(weekOfYear)
20
+
21
+ /**
22
+ * @typedef {Object} TAppLib
23
+ * @property {Object} _ - Access to {@link https://lodash.com|lodash}
24
+ * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
25
+ * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
26
+ * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
27
+ * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
28
+ * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
29
+ * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
30
+ * @property {Object} freeze
31
+ * @property {Object} findDeep
32
+ * @see App
33
+ */
34
+ export const lib = {
35
+ _: lodash,
36
+ fs,
37
+ fastGlob,
38
+ sprintf,
39
+ outmatch,
40
+ dayjs,
41
+ aneka,
42
+ freeze,
43
+ findDeep,
44
+ omitDeep
45
+ }
46
+
47
+ export function outmatchNs (source, pattern) {
48
+ const { breakNsPath } = this.bajo
49
+ const [src, subSrc] = source.split(':')
50
+ if (!subSrc) return pattern === src
51
+ try {
52
+ const { fullNs, path } = breakNsPath(pattern)
53
+ const isMatch = outmatch(path)
54
+ return src === fullNs && isMatch(subSrc)
55
+ } catch (err) {
56
+ return false
57
+ }
58
+ }
59
+
60
+ export function parseObject (obj, options = {}) {
61
+ const me = this
62
+ const { ns = 'bajo', lang } = options
63
+ options.translator = {
64
+ lang,
65
+ prefix: 't:',
66
+ handler: val => {
67
+ const [text, ...args] = val.split('|')
68
+ args.push({ lang })
69
+ return me[ns].t(text, ...args)
70
+ }
71
+ }
72
+ return aneka.parseObject(obj, options)
73
+ }
@@ -1,6 +1,5 @@
1
1
  import Print from '../plugin/print.js'
2
2
  import Log from '../app/log.js'
3
- import omitDeep from 'omit-deep'
4
3
  import os from 'os'
5
4
  import fs from 'fs-extra'
6
5
  import lodash from 'lodash'
@@ -25,8 +24,6 @@ const {
25
24
  keys,
26
25
  set,
27
26
  get,
28
- filter,
29
- trim,
30
27
  without,
31
28
  uniq,
32
29
  camelCase,
@@ -125,20 +122,24 @@ export async function buildBaseConfig () {
125
122
  fs.ensureDirSync(this.dir.tmp)
126
123
  }
127
124
  this.pkg = await this.getPkgInfo()
128
- // collect list of plugins
129
- const mainPkg = await this.getPkgInfo(this.app.dir)
130
- let pluginPkgs = get(mainPkg, 'bajo.plugins', [])
125
+ let pluginPkgs = this.app.pluginPkgs
131
126
  if (isEmpty(pluginPkgs)) {
132
- const pluginsFile = `${this.dir.data}/config/.plugins`
133
- if (fs.existsSync(pluginsFile)) {
134
- pluginPkgs = pluginPkgs.concat(filter(map(trim(fs.readFileSync(pluginsFile, 'utf8')).split('\n'), p => trim(p)), b => !isEmpty(b)))
127
+ // collect list of plugins
128
+ const mainPkg = await this.getPkgInfo(this.app.dir)
129
+ pluginPkgs = get(mainPkg, 'bajo.plugins', [])
130
+ if (isEmpty(pluginPkgs)) {
131
+ const pluginsFile = `${this.dir.data}/config/.plugins`
132
+ if (fs.existsSync(pluginsFile)) {
133
+ let lines = fs.readFileSync(pluginsFile, 'utf8')
134
+ lines = lines.trim().split('\n').map(p => p.trim())
135
+ pluginPkgs = lines.filter(c => {
136
+ const line = c.split('#')[0].trim()
137
+ return !isEmpty(line)
138
+ })
139
+ }
135
140
  }
136
141
  }
137
- this.app.pluginPkgs = map(filter(without(uniq(pluginPkgs), this.app.mainNs), p => {
138
- return p[0] !== '#'
139
- }), p => {
140
- return trim(p.split('#')[0])
141
- })
142
+ this.app.pluginPkgs = without(uniq(pluginPkgs), this.app.mainNs)
142
143
  this.app.pluginPkgs.push(this.app.mainNs)
143
144
  }
144
145
 
@@ -210,9 +211,11 @@ export async function collectConfigHandlers () {
210
211
  export async function buildExtConfig () {
211
212
  // config merging
212
213
  const { defaultsDeep } = this.app.lib.aneka
213
- const { parseObject } = this.app.lib
214
+ const { parseObject, omitDeep } = this.app.lib
215
+ const { isEmpty, get } = this.app.lib._
214
216
 
215
- let resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
217
+ let resp = get(this, `app.options.config.${this.ns}`, {})
218
+ if (isEmpty(resp)) resp = await this.readAllConfigs(`${this.dir.data}/config/${this.ns}`)
216
219
  resp = omitDeep(pick(resp, ['log', 'exitHandler', 'env', 'runtime']), omitted)
217
220
  const envs = this.app.envs
218
221
  this.config = defaultsDeep({}, this.config, resp, defConfig)
@@ -78,14 +78,14 @@ export async function checkDependencies () {
78
78
  const deps = keys(odep)
79
79
  if (deps.length > 0) {
80
80
  if (intersection(this.bajo.app.pluginPkgs, deps).length !== deps.length) {
81
- throw this.error('dependencyUnfulfilled%s%s', pkg, join(deps), { code: 'BAJO_DEPENDENCY' })
81
+ throw this.bajo.error('dependencyUnfulfilled%s%s', pkg, join(deps), { code: 'BAJO_DEPENDENCY' })
82
82
  }
83
83
  each(deps, d => {
84
84
  if (!odep[d]) return
85
85
  const ver = get(this.bajo.app[camelCase(d)], 'pkg.version')
86
86
  if (!ver) return
87
87
  if (!semver.satisfies(ver, odep[d])) {
88
- throw this.error('semverCheckFailed%s%s', pkg, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
88
+ throw this.bajo.error('semverCheckFailed%s%s', pkg, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
89
89
  }
90
90
  })
91
91
  }
package/index.js CHANGED
@@ -21,19 +21,21 @@ shim()
21
21
  *
22
22
  * @global
23
23
  * @async
24
- * @see {@tutorial 01-getting-started}
25
- * @param {string} [cwd] - Current working directory
24
+ * @param {Object} [options] - App options
25
+ * @param {string} [options.cwd] - Set current working directory. Defaults to the script directory
26
+ * @param {string[]} [options.plugins] - Array of plugins to load. If provided, it override the list in ```package.json``` and ```.plugins``` file
27
+ * @param {Object} [options.config] - Plugin's config object. If provided, plugin configs will no longer be read from its config files
26
28
  * @returns {App}
27
29
  */
28
- async function boot (cwd) {
29
- if (!cwd) cwd = process.cwd()
30
- const pkgFile = `${cwd}/package.json`
30
+ async function boot (options = {}) {
31
+ if (!options.cwd) options.cwd = process.cwd()
32
+ const pkgFile = `${options.cwd}/package.json`
31
33
  const pkg = fs.readJsonSync(pkgFile)
32
34
  if (pkg.type !== 'module') {
33
35
  console.error(`Please turn on ES6 parsing by adding "type": "module" to ${pkgFile}!`)
34
36
  process.exit(1)
35
37
  }
36
- const app = new App(cwd)
38
+ const app = new App(options)
37
39
  return await app.boot()
38
40
  }
39
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bajo",
3
- "version": "2.2.1",
3
+ "version": "2.3.1",
4
4
  "description": "The ultimate framework for whipping up massive apps in no time",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -26,7 +26,7 @@
26
26
  "homepage": "https://github.com/ardhi/bajo#readme",
27
27
  "dependencies": {
28
28
  "add-filename-increment": "^1.0.0",
29
- "aneka": "^0.10.0",
29
+ "aneka": "^0.11.1",
30
30
  "chalk": "^5.6.0",
31
31
  "dayjs": "^1.11.13",
32
32
  "deep-freeze-strict": "^1.1.1",
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-01-21
4
+
5
+ - [2.3.1] Bug fix on keys that needs to be used while reading plugin's config files
6
+
7
+ ## 2026-01-18
8
+
9
+ - [2.3.0] ```App``` constructor now accept an object as its parameter. For details, please see documentation
10
+ - [2.3.0] Package upgrade to ```aneka@0.11.0```
11
+ - [2.3.0] Bug fix on ```checkDependencies()```
12
+
3
13
  ## 2026-01-16
4
14
 
5
15
  - [2.2.1] Bug fix on multiple appearance of loaded plugins info