@nutin/cli 1.3.0 → 2.0.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 (284) hide show
  1. package/CHANGELOG.md +122 -8
  2. package/README.md +21 -37
  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 +81 -30
  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/create-app/new-app-prompt.mjs +51 -0
  22. package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +15 -13
  23. package/lib/src/create-app/success-message.mjs +14 -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 +136 -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 +169 -0
  36. package/templates/base/GETTING_STARTED.md.hbs +227 -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 +51 -0
  41. package/templates/base/src/app/components/index.ts.hbs +1 -2
  42. package/templates/base/src/app/globals.d.ts.hbs +3 -15
  43. package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
  44. package/templates/base/src/app/main.ts.hbs +7 -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 +1 -56
  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 +4 -37
  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 +81 -31
  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 +38 -23
  55. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +74 -0
  56. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +1 -1
  57. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +145 -0
  58. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +41 -1
  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 +13 -14
  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 +82 -0
  63. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +142 -0
  64. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +67 -22
  65. package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +183 -0
  66. package/templates/base/src/core/base-classes/component/component.test.js.hbs +85 -0
  67. package/templates/base/src/core/base-classes/component/component.ts.hbs +27 -40
  68. package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +58 -0
  69. package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +17 -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 +74 -0
  74. package/templates/base/src/core/base-classes/view/view.ts.hbs +17 -41
  75. package/templates/base/src/core/config.ts.hbs +7 -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 +149 -0
  87. package/templates/base/src/core/services/globals/globals.ts.hbs +95 -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 +43 -0
  91. package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
  92. package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +60 -9
  93. package/templates/base/src/core/services/index.ts.hbs +5 -6
  94. package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
  95. package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
  96. package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
  97. package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
  98. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +149 -6
  99. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +42 -30
  100. package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
  101. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
  102. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +13 -70
  103. package/templates/base/src/core/services/router/router.test.js.hbs +226 -2
  104. package/templates/base/src/core/services/router/router.ts.hbs +40 -9
  105. package/templates/base/src/index.html.hbs +2 -6
  106. package/templates/base/src/styles/_styles.scss.hbs +102 -43
  107. package/templates/base/src/styles/base/_variables.scss.hbs +1 -85
  108. package/templates/base/src/styles/main.scss.hbs +1 -18
  109. package/templates/base/src/styles/tailwind.css.hbs +2 -0
  110. package/templates/base/tools/builder/builder.config.js.hbs +40 -0
  111. package/templates/base/tools/builder/builder.js.hbs +24 -26
  112. package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
  113. package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
  114. package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
  115. package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
  116. package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
  117. package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
  118. package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
  119. package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
  120. package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
  121. package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
  122. package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
  123. package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
  124. package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
  125. package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
  126. package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
  127. package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
  128. package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
  129. package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
  130. package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
  131. package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
  132. package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
  133. package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
  134. package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
  135. package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
  136. package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
  137. package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
  138. package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
  139. package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
  140. package/templates/base/tools/dev/serve.js.hbs +11 -1
  141. package/templates/base/tools/dev/watcher.js.hbs +14 -6
  142. package/templates/base/tools/generator/generator.js.hbs +64 -47
  143. package/templates/base/tools/generator/handle-file.js.hbs +17 -44
  144. package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
  145. package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
  146. package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
  147. package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
  148. package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
  149. package/templates/base/tools/generator/templates/index.js.hbs +7 -12
  150. package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +4 -0
  151. package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +7 -0
  152. package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
  153. package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
  154. package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
  155. package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
  156. package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
  157. package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
  158. package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +3 -2
  159. package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
  160. package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
  161. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
  162. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
  163. package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
  164. package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
  165. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
  166. package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
  167. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
  168. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
  169. package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
  170. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
  171. package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
  172. package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
  173. package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
  174. package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
  175. package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
  176. package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
  177. package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
  178. package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
  179. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
  180. package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
  181. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
  182. package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
  183. package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
  184. package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
  185. package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
  186. package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
  187. package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
  188. package/templates/base/tools/utils/error-exit.js.hbs +8 -0
  189. package/templates/base/tools/utils/format.js.hbs +10 -10
  190. package/templates/base/tools/utils/format.test.js.hbs +45 -0
  191. package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
  192. package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
  193. package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
  194. package/templates/base/tools/utils/index.js.hbs +1 -3
  195. package/templates/base/tools/utils/languages.js.hbs +15 -0
  196. package/templates/base/tools/utils/languages.test.js.hbs +16 -0
  197. package/templates/base/tools/utils/print.js.hbs +19 -13
  198. package/templates/base/tools/utils/print.test.js.hbs +60 -0
  199. package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
  200. package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
  201. package/templates/base/tools/utils/run-command.js.hbs +4 -2
  202. package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
  203. package/templates/base/tools/utils/run-script.js.hbs +14 -3
  204. package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
  205. package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
  206. package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
  207. package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
  208. package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
  209. package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
  210. package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
  211. package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
  212. package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
  213. package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
  214. package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
  215. package/lib/src/context-builder.mjs +0 -35
  216. package/lib/src/file-generator.mjs +0 -109
  217. package/lib/src/json-generator.mjs +0 -31
  218. package/lib/src/package-manager.mjs +0 -139
  219. package/lib/src/print.mjs +0 -31
  220. package/lib/src/prompts.mjs +0 -123
  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 -35
  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 -85
  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 -108
  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/popover.ts.hbs +0 -145
  239. package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -72
  240. package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
  241. package/templates/base/src/styles/core/_libs.scss.hbs +0 -132
  242. package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
  243. package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
  244. package/templates/base/tools/builder/core/sass.js.hbs +0 -22
  245. package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
  246. package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
  247. package/templates/base/tools/utils/export-flags.js.hbs +0 -6
  248. package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
  249. package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
  250. package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
  251. package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
  252. package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
  253. package/templates/features/i18n/src/core/config.ts.hbs +0 -3
  254. package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
  255. package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
  256. package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
  257. package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
  258. package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
  259. package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
  260. package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -240
  261. package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
  262. package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
  263. package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
  264. package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
  265. package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
  266. package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
  267. package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
  268. package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
  269. package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
  270. package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
  271. package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
  272. package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
  273. package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
  274. package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
  275. /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
  276. /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
  277. /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
  278. /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
  279. /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
  280. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
  281. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
  282. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
  283. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
  284. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
@@ -1,7 +1,231 @@
1
- import { AppRouter } from '#root/dist/src/core/services/index.js'
1
+ import { AppRouter, Navigation, I18nService } from '#root/dist/src/core/services/index.js';
2
+ import { CONFIG } from '#root/dist/src/core/config.js';
3
+
4
+ function makeView(name) {
5
+ const calls = [];
6
+ const el = document.createElement('div');
7
+ return {
8
+ viewName: name,
9
+ calls,
10
+ getElement: () => el,
11
+ setRouteParams: (params) => calls.push(['setRouteParams', params]),
12
+ render: () => { calls.push(['render']); return el; },
13
+ onEnter: () => calls.push(['onEnter']),
14
+ onExit: () => calls.push(['onExit']),
15
+ destroy: () => calls.push(['destroy']),
16
+ };
17
+ }
2
18
 
3
19
  describe('Router', () => {
20
+ let router = null;
21
+
22
+ beforeEach(() => {
23
+ CONFIG.i18n = false;
24
+ window.history.pushState({}, '', '/');
25
+ });
26
+
27
+ afterEach(() => {
28
+ if (router) {
29
+ router.dispose();
30
+ router = null;
31
+ }
32
+ CONFIG.i18n = false;
33
+ window.history.pushState({}, '', '/');
34
+ });
35
+
4
36
  it('should be defined', () => {
5
37
  expect(AppRouter).toBeDefined();
6
38
  });
7
- })
39
+
40
+ it('AppRouter returns the same singleton instance on repeated getInstance() calls', async () => {
41
+ const home = makeView('home');
42
+ router = AppRouter({ '/': () => home });
43
+ await flushPromises();
44
+
45
+ const again = router.constructor.getInstance();
46
+ expect(again).toBe(router);
47
+ });
48
+
49
+ it('navigate() matches a route and renders its view (via the initial construction-time navigate)', async () => {
50
+ const home = makeView('home');
51
+ router = AppRouter({ '/': () => home });
52
+ await flushPromises();
53
+
54
+ expect(home.calls).toEqual([
55
+ ['setRouteParams', {}],
56
+ ['render'],
57
+ ['onEnter'],
58
+ ]);
59
+ });
60
+
61
+ it('navigate() passes matched route params to the rendered view', async () => {
62
+ const home = makeView('home');
63
+ const userView = makeView('user');
64
+ router = AppRouter({ '/': () => home, '/users/:id': () => userView });
65
+ await flushPromises();
66
+
67
+ await router.navigate('/users/42');
68
+
69
+ expect(userView.calls[0]).toEqual(['setRouteParams', { id: '42' }]);
70
+ expect(router.getCurrentParams()).toEqual({ id: '42' });
71
+ expect(router.getParam('id')).toBe('42');
72
+ });
73
+
74
+ it('navigate() renders the /404 route when no pattern matches', async () => {
75
+ const home = makeView('home');
76
+ const notFound = makeView('not-found');
77
+ router = AppRouter({ '/': () => home, '/404': () => notFound });
78
+ await flushPromises();
79
+
80
+ await router.navigate('/nope');
81
+
82
+ expect(notFound.calls.some(c => c[0] === 'render')).toBe(true);
83
+ });
84
+
85
+ it('navigate() logs an error and renders nothing when no route matches and there is no /404 route', async () => {
86
+ const home = makeView('home');
87
+ router = AppRouter({ '/': () => home });
88
+ await flushPromises();
89
+
90
+ const errSpy = spyOn(console, 'error');
91
+ errSpy.andCallFake(() => {});
92
+
93
+ await router.navigate('/nope');
94
+
95
+ expect(errSpy.callCount).toBe(1);
96
+ errSpy.restore();
97
+ });
98
+
99
+ it('navigate() blocks navigation when a guard returns false', async () => {
100
+ const home = makeView('home');
101
+ const secret = makeView('secret');
102
+ router = AppRouter({
103
+ '/': () => home,
104
+ '/secret': { view: () => secret, guards: [() => false] },
105
+ });
106
+ await flushPromises();
107
+
108
+ await router.navigate('/secret');
109
+
110
+ expect(secret.calls.length).toBe(0);
111
+ });
112
+
113
+ it('navigate() redirects when a guard returns a redirect path', async () => {
114
+ const home = makeView('home');
115
+ const login = makeView('login');
116
+ const secret = makeView('secret');
117
+ router = AppRouter({
118
+ '/': () => home,
119
+ '/login': () => login,
120
+ '/secret': { view: () => secret, guards: [() => '/login'] },
121
+ });
122
+ await flushPromises();
123
+
124
+ await router.navigate('/secret');
125
+
126
+ expect(login.calls.some(c => c[0] === 'render')).toBe(true);
127
+ expect(secret.calls.length).toBe(0);
128
+ });
129
+
130
+ it('navigate() renders the view when all guards pass', async () => {
131
+ const home = makeView('home');
132
+ const dashboard = makeView('dashboard');
133
+ router = AppRouter({
134
+ '/': () => home,
135
+ '/dashboard': { view: () => dashboard, guards: [() => true] },
136
+ });
137
+ await flushPromises();
138
+
139
+ await router.navigate('/dashboard');
140
+
141
+ expect(dashboard.calls.some(c => c[0] === 'render')).toBe(true);
142
+ });
143
+
144
+ it('reload() re-navigates to the current path without pushing a new history entry', async () => {
145
+ const home = makeView('home');
146
+ router = AppRouter({ '/': () => home });
147
+ await flushPromises();
148
+ home.calls.length = 0;
149
+
150
+ await router.reload();
151
+
152
+ expect(home.calls.some(c => c[0] === 'render')).toBe(true);
153
+ });
154
+
155
+ it('responds to a popstate event by re-navigating to the new current path', async () => {
156
+ const home = makeView('home');
157
+ const about = makeView('about');
158
+ router = AppRouter({ '/': () => home, '/about': () => about });
159
+ await flushPromises();
160
+
161
+ window.history.pushState({}, '', '/about');
162
+ window.dispatchEvent(new window.PopStateEvent('popstate'));
163
+ await flushPromises();
164
+
165
+ expect(about.calls.some(c => c[0] === 'render')).toBe(true);
166
+ });
167
+
168
+ it('switches the language on popstate when the URL locale differs from the current one', async () => {
169
+ CONFIG.i18n = true;
170
+ I18nService['_LANGUAGES'] = ['en', 'fr'];
171
+ I18nService['_currentLanguage'] = 'en';
172
+
173
+ const home = makeView('home');
174
+ router = AppRouter({ '/': () => home });
175
+ await flushPromises();
176
+
177
+ const setLangSpy = spyOn(I18nService, 'setCurrentLanguage');
178
+ setLangSpy.andCallFake(async (lang) => { I18nService['_currentLanguage'] = lang; });
179
+
180
+ window.history.pushState({}, '', '/fr');
181
+ window.dispatchEvent(new window.PopStateEvent('popstate'));
182
+ await flushPromises();
183
+
184
+ expect(setLangSpy).toHaveBeenCalledWith('fr');
185
+
186
+ setLangSpy.restore();
187
+ I18nService['_currentLanguage'] = 'en';
188
+ });
189
+
190
+ it('wires Navigation.navigateTo() through to router.navigate()', async () => {
191
+ const home = makeView('home');
192
+ const about = makeView('about');
193
+ router = AppRouter({ '/': () => home, '/about': () => about });
194
+ await flushPromises();
195
+
196
+ Navigation.navigateTo('/about');
197
+ await flushPromises();
198
+
199
+ expect(about.calls.some(c => c[0] === 'render')).toBe(true);
200
+ });
201
+
202
+ it('wires Navigation.reload() through to router.reload()', async () => {
203
+ const home = makeView('home');
204
+ router = AppRouter({ '/': () => home });
205
+ await flushPromises();
206
+ home.calls.length = 0;
207
+
208
+ Navigation.reload();
209
+ await flushPromises();
210
+
211
+ expect(home.calls.some(c => c[0] === 'render')).toBe(true);
212
+ });
213
+
214
+ it('removeEventListeners() stops the router from responding to further navigate/popstate events', async () => {
215
+ const home = makeView('home');
216
+ const about = makeView('about');
217
+ router = AppRouter({ '/': () => home, '/about': () => about });
218
+ await flushPromises();
219
+
220
+ router.removeEventListeners();
221
+
222
+ Navigation.navigateTo('/about');
223
+ await flushPromises();
224
+ expect(about.calls.length).toBe(0);
225
+
226
+ window.history.pushState({}, '', '/about');
227
+ window.dispatchEvent(new window.PopStateEvent('popstate'));
228
+ await flushPromises();
229
+ expect(about.calls.length).toBe(0);
230
+ });
231
+ });
@@ -1,11 +1,11 @@
1
- import { View, AppEventBus } from '../../index.js';
1
+ import { View, Navigation, Service, I18nService, AppEventBus } from '../../index.js';
2
+ import { Language } from '../i18n/languages.js';
2
3
  import { Routes, RouteConfig, RouteGuardsManager, GuardResult } from './helpers/route-guard-manager.helper.js';
3
4
  import { ViewRenderManager } from './helpers/view-render-manager.helper.js';
4
5
  import { NavigationManager } from './helpers/navigation-manager.helper.js';
5
6
 
6
7
  // centralized export
7
8
  export { Routes, RouteGuard } from './helpers/route-guard-manager.helper.js';
8
- export { ViewRenderManager } from './helpers/view-render-manager.helper.js';
9
9
 
10
10
  /**
11
11
  * IRouter is a type alias for the instance of Router, not a true interface
@@ -17,13 +17,27 @@ export interface RouteMatch {
17
17
  params: Record<string, string>;
18
18
  }
19
19
 
20
- class Router {
20
+ class Router extends Service<Router> {
21
21
  private _currentView: View | null = null;
22
22
  private _currentParams: Record<string, string> = {};
23
+ private onPopState = () => this.handlePopState();
24
+ private onNavigate = (data: { path: string }) => this.navigate(data.path);
25
+ private onReload = () => this.reload();
26
+ private _busSubscriptions: Array<() => void> = [];
23
27
 
24
28
  constructor(private routes: Routes) {
29
+ super();
25
30
  this.initializeEventListeners();
26
31
  this.navigate(NavigationManager.getCurrentPath());
32
+ this.registerCleanup(this.removeEventListeners);
33
+ }
34
+
35
+ public removeEventListeners(): void {
36
+ this._busSubscriptions.forEach((callback) => {
37
+ callback();
38
+ });
39
+ this._busSubscriptions = [];
40
+ window.removeEventListener('popstate', this.onPopState);
27
41
  }
28
42
 
29
43
  public async reload(): Promise<void> {
@@ -59,8 +73,8 @@ class Router {
59
73
  routeMatch.params
60
74
  );
61
75
 
62
- NavigationManager.updateMetaContent(this._currentView);
63
76
  NavigationManager.updateHistory(normalizedPath, currentPath, pushState);
77
+ window.scrollTo({ top: 0 });
64
78
  }
65
79
 
66
80
  public getCurrentParams(): Record<string, string> {
@@ -72,12 +86,29 @@ class Router {
72
86
  }
73
87
 
74
88
  private initializeEventListeners(): void {
75
- window.addEventListener('popstate', () => this.handlePopState());
76
- AppEventBus.subscribe('navigate', (path: string) => this.navigate(path));
77
- AppEventBus.subscribe('reload', () => this.reload());
89
+ this.initializeNavigationEvents();
90
+ this.initializeI18nEvents();
91
+ }
92
+
93
+ private initializeNavigationEvents(): void {
94
+ window.addEventListener('popstate', this.onPopState);
95
+ const unsubOnNav = Navigation.onNavigate(this.onNavigate);
96
+ const unsubOnReload = Navigation.onReload(this.onReload);
97
+ this._busSubscriptions.push(unsubOnNav, unsubOnReload);
78
98
  }
79
99
 
80
- private handlePopState(): void {
100
+ private initializeI18nEvents(): void {
101
+ const onLangChanged = () => NavigationManager.updateLocaleInUrl();
102
+ AppEventBus.subscribe('language-changed', onLangChanged);
103
+ const unsubOnLang = () => AppEventBus.off('language-changed', onLangChanged);
104
+ this._busSubscriptions.push(unsubOnLang);
105
+ }
106
+
107
+ private async handlePopState(): Promise<void> {
108
+ const newLocale = NavigationManager.getCurrentLocale();
109
+ if (newLocale && newLocale !== I18nService.currentLanguage) {
110
+ await I18nService.setCurrentLanguage(newLocale as Language);
111
+ }
81
112
  this.navigate(NavigationManager.getCurrentPath(), false);
82
113
  }
83
114
 
@@ -142,4 +173,4 @@ class Router {
142
173
  }
143
174
  }
144
175
 
145
- export const AppRouter = (routes: Routes) => new Router(routes);
176
+ export const AppRouter = (routes: Routes) => Router.getInstance(routes);
@@ -1,14 +1,10 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <meta name="description" content="{{projectName}}">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <link rel="stylesheet" href="/main.css">
8
5
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
9
- <title>{{projectName}}</title>
10
6
  </head>
11
7
  <body>
12
8
  <main id="app"></main>
13
9
  </body>
14
- </html>
10
+ </html>
@@ -1,54 +1,113 @@
1
1
  @use "variables" as *;
2
- {{#if stylinNutin}}
3
- @use "sass:map";
4
- @use "mixins";
5
- {{/if}}
6
2
 
7
- // css reset
8
- html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video, button, input, textarea {
3
+ /* ===========
4
+ CSS Reset
5
+ =========== */
6
+
7
+ /* Box sizing */
8
+
9
+ *,
10
+ *::before,
11
+ *::after {
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ /* Remove default spacing */
16
+
17
+ * {
9
18
  margin: 0;
10
19
  padding: 0;
11
- border: 0;
20
+ }
21
+
22
+ /* Document */
23
+
24
+ html {
12
25
  font-size: 100%;
26
+ -webkit-text-size-adjust: 100%;
27
+ }
28
+
29
+ body {
30
+ min-height: 100vh;
13
31
  font: inherit;
14
- vertical-align: baseline;
32
+ line-height: 1.5;
15
33
  }
16
34
 
17
- *,
18
- *::before,
19
- *::after {
20
- box-sizing: border-box;
35
+ /* Media */
36
+
37
+ img,
38
+ picture,
39
+ svg,
40
+ canvas,
41
+ video {
42
+ display: block;
43
+ max-width: 100%;
44
+ }
45
+
46
+ /* Forms */
47
+
48
+ button,
49
+ input,
50
+ select,
51
+ textarea {
52
+ font: inherit;
53
+ color: inherit;
54
+ }
55
+
56
+ button {
57
+ background: none;
58
+ border: none;
59
+ cursor: pointer;
60
+ }
61
+
62
+ textarea {
63
+ resize: vertical;
64
+ }
65
+
66
+ /* Tables */
67
+
68
+ table {
69
+ border-collapse: collapse;
70
+ border-spacing: 0;
71
+ }
72
+
73
+ /* Lists */
74
+
75
+ ul,
76
+ ol {
77
+ list-style: none;
78
+ }
79
+
80
+ /* Links */
81
+
82
+ a {
83
+ color: inherit;
84
+ text-decoration: none;
85
+ }
86
+
87
+ /* Quotes */
88
+
89
+ blockquote,
90
+ q {
91
+ quotes: none;
92
+ }
93
+
94
+ blockquote::before,
95
+ blockquote::after,
96
+ q::before,
97
+ q::after {
98
+ content: "";
99
+ }
100
+
101
+ /* Miscellaneous */
102
+
103
+ hr {
104
+ border: none;
105
+ }
106
+
107
+ fieldset {
108
+ border: none;
21
109
  }
22
110
 
23
- // Global styles
24
- :root {
25
- body {
26
- background-color: $background-color;
27
- color: $text-color;
28
- min-height: 100vh;
29
- cursor: default;
30
- {{#if stylinNutin}}
31
- line-height: 1.6;
32
- font-family: $primary-font;
33
- font-size: map.get($font-sizes, medium);
34
- {{else}}
35
- font-family: Arial, Helvetica, sans-serif;
36
- font-size: 1rem;
37
- {{/if}}
38
- }
39
-
40
- {{#if stylinNutin}}
41
- h1 {
42
- font-size: map.get($font-sizes, largest);
43
- font-family: $secondary-font;
44
- }
45
-
46
- a, button {
47
- cursor: pointer;
48
- }
49
-
50
- a {
51
- text-decoration: none;
52
- }
53
- {{/if}}
111
+ :where([hidden]) {
112
+ display: none !important;
54
113
  }
@@ -1,85 +1 @@
1
- /*
2
- Variables used in libs (snackbar, popover){{#if stylinNutin}} and utility classes{{/if}}
3
- Adapt to customize
4
- */
5
-
6
- // colours (RGB)
7
- $background-color: rgb(255 255 255); // WHITE
8
- $text-color: rgb(0 0 0); // BLACK
9
- $primary-color: rgb(0 0 0); // BLACK
10
- $secondary-color: rgb(0 0 0); // BLACK
11
-
12
- // snackbar colours
13
- $snack-success: rgb(76 175 80);
14
- $snack-error: rgb(244 67 54);
15
- $snack-info: rgb(33 150 243);
16
- $snack-default: rgb(153 153 153);
17
-
18
- {{#if stylinNutin}}
19
- /*
20
- Variables used in utility classes
21
- Adapt to customize
22
- */
23
-
24
- // fonts
25
- $primary-font: Arial, Helvetica, sans-serif;
26
- $secondary-font: Arial, Helvetica, sans-serif;
27
-
28
- // font sizes
29
- $font-sizes: (
30
- smallest: 0.75rem, // 12px
31
- small: 0.875rem, // 14px
32
- medium: 1rem, // 16px (base)
33
- large: 1.5rem, // 24px
34
- largest: 2rem, // 32px
35
- );
36
-
37
- // spacing (margin / padding / gap)
38
- $spacings: (
39
- smallest: 0.25rem, // 4px
40
- small: 0.5rem, // 8px
41
- medium: 1rem, // 16px
42
- large: 1.5rem, // 24px
43
- largest: 2rem, // 32px
44
- );
45
-
46
- // border radius
47
- $border-radius: (
48
- small: 2px,
49
- medium: 4px,
50
- large: 8px,
51
- pill: 9999px,
52
- );
53
-
54
- // box sizes
55
- $box-sizes: (
56
- small: 32px,
57
- medium: 64px,
58
- large: 128px,
59
- largest: 256px,
60
- );
61
-
62
- // shadows
63
- $shadows: (
64
- small: 0 1px 2px rgba(0, 0, 0, 0.05),
65
- medium: 0 4px 6px rgba(0, 0, 0, 0.1),
66
- large: 0 10px 15px rgba(0, 0, 0, 0.15),
67
- );
68
-
69
- // breakpoints
70
- $breakpoints: (
71
- small: 576px,
72
- medium: 768px,
73
- large: 992px,
74
- largest: 1201px,
75
- );
76
-
77
- // animations
78
- // transition Duration
79
- $transitions: (
80
- fast: 0.2s,
81
- normal: 0.3s,
82
- slow: 0.5s,
83
- );
84
-
85
- {{/if}}
1
+ // variables
@@ -1,19 +1,2 @@
1
- // Libraries styles (snackbar, popover){{#if transition}}, view transition styles{{/if}}.
2
- @use "libs";
3
-
4
- {{#if stylinNutin}}
5
- // built-in classes
6
- @use "utilities";
7
-
8
- {{/if}}
9
- // global styles
1
+ // CSS reset & global styles
10
2
  @use "styles";
11
-
12
- {{#if stylinNutin}}
13
- // custom classes
14
- @use "custom-classes";
15
- // components styles
16
- @use "components";
17
- // views styles
18
- @use "views";
19
- {{/if}}
@@ -0,0 +1,2 @@
1
+ @import "tailwindcss/theme.css" layer(theme);
2
+ @import "tailwindcss/utilities.css" layer(utilities);
@@ -0,0 +1,40 @@
1
+ import process from 'process';
2
+ import nutinConfig from '../../nutin.config.js';
3
+ import { errorExit } from '../utils/index.js';
4
+
5
+ function validateConfig(config) {
6
+ if (!config || typeof config !== 'object') {
7
+ errorExit(new Error('nutin.config.js must export a default object.'), 'builder.config');
8
+ }
9
+ if (!config.builder || typeof config.builder !== 'object') {
10
+ errorExit(new Error('nutin.config.js is missing the required "builder" object.'), 'builder.config');
11
+ }
12
+ if (!Array.isArray(config.builder.sass?.paths)) {
13
+ errorExit(new Error('nutin.config.js is missing "builder.sass.paths" (expected an array).'), 'builder.config');
14
+ }
15
+ if (!config.builder.esbuild || typeof config.builder.esbuild !== 'object') {
16
+ errorExit(new Error('nutin.config.js is missing the required "builder.esbuild" object.'), 'builder.config');
17
+ }
18
+ }
19
+
20
+ validateConfig(nutinConfig);
21
+
22
+ const wellKnownNonSEORoutes = [
23
+ '/400',
24
+ '/401',
25
+ '/403',
26
+ '/404',
27
+ '/500',
28
+ '/502',
29
+ '/503',
30
+ '/504',
31
+ ]
32
+
33
+ export const builderConfig = {
34
+ ...nutinConfig.builder,
35
+ isProd: process.env.NODE_ENV === 'production',
36
+ i18n: nutinConfig.i18n,
37
+ tailwind: nutinConfig.tailwind,
38
+ generateSEO: nutinConfig.generateSEOFiles,
39
+ WELL_KNOWN_NON_SEO_ROUTES: wellKnownNonSEORoutes,
40
+ };