@nutin/cli 1.3.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/CHANGELOG.md +106 -9
  2. package/README.md +17 -35
  3. package/bin/add-feature.mjs +9 -0
  4. package/bin/cli.mjs +8 -0
  5. package/bin/create-app.mjs +5 -4
  6. package/bin/update-app.mjs +9 -0
  7. package/lib/index.mjs +76 -25
  8. package/lib/src/add-feature/feature-adder.mjs +83 -0
  9. package/lib/src/add-feature/feature-context-builder.mjs +25 -0
  10. package/lib/src/add-feature/nutin-config-updater.mjs +29 -0
  11. package/lib/src/add-feature/package-json-updater.mjs +49 -0
  12. package/lib/src/common/feature-registry.mjs +11 -0
  13. package/lib/src/common/file-generator.mjs +159 -0
  14. package/lib/src/common/meta-bootstrap-prompt.mjs +33 -0
  15. package/lib/src/common/package-data.mjs +10 -0
  16. package/lib/src/common/package-json-helper.mjs +103 -0
  17. package/lib/src/common/project-meta.mjs +54 -0
  18. package/lib/src/create-app/context-builder.mjs +21 -0
  19. package/lib/src/{git-manager.mjs → create-app/git-manager.mjs} +2 -2
  20. package/lib/src/create-app/json-manager.mjs +73 -0
  21. package/lib/src/{prompts.mjs → create-app/new-app-prompt.mjs} +6 -6
  22. package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +10 -8
  23. package/lib/src/create-app/success-message.mjs +13 -0
  24. package/lib/src/update-app/conflict-report-writer.mjs +31 -0
  25. package/lib/src/update-app/fetch-old-templates.mjs +61 -0
  26. package/lib/src/update-app/project-updater.mjs +142 -0
  27. package/lib/src/update-app/template-differ.mjs +124 -0
  28. package/lib/src/update-app/update-context-builder.mjs +25 -0
  29. package/lib/src/update-app/update-summary-printer.mjs +22 -0
  30. package/lib/src/update-app/version-compare.mjs +12 -0
  31. package/lib/src/utils/print.mjs +36 -0
  32. package/lib/src/utils/promise-exec-alias.mjs +4 -0
  33. package/package.json +17 -12
  34. package/templates/base/.gitignore.hbs +6 -1
  35. package/templates/base/AGENTS.md.hbs +168 -0
  36. package/templates/base/GETTING_STARTED.md.hbs +199 -0
  37. package/templates/base/README.md.hbs +8 -17
  38. package/templates/base/config/languages.json.hbs +6 -0
  39. package/templates/base/config/seo.json.hbs +12 -0
  40. package/templates/base/nutin.config.js.hbs +44 -0
  41. package/templates/base/src/app/components/index.ts.hbs +1 -2
  42. package/templates/base/src/app/globals.d.ts.hbs +3 -19
  43. package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
  44. package/templates/base/src/app/main.ts.hbs +6 -26
  45. package/templates/base/src/app/services/index.ts.hbs +1 -1
  46. package/templates/base/src/app/views/home/home.view.html.hbs +7 -0
  47. package/templates/base/src/app/views/home/home.view.ts.hbs +2 -57
  48. package/templates/base/src/app/views/index.ts.hbs +1 -0
  49. package/templates/base/src/app/views/not-found/not-found.view.html.hbs +8 -0
  50. package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +5 -38
  51. package/templates/base/src/core/base-classes/base-component/base-component.test.js.hbs +158 -0
  52. package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +68 -35
  53. package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.test.js.hbs +124 -0
  54. package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +9 -8
  55. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +96 -0
  56. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +6 -3
  57. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +151 -0
  58. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +8 -2
  59. package/templates/base/src/core/base-classes/base-component/helpers/event.helper.test.js.hbs +88 -0
  60. package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +2 -2
  61. package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.test.js.hbs +62 -0
  62. package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.test.js.hbs +89 -0
  63. package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.ts.hbs +4 -1
  64. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +142 -0
  65. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +67 -22
  66. package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +183 -0
  67. package/templates/base/src/core/base-classes/component/component.test.js.hbs +85 -0
  68. package/templates/base/src/core/base-classes/component/component.ts.hbs +24 -33
  69. package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +58 -0
  70. package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.test.js.hbs +75 -0
  71. package/templates/base/src/core/base-classes/service/service.test.js.hbs +150 -0
  72. package/templates/base/src/core/base-classes/service/service.ts.hbs +26 -20
  73. package/templates/base/src/core/base-classes/view/view.test.js.hbs +71 -0
  74. package/templates/base/src/core/base-classes/view/view.ts.hbs +24 -49
  75. package/templates/base/src/core/config.ts.hbs +10 -0
  76. package/templates/base/src/core/index.ts.hbs +0 -3
  77. package/templates/base/src/core/internals.d.ts.hbs +27 -0
  78. package/templates/base/src/core/nutin-config.d.ts.hbs +4 -0
  79. package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +67 -3
  80. package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +9 -4
  81. package/templates/base/src/core/services/event-bus/index.ts.hbs +3 -0
  82. package/templates/base/src/core/services/event-bus/lifecycle.facade.test.js.hbs +123 -0
  83. package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +61 -0
  84. package/templates/base/src/core/services/event-bus/navigation.facade.test.js.hbs +46 -0
  85. package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +25 -0
  86. package/templates/base/src/core/services/globals/globals.test.js.hbs +187 -0
  87. package/templates/base/src/core/services/globals/globals.ts.hbs +104 -0
  88. package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +25 -0
  89. package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +20 -0
  90. package/templates/base/src/core/services/http-client/http-client.test.js.hbs +73 -2
  91. package/templates/base/src/core/services/http-client/http-client.ts.hbs +4 -1
  92. package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
  93. package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +22 -4
  94. package/templates/base/src/core/services/index.ts.hbs +5 -6
  95. package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
  96. package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
  97. package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
  98. package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
  99. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +257 -6
  100. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +56 -29
  101. package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
  102. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
  103. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +14 -38
  104. package/templates/base/src/core/services/router/router.test.js.hbs +304 -2
  105. package/templates/base/src/core/services/router/router.ts.hbs +47 -12
  106. package/templates/base/src/index.html.hbs +2 -6
  107. package/templates/base/src/styles/_styles.scss.hbs +102 -43
  108. package/templates/base/src/styles/base/_variables.scss.hbs +1 -86
  109. package/templates/base/src/styles/main.scss.hbs +1 -18
  110. package/templates/base/src/styles/tailwind.css.hbs +2 -0
  111. package/templates/base/tools/builder/builder.config.js.hbs +40 -0
  112. package/templates/base/tools/builder/builder.js.hbs +24 -26
  113. package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
  114. package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
  115. package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
  116. package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
  117. package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
  118. package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
  119. package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
  120. package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
  121. package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
  122. package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
  123. package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
  124. package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
  125. package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
  126. package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
  127. package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
  128. package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
  129. package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
  130. package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
  131. package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
  132. package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
  133. package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
  134. package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
  135. package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
  136. package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
  137. package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
  138. package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
  139. package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
  140. package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
  141. package/templates/base/tools/dev/serve.js.hbs +11 -1
  142. package/templates/base/tools/dev/watcher.js.hbs +14 -6
  143. package/templates/base/tools/generator/generator.js.hbs +44 -47
  144. package/templates/base/tools/generator/handle-file.js.hbs +17 -44
  145. package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
  146. package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
  147. package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
  148. package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
  149. package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
  150. package/templates/base/tools/generator/templates/index.js.hbs +7 -12
  151. package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +8 -0
  152. package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +17 -0
  153. package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
  154. package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
  155. package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
  156. package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
  157. package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
  158. package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
  159. package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +4 -3
  160. package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
  161. package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
  162. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
  163. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
  164. package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
  165. package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
  166. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
  167. package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
  168. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
  169. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
  170. package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
  171. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
  172. package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
  173. package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
  174. package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
  175. package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
  176. package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
  177. package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
  178. package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
  179. package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
  180. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
  181. package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
  182. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
  183. package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
  184. package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
  185. package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
  186. package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
  187. package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
  188. package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
  189. package/templates/base/tools/utils/error-exit.js.hbs +8 -0
  190. package/templates/base/tools/utils/format.js.hbs +10 -10
  191. package/templates/base/tools/utils/format.test.js.hbs +45 -0
  192. package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
  193. package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
  194. package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
  195. package/templates/base/tools/utils/index.js.hbs +1 -3
  196. package/templates/base/tools/utils/languages.js.hbs +15 -0
  197. package/templates/base/tools/utils/languages.test.js.hbs +16 -0
  198. package/templates/base/tools/utils/print.js.hbs +19 -13
  199. package/templates/base/tools/utils/print.test.js.hbs +60 -0
  200. package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
  201. package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
  202. package/templates/base/tools/utils/run-command.js.hbs +4 -2
  203. package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
  204. package/templates/base/tools/utils/run-script.js.hbs +14 -3
  205. package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
  206. package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
  207. package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
  208. package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
  209. package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
  210. package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
  211. package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
  212. package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
  213. package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
  214. package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
  215. package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
  216. package/lib/src/context-builder.mjs +0 -40
  217. package/lib/src/file-generator.mjs +0 -109
  218. package/lib/src/json-generator.mjs +0 -31
  219. package/lib/src/package-manager.mjs +0 -139
  220. package/lib/src/print.mjs +0 -32
  221. package/lib/src/utils.mjs +0 -18
  222. package/lib/src/version.mjs +0 -1
  223. package/templates/base/builder.config.js.hbs +0 -36
  224. package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +0 -32
  225. package/templates/base/src/app/components/common/button/button.component.ts.hbs +0 -40
  226. package/templates/base/src/app/dom.test.js.hbs +0 -13
  227. package/templates/base/src/app/services/example/example.service.ts.hbs +0 -18
  228. package/templates/base/src/app/views/home/home.view.test.js.hbs +0 -26
  229. package/templates/base/src/core/services/store/store.test.js.hbs +0 -8
  230. package/templates/base/src/core/services/store/store.ts.hbs +0 -39
  231. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.test.js.hbs +0 -0
  232. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +0 -113
  233. package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +0 -249
  234. package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +0 -131
  235. package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +0 -50
  236. package/templates/base/src/core/utils/index.ts.hbs +0 -2
  237. package/templates/base/src/libs/index.ts.hbs +0 -4
  238. package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +0 -197
  239. package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +0 -75
  240. package/templates/base/src/libs/popover/popover.ts.hbs +0 -138
  241. package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -75
  242. package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
  243. package/templates/base/src/styles/core/_anchors.scss.hbs +0 -10
  244. package/templates/base/src/styles/core/_index.scss.hbs +0 -6
  245. package/templates/base/src/styles/core/_popover.scss.hbs +0 -96
  246. package/templates/base/src/styles/core/_snackbar.scss.hbs +0 -50
  247. package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
  248. package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
  249. package/templates/base/tools/builder/core/sass.js.hbs +0 -22
  250. package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
  251. package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
  252. package/templates/base/tools/utils/export-flags.js.hbs +0 -6
  253. package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
  254. package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
  255. package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
  256. package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
  257. package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
  258. package/templates/features/i18n/src/core/config.ts.hbs +0 -3
  259. package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
  260. package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
  261. package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
  262. package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
  263. package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
  264. package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
  265. package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -254
  266. package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
  267. package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
  268. package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
  269. package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
  270. package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
  271. package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
  272. package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
  273. package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
  274. package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
  275. package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
  276. package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
  277. package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
  278. package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
  279. package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
  280. /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
  281. /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
  282. /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
  283. /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
  284. /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
  285. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
  286. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
  287. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
  288. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
  289. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
@@ -0,0 +1,150 @@
1
+ import { Service } from '#root/dist/src/core/base-classes/service/service.js';
2
+
3
+ class TestService extends Service {
4
+ constructor() {
5
+ super();
6
+ this.destroyCalls = 0;
7
+ this.cleanupCalls = [];
8
+ }
9
+
10
+ addCleanup(fn) {
11
+ this.registerCleanup(fn);
12
+ }
13
+
14
+ onDestroy() {
15
+ this.destroyCalls++;
16
+ }
17
+
18
+ ping() {
19
+ return this;
20
+ }
21
+ }
22
+
23
+ class OtherTestService extends Service {
24
+ constructor() {
25
+ super();
26
+ this.destroyCalls = 0;
27
+ }
28
+
29
+ onDestroy() {
30
+ this.destroyCalls++;
31
+ }
32
+ }
33
+
34
+ describe('Service', () => {
35
+ afterEach(() => {
36
+ TestService.testingResetAll();
37
+ OtherTestService.testingResetAll();
38
+ });
39
+
40
+ it('throws when instantiated directly instead of via getInstance', () => {
41
+ expect(() => new TestService()).toThrow('TestService is a Service. Use TestService.getInstance() instead.');
42
+ });
43
+
44
+ it('creates the instance lazily on first getInstance call', () => {
45
+ expect(Service.hasInstance(TestService)).toBe(false);
46
+ const instance = TestService.getInstance();
47
+ expect(instance).toBeInstanceOf(TestService);
48
+ expect(Service.hasInstance(TestService)).toBe(true);
49
+ });
50
+
51
+ it('returns the same instance on repeated getInstance calls without args', () => {
52
+ const instance1 = TestService.getInstance();
53
+ const instance2 = TestService.getInstance();
54
+ expect(instance1).toBe(instance2);
55
+ });
56
+
57
+ it('throws if getInstance is called with args on an already-initialized instance', () => {
58
+ TestService.getInstance();
59
+ expect(() => TestService.getInstance('arg')).toThrow('already initialized');
60
+ });
61
+
62
+ it('does not throw when getInstance is called with args for a fresh instance', () => {
63
+ expect(() => TestService.getInstance()).not.toThrow();
64
+ });
65
+
66
+ it('hasInstance reflects whether an instance has been created', () => {
67
+ expect(Service.hasInstance(TestService)).toBe(false);
68
+ TestService.getInstance();
69
+ expect(Service.hasInstance(TestService)).toBe(true);
70
+ });
71
+
72
+ it('registerCleanup callbacks run on dispose and are cleared afterward', () => {
73
+ const instance = TestService.getInstance();
74
+ let cleaned = false;
75
+ instance.addCleanup(() => { cleaned = true; });
76
+
77
+ instance.dispose();
78
+
79
+ expect(cleaned).toBe(true);
80
+ expect(Service.hasInstance(TestService)).toBe(false);
81
+ });
82
+
83
+ it('dispose clears cleanup callbacks so they only run once', () => {
84
+ const instance = TestService.getInstance();
85
+ let calls = 0;
86
+ instance.addCleanup(() => { calls++; });
87
+
88
+ instance.dispose();
89
+ instance.dispose();
90
+
91
+ expect(calls).toBe(1);
92
+ });
93
+
94
+ it('destroy calls onDestroy and removes the instance from the registry', async () => {
95
+ const instance = TestService.getInstance();
96
+ await TestService.destroy();
97
+
98
+ expect(instance.destroyCalls).toBe(1);
99
+ expect(Service.hasInstance(TestService)).toBe(false);
100
+ });
101
+
102
+ it('destroy is a no-op when no instance exists', async () => {
103
+ expect(Service.hasInstance(TestService)).toBe(false);
104
+ await TestService.destroy();
105
+ expect(Service.hasInstance(TestService)).toBe(false);
106
+ });
107
+
108
+ it('destroyAll calls onDestroy on every instance and clears the registry', async () => {
109
+ const a = TestService.getInstance();
110
+ const b = OtherTestService.getInstance();
111
+
112
+ await Service.destroyAll();
113
+
114
+ expect(a.destroyCalls).toBe(1);
115
+ expect(b.destroyCalls).toBe(1);
116
+ expect(Service.hasInstance(TestService)).toBe(false);
117
+ expect(Service.hasInstance(OtherTestService)).toBe(false);
118
+ });
119
+
120
+ it('autoBindMethods binds subclass methods so they work detached from the instance', () => {
121
+ const instance = TestService.getInstance();
122
+ const { ping } = instance;
123
+ expect(ping()).toBe(instance);
124
+ });
125
+
126
+ it('testingReset removes only the targeted instance without running cleanup', () => {
127
+ const instance1 = TestService.getInstance();
128
+ let cleaned = false;
129
+ instance1.addCleanup(() => { cleaned = true; });
130
+
131
+ TestService.testingReset();
132
+
133
+ expect(cleaned).toBe(false);
134
+ const instance2 = TestService.getInstance();
135
+ expect(instance1).not.toBe(instance2);
136
+ });
137
+
138
+ it('testingResetAll clears every instance without running cleanup', () => {
139
+ const a = TestService.getInstance();
140
+ OtherTestService.getInstance();
141
+ let cleaned = false;
142
+ a.addCleanup(() => { cleaned = true; });
143
+
144
+ Service.testingResetAll();
145
+
146
+ expect(cleaned).toBe(false);
147
+ expect(Service.hasInstance(TestService)).toBe(false);
148
+ expect(Service.hasInstance(OtherTestService)).toBe(false);
149
+ });
150
+ });
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Abstract class for implementing the Singleton pattern.
2
+ * Abstract class for implementing the Singleton pattern.
3
3
  * Supports constructor parameters for initialization.
4
4
  */
5
5
  export abstract class Service<T extends Service<T>> {
6
6
  private static _instances = new Map<Function, any>();
7
7
  private static _instantiating = new Set<Function>();
8
8
  private _cleanupCallbacks: (() => void)[] = [];
9
-
9
+
10
10
  constructor(...args: any[]) {
11
11
  const constructor = this.constructor as new (...args: any[]) => T;
12
-
12
+
13
13
  if (!Service._instantiating.has(constructor)) {
14
14
  throw new Error(
15
15
  `${constructor.name} is a Service. Use ${constructor.name}.getInstance() instead.`
@@ -19,31 +19,39 @@ export abstract class Service<T extends Service<T>> {
19
19
  window.addEventListener('beforeunload', this.dispose);
20
20
  }
21
21
 
22
- public static createInstance<T extends Service<T>>(
23
- constructor: new (...args: any[]) => T,
22
+ /**
23
+ * Returns the singleton instance, creating it with the provided args if it
24
+ * doesn't exist yet. Throws if args are passed to an already-initialized instance,
25
+ * preventing silent re-initialization mismatches.
26
+ */
27
+ public static getInstance<T extends Service<T>>(
28
+ this: new (...args: any[]) => T,
24
29
  ...args: any[]
25
30
  ): T {
26
- if (!Service._instances.has(constructor)) {
27
- Service._instantiating.add(constructor);
28
- Service._instances.set(constructor, new constructor(...args));
29
- Service._instantiating.delete(constructor);
31
+ const alreadyExists = Service._instances.has(this);
32
+
33
+ if (alreadyExists && args.length > 0) {
34
+ throw new Error(
35
+ `${this.name} is already initialized. ` +
36
+ `getInstance() was called with arguments on an existing instance, ` +
37
+ `which would silently ignore them. ` +
38
+ `Call getInstance() without arguments to retrieve the existing instance.`
39
+ );
30
40
  }
31
- return Service._instances.get(constructor);
32
- }
33
41
 
34
- public static getInstance<T extends Service<T>>(this: new () => T): T {
35
- if (!Service._instances.has(this)) {
42
+ if (!alreadyExists) {
36
43
  Service._instantiating.add(this);
37
- Service._instances.set(this, new this());
44
+ Service._instances.set(this, new this(...args));
38
45
  Service._instantiating.delete(this);
39
46
  }
47
+
40
48
  return Service._instances.get(this);
41
49
  }
42
50
 
43
51
  public static hasInstance<T extends Service<T>>(
44
52
  constructor: new (...args: any[]) => T
45
53
  ): boolean {
46
- return Service._instances.has(constructor);
54
+ return Service._instances.has(constructor);
47
55
  }
48
56
 
49
57
  public static async destroy<T extends Service<T>>(
@@ -65,7 +73,7 @@ export abstract class Service<T extends Service<T>> {
65
73
  public static async destroyAll(): Promise<void> {
66
74
  const destroyPromises = Array.from(Service._instances.values())
67
75
  .map(instance => instance.onDestroy());
68
-
76
+
69
77
  await Promise.all(destroyPromises);
70
78
  Service._instances.clear();
71
79
  Service._instantiating.clear();
@@ -76,11 +84,9 @@ export abstract class Service<T extends Service<T>> {
76
84
  }
77
85
 
78
86
  /**
79
- * For async and custom cleanup operations
87
+ * For async and custom cleanup operations.
80
88
  */
81
- protected onDestroy<T extends Service<T>>(this: () => T): void {
82
- console.log(`No onDestroy operations.`)
83
- }
89
+ protected onDestroy(): void {}
84
90
 
85
91
  private autoBindMethods(): void {
86
92
  let proto = Object.getPrototypeOf(this);
@@ -0,0 +1,71 @@
1
+ import { View } from '#root/dist/src/core/index.js';
2
+
3
+ class HomeView extends View {}
4
+ class NamedView extends View {}
5
+
6
+ describe('View', () => {
7
+ let app;
8
+
9
+ beforeEach(() => {
10
+ app = document.getElementById('app');
11
+ });
12
+
13
+ afterEach(() => {
14
+ app.innerHTML = '';
15
+ app = null;
16
+ });
17
+
18
+ it('throws when viewName is omitted', () => {
19
+ expect(() => new HomeView({})).toThrow();
20
+ });
21
+
22
+ it('throws when viewName is an empty string', () => {
23
+ expect(() => new HomeView({ viewName: '' })).toThrow();
24
+ });
25
+
26
+ it('uses the explicit viewName option', () => {
27
+ const view = new NamedView({ viewName: 'custom-name' });
28
+ expect(view.viewName).toBe('custom-name');
29
+ });
30
+
31
+ it('generateTemplate() returns the constructor template', () => {
32
+ const view = new HomeView({ viewName: 'home', template: '<p>hi</p>' });
33
+ expect(view.render().innerHTML).toBe('<p>hi</p>');
34
+ });
35
+
36
+ it('defaults the template to an empty string', () => {
37
+ const view = new HomeView({ viewName: 'home' });
38
+ expect(view.render().innerHTML).toBe('');
39
+ });
40
+
41
+ it('defaults the tagName to "section"', () => {
42
+ const view = new HomeView({ viewName: 'home' });
43
+ expect(view.getElement().tagName).toBe('SECTION');
44
+ });
45
+
46
+ it('setRouteParams/getRouteParams/getRouteParam/hasRouteParam manage route params', () => {
47
+ const view = new HomeView({ viewName: 'home' });
48
+ expect(view.hasRouteParam('id')).toBe(false);
49
+ expect(view.getRouteParam('id')).toBeUndefined();
50
+
51
+ view.setRouteParams({ id: '42' });
52
+
53
+ expect(view.hasRouteParam('id')).toBe(true);
54
+ expect(view.getRouteParam('id')).toBe('42');
55
+ expect(view.getRouteParams()).toEqual({ id: '42' });
56
+ });
57
+
58
+ it('getRouteParams() returns a copy, not a live reference', () => {
59
+ const view = new HomeView({ viewName: 'home' });
60
+ view.setRouteParams({ id: '1' });
61
+ const params = view.getRouteParams();
62
+ params.id = 'mutated';
63
+ expect(view.getRouteParam('id')).toBe('1');
64
+ });
65
+
66
+ it('onEnter and onExit are present as no-op hooks', () => {
67
+ const view = new HomeView({ viewName: 'home' });
68
+ expect(() => view.onEnter()).not.toThrow();
69
+ expect(() => view.onExit()).not.toThrow();
70
+ });
71
+ });
@@ -1,77 +1,52 @@
1
1
  import { BaseComponent, BaseComponentOptions } from '../../index.js';
2
2
 
3
3
  export interface ViewOptions extends BaseComponentOptions {
4
- viewName?: string;
4
+ viewName: string;
5
5
  }
6
6
 
7
7
  export abstract class View<T extends HTMLElement = HTMLElement> extends BaseComponent<T> {
8
- public viewName: string;
8
+ private _viewName: string;
9
9
  protected routeParams: Record<string, string> = {};
10
+ private _template: string;
10
11
 
11
12
  constructor({
12
- template,
13
- tagName = 'section',
14
- mountTarget = '#app',
15
- viewName
16
- }: ViewOptions) {
17
- super({template, mountTarget, tagName});
18
- this.viewName = viewName || this.getKebabCaseViewName();
13
+ template,
14
+ tagName = 'section',
15
+ mountTarget = '#app',
16
+ viewName,
17
+ trustLevel,
18
+ }: ViewOptions) {
19
+ super({ mountTarget, tagName, trustLevel });
20
+ if (!viewName) throw new Error('View requires a viewName.');
21
+ this._template = template ?? '';
22
+ this._viewName = viewName;
23
+ }
24
+
25
+ public get viewName(): string {
26
+ return this._viewName;
27
+ }
28
+
29
+ protected override generateTemplate(): string {
30
+ return this._template;
19
31
  }
20
32
 
21
- /**
22
- * Set route parameters for this view instance
23
- * Called by the router when the view is instantiated
24
- */
25
33
  public setRouteParams(params: Record<string, string>): void {
26
34
  this.routeParams = { ...params };
27
35
  }
28
36
 
29
- /**
30
- * Get all route parameters
31
- */
32
37
  public getRouteParams(): Record<string, string> {
33
38
  return { ...this.routeParams };
34
39
  }
35
40
 
36
- /**
37
- * Get a specific route parameter
38
- */
39
41
  public getRouteParam(key: string): string | undefined {
40
42
  return this.routeParams[key];
41
43
  }
42
44
 
43
- /**
44
- * Check if a route parameter exists and has a value
45
- */
46
45
  public hasRouteParam(key: string): boolean {
47
46
  return key in this.routeParams && this.routeParams[key] !== undefined;
48
47
  }
49
48
 
50
- public shouldUpdateMetaContent(): boolean {
51
- return true;
52
- }
53
-
54
- public onEnter(): void {
55
- console.log(`${this.viewName} mounted with params:`, this.routeParams);
56
- }
57
-
58
- public onExit(): void {
59
- console.log(`${this.viewName} unmounted`);
60
- }
61
-
62
- public override render(): HTMLElement {
63
- this.onEnter();
64
- return super.render();
65
- }
66
-
67
- public override destroy(): void {
68
- super.destroy();
69
- this.onExit();
70
- }
71
-
72
- private getKebabCaseViewName(): string {
73
- const className = this.constructor.name;
74
- const baseName = className.replace(/View$/, '');
75
- return baseName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
76
- }
49
+ // Navigation hooks — called by router only, never by render lifecycle
50
+ public onEnter(): void {}
51
+ public onExit(): void {}
77
52
  }
@@ -0,0 +1,10 @@
1
+ import langs from '../../config/languages.json' with { type: 'json' };
2
+ import seo from '../../config/seo.json' with { type: 'json' };
3
+ import nutinConfig from '../../nutin.config.js';
4
+
5
+ export const CONFIG = {
6
+ langs,
7
+ seo,
8
+ i18n: nutinConfig.i18n,
9
+ generateSEOFiles: nutinConfig.generateSEOFiles,
10
+ } as const;
@@ -3,6 +3,3 @@ export * from './base-classes/index.js';
3
3
 
4
4
  // Core functionalities
5
5
  export * from './services/index.js';
6
-
7
- // Helper functions
8
- export * from './utils/index.js';
@@ -0,0 +1,27 @@
1
+ declare interface NavigationEventMap {
2
+ 'navigate': { path: string };
3
+ 'reload': {};
4
+ }
5
+
6
+ declare interface LifecycleEventMap {
7
+ 'before-render': {};
8
+ 'after-render': {};
9
+ 'before-destroy': {};
10
+ 'after-destroy': {};
11
+ 'view-mount': { viewName: string };
12
+ 'view-unmount': { viewName: string };
13
+ }
14
+
15
+ declare interface I18nEventMap {
16
+ 'language-changed': {
17
+ lang: string;
18
+ };
19
+ }
20
+
21
+ declare interface FrameworkEventMap extends NavigationEventMap, LifecycleEventMap, I18nEventMap {}
22
+
23
+ declare interface AppEventMap {}
24
+
25
+ declare interface EventMap extends FrameworkEventMap, AppEventMap {}
26
+
27
+ declare type EventKey = keyof EventMap;
@@ -0,0 +1,4 @@
1
+ declare module '*/nutin.config.js' {
2
+ const nutinConfig: { i18n: boolean; [key: string]: unknown };
3
+ export default nutinConfig;
4
+ }
@@ -127,11 +127,75 @@ describe('EventBus', () => {
127
127
  });
128
128
  it('should reset all service instances', () => {
129
129
  const eventBus1 = EventBus.getInstance();
130
-
130
+
131
131
  EventBus.testingResetAll();
132
-
132
+
133
133
  const eventBus2 = EventBus.getInstance();
134
-
134
+
135
135
  expect(eventBus1).not.toBe(eventBus2);
136
136
  });
137
+
138
+ it('should only invoke a once() handler on the first emit', () => {
139
+ let callCount = 0;
140
+ eventBus.once('user-login', () => { callCount++; });
141
+
142
+ eventBus.emit('user-login', { userId: '1' });
143
+ expect(callCount).toBe(1);
144
+
145
+ eventBus.emit('user-login', { userId: '2' });
146
+ expect(callCount).toBe(1);
147
+ });
148
+
149
+ it('should auto-unsubscribe a once() handler from _subscriptions after it fires', () => {
150
+ eventBus.once('user-login', () => {});
151
+ expect(eventBus._subscriptions.length).toBe(1);
152
+
153
+ eventBus.emit('user-login', {});
154
+ expect(eventBus._subscriptions.length).toBe(0);
155
+ });
156
+
157
+ it('should not remove a once() subscription before it has fired', () => {
158
+ eventBus.once('user-login', () => {});
159
+ eventBus.emit('notification', {});
160
+ expect(eventBus._subscriptions.length).toBe(1);
161
+ });
162
+
163
+ it('should let a once() handler and a regular subscriber on the same event coexist', () => {
164
+ let onceCalls = 0;
165
+ let regularCalls = 0;
166
+ eventBus.once('notification', () => { onceCalls++; });
167
+ eventBus.subscribe('notification', () => { regularCalls++; });
168
+
169
+ eventBus.emit('notification', {});
170
+ expect(onceCalls).toBe(1);
171
+ expect(regularCalls).toBe(1);
172
+
173
+ eventBus.emit('notification', {});
174
+ expect(onceCalls).toBe(1);
175
+ expect(regularCalls).toBe(2);
176
+ });
177
+
178
+ it('off(event) with no callback removes every handler for that event', () => {
179
+ let callsA = 0;
180
+ let callsB = 0;
181
+ eventBus.subscribe('notification', () => { callsA++; });
182
+ eventBus.subscribe('notification', () => { callsB++; });
183
+
184
+ eventBus.off('notification');
185
+ eventBus.emit('notification', {});
186
+
187
+ expect(callsA).toBe(0);
188
+ expect(callsB).toBe(0);
189
+ expect(eventBus.handlers['notification']).toBeUndefined();
190
+ });
191
+
192
+ it('off(event) with no callback does not affect other events', () => {
193
+ let called = false;
194
+ eventBus.subscribe('user-login', () => { called = true; });
195
+
196
+ eventBus.off('notification');
197
+ eventBus.emit('user-login', {});
198
+
199
+ expect(called).toBe(true);
200
+ });
137
201
  });
@@ -2,7 +2,7 @@ import { Service } from "../../index.js";
2
2
 
3
3
  export type IEventBus = InstanceType<typeof EventBus>;
4
4
 
5
- type Subscription<K extends EventKey = EventKey> = {
5
+ export type Subscription<K extends EventKey = EventKey> = {
6
6
  event: K;
7
7
  callback: (data: EventMap[K]) => void;
8
8
  once?: boolean;
@@ -58,7 +58,10 @@ export class EventBus extends Service<EventBus> {
58
58
  if (!handlers) return;
59
59
 
60
60
  if (callback) {
61
- this.handlers[event] = handlers.filter(h => h !== callback);
61
+ (this.handlers as Record<
62
+ EventKey,
63
+ Array<(data: EventMap[K]) => void>
64
+ >)[event] = handlers.filter(h => h !== callback);
62
65
  } else {
63
66
  delete this.handlers[event];
64
67
  }
@@ -76,8 +79,10 @@ export class EventBus extends Service<EventBus> {
76
79
  if (!this.handlers[event]) {
77
80
  this.handlers[event] = [];
78
81
  }
79
- this.handlers[event]!.push(callback);
80
- this._subscriptions.push({ event, callback, once });
82
+ (
83
+ this.handlers as Record<EventKey, Array<(data: EventMap[K]) => void>>
84
+ )[event].push(callback);
85
+ this._subscriptions.push({ event, callback, once } as Subscription);
81
86
  }
82
87
 
83
88
  private cleanupEventListeners = (): void => {
@@ -0,0 +1,3 @@
1
+ export * from './event-bus.js';
2
+ export * from './lifecycle.facade.js';
3
+ export * from './navigation.facade.js';