@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.
- package/CHANGELOG.md +122 -8
- package/README.md +21 -37
- package/bin/add-feature.mjs +9 -0
- package/bin/cli.mjs +8 -0
- package/bin/create-app.mjs +5 -4
- package/bin/update-app.mjs +9 -0
- package/lib/index.mjs +81 -30
- package/lib/src/add-feature/feature-adder.mjs +83 -0
- package/lib/src/add-feature/feature-context-builder.mjs +25 -0
- package/lib/src/add-feature/nutin-config-updater.mjs +29 -0
- package/lib/src/add-feature/package-json-updater.mjs +49 -0
- package/lib/src/common/feature-registry.mjs +11 -0
- package/lib/src/common/file-generator.mjs +159 -0
- package/lib/src/common/meta-bootstrap-prompt.mjs +33 -0
- package/lib/src/common/package-data.mjs +10 -0
- package/lib/src/common/package-json-helper.mjs +103 -0
- package/lib/src/common/project-meta.mjs +54 -0
- package/lib/src/create-app/context-builder.mjs +21 -0
- package/lib/src/{git-manager.mjs → create-app/git-manager.mjs} +2 -2
- package/lib/src/create-app/json-manager.mjs +73 -0
- package/lib/src/create-app/new-app-prompt.mjs +51 -0
- package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +15 -13
- package/lib/src/create-app/success-message.mjs +14 -0
- package/lib/src/update-app/conflict-report-writer.mjs +31 -0
- package/lib/src/update-app/fetch-old-templates.mjs +61 -0
- package/lib/src/update-app/project-updater.mjs +136 -0
- package/lib/src/update-app/template-differ.mjs +124 -0
- package/lib/src/update-app/update-context-builder.mjs +25 -0
- package/lib/src/update-app/update-summary-printer.mjs +22 -0
- package/lib/src/update-app/version-compare.mjs +12 -0
- package/lib/src/utils/print.mjs +36 -0
- package/lib/src/utils/promise-exec-alias.mjs +4 -0
- package/package.json +17 -12
- package/templates/base/.gitignore.hbs +6 -1
- package/templates/base/AGENTS.md.hbs +169 -0
- package/templates/base/GETTING_STARTED.md.hbs +227 -0
- package/templates/base/README.md.hbs +8 -17
- package/templates/base/config/languages.json.hbs +6 -0
- package/templates/base/config/seo.json.hbs +12 -0
- package/templates/base/nutin.config.js.hbs +51 -0
- package/templates/base/src/app/components/index.ts.hbs +1 -2
- package/templates/base/src/app/globals.d.ts.hbs +3 -15
- package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
- package/templates/base/src/app/main.ts.hbs +7 -26
- package/templates/base/src/app/services/index.ts.hbs +1 -1
- package/templates/base/src/app/views/home/home.view.html.hbs +7 -0
- package/templates/base/src/app/views/home/home.view.ts.hbs +1 -56
- package/templates/base/src/app/views/index.ts.hbs +1 -0
- package/templates/base/src/app/views/not-found/not-found.view.html.hbs +8 -0
- package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +4 -37
- package/templates/base/src/core/base-classes/base-component/base-component.test.js.hbs +158 -0
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +81 -31
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.test.js.hbs +124 -0
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +38 -23
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +74 -0
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +1 -1
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +145 -0
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +41 -1
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.test.js.hbs +88 -0
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +13 -14
- package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.test.js.hbs +62 -0
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.test.js.hbs +82 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +142 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +67 -22
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +183 -0
- package/templates/base/src/core/base-classes/component/component.test.js.hbs +85 -0
- package/templates/base/src/core/base-classes/component/component.ts.hbs +27 -40
- package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +58 -0
- package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +17 -0
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.test.js.hbs +75 -0
- package/templates/base/src/core/base-classes/service/service.test.js.hbs +150 -0
- package/templates/base/src/core/base-classes/service/service.ts.hbs +26 -20
- package/templates/base/src/core/base-classes/view/view.test.js.hbs +74 -0
- package/templates/base/src/core/base-classes/view/view.ts.hbs +17 -41
- package/templates/base/src/core/config.ts.hbs +7 -0
- package/templates/base/src/core/index.ts.hbs +0 -3
- package/templates/base/src/core/internals.d.ts.hbs +27 -0
- package/templates/base/src/core/nutin-config.d.ts.hbs +4 -0
- package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +67 -3
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +9 -4
- package/templates/base/src/core/services/event-bus/index.ts.hbs +3 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.test.js.hbs +123 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +61 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.test.js.hbs +46 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +25 -0
- package/templates/base/src/core/services/globals/globals.test.js.hbs +149 -0
- package/templates/base/src/core/services/globals/globals.ts.hbs +95 -0
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +25 -0
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +20 -0
- package/templates/base/src/core/services/http-client/http-client.test.js.hbs +43 -0
- package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
- package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +60 -9
- package/templates/base/src/core/services/index.ts.hbs +5 -6
- package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
- package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
- package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
- package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +149 -6
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +42 -30
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +13 -70
- package/templates/base/src/core/services/router/router.test.js.hbs +226 -2
- package/templates/base/src/core/services/router/router.ts.hbs +40 -9
- package/templates/base/src/index.html.hbs +2 -6
- package/templates/base/src/styles/_styles.scss.hbs +102 -43
- package/templates/base/src/styles/base/_variables.scss.hbs +1 -85
- package/templates/base/src/styles/main.scss.hbs +1 -18
- package/templates/base/src/styles/tailwind.css.hbs +2 -0
- package/templates/base/tools/builder/builder.config.js.hbs +40 -0
- package/templates/base/tools/builder/builder.js.hbs +24 -26
- package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
- package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
- package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
- package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
- package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
- package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
- package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
- package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
- package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
- package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
- package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
- package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
- package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
- package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
- package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
- package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
- package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
- package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
- package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
- package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
- package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
- package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
- package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
- package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
- package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
- package/templates/base/tools/dev/serve.js.hbs +11 -1
- package/templates/base/tools/dev/watcher.js.hbs +14 -6
- package/templates/base/tools/generator/generator.js.hbs +64 -47
- package/templates/base/tools/generator/handle-file.js.hbs +17 -44
- package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
- package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
- package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
- package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
- package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/index.js.hbs +7 -12
- package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +4 -0
- package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
- package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
- package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
- package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
- package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
- package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +3 -2
- package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
- package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
- package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
- package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
- package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
- package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
- package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
- package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
- package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
- package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
- package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
- package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
- package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
- package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
- package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
- package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
- package/templates/base/tools/utils/error-exit.js.hbs +8 -0
- package/templates/base/tools/utils/format.js.hbs +10 -10
- package/templates/base/tools/utils/format.test.js.hbs +45 -0
- package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
- package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
- package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
- package/templates/base/tools/utils/index.js.hbs +1 -3
- package/templates/base/tools/utils/languages.js.hbs +15 -0
- package/templates/base/tools/utils/languages.test.js.hbs +16 -0
- package/templates/base/tools/utils/print.js.hbs +19 -13
- package/templates/base/tools/utils/print.test.js.hbs +60 -0
- package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
- package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
- package/templates/base/tools/utils/run-command.js.hbs +4 -2
- package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
- package/templates/base/tools/utils/run-script.js.hbs +14 -3
- package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
- package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
- package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
- package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
- package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
- package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
- package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
- package/lib/src/context-builder.mjs +0 -35
- package/lib/src/file-generator.mjs +0 -109
- package/lib/src/json-generator.mjs +0 -31
- package/lib/src/package-manager.mjs +0 -139
- package/lib/src/print.mjs +0 -31
- package/lib/src/prompts.mjs +0 -123
- package/lib/src/utils.mjs +0 -18
- package/lib/src/version.mjs +0 -1
- package/templates/base/builder.config.js.hbs +0 -35
- package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +0 -32
- package/templates/base/src/app/components/common/button/button.component.ts.hbs +0 -40
- package/templates/base/src/app/dom.test.js.hbs +0 -13
- package/templates/base/src/app/services/example/example.service.ts.hbs +0 -18
- package/templates/base/src/app/views/home/home.view.test.js.hbs +0 -26
- package/templates/base/src/core/services/store/store.test.js.hbs +0 -8
- package/templates/base/src/core/services/store/store.ts.hbs +0 -39
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.test.js.hbs +0 -0
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +0 -85
- package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +0 -249
- package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +0 -108
- package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +0 -50
- package/templates/base/src/core/utils/index.ts.hbs +0 -2
- package/templates/base/src/libs/index.ts.hbs +0 -4
- package/templates/base/src/libs/popover/popover.ts.hbs +0 -145
- package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -72
- package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
- package/templates/base/src/styles/core/_libs.scss.hbs +0 -132
- package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
- package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
- package/templates/base/tools/builder/core/sass.js.hbs +0 -22
- package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
- package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
- package/templates/base/tools/utils/export-flags.js.hbs +0 -6
- package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
- package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
- package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
- package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
- package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
- package/templates/features/i18n/src/core/config.ts.hbs +0 -3
- package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
- package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
- package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
- package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
- package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
- package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -240
- package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
- package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
- package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
- package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
- package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
- package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
- package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
- package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
- package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
- package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
- package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
- /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
- /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
- /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
- /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
- /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { NavigationManager } from '#root/dist/src/core/services/router/helpers/navigation-manager.helper.js';
|
|
2
|
+
import { I18nService } from '#root/dist/src/core/services/index.js';
|
|
3
|
+
import { CONFIG } from '#root/dist/src/core/config.js';
|
|
2
4
|
|
|
3
5
|
describe('NavigationManager', () => {
|
|
4
6
|
it('should normalize paths by removing trailing slashes', () => {
|
|
@@ -8,12 +10,6 @@ describe('NavigationManager', () => {
|
|
|
8
10
|
expect(NavigationManager.normalizePath('')).toBe('/');
|
|
9
11
|
});
|
|
10
12
|
|
|
11
|
-
it('should determine if pushState should be used', () => {
|
|
12
|
-
expect(NavigationManager.shouldPushState(true, '/home', '/about')).toBe(true);
|
|
13
|
-
expect(NavigationManager.shouldPushState(false, '/home', '/about')).toBe(false);
|
|
14
|
-
expect(NavigationManager.shouldPushState(true, '/about', '/about')).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
13
|
it('should call pushState when conditions are met', () => {
|
|
18
14
|
const originalPushState = window.history.pushState;
|
|
19
15
|
let called = false;
|
|
@@ -48,4 +44,151 @@ describe('NavigationManager', () => {
|
|
|
48
44
|
const currentPath = window.location.pathname;
|
|
49
45
|
expect(NavigationManager.getCurrentPath()).toBe(currentPath);
|
|
50
46
|
});
|
|
47
|
+
|
|
48
|
+
it('extractLocale returns the original path untouched when CONFIG.i18n is disabled', () => {
|
|
49
|
+
CONFIG.i18n = false;
|
|
50
|
+
expect(NavigationManager.extractLocale('/fr/about')).toEqual({ locale: null, strippedPath: '/fr/about' });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('extractLocale strips a recognized locale prefix when CONFIG.i18n is enabled', () => {
|
|
54
|
+
CONFIG.i18n = true;
|
|
55
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
56
|
+
try {
|
|
57
|
+
expect(NavigationManager.extractLocale('/fr/about')).toEqual({ locale: 'fr', strippedPath: '/about' });
|
|
58
|
+
expect(NavigationManager.extractLocale('/fr')).toEqual({ locale: 'fr', strippedPath: '/' });
|
|
59
|
+
} finally {
|
|
60
|
+
CONFIG.i18n = false;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('extractLocale leaves the path untouched when the first segment is not a recognized locale', () => {
|
|
65
|
+
CONFIG.i18n = true;
|
|
66
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
67
|
+
try {
|
|
68
|
+
expect(NavigationManager.extractLocale('/about')).toEqual({ locale: null, strippedPath: '/about' });
|
|
69
|
+
} finally {
|
|
70
|
+
CONFIG.i18n = false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('addLocalePrefix returns the path unchanged when CONFIG.i18n is disabled', () => {
|
|
75
|
+
CONFIG.i18n = false;
|
|
76
|
+
expect(NavigationManager.addLocalePrefix('/about')).toBe('/about');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('addLocalePrefix prefixes the path with the current language when CONFIG.i18n is enabled', () => {
|
|
80
|
+
CONFIG.i18n = true;
|
|
81
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
82
|
+
I18nService['_currentLanguage'] = 'fr';
|
|
83
|
+
try {
|
|
84
|
+
expect(NavigationManager.addLocalePrefix('/about')).toBe('/fr/about');
|
|
85
|
+
expect(NavigationManager.addLocalePrefix('/')).toBe('/fr');
|
|
86
|
+
} finally {
|
|
87
|
+
CONFIG.i18n = false;
|
|
88
|
+
I18nService['_currentLanguage'] = 'en';
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('getCurrentLocale reads the locale from the current URL path when CONFIG.i18n is enabled', () => {
|
|
93
|
+
CONFIG.i18n = true;
|
|
94
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
95
|
+
window.history.pushState({}, '', '/fr/about');
|
|
96
|
+
try {
|
|
97
|
+
expect(NavigationManager.getCurrentLocale()).toBe('fr');
|
|
98
|
+
} finally {
|
|
99
|
+
CONFIG.i18n = false;
|
|
100
|
+
window.history.pushState({}, '', '/');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('getCurrentLocale returns null when CONFIG.i18n is disabled', () => {
|
|
105
|
+
window.history.pushState({}, '', '/fr/about');
|
|
106
|
+
try {
|
|
107
|
+
expect(NavigationManager.getCurrentLocale()).toBe(null);
|
|
108
|
+
} finally {
|
|
109
|
+
window.history.pushState({}, '', '/');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('updateLocaleInUrl pushes a new URL when the locale-prefixed path differs from the current one', () => {
|
|
114
|
+
CONFIG.i18n = true;
|
|
115
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
116
|
+
I18nService['_currentLanguage'] = 'fr';
|
|
117
|
+
window.history.pushState({}, '', '/about');
|
|
118
|
+
|
|
119
|
+
const originalPushState = window.history.pushState;
|
|
120
|
+
const calls = [];
|
|
121
|
+
window.history.pushState = (...args) => {
|
|
122
|
+
calls.push(args);
|
|
123
|
+
originalPushState.apply(window.history, args);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
NavigationManager.updateLocaleInUrl();
|
|
128
|
+
expect(calls.length).toBe(1);
|
|
129
|
+
expect(calls[0][2]).toBe('/fr/about');
|
|
130
|
+
} finally {
|
|
131
|
+
window.history.pushState = originalPushState;
|
|
132
|
+
CONFIG.i18n = false;
|
|
133
|
+
I18nService['_currentLanguage'] = 'en';
|
|
134
|
+
window.history.pushState({}, '', '/');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('updateLocaleInUrl handles a root pathname that collapses to an empty string when stripped', () => {
|
|
139
|
+
CONFIG.i18n = false;
|
|
140
|
+
window.history.pushState({}, '', '/');
|
|
141
|
+
|
|
142
|
+
const originalPushState = window.history.pushState;
|
|
143
|
+
window.history.pushState = () => {};
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
expect(() => NavigationManager.updateLocaleInUrl()).not.toThrow();
|
|
147
|
+
} finally {
|
|
148
|
+
window.history.pushState = originalPushState;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('updateLocaleInUrl does nothing when the URL already has the correct locale prefix', () => {
|
|
153
|
+
CONFIG.i18n = true;
|
|
154
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
155
|
+
I18nService['_currentLanguage'] = 'fr';
|
|
156
|
+
window.history.pushState({}, '', '/fr/about');
|
|
157
|
+
|
|
158
|
+
const originalPushState = window.history.pushState;
|
|
159
|
+
let called = false;
|
|
160
|
+
window.history.pushState = () => { called = true; };
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
NavigationManager.updateLocaleInUrl();
|
|
164
|
+
expect(called).toBe(false);
|
|
165
|
+
} finally {
|
|
166
|
+
window.history.pushState = originalPushState;
|
|
167
|
+
CONFIG.i18n = false;
|
|
168
|
+
I18nService['_currentLanguage'] = 'en';
|
|
169
|
+
window.history.pushState({}, '', '/');
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('matchPattern matches a required param', () => {
|
|
174
|
+
expect(NavigationManager.matchPattern('/posts/:id', '/posts/123')).toEqual({ id: '123' });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('matchPattern does not match when a required param segment is missing', () => {
|
|
178
|
+
expect(NavigationManager.matchPattern('/posts/:id', '/posts')).toBe(null);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('matchPattern matches an optional param when present, and omits it when absent', () => {
|
|
182
|
+
expect(NavigationManager.matchPattern('/users/:id?', '/users/123')).toEqual({ id: '123' });
|
|
183
|
+
expect(NavigationManager.matchPattern('/users/:id?', '/users')).toEqual({});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('matchPattern matches multiple params in the same pattern', () => {
|
|
187
|
+
expect(NavigationManager.matchPattern('/posts/:postId/comments/:commentId', '/posts/1/comments/2'))
|
|
188
|
+
.toEqual({ postId: '1', commentId: '2' });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('matchPattern returns null when the path does not match the pattern at all', () => {
|
|
192
|
+
expect(NavigationManager.matchPattern('/posts/:id', '/other/123')).toBe(null);
|
|
193
|
+
});
|
|
51
194
|
});
|
|
@@ -1,12 +1,48 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I18nService } from "../../../index.js";
|
|
2
|
+
import { Language } from "../../i18n/languages.js";
|
|
3
|
+
import { CONFIG } from "../../../config.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Navigation - handles path normalization and history management
|
|
5
7
|
*/
|
|
6
8
|
export class NavigationManager {
|
|
9
|
+
public static extractLocale(path: string): { locale: Language | null; strippedPath: string } {
|
|
10
|
+
if (!CONFIG.i18n) return { locale: null, strippedPath: path };
|
|
11
|
+
|
|
12
|
+
const segments = path.split('/').filter(Boolean);
|
|
13
|
+
const first = segments[0] as Language;
|
|
14
|
+
if (first && I18nService.languages.includes(first)) {
|
|
15
|
+
const rest = segments.slice(1).join('/');
|
|
16
|
+
return { locale: first, strippedPath: rest ? `/${rest}` : '/' };
|
|
17
|
+
}
|
|
18
|
+
return { locale: null, strippedPath: path };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static addLocalePrefix(strippedPath: string): string {
|
|
22
|
+
if (!CONFIG.i18n) return strippedPath;
|
|
23
|
+
|
|
24
|
+
const lang = I18nService.currentLanguage;
|
|
25
|
+
return strippedPath === '/' ? `/${lang}` : `/${lang}${strippedPath}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static getCurrentLocale(): Language | null {
|
|
29
|
+
return this.extractLocale(window.location.pathname).locale;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static updateLocaleInUrl(): void {
|
|
33
|
+
const rawPathname = (new URL(window.location.pathname, window.location.origin).pathname || '/').replace(/\/+$/, '') || '/';
|
|
34
|
+
const { strippedPath } = this.extractLocale(rawPathname);
|
|
35
|
+
const newUrl = this.addLocalePrefix(strippedPath);
|
|
36
|
+
if (newUrl !== window.location.pathname) {
|
|
37
|
+
window.history.pushState({}, '', newUrl);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
7
41
|
public static normalizePath(path: string): string {
|
|
8
|
-
const
|
|
9
|
-
|
|
42
|
+
const collapsed = path.replace(/\/\/+/g, '/');
|
|
43
|
+
const url = new URL(collapsed, window.location.origin);
|
|
44
|
+
const pathname = (url.pathname || '/').replace(/\/+$/, '') || '/';
|
|
45
|
+
return this.extractLocale(pathname).strippedPath;
|
|
10
46
|
}
|
|
11
47
|
|
|
12
48
|
public static updateHistory(
|
|
@@ -14,28 +50,12 @@ export class NavigationManager {
|
|
|
14
50
|
currentPath: string,
|
|
15
51
|
pushState: boolean
|
|
16
52
|
): void {
|
|
17
|
-
|
|
18
|
-
|
|
53
|
+
const localizedPath = this.addLocalePrefix(normalizedPath);
|
|
54
|
+
if (pushState && window.location.pathname !== localizedPath) {
|
|
55
|
+
window.history.pushState({}, '', localizedPath);
|
|
19
56
|
}
|
|
20
57
|
}
|
|
21
58
|
|
|
22
|
-
public static updateMetaContent(currentView: View): void {
|
|
23
|
-
if (!currentView.shouldUpdateMetaContent()) return;
|
|
24
|
-
|
|
25
|
-
const strippedName = currentView.viewName.replace('-view', '');
|
|
26
|
-
{{#if i18n}}
|
|
27
|
-
const title = `${strippedName}.meta.title`;
|
|
28
|
-
const description = `${strippedName}.meta.description`;
|
|
29
|
-
|
|
30
|
-
document.title = I18nService.translate(title);
|
|
31
|
-
document.querySelector('meta[name="description"]')
|
|
32
|
-
?.setAttribute('content', I18nService.translate(description));
|
|
33
|
-
{{else}}
|
|
34
|
-
|
|
35
|
-
document.title = strippedName.charAt(0).toUpperCase() + strippedName.slice(1).toLowerCase();
|
|
36
|
-
{{/if}}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
59
|
public static getCurrentPath(): string {
|
|
40
60
|
return this.normalizePath(window.location.pathname);
|
|
41
61
|
}
|
|
@@ -67,12 +87,4 @@ export class NavigationManager {
|
|
|
67
87
|
});
|
|
68
88
|
return params;
|
|
69
89
|
}
|
|
70
|
-
|
|
71
|
-
private static shouldPushState(
|
|
72
|
-
pushState: boolean,
|
|
73
|
-
currentPath: string,
|
|
74
|
-
normalizedPath: string
|
|
75
|
-
): boolean {
|
|
76
|
-
return pushState && currentPath !== normalizedPath;
|
|
77
|
-
}
|
|
78
90
|
}
|
package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs
CHANGED
|
@@ -29,6 +29,12 @@ describe('RouteGuardsManager', () => {
|
|
|
29
29
|
expect(JSON.stringify(result)).toBe(JSON.stringify([guard]));
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
it('should default to an empty guards array when an object route config has no guards', () => {
|
|
33
|
+
const config = { view: mockViewConstructor };
|
|
34
|
+
const result = RouteGuardsManager.getRouteGuards(config);
|
|
35
|
+
expect(result).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
32
38
|
it('should pass all guards and return true from runGuards', async () => {
|
|
33
39
|
const guards = [() => true, () => Promise.resolve(true)];
|
|
34
40
|
const result = await RouteGuardsManager.runGuards(guards);
|
|
@@ -70,4 +76,25 @@ describe('RouteGuardsManager', () => {
|
|
|
70
76
|
expect(typeof result.viewConstructor).toBe('function');
|
|
71
77
|
expect(result.redirectTo).toBeUndefined();
|
|
72
78
|
});
|
|
79
|
+
|
|
80
|
+
it('passes the actual route params through processRouteGuards to each guard', async () => {
|
|
81
|
+
const receivedParams = [];
|
|
82
|
+
const guard = (params) => { receivedParams.push(params); return true; };
|
|
83
|
+
const config = { view: mockViewConstructor, guards: [guard] };
|
|
84
|
+
|
|
85
|
+
await RouteGuardsManager.processRouteGuards(config, '/users/42', { id: '42' });
|
|
86
|
+
|
|
87
|
+
expect(receivedParams.length).toBe(1);
|
|
88
|
+
expect(receivedParams[0]).toEqual({ id: '42' });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('defaults params to an empty object when none are passed to processRouteGuards', async () => {
|
|
92
|
+
const receivedParams = [];
|
|
93
|
+
const guard = (params) => { receivedParams.push(params); return true; };
|
|
94
|
+
const config = { view: mockViewConstructor, guards: [guard] };
|
|
95
|
+
|
|
96
|
+
await RouteGuardsManager.processRouteGuards(config, '/dashboard');
|
|
97
|
+
|
|
98
|
+
expect(receivedParams[0]).toEqual({});
|
|
99
|
+
});
|
|
73
100
|
});
|
package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
1
|
+
import { View, Lifecycle } from '../../../index.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* handles all view rendering
|
|
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
|
-
|
|
10
|
+
currentView.onExit();
|
|
11
|
+
Lifecycle.viewUnmount(currentView.viewName);
|
|
33
12
|
return null;
|
|
34
13
|
}
|
|
35
14
|
|
|
@@ -38,61 +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
|
-
|
|
52
|
-
|
|
53
|
-
this.cleanupOptionalContent();
|
|
54
|
-
|
|
55
|
-
this.emitEvent('view-mount', view.viewName);
|
|
28
|
+
Lifecycle.viewMount(view.viewName);
|
|
56
29
|
return view;
|
|
57
30
|
}
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (attrName && attrName !== "") {
|
|
64
|
-
const attrValue = el.getAttribute(attrName);
|
|
65
|
-
return !attrValue || attrValue.trim() === "" || attrValue === "undefined";
|
|
66
|
-
}
|
|
32
|
+
private static clearStaleMountContent(view: View): void {
|
|
33
|
+
const viewElement = view.getElement();
|
|
34
|
+
const container = viewElement.parentElement;
|
|
35
|
+
if (!container) return;
|
|
67
36
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
73
|
-
return !el.value?.trim();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (el instanceof HTMLMediaElement || el instanceof HTMLSourceElement) {
|
|
77
|
-
return !el.getAttribute("src");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const content = el.textContent?.trim();
|
|
81
|
-
return !content || content === "undefined";
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
document.querySelectorAll<HTMLElement>("[data-optional]").forEach(el => {
|
|
85
|
-
if (isEmpty(el)) el.remove();
|
|
37
|
+
Array.from(container.childNodes).forEach((node) => {
|
|
38
|
+
if (node !== viewElement) node.remove();
|
|
86
39
|
});
|
|
87
40
|
}
|
|
88
|
-
|
|
89
|
-
private static emitEvent(event: EventKey, viewName: string): void {
|
|
90
|
-
AppEventBus.emit(event, viewName);
|
|
91
|
-
}
|
|
92
|
-
{{#if transition}}
|
|
93
|
-
|
|
94
|
-
private static delay(ms: number): Promise<void> {
|
|
95
|
-
return new Promise(res => setTimeout(res, ms));
|
|
96
|
-
}
|
|
97
|
-
{{/if}}
|
|
98
41
|
}
|