bajo 2.11.0 → 2.12.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/class/bajo.js CHANGED
@@ -91,7 +91,7 @@ class Bajo extends Plugin {
91
91
  }
92
92
  }
93
93
 
94
- breakNsPathFromFile = ({ file, dir, baseNs, suffix = '', getType } = {}) => {
94
+ breakNsPathFromFile = ({ file = '', dir = '', ns, suffix = '', getType } = {}) => {
95
95
  let item = file.replace(dir + suffix, '')
96
96
  let type
97
97
  if (getType) {
@@ -103,12 +103,12 @@ class Bajo extends Plugin {
103
103
  let [name, _path] = item.split('@')
104
104
  if (!_path) {
105
105
  _path = name
106
- name = baseNs
106
+ name = ns
107
107
  }
108
108
  _path = camelCase(_path)
109
109
  const names = map(name.split('.'), n => camelCase(n))
110
- const [ns, subNs] = names
111
- return { ns, subNs, path: _path, fullNs: names.join('.'), type }
110
+ const [_ns, subNs] = names
111
+ return { ns: _ns, subNs, path: _path, fullNs: names.join('.'), type }
112
112
  }
113
113
 
114
114
  /**
@@ -301,15 +301,14 @@ class Bajo extends Plugin {
301
301
  scope = item
302
302
  item = args.shift()
303
303
  }
304
- const bajo = scope.app.bajo
305
304
  if (isString(item)) {
306
- if (item.startsWith('applet:') && bajo.app.applets.length > 0) {
305
+ if (item.startsWith('applet:') && this.app.applets.length > 0) {
307
306
  const [, ns, path] = item.split(':')
308
- const applet = find(bajo.app.applets, a => (a.ns === ns || a.alias === ns))
309
- if (applet && scope.app.bajoCli) result = await scope.app.bajoCli.runApplet(applet, path, ...args)
307
+ const applet = find(this.app.applets, a => (a.ns === ns || a.alias === ns))
308
+ if (applet && this.app.bajoCli) result = await this.app.bajoCli.runApplet(applet, path, ...args)
310
309
  } else {
311
310
  const [ns, method, ...params] = item.split(':')
312
- const fn = bajo.getMethod(`${ns}:${method}`)
311
+ const fn = this.getMethod(`${ns}:${method}`)
313
312
  if (fn) {
314
313
  if (params.length > 0) args.unshift(...params)
315
314
  result = await fn(...args)
@@ -454,7 +453,7 @@ class Bajo extends Plugin {
454
453
  * @returns {string} Formatted value
455
454
  */
456
455
  format = (value, type, options = {}) => {
457
- const { defaultsDeep } = this.app.lib.aneka
456
+ const { defaultsDeep, isSet } = this.app.lib.aneka
458
457
  const { format } = this.config.intl
459
458
  const { emptyValue = format.emptyValue } = options
460
459
  const lang = options.lang ?? this.config.lang
@@ -496,6 +495,7 @@ class Bajo extends Plugin {
496
495
  }
497
496
  if (['array'].includes(type)) return value.join(', ')
498
497
  if (['object'].includes(type)) return JSON.stringify(value)
498
+ if (['boolean'].includes(type) && isSet(value)) return value ? this.t('true', { lang }) : this.t('false', { lang })
499
499
  return value
500
500
  }
501
501
 
@@ -800,15 +800,17 @@ class Bajo extends Plugin {
800
800
  * @param {string} [options.lastSeparator=and] - Text to use as the last separator
801
801
  * @returns {string}
802
802
  */
803
- join = (array, options) => {
804
- const { isSet } = this.app.lib.aneka
805
- const translate = val => {
806
- return this.t(val).toLowerCase()
803
+ join = (array, options = {}) => {
804
+ let separator = ', '
805
+ let lastSeparator = 'and'
806
+ let lang
807
+ if (isString(options)) separator = options
808
+ else ({ separator, lastSeparator, lang } = options)
809
+ const translate = (val) => {
810
+ return this.t(val, { lang }).toLowerCase()
807
811
  }
808
812
  if (array.length === 0) return translate('none')
809
813
  if (array.length === 1) return array[0]
810
- if (isSet(options) && !isPlainObject(options)) return array.join(options)
811
- let { separator = ', ', lastSeparator = 'and' } = options ?? {}
812
814
  lastSeparator = translate(lastSeparator)
813
815
  const last = (array.pop() ?? '').trim()
814
816
  return array.map(a => (a + '').trim()).join(separator) + ` ${lastSeparator} ${last}`
@@ -841,14 +843,53 @@ class Bajo extends Plugin {
841
843
  * @param {boolean} [options.ignoreError] - Any exception will be silently discarded
842
844
  * @param {string} [options.ns] - If given, use this as the scope
843
845
  * @param {string} [options.pattern] - If given and auto detection is on (extension is ```.*```), it will be used for instead the default one
844
- * @param {Object} [options.globOptions={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options
845
846
  * @param {Object} [options.defValue={}] - Default value to use if value returned empty
846
- * @param {Object} [options.opts={}] - Parser setting
847
+ * @param {Object} [options.parserOpts={}] - Parser setting
848
+ * @param {Object} [options.globOpts={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options
847
849
  * @returns {Object}
848
850
  */
849
- readConfig = async (file, { ns, pattern, ignoreError = true, defValue = {}, options = {} } = {}) => {
851
+ readConfig = async (file, options = {}) => {
850
852
  const { parseObject } = this.app.lib
851
- options.readFromFile = true
853
+ const { defaultsDeep } = this.app.lib.aneka
854
+ const { uniq, isString, isArray, findIndex, isPlainObject } = this.app.lib._
855
+ let { ns, baseNs, extend, pattern, ignoreError = true, defValue = {}, parserOpts = {}, globOpts = {} } = options
856
+
857
+ const output = async (obj) => {
858
+ const orig = parseObject(obj)
859
+ if (!baseNs || extend === false) return orig
860
+ const { suffix = '', keys = [] } = options
861
+ let bases = this.app.getAllNs()
862
+ if (isString(extend)) extend = extend.split(',').map(i => i.trim)
863
+ if (isArray(extend)) bases = [...extend, 'main']
864
+ bases = uniq(bases)
865
+ let ext = isArray(obj) ? [] : {}
866
+ const dir = this.app[ns].dir.pkg
867
+ let [names, _path] = file.split(':')
868
+ if (file.slice(0, names.length + 1) !== `${ns}:`) _path = file.slice(dir.length + 1)
869
+ if (_path.startsWith('extend/')) _path = _path.slice(7)
870
+ if (_path.startsWith(`${baseNs}/`)) _path = _path.slice(baseNs.length + 1)
871
+ _path = _path.slice(0, -(path.extname(_path).length)) + '.*'
872
+ const opts = omit(options, ['suffix', 'keys', 'extend'])
873
+ opts.parserOpts = opts.parserOpts ?? {}
874
+ opts.parserOpts.args = opts.parserOpts.args ?? []
875
+ const idx = findIndex(opts.parserOpts.args, item => {
876
+ return isPlainObject(item) && Object.keys(item)[0] === '_orig'
877
+ })
878
+ if (idx > -1) opts.parserOpts.args[idx] = { _orig: orig }
879
+ else opts.parserOpts.args.push({ _orig: orig })
880
+
881
+ for (const base of bases) {
882
+ if (!this.app[base]) continue
883
+ const fileExt = `${this.app[base].dir.pkg}/extend/${baseNs}/extend/${ns}${suffix}/${_path}`
884
+ const result = parseObject(await this.readConfig(fileExt, { ...opts, extend: false }))
885
+ if (isEmpty(result)) continue
886
+ if (isArray(result)) ext = [...result, ...ext]
887
+ else ext = defaultsDeep({}, result, ext)
888
+ }
889
+ return isArray(orig) ? [...orig, ...ext] : defaultsDeep({}, keys.length > 0 ? pick(ext, keys) : ext, orig)
890
+ }
891
+
892
+ parserOpts.readFromFile = true
852
893
  if (!ns) ns = this.ns
853
894
  file = resolvePath(this.getPluginFile(file))
854
895
  let ext = path.extname(file)
@@ -856,22 +897,22 @@ class Bajo extends Plugin {
856
897
  ext = ext.toLowerCase()
857
898
  if (ext === '.js') {
858
899
  const { readHandler } = find(this.app.configHandlers, { ext })
859
- return parseObject(await readHandler.call(this.app[ns], file, options))
900
+ return await output(await readHandler.call(this.app[ns], file, parserOpts))
860
901
  }
861
- if (ext === '.json') return await this.fromJson(file, options)
902
+ if (ext === '.json') return await output(await this.fromJson(file, parserOpts))
862
903
  if (!['', '.*'].includes(ext)) {
863
904
  const item = find(this.app.configHandlers, { ext })
864
905
  if (!item) {
865
906
  if (!ignoreError) throw this.error('cantParse%s', file, { code: 'BAJO_CONFIG_NO_PARSER' })
866
- return parseObject(defValue)
907
+ return await output(defValue)
867
908
  }
868
- return parseObject(await item.readHandler.call(this.app[ns], file, options))
909
+ return await output(await item.readHandler.call(this.app[ns], file, parserOpts))
869
910
  }
870
911
  const item = pattern ?? `${fname}.{${map(map(this.app.configHandlers, 'ext'), k => k.slice(1)).join(',')}}`
871
- const files = await fastGlob(item, options.glob ?? {})
912
+ const files = await fastGlob(item, globOpts ?? {})
872
913
  if (files.length === 0) {
873
914
  if (!ignoreError) throw this.error('noConfigFileFound', { code: 'BAJO_CONFIG_FILE_NOT_FOUND' })
874
- return parseObject(defValue)
915
+ return await output(defValue)
875
916
  }
876
917
  let config = defValue
877
918
  for (const f of files) {
@@ -881,10 +922,10 @@ class Bajo extends Plugin {
881
922
  if (!ignoreError) throw this.error('cantParse%s', f, { code: 'BAJO_CONFIG_NO_PARSER' })
882
923
  continue
883
924
  }
884
- config = await item.readHandler.call(this.app[ns], f, options)
925
+ config = await item.readHandler.call(this.app[ns], f, parserOpts)
885
926
  if (!isEmpty(config)) break
886
927
  }
887
- return parseObject(config)
928
+ return await output(config)
888
929
  }
889
930
 
890
931
  /**
@@ -99,16 +99,17 @@ export async function checkDependencies () {
99
99
  * @fires bajo:afterCollectHooks
100
100
  */
101
101
  export async function collectHooks () {
102
- const { eachPlugins, runHook, isLogInRange, importModule, breakNsPathFromFile } = this.bajo
102
+ const { eachPlugins, runHook, isLogInRange, importModule } = this.bajo
103
103
  const me = this
104
104
  me.bajo.log.trace('collecting%s', this.t('hooks'))
105
105
  // collects
106
106
  await eachPlugins(async function ({ dir, file }) {
107
- const { ns: baseNs } = this
108
- const { ns, subNs, path } = breakNsPathFromFile({ file, dir, baseNs, suffix: '/hook/' })
107
+ const _file = file.replace(dir + '/hook/', '').replace('.js', '')
108
+ const [names, path] = _file.split('@')
109
+ const [ns, subNs] = names.split('.').map(n => camelCase(n))
109
110
  const mod = await importModule(file, { asHandler: true })
110
111
  if (!mod) return undefined
111
- merge(mod, { ns, subNs, path, src: baseNs })
112
+ merge(mod, { ns, subNs, path: camelCase(path), src: this.ns })
112
113
  me.bajo.hooks.push(mod)
113
114
  }, { glob: 'hook/**/*.js', prefix: me.bajo.ns })
114
115
  // for log trace purpose only
@@ -182,5 +182,7 @@
182
182
  "fieldError%s%s": "'%s': %s|lowerFirst",
183
183
  "reservedName%s%s": "%s|upperFirst name '%s' is a reserved name. Please rename to something else",
184
184
  "missing%s%s": "Missing '%s' %s",
185
- "processing...": "Processing..."
185
+ "processing...": "Processing...",
186
+ "true": "True",
187
+ "false": "False"
186
188
  }
@@ -182,5 +182,7 @@
182
182
  "fieldError%s%s": "'%s': %s|lowerFirst",
183
183
  "reservedName%s%s": "Nama %s|lowerFirst '%s' adalah nama tang di reserve. Silahkan gunakan nama lainnya",
184
184
  "missing%s%s": "%s|upperFirst '%s' tidak ditemukan",
185
- "processing...": "Memroses..."
185
+ "processing...": "Memroses...",
186
+ "true": "Benar",
187
+ "false": "Salah"
186
188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bajo",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "The ultimate framework for whipping up massive apps in no time",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-04-23
4
+
5
+ - [2.12.0] Remove ```breakNsPathFromFile()```
6
+ - [2.12.0] Add feature to read in extended path ```readConfig()```
7
+
8
+ ## 2026-04-11
9
+
10
+ - [2.11.1] Bug fix in ```join()```
11
+ - [2.11.1] Bug fix in ```format()```
12
+
3
13
  ## 2026-04-07
4
14
 
5
15
  - [2.11.0] Change ```dispose()``` to be an async function