@nutin/cli 2.1.0 → 3.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 +322 -0
- package/README.md +14 -6
- package/bin/add-feature.mjs +1 -1
- package/bin/cli.mjs +1 -1
- package/bin/create-app.mjs +1 -1
- package/bin/update-app.mjs +1 -1
- package/lib/index.mjs +6 -2
- package/lib/src/add-feature/base-template-patcher.mjs +139 -0
- package/lib/src/add-feature/feature-adder.mjs +20 -9
- package/lib/src/add-feature/feature-context-builder.mjs +1 -0
- package/lib/src/add-feature/nutin-config-updater.mjs +34 -8
- package/lib/src/add-feature/package-json-updater.mjs +7 -9
- package/lib/src/common/feature-registry.mjs +4 -0
- package/lib/src/common/file-generator.mjs +25 -3
- package/lib/src/common/git.mjs +31 -0
- package/lib/src/common/meta-bootstrap-prompt.mjs +28 -2
- package/lib/src/common/package-json-helper.mjs +14 -4
- package/lib/src/common/project-meta.mjs +2 -2
- package/lib/src/common/template-compiler.mjs +2 -0
- package/lib/src/common/transpile-ts.mjs +94 -0
- package/lib/src/create-app/context-builder.mjs +1 -0
- package/lib/src/create-app/git-manager.mjs +12 -2
- package/lib/src/create-app/json-manager.mjs +21 -18
- package/lib/src/create-app/new-app-prompt.mjs +4 -2
- package/lib/src/create-app/success-message.mjs +1 -1
- package/lib/src/update-app/conflict-report-writer.mjs +33 -21
- package/lib/src/update-app/json-drift.mjs +54 -0
- package/lib/src/update-app/project-updater.mjs +123 -14
- package/lib/src/update-app/template-differ.mjs +5 -5
- package/lib/src/update-app/three-way-merge.mjs +42 -0
- package/lib/src/update-app/update-context-builder.mjs +1 -0
- package/lib/src/update-app/update-summary-printer.mjs +10 -2
- package/package.json +12 -11
- package/templates/base/.gitignore.hbs +1 -0
- package/templates/base/AGENTS.md.hbs +45 -43
- package/templates/base/GETTING_STARTED.md.hbs +32 -23
- package/templates/base/README.md.hbs +7 -4
- package/templates/base/nutin.config.js.hbs +1 -1
- package/templates/base/src/app/components/.gitkeep +0 -0
- package/templates/base/src/app/globals.d.ts.hbs +6 -3
- package/templates/base/src/app/guards.ts.hbs +1 -3
- package/templates/base/src/app/main.ts.hbs +2 -9
- package/templates/base/src/app/routes.ts.hbs +9 -3
- package/templates/base/src/app/services/.gitkeep +0 -0
- package/templates/base/src/app/views/home/home.view.html.hbs +1 -1
- package/templates/base/src/app/views/home/home.view.ts.hbs +2 -2
- package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/base-component/base-component.test.js.hbs +461 -1
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +39 -48
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.test.js.hbs +36 -1
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +47 -77
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +307 -21
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +144 -26
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +105 -4
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +136 -62
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.test.js.hbs +85 -4
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +101 -51
- package/templates/base/src/core/base-classes/base-component/helpers/html.helper.test.js.hbs +404 -0
- package/templates/base/src/core/base-classes/base-component/helpers/html.helper.ts.hbs +242 -0
- package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.test.js.hbs +70 -1
- package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +54 -15
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.test.js.hbs +12 -1
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.ts.hbs +39 -36
- package/templates/base/src/core/base-classes/base-component/helpers/preservation.helper.test.js.hbs +179 -0
- package/templates/base/src/core/base-classes/base-component/helpers/preservation.helper.ts.hbs +93 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +135 -17
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +199 -81
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +40 -23
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +67 -65
- package/templates/base/src/core/base-classes/component/component.test.js.hbs +17 -17
- package/templates/base/src/core/base-classes/component/component.ts.hbs +8 -41
- package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +1 -1
- package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +37 -39
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.test.js.hbs +14 -1
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.ts.hbs +33 -31
- package/templates/base/src/core/base-classes/service/service.test.js.hbs +32 -0
- package/templates/base/src/core/base-classes/service/service.ts.hbs +21 -13
- package/templates/base/src/core/base-classes/view/view.test.js.hbs +4 -0
- package/templates/base/src/core/base-classes/view/view.ts.hbs +11 -8
- package/templates/base/src/core/index.ts.hbs +4 -2
- package/templates/base/src/core/internals.d.ts.hbs +199 -20
- package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +18 -0
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +26 -25
- package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +3 -3
- package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +3 -3
- package/templates/base/src/core/services/globals/globals.ts.hbs +7 -32
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +133 -50
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.ts.hbs +147 -39
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +75 -19
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.ts.hbs +73 -47
- package/templates/base/src/core/services/http-client/http-client.test.js.hbs +339 -232
- package/templates/base/src/core/services/http-client/http-client.ts.hbs +37 -25
- package/templates/base/src/core/services/i18n/i18n.test.js.hbs +57 -16
- package/templates/base/src/core/services/i18n/i18n.ts.hbs +42 -42
- package/templates/base/src/core/services/i18n/languages.ts.hbs +0 -2
- package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +4 -6
- package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +10 -0
- package/templates/base/src/core/services/pipe-registry/pipes.ts.hbs +4 -2
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +217 -4
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +210 -65
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +26 -27
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.ts.hbs +38 -60
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +4 -4
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +30 -30
- package/templates/base/src/core/services/router/router.test.js.hbs +321 -0
- package/templates/base/src/core/services/router/router.ts.hbs +122 -54
- package/templates/base/src/index.html.hbs +1 -0
- package/templates/base/src/styles/_styles.scss.hbs +1 -113
- package/templates/base/src/styles/main.scss.hbs +1 -1
- package/templates/base/tools/builder/builder.config.js.hbs +6 -0
- package/templates/base/tools/builder/builder.js.hbs +26 -8
- package/templates/base/tools/builder/core/app/compile-ts.js.hbs +81 -6
- package/templates/base/tools/builder/core/app/copy-static.js.hbs +12 -1
- package/templates/base/tools/builder/core/app/handler-refs.js.hbs +114 -0
- package/templates/base/tools/builder/core/app/handler-refs.test.js.hbs +70 -0
- package/templates/base/tools/builder/core/app/paths.js.hbs +17 -0
- package/templates/base/tools/builder/core/app/route-keys.js.hbs +47 -0
- package/templates/base/tools/builder/core/app/route-keys.test.js.hbs +97 -0
- package/templates/base/tools/builder/core/app/ts-errors.js.hbs +58 -0
- package/templates/base/tools/builder/core/app/ts-errors.test.js.hbs +84 -0
- package/templates/base/tools/builder/core/app/validate-routes.js.hbs +21 -55
- package/templates/base/tools/builder/core/finalize-build.js.hbs +6 -0
- package/templates/base/tools/builder/core/html-index/add-tags.js.hbs +1 -4
- package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +3 -3
- package/templates/base/tools/builder/core/html-index/validate-html.js.hbs +10 -2
- package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +111 -23
- package/templates/base/tools/builder/core/html-templates/minify-templates.js.hbs +8 -0
- package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +5 -3
- package/templates/base/tools/builder/core/html-templates/template-literal.js.hbs +193 -0
- package/templates/base/tools/builder/core/html-templates/template-literal.test.js.hbs +69 -0
- package/templates/base/tools/builder/core/i18n/build-i18n.js.hbs +14 -6
- package/templates/base/tools/builder/core/prod-bundle/bundle-options.js.hbs +82 -0
- package/templates/base/tools/builder/core/prod-bundle/bundle-options.test.js.hbs +65 -0
- package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +6 -6
- package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +25 -8
- package/templates/base/tools/builder/core/prod-bundle/hash-files.js.hbs +4 -4
- package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +31 -13
- package/templates/base/tools/builder/core/seo/generate-robots-txt.test.js.hbs +48 -0
- package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +3 -3
- package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +61 -15
- package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +22 -7
- package/templates/base/tools/builder/core/seo/generate-sitemap-xml.test.js.hbs +30 -0
- package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +48 -18
- package/templates/base/tools/builder/core/seo/html-substitutions.test.js.hbs +45 -0
- package/templates/base/tools/builder/core/seo/route-output.js.hbs +26 -8
- package/templates/base/tools/builder/core/seo/route-output.test.js.hbs +31 -0
- package/templates/base/tools/builder/core/seo/seo-routes.js.hbs +29 -0
- package/templates/base/tools/builder/core/seo/seo-routes.test.js.hbs +21 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +6 -7
- package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +34 -5
- package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.test.js.hbs +48 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +26 -9
- package/templates/base/tools/builder/core/styles/sass.js.hbs +7 -5
- package/templates/base/tools/builder/core/styles/tailwind.js.hbs +19 -59
- package/templates/base/tools/dev/dev-serve.js.hbs +71 -7
- package/templates/base/tools/dev/serve.js.hbs +152 -22
- package/templates/base/tools/dev/watcher.js.hbs +75 -28
- package/templates/base/tools/generator/generator.js.hbs +23 -18
- package/templates/base/tools/generator/handle-file.js.hbs +4 -24
- package/templates/base/tools/generator/handle-file.test.js.hbs +4 -23
- package/templates/base/tools/generator/templates/component/component.template.js.hbs +3 -3
- package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +3 -3
- package/templates/base/tools/generator/templates/service/service.template.js.hbs +1 -1
- package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +1 -1
- package/templates/base/tools/generator/templates/view/view.template.js.hbs +2 -2
- package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +2 -2
- package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +1 -1
- package/templates/base/tools/testin-nutin/core/globals/assertion-lib.js.hbs +2 -2
- package/templates/base/tools/testin-nutin/core/{tests/assertions.test.js.hbs → globals/assertion-lib.test.js.hbs} +6 -0
- package/templates/base/tools/testin-nutin/core/globals/register-test-globals.js.hbs +6 -7
- package/templates/base/tools/testin-nutin/core/{tests/globals-each-all.test.js.hbs → globals/register-test-globals.test.js.hbs} +12 -0
- package/templates/base/tools/testin-nutin/core/printer.js.hbs +10 -4
- package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +8 -8
- package/templates/base/tools/testin-nutin/mocks/mock-event-bus.js.hbs +27 -25
- package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +30 -22
- package/templates/base/tools/testin-nutin/mocks/mock-http-client.js.hbs +7 -10
- package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +12 -0
- package/templates/base/tools/testin-nutin/mocks/mock-i18n.js.hbs +47 -22
- package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +47 -26
- package/templates/base/tools/testin-nutin/mocks/mock-router.js.hbs +19 -11
- package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +24 -7
- package/templates/base/tools/testin-nutin/runner.js.hbs +12 -3
- package/templates/base/tools/testin-nutin/watch-tests.js.hbs +45 -20
- package/templates/base/tools/utils/build-lock.js.hbs +97 -0
- package/templates/base/tools/utils/ensure-deps.js.hbs +72 -0
- package/templates/base/tools/utils/ensure-deps.test.js.hbs +17 -0
- package/templates/base/tools/utils/format.js.hbs +1 -1
- package/templates/base/tools/utils/index.js.hbs +3 -1
- package/templates/base/tools/utils/print.js.hbs +2 -2
- package/templates/base/tools/utils/resolve-port.js.hbs +30 -0
- package/templates/base/tools/utils/resolve-port.test.js.hbs +47 -0
- package/templates/base/tools/utils/run-command.js.hbs +30 -1
- package/templates/base/tools/utils/run-command.test.js.hbs +12 -1
- package/templates/base/tools/utils/run-script.js.hbs +2 -2
- package/templates/features/docker/tools/docker/Dockerfile.template.hbs +5 -2
- package/templates/features/docker/tools/docker/nginx.conf.template +19 -2
- package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +1 -1
- package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +2 -2
- package/templates/features/docker/tools/docker/scripts/locale-redirects.js.hbs +28 -0
- package/templates/features/docker/tools/docker/scripts/locale-redirects.test.js.hbs +34 -0
- package/templates/features/docker/tools/docker/scripts/markdown-sources-copy.js.hbs +25 -0
- package/templates/features/docker/tools/docker/scripts/markdown-sources-copy.test.js.hbs +34 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +3 -1
- package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +12 -0
- package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +29 -0
- package/templates/features/markdown/markdown-content/getting-started.md.hbs +28 -0
- package/templates/features/markdown/markdown-content/writing-pages.md.hbs +19 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-content/markdown-content.component.scss.hbs +75 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-content/markdown-content.component.test.js.hbs +40 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-content/markdown-content.component.ts.hbs +65 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-landing/markdown-landing.component.test.js.hbs +67 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-landing/markdown-landing.component.ts.hbs +87 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-nav/markdown-nav.component.scss.hbs +42 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-nav/markdown-nav.component.test.js.hbs +59 -0
- package/templates/features/markdown/src/app/markdown/components/markdown-nav/markdown-nav.component.ts.hbs +72 -0
- package/templates/features/markdown/src/app/markdown/locales/en.json.hbs +6 -0
- package/templates/features/markdown/src/app/markdown/markdown-guards.test.js.hbs +63 -0
- package/templates/features/markdown/src/app/markdown/markdown-guards.ts.hbs +31 -0
- package/templates/features/markdown/src/app/markdown/markdown-i18n.test.js.hbs +78 -0
- package/templates/features/markdown/src/app/markdown/markdown-i18n.ts.hbs +31 -0
- package/templates/features/markdown/src/app/markdown/markdown-manifest.test.js.hbs +98 -0
- package/templates/features/markdown/src/app/markdown/markdown-manifest.ts.hbs +157 -0
- package/templates/features/markdown/src/app/markdown/markdown-manifests.service.test.js.hbs +87 -0
- package/templates/features/markdown/src/app/markdown/markdown-manifests.service.ts.hbs +50 -0
- package/templates/features/markdown/src/app/markdown/markdown-routes.test.js.hbs +74 -0
- package/templates/features/markdown/src/app/markdown/markdown-routes.ts.hbs +26 -0
- package/templates/features/markdown/src/app/markdown/markdown-sources.test.js.hbs +22 -0
- package/templates/features/markdown/src/app/markdown/markdown-sources.ts.hbs +42 -0
- package/templates/features/markdown/src/app/markdown/view/markdown.view.html.hbs +4 -0
- package/templates/features/markdown/src/app/markdown/view/markdown.view.scss.hbs +19 -0
- package/templates/features/markdown/src/app/markdown/view/markdown.view.test.js.hbs +125 -0
- package/templates/features/markdown/src/app/markdown/view/markdown.view.ts.hbs +146 -0
- package/templates/features/markdown/tools/builder/core/markdown/generate-markdown-pages.js.hbs +71 -0
- package/templates/features/markdown/tools/builder/core/markdown/markdown-compiler.js.hbs +582 -0
- package/templates/features/markdown/tools/builder/core/markdown/markdown-compiler.test.js.hbs +390 -0
- package/templates/features/markdown/tools/builder/core/markdown/markdown-seo-routes.js.hbs +155 -0
- package/templates/features/markdown/tools/builder/core/markdown/markdown-seo-routes.test.js.hbs +112 -0
- package/templates/base/src/app/components/index.ts.hbs +0 -1
- package/templates/base/src/app/services/index.ts.hbs +0 -1
- package/templates/base/src/app/views/index.ts.hbs +0 -3
- /package/templates/base/tools/testin-nutin/core/{tests → globals}/clock.test.js.hbs +0 -0
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import * as fsExtra from 'fs-extra';
|
|
3
|
-
import
|
|
3
|
+
import { CONFLICT_MARKER } from './three-way-merge.mjs';
|
|
4
4
|
|
|
5
5
|
const fs = fsExtra.default;
|
|
6
|
-
const REPORT_FILE_NAME = 'NUTIN-UPDATE-REPORT.md';
|
|
6
|
+
export const REPORT_FILE_NAME = 'NUTIN-UPDATE-REPORT.md';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
const sections = conflicts.map(({ relPath, actualContent, newEntry }) => {
|
|
10
|
-
if (newEntry.isBinary || Buffer.isBuffer(actualContent)) {
|
|
11
|
-
return `## ${relPath}\n\nBinary file — differs from both your recorded baseline and the new nutin version. Compare manually.\n`;
|
|
12
|
-
}
|
|
8
|
+
const list = (items, describe = ({ relPath }) => `\`${relPath}\``) => items.map((item) => `- ${describe(item)}`).join('\n');
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
// Lists the files you edited that this update touched. No diffs: merged files hold both versions'
|
|
11
|
+
// changes, and overlaps are marked in the files themselves.
|
|
12
|
+
export async function writeUpdateReport(projectPath, { merged, conflicted, binaryConflicts }, oldVersion, newVersion) {
|
|
13
|
+
const sections = [
|
|
14
|
+
`# nutin-update report\n\nUpdate from nutin v${oldVersion} to v${newVersion}. These files had changes of yours and changes in the new nutin version.`,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
if (conflicted.length > 0) {
|
|
18
|
+
sections.push(
|
|
19
|
+
'## Conflicts to resolve\n\n' +
|
|
20
|
+
'Your edits and nutin\'s overlap in these files. **The project won\'t build until they\'re resolved.** ' +
|
|
21
|
+
`Each conflict is marked in the file: \`${CONFLICT_MARKER}\` starts your version, \`||||||| nutin v${oldVersion}\` ` +
|
|
22
|
+
`the original one, \`=======\` the new one, ending at \`>>>>>>> nutin v${newVersion}\`. ` +
|
|
23
|
+
'Keep what you need, delete the markers, then run nutin-update again to confirm.\n\n' +
|
|
24
|
+
list(conflicted, ({ relPath, conflictCount }) => `\`${relPath}\` (${conflictCount} conflict${conflictCount > 1 ? 's' : ''})`),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (merged.length > 0) {
|
|
29
|
+
sections.push(
|
|
30
|
+
'## Merged automatically\n\nYour edits and the new version\'s changes didn\'t overlap, so both were kept. Review them.\n\n' +
|
|
31
|
+
list(merged),
|
|
21
32
|
);
|
|
22
|
-
|
|
23
|
-
});
|
|
33
|
+
}
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
if (binaryConflicts.length > 0) {
|
|
36
|
+
sections.push(
|
|
37
|
+
'## Binary files left as they are\n\nThese can\'t be merged: your version was kept. Compare it with the new one manually.\n\n' +
|
|
38
|
+
list(binaryConflicts),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
29
41
|
|
|
30
|
-
await fs.writeFile(path.join(projectPath, REPORT_FILE_NAME),
|
|
42
|
+
await fs.writeFile(path.join(projectPath, REPORT_FILE_NAME), sections.join('\n\n') + '\n');
|
|
31
43
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { JsonManager } from '../create-app/json-manager.mjs';
|
|
4
|
+
|
|
5
|
+
const fs = fsExtra.default;
|
|
6
|
+
|
|
7
|
+
// package.json and tsconfig.json aren't rendered from templates (JsonManager writes them
|
|
8
|
+
// once, at creation), so the template diff never sees them. They're compared with what
|
|
9
|
+
// this CLI would generate instead, and only reported.
|
|
10
|
+
// Only the entries nutin generates are compared; anything the user added is ignored.
|
|
11
|
+
const PACKAGE_JSON_SECTIONS = ['scripts', 'devDependencies'];
|
|
12
|
+
|
|
13
|
+
function same(a, b) {
|
|
14
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function compareEntries(file, section, yours = {}, generated = {}) {
|
|
18
|
+
return Object.entries(generated)
|
|
19
|
+
.filter(([key, value]) => !same(yours[key], value))
|
|
20
|
+
.map(([key, value]) => ({ file, key: section ? `${section}.${key}` : key, yours: yours[key], nutin: value }));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readJsonIfExists(filePath) {
|
|
24
|
+
if (!(await fs.pathExists(filePath))) return null;
|
|
25
|
+
try {
|
|
26
|
+
return await fs.readJSON(filePath);
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function findJsonDrift(projectPath, context) {
|
|
33
|
+
const jsonManager = new JsonManager();
|
|
34
|
+
const drift = [];
|
|
35
|
+
|
|
36
|
+
const packageJson = await readJsonIfExists(path.join(projectPath, 'package.json'));
|
|
37
|
+
if (packageJson) {
|
|
38
|
+
const generated = jsonManager.buildPackageJson(context);
|
|
39
|
+
for (const section of PACKAGE_JSON_SECTIONS) {
|
|
40
|
+
drift.push(...compareEntries('package.json', section, packageJson[section], generated[section]));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (context.lang !== 'js') {
|
|
45
|
+
const tsconfig = await readJsonIfExists(path.join(projectPath, 'tsconfig.json'));
|
|
46
|
+
if (tsconfig) {
|
|
47
|
+
const generated = jsonManager.buildTsconfigJson();
|
|
48
|
+
drift.push(...compareEntries('tsconfig.json', 'compilerOptions', tsconfig.compilerOptions, generated.compilerOptions));
|
|
49
|
+
drift.push(...compareEntries('tsconfig.json', null, { include: tsconfig.include, exclude: tsconfig.exclude }, { include: generated.include, exclude: generated.exclude }));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return drift;
|
|
54
|
+
}
|
|
@@ -8,12 +8,16 @@ import { fetchOldTemplates } from './fetch-old-templates.mjs';
|
|
|
8
8
|
import { parseVersion, compareVersions } from './version-compare.mjs';
|
|
9
9
|
import { bootstrapProjectMeta } from '../common/meta-bootstrap-prompt.mjs';
|
|
10
10
|
import { printUpdateSummary } from './update-summary-printer.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import { writeUpdateReport, REPORT_FILE_NAME } from './conflict-report-writer.mjs';
|
|
12
|
+
import { mergeFile, hasConflictMarkers, CONFLICT_MARKER } from './three-way-merge.mjs';
|
|
13
|
+
import { isGitAvailable, getWorkingTreeStatus } from '../common/git.mjs';
|
|
12
14
|
import { UpdateContextBuilder } from './update-context-builder.mjs';
|
|
13
15
|
import { TemplateDiffer } from './template-differ.mjs';
|
|
16
|
+
import { findJsonDrift } from './json-drift.mjs';
|
|
14
17
|
|
|
15
18
|
const fs = fsExtra.default;
|
|
16
|
-
|
|
19
|
+
|
|
20
|
+
const UNDO_COMMAND = 'git checkout -- . && git clean -fd';
|
|
17
21
|
|
|
18
22
|
export class ProjectUpdater {
|
|
19
23
|
constructor() {
|
|
@@ -22,8 +26,12 @@ export class ProjectUpdater {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
async updateProject(projectPath, options = {}) {
|
|
25
|
-
const { yes = false, from } = options;
|
|
29
|
+
const { yes = false, from, allowDirty = false } = options;
|
|
30
|
+
// Read before anything here writes .nutin-meta.json (bootstrap, resolved conflicts), so only your
|
|
31
|
+
// own changes count. Enforced right before the first file write.
|
|
32
|
+
const gitStatus = allowDirty ? null : await getWorkingTreeStatus(projectPath);
|
|
26
33
|
const meta = await this.resolveMeta(projectPath);
|
|
34
|
+
await this.assertNoConflictMarkers(projectPath, meta);
|
|
27
35
|
|
|
28
36
|
if (this.isUpToDate(meta)) {
|
|
29
37
|
print.boldSuccess(`✅ Already up to date (nutin v${packageVersion}).`);
|
|
@@ -37,7 +45,7 @@ export class ProjectUpdater {
|
|
|
37
45
|
|
|
38
46
|
try {
|
|
39
47
|
const changeSet = await this.computeChangeSet(projectPath, meta, oldTemplatesRoot);
|
|
40
|
-
await this.applyChangeSet(projectPath, meta, changeSet, { yes });
|
|
48
|
+
await this.applyChangeSet(projectPath, meta, changeSet, { yes, gitStatus });
|
|
41
49
|
} finally {
|
|
42
50
|
await cleanup();
|
|
43
51
|
}
|
|
@@ -62,6 +70,28 @@ export class ProjectUpdater {
|
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
// Files the last update left with conflict markers (meta.unmergedFiles): nothing is updated until
|
|
74
|
+
// they're resolved, so a merge never runs on top of markers and a re-run still reports them.
|
|
75
|
+
async assertNoConflictMarkers(projectPath, meta) {
|
|
76
|
+
const unmerged = meta.unmergedFiles ?? [];
|
|
77
|
+
if (unmerged.length === 0) return;
|
|
78
|
+
|
|
79
|
+
const stillMarked = [];
|
|
80
|
+
for (const relPath of unmerged) {
|
|
81
|
+
if (await hasConflictMarkers(path.join(projectPath, relPath))) stillMarked.push(relPath);
|
|
82
|
+
}
|
|
83
|
+
if (stillMarked.length > 0) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`${stillMarked.length} file(s) still have conflict markers from the last update — the project won't build until ` +
|
|
86
|
+
`they're resolved. Search for "${CONFLICT_MARKER}", merge, then run nutin-update again:\n` +
|
|
87
|
+
stillMarked.map((relPath) => ` - ${relPath}`).join('\n'),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await updateProjectMeta(projectPath, { unmergedFiles: [] });
|
|
92
|
+
print.info('Conflict markers from the last update are resolved.');
|
|
93
|
+
}
|
|
94
|
+
|
|
65
95
|
isUpToDate(meta) {
|
|
66
96
|
return compareVersions(meta.version, packageVersion) === 0;
|
|
67
97
|
}
|
|
@@ -84,12 +114,13 @@ export class ProjectUpdater {
|
|
|
84
114
|
|
|
85
115
|
async computeChangeSet(projectPath, meta, oldTemplatesRoot) {
|
|
86
116
|
const { oldContext, newContext } = await this.contextBuilder.buildContexts(projectPath, meta);
|
|
87
|
-
|
|
117
|
+
const changeSet = await this.differ.diff(projectPath, oldTemplatesRoot, oldContext, newContext);
|
|
118
|
+
return { ...changeSet, jsonDrift: await findJsonDrift(projectPath, newContext) };
|
|
88
119
|
}
|
|
89
120
|
|
|
90
|
-
async applyChangeSet(projectPath, meta, changeSet, { yes }) {
|
|
91
|
-
const { toUpdate, toAdd, conflicts, unknown, removedByUser, noLongerGenerated } = changeSet;
|
|
92
|
-
printUpdateSummary({ toUpdate, toAdd, conflicts, unknown, removedByUser, noLongerGenerated });
|
|
121
|
+
async applyChangeSet(projectPath, meta, changeSet, { yes, gitStatus = null }) {
|
|
122
|
+
const { toUpdate, toAdd, conflicts, unknown, removedByUser, noLongerGenerated, jsonDrift } = changeSet;
|
|
123
|
+
printUpdateSummary({ toUpdate, toAdd, conflicts, unknown, removedByUser, noLongerGenerated, jsonDrift });
|
|
93
124
|
|
|
94
125
|
if (toUpdate.length === 0 && toAdd.length === 0 && conflicts.length === 0) {
|
|
95
126
|
print.boldSuccess('\n✅ Nothing to update.');
|
|
@@ -97,18 +128,32 @@ export class ProjectUpdater {
|
|
|
97
128
|
return;
|
|
98
129
|
}
|
|
99
130
|
|
|
100
|
-
|
|
131
|
+
// Binary files can't be merged: they're kept as they are and listed in the report.
|
|
132
|
+
const toMerge = conflicts.filter(({ newEntry, actualContent }) => !newEntry.isBinary && !Buffer.isBuffer(actualContent));
|
|
133
|
+
const binaryConflicts = conflicts.filter((conflict) => !toMerge.includes(conflict));
|
|
134
|
+
|
|
135
|
+
if (gitStatus) this.assertPristine(gitStatus);
|
|
136
|
+
|
|
137
|
+
if (toMerge.length > 0 && !(await isGitAvailable())) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`nutin-update needs git to merge the ${toMerge.length} file(s) you edited, but git was not found. ` +
|
|
140
|
+
'Install git and run nutin-update again. No files were changed.',
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!yes && (toUpdate.length > 0 || toAdd.length > 0 || toMerge.length > 0)) {
|
|
101
145
|
if (!process.stdin.isTTY) {
|
|
102
146
|
print.warn('\n⚠️ Non-interactive shell detected — skipping the confirmation prompt.');
|
|
103
147
|
print.boldInfo('Re-run with --yes to apply these updates automatically. No files were changed.');
|
|
104
148
|
return;
|
|
105
149
|
}
|
|
106
150
|
|
|
151
|
+
const merges = toMerge.length > 0 ? ` and merge ${toMerge.length} file(s) you edited` : '';
|
|
107
152
|
const { proceed } = await inquirer.prompt([
|
|
108
153
|
{
|
|
109
154
|
type: 'confirm',
|
|
110
155
|
name: 'proceed',
|
|
111
|
-
message: `\nApply ${toUpdate.length + toAdd.length}
|
|
156
|
+
message: `\nApply ${toUpdate.length + toAdd.length} file update(s)${merges}?`,
|
|
112
157
|
default: true,
|
|
113
158
|
},
|
|
114
159
|
]);
|
|
@@ -124,15 +169,79 @@ export class ProjectUpdater {
|
|
|
124
169
|
await fs.writeFile(outputPath, newEntry.content);
|
|
125
170
|
}
|
|
126
171
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
172
|
+
const { merged, conflicted } = await this.mergeEditedFiles(projectPath, meta, toMerge);
|
|
173
|
+
|
|
174
|
+
if (merged.length > 0 || conflicted.length > 0 || binaryConflicts.length > 0) {
|
|
175
|
+
await writeUpdateReport(projectPath, { merged, conflicted, binaryConflicts }, meta.version, packageVersion);
|
|
130
176
|
}
|
|
131
177
|
|
|
132
|
-
await updateProjectMeta(projectPath, {
|
|
178
|
+
await updateProjectMeta(projectPath, {
|
|
179
|
+
version: packageVersion,
|
|
180
|
+
unmergedFiles: conflicted.map(({ relPath }) => relPath),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (merged.length > 0) {
|
|
184
|
+
print.info(`\n${merged.length} file(s) you edited were merged with the new version — review them (listed in ${REPORT_FILE_NAME}).`);
|
|
185
|
+
}
|
|
186
|
+
if (binaryConflicts.length > 0) {
|
|
187
|
+
print.boldError(`\n⚠️ ${binaryConflicts.length} binary file(s) you modified were left as they are — see ${REPORT_FILE_NAME}`);
|
|
188
|
+
}
|
|
189
|
+
if (conflicted.length > 0) {
|
|
190
|
+
print.boldError(
|
|
191
|
+
`\n⚠️ ${conflicted.length} file(s) have conflict markers — the project won't build until they're resolved ` +
|
|
192
|
+
`(search for "${CONFLICT_MARKER}"):`,
|
|
193
|
+
);
|
|
194
|
+
conflicted.forEach(({ relPath }) => print.boldError(` - ${relPath}`));
|
|
195
|
+
}
|
|
196
|
+
if (gitStatus) {
|
|
197
|
+
print.info(`\nTo undo this update, run from the project folder: ${UNDO_COMMAND}`);
|
|
198
|
+
print.info(`Important: this will also remove any untracked folder.`);
|
|
199
|
+
}
|
|
133
200
|
|
|
134
201
|
print.boldSuccess(`\n✅ Updated to nutin v${packageVersion}.`);
|
|
135
202
|
}
|
|
203
|
+
|
|
204
|
+
// The update must be undoable with git: no uncommitted changes and no untracked files in the
|
|
205
|
+
// project folder, so checkout + clean only reverts what the update wrote. --allow-dirty skips this.
|
|
206
|
+
assertPristine({ repo, gitFound, changes }) {
|
|
207
|
+
if (!gitFound) {
|
|
208
|
+
throw new Error('git was not found: nutin-update uses it to make the update undoable. Install git, or pass --allow-dirty. No files were changed.');
|
|
209
|
+
}
|
|
210
|
+
if (!repo) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
'This project isn\'t in a git repository, so the update couldn\'t be undone. Run "git init" and commit, ' +
|
|
213
|
+
'or pass --allow-dirty. No files were changed.',
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (changes.length > 0) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
'Commit or stash your changes first (untracked files included), so the update can be undone with git, ' +
|
|
219
|
+
`or pass --allow-dirty. No files were changed:\n${changes.map((line) => ` ${line}`).join('\n')}`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Three-way merge of each file you edited: the old nutin version is the base, so your edits and
|
|
225
|
+
// nutin's changes are both kept, and only overlapping edits get conflict markers.
|
|
226
|
+
async mergeEditedFiles(projectPath, meta, toMerge) {
|
|
227
|
+
const merged = [];
|
|
228
|
+
const conflicted = [];
|
|
229
|
+
const labels = { base: `nutin v${meta.version}`, theirs: `nutin v${packageVersion}` };
|
|
230
|
+
|
|
231
|
+
for (const { relPath, actualContent, baseContent, newEntry } of toMerge) {
|
|
232
|
+
const { content, conflictCount } = await mergeFile({
|
|
233
|
+
base: baseContent,
|
|
234
|
+
yours: actualContent,
|
|
235
|
+
theirs: newEntry.content,
|
|
236
|
+
labels,
|
|
237
|
+
});
|
|
238
|
+
await fs.writeFile(path.join(projectPath, relPath), content);
|
|
239
|
+
if (conflictCount > 0) conflicted.push({ relPath, conflictCount });
|
|
240
|
+
else merged.push({ relPath });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { merged, conflicted };
|
|
244
|
+
}
|
|
136
245
|
}
|
|
137
246
|
|
|
138
247
|
export const projectUpdater = new ProjectUpdater();
|
|
@@ -11,8 +11,8 @@ function contentEquals(a, b) {
|
|
|
11
11
|
return a === b;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Paths the user is expected to hand-edit right after `create-app
|
|
15
|
-
// content is just placeholder/starter scaffolding, so nutin-update must never rewrite
|
|
14
|
+
// Paths the user is expected to hand-edit right after `create-app`.
|
|
15
|
+
// Their generated content is just placeholder/starter scaffolding, so nutin-update must never rewrite
|
|
16
16
|
// or conflict-flag them.
|
|
17
17
|
const FULLY_EXCLUDED_PATTERNS = [
|
|
18
18
|
/^config\/[^/]+\.json$/,
|
|
@@ -21,7 +21,7 @@ const FULLY_EXCLUDED_PATTERNS = [
|
|
|
21
21
|
];
|
|
22
22
|
|
|
23
23
|
// Like FULLY_EXCLUDED_PATTERNS, but only for files that already existed before this
|
|
24
|
-
// update
|
|
24
|
+
// update. A brand new file introduced by a newer template version should still be added.
|
|
25
25
|
const ADD_ONLY_PATTERNS = [
|
|
26
26
|
/^src\/styles(\/|$)/,
|
|
27
27
|
];
|
|
@@ -114,8 +114,8 @@ export class TemplateDiffer {
|
|
|
114
114
|
if (contentEquals(actualContent, oldEntry.content)) {
|
|
115
115
|
toUpdate.push({ relPath, newEntry });
|
|
116
116
|
} else if (!contentEquals(actualContent, newEntry.content)) {
|
|
117
|
-
// Differs from both baselines
|
|
118
|
-
conflicts.push({ relPath, actualContent, newEntry });
|
|
117
|
+
// Differs from both baselines: a real conflict, merged from the old baseline at apply time.
|
|
118
|
+
conflicts.push({ relPath, actualContent, baseContent: oldEntry.content, newEntry });
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as fsExtra from 'fs-extra';
|
|
4
|
+
import { execFileAsync } from '../common/git.mjs';
|
|
5
|
+
|
|
6
|
+
const fs = fsExtra.default;
|
|
7
|
+
|
|
8
|
+
export const CONFLICT_MARKER = '<<<<<<< yours';
|
|
9
|
+
|
|
10
|
+
// Merges nutin's changes (base -> theirs) into your file with `git merge-file`, which needs the git
|
|
11
|
+
// binary but no repository. Overlapping edits get diff3 markers: yours, the old nutin version, the new one.
|
|
12
|
+
export async function mergeFile({ base, yours, theirs, labels }) {
|
|
13
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nutin-merge-'));
|
|
14
|
+
try {
|
|
15
|
+
const [yoursPath, basePath, theirsPath] = ['yours', 'base', 'theirs'].map((name) => path.join(tmpDir, name));
|
|
16
|
+
await Promise.all([
|
|
17
|
+
fs.writeFile(yoursPath, yours),
|
|
18
|
+
fs.writeFile(basePath, base),
|
|
19
|
+
fs.writeFile(theirsPath, theirs),
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const args = ['merge-file', '-p', '--diff3', '-L', 'yours', '-L', labels.base, '-L', labels.theirs, yoursPath, basePath, theirsPath];
|
|
23
|
+
try {
|
|
24
|
+
const { stdout } = await execFileAsync('git', args, { maxBuffer: 1024 * 1024 * 50 });
|
|
25
|
+
return { content: stdout, conflictCount: 0 };
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// A positive exit code below 255 is the number of conflicts; anything else is a real failure.
|
|
28
|
+
if (Number.isInteger(error.code) && error.code > 0 && error.code < 255) {
|
|
29
|
+
return { content: error.stdout, conflictCount: error.code };
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
} finally {
|
|
34
|
+
await fs.remove(tmpDir);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function hasConflictMarkers(filePath) {
|
|
39
|
+
if (!(await fs.pathExists(filePath))) return false;
|
|
40
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
41
|
+
return content.split('\n').some((line) => line.startsWith(CONFLICT_MARKER));
|
|
42
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { print } from '../utils/print.mjs';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const describeValue = (value) => (value === undefined ? '(missing)' : JSON.stringify(value));
|
|
4
|
+
|
|
5
|
+
export function printUpdateSummary({ toUpdate, toAdd, conflicts, unknown, removedByUser, noLongerGenerated, jsonDrift = [] }) {
|
|
4
6
|
print.section('\nSummary:');
|
|
5
7
|
print.info(` ${toUpdate.length} file(s) will be updated (untouched since generation)`);
|
|
6
8
|
print.info(` ${toAdd.length} new file(s) will be added`);
|
|
7
9
|
if (conflicts.length > 0) {
|
|
8
|
-
print.boldError(` ${conflicts.length} file(s) you
|
|
10
|
+
print.boldError(` ${conflicts.length} file(s) you edited will be merged with the new version`);
|
|
9
11
|
}
|
|
10
12
|
if (noLongerGenerated.length > 0) {
|
|
11
13
|
print.boldError(` ${noLongerGenerated.length} file(s) nutin no longer generates — left in place, review manually:`);
|
|
@@ -19,4 +21,10 @@ export function printUpdateSummary({ toUpdate, toAdd, conflicts, unknown, remove
|
|
|
19
21
|
print.section(` ${removedByUser.length} file(s) nutin used to generate but you removed — not recreated:`);
|
|
20
22
|
removedByUser.forEach(({ relPath }) => print.section(` - ${relPath}`));
|
|
21
23
|
}
|
|
24
|
+
if (jsonDrift.length > 0) {
|
|
25
|
+
print.section(` ${jsonDrift.length} package.json/tsconfig.json value(s) differ from what this nutin version generates — never changed automatically, apply by hand if wanted:`);
|
|
26
|
+
jsonDrift.forEach(({ file, key, yours, nutin }) =>
|
|
27
|
+
print.section(` - ${file} ${key}: yours ${describeValue(yours)}, nutin ${describeValue(nutin)}`)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
22
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nutin/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "The structure of a framework. The freedom of vanilla.",
|
|
5
5
|
"preferGlobal": true,
|
|
6
6
|
"main": "lib/index.mjs",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"minor": "npm version minor -m 'CI/CD: Bump version to %s'",
|
|
18
18
|
"major": "npm version major -m 'CI/CD: Bump version to %s'",
|
|
19
19
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
20
|
-
"
|
|
20
|
+
"release": "npm publish --access public"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"Nutin",
|
|
@@ -30,22 +30,23 @@
|
|
|
30
30
|
"author": "Loïs Kouninef <loiskouninef@gmail.com>",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"commander": "^
|
|
33
|
+
"commander": "^15.0.0",
|
|
34
34
|
"diff": "^9.0.0",
|
|
35
|
-
"fs-extra": "^11.
|
|
36
|
-
"handlebars": "^4.7.
|
|
37
|
-
"inquirer": "^
|
|
38
|
-
"tar": "^7.5.22"
|
|
35
|
+
"fs-extra": "^11.4.1",
|
|
36
|
+
"handlebars": "^4.7.10",
|
|
37
|
+
"inquirer": "^14.2.3",
|
|
38
|
+
"tar": "^7.5.22",
|
|
39
|
+
"typescript": "5.9.3"
|
|
39
40
|
},
|
|
40
41
|
"engines": {
|
|
41
|
-
"node": ">=
|
|
42
|
+
"node": ">=24"
|
|
42
43
|
},
|
|
43
44
|
"repository": {
|
|
44
45
|
"type": "git",
|
|
45
|
-
"url": "https://github.com/
|
|
46
|
+
"url": "git+https://github.com/LoisKOUNINEF/nutin.git"
|
|
46
47
|
},
|
|
47
48
|
"bugs": {
|
|
48
|
-
"url": "https://github.com/
|
|
49
|
+
"url": "https://github.com/LoisKOUNINEF/nutin/issues"
|
|
49
50
|
},
|
|
50
|
-
"homepage": "www.nutin.
|
|
51
|
+
"homepage": "www.nutin.dev"
|
|
51
52
|
}
|