@nutin/cli 1.2.3 → 1.3.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 +36 -1
- package/README.md +23 -15
- package/lib/index.mjs +7 -4
- package/lib/src/context-builder.mjs +35 -0
- package/lib/src/file-generator.mjs +26 -126
- package/lib/src/git-manager.mjs +1 -1
- package/lib/src/json-generator.mjs +31 -0
- package/lib/src/package-manager.mjs +21 -18
- package/lib/src/project-generator.mjs +53 -0
- package/lib/src/prompts.mjs +28 -15
- package/lib/src/template-compiler.mjs +1 -1
- package/lib/src/utils.mjs +9 -9
- package/lib/src/version.mjs +1 -0
- package/package.json +1 -1
- package/templates/base/.gitignore.hbs +1 -0
- package/templates/base/builder.config.js.hbs +7 -0
- package/templates/base/src/app/main.ts.hbs +2 -1
- package/templates/base/src/app/views/home/home.view.test.js.hbs +26 -0
- 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 +1 -1
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +23 -20
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +4 -4
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +26 -13
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +9 -9
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +40 -8
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +16 -16
- package/templates/base/src/core/base-classes/component/component.ts.hbs +12 -12
- package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/service/service.ts.hbs +35 -12
- package/templates/base/src/core/base-classes/view/view.ts.hbs +6 -6
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +13 -13
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.ts.hbs +18 -18
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.ts.hbs +24 -24
- package/templates/base/src/core/services/http-client/http-client.ts.hbs +19 -19
- package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +6 -6
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +13 -13
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.ts.hbs +22 -22
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +6 -5
- package/templates/base/src/core/services/router/router.ts.hbs +45 -45
- package/templates/base/src/core/services/store/store.ts.hbs +6 -6
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +6 -8
- package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +9 -11
- package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +11 -5
- package/templates/base/src/libs/pipes/pipes.ts.hbs +1 -1
- package/templates/base/src/libs/popover/popover.ts.hbs +48 -48
- package/templates/base/src/libs/snackbar/snackbar.ts.hbs +1 -1
- package/templates/base/tools/builder/builder.js.hbs +9 -3
- package/templates/base/tools/builder/core/add-tags.js.hbs +46 -0
- package/templates/base/tools/builder/core/copy-static.js.hbs +35 -35
- package/templates/base/tools/builder/core/finalize-build.js.hbs +6 -6
- package/templates/base/tools/builder/core/hash-files.js.hbs +52 -0
- package/templates/base/tools/builder/core/validate-html.js.hbs +12 -32
- package/templates/base/tools/utils/get-files-recursive.js.hbs +17 -8
- package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +56 -0
- package/templates/{base → features/deployHelper}/tools/deployment/Dockerfile.hbs +12 -4
- package/templates/{base → features/deployHelper}/tools/deployment/nginx.conf.hbs +30 -2
- package/templates/features/i18n/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +4 -6
- package/templates/features/i18n/src/core/services/i18n/i18n.ts.hbs +38 -47
- package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +17 -0
- package/templates/features/template/src/app/views/home/home.view.html.hbs +1 -1
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +2 -2
- package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +2 -2
- package/templates/features/testinNutin/testin-nutin/runner.js.hbs +1 -0
- package/templates/features/testinNutin/testin-nutin.config.js.hbs +2 -2
- package/templates/base/tools/deployment/gzip.conf.hbs +0 -19
- package/templates/features/i18n/src/app/views/home/locales/fr.json.hbs +0 -9
- /package/templates/{base → features/deployHelper}/.dockerignore.hbs +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## V1.3.0
|
|
4
|
+
|
|
5
|
+
Version 1.3.0 marks a stability milestone with various improvements and refinements, making this the recommended version for new projects.
|
|
6
|
+
|
|
7
|
+
- Global
|
|
8
|
+
- `Service` abstract class now auto-binds methods with `this`, preventing `"this" is undefined` potential warning on initial page load.
|
|
9
|
+
- Added HTML template sanitization with trustLevel that can be passed to component's `super()`.
|
|
10
|
+
- trusted : no template sanitization (returns original)
|
|
11
|
+
- normal (default) : remove scripts and inline event handlers
|
|
12
|
+
- strict : remove iframe, object, embed, href (javascript), data: protocol, scripts and inline event handlers
|
|
13
|
+
|
|
14
|
+
- I18n
|
|
15
|
+
- Removed `data-i18n-params` pipe : Default values (fallback) must now be inlined text content in component's HTML template.
|
|
16
|
+
|
|
17
|
+
- CLI :
|
|
18
|
+
- Deployment helpers is now optional (default : disabled). Flags : `--deploy-helper` `--no-deploy-helper`
|
|
19
|
+
- Fixed CLI prompts (no longer overrides answers)
|
|
20
|
+
|
|
21
|
+
- Builder / Deployment Helper
|
|
22
|
+
- Now adds stylesheet & script tags in `index.html` on build time (`add-tags.js`)
|
|
23
|
+
- (Production) Added file hashing (js & css) (`hash-files.js`)
|
|
24
|
+
- (Production) Added Gzip and Brotli compression (`compress-files.js`). Configurable in `builder.config.js`
|
|
25
|
+
- **Note : default nginx alpine image does NOT support Brotli compression.** If you want to use it :
|
|
26
|
+
- uncomment brotli-related sections (`BROTLI OPTIONAL`) in `builder.config.js` and `tools/builder/core/compress-files.js`
|
|
27
|
+
- enable brotli in nginx.conf
|
|
28
|
+
- You'll also need to use an existing Brotli-enabled nginx image or build your own from source.
|
|
29
|
+
- Enabled gzip globally in `nginx.conf` and removed `gzip.conf` (compress during build).
|
|
30
|
+
|
|
31
|
+
- StylinNutin
|
|
32
|
+
- Added utility classes `u-text-center`, `u-text-right`, `u-text-left`, `u-font-primary`
|
|
33
|
+
- box-shadow variables now use `$primary-color`
|
|
34
|
+
|
|
35
|
+
- TestinNutin
|
|
36
|
+
- Now applies `setupJsdom()` beforeAll (was beforeEach) and `teardownJsdom()` afterAll (was afterEach) in `test-queue.js` (improved efficiency / speed)
|
|
37
|
+
|
|
3
38
|
## V1.2.3
|
|
4
39
|
|
|
5
40
|
- `nginx.conf` : fixed multi-line CSP map warning (single line map)
|
|
@@ -69,7 +104,7 @@
|
|
|
69
104
|
- Improved deployment tools Dockerfile (nginx conf + gzip)
|
|
70
105
|
|
|
71
106
|
- stylin-nutin (generator)
|
|
72
|
-
- When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `
|
|
107
|
+
- When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `styles/components/_index.scss`).
|
|
73
108
|
|
|
74
109
|
## V1.1.0
|
|
75
110
|
|
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# [nutin](https://www.nutin.org)
|
|
2
2
|
|
|
3
3
|
**Build SPAs by understanding SPAs. A minimal frontend toolkit crafted for learning and lean applications.**
|
|
4
|
-
Includes essential features out of the box
|
|
5
|
-
*TypeScript
|
|
4
|
+
Includes essential features out of the box.
|
|
5
|
+
*Pure TypeScript, SASS, and DOM-driven components — zero runtime dependencies.*
|
|
6
6
|
|
|
7
|
-
- Required : Node >=18
|
|
7
|
+
- Required : Node >=18
|
|
8
|
+
- **JSDOM requirement** (testin-nutin companion) : `{ node: '^20.19.0 || ^22.12.0 || >=24.0.0' }`
|
|
8
9
|
|
|
9
10
|
## Install
|
|
10
11
|
|
|
@@ -19,31 +20,38 @@ npm install -g @nutin/cli
|
|
|
19
20
|
# create a new app
|
|
20
21
|
create-nutin-app # or
|
|
21
22
|
nutin-new
|
|
23
|
+
|
|
24
|
+
# without global installation
|
|
25
|
+
npx @nutin/cli
|
|
22
26
|
```
|
|
23
27
|
|
|
24
28
|
- Flags
|
|
25
29
|
|
|
26
30
|
```bash
|
|
27
|
-
# argument: project-name
|
|
31
|
+
# argument: project-name (prompt if not provided)
|
|
32
|
+
|
|
33
|
+
# Help
|
|
34
|
+
-h --help
|
|
28
35
|
|
|
36
|
+
# Defaults settings - see below
|
|
29
37
|
-d --defaults
|
|
38
|
+
|
|
39
|
+
# Package manager
|
|
30
40
|
-pm --package-manager <manager> # npm (Default), yarn, pnpm, bun
|
|
31
41
|
|
|
42
|
+
# Features
|
|
43
|
+
--template # (Default) Use external templates
|
|
32
44
|
--no-template # Use inline templates
|
|
45
|
+
--stylin-nutin # (Default) Use built-in SCSS utility classes & mixins
|
|
33
46
|
--no-stylin-nutin # Do not use built-in SCSS utility
|
|
34
47
|
--testin-nutin # Use testin-nutin toolkit
|
|
35
|
-
--no-i18n # Do not use i18n & json-based content
|
|
36
|
-
--transition # Use animated view transitions. Note: May interfere with CSS `position: fixed`, `z-index`...
|
|
37
|
-
|
|
38
|
-
# Defaults
|
|
39
|
-
--template # (Default) Use external templates
|
|
40
|
-
--stylin-nutin # (Default) Use built-in SCSS utility classes & mixins
|
|
41
|
-
--i18n # (Default) Use i18n & json-based content
|
|
42
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`...
|
|
43
54
|
--no-transition # (Default) Do not use animated view transitions
|
|
44
|
-
|
|
45
|
-
# Help
|
|
46
|
-
-h --help
|
|
47
55
|
```
|
|
48
56
|
|
|
49
57
|
## [First app tutorial](https://www.nutin.org/tutorial)
|
|
@@ -51,4 +59,4 @@ nutin-new
|
|
|
51
59
|
## 📄 Docs
|
|
52
60
|
|
|
53
61
|
- Docs on [website](https://www.nutin.org).
|
|
54
|
-
- Docs on [
|
|
62
|
+
- Docs on [repository](https://github.com/LoisKOUNINEF/nutin/tree/main/docs).
|
package/lib/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { program } from 'commander';
|
|
2
2
|
import { print } from './src/print.mjs';
|
|
3
3
|
import { promptUser } from './src/prompts.mjs';
|
|
4
|
-
import { createProject } from './src/
|
|
5
|
-
import { displaySuccessMessage
|
|
4
|
+
import { createProject } from './src/project-generator.mjs';
|
|
5
|
+
import { displaySuccessMessage } from './src/utils.mjs';
|
|
6
|
+
import { packageVersion } from './src/version.mjs';
|
|
6
7
|
|
|
7
8
|
export async function createApp() {
|
|
8
9
|
program
|
|
@@ -14,8 +15,10 @@ export async function createApp() {
|
|
|
14
15
|
.option('--no-template', 'Do not use external templates')
|
|
15
16
|
.option('--stylin-nutin', '(Default) Use built-in SCSS utility classes & mixins')
|
|
16
17
|
.option('--no-stylin-nutin', 'Do not use built-in SCSS utility')
|
|
17
|
-
.option('--i18n', '
|
|
18
|
-
.option('--no-i18n', 'Do not use i18n & json-based content')
|
|
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')
|
|
19
22
|
.option('--testin-nutin', 'Use testin-nutin toolkit')
|
|
20
23
|
.option('--no-testin-nutin', '(Default) Do not use testin-nutin toolkit')
|
|
21
24
|
.option('--transition', 'Use animated view transitions\nNote: May interfere with CSS `position: fixed`, `z-index`...')
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getCiCommand } from './package-manager.mjs';
|
|
2
|
+
import { packageVersion } from './version.mjs';
|
|
3
|
+
|
|
4
|
+
export const defaults = {
|
|
5
|
+
projectName: 'my-nutin-app',
|
|
6
|
+
packageManager: 'npm',
|
|
7
|
+
stylinNutin: true,
|
|
8
|
+
template: true,
|
|
9
|
+
i18n: false,
|
|
10
|
+
deployHelper: false,
|
|
11
|
+
testinNutin: false,
|
|
12
|
+
transition: false
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class ContextBuilder {
|
|
16
|
+
buildContext(answers) {
|
|
17
|
+
const version = packageVersion;
|
|
18
|
+
const ciCommand = getCiCommand(answers.packageManager);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
projectName: answers.projectName,
|
|
22
|
+
packageManager: answers.packageManager,
|
|
23
|
+
|
|
24
|
+
template: answers.template ?? defaults.template,
|
|
25
|
+
stylinNutin: answers.stylinNutin ?? defaults.stylinNutin,
|
|
26
|
+
i18n: answers.i18n ?? defaults.i18n,
|
|
27
|
+
deployHelper: answers.deployHelper ?? defaults.deployHelper,
|
|
28
|
+
testinNutin: answers.testinNutin ?? defaults.testinNutin,
|
|
29
|
+
transition: answers.transition ?? defaults.transition,
|
|
30
|
+
|
|
31
|
+
ciCommand: ciCommand,
|
|
32
|
+
version: version
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -3,9 +3,6 @@ import { fileURLToPath } from 'url';
|
|
|
3
3
|
import * as fsExtra from 'fs-extra';
|
|
4
4
|
import { TemplateCompiler } from './template-compiler.mjs';
|
|
5
5
|
import { print } from './print.mjs';
|
|
6
|
-
import { initializeGit } from './git-manager.mjs';
|
|
7
|
-
import { installDependencies, generatePackageJson, generateTsconfigJson, getCiCommand } from './package-manager.mjs';
|
|
8
|
-
import { packageVersion } from './utils.mjs';
|
|
9
6
|
|
|
10
7
|
const fs = fsExtra.default;
|
|
11
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -18,36 +15,13 @@ const BINARY_EXTENSIONS = new Set([
|
|
|
18
15
|
'.pdf', '.zip', '.tar', '.gz'
|
|
19
16
|
]);
|
|
20
17
|
|
|
21
|
-
export class
|
|
18
|
+
export class FileGenerator {
|
|
22
19
|
constructor() {
|
|
23
20
|
this.compiler = new TemplateCompiler();
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
async
|
|
27
|
-
const
|
|
28
|
-
const projectPath = path.join(process.cwd(), projectName);
|
|
29
|
-
|
|
30
|
-
await this.validateProjectPath(projectPath, projectName);
|
|
31
|
-
await this.setupProjectStructure(projectPath, answers);
|
|
32
|
-
await this.runPostSetupTasks(projectPath, answers);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async validateProjectPath(projectPath, projectName) {
|
|
36
|
-
if (await fs.pathExists(projectPath)) {
|
|
37
|
-
throw new Error(`Directory ${projectName} already exists`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async setupProjectStructure(projectPath, answers) {
|
|
42
|
-
print.boldHead(`\n📁 Creating project in ${projectPath}...`);
|
|
43
|
-
|
|
44
|
-
await fs.ensureDir(projectPath);
|
|
45
|
-
await this.generateProjectFromTemplates(projectPath, answers);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async generateProjectFromTemplates(projectPath, answers) {
|
|
49
|
-
const context = this.buildContext(answers);
|
|
50
|
-
const templateDir = this.getTemplateDirectory(answers);
|
|
23
|
+
async generateProjectFromTemplates(projectPath, context) {
|
|
24
|
+
const templateDir = this.getTemplateDirectory();
|
|
51
25
|
|
|
52
26
|
print.section('📝 Processing templates...');
|
|
53
27
|
await this.processTemplateDirectory(templateDir, projectPath, context);
|
|
@@ -55,31 +29,8 @@ export class ProjectGenerator {
|
|
|
55
29
|
await this.processFeatureTemplates(projectPath, context);
|
|
56
30
|
}
|
|
57
31
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const ciCommand = getCiCommand(answers.packageManager);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
projectName: answers.projectName,
|
|
64
|
-
description: answers.description || `A modern web application`,
|
|
65
|
-
|
|
66
|
-
template: answers.template || false,
|
|
67
|
-
stylinNutin: answers.stylinNutin || false,
|
|
68
|
-
i18n: answers.i18n || false,
|
|
69
|
-
transition: answers.transition || false,
|
|
70
|
-
testinNutin: answers.testinNutin || false,
|
|
71
|
-
|
|
72
|
-
hasFeatures: answers.template || answers.stylinNutin || answers.i18n || answers.transition || answers.testinNutin,
|
|
73
|
-
|
|
74
|
-
year: new Date().getFullYear(),
|
|
75
|
-
packageManager: answers.packageManager || 'npm',
|
|
76
|
-
ciCommand: ciCommand,
|
|
77
|
-
version: version
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
getTemplateDirectory(answers) {
|
|
82
|
-
return path.join(__dirname, '../../templates/base');
|
|
32
|
+
getTemplateDirectory() {
|
|
33
|
+
return path.join(__dirname, '..', '..', 'templates', 'base');
|
|
83
34
|
}
|
|
84
35
|
|
|
85
36
|
async processTemplateDirectory(templateDir, outputDir, context) {
|
|
@@ -104,6 +55,27 @@ export class ProjectGenerator {
|
|
|
104
55
|
}
|
|
105
56
|
}
|
|
106
57
|
|
|
58
|
+
async processFeatureTemplates(projectPath, context) {
|
|
59
|
+
const featuresDir = path.join(__dirname, '..', '..', 'templates', 'features');
|
|
60
|
+
|
|
61
|
+
if (!(await fs.pathExists(featuresDir))) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const features = [ 'template', 'i18n', 'stylinNutin', 'testinNutin', 'deployHelper' ];
|
|
66
|
+
|
|
67
|
+
for (const feature of features) {
|
|
68
|
+
if (context[feature]) {
|
|
69
|
+
const featureTemplateDir = path.join(featuresDir, feature);
|
|
70
|
+
|
|
71
|
+
if (await fs.pathExists(featureTemplateDir)) {
|
|
72
|
+
print.info(` 🔧 Adding ${feature} feature...`);
|
|
73
|
+
await this.processTemplateDirectory(featureTemplateDir, projectPath, context);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
107
79
|
async processTemplateFile(templatePath, outputDir, fileName, context) {
|
|
108
80
|
if (fileName.includes("test.js") && !(context.testinNutin)) {
|
|
109
81
|
return;
|
|
@@ -134,76 +106,4 @@ export class ProjectGenerator {
|
|
|
134
106
|
// print.info(`📄 Copied: ${fileName}`);
|
|
135
107
|
}
|
|
136
108
|
}
|
|
137
|
-
|
|
138
|
-
async processFeatureTemplates(projectPath, context) {
|
|
139
|
-
const featuresDir = path.join(__dirname, '../../templates/features');
|
|
140
|
-
|
|
141
|
-
if (!(await fs.pathExists(featuresDir))) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const features = [ 'template', 'i18n', 'stylinNutin', 'testinNutin' ];
|
|
146
|
-
|
|
147
|
-
for (const feature of features) {
|
|
148
|
-
if (context[feature]) {
|
|
149
|
-
const featureTemplateDir = path.join(featuresDir, feature);
|
|
150
|
-
|
|
151
|
-
if (await fs.pathExists(featureTemplateDir)) {
|
|
152
|
-
print.info(`🔧 Adding ${feature} feature...`);
|
|
153
|
-
await this.processTemplateDirectory(featureTemplateDir, projectPath, context);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async generateJsonFiles(projectPath, answers) {
|
|
160
|
-
await generatePackageJson(projectPath, answers);
|
|
161
|
-
await generateTsconfigJson(projectPath, answers);
|
|
162
|
-
if (answers.i18n) this.generateConfigFiles(projectPath);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async generateConfigFiles(projectPath) {
|
|
166
|
-
const configPath = path.join(projectPath, 'config');
|
|
167
|
-
await fs.ensureDir(configPath);
|
|
168
|
-
const languages = {
|
|
169
|
-
"languages": ["en"],
|
|
170
|
-
"defaultLanguage": "en"
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
await fs.writeJSON(path.join(configPath, 'languages.json'), languages, { spaces: 2 });
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async runPostSetupTasks(projectPath, answers) {
|
|
177
|
-
await this.generateJsonFiles(projectPath, answers);
|
|
178
|
-
await initializeGit(projectPath);
|
|
179
|
-
|
|
180
|
-
await installDependencies(projectPath, answers.packageManager);
|
|
181
|
-
|
|
182
|
-
await this.runSetupScripts(projectPath, answers);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async runSetupScripts(projectPath, answers) {
|
|
186
|
-
print.section('⚙️ Running setup scripts...');
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const setupScriptsTemplate = path.join(__dirname, '..', '..', 'templates', 'scripts');
|
|
190
|
-
|
|
191
|
-
if (await fs.pathExists(setupScriptsTemplate)) {
|
|
192
|
-
const scriptsDir = path.join(projectPath, 'scripts');
|
|
193
|
-
const context = this.buildContext(answers);
|
|
194
|
-
|
|
195
|
-
await fs.ensureDir(scriptsDir);
|
|
196
|
-
await this.processTemplateDirectory(setupScriptsTemplate, scriptsDir, context);
|
|
197
|
-
}
|
|
198
|
-
} catch (error) {
|
|
199
|
-
print.boldError('⚠️ Warning: Could not set up build scripts');
|
|
200
|
-
console.error(error);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export const projectGenerator = new ProjectGenerator();
|
|
206
|
-
|
|
207
|
-
export async function createProject(answers) {
|
|
208
|
-
return projectGenerator.createProject(answers);
|
|
209
109
|
}
|
package/lib/src/git-manager.mjs
CHANGED
|
@@ -2,6 +2,6 @@ import { print } from './print.mjs';
|
|
|
2
2
|
import { promiseExec } from './utils.mjs';
|
|
3
3
|
|
|
4
4
|
export async function initializeGit(projectPath) {
|
|
5
|
-
print.section('
|
|
5
|
+
print.section('⚙️ Initializing Git repository...');
|
|
6
6
|
await promiseExec('git init', { cwd: projectPath });
|
|
7
7
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { generatePackageJson, generateTsconfigJson } from './package-manager.mjs';
|
|
4
|
+
|
|
5
|
+
const fs = fsExtra.default;
|
|
6
|
+
|
|
7
|
+
export class JsonGenerator {
|
|
8
|
+
async generateJsonFiles(projectPath, context) {
|
|
9
|
+
await generatePackageJson(projectPath, context);
|
|
10
|
+
await generateTsconfigJson(projectPath, context);
|
|
11
|
+
this.generateConfigFiles(projectPath, context);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async generateConfigFiles(projectPath, context) {
|
|
15
|
+
const configPath = path.join(projectPath, 'config');
|
|
16
|
+
|
|
17
|
+
if (context.i18n) {
|
|
18
|
+
await fs.ensureDir(configPath);
|
|
19
|
+
await this.generateLanguageConfig(configPath);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async generateLanguageConfig(configPath) {
|
|
24
|
+
const languages = {
|
|
25
|
+
"languages": [ "en" ],
|
|
26
|
+
"defaultLanguage": "en"
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
await fs.writeJSON(path.join(configPath, 'languages.json'), languages, { spaces: 2 });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -6,7 +6,7 @@ import * as path from 'path';
|
|
|
6
6
|
const fs = fsExtra.default;
|
|
7
7
|
|
|
8
8
|
export async function installDependencies(projectPath, packageManager) {
|
|
9
|
-
print.section(
|
|
9
|
+
print.section(`📦 Installing dependencies with ${packageManager}...`);
|
|
10
10
|
|
|
11
11
|
const installCommand = getInstallCommand(packageManager);
|
|
12
12
|
await promiseExec(installCommand, { cwd: projectPath });
|
|
@@ -38,8 +38,8 @@ function getInstallCommand(packageManager) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function getScripts(
|
|
42
|
-
const { testinNutin, packageManager, projectName } =
|
|
41
|
+
function getScripts(context) {
|
|
42
|
+
const { testinNutin, packageManager, projectName, deployHelper } = context;
|
|
43
43
|
|
|
44
44
|
const baseScripts = {
|
|
45
45
|
"build": "node tools/builder/builder.js",
|
|
@@ -47,30 +47,33 @@ function getScripts(answers) {
|
|
|
47
47
|
"serve:only": "node tools/dev/serve.js",
|
|
48
48
|
"serve": `${packageManager} run build && ${packageManager} run serve:only`,
|
|
49
49
|
"dev": "node tools/dev/dev-serve.js",
|
|
50
|
-
"generate": "node tools/generator/generator.js"
|
|
50
|
+
"generate": "node tools/generator/generator.js"
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const deployHelperScripts = {
|
|
51
54
|
"docker:build": `docker build -t ${projectName} -f tools/deployment/Dockerfile .`,
|
|
52
|
-
"docker:run": `docker run -p 9090:9090
|
|
55
|
+
"docker:run": `docker run -p 9090:9090 ${projectName}:latest`,
|
|
53
56
|
"patch": `${packageManager} version patch -m 'CI/CD: Bump version to %s'`,
|
|
54
57
|
"minor": `${packageManager} version minor -m 'CI/CD: Bump version to %s'`,
|
|
55
58
|
"major": `${packageManager} version major -m 'CI/CD: Bump version to %s'`
|
|
56
|
-
}
|
|
59
|
+
}
|
|
57
60
|
|
|
58
61
|
const testinNutinScripts = {
|
|
59
62
|
"test": "node testin-nutin/runner.js",
|
|
60
63
|
"test:rebuild": `${packageManager} run build && ${packageManager} run test`,
|
|
61
64
|
"test:watch": `${packageManager} run build && node testin-nutin/watch-tests.js`
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let scripts = { ...baseScripts };
|
|
68
|
+
|
|
69
|
+
if (testinNutin) scripts = { ...scripts, ...testinNutinScripts };
|
|
70
|
+
if (deployHelper) scripts = { ...scripts, ...deployHelperScripts };
|
|
71
|
+
|
|
72
|
+
return scripts;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
export async function generatePackageJson(projectPath,
|
|
73
|
-
const { testinNutin, projectName
|
|
75
|
+
export async function generatePackageJson(projectPath, context) {
|
|
76
|
+
const { testinNutin, projectName } = context;
|
|
74
77
|
|
|
75
78
|
const devDependencies = {
|
|
76
79
|
"chokidar": "^4.0.3",
|
|
@@ -84,7 +87,7 @@ export async function generatePackageJson(projectPath, answers) {
|
|
|
84
87
|
})
|
|
85
88
|
};
|
|
86
89
|
|
|
87
|
-
const scripts = getScripts(
|
|
90
|
+
const scripts = getScripts(context);
|
|
88
91
|
|
|
89
92
|
const packageJson = {
|
|
90
93
|
"name": projectName,
|
|
@@ -107,7 +110,7 @@ export async function generatePackageJson(projectPath, answers) {
|
|
|
107
110
|
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
export async function generateTsconfigJson(projectPath,
|
|
113
|
+
export async function generateTsconfigJson(projectPath, context) {
|
|
111
114
|
const tsconfig = {
|
|
112
115
|
"compilerOptions": {
|
|
113
116
|
"baseUrl": "./",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import * as fsExtra from 'fs-extra';
|
|
3
|
+
import { print } from './print.mjs';
|
|
4
|
+
import { initializeGit } from './git-manager.mjs';
|
|
5
|
+
import { installDependencies } from './package-manager.mjs';
|
|
6
|
+
import { FileGenerator } from './file-generator.mjs';
|
|
7
|
+
import { JsonGenerator } from './json-generator.mjs';
|
|
8
|
+
import { ContextBuilder } from './context-builder.mjs';
|
|
9
|
+
|
|
10
|
+
const fs = fsExtra.default;
|
|
11
|
+
|
|
12
|
+
export class ProjectGenerator {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.fileGenerator = new FileGenerator();
|
|
15
|
+
this.jsonGenerator = new JsonGenerator();
|
|
16
|
+
this.builder = new ContextBuilder();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async createProject(answers) {
|
|
20
|
+
const { projectName } = answers;
|
|
21
|
+
const projectPath = path.join(process.cwd(), projectName);
|
|
22
|
+
const context = this.builder.buildContext(answers);
|
|
23
|
+
|
|
24
|
+
await this.validateProjectPath(projectPath, projectName);
|
|
25
|
+
await this.setupProjectStructure(projectPath, context);
|
|
26
|
+
await this.runPostSetupTasks(projectPath, context);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async validateProjectPath(projectPath, projectName) {
|
|
30
|
+
if (await fs.pathExists(projectPath)) {
|
|
31
|
+
throw new Error(`Directory ${projectName} already exists`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async setupProjectStructure(projectPath, context) {
|
|
36
|
+
print.boldHead(`\n📁 Creating project in ${projectPath}...\n`);
|
|
37
|
+
|
|
38
|
+
await fs.ensureDir(projectPath);
|
|
39
|
+
await this.fileGenerator.generateProjectFromTemplates(projectPath, context);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async runPostSetupTasks(projectPath, context) {
|
|
43
|
+
await this.jsonGenerator.generateJsonFiles(projectPath, context);
|
|
44
|
+
await initializeGit(projectPath);
|
|
45
|
+
await installDependencies(projectPath, context.packageManager);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const projectGenerator = new ProjectGenerator();
|
|
50
|
+
|
|
51
|
+
export async function createProject(answers) {
|
|
52
|
+
return projectGenerator.createProject(answers);
|
|
53
|
+
}
|
package/lib/src/prompts.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
|
+
import { defaults } from './context-builder.mjs';
|
|
2
3
|
|
|
3
4
|
export async function promptUser(initialName, cliOptions = {}) {
|
|
5
|
+
|
|
4
6
|
let projectName = initialName;
|
|
5
7
|
if (!projectName) {
|
|
6
8
|
const nameAnswer = await inquirer.prompt([
|
|
@@ -8,7 +10,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
8
10
|
type: 'input',
|
|
9
11
|
name: 'projectName',
|
|
10
12
|
message: 'Project name:',
|
|
11
|
-
default:
|
|
13
|
+
default: defaults.projectName,
|
|
12
14
|
validate: validateProjectName
|
|
13
15
|
}
|
|
14
16
|
]);
|
|
@@ -23,7 +25,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
23
25
|
name: 'packageManager',
|
|
24
26
|
message: 'Package manager:',
|
|
25
27
|
choices: ['npm', 'yarn', 'pnpm', 'bun'],
|
|
26
|
-
default:
|
|
28
|
+
default: defaults.packageManager
|
|
27
29
|
}
|
|
28
30
|
]);
|
|
29
31
|
packageManager = pmAnswer.packageManager;
|
|
@@ -33,11 +35,12 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
33
35
|
return {
|
|
34
36
|
projectName,
|
|
35
37
|
packageManager,
|
|
36
|
-
stylinNutin: cliOptions.stylinNutin
|
|
37
|
-
template: cliOptions.template
|
|
38
|
-
i18n: cliOptions.i18n
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
stylinNutin: cliOptions.stylinNutin,
|
|
39
|
+
template: cliOptions.template,
|
|
40
|
+
i18n: cliOptions.i18n,
|
|
41
|
+
deployHelper: cliOptions.deployHelper,
|
|
42
|
+
testinNutin: cliOptions.testinNutin,
|
|
43
|
+
transition: cliOptions.transitions
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
46
|
|
|
@@ -48,7 +51,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
48
51
|
type: 'confirm',
|
|
49
52
|
name: 'stylinNutin',
|
|
50
53
|
message: 'Use built-in SCSS utility classes ?',
|
|
51
|
-
default:
|
|
54
|
+
default: defaults.stylinNutin
|
|
52
55
|
});
|
|
53
56
|
}
|
|
54
57
|
|
|
@@ -57,7 +60,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
57
60
|
type: 'confirm',
|
|
58
61
|
name: 'template',
|
|
59
62
|
message: 'Use external templates ?',
|
|
60
|
-
default:
|
|
63
|
+
default: defaults.template
|
|
61
64
|
});
|
|
62
65
|
}
|
|
63
66
|
|
|
@@ -66,16 +69,16 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
66
69
|
type: 'confirm',
|
|
67
70
|
name: 'i18n',
|
|
68
71
|
message: 'Use i18n with json-based content ?',
|
|
69
|
-
default:
|
|
72
|
+
default: defaults.i18n
|
|
70
73
|
});
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
if (cliOptions.
|
|
76
|
+
if (cliOptions.deployHelper === undefined) {
|
|
74
77
|
questions.push({
|
|
75
78
|
type: 'confirm',
|
|
76
|
-
name: '
|
|
77
|
-
message: 'Use
|
|
78
|
-
default:
|
|
79
|
+
name: 'deployHelper',
|
|
80
|
+
message: 'Use Docker & deployment helpers ?',
|
|
81
|
+
default: defaults.deployHelper
|
|
79
82
|
});
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -84,7 +87,16 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
84
87
|
type: 'confirm',
|
|
85
88
|
name: 'testinNutin',
|
|
86
89
|
message: 'Use testin-nutin toolkit?',
|
|
87
|
-
default:
|
|
90
|
+
default: defaults.testinNutin
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (cliOptions.transition === undefined) {
|
|
95
|
+
questions.push({
|
|
96
|
+
type: 'confirm',
|
|
97
|
+
name: 'transition',
|
|
98
|
+
message: 'Use animated view transitions?',
|
|
99
|
+
default: defaults.transition
|
|
88
100
|
});
|
|
89
101
|
}
|
|
90
102
|
|
|
@@ -96,6 +108,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
96
108
|
template: cliOptions.template ?? answers.template,
|
|
97
109
|
stylinNutin: cliOptions.stylinNutin ?? answers.stylinNutin,
|
|
98
110
|
i18n: cliOptions.i18n ?? answers.i18n,
|
|
111
|
+
deployHelper: cliOptions.deployHelper ?? answers.deployHelper,
|
|
99
112
|
transition: cliOptions.transition ?? answers.transition,
|
|
100
113
|
testinNutin: cliOptions.testinNutin ?? answers.testinNutin
|
|
101
114
|
};
|
|
@@ -36,7 +36,7 @@ export class TemplateCompiler {
|
|
|
36
36
|
|
|
37
37
|
Handlebars.registerHelper('hasAnyFeature', function(options) {
|
|
38
38
|
const context = options.data.root;
|
|
39
|
-
return context.i18n || context.template || context.
|
|
39
|
+
return context.i18n || context.template || context.testinNutin || context.stylinNutin
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|