@nutin/cli 2.1.1 ā 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 +268 -0
- package/README.md +13 -5
- 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 +9 -8
- package/templates/base/AGENTS.md.hbs +42 -41
- package/templates/base/GETTING_STARTED.md.hbs +26 -20
- package/templates/base/README.md.hbs +7 -4
- 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 +7 -2
- 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 +286 -24
- 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 +66 -4
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +135 -75
- 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 +93 -54
- 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 +183 -4
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +207 -84
- 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 +306 -0
- package/templates/base/src/core/services/router/router.ts.hbs +113 -53
- package/templates/base/tools/builder/builder.config.js.hbs +6 -0
- package/templates/base/tools/builder/builder.js.hbs +18 -6
- 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/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 +15 -7
- 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 +43 -13
- 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 +28 -21
- 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 +24 -6
- 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 +24 -11
- package/templates/base/tools/builder/core/styles/sass.js.hbs +3 -2
- package/templates/base/tools/builder/core/styles/tailwind.js.hbs +19 -59
- package/templates/base/tools/dev/dev-serve.js.hbs +30 -5
- package/templates/base/tools/dev/serve.js.hbs +152 -22
- package/templates/base/tools/dev/watcher.js.hbs +34 -3
- package/templates/base/tools/generator/generator.js.hbs +1 -1
- package/templates/base/tools/generator/handle-file.js.hbs +4 -2
- package/templates/base/tools/generator/handle-file.test.js.hbs +3 -3
- 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 +30 -2
- 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 +2 -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 +3 -0
- package/templates/features/docker/tools/docker/nginx.conf.template +6 -4
- 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 +21 -4
- 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/tools/testin-nutin/core/{tests ā globals}/clock.test.js.hbs +0 -0
|
@@ -5,25 +5,51 @@ import { print } from '../utils/print.mjs';
|
|
|
5
5
|
const fs = fsExtra.default;
|
|
6
6
|
|
|
7
7
|
const GENERATE_SEO_LINE_RE = /^([ \t]*generateSEOFiles:[^\n]*\n)/m;
|
|
8
|
-
const
|
|
8
|
+
const FEATURES_HEADER_RE = /^([ \t]*\/\/ Nutin features[^\n]*\n)/m;
|
|
9
|
+
const FEATURES_HEADER = '\n // Nutin features\n';
|
|
10
|
+
|
|
11
|
+
const FEATURE_CONFIGS = {
|
|
12
|
+
docker: {
|
|
13
|
+
key: 'dockerPorts',
|
|
14
|
+
lines: ' dockerPorts: [9090], // Ports the Docker container exposes/listens on ā edit as needed\n',
|
|
15
|
+
added: 'Added "dockerPorts" to nutin.config.js ā edit it if you need different port(s).',
|
|
16
|
+
manual: 'add "dockerPorts: [9090]" to it manually before building.',
|
|
17
|
+
},
|
|
18
|
+
markdown: {
|
|
19
|
+
key: 'markdownSources',
|
|
20
|
+
lines:
|
|
21
|
+
' markdownSources: { // Markdown folders compiled to /generated/<name>.json at build time\n' +
|
|
22
|
+
" sourceFolders: ['markdown-content'], // folder paths, or { folder, hubFiles, routePrefix, sectionInPath, prefixReplacements, seo }\n" +
|
|
23
|
+
' },\n',
|
|
24
|
+
added: 'Added "markdownSources" to nutin.config.js ā add your own folders to "sourceFolders".',
|
|
25
|
+
manual: 'add "markdownSources: { sourceFolders: [\'markdown-content\'] }" to it manually.',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
9
28
|
|
|
10
29
|
export async function updateNutinConfig(projectPath, feature) {
|
|
11
|
-
|
|
30
|
+
const featureConfig = FEATURE_CONFIGS[feature.key];
|
|
31
|
+
if (!featureConfig) return;
|
|
12
32
|
|
|
13
33
|
const configPath = path.join(projectPath, 'nutin.config.js');
|
|
14
34
|
const content = await fs.readFile(configPath, 'utf-8');
|
|
15
35
|
|
|
16
|
-
if (
|
|
17
|
-
print.info(
|
|
36
|
+
if (new RegExp(`${featureConfig.key}\\s*:`).test(content)) {
|
|
37
|
+
print.info(`Kept existing "${featureConfig.key}" value in nutin.config.js`);
|
|
18
38
|
return;
|
|
19
39
|
}
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
// Features share one "// Nutin features" block, created after generateSEOFiles by
|
|
42
|
+
// whichever feature is added first.
|
|
43
|
+
let updated;
|
|
44
|
+
if (FEATURES_HEADER_RE.test(content)) {
|
|
45
|
+
updated = content.replace(FEATURES_HEADER_RE, `$1${featureConfig.lines}`);
|
|
46
|
+
} else if (GENERATE_SEO_LINE_RE.test(content)) {
|
|
47
|
+
updated = content.replace(GENERATE_SEO_LINE_RE, `$1${FEATURES_HEADER}${featureConfig.lines}`);
|
|
48
|
+
} else {
|
|
49
|
+
print.warn(`Could not find a "generateSEOFiles:" line in nutin.config.js ā ${featureConfig.manual}`);
|
|
23
50
|
return;
|
|
24
51
|
}
|
|
25
52
|
|
|
26
|
-
const updated = content.replace(GENERATE_SEO_LINE_RE, `$1${DOCKER_PORTS_LINE}`);
|
|
27
53
|
await fs.writeFile(configPath, updated);
|
|
28
|
-
print.warn(
|
|
54
|
+
print.warn(featureConfig.added);
|
|
29
55
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import * as fsExtra from 'fs-extra';
|
|
3
3
|
import { print } from '../utils/print.mjs';
|
|
4
|
-
import { getDockerScripts } from '../common/package-json-helper.mjs';
|
|
4
|
+
import { getDockerScripts, getMarkdownScripts } from '../common/package-json-helper.mjs';
|
|
5
5
|
|
|
6
6
|
const fs = fsExtra.default;
|
|
7
7
|
|
|
@@ -26,15 +26,13 @@ export async function updatePackageJson(projectPath, feature) {
|
|
|
26
26
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
27
27
|
const packageJson = await fs.readJSON(packageJsonPath);
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const featureScripts = {
|
|
30
|
+
docker: getDockerScripts,
|
|
31
|
+
markdown: getMarkdownScripts,
|
|
32
|
+
};
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (isDocker) {
|
|
34
|
-
scripts = getDockerScripts();
|
|
35
|
-
} else {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
34
|
+
if (!featureScripts[feature.key]) return;
|
|
35
|
+
const scripts = featureScripts[feature.key]();
|
|
38
36
|
|
|
39
37
|
const scriptsResult = mergeAdditive(packageJson.scripts ?? {}, scripts);
|
|
40
38
|
packageJson.scripts = scriptsResult.merged;
|
|
@@ -4,6 +4,7 @@ import * as fsExtra from 'fs-extra';
|
|
|
4
4
|
import { TemplateCompiler } from './template-compiler.mjs';
|
|
5
5
|
import { print } from '../utils/print.mjs';
|
|
6
6
|
import { FEATURES } from './feature-registry.mjs';
|
|
7
|
+
import { toJsOutput } from './transpile-ts.mjs';
|
|
7
8
|
|
|
8
9
|
const fs = fsExtra.default;
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -16,6 +17,17 @@ const BINARY_EXTENSIONS = new Set([
|
|
|
16
17
|
'.pdf', '.zip', '.tar', '.gz'
|
|
17
18
|
]);
|
|
18
19
|
|
|
20
|
+
// Template-repo housekeeping, never generated. .gitkeep only keeps otherwise-empty
|
|
21
|
+
// template folders in git/npm; the folder itself is still created (ensureDir).
|
|
22
|
+
const IGNORED_TEMPLATE_FILES = new Set(['.DS_Store', '.gitkeep']);
|
|
23
|
+
|
|
24
|
+
// Templates that only make sense in a TypeScript project ā not generated for JS-only ones.
|
|
25
|
+
const TS_ONLY_TEMPLATES = [
|
|
26
|
+
path.join('tools', 'builder', 'core', 'app', 'compile-ts.js.hbs'),
|
|
27
|
+
path.join('tools', 'builder', 'core', 'app', 'handler-refs.js.hbs'),
|
|
28
|
+
path.join('tools', 'builder', 'core', 'app', 'handler-refs.test.js.hbs'),
|
|
29
|
+
];
|
|
30
|
+
|
|
19
31
|
export class FileGenerator {
|
|
20
32
|
constructor() {
|
|
21
33
|
this.compiler = new TemplateCompiler();
|
|
@@ -42,7 +54,7 @@ export class FileGenerator {
|
|
|
42
54
|
const entries = await fs.readdir(templateDir, { withFileTypes: true });
|
|
43
55
|
|
|
44
56
|
for (const entry of entries) {
|
|
45
|
-
if (entry.name
|
|
57
|
+
if (IGNORED_TEMPLATE_FILES.has(entry.name)) continue;
|
|
46
58
|
|
|
47
59
|
const templatePath = path.join(templateDir, entry.name);
|
|
48
60
|
|
|
@@ -76,8 +88,13 @@ export class FileGenerator {
|
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
async isRenderTemplateFile(templatePath, fileName, context) {
|
|
91
|
+
const isJsOnly = context.lang === 'js';
|
|
92
|
+
if (isJsOnly && TS_ONLY_TEMPLATES.some((template) => templatePath.endsWith(template))) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
const fileExt = path.extname(fileName).toLowerCase();
|
|
80
|
-
const outputFileName = fileName.
|
|
97
|
+
const outputFileName = fileName.replace(/\.hbs$/, '');
|
|
81
98
|
const isBinary = BINARY_EXTENSIONS.has(fileExt);
|
|
82
99
|
|
|
83
100
|
let content;
|
|
@@ -89,6 +106,11 @@ export class FileGenerator {
|
|
|
89
106
|
content = await fs.readFile(templatePath, 'utf8');
|
|
90
107
|
}
|
|
91
108
|
|
|
109
|
+
if (isJsOnly && !isBinary) {
|
|
110
|
+
const jsOutput = await toJsOutput(outputFileName, content);
|
|
111
|
+
return jsOutput && { ...jsOutput, isBinary };
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
return { outputFileName, isBinary, content };
|
|
93
115
|
}
|
|
94
116
|
|
|
@@ -140,7 +162,7 @@ export class FileGenerator {
|
|
|
140
162
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
141
163
|
|
|
142
164
|
for (const entry of entries) {
|
|
143
|
-
if (entry.name
|
|
165
|
+
if (IGNORED_TEMPLATE_FILES.has(entry.name)) continue;
|
|
144
166
|
|
|
145
167
|
const entryPath = path.join(dir, entry.name);
|
|
146
168
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
|
|
4
|
+
export const execFileAsync = promisify(execFile);
|
|
5
|
+
|
|
6
|
+
// nutin new runs `git init` with it, and nutin-update needs it to merge files you edited.
|
|
7
|
+
export async function isGitAvailable() {
|
|
8
|
+
try {
|
|
9
|
+
await execFileAsync('git', ['--version']);
|
|
10
|
+
return true;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// The project folder's uncommitted changes, untracked files included (ignored ones aren't listed).
|
|
17
|
+
// Scoped to the folder, so a project inside a bigger repository isn't blocked by changes elsewhere.
|
|
18
|
+
// `repo: false` when git is missing or the folder isn't in a repository.
|
|
19
|
+
export async function getWorkingTreeStatus(projectPath) {
|
|
20
|
+
try {
|
|
21
|
+
await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: projectPath });
|
|
22
|
+
} catch {
|
|
23
|
+
return { repo: false, gitFound: await isGitAvailable(), changes: [] };
|
|
24
|
+
}
|
|
25
|
+
const { stdout } = await execFileAsync(
|
|
26
|
+
'git',
|
|
27
|
+
['status', '--porcelain', '--untracked-files=all', '--', '.'],
|
|
28
|
+
{ cwd: projectPath, maxBuffer: 1024 * 1024 * 10 },
|
|
29
|
+
);
|
|
30
|
+
return { repo: true, gitFound: true, changes: stdout.split('\n').filter(Boolean) };
|
|
31
|
+
}
|
|
@@ -1,11 +1,35 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
1
3
|
import inquirer from 'inquirer';
|
|
2
4
|
import { print } from '../utils/print.mjs';
|
|
3
5
|
import { FEATURES } from './feature-registry.mjs';
|
|
4
6
|
import { detectPackageManager } from './package-json-helper.mjs';
|
|
5
7
|
import { writeProjectMeta } from './project-meta.mjs';
|
|
8
|
+
import { META_FILE_NAME } from './package-data.mjs';
|
|
9
|
+
|
|
10
|
+
const fs = fsExtra.default;
|
|
11
|
+
|
|
12
|
+
// `--js-only` apps have src/app/main.js and no main.ts. Projects from before JS-only
|
|
13
|
+
// support are all TypeScript, so anything else counts as 'ts'.
|
|
14
|
+
async function detectLang(projectPath) {
|
|
15
|
+
const appDir = path.join(projectPath, 'src', 'app');
|
|
16
|
+
const hasMainJs = await fs.pathExists(path.join(appDir, 'main.js'));
|
|
17
|
+
const hasMainTs = await fs.pathExists(path.join(appDir, 'main.ts'));
|
|
18
|
+
return hasMainJs && !hasMainTs ? 'js' : 'ts';
|
|
19
|
+
}
|
|
6
20
|
|
|
7
21
|
export async function bootstrapProjectMeta(projectPath) {
|
|
8
|
-
|
|
22
|
+
// inquirer can't prompt without a TTY: it throws on a closed stdin and waits forever on an open pipe.
|
|
23
|
+
if (!process.stdin.isTTY) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`No ${META_FILE_NAME} found for this project, and this shell is non-interactive, so it can't be reconstructed. ` +
|
|
26
|
+
`Run this command again in a terminal to answer a couple of questions, or create ${META_FILE_NAME} yourself, e.g. ` +
|
|
27
|
+
`{ "version": "1.3.1", "packageManager": "npm", "lang": "ts", "features": { "docker": false, "markdown": false } } ` +
|
|
28
|
+
`("lang": "js" for a --js-only project)`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
print.warn(`ā ļø No ${META_FILE_NAME} found for this project.`);
|
|
9
33
|
print.section('Answer a couple of questions so a baseline can be reconstructed:\n');
|
|
10
34
|
|
|
11
35
|
const { version } = await inquirer.prompt([
|
|
@@ -27,7 +51,9 @@ export async function bootstrapProjectMeta(projectPath) {
|
|
|
27
51
|
]);
|
|
28
52
|
|
|
29
53
|
const packageManager = await detectPackageManager(projectPath);
|
|
54
|
+
const lang = await detectLang(projectPath);
|
|
30
55
|
const flags = Object.fromEntries(FEATURES.map((f) => [f.key, features.includes(f.key)]));
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
print.gray(`Detected ${lang === 'js' ? 'a JavaScript' : 'a TypeScript'} project using ${packageManager} ā edit ${META_FILE_NAME} if that's wrong.`);
|
|
58
|
+
return writeProjectMeta(projectPath, { version: version.trim(), packageManager, lang, ...flags });
|
|
33
59
|
}
|
|
@@ -5,6 +5,8 @@ import * as path from 'path';
|
|
|
5
5
|
|
|
6
6
|
const fs = fsExtra.default;
|
|
7
7
|
|
|
8
|
+
export const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm', 'bun'];
|
|
9
|
+
|
|
8
10
|
export async function detectPackageManager(projectPath) {
|
|
9
11
|
if (await fs.pathExists(path.join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
10
12
|
if (await fs.pathExists(path.join(projectPath, 'yarn.lock'))) return 'yarn';
|
|
@@ -78,18 +80,25 @@ export function getDockerScripts() {
|
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
export function getMarkdownScripts() {
|
|
84
|
+
return {
|
|
85
|
+
"markdown:check": "node tools/builder/core/markdown/generate-markdown-pages.js --check"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
export function getAllScripts(context) {
|
|
82
|
-
const { packageManager, docker } = context;
|
|
90
|
+
const { packageManager, docker, markdown } = context;
|
|
83
91
|
|
|
84
92
|
const baseScripts = {
|
|
85
93
|
"build": "node tools/builder/builder.js",
|
|
86
|
-
"build:prod": "
|
|
87
|
-
"serve": `${packageManager} run build &&
|
|
94
|
+
"build:prod": "node tools/builder/builder.js --prod",
|
|
95
|
+
"serve": `${packageManager} run build && node tools/dev/serve.js`,
|
|
88
96
|
"serve:only": "node tools/dev/serve.js",
|
|
89
|
-
"serve:prod": `${packageManager} run build:prod &&
|
|
97
|
+
"serve:prod": `${packageManager} run build:prod && node tools/dev/serve.js`,
|
|
90
98
|
"dev": "node tools/dev/dev-serve.js",
|
|
91
99
|
"generate": "node tools/generator/generator.js",
|
|
92
100
|
"testin-nutin": `${packageManager} run build && node tools/testin-nutin/runner.js`,
|
|
101
|
+
"testin-nutin:only": "node tools/testin-nutin/runner.js",
|
|
93
102
|
"testin-nutin:watch": `${packageManager} run build && node tools/testin-nutin/watch-tests.js`,
|
|
94
103
|
"testin-nutin:coverage": `${packageManager} run build && node tools/testin-nutin/runner.js --coverage`,
|
|
95
104
|
"testin-nutin:verbose": `${packageManager} run build && node tools/testin-nutin/runner.js --verbose`
|
|
@@ -98,6 +107,7 @@ export function getAllScripts(context) {
|
|
|
98
107
|
let scripts = { ...baseScripts };
|
|
99
108
|
|
|
100
109
|
if (docker) scripts = { ...scripts, ...getDockerScripts() };
|
|
110
|
+
if (markdown) scripts = { ...scripts, ...getMarkdownScripts() };
|
|
101
111
|
|
|
102
112
|
return scripts;
|
|
103
113
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import * as fsExtra from 'fs-extra';
|
|
3
3
|
import { FEATURES } from './feature-registry.mjs';
|
|
4
|
+
import { META_FILE_NAME } from './package-data.mjs';
|
|
4
5
|
|
|
5
6
|
const fs = fsExtra.default;
|
|
6
7
|
|
|
7
|
-
const META_FILE_NAME = '.nutin-meta.json';
|
|
8
|
-
|
|
9
8
|
function metaPath(projectPath) {
|
|
10
9
|
return path.join(projectPath, META_FILE_NAME);
|
|
11
10
|
}
|
|
@@ -22,6 +21,7 @@ export async function writeProjectMeta(projectPath, context) {
|
|
|
22
21
|
const meta = {
|
|
23
22
|
version: context.version,
|
|
24
23
|
packageManager: context.packageManager,
|
|
24
|
+
lang: context.lang ?? 'ts',
|
|
25
25
|
features: extractFeatures(context),
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -34,6 +34,8 @@ export class TemplateCompiler {
|
|
|
34
34
|
return a === b;
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
// Unused by the current templates, but kept: nutin-update renders an older version's
|
|
38
|
+
// templates with this compiler, and those may still call it.
|
|
37
39
|
Handlebars.registerHelper('hasAnyFeature', function(options) {
|
|
38
40
|
const context = options.data.root;
|
|
39
41
|
return context.i18n || context.template || context.testinNutin || context.stylinNutin
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// JS-only projects: the templates stay TypeScript-authored, and each rendered .ts file is
|
|
2
|
+
// transpiled here at generation time. typescript is imported lazily so TS projects never load it.
|
|
3
|
+
// Its version is pinned in package.json: nutin-update re-transpiles the old baseline and compares
|
|
4
|
+
// it to the user's files, so any output change between versions would read as a user edit.
|
|
5
|
+
// This pin is the CLI's own: generated apps install their own TypeScript (7.x) for tsc.
|
|
6
|
+
let tsModule;
|
|
7
|
+
|
|
8
|
+
async function loadTypeScript() {
|
|
9
|
+
tsModule ??= (await import('typescript')).default;
|
|
10
|
+
return tsModule;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// TypeScript's printer drops blank lines and indents by 4. Blank lines survive as marker
|
|
14
|
+
// comments, which are turned back into blank lines after transpiling.
|
|
15
|
+
const BLANK_LINE_MARKER = '//__NUTIN_BLANK_LINE__';
|
|
16
|
+
const BLANK_LINE_MARKER_LINE = /^[ \t]*\/\/__NUTIN_BLANK_LINE__[ \t]*$/;
|
|
17
|
+
|
|
18
|
+
function markBlankLines(source) {
|
|
19
|
+
return source
|
|
20
|
+
.split('\n')
|
|
21
|
+
.map((line) => (line.trim() === '' ? BLANK_LINE_MARKER : line))
|
|
22
|
+
.join('\n');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function restoreBlankLines(output) {
|
|
26
|
+
return output
|
|
27
|
+
.split('\n')
|
|
28
|
+
.map((line) => (BLANK_LINE_MARKER_LINE.test(line) ? '' : line))
|
|
29
|
+
.join('\n')
|
|
30
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
31
|
+
.replace(/^\n+/, '')
|
|
32
|
+
.replace(/\n*$/, '\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Template literal bodies are emitted verbatim (already 2-space), so lines starting inside
|
|
36
|
+
// one are left alone.
|
|
37
|
+
function getTemplateLiteralRanges(ts, code) {
|
|
38
|
+
const sourceFile = ts.createSourceFile('output.js', code, ts.ScriptTarget.Latest, true);
|
|
39
|
+
const ranges = [];
|
|
40
|
+
|
|
41
|
+
(function visit(node) {
|
|
42
|
+
if (ts.isTemplateExpression(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
43
|
+
ranges.push([node.getStart(sourceFile), node.end]);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
ts.forEachChild(node, visit);
|
|
47
|
+
})(sourceFile);
|
|
48
|
+
|
|
49
|
+
return ranges;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Each 4-space level becomes 2; a smaller remainder (JSDoc ` * ` alignment) is kept.
|
|
53
|
+
function reindent(ts, code) {
|
|
54
|
+
const ranges = getTemplateLiteralRanges(ts, code);
|
|
55
|
+
let offset = 0;
|
|
56
|
+
|
|
57
|
+
return code
|
|
58
|
+
.split('\n')
|
|
59
|
+
.map((line) => {
|
|
60
|
+
const lineStart = offset;
|
|
61
|
+
offset += line.length + 1;
|
|
62
|
+
|
|
63
|
+
if (ranges.some(([start, end]) => lineStart > start && lineStart < end)) return line;
|
|
64
|
+
|
|
65
|
+
const indent = line.match(/^ */)[0].length;
|
|
66
|
+
return ' '.repeat(indent - 2 * Math.floor(indent / 4)) + line.slice(indent);
|
|
67
|
+
})
|
|
68
|
+
.join('\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns `{ outputFileName, content }` with a `.js` name and transpiled content,
|
|
73
|
+
* `null` for ambient `.d.ts` files (type-only, nothing to emit), or the input unchanged
|
|
74
|
+
* for any other file.
|
|
75
|
+
*/
|
|
76
|
+
export async function toJsOutput(outputFileName, content) {
|
|
77
|
+
if (outputFileName.endsWith('.d.ts')) return null;
|
|
78
|
+
if (!outputFileName.endsWith('.ts')) return { outputFileName, content };
|
|
79
|
+
|
|
80
|
+
const ts = await loadTypeScript();
|
|
81
|
+
const { outputText } = ts.transpileModule(markBlankLines(content), {
|
|
82
|
+
fileName: outputFileName,
|
|
83
|
+
compilerOptions: {
|
|
84
|
+
target: ts.ScriptTarget.ESNext,
|
|
85
|
+
module: ts.ModuleKind.ESNext,
|
|
86
|
+
removeComments: false,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
outputFileName: outputFileName.replace(/\.ts$/, '.js'),
|
|
92
|
+
content: restoreBlankLines(reindent(ts, outputText)),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { print } from '../utils/print.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { execFileAsync, isGitAvailable } from '../common/git.mjs';
|
|
3
3
|
|
|
4
|
+
// A missing or failing git doesn't stop project creation: the project works without a repository.
|
|
4
5
|
export async function initializeGit(projectPath) {
|
|
6
|
+
if (!(await isGitAvailable())) {
|
|
7
|
+
print.warn('ā ļø git was not found ā skipping "git init". Install git to version the project; nutin-update also needs it to merge files you edit.');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
print.section('āļø Initializing Git repository...');
|
|
6
|
-
|
|
12
|
+
try {
|
|
13
|
+
await execFileAsync('git', ['init'], { cwd: projectPath });
|
|
14
|
+
} catch (error) {
|
|
15
|
+
print.warn(`ā ļø "git init" failed ā skipping it (${(error.stderr || error.message).trim()}). Run it yourself once the issue is fixed.`);
|
|
16
|
+
}
|
|
7
17
|
}
|
|
@@ -7,26 +7,34 @@ const fs = fsExtra.default;
|
|
|
7
7
|
export class JsonManager {
|
|
8
8
|
async generateJsonFiles(projectPath, context) {
|
|
9
9
|
await this.generatePackageJson(projectPath, context);
|
|
10
|
-
await this.generateTsconfigJson(projectPath
|
|
10
|
+
if (context.lang !== 'js') await this.generateTsconfigJson(projectPath);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async generatePackageJson(projectPath, context) {
|
|
14
|
-
|
|
14
|
+
await fs.writeJSON(path.join(projectPath, 'package.json'), this.buildPackageJson(context), { spaces: 2 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async generateTsconfigJson(projectPath) {
|
|
18
|
+
await fs.writeJSON(path.join(projectPath, 'tsconfig.json'), this.buildTsconfigJson(), { spaces: 2 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Also used by nutin-update to compare an existing project's files with the current defaults.
|
|
22
|
+
buildPackageJson(context) {
|
|
23
|
+
const { projectName, lang } = context;
|
|
15
24
|
|
|
16
25
|
const devDependencies = {
|
|
17
|
-
"chokidar": "^
|
|
18
|
-
"esbuild": "^0.
|
|
26
|
+
"chokidar": "^5.0.0",
|
|
27
|
+
"esbuild": "^0.28.2",
|
|
19
28
|
"html-minifier-terser": "^7.2.0",
|
|
20
|
-
"jsdom": "^
|
|
21
|
-
"linkedom": "^0.18.
|
|
22
|
-
"live-server": "^1.2.2",
|
|
29
|
+
"jsdom": "^30.1.1",
|
|
30
|
+
"linkedom": "^0.18.13",
|
|
23
31
|
"sass": "^1.89.0",
|
|
24
|
-
"typescript": "^
|
|
32
|
+
...(lang === 'js' ? {} : { "typescript": "^7.0.2" })
|
|
25
33
|
};
|
|
26
34
|
|
|
27
35
|
const scripts = getAllScripts(context);
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
return {
|
|
30
38
|
"name": projectName,
|
|
31
39
|
"version": "0.1.0",
|
|
32
40
|
"type": "module",
|
|
@@ -36,18 +44,14 @@ export class JsonManager {
|
|
|
36
44
|
scripts,
|
|
37
45
|
devDependencies,
|
|
38
46
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
47
|
+
"node": ">=24"
|
|
40
48
|
}
|
|
41
49
|
};
|
|
42
|
-
|
|
43
|
-
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
buildTsconfigJson() {
|
|
53
|
+
return {
|
|
48
54
|
"compilerOptions": {
|
|
49
|
-
"baseUrl": "./",
|
|
50
|
-
"paths": {},
|
|
51
55
|
"target": "ESNext",
|
|
52
56
|
"module": "NodeNext",
|
|
53
57
|
"moduleResolution": "NodeNext",
|
|
@@ -57,6 +61,7 @@ export class JsonManager {
|
|
|
57
61
|
"skipLibCheck": true,
|
|
58
62
|
"forceConsistentCasingInFileNames": true,
|
|
59
63
|
"noUncheckedIndexedAccess": true,
|
|
64
|
+
"noUnusedLocals": true,
|
|
60
65
|
"esModuleInterop": true,
|
|
61
66
|
"allowSyntheticDefaultImports": true,
|
|
62
67
|
"lib": ["es2022", "DOM"],
|
|
@@ -67,7 +72,5 @@ export class JsonManager {
|
|
|
67
72
|
"include": ["src/app", "src/core"],
|
|
68
73
|
"exclude": ["node_modules"]
|
|
69
74
|
};
|
|
70
|
-
|
|
71
|
-
await fs.writeJSON(path.join(projectPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
|
|
72
75
|
}
|
|
73
76
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { defaults } from './context-builder.mjs';
|
|
3
|
+
import { PACKAGE_MANAGERS } from '../common/package-json-helper.mjs';
|
|
3
4
|
|
|
4
5
|
export async function newAppPrompt(initialName, cliOptions = {}) {
|
|
5
6
|
|
|
@@ -26,10 +27,10 @@ export async function newAppPrompt(initialName, cliOptions = {}) {
|
|
|
26
27
|
if (!packageManager) {
|
|
27
28
|
const pmPreference = await inquirer.prompt([
|
|
28
29
|
{
|
|
29
|
-
type: '
|
|
30
|
+
type: 'select',
|
|
30
31
|
name: 'packageManager',
|
|
31
32
|
message: 'Package manager:',
|
|
32
|
-
choices:
|
|
33
|
+
choices: PACKAGE_MANAGERS,
|
|
33
34
|
default: defaults.packageManager,
|
|
34
35
|
},
|
|
35
36
|
]);
|
|
@@ -39,6 +40,7 @@ export async function newAppPrompt(initialName, cliOptions = {}) {
|
|
|
39
40
|
return {
|
|
40
41
|
projectName,
|
|
41
42
|
packageManager,
|
|
43
|
+
lang: cliOptions.jsOnly ? 'js' : 'ts',
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -2,7 +2,7 @@ import { print } from '../utils/print.mjs';
|
|
|
2
2
|
import { PACKAGE_HOMEPAGE } from '../common/package-data.mjs';
|
|
3
3
|
|
|
4
4
|
export function displaySuccessMessage(answers) {
|
|
5
|
-
const { projectName, packageManager
|
|
5
|
+
const { projectName, packageManager } = answers;
|
|
6
6
|
print.info(`Visit https://${PACKAGE_HOMEPAGE}`);
|
|
7
7
|
|
|
8
8
|
print.boldSuccess('\nš Your project is ready!\n');
|
|
@@ -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
|
}
|