bajo 2.23.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.
Files changed (108) hide show
  1. package/.jsdoc.conf.json +2 -1
  2. package/class/app.js +220 -107
  3. package/class/bajo.js +246 -174
  4. package/class/base.js +85 -24
  5. package/class/cache.js +8 -16
  6. package/class/err.js +31 -10
  7. package/class/log.js +46 -38
  8. package/class/plugin.js +53 -30
  9. package/class/print.js +18 -15
  10. package/class/tools.js +6 -5
  11. package/docs/App.html +51 -1
  12. package/docs/Bajo.html +1 -1
  13. package/docs/Base.html +34 -1
  14. package/docs/Cache.html +1 -1
  15. package/docs/Err.html +2 -2
  16. package/docs/Log.html +2 -2
  17. package/docs/Main.html +3 -0
  18. package/docs/Plugin.html +1 -1
  19. package/docs/Print.html +1 -1
  20. package/docs/Tools.html +1 -1
  21. package/docs/class_app.js.html +222 -109
  22. package/docs/class_bajo.js.html +260 -179
  23. package/docs/class_base.js.html +87 -26
  24. package/docs/class_cache.js.html +10 -18
  25. package/docs/class_err.js.html +33 -12
  26. package/docs/class_log.js.html +48 -40
  27. package/docs/class_plugin.js.html +55 -32
  28. package/docs/class_print.js.html +20 -17
  29. package/docs/class_tools.js.html +8 -7
  30. package/docs/data/search.json +1 -1
  31. package/docs/extend_bajo_hook.js.html +289 -0
  32. package/docs/global.html +22 -1
  33. package/docs/index.html +1 -1
  34. package/docs/index.js.html +53 -16
  35. package/docs/lib_applet-hook.js.html +328 -0
  36. package/docs/lib_config.js.html +167 -0
  37. package/docs/lib_helper.js.html +186 -192
  38. package/docs/module-Applet.html +3 -0
  39. package/docs/module-Helper-Main.html +3 -0
  40. package/docs/module-Helper.html +1 -8
  41. package/docs/module-Hook.html +1 -1
  42. package/docs/tutorial-01_WELCOME.html +3 -0
  43. package/docs/tutorial-02_GETTING_STARTED.html +125 -0
  44. package/docs/tutorial-03_USER_GUIDE.html +54 -0
  45. package/docs/tutorial-04_DEV_GUIDE.html +178 -0
  46. package/docs/tutorial-05_ECOSYSTEM.html +3 -0
  47. package/extend/bajo/intl/en-US.json +2 -1
  48. package/extend/bajo/intl/id.json +2 -1
  49. package/index.js +51 -14
  50. package/lib/applet-hook.js +325 -0
  51. package/lib/config.js +164 -0
  52. package/lib/helper.js +184 -190
  53. package/package.json +6 -2
  54. package/test/00-global-hooks.test.js +44 -0
  55. package/test/e2e/_run.js +19 -0
  56. package/test/e2e/e2e-applet-process.test.js +66 -0
  57. package/test/e2e/e2e-boot-process.test.js +62 -0
  58. package/test/e2e/e2e-hooks-process.test.js +68 -0
  59. package/test/integration/_setup.js +55 -0
  60. package/test/integration/aspect-01-boot-instance.test.js +24 -0
  61. package/test/integration/aspect-02-plugin-loading.test.js +24 -0
  62. package/test/integration/aspect-03-config-merge.test.js +27 -0
  63. package/test/integration/aspect-04-intl-loading.test.js +29 -0
  64. package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
  65. package/test/integration/aspect-06-log-file-save.test.js +28 -0
  66. package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
  67. package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
  68. package/test/integration/aspect-09-config-formats.test.js +26 -0
  69. package/test/integration/aspect-10-hooks.test.js +38 -0
  70. package/test/unit/app.test.js +115 -0
  71. package/test/unit/applet-hook.test.js +10 -0
  72. package/test/unit/bajo.test.js +151 -0
  73. package/test/unit/base.test.js +90 -0
  74. package/test/unit/cache.test.js +89 -0
  75. package/test/unit/config.test.js +14 -0
  76. package/test/unit/err.test.js +57 -0
  77. package/test/unit/helper.test.js +116 -0
  78. package/test/unit/index.test.js +40 -0
  79. package/test/unit/log.test.js +84 -0
  80. package/test/unit/plugin.test.js +95 -0
  81. package/test/unit/print.test.js +72 -0
  82. package/test/unit/tools.test.js +29 -0
  83. package/wiki/CHANGES.md +4 -0
  84. package/wiki/tutorials/01_WELCOME.md +18 -0
  85. package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
  86. package/wiki/tutorials/03_USER_GUIDE.md +262 -0
  87. package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
  88. package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
  89. package/wiki/tutorials/tutorials.json +17 -0
  90. package/lib/hook.js +0 -228
  91. package/test/app.test.js +0 -183
  92. package/test/bajo.test.js +0 -124
  93. package/test/base.test.js +0 -75
  94. package/test/cache.test.js +0 -94
  95. package/test/e2e.test.js +0 -137
  96. package/test/err.test.js +0 -73
  97. package/test/helper.test.js +0 -39
  98. package/test/import-module.test.js +0 -138
  99. package/test/integration.test.js +0 -218
  100. package/test/log.test.js +0 -119
  101. package/test/plugin.test.js +0 -116
  102. package/test/print.test.js +0 -100
  103. package/test/tools.test.js +0 -38
  104. package/wiki/CONFIG.md +0 -36
  105. package/wiki/DEV_GUIDE.md +0 -3
  106. package/wiki/ECOSYSTEM.md +0 -93
  107. package/wiki/GETTING-STARTED.md +0 -356
  108. package/wiki/USER-GUIDE.md +0 -256
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: lib/helper.js</title><!--[if lt IE 9]>
2
2
  <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
3
- <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">lib_helper.js</h1></header><article><pre class="prettyprint source lang-js"><code>import Print from '../class/print.js'
3
+ <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-01_WELCOME.html">Welcome</a></div><div class="sidebar-section-children"><a href="tutorial-02_GETTING_STARTED.html">Getting Started</a></div><div class="sidebar-section-children"><a href="tutorial-03_USER_GUIDE.html">User Guide</a></div><div class="sidebar-section-children"><a href="tutorial-04_DEV_GUIDE.html">Developer Guide</a></div><div class="sidebar-section-children"><a href="tutorial-05_ECOSYSTEM.html">Ecosystem</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Main.html">Main</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Applet.html">Applet</a></div><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TConfig">TConfig</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div><div class="sidebar-section-children"><a href="global.html#package%25C2%25B7json">package·json</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">lib_helper.js</h1></header><article><pre class="prettyprint source lang-js"><code>import Print from '../class/print.js'
4
4
  import Log from '../class/log.js'
5
5
  import os from 'os'
6
6
  import fs from 'fs-extra'
@@ -9,12 +9,14 @@ import semver from 'semver'
9
9
  import aneka from 'aneka/index.js'
10
10
  import outmatch from 'outmatch'
11
11
  import fastGlob from 'fast-glob'
12
+ import * as readline from 'readline/promises'
12
13
  import { sprintf } from 'sprintf-js'
13
14
  import dayjs from 'dayjs'
14
15
  import utc from 'dayjs/plugin/utc.js'
15
16
  import customParseFormat from 'dayjs/plugin/customParseFormat.js'
16
17
  import localizedFormat from 'dayjs/plugin/localizedFormat.js'
17
18
  import weekOfYear from 'dayjs/plugin/weekOfYear.js'
19
+ import defConfig from './config.js'
18
20
  const { resolvePath } = aneka
19
21
  const {
20
22
  isFunction, isPlainObject, cloneDeep, merge, forOwn, groupBy, find, reduce, map,
@@ -23,74 +25,40 @@ const {
23
25
  } = lodash
24
26
 
25
27
  /**
28
+ * # Helper
29
+ *
26
30
  * Internal helpers called by Bajo and other classes. It should remains
27
- * hidden and not to be imported by any program.
31
+ * hidden and not to be imported by any program. It listed here for documentation purpose only.
28
32
  *
29
33
  * @module Helper
30
34
  */
31
35
 
32
36
  const omitted = ['spawn', 'cwd', 'name', 'alias', 'applet', 'a', 'plugins']
33
37
 
34
- const defConfig = {
35
- env: 'dev',
36
- runtime: {
37
- noWarning: false
38
- },
39
- log: {
40
- timeTaken: false,
41
- dateFormat: 'YYYY-MM-DDTHH:mm:ss.SSS',
42
- useUtc: false,
43
- pretty: false,
44
- applet: false,
45
- traceHook: false,
46
- save: false,
47
- rotation: {
48
- cycle: 'none', // none, daily, weekly, monthly
49
- compressOld: true,
50
- byPlugin: false,
51
- retain: 5
52
- }
53
- },
54
- dump: {
55
- depth: 2,
56
- compact: false,
57
- colors: true,
58
- breakLength: 80,
59
- caller: true,
60
- frame: {
61
- titleAllignment: 'center',
62
- padding: 1,
63
- margin: 1,
64
- borderStyle: 'round'
65
- }
66
- },
67
- lang: Intl.DateTimeFormat().resolvedOptions().lang ?? 'en-US',
68
- intl: {
69
- supported: ['en-US', 'id'],
70
- fallback: 'en-US',
71
- lookupOrder: [],
72
- format: {
73
- emptyValue: '',
74
- datetime: { dateStyle: 'medium', timeStyle: 'short', timeZone: 'UTC' },
75
- date: { dateStyle: 'medium', timeZone: 'UTC' },
76
- time: { timeStyle: 'short', timeZone: 'UTC' },
77
- float: { maximumFractionDigits: 2 },
78
- double: { maximumFractionDigits: 5 },
79
- smallint: {},
80
- integer: {}
81
- },
82
- unitSys: {
83
- 'en-US': 'imperial',
84
- id: 'metric'
85
- }
86
- },
87
- exitHandler: true,
88
- cache: {
89
- purge: [],
90
- purgeIntvDur: '5m'
91
- }
92
- }
93
-
38
+ /**
39
+ * Main plugin class. This is the special plugin created by Bajo during the boot process, solely made
40
+ * for you to write your own code for your application. It behaves like any other plugin, with a few differences:
41
+ *
42
+ * - It always there, can't be disabled nor removed, reside in the app's root directory named `main`.
43
+ * If it's folder is missing, Bajo will create it during the boot process with the default template and settings.
44
+ * - You don't have to put this plugin in your `.plugin` file or `package.json` file,
45
+ * it is always present and always loaded as the **last** plugin to load.
46
+ * - You write your app the same way you would do it with plugin:
47
+ * - by creating properties &amp; methods within your class
48
+ * - by extending other plugins inside `/main/extend/{pluginNs}` folder
49
+ * - by tapping hooks inside `/main/extend/bajo/hook` folder or `/main/extend/bajo/hook.js` file
50
+ * - you even have your own config file in `{dataDir}/config/main.{ext}`
51
+ *
52
+ * Why it has to be a plugin? Because Bajo is a plugin-based framework, and everything is a plugin.
53
+ * This brings some flexibilities, e.g. when someday you think your main plugin become too big,
54
+ * just turn it into a separate plugin and move it to its own package with almost no code changes at all.
55
+ *
56
+ * For more on this, see {@link Plugin} and {@link module:Hook}
57
+ *
58
+ * @class
59
+ * @global
60
+ * @name Main
61
+ */
94
62
  const defMain = `async function factory (pkgName) {
95
63
  const me = this
96
64
 
@@ -105,7 +73,19 @@ const defMain = `async function factory (pkgName) {
105
73
  export default factory
106
74
  `
107
75
 
108
- function outmatchNs (source, pattern) {
76
+ /**
77
+ * Outmacth with support for scoped source and pattern. Scoped source/pattern is a source/pattern that has a plugin's namespace prefix,
78
+ * separated by `:` symbol.
79
+ *
80
+ * This function is usefull for matching a source/pattern that is scoped to a specific plugin's namespace.
81
+ * For example, if you have a source `myPlugin:foo/bar` and a pattern `myPlugin:foo/*`,
82
+ * this function will return `true` because the source matches the pattern within the same namespace.
83
+ *
84
+ * @param {string} source - Scoped source to match against the pattern. It should be in the format of `ns:path`
85
+ * @param {string} pattern - Scoped pattern to match against the source. It should be in the format of `ns:path`
86
+ * @returns {boolean} Returns `true` if the source matches the pattern, otherwise `false`.
87
+ */
88
+ export function outmatchNs (source, pattern) {
109
89
  const { breakNsPath } = this.bajo
110
90
  const [src, subSrc] = source.split(':')
111
91
  if (!subSrc) return pattern === src
@@ -118,7 +98,20 @@ function outmatchNs (source, pattern) {
118
98
  }
119
99
  }
120
100
 
121
- function parseObject (obj, options = {}) {
101
+ /**
102
+ * Parse object and normalize their values.
103
+ *
104
+ * Support automatic value translation for keys with `t:` prefix. The keys are then
105
+ * converted to the one without the `t:` prefix, and the original key is removed from the object.
106
+ *
107
+ * See {@link https://ardhi.github.io/aneka/global.html#parseObject|aneka.parseObject()} for more details.
108
+ *
109
+ * @method
110
+ * @param {*} obj - The object to parse and normalize.
111
+ * @param {object} [options={}] - Options for parsing and translation.
112
+ * @returns {object} The parsed and normalized object.
113
+ */
114
+ export function parseObject (obj, options = {}) {
122
115
  const me = this
123
116
  const { ns = 'bajo', lang } = options
124
117
  options.translator = {
@@ -139,6 +132,15 @@ dayjs.extend(localizedFormat)
139
132
  dayjs.extend(weekOfYear)
140
133
 
141
134
  /**
135
+ * Most commonly used libraries by Bajo and its plugins. They are already imported and ready to use,
136
+ * so you don't have to import them again in your plugin.
137
+ *
138
+ * Example:
139
+ * ```javascript
140
+ * const { fs, dayjs } = this.app.lib
141
+ * fs.ensureDirSync('/path/to/dir')
142
+ * const now = dayjs().format('YYYY-MM-DD HH:mm:ss')
143
+ * ```
142
144
  * @typedef {Object} TLib
143
145
  * @memberof App
144
146
  * @property {Object} _ Access to {@link https://lodash.com|lodash}.
@@ -148,8 +150,11 @@ dayjs.extend(weekOfYear)
148
150
  * @property {Object} aneka Access to {@link https://github.com/ardhi/aneka|aneka}.
149
151
  * @property {Object} outmatch Access to {@link https://github.com/axtgr/outmatch|outmatch}.
150
152
  * @property {Object} dayjs Access to {@link https://day.js.org|dayjs} with utc &amp; customParseFormat plugin already applied.
151
- * @property {Object} freeze
152
- * @property {Object} findDeep
153
+ * @property {Object} freeze Freeze object. See {@link module:Helper.freeze|freeze} for more details.
154
+ * @property {Object} findDeep Deep file file in an array of files. See {@link module:Helper.findDeep|findDeep} for more details.
155
+ * @property {Object} outmatchNs Like outmatch, but support scoped source &amp; pattern. See {@link module:Helper.outmatchNs|outmatchNs} for more details.
156
+ * @property {Object} parseObject Parse object and normalize their values. Also support translation. See {@link module:Helper.parseObject|parseObject} for more details.
157
+ * @property {Object} [anekaSpatial] Access to {@link https://ardhi.github.io/bajo-spatial|aneka-spatial} helpers if `bajoSpatial` plugin is loaded.
153
158
  * @see App
154
159
  */
155
160
  export const lib = {
@@ -166,27 +171,49 @@ export const lib = {
166
171
  parseObject
167
172
  }
168
173
 
174
+ export async function ask (question, ...args) {
175
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
176
+ let answer
177
+ try {
178
+ answer = await rl.question(this.t(question, ...args))
179
+ } catch (err) {
180
+ console.error(err)
181
+ } finally {
182
+ rl.close()
183
+ }
184
+ return answer
185
+ }
186
+
169
187
  /**
170
- * Building bajo base config. Mostly dealing with directory setups:
171
- * - determine base directory
172
- * - check whether data directory is valid. If not exist, create one inside app dir
173
- * - ensure data config directory is there
174
- * - ensure tmp dir is there
175
- * - read the list of plugins from ```.plugins``` file
188
+ * Build base configuration object for Bajo. This function is called by Bajo during the {@link Bajo#init|initialization process}:
189
+ * - it sets up the base directory,
190
+ * - it checks the validity of the data directory,
191
+ * - it ensures the temporary directory exists,
192
+ * - it reads the list of used plugins from the `package.json`, `.plugins` file or command line options.
176
193
  *
177
194
  * @async
178
195
  * @method
179
196
  */
180
197
  export async function buildBaseConfig () {
181
- // dirs
182
198
  const { defaultsDeep, textToArray, currentLoc, resolvePath } = this.app.lib.aneka
183
- this.config = defaultsDeep({}, this.app.argv._, this.app.envVars._)
199
+ // dirs
200
+ this.config = defaultsDeep({}, this.app.envVars._, this.app.argv._)
184
201
  set(this, 'dir.base', this.app.dir)
185
202
  const path = currentLoc(import.meta).dir + '/..'
186
203
  set(this, 'dir.pkg', resolvePath(path))
187
204
  if (get(this, 'config.dir.data')) set(this, 'dir.data', this.config.dir.data)
188
205
  if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
189
206
  this.dir.data = resolvePath(this.dir.data)
207
+ if (!fs.existsSync(this.dir.data)) {
208
+ console.error("Data directory does not exist: '%s'", this.dir.data)
209
+ let answer
210
+ do {
211
+ answer = await ask.call(this, 'Create one now? (y/n) ', this.dir.data)
212
+ answer = answer.trim().toLowerCase()
213
+ } while (!['y', 'n'].includes(answer))
214
+ if (answer === 'n') this.app.exit(true, 'Aborted')
215
+ fs.ensureDirSync(this.dir.data)
216
+ }
190
217
  fs.ensureDirSync(`${this.dir.data}/config`)
191
218
  if (!this.dir.tmp) {
192
219
  this.dir.tmp = `${resolvePath(os.tmpdir())}/${this.ns}`
@@ -194,8 +221,8 @@ export async function buildBaseConfig () {
194
221
  }
195
222
  this.pkg = await this.getPkgInfo()
196
223
  let pluginPkgs = this.app.pluginPkgs
224
+ // collect list of plugins
197
225
  if (isEmpty(pluginPkgs)) {
198
- // collect list of plugins
199
226
  const mainPkg = await this.getPkgInfo(this.app.dir)
200
227
  pluginPkgs = get(mainPkg, 'bajo.plugins', [])
201
228
  if (isEmpty(pluginPkgs)) {
@@ -210,12 +237,12 @@ export async function buildBaseConfig () {
210
237
  }
211
238
 
212
239
  /**
213
- * Building all plugins:
214
- * - load from app's pluginPkgs
215
- * - iterate through the list and build related plugins
216
- * - making sure main plugin is there. If not, create from template
217
- * - attach these plugins to the app instance
240
+ * Load, build and sanitize all plugins defined in `package.json` or `.plugins` file, and add them to the app's plugin list.
241
+ * It will also make sure the `main` plugin is there too, if not, it will be created with the default template and settings.
218
242
  *
243
+ * Finally, it will loop through all loaded plugins and its config and language files.
244
+ *
245
+ * This function is called by Bajo during the {@link Bajo#init|initialization process}.
219
246
  * @async
220
247
  */
221
248
  export async function buildPlugins () {
@@ -243,12 +270,17 @@ export async function buildPlugins () {
243
270
  this.app.addPlugin(plugin, ClassDef)
244
271
  this.log.trace('- ' + pkg)
245
272
  }
273
+ // load all plugins' config &amp; language files
274
+ for (const ns of this.app.getAllNs()) {
275
+ await this.app[ns].loadConfig()
276
+ this.app[ns].print = new Print(this.app[ns])
277
+ this.app.loadIntl(ns)
278
+ }
246
279
  this.log.debug('buildPluginsComplete')
247
280
  }
248
281
 
249
282
  /**
250
- * Collect all config handlers, including the one provided by plugins
251
- *
283
+ * Collect all plugins' config handlers and push them to the app's {@link App#configHandlers|configHandlers} array.
252
284
  * @async
253
285
  */
254
286
  export async function collectConfigHandlers () {
@@ -269,14 +301,17 @@ export async function collectConfigHandlers () {
269
301
  }
270
302
 
271
303
  /**
272
- * Bajo extra config:
273
- * - reading config file
274
- * - merge config with arguments &amp; environments values
275
- * - Set environment (```dev``` or ```prod```)
304
+ * Building Bajo configuration object. Called after all config handlers are collected,
305
+ * this function will read config files written in any of config handlers' supported formats:
306
+ * - if runtime configuration object is provided (the one passed to the app constructor), it will be
307
+ * used as the base. Otherwise, files are read from the `{dataDir}/config`
308
+ * - then it will be merged with the configuration coming from the command line arguments and environment variables
309
+ * - doing some normalization and validation works, including setting the language, environment, log level, and cache purge settings
310
+ * - and setting up the app's log instance with the proper log level
276
311
  *
277
312
  * @async
278
313
  */
279
- export async function buildExtConfig () {
314
+ export async function buildConfig () {
280
315
  // config merging
281
316
  const { defaultsDeep, includes } = this.app.lib.aneka
282
317
  const { parseObject } = this.app.lib
@@ -322,7 +357,8 @@ export async function buildExtConfig () {
322
357
  }
323
358
 
324
359
  /**
325
- * Setup plugins boot orders by reading plugin's ```.bootorder``` file if provided.
360
+ * Determine the boot order of plugins based on their `bajo.bootorder` property in their `package.json` file.
361
+ * If not provided, the plugin will be booted in the order they are listed in the `package.json` file or `.plugins` file.
326
362
  *
327
363
  * @async
328
364
  */
@@ -350,50 +386,36 @@ export async function bootOrder () {
350
386
  }
351
387
 
352
388
  /**
353
- * Build configurations
354
- *
355
- * @async
356
- */
357
- export async function buildConfigs () {
358
- this.bajo.log.debug('readConfigs')
359
- for (const ns of this.getAllNs()) {
360
- await this[ns].loadConfig()
361
- this[ns].print = new Print(this[ns])
362
- this.loadIntl(ns)
363
- }
364
- }
365
-
366
- /**
367
- * Ensure for names and aliases to be unique and no clashes with other plugins
368
- *
389
+ * Ensure the uniqueness of all plugins' name and alias. If there is a clash, an error will be thrown.
369
390
  * @async
370
391
  */
371
392
  export async function checkNameAliases () {
372
- this.bajo.log.debug('checkAliasNameClash')
393
+ this.log.debug('checkAliasNameClash')
373
394
  const refs = []
374
- for (const pkg of this.bajo.app.pluginPkgs) {
375
- const plugin = this.bajo.app[camelCase(pkg)]
395
+ for (const pkg of this.app.pluginPkgs) {
396
+ const plugin = this.app[camelCase(pkg)]
376
397
  const { ns, alias } = plugin
377
398
  let item = find(refs, { ns })
378
- if (item) throw this.bajo.error('pluginNameClash%s%s%s%s', ns, pkg, item.ns, item.pkg, { code: 'BAJO_NAME_CLASH' })
399
+ if (item) throw this.error('pluginNameClash%s%s%s%s', ns, pkg, item.ns, item.pkg, { code: 'BAJO_NAME_CLASH' })
379
400
  item = find(refs, { alias })
380
- if (item) throw this.bajo.error('pluginNameClash%s%s%s%s', alias, pkg, item.alias, item.pkg, { code: 'BAJO_ALIAS_CLASH' })
401
+ if (item) throw this.error('pluginNameClash%s%s%s%s', alias, pkg, item.alias, item.pkg, { code: 'BAJO_ALIAS_CLASH' })
381
402
  refs.push({ ns, alias, pkg })
382
403
  }
383
404
  }
384
405
 
385
406
  /**
386
- * Ensure dependencies are met
407
+ * Ensure all plugins' dependencies are met. If there is a missing dependency, an error will be thrown.
387
408
  *
409
+ * Semver is also supported, so if a plugin requires a specific version of another plugin, it will be checked as well.
388
410
  * @async
389
411
  */
390
412
  export async function checkDependencies () {
391
- const { join } = this.bajo
392
- this.bajo.log.debug('checkDeps')
393
- for (const pkg of this.bajo.app.pluginPkgs) {
394
- const plugin = this.bajo.app[camelCase(pkg)]
413
+ const { join } = this
414
+ this.log.debug('checkDeps')
415
+ for (const pkg of this.app.pluginPkgs) {
416
+ const plugin = this.app[camelCase(pkg)]
395
417
  const { ns, dependencies } = plugin
396
- this.bajo.log.trace('- %s', ns)
418
+ this.log.trace('- %s', ns)
397
419
  const odep = reduce(dependencies, (o, k) => {
398
420
  const item = map(k.split('@'), m => trim(m))
399
421
  if (k[0] === '@') o['@' + item[1]] = item[2]
@@ -402,15 +424,15 @@ export async function checkDependencies () {
402
424
  }, {})
403
425
  const deps = keys(odep)
404
426
  if (deps.length > 0) {
405
- if (intersection(this.bajo.app.pluginPkgs, deps).length !== deps.length) {
406
- throw this.bajo.error('dependencyUnfulfilled%s%s', pkg, join(deps), { code: 'BAJO_DEPENDENCY' })
427
+ if (intersection(this.app.pluginPkgs, deps).length !== deps.length) {
428
+ throw this.error('dependencyUnfulfilled%s%s', pkg, join(deps), { code: 'BAJO_DEPENDENCY' })
407
429
  }
408
430
  each(deps, d => {
409
431
  if (!odep[d]) return
410
- const ver = get(this.bajo.app[camelCase(d)], 'pkg.version')
432
+ const ver = get(this.app[camelCase(d)], 'pkg.version')
411
433
  if (!ver) return
412
434
  if (!semver.satisfies(ver, odep[d])) {
413
- throw this.bajo.error('semverCheckFailed%s%s', pkg, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
435
+ throw this.error('semverCheckFailed%s%s', pkg, `${d}@${odep[d]}`, { code: 'BAJO_DEPENDENCY_SEMVER' })
414
436
  }
415
437
  })
416
438
  }
@@ -418,15 +440,14 @@ export async function checkDependencies () {
418
440
  }
419
441
 
420
442
  /**
421
- * Collect and build hooks and push them to the bajo's hook system
422
- *
443
+ * Collect all plugins' hooks and push them to the app's {@link App#hooks|hooks} array.
423
444
  * @async
424
445
  */
425
446
  export async function collectHooks () {
426
- const { eachPlugins, runHook, isLogInRange, importModule } = this.bajo
427
- const { isArray, isPlainObject } = this.lib._
428
- const me = this // "this" is "app"
429
- me.bajo.log.trace('collecting%s', this.t('hooks'))
447
+ const { eachPlugins, runHook, isLogInRange, importModule } = this
448
+ const { isArray, isPlainObject } = this.app.lib._
449
+ const me = this // "this" is "bajo"
450
+ this.log.trace('collecting%s', this.t('hooks'))
430
451
  await eachPlugins(async function ({ dir, file }) {
431
452
  let mod = await importModule(file, { asHandler: true })
432
453
  if (!mod) return undefined
@@ -437,11 +458,11 @@ export async function collectHooks () {
437
458
  if (!m.name) throw me.bajo.error('missing%s%s', 'name', file)
438
459
  if (isArray(m.name)) {
439
460
  for (const name of m.name) {
440
- me.bajo.hooks.push(merge({}, m, { name, src: this.ns }))
461
+ me.hooks.push(merge({}, m, { name, src: this.ns }))
441
462
  }
442
463
  } else {
443
464
  m.src = this.ns
444
- me.bajo.hooks.push(m)
465
+ me.hooks.push(m)
445
466
  }
446
467
  }
447
468
  } else {
@@ -451,81 +472,55 @@ export async function collectHooks () {
451
472
  for (let name of names) {
452
473
  name = name.split('.').map(n => camelCase(n)).join('.')
453
474
  const m = merge({}, mod, { name: `${name}:${camelCase(path)}`, src: this.ns })
454
- me.bajo.hooks.push(m)
475
+ me.hooks.push(m)
455
476
  }
456
477
  }
457
- }, { glob: ['hook/*.js', 'hook.js'], prefix: me.bajo.ns })
478
+ }, { glob: ['hook/*.js', 'hook.js'], prefix: this.ns })
458
479
  // for log trace purpose only
459
480
  if (isLogInRange('trace')) {
460
- const items = groupBy(me.bajo.hooks, item => item.name)
481
+ const items = groupBy(this.hooks, item => item.name)
461
482
  forOwn(items, (v, k) => {
462
483
  const [name, path] = k.split(':')
463
- me.bajo.log.trace('- %s:%s (%d)', name, path, v.length)
484
+ this.log.trace('- %s:%s (%s)', name, path, v.length)
464
485
  })
465
486
  }
466
487
 
467
- await runHook('bajo:afterCollectHooks', this.bajo.hooks)
468
- me.bajo.log.debug('collected%s%d', this.t('hooks'), me.bajo.hooks.length)
488
+ await runHook('bajo:afterCollectHooks', this.hooks)
489
+ this.log.debug('collected%s%d', this.t('hooks'), this.hooks.length)
469
490
  }
470
491
 
471
492
  /**
472
- * Finally, run all plugins
493
+ * Run all loaded plugins' `init()` and `start()` methods in the order determined previously during boot process.
473
494
  *
474
495
  * @async
475
- * @see module:Hook~bajo:beforeAllInit
476
- * @see module:Hook~{ns}:beforeInit
477
- * @see module:Hook~{ns}:afterInit
478
- * @see module:Hook~bajo:afterAllInit
479
- * @see module:Hook~bajo:beforeAllStart
480
- * @see module:Hook~{ns}:beforeStart
481
- * @see module:Hook~{ns}:afterStart
482
- * @see module:Hook~bajo:afterAllStart
496
+ * @see module:Hook
483
497
  */
484
- export async function run () {
498
+ export async function runPlugins () {
485
499
  const me = this
486
- const { runHook, eachPlugins, join } = me.bajo
487
- const { freeze } = me.lib
500
+ const { runHook, eachPlugins, join } = this
501
+ const { freeze } = this.app.lib
488
502
  const methods = ['init']
489
- if (!me.applet) methods.push('start')
503
+ if (!this.app.applet) methods.push('start')
490
504
  for (const method of methods) {
491
505
  await runHook(`bajo:${camelCase(`before all ${method}`)}`)
492
506
  await eachPlugins(async function () {
493
507
  const { ns } = this
494
508
  await runHook(`${ns}:${camelCase(`before ${method}`)}`)
495
- await me[ns][method]()
509
+ await me.app[ns][method]()
496
510
  await runHook(`${ns}:${camelCase(`after ${method}`)}`)
497
- if (method === 'start') freeze(me[ns].config)
511
+ if (method === 'start') freeze(me.app[ns].config)
498
512
  })
499
513
  await runHook(`bajo:${camelCase(`after all ${method}`)}`)
500
514
  }
501
- if (me.bajo.config.log.level === 'trace') {
502
- let text = join(map(me.bajo.app.pluginPkgs, b => camelCase(b)))
503
- text += ` (${me.bajo.app.pluginPkgs.length})`
504
- me.bajo.log.trace('loadedPlugins%s', text)
505
- } else me.bajo.log.debug('loadedPlugins%s', me.bajo.app.pluginPkgs.length)
506
- }
507
-
508
- /**
509
- * Iterate through all plugins loaded and do:
510
- *
511
- * 1. {@link module:Helper/Base.buildConfigs|build configs}
512
- * 2. {@link module:Helper/Base.checkNameAliases|ensure names &amp; aliases uniqueness}
513
- * 3. {@link module:Helper/Base.checkDependencies|ensure dependencies are met}
514
- * 4. {@link module:Helper/Base.collectHooks|collect hooks}
515
- * 5. {@link module:Helper/Base.run|run plugins}
516
- *
517
- * @async
518
- */
519
- export async function bootPlugins () {
520
- await buildConfigs.call(this.app)
521
- await checkNameAliases.call(this.app)
522
- await checkDependencies.call(this.app)
523
- await collectHooks.call(this.app)
524
- await run.call(this.app)
515
+ if (this.config.log.level === 'trace') {
516
+ let text = join(map(this.app.pluginPkgs, b => camelCase(b)))
517
+ text += ` (${this.app.pluginPkgs.length})`
518
+ this.log.trace('loadedPlugins%s', text)
519
+ } else this.log.debug('loadedPlugins%s', this.app.pluginPkgs.length)
525
520
  }
526
521
 
527
522
  /**
528
- * Attach plugins exit handlers and make sure the app shutdowns gracefully
523
+ * Attach plugins exit handlers to make sure when the app shuts down, all plugins shut down gracefully first
529
524
  *
530
525
  * @async
531
526
  */
@@ -533,15 +528,14 @@ export async function exitHandler () {
533
528
  if (!this.config.exitHandler) return
534
529
 
535
530
  async function exit (signal) {
536
- const { eachPlugins } = this
537
531
  if (signal) this.log.warn('signalReceived%s', signal)
538
- const me = this
539
- await eachPlugins(async function ({ ns }) {
532
+ const allNs = this.app.getAllNs().reverse()
533
+ for (const ns of allNs) {
540
534
  try {
541
- await this.exit()
535
+ await this.app[ns].exit()
542
536
  } catch (err) {}
543
- me.log.trace('exited%s', this.ns)
544
- })
537
+ this.log.trace('exited%s', ns)
538
+ }
545
539
  this.log.debug('appShutdown')
546
540
  process.exit(0)
547
541
  }
@@ -587,11 +581,10 @@ export async function exitHandler () {
587
581
  }
588
582
 
589
583
  /**
590
- * If app is in ```applet``` mode, this little helper should take care plugin's applet boot process
584
+ * If app is in `applet` mode, this little helper should take care plugin's applet boot process
591
585
  *
592
586
  * @async
593
- * @fires {ns}:beforeAppletRun
594
- * @fires {ns}:afterAppletRun
587
+ * @see module:Applet
595
588
  */
596
589
  export async function runAsApplet () {
597
590
  const { isString, map, find } = this.app.lib._
@@ -624,27 +617,27 @@ export async function runAsApplet () {
624
617
  * Import file/module from any loaded plugins.
625
618
  *
626
619
  * E.g. your plugin structure looks like this:
627
- * ```
620
+ * `
628
621
  * |- src
629
622
  * | |- lib
630
623
  * | | |- my-module.js
631
624
  * |- index.js
632
625
  * |- package.json
633
- * ```
626
+ * `
634
627
  *
635
- * And this is how to import ```my-module.js```:
636
- * ```javascript
628
+ * And this is how to import `my-module.js`:
629
+ * `javascript
637
630
  * const { importModule } = this.app.bajo
638
631
  * const myModule = await importModule('myPlugin:/src/lib/my-module.js')
639
- * ```
632
+ * `
640
633
  *
641
634
  * @method
642
635
  * @async
643
636
  * @param {TNsPathPairs} file File to import.
644
637
  * @param {Object} [options={}] Options.
645
- * @param {boolean} [options.asDefaultImport=true] If ```true``` (default), return default imported module.
646
- * @param {boolean} [options.asHandler] If ```true```, return as a {@link HandlerType|handler}.
647
- * @param {boolean} [options.noCache] If ```true```, always import as a fresh copy.
638
+ * @param {boolean} [options.asDefaultImport=true] If `true` (default), return default imported module.
639
+ * @param {boolean} [options.asHandler] If `true`, return as a {@link HandlerType|handler}.
640
+ * @param {boolean} [options.noCache] If `true`, always import as a fresh copy.
648
641
  * @returns {any}
649
642
  * @see Bajo#importModule
650
643
  */
@@ -660,6 +653,7 @@ export async function importModule (file, { asDefaultImport = true, asHandler, n
660
653
  if (this) file = this.app.getPluginFile(file)
661
654
  if (!fs.existsSync(file)) return
662
655
  let mod = await load(file, asDefaultImport, noCache)
656
+ if (!mod) return
663
657
  if (!asHandler) return mod
664
658
  if (isFunction(mod)) mod = { level: 999, handler: mod }
665
659
  if (!isPlainObject(mod)) {
@@ -674,7 +668,7 @@ export async function importModule (file, { asDefaultImport = true, asHandler, n
674
668
  *
675
669
  * @method
676
670
  * @param {Object} obj Object to freeze.
677
- * @param {boolean} [shallow=false] If ```false``` (default), deep freeze object.
671
+ * @param {boolean} [shallow=false] If `false` (default), deep freeze object.
678
672
  */
679
673
  export function freeze (obj, options = {}) {
680
674
  const { shallow = false, clone = false } = options
@@ -802,4 +796,4 @@ export const formats = {
802
796
  degreeUnitSep: ''
803
797
  }
804
798
  }
805
- </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="mobile-nav-links"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>
799
+ </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="mobile-nav-links"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-01_WELCOME.html">Welcome</a></div><div class="sidebar-section-children"><a href="tutorial-02_GETTING_STARTED.html">Getting Started</a></div><div class="sidebar-section-children"><a href="tutorial-03_USER_GUIDE.html">User Guide</a></div><div class="sidebar-section-children"><a href="tutorial-04_DEV_GUIDE.html">Developer Guide</a></div><div class="sidebar-section-children"><a href="tutorial-05_ECOSYSTEM.html">Ecosystem</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Main.html">Main</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Applet.html">Applet</a></div><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TConfig">TConfig</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div><div class="sidebar-section-children"><a href="global.html#package%25C2%25B7json">package·json</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>