bajo 2.22.0 → 2.24.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 +2 -1
- package/class/app.js +220 -107
- package/class/bajo.js +258 -177
- package/class/base.js +85 -24
- package/class/cache.js +8 -16
- package/class/err.js +31 -10
- package/class/log.js +46 -38
- package/class/plugin.js +53 -30
- package/class/print.js +18 -15
- package/class/tools.js +6 -5
- package/docs/App.html +51 -1
- package/docs/Bajo.html +1 -1
- package/docs/Base.html +34 -1
- package/docs/Cache.html +1 -1
- package/docs/Err.html +2 -2
- package/docs/Log.html +2 -2
- package/docs/Main.html +3 -0
- package/docs/Plugin.html +1 -1
- package/docs/Print.html +1 -1
- package/docs/Tools.html +1 -1
- package/docs/class_app.js.html +222 -109
- package/docs/class_bajo.js.html +260 -179
- package/docs/class_base.js.html +87 -26
- package/docs/class_cache.js.html +10 -18
- package/docs/class_err.js.html +33 -12
- package/docs/class_log.js.html +48 -40
- package/docs/class_plugin.js.html +55 -32
- package/docs/class_print.js.html +20 -17
- package/docs/class_tools.js.html +8 -7
- package/docs/data/search.json +1 -1
- package/docs/extend_bajo_hook.js.html +289 -0
- package/docs/global.html +22 -1
- package/docs/index.html +1 -1
- package/docs/index.js.html +53 -16
- package/docs/lib_applet-hook.js.html +328 -0
- package/docs/lib_config.js.html +167 -0
- package/docs/lib_helper.js.html +186 -192
- package/docs/module-Applet.html +3 -0
- package/docs/module-Helper-Main.html +3 -0
- package/docs/module-Helper.html +1 -8
- package/docs/module-Hook.html +1 -1
- package/docs/tutorial-01_WELCOME.html +3 -0
- package/docs/tutorial-02_GETTING_STARTED.html +125 -0
- package/docs/tutorial-03_USER_GUIDE.html +54 -0
- package/docs/tutorial-04_DEV_GUIDE.html +178 -0
- package/docs/tutorial-05_ECOSYSTEM.html +3 -0
- package/extend/bajo/intl/en-US.json +2 -1
- package/extend/bajo/intl/id.json +2 -1
- package/index.js +51 -14
- package/lib/applet-hook.js +325 -0
- package/lib/config.js +164 -0
- package/lib/helper.js +184 -190
- package/package.json +6 -2
- package/test/00-global-hooks.test.js +44 -0
- package/test/e2e/_run.js +19 -0
- package/test/e2e/e2e-applet-process.test.js +66 -0
- package/test/e2e/e2e-boot-process.test.js +62 -0
- package/test/e2e/e2e-hooks-process.test.js +68 -0
- package/test/integration/_setup.js +55 -0
- package/test/integration/aspect-01-boot-instance.test.js +24 -0
- package/test/integration/aspect-02-plugin-loading.test.js +24 -0
- package/test/integration/aspect-03-config-merge.test.js +27 -0
- package/test/integration/aspect-04-intl-loading.test.js +29 -0
- package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
- package/test/integration/aspect-06-log-file-save.test.js +28 -0
- package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
- package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
- package/test/integration/aspect-09-config-formats.test.js +26 -0
- package/test/integration/aspect-10-hooks.test.js +38 -0
- package/test/unit/app.test.js +115 -0
- package/test/unit/applet-hook.test.js +10 -0
- package/test/unit/bajo.test.js +151 -0
- package/test/unit/base.test.js +90 -0
- package/test/unit/cache.test.js +89 -0
- package/test/unit/config.test.js +14 -0
- package/test/unit/err.test.js +57 -0
- package/test/unit/helper.test.js +116 -0
- package/test/unit/index.test.js +40 -0
- package/test/unit/log.test.js +84 -0
- package/test/unit/plugin.test.js +95 -0
- package/test/unit/print.test.js +72 -0
- package/test/unit/tools.test.js +29 -0
- package/wiki/CHANGES.md +9 -0
- package/wiki/tutorials/01_WELCOME.md +18 -0
- package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
- package/wiki/tutorials/03_USER_GUIDE.md +262 -0
- package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
- package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
- package/wiki/tutorials/tutorials.json +17 -0
- package/lib/hook.js +0 -228
- package/test/app.test.js +0 -183
- package/test/bajo.test.js +0 -124
- package/test/base.test.js +0 -75
- package/test/cache.test.js +0 -94
- package/test/e2e.test.js +0 -137
- package/test/err.test.js +0 -73
- package/test/helper.test.js +0 -39
- package/test/import-module.test.js +0 -138
- package/test/integration.test.js +0 -218
- package/test/log.test.js +0 -119
- package/test/plugin.test.js +0 -116
- package/test/print.test.js +0 -100
- package/test/tools.test.js +0 -38
- package/wiki/CONFIG.md +0 -36
- package/wiki/DEV_GUIDE.md +0 -3
- package/wiki/ECOSYSTEM.md +0 -93
- package/wiki/GETTING-STARTED.md +0 -356
- package/wiki/USER-GUIDE.md +0 -256
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import boot from '../../index.js'
|
|
7
|
+
|
|
8
|
+
describe('index (unit)', () => {
|
|
9
|
+
let root
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
root = fs.mkdtempSync(path.join('/tmp', 'bajo-index-unit-'))
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('boot returns app instance for valid module app', async function () {
|
|
20
|
+
this.timeout(10000)
|
|
21
|
+
await fs.writeJson(path.join(root, 'package.json'), { name: 'app', type: 'module', bajo: { plugins: [] } })
|
|
22
|
+
await fs.ensureDir(path.join(root, 'data', 'config'))
|
|
23
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'bajo.json'), { log: { level: 'silent', save: false }, exitHandler: false, cache: { purgeIntvDur: '1h' } })
|
|
24
|
+
|
|
25
|
+
const old = global.setInterval
|
|
26
|
+
const ids = []
|
|
27
|
+
global.setInterval = (fn, delay, ...args) => {
|
|
28
|
+
const id = old(fn, delay, ...args)
|
|
29
|
+
ids.push(id)
|
|
30
|
+
return id
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const app = await boot({ cwd: root })
|
|
34
|
+
expect(app).to.be.an('object')
|
|
35
|
+
expect(app.bajo).to.be.an('object')
|
|
36
|
+
|
|
37
|
+
global.setInterval = old
|
|
38
|
+
for (const id of ids) clearInterval(id)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import lodash from 'lodash'
|
|
6
|
+
import dayjs from 'dayjs'
|
|
7
|
+
import { expect } from 'chai'
|
|
8
|
+
import App from '../../class/app.js'
|
|
9
|
+
import Log, { logLevels } from '../../class/log.js'
|
|
10
|
+
|
|
11
|
+
describe('log (unit)', () => {
|
|
12
|
+
let root
|
|
13
|
+
let app
|
|
14
|
+
let log
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
root = fs.mkdtempSync(path.join('/tmp', 'bajo-log-unit-'))
|
|
18
|
+
await fs.ensureDir(path.join(root, 'data'))
|
|
19
|
+
app = new App({ cwd: root })
|
|
20
|
+
app.lib._ = lodash
|
|
21
|
+
app.lib.dayjs = dayjs
|
|
22
|
+
app.t = (_prefix, text, ...args) => args.length ? `${text}${args.join('')}` : text
|
|
23
|
+
app.bajo = {
|
|
24
|
+
dir: { data: path.join(root, 'data') },
|
|
25
|
+
config: { env: 'dev', log: { level: 'trace', save: false, pretty: false, useUtc: false, timeTaken: true, dateFormat: 'YYYY-MM-DD', rotation: { cycle: 'none', byPlugin: false } } },
|
|
26
|
+
isLogInRange: () => true
|
|
27
|
+
}
|
|
28
|
+
log = new Log(app)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('exports known log levels', () => {
|
|
36
|
+
expect(logLevels.trace.number).to.equal(10)
|
|
37
|
+
expect(logLevels.silent.number).to.equal(99)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('getErrorMessage handles message and fallback fields', () => {
|
|
41
|
+
expect(log.getErrorMessage(new Error('x'))).to.equal('x')
|
|
42
|
+
expect(log.getErrorMessage({ message: '', code: 'E' })).to.equal('E')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('getRotationPattern supports none/daily/weekly/monthly', () => {
|
|
46
|
+
app.bajo.config.log.rotation.cycle = 'none'
|
|
47
|
+
expect(log.getRotationPattern()).to.equal(undefined)
|
|
48
|
+
app.bajo.config.log.rotation.cycle = 'daily'
|
|
49
|
+
expect(log.getRotationPattern()).to.be.a('string')
|
|
50
|
+
app.bajo.config.log.rotation.cycle = 'weekly'
|
|
51
|
+
expect(log.getRotationPattern()).to.be.a('string')
|
|
52
|
+
app.bajo.config.log.rotation.cycle = 'monthly'
|
|
53
|
+
expect(log.getRotationPattern()).to.be.a('string')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('formatMsg handles string payload and error payload paths', () => {
|
|
57
|
+
log.formatMsg('info', 'demo', 'hi%s', 'x')
|
|
58
|
+
log.formatMsg('error', 'demo', new Error('boom'))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('save writes clean lines to log files', () => {
|
|
62
|
+
app.bajo.config.log.save = true
|
|
63
|
+
app.bajo.config.log.rotation.cycle = 'daily'
|
|
64
|
+
fs.ensureDirSync(log.logDir)
|
|
65
|
+
log.save('line', 'demo')
|
|
66
|
+
const files = fs.readdirSync(log.logDir)
|
|
67
|
+
expect(files.length).to.be.greaterThan(0)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('trace/debug/info/warn/error/fatal/silent wrappers call format path', () => {
|
|
71
|
+
log.trace('d', 'a')
|
|
72
|
+
log.debug('d', 'a')
|
|
73
|
+
log.info('d', 'a')
|
|
74
|
+
log.warn('d', 'a')
|
|
75
|
+
log.error('d', 'a')
|
|
76
|
+
log.fatal('d', 'a')
|
|
77
|
+
log.silent('d', 'a')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('dispose clears app reference', async () => {
|
|
81
|
+
await log.dispose()
|
|
82
|
+
expect(log.app).to.equal(null)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import lodash from 'lodash'
|
|
6
|
+
import { expect } from 'chai'
|
|
7
|
+
import Plugin from '../../class/plugin.js'
|
|
8
|
+
|
|
9
|
+
const mk = async () => fs.mkdtemp(path.join('/tmp', 'bajo-plugin-unit-'))
|
|
10
|
+
|
|
11
|
+
describe('plugin (unit)', () => {
|
|
12
|
+
let dir
|
|
13
|
+
let app
|
|
14
|
+
let plugin
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
dir = await mk()
|
|
18
|
+
await fs.writeJson(path.join(dir, 'package.json'), { name: 'pkg', version: '1.2.3', description: 'd', author: 'a', license: 'MIT', homepage: 'h', bajo: { type: 'plugin' } })
|
|
19
|
+
app = {
|
|
20
|
+
lib: { _: lodash, fs },
|
|
21
|
+
log: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, fatal: () => {}, silent: () => {} },
|
|
22
|
+
t: (_ns, text, ...args) => args.length ? `${text}:${args.join(',')}` : text,
|
|
23
|
+
te: () => true,
|
|
24
|
+
dump: () => {}
|
|
25
|
+
}
|
|
26
|
+
plugin = new Plugin('demo-plugin', app)
|
|
27
|
+
plugin.dir = { pkg: dir }
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
if (dir) fs.rmSync(dir, { recursive: true, force: true })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('constructor maps namespace and log wrappers', () => {
|
|
35
|
+
expect(plugin.pkgName).to.equal('demo-plugin')
|
|
36
|
+
expect(plugin.ns).to.equal('demoPlugin')
|
|
37
|
+
expect(plugin.alias).to.equal(null)
|
|
38
|
+
expect(plugin.config).to.deep.equal({})
|
|
39
|
+
plugin.log.info('x')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('getPkgInfo returns selected keys and full package', () => {
|
|
43
|
+
const selected = plugin.getPkgInfo()
|
|
44
|
+
const full = plugin.getPkgInfo(undefined, [])
|
|
45
|
+
expect(selected).to.deep.equal({ name: 'pkg', version: '1.2.3', description: 'd', author: 'a', license: 'MIT', homepage: 'h', bajo: { type: 'plugin' } })
|
|
46
|
+
expect(full.name).to.equal('pkg')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('getConfig supports path, defaults, omit and noClone', () => {
|
|
50
|
+
plugin.config = { a: { b: 1, c: 2 } }
|
|
51
|
+
const p = plugin.getConfig('a', { omit: ['c'] })
|
|
52
|
+
expect(p).to.deep.equal({ b: 1 })
|
|
53
|
+
const def = plugin.getConfig('a.d', { defValue: 9 })
|
|
54
|
+
expect(def).to.equal(9)
|
|
55
|
+
const nc = plugin.getConfig('a', { noClone: true })
|
|
56
|
+
nc.b = 7
|
|
57
|
+
expect(plugin.config.a.b).to.equal(7)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('error/fatal return native Error when print is absent', () => {
|
|
61
|
+
const e = plugin.error('m')
|
|
62
|
+
const f = plugin.fatal('n')
|
|
63
|
+
expect(e).to.be.instanceOf(Error)
|
|
64
|
+
expect(f).to.be.instanceOf(Error)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('t and te delegate to app translation APIs', () => {
|
|
68
|
+
expect(plugin.t('hello', 'a')).to.equal('hello:a')
|
|
69
|
+
expect(plugin.te('hello')).to.equal(true)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('bindThis binds string and function entries', () => {
|
|
73
|
+
plugin.value = 'ok'
|
|
74
|
+
plugin.one = function () { return this.value }
|
|
75
|
+
function two () { return this.pkgName }
|
|
76
|
+
plugin.bindThis('one', two)
|
|
77
|
+
const a = plugin.one
|
|
78
|
+
const b = plugin.two
|
|
79
|
+
expect(a()).to.equal('ok')
|
|
80
|
+
expect(b()).to.equal('demo-plugin')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('dump forwards call to app.dump', () => {
|
|
84
|
+
let args
|
|
85
|
+
app.dump = (...a) => { args = a }
|
|
86
|
+
plugin.dump('x', 1)
|
|
87
|
+
expect(args).to.deep.equal(['x', 1])
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('dispose clears app and config references', async () => {
|
|
91
|
+
await plugin.dispose()
|
|
92
|
+
expect(plugin.app).to.equal(null)
|
|
93
|
+
expect(plugin.config).to.equal(null)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* global describe, it */
|
|
2
|
+
|
|
3
|
+
import dayjs from 'dayjs'
|
|
4
|
+
import { expect } from 'chai'
|
|
5
|
+
import Print from '../../class/print.js'
|
|
6
|
+
|
|
7
|
+
describe('print (unit)', () => {
|
|
8
|
+
const make = () => {
|
|
9
|
+
let exited = false
|
|
10
|
+
const plugin = {
|
|
11
|
+
ns: 'demo',
|
|
12
|
+
t: (text, ...args) => args.length ? `${text}${args.join('')}` : text,
|
|
13
|
+
app: {
|
|
14
|
+
applet: false,
|
|
15
|
+
lib: { dayjs },
|
|
16
|
+
bajo: { config: { silent: false, counter: false, datetime: false } },
|
|
17
|
+
exit: () => { exited = true }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { print: new Print(plugin, { ora: { isEnabled: false }, showCounter: true, showDatetime: true }), getExited: () => exited }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
it('setOpts updates options with silent inheritance', () => {
|
|
24
|
+
const { print } = make()
|
|
25
|
+
print.setOpts({ silent: false })
|
|
26
|
+
expect(print.options.silent).to.equal(false)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('buildText prefixes datetime and counter', () => {
|
|
30
|
+
const { print } = make()
|
|
31
|
+
const t = print.buildText('hello')
|
|
32
|
+
expect(t).to.include('hello')
|
|
33
|
+
expect(t.startsWith('[')).to.equal(true)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('setText updates spinner text', () => {
|
|
37
|
+
const { print } = make()
|
|
38
|
+
print.setText('abc')
|
|
39
|
+
expect(print.ora.text).to.include('abc')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('getElapsed supports default and custom units', () => {
|
|
43
|
+
const { print } = make()
|
|
44
|
+
expect(print.getElapsed('hms')).to.be.a('string')
|
|
45
|
+
expect(print.getElapsed('ms')).to.be.a('number')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('start/stop/succeed/fail/warn/info/clear/render are chainable', () => {
|
|
49
|
+
const { print } = make()
|
|
50
|
+
expect(print.start('a')).to.equal(print)
|
|
51
|
+
expect(print.stop()).to.equal(print)
|
|
52
|
+
expect(print.succeed('b')).to.equal(print)
|
|
53
|
+
expect(print.fail('c')).to.equal(print)
|
|
54
|
+
expect(print.warn('d')).to.equal(print)
|
|
55
|
+
expect(print.info('e')).to.equal(print)
|
|
56
|
+
expect(print.clear()).to.equal(print)
|
|
57
|
+
expect(print.render()).to.equal(print)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('fatal handles Error and exits app', () => {
|
|
61
|
+
const { print, getExited } = make()
|
|
62
|
+
print.fatal(new Error('bad'))
|
|
63
|
+
expect(getExited()).to.equal(true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('spinner creates new instance sharing baseline time', () => {
|
|
67
|
+
const { print } = make()
|
|
68
|
+
const s = print.spinner()
|
|
69
|
+
expect(s).to.be.instanceOf(Print)
|
|
70
|
+
expect(s.startTime.valueOf()).to.equal(print.startTime.valueOf())
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* global describe, it */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import Tools from '../../class/tools.js'
|
|
5
|
+
|
|
6
|
+
describe('tools (unit)', () => {
|
|
7
|
+
it('constructor stores plugin and app references', () => {
|
|
8
|
+
const plugin = { app: { name: 'app' } }
|
|
9
|
+
const t = new Tools(plugin)
|
|
10
|
+
expect(t.plugin).to.equal(plugin)
|
|
11
|
+
expect(t.app).to.equal(plugin.app)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('selfBind binds methods to instance context', () => {
|
|
15
|
+
const t = new Tools({ app: {} })
|
|
16
|
+
t.value = 42
|
|
17
|
+
t.getValue = function () { return this.value }
|
|
18
|
+
t.selfBind(['getValue'])
|
|
19
|
+
const fn = t.getValue
|
|
20
|
+
expect(fn()).to.equal(42)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('dispose clears internal references', async () => {
|
|
24
|
+
const t = new Tools({ app: { ok: true } })
|
|
25
|
+
await t.dispose()
|
|
26
|
+
expect(t.app).to.equal(null)
|
|
27
|
+
expect(t.plugin).to.equal(null)
|
|
28
|
+
})
|
|
29
|
+
})
|
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-07-23
|
|
4
|
+
|
|
5
|
+
- [2.24.0] Revamped documentations and test cases
|
|
6
|
+
- [2.24.0] `plugin.bindThis()` now accepts rest parameters
|
|
7
|
+
|
|
8
|
+
## 2026-07-13
|
|
9
|
+
|
|
10
|
+
- [2.23.0] Add `getDownloadDir()`
|
|
11
|
+
|
|
3
12
|
## 2026-07-08
|
|
4
13
|
|
|
5
14
|
- [2.22.0] Update documentations
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Welcome to Bajo
|
|
2
|
+
|
|
3
|
+
Welcome to Bajo, __the ultimate framework for whipping up a massive app in no time!__
|
|
4
|
+
|
|
5
|
+
Bajo is a framework that allows you to build apps with ease, whether it's a command line app or web app of any size. It is designed to be simple, yet powerful, and can be used for a wide range of applications.
|
|
6
|
+
|
|
7
|
+
Bajo grew out of the need for a framework that:
|
|
8
|
+
- is easy to use and understand even for beginners.
|
|
9
|
+
- is modular and extensible. It's like playing with Lego blocks, you can build anything you want by combining different plugins with each other.
|
|
10
|
+
- is plug & play, yet easy to customize through configurations only. Less code is always better.
|
|
11
|
+
- is fast and efficient, yet easy to scale.
|
|
12
|
+
- and last but not least: so much magic! Many people will be critical about it, but hey, who doesn't like magic?
|
|
13
|
+
|
|
14
|
+
In Bajo philosophy, everything is a plugin. A plugin should be handling a single responsibility, and can be extended by other plugins. This allows for a very flexible and modular architecture, where you can easily add or remove features as needed.
|
|
15
|
+
|
|
16
|
+
A typical Bajo app should be able to run within a few minutes: create a new project directory, create `package.json` file, create a {@link boot|boot file}, install as many plugins as you want, done! Yes, it's that simple.
|
|
17
|
+
|
|
18
|
+
Now dive in to the tutorials to learn more about Bajo and how to use it to build your next app.
|