@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/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,120 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
Version 2.0.0 focuses on simplicity and clear APIs over feature accumulation: fewer abstractions, clearer responsibilities, and a more focused API.
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Renamed `BaseComponent`'s `childConfigs()` to `registerChildren()` and `catalogConfig` to `createCatalogComponents()`.
|
|
10
|
+
- Raised the required Node.js version from `>=18` to `>=22`.
|
|
11
|
+
- Removed all libraries except `Pipes`, as they did not fit Nutin's philosophy.
|
|
12
|
+
- Removed `Store`. Its responsibilities were unclear and largely duplicated what services already provide.
|
|
13
|
+
- Removed the `force` parameter from `listenToRenderEvents()`. It is no longer needed.
|
|
14
|
+
|
|
15
|
+
### Architecture & API
|
|
16
|
+
|
|
17
|
+
- Clarified and separated the responsibilities of the base classes:
|
|
18
|
+
- **`BaseComponent`**: render lifecycle, hydration, DOM lifecycle, DOM and EventBus subscriptions, teardown, render guard, and composition orchestration.
|
|
19
|
+
- **`Component`**: props (`className`, `style`, `data-bindings`), configuration/defaults/normalization, and template generation through `templateFn`.
|
|
20
|
+
- **`View`**: route parameters, navigation hooks (`onEnter()` / `onExit()`), and view identity (`viewName`).
|
|
21
|
+
|
|
22
|
+
- Components and Views now inherit `listen()` and `listenToRenderEvents()` methods with automatic unsubscription.
|
|
23
|
+
- Added explicit lifecycle hooks:
|
|
24
|
+
- **Component:** `onBeforeRender()`, `onAfterRender()`, `onBeforeDestroy()`, `onAfterDestroy()`
|
|
25
|
+
- **View:** `onEnter()`, `onExit()`
|
|
26
|
+
|
|
27
|
+
- Simplified the EventBus API:
|
|
28
|
+
- Added `AppEventMap` for app-level events. Nutin's internal events are no longer declared in `globals.d.ts`.
|
|
29
|
+
- Event payload types are now objects.
|
|
30
|
+
- EventBus now exposes domain facades for `Navigation` and `Lifecycle` events.
|
|
31
|
+
- Event subscription functions (`on*`) now return a closure for unsubscription.
|
|
32
|
+
- `AppEventBus` can still be used directly when needed (`once()`, etc.), but subscriptions must then be unsubscribed manually through the `onBeforeDestroy()` hook.
|
|
33
|
+
```ts
|
|
34
|
+
// emit
|
|
35
|
+
Navigation.navigateTo('path');
|
|
36
|
+
// closure
|
|
37
|
+
const unsub = Navigation.onNavigate(this.doStuff);
|
|
38
|
+
// onDestroy
|
|
39
|
+
unsub()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Services
|
|
43
|
+
|
|
44
|
+
- Services now expose a single `getInstance()` method.
|
|
45
|
+
- Arguments can be passed to `getInstance()`, but are only used during the first instantiation.
|
|
46
|
+
|
|
47
|
+
### Configuration & Build
|
|
48
|
+
|
|
49
|
+
- Added `nutin.config.js` as the central configuration file for Nutin options, builder, generator, and testing.
|
|
50
|
+
- Added optional Tailwind CSS v4 support as a utility layer alongside SASS.
|
|
51
|
+
- Added globally scoped stylesheets co-located with feature files. **Use unique class names.** Nutin's naming convention encourages prefixes such as `home__header`.
|
|
52
|
+
- HTML templates can now be either inline or external `.html` files. Defining both, or neither, fails the build.
|
|
53
|
+
- Added configurable Docker ports through `nutin.config.js`.
|
|
54
|
+
- Dockerfile now builds a Brotli-enabled image.
|
|
55
|
+
|
|
56
|
+
### SEO & i18n
|
|
57
|
+
|
|
58
|
+
- Added optional generation of static SEO files from `config/seo.json`.
|
|
59
|
+
- Production builds generate static HTML for each route using the actual components.
|
|
60
|
+
- Only `title`, `description`, and `ogImage` remain hand-authored SEO content.
|
|
61
|
+
- Generates `robots.txt` and `sitemap.xml`.
|
|
62
|
+
- Supports per-route `disallow` and `disallowBots`.
|
|
63
|
+
|
|
64
|
+
- i18n now uses the URL, aligning with common practices and allowing static SEO HTML to be generated for each language.
|
|
65
|
+
|
|
66
|
+
### CLI & Developer Experience
|
|
67
|
+
|
|
68
|
+
- Added `nutin-update`, which updates Nutin to the latest patch or minor version by diffing the project against the new templates and merging compatible changes. Unresolved conflicts are reported.
|
|
69
|
+
- Added `nutin-add docker`.
|
|
70
|
+
- Added `GETTING_STARTED.md`, providing the architectural context and conventions needed to start working with a Nutin project without immediately consulting the full documentation.
|
|
71
|
+
- Added `AGENTS.md`, providing LLMs with the minimal context required to work effectively in a Nutin project.
|
|
72
|
+
|
|
73
|
+
### TestinNutin
|
|
74
|
+
|
|
75
|
+
- Added code coverage summaries for branches, functions, and lines.
|
|
76
|
+
- Added `it.todo`.
|
|
77
|
+
- Added clock mocking for `setTimeout` and `setInterval`.
|
|
78
|
+
|
|
79
|
+
### Bug Fixes
|
|
80
|
+
|
|
81
|
+
- Numerous bug fixes and stability improvements.
|
|
82
|
+
|
|
83
|
+
## 1.3.1
|
|
84
|
+
|
|
85
|
+
- Minor features:
|
|
86
|
+
- New BaseComponent protected method.
|
|
87
|
+
Register events that will trigger re-render. Call this in component's constructor.
|
|
88
|
+
```ts
|
|
89
|
+
// force = true: calls forceRender() instead of render()
|
|
90
|
+
listenToRenderEvents(events: EventKey[], force: boolean = false): void
|
|
91
|
+
```
|
|
92
|
+
- data-optional now supports JS values
|
|
93
|
+
```html
|
|
94
|
+
data-optional="${myValue}"
|
|
95
|
+
```
|
|
96
|
+
- PopoverView and AnchorComponents now have accessibility features
|
|
97
|
+
- ButtonManager now handles checkboxes as well
|
|
98
|
+
- i18nService now emits `language-changed` event and exposes related methods
|
|
99
|
+
|
|
100
|
+
- Fixes:
|
|
101
|
+
- CatalogConfig no longer renders multiple containers when render() or forceRender() are triggered by events
|
|
102
|
+
- Event Listeners are now destroyed properly before triggering a re-render
|
|
103
|
+
- Router now correctly redirects to 404 page when URL starts with two slashes `//`.
|
|
104
|
+
- Popover no longer flickers on close
|
|
105
|
+
- Snackbar messages are now properly sanitized
|
|
106
|
+
|
|
107
|
+
- CLI
|
|
108
|
+
- Rework: App creation flow
|
|
109
|
+
- No features enabled by default
|
|
110
|
+
- Presets `--preset <minimal|standard|full|cicd>`
|
|
111
|
+
Minimal: External templates
|
|
112
|
+
Standard: Minimal + i18n & built-in SCSS utilities.
|
|
113
|
+
Full: Standard + deployment helpers & built-in testing toolkit
|
|
114
|
+
CI/CD: Minimal + deployment helpers
|
|
115
|
+
- Remaining flags: `--i18n`, `--deploy-helper`, `--testin-nutin`, `--transition`
|
|
116
|
+
|
|
117
|
+
## 1.3.0
|
|
4
118
|
|
|
5
119
|
Version 1.3.0 marks a stability milestone with various improvements and refinements, making this the recommended version for new projects.
|
|
6
120
|
|
|
@@ -35,11 +149,11 @@ Version 1.3.0 marks a stability milestone with various improvements and refineme
|
|
|
35
149
|
- TestinNutin
|
|
36
150
|
- Now applies `setupJsdom()` beforeAll (was beforeEach) and `teardownJsdom()` afterAll (was afterEach) in `test-queue.js` (improved efficiency / speed)
|
|
37
151
|
|
|
38
|
-
##
|
|
152
|
+
## 1.2.3
|
|
39
153
|
|
|
40
154
|
- `nginx.conf` : fixed multi-line CSP map warning (single line map)
|
|
41
155
|
|
|
42
|
-
##
|
|
156
|
+
## 1.2.2
|
|
43
157
|
|
|
44
158
|
- Fixed Nginx security headers in child location blocks.
|
|
45
159
|
|
|
@@ -47,7 +161,7 @@ Version 1.3.0 marks a stability milestone with various improvements and refineme
|
|
|
47
161
|
|
|
48
162
|
- Removed ghost files
|
|
49
163
|
|
|
50
|
-
##
|
|
164
|
+
## 1.2.1
|
|
51
165
|
|
|
52
166
|
- Builder
|
|
53
167
|
|
|
@@ -75,7 +189,7 @@ Version 1.3.0 marks a stability milestone with various improvements and refineme
|
|
|
75
189
|
|
|
76
190
|
- Added explicit chokidar devDependency (sass & live-server transitive).
|
|
77
191
|
|
|
78
|
-
##
|
|
192
|
+
## 1.2.0
|
|
79
193
|
|
|
80
194
|
- Fixed `npm run dev` command. Added middleware to reload nested routes.
|
|
81
195
|
|
|
@@ -106,7 +220,7 @@ Version 1.3.0 marks a stability milestone with various improvements and refineme
|
|
|
106
220
|
- stylin-nutin (generator)
|
|
107
221
|
- When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `styles/components/_index.scss`).
|
|
108
222
|
|
|
109
|
-
##
|
|
223
|
+
## 1.1.0
|
|
110
224
|
|
|
111
225
|
- Added index access in CatalogConfig. Use with `config.index`.
|
|
112
226
|
```typescript
|
|
@@ -125,10 +239,10 @@ type CatalogItemConfig<T = any> =
|
|
|
125
239
|
|
|
126
240
|
**Notes:** *Primitive data arrays (string, number, etc) needs to be accessed with `config.value`.*
|
|
127
241
|
|
|
128
|
-
##
|
|
242
|
+
## 1.0.2
|
|
129
243
|
|
|
130
244
|
- Partially fixed `npm run dev` script when using internal templates. *Page still needs to be reload manually from time to time.*
|
|
131
245
|
|
|
132
|
-
##
|
|
246
|
+
## 1.0.1
|
|
133
247
|
|
|
134
248
|
- Fixed typo issue when using i18n feature
|
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
# [
|
|
1
|
+
# [Nutin](https://www.nutin.org)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Includes essential features out of the box.
|
|
5
|
-
*Pure TypeScript, SASS, and DOM-driven components — zero runtime dependencies.*
|
|
3
|
+
`The structure of a framework. The freedom of vanilla.`
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Nutin provides the structure and tooling you'd expect from a framework, while keeping the underlying web platform visible and giving you code ownership.
|
|
6
|
+
|
|
7
|
+
**Your app owns Nutin — not the other way around.** The source code lives alongside your application, so you can read it, modify it, and make it yours. Nutin's update system is designed to preserve those changes.
|
|
8
|
+
|
|
9
|
+
Nutin is deliberately pragmatic, lightweight, and dependency-free on runtime.
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
@@ -18,45 +19,28 @@ npm install -g @nutin/cli
|
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
21
|
# create a new app
|
|
21
|
-
|
|
22
|
-
nutin-new
|
|
22
|
+
nutin-new # or create-nutin-app
|
|
23
23
|
|
|
24
24
|
# without global installation
|
|
25
25
|
npx @nutin/cli
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
## Update Nutin
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
# Help
|
|
34
|
-
-h --help
|
|
35
|
-
|
|
36
|
-
# Defaults settings - see below
|
|
37
|
-
-d --defaults
|
|
38
|
-
|
|
39
|
-
# Package manager
|
|
40
|
-
-pm --package-manager <manager> # npm (Default), yarn, pnpm, bun
|
|
41
|
-
|
|
42
|
-
# Features
|
|
43
|
-
--template # (Default) Use external templates
|
|
44
|
-
--no-template # Use inline templates
|
|
45
|
-
--stylin-nutin # (Default) Use built-in SCSS utility classes & mixins
|
|
46
|
-
--no-stylin-nutin # Do not use built-in SCSS utility
|
|
47
|
-
--testin-nutin # Use testin-nutin toolkit
|
|
48
|
-
--no-testin-nutin # (Default) Do not use testin-nutin toolkit
|
|
49
|
-
--i18n # Use i18n & json-based content
|
|
50
|
-
--no-i18n # (Default) Do not use i18n & json-based content
|
|
51
|
-
--deploy-helper # Use Docker & deployment helpers
|
|
52
|
-
--no-deploy-helper, # (Default) Do not use Docker & deployment helpers
|
|
53
|
-
--transition # Use animated view transitions. Note: May interfere with CSS `position: fixed`, `z-index`...
|
|
54
|
-
--no-transition # (Default) Do not use animated view transitions
|
|
31
|
+
# update Nutin while preserving your changes
|
|
32
|
+
nutin-update
|
|
55
33
|
```
|
|
56
34
|
|
|
57
|
-
##
|
|
35
|
+
## Add Docker
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# ready-to-use Dockerfile for Nutin apps
|
|
39
|
+
nutin-add docker
|
|
40
|
+
```
|
|
58
41
|
|
|
59
|
-
##
|
|
42
|
+
## Useful links
|
|
60
43
|
|
|
61
|
-
-
|
|
62
|
-
-
|
|
44
|
+
- [Nutin website](https://www.nutin.org)
|
|
45
|
+
- [First app tutorial](https://www.nutin.org/tutorial)
|
|
46
|
+
- [Repository documentation](https://github.com/LoisKOUNINEF/nutin/tree/main/docs)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { addFeatureCommand } from '../lib/index.mjs';
|
|
5
|
+
|
|
6
|
+
addFeatureCommand(new Command()).parseAsync(process.argv).catch((err) => {
|
|
7
|
+
console.error(err instanceof Error ? err.stack : err);
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
});
|
package/bin/cli.mjs
ADDED
package/bin/create-app.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { createAppCommand } from '../lib/index.mjs';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
console.error(
|
|
7
|
-
process.
|
|
6
|
+
createAppCommand(new Command()).parseAsync(process.argv).catch((err) => {
|
|
7
|
+
console.error(err instanceof Error ? err.stack : err);
|
|
8
|
+
process.exitCode = 1;
|
|
8
9
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { updateAppCommand } from '../lib/index.mjs';
|
|
5
|
+
|
|
6
|
+
updateAppCommand(new Command()).parseAsync(process.argv).catch((err) => {
|
|
7
|
+
console.error(err instanceof Error ? err.stack : err);
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
});
|
package/lib/index.mjs
CHANGED
|
@@ -1,41 +1,92 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { print } from './src/print.mjs';
|
|
3
|
-
import {
|
|
4
|
-
import { createProject } from './src/project-generator.mjs';
|
|
5
|
-
import { displaySuccessMessage } from './src/
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { print, chalk } from './src/utils/print.mjs';
|
|
3
|
+
import { newAppPrompt } from './src/create-app/new-app-prompt.mjs';
|
|
4
|
+
import { createProject } from './src/create-app/project-generator.mjs';
|
|
5
|
+
import { displaySuccessMessage } from './src/create-app/success-message.mjs';
|
|
6
|
+
import { PACKAGE_VERSION } from './src/common/package-data.mjs';
|
|
7
|
+
import { FEATURES, findFeatureByCli } from './src/common/feature-registry.mjs';
|
|
8
|
+
import { addFeatureToProject } from './src/add-feature/feature-adder.mjs';
|
|
9
|
+
import { updateProject } from './src/update-app/project-updater.mjs';
|
|
10
|
+
|
|
11
|
+
export function createAppCommand(command) {
|
|
12
|
+
return command
|
|
13
|
+
.version(PACKAGE_VERSION)
|
|
14
|
+
.configureOutput({
|
|
15
|
+
helpWidth: 100
|
|
16
|
+
})
|
|
11
17
|
.argument('[projectName]', 'Name of the project')
|
|
12
|
-
.option('-d, --defaults', 'Use default options')
|
|
13
18
|
.option('-pm, --package-manager <manager>', 'Specify package manager (npm, yarn, pnpm, bun)')
|
|
14
|
-
.option('--template', '(Default) Use external templates')
|
|
15
|
-
.option('--no-template', 'Do not use external templates')
|
|
16
|
-
.option('--stylin-nutin', '(Default) Use built-in SCSS utility classes & mixins')
|
|
17
|
-
.option('--no-stylin-nutin', 'Do not use built-in SCSS utility')
|
|
18
|
-
.option('--i18n', 'Use i18n & json-based content')
|
|
19
|
-
.option('--no-i18n', '(Default) Do not use i18n & json-based content')
|
|
20
|
-
.option('--deploy-helper', 'Use Docker & deployment helpers')
|
|
21
|
-
.option('--no-deploy-helper', '(Default) Do not use Docker & deployment helpers')
|
|
22
|
-
.option('--testin-nutin', 'Use testin-nutin toolkit')
|
|
23
|
-
.option('--no-testin-nutin', '(Default) Do not use testin-nutin toolkit')
|
|
24
|
-
.option('--transition', 'Use animated view transitions\nNote: May interfere with CSS `position: fixed`, `z-index`...')
|
|
25
|
-
.option('--no-transition', '(Default) Do not use animated view transitions')
|
|
26
|
-
.addHelpText('afterAll', '\nChaining options :\nExample: -d -pm npm --i18n --testinNutin')
|
|
27
19
|
.action(async (projectName, cliOptions) => {
|
|
28
|
-
print.
|
|
20
|
+
print.boldSuccess('\n🚀 Welcome to your new nutin app!\n');
|
|
21
|
+
try {
|
|
22
|
+
const preferences = await newAppPrompt(projectName, cliOptions);
|
|
23
|
+
await createProject(preferences);
|
|
24
|
+
displaySuccessMessage(preferences);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
print.boldError(`❌ Error creating project: ${error.stack}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addFeatureCommand(command) {
|
|
33
|
+
const featureChoices = [...FEATURES.map((feature) => feature.cli), 'all'];
|
|
34
|
+
|
|
35
|
+
return command
|
|
36
|
+
.version(PACKAGE_VERSION)
|
|
37
|
+
.configureOutput({
|
|
38
|
+
helpWidth: 100
|
|
39
|
+
})
|
|
40
|
+
.argument('<feature>', `Feature to add to the current project: ${featureChoices.join(', ')}`)
|
|
41
|
+
.action(async (featureArg) => {
|
|
42
|
+
if (!featureChoices.includes(featureArg)) {
|
|
43
|
+
print.boldError(`❌ Unknown feature "${featureArg}". Choose one of: ${featureChoices.join(', ')}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
29
46
|
|
|
30
47
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
if (featureArg === 'all') {
|
|
49
|
+
for (const feature of FEATURES) {
|
|
50
|
+
await addFeatureToProject(feature.key);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
const feature = findFeatureByCli(featureArg);
|
|
54
|
+
await addFeatureToProject(feature.key);
|
|
55
|
+
}
|
|
34
56
|
} catch (error) {
|
|
35
|
-
print.boldError(`❌ Error
|
|
57
|
+
print.boldError(`❌ Error adding feature: ${error.stack}`);
|
|
36
58
|
process.exit(1);
|
|
37
59
|
}
|
|
38
60
|
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function updateAppCommand(command) {
|
|
64
|
+
return command
|
|
65
|
+
.version(PACKAGE_VERSION)
|
|
66
|
+
.configureOutput({
|
|
67
|
+
helpWidth: 100
|
|
68
|
+
})
|
|
69
|
+
.option('-y, --yes', 'Skip the confirmation prompt and apply safe updates immediately')
|
|
70
|
+
.option('--from <path>', 'Use a local templates/ directory as the old baseline instead of fetching from npm')
|
|
71
|
+
.action(async (cliOptions) => {
|
|
72
|
+
print.boldSuccess('🚀 nutin — update');
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await updateProject(process.cwd(), cliOptions);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
print.boldError(`❌ Error updating project: ${error.stack}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function buildCli() {
|
|
84
|
+
const program = new Command();
|
|
85
|
+
program.name('nutin').version(PACKAGE_VERSION);
|
|
86
|
+
|
|
87
|
+
createAppCommand(program.command('new', { isDefault: true }).description('Create a new nutin app'));
|
|
88
|
+
addFeatureCommand(program.command('add').description('Add a feature to the current project'));
|
|
89
|
+
updateAppCommand(program.command('update').description('Update nutin while preserving your changes'));
|
|
39
90
|
|
|
40
|
-
program
|
|
91
|
+
return program;
|
|
41
92
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import * as fsExtra from 'fs-extra';
|
|
4
|
+
import { print } from '../utils/print.mjs';
|
|
5
|
+
import { FileGenerator } from '../common/file-generator.mjs';
|
|
6
|
+
import { FEATURES } from '../common/feature-registry.mjs';
|
|
7
|
+
import { PACKAGE_VERSION } from '../common/package-data.mjs';
|
|
8
|
+
import { installDependencies } from '../common/package-json-helper.mjs';
|
|
9
|
+
import { readProjectMeta, updateProjectMeta } from '../common/project-meta.mjs';
|
|
10
|
+
import { FeatureContextBuilder } from './feature-context-builder.mjs';
|
|
11
|
+
import { updatePackageJson } from './package-json-updater.mjs';
|
|
12
|
+
import { updateNutinConfig } from './nutin-config-updater.mjs';
|
|
13
|
+
|
|
14
|
+
const fs = fsExtra.default;
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
export class FeatureAdder {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.fileGenerator = new FileGenerator();
|
|
21
|
+
this.builder = new FeatureContextBuilder();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async addFeatureToProject(featureKey) {
|
|
25
|
+
const feature = FEATURES.find((candidate) => candidate.key === featureKey);
|
|
26
|
+
if (!feature) {
|
|
27
|
+
throw new Error(`Unknown feature: ${featureKey}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const projectPath = process.cwd();
|
|
31
|
+
await this.validateProject(projectPath, feature);
|
|
32
|
+
|
|
33
|
+
const meta = await readProjectMeta(projectPath);
|
|
34
|
+
if (meta?.features?.[feature.key]) {
|
|
35
|
+
print.gray(`${feature.key} is already enabled in this project — skipping.`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
print.boldInfo(`\nAdding ${feature.key} to ${projectPath}...`);
|
|
40
|
+
|
|
41
|
+
const context = await this.builder.buildContext(projectPath, feature);
|
|
42
|
+
|
|
43
|
+
await this.applyFeatureTemplate(projectPath, feature, context);
|
|
44
|
+
await this.runPostAddTasks(projectPath, feature, context);
|
|
45
|
+
|
|
46
|
+
print.boldInfo(`${feature.key} added.`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async validateProject(projectPath, feature) {
|
|
50
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
51
|
+
if (!(await fs.pathExists(packageJsonPath))) {
|
|
52
|
+
throw new Error(`No package.json found in ${projectPath} — run this from a nutin project's root.`);
|
|
53
|
+
}
|
|
54
|
+
if (!(await fs.pathExists(path.join(projectPath, 'nutin.config.js')))) {
|
|
55
|
+
throw new Error(`No nutin.config.js file found in ${projectPath} — is this a nutin project?`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async applyFeatureTemplate(projectPath, feature, context) {
|
|
60
|
+
const featureTemplateDir = path.join(__dirname, '..', '..', '..', 'templates', 'features', feature.key);
|
|
61
|
+
|
|
62
|
+
await this.fileGenerator.processTemplateDirectory(featureTemplateDir, projectPath, context, { skipExisting: true });
|
|
63
|
+
|
|
64
|
+
await updatePackageJson(projectPath, feature);
|
|
65
|
+
await updateNutinConfig(projectPath, feature);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async runPostAddTasks(projectPath, feature, context) {
|
|
69
|
+
await updateProjectMeta(projectPath, {
|
|
70
|
+
version: PACKAGE_VERSION,
|
|
71
|
+
packageManager: context.packageManager,
|
|
72
|
+
features: { [feature.key]: true },
|
|
73
|
+
});
|
|
74
|
+
if (feature.key === 'docker') return;
|
|
75
|
+
await installDependencies(projectPath, context.packageManager);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const featureAdder = new FeatureAdder();
|
|
80
|
+
|
|
81
|
+
export async function addFeatureToProject(featureKey) {
|
|
82
|
+
return featureAdder.addFeatureToProject(featureKey);
|
|
83
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { getCiCommand, detectPackageManager } from '../common/package-json-helper.mjs';
|
|
4
|
+
import { PACKAGE_VERSION } from '../common/package-data.mjs';
|
|
5
|
+
import { readProjectMeta } from '../common/project-meta.mjs';
|
|
6
|
+
import { bootstrapProjectMeta } from '../common/meta-bootstrap-prompt.mjs';
|
|
7
|
+
|
|
8
|
+
const fs = fsExtra.default;
|
|
9
|
+
|
|
10
|
+
export class FeatureContextBuilder {
|
|
11
|
+
async buildContext(projectPath, feature) {
|
|
12
|
+
const packageJson = await fs.readJSON(path.join(projectPath, 'package.json'));
|
|
13
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
14
|
+
const meta = (await readProjectMeta(projectPath)) ?? (await bootstrapProjectMeta(projectPath));
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
projectName: packageJson.name,
|
|
18
|
+
packageManager,
|
|
19
|
+
ciCommand: getCiCommand(packageManager),
|
|
20
|
+
version: PACKAGE_VERSION,
|
|
21
|
+
...(meta.features ?? {}),
|
|
22
|
+
[feature.key]: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { print } from '../utils/print.mjs';
|
|
4
|
+
|
|
5
|
+
const fs = fsExtra.default;
|
|
6
|
+
|
|
7
|
+
const GENERATE_SEO_LINE_RE = /^([ \t]*generateSEOFiles:[^\n]*\n)/m;
|
|
8
|
+
const DOCKER_PORTS_LINE = '\n// Nutin features\n dockerPorts: [9090], // Ports the Docker container exposes/listens on — edit as needed\n';
|
|
9
|
+
|
|
10
|
+
export async function updateNutinConfig(projectPath, feature) {
|
|
11
|
+
if (feature.key !== 'docker') return;
|
|
12
|
+
|
|
13
|
+
const configPath = path.join(projectPath, 'nutin.config.js');
|
|
14
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
15
|
+
|
|
16
|
+
if (/dockerPorts\s*:/.test(content)) {
|
|
17
|
+
print.info('Kept existing "dockerPorts" value in nutin.config.js');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!GENERATE_SEO_LINE_RE.test(content)) {
|
|
22
|
+
print.warn('Could not find a "generateSEOFiles:" line in nutin.config.js — add "dockerPorts: [9090]" to it manually before building.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const updated = content.replace(GENERATE_SEO_LINE_RE, `$1${DOCKER_PORTS_LINE}`);
|
|
27
|
+
await fs.writeFile(configPath, updated);
|
|
28
|
+
print.warn('Added "dockerPorts" to nutin.config.js — edit it if you need different port(s).');
|
|
29
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { print } from '../utils/print.mjs';
|
|
4
|
+
import { getDockerScripts } from '../common/package-json-helper.mjs';
|
|
5
|
+
|
|
6
|
+
const fs = fsExtra.default;
|
|
7
|
+
|
|
8
|
+
function mergeAdditive(target, additions) {
|
|
9
|
+
const merged = { ...target };
|
|
10
|
+
const added = [];
|
|
11
|
+
const skipped = [];
|
|
12
|
+
|
|
13
|
+
for (const [key, value] of Object.entries(additions)) {
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(merged, key)) {
|
|
15
|
+
skipped.push(key);
|
|
16
|
+
} else {
|
|
17
|
+
merged[key] = value;
|
|
18
|
+
added.push(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { merged, added, skipped };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function updatePackageJson(projectPath, feature) {
|
|
26
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
27
|
+
const packageJson = await fs.readJSON(packageJsonPath);
|
|
28
|
+
|
|
29
|
+
const isDocker = feature.key === 'docker';
|
|
30
|
+
|
|
31
|
+
let scripts = {};
|
|
32
|
+
|
|
33
|
+
if (isDocker) {
|
|
34
|
+
scripts = getDockerScripts();
|
|
35
|
+
} else {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const scriptsResult = mergeAdditive(packageJson.scripts ?? {}, scripts);
|
|
40
|
+
packageJson.scripts = scriptsResult.merged;
|
|
41
|
+
|
|
42
|
+
await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
|
|
43
|
+
|
|
44
|
+
const skipped = scriptsResult.skipped;
|
|
45
|
+
if (skipped.length > 0) {
|
|
46
|
+
print.info(`Kept existing package.json values for: ${skipped.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
print.info('Updated package.json');
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Single source of truth for the optional, additive features under templates/features
|
|
2
|
+
export const FEATURES = [
|
|
3
|
+
{
|
|
4
|
+
key: 'docker',
|
|
5
|
+
cli: 'docker',
|
|
6
|
+
},
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export function findFeatureByCli(cliName) {
|
|
10
|
+
return FEATURES.find((feature) => feature.cli === cliName);
|
|
11
|
+
}
|