@nutin/cli 1.3.1 → 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 (287) hide show
  1. package/CHANGELOG.md +89 -9
  2. package/README.md +20 -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 +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 -19
  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 +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 +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 +7 -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 +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 +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 +24 -33
  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/data-binding.helper.test.js.hbs +75 -0
  70. package/templates/base/src/core/base-classes/service/service.test.js.hbs +150 -0
  71. package/templates/base/src/core/base-classes/service/service.ts.hbs +26 -20
  72. package/templates/base/src/core/base-classes/view/view.test.js.hbs +74 -0
  73. package/templates/base/src/core/base-classes/view/view.ts.hbs +17 -41
  74. package/templates/base/src/core/config.ts.hbs +7 -0
  75. package/templates/base/src/core/index.ts.hbs +0 -3
  76. package/templates/base/src/core/internals.d.ts.hbs +27 -0
  77. package/templates/base/src/core/nutin-config.d.ts.hbs +4 -0
  78. package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +67 -3
  79. package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +9 -4
  80. package/templates/base/src/core/services/event-bus/index.ts.hbs +3 -0
  81. package/templates/base/src/core/services/event-bus/lifecycle.facade.test.js.hbs +123 -0
  82. package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +61 -0
  83. package/templates/base/src/core/services/event-bus/navigation.facade.test.js.hbs +46 -0
  84. package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +25 -0
  85. package/templates/base/src/core/services/globals/globals.test.js.hbs +149 -0
  86. package/templates/base/src/core/services/globals/globals.ts.hbs +95 -0
  87. package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +25 -0
  88. package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +20 -0
  89. package/templates/base/src/core/services/http-client/http-client.test.js.hbs +43 -0
  90. package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
  91. package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +22 -4
  92. package/templates/base/src/core/services/index.ts.hbs +5 -6
  93. package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
  94. package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
  95. package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
  96. package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
  97. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +149 -6
  98. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +40 -29
  99. package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
  100. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
  101. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +14 -38
  102. package/templates/base/src/core/services/router/router.test.js.hbs +226 -2
  103. package/templates/base/src/core/services/router/router.ts.hbs +40 -8
  104. package/templates/base/src/index.html.hbs +2 -6
  105. package/templates/base/src/styles/_styles.scss.hbs +102 -43
  106. package/templates/base/src/styles/base/_variables.scss.hbs +1 -86
  107. package/templates/base/src/styles/main.scss.hbs +1 -18
  108. package/templates/base/src/styles/tailwind.css.hbs +2 -0
  109. package/templates/base/tools/builder/builder.config.js.hbs +40 -0
  110. package/templates/base/tools/builder/builder.js.hbs +24 -26
  111. package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
  112. package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
  113. package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
  114. package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
  115. package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
  116. package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
  117. package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
  118. package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
  119. package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
  120. package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
  121. package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
  122. package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
  123. package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
  124. package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
  125. package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
  126. package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
  127. package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
  128. package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
  129. package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
  130. package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
  131. package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
  132. package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
  133. package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
  134. package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
  135. package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
  136. package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
  137. package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
  138. package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
  139. package/templates/base/tools/dev/serve.js.hbs +11 -1
  140. package/templates/base/tools/dev/watcher.js.hbs +14 -6
  141. package/templates/base/tools/generator/generator.js.hbs +64 -47
  142. package/templates/base/tools/generator/handle-file.js.hbs +17 -44
  143. package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
  144. package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
  145. package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
  146. package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
  147. package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
  148. package/templates/base/tools/generator/templates/index.js.hbs +7 -12
  149. package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +4 -0
  150. package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +7 -0
  151. package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
  152. package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
  153. package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
  154. package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
  155. package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
  156. package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
  157. package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +3 -2
  158. package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
  159. package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
  160. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
  161. package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
  162. package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
  163. package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
  164. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
  165. package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
  166. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
  167. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
  168. package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
  169. package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
  170. package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
  171. package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
  172. package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
  173. package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
  174. package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
  175. package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
  176. package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
  177. package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
  178. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
  179. package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
  180. package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
  181. package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
  182. package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
  183. package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
  184. package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
  185. package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
  186. package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
  187. package/templates/base/tools/utils/error-exit.js.hbs +8 -0
  188. package/templates/base/tools/utils/format.js.hbs +10 -10
  189. package/templates/base/tools/utils/format.test.js.hbs +45 -0
  190. package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
  191. package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
  192. package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
  193. package/templates/base/tools/utils/index.js.hbs +1 -3
  194. package/templates/base/tools/utils/languages.js.hbs +15 -0
  195. package/templates/base/tools/utils/languages.test.js.hbs +16 -0
  196. package/templates/base/tools/utils/print.js.hbs +19 -13
  197. package/templates/base/tools/utils/print.test.js.hbs +60 -0
  198. package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
  199. package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
  200. package/templates/base/tools/utils/run-command.js.hbs +4 -2
  201. package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
  202. package/templates/base/tools/utils/run-script.js.hbs +14 -3
  203. package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
  204. package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
  205. package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
  206. package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
  207. package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
  208. package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
  209. package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
  210. package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
  211. package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
  212. package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
  213. package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
  214. package/lib/src/context-builder.mjs +0 -40
  215. package/lib/src/file-generator.mjs +0 -109
  216. package/lib/src/json-generator.mjs +0 -31
  217. package/lib/src/package-manager.mjs +0 -139
  218. package/lib/src/print.mjs +0 -32
  219. package/lib/src/utils.mjs +0 -18
  220. package/lib/src/version.mjs +0 -1
  221. package/templates/base/builder.config.js.hbs +0 -36
  222. package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +0 -32
  223. package/templates/base/src/app/components/common/button/button.component.ts.hbs +0 -40
  224. package/templates/base/src/app/dom.test.js.hbs +0 -13
  225. package/templates/base/src/app/services/example/example.service.ts.hbs +0 -18
  226. package/templates/base/src/app/views/home/home.view.test.js.hbs +0 -26
  227. package/templates/base/src/core/services/store/store.test.js.hbs +0 -8
  228. package/templates/base/src/core/services/store/store.ts.hbs +0 -39
  229. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.test.js.hbs +0 -0
  230. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +0 -113
  231. package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +0 -249
  232. package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +0 -131
  233. package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +0 -50
  234. package/templates/base/src/core/utils/index.ts.hbs +0 -2
  235. package/templates/base/src/libs/index.ts.hbs +0 -4
  236. package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +0 -197
  237. package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +0 -75
  238. package/templates/base/src/libs/popover/popover.ts.hbs +0 -138
  239. package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -75
  240. package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
  241. package/templates/base/src/styles/core/_anchors.scss.hbs +0 -10
  242. package/templates/base/src/styles/core/_index.scss.hbs +0 -6
  243. package/templates/base/src/styles/core/_popover.scss.hbs +0 -96
  244. package/templates/base/src/styles/core/_snackbar.scss.hbs +0 -50
  245. package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
  246. package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
  247. package/templates/base/tools/builder/core/sass.js.hbs +0 -22
  248. package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
  249. package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
  250. package/templates/base/tools/utils/export-flags.js.hbs +0 -6
  251. package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
  252. package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
  253. package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
  254. package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
  255. package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
  256. package/templates/features/i18n/src/core/config.ts.hbs +0 -3
  257. package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
  258. package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
  259. package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
  260. package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
  261. package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
  262. package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
  263. package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -254
  264. package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
  265. package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
  266. package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
  267. package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
  268. package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
  269. package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
  270. package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
  271. package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
  272. package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
  273. package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
  274. package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
  275. package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
  276. package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
  277. package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
  278. /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
  279. /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
  280. /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
  281. /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
  282. /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
  283. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
  284. /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
  285. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
  286. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
  287. /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
@@ -26,24 +26,11 @@ describe('ViewRenderManager', () => {
26
26
  return el;
27
27
  },
28
28
  destroy: () => {},
29
+ onEnter: () => {},
30
+ onExit: () => {},
29
31
  };
30
32
  };
31
33
 
32
- {{#if animation}}
33
- it('should add exit class and destroy current view on transition out', async () => {
34
- const view = mockView();
35
- const el = view.render();
36
- container.appendChild(el);
37
-
38
- // Spy on destroy
39
- const spy = spyOn(view, 'destroy');
40
-
41
- await ViewRenderManager.transitionOutCurrentView(view);
42
-
43
- expect(spy).toHaveBeenCalled();
44
- });
45
-
46
- {{/if}}
47
34
  it('should do nothing if no current view is passed to transitionOutCurrentView', async () => {
48
35
  const result = await ViewRenderManager.transitionOutCurrentView(null);
49
36
  expect(result).toBe(null);
@@ -51,7 +38,8 @@ describe('ViewRenderManager', () => {
51
38
 
52
39
  it('should emit view-unmount event on transition out', async () => {
53
40
  const emitted = [];
54
- AppEventBus.emit = (event, data) => emitted.push({ event, data });
41
+ const emitSpy = spyOn(AppEventBus, 'emit');
42
+ emitSpy.andCallFake((event, data) => emitted.push({ event, data }));
55
43
 
56
44
  const view = mockView('SettingsView');
57
45
  const el = view.render();
@@ -59,6 +47,84 @@ describe('ViewRenderManager', () => {
59
47
 
60
48
  await ViewRenderManager.transitionOutCurrentView(view);
61
49
 
62
- expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-unmount', data: 'SettingsView' }));
50
+ expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-unmount', data: { viewName: 'SettingsView' } }));
51
+ emitSpy.restore();
52
+ });
53
+
54
+ it('should call destroy() and onExit() on the current view when transitioning out', async () => {
55
+ let destroyed = false;
56
+ let exited = false;
57
+ const view = mockView('SettingsView');
58
+ view.destroy = () => { destroyed = true; };
59
+ view.onExit = () => { exited = true; };
60
+
61
+ await ViewRenderManager.transitionOutCurrentView(view);
62
+
63
+ expect(destroyed).toBe(true);
64
+ expect(exited).toBe(true);
65
+ });
66
+
67
+ it('should set route params, render, call onEnter and emit view-mount when rendering a new view', () => {
68
+ const emitted = [];
69
+ const emitSpy = spyOn(AppEventBus, 'emit');
70
+ emitSpy.andCallFake((event, data) => emitted.push({ event, data }));
71
+
72
+ const calls = [];
73
+ const viewElement = document.createElement('div');
74
+ const view = {
75
+ viewName: 'HomeView',
76
+ getElement: () => viewElement,
77
+ setRouteParams: (params) => calls.push(['setRouteParams', params]),
78
+ render: () => { calls.push(['render']); return viewElement; },
79
+ onEnter: () => calls.push(['onEnter']),
80
+ };
81
+
82
+ ViewRenderManager.renderNewView(() => view, { id: '42' });
83
+
84
+ expect(calls).toEqual([
85
+ ['setRouteParams', { id: '42' }],
86
+ ['render'],
87
+ ['onEnter'],
88
+ ]);
89
+ expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-mount', data: { viewName: 'HomeView' } }));
90
+ emitSpy.restore();
91
+ });
92
+
93
+ it('should not attempt to clear stale content when the new view has no parent element yet', () => {
94
+ const viewElement = document.createElement('div');
95
+ // Note: viewElement is intentionally never attached to the container.
96
+ const view = {
97
+ viewName: 'HomeView',
98
+ getElement: () => viewElement,
99
+ setRouteParams: () => {},
100
+ render: () => viewElement,
101
+ onEnter: () => {},
102
+ };
103
+
104
+ expect(() => ViewRenderManager.renderNewView(() => view)).not.toThrow();
105
+ });
106
+
107
+ it('should clear stale mount content before rendering the new view', () => {
108
+ const stale = document.createElement('div');
109
+ stale.className = 'stale-seo-content';
110
+ container.appendChild(stale);
111
+
112
+ const viewElement = document.createElement('div');
113
+ viewElement.className = 'mock-view';
114
+ container.appendChild(viewElement);
115
+
116
+ const view = {
117
+ viewName: 'HomeView',
118
+ getElement: () => viewElement,
119
+ setRouteParams: () => {},
120
+ render: () => viewElement,
121
+ onEnter: () => {},
122
+ };
123
+
124
+ ViewRenderManager.renderNewView(() => view);
125
+
126
+ expect(container.children.length).toBe(1);
127
+ expect(container.contains(stale)).toBe(false);
128
+ expect(container.contains(viewElement)).toBe(true);
63
129
  });
64
130
  });
@@ -1,35 +1,14 @@
1
- import { View, AppEventBus } from '../../../index.js';
1
+ import { View, Lifecycle } from '../../../index.js';
2
2
 
3
3
  /**
4
- * handles all view rendering{{#if transition}} and transition animation logic{{/if}}.
4
+ * handles all view rendering.
5
5
  */
6
6
  export class ViewRenderManager {
7
- {{#if transition}}
8
- public static renderAnimatedView(view: View): void {
9
- const container = document.getElementById('app');
10
- const el = view.render();
11
- el.classList.add('app-view-transition');
12
- container?.appendChild(el);
13
-
14
- requestAnimationFrame(() => {
15
- el.classList.add('enter-active');
16
- });
17
- }
18
-
19
- {{/if}}
20
7
  public static async transitionOutCurrentView(currentView: View | null): Promise<null> {
21
8
  if (!currentView) return null;
22
- {{#if transition}}
23
- const container = document.getElementById('app');
24
- const el = container?.firstElementChild as HTMLElement;
25
-
26
- if (el) {
27
- el.classList.add('exit-active');
28
- await this.delay(300); // match transition duration in _libs.scss
29
- }
30
- {{/if}}
31
9
  currentView.destroy();
32
- this.emitEvent('view-unmount', currentView.viewName);
10
+ currentView.onExit();
11
+ Lifecycle.viewUnmount(currentView.viewName);
33
12
  return null;
34
13
  }
35
14
 
@@ -38,28 +17,25 @@ export class ViewRenderManager {
38
17
  params: Record<string, string> = {}
39
18
  ): View {
40
19
  const view = viewConstructor();
20
+ ViewRenderManager.clearStaleMountContent(view);
41
21
 
42
22
  // Set route parameters before rendering
43
23
  view.setRouteParams(params);
44
24
 
45
- {{#if transition}}
46
- this.renderAnimatedView(view);
47
-
48
- {{else}}
49
25
  view.render();
26
+ view.onEnter();
50
27
 
51
- {{/if}}
52
- this.emitEvent('view-mount', view.viewName);
28
+ Lifecycle.viewMount(view.viewName);
53
29
  return view;
54
30
  }
55
31
 
56
- private static emitEvent(event: EventKey, viewName: string): void {
57
- AppEventBus.emit(event, viewName);
58
- }
59
- {{#if transition}}
32
+ private static clearStaleMountContent(view: View): void {
33
+ const viewElement = view.getElement();
34
+ const container = viewElement.parentElement;
35
+ if (!container) return;
60
36
 
61
- private static delay(ms: number): Promise<void> {
62
- return new Promise(res => setTimeout(res, ms));
37
+ Array.from(container.childNodes).forEach((node) => {
38
+ if (node !== viewElement) node.remove();
39
+ });
63
40
  }
64
- {{/if}}
65
41
  }
@@ -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,4 +1,5 @@
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';
@@ -16,13 +17,27 @@ export interface RouteMatch {
16
17
  params: Record<string, string>;
17
18
  }
18
19
 
19
- class Router {
20
+ class Router extends Service<Router> {
20
21
  private _currentView: View | null = null;
21
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> = [];
22
27
 
23
28
  constructor(private routes: Routes) {
29
+ super();
24
30
  this.initializeEventListeners();
25
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);
26
41
  }
27
42
 
28
43
  public async reload(): Promise<void> {
@@ -58,8 +73,8 @@ class Router {
58
73
  routeMatch.params
59
74
  );
60
75
 
61
- NavigationManager.updateMetaContent(this._currentView);
62
76
  NavigationManager.updateHistory(normalizedPath, currentPath, pushState);
77
+ window.scrollTo({ top: 0 });
63
78
  }
64
79
 
65
80
  public getCurrentParams(): Record<string, string> {
@@ -71,12 +86,29 @@ class Router {
71
86
  }
72
87
 
73
88
  private initializeEventListeners(): void {
74
- window.addEventListener('popstate', () => this.handlePopState());
75
- AppEventBus.subscribe('navigate', (path: string) => this.navigate(path));
76
- 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);
77
98
  }
78
99
 
79
- 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
+ }
80
112
  this.navigate(NavigationManager.getCurrentPath(), false);
81
113
  }
82
114
 
@@ -141,4 +173,4 @@ class Router {
141
173
  }
142
174
  }
143
175
 
144
- 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
  }