@nutin/cli 1.0.1 → 1.2.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 +58 -0
- package/README.md +47 -1
- package/lib/index.mjs +6 -6
- package/lib/src/file-generator.mjs +11 -6
- package/lib/src/package-manager.mjs +54 -49
- package/lib/src/prompts.mjs +11 -11
- package/lib/src/utils.mjs +2 -1
- package/package.json +2 -1
- package/templates/base/.dockerignore.hbs +10 -1
- package/templates/base/Dockerfile.hbs +28 -8
- package/templates/base/README.md.hbs +23 -0
- package/templates/base/nginx.conf.hbs +43 -0
- package/templates/base/src/app/views/home/home.view.ts.hbs +1 -1
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +2 -1
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +30 -7
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +3 -2
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +55 -27
- package/templates/base/src/core/services/http-client/http-client.ts.hbs +24 -17
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +25 -10
- package/templates/base/src/core/services/router/router.ts.hbs +1 -0
- package/templates/base/src/index.html.hbs +1 -2
- package/templates/base/src/libs/popover/popover.ts.hbs +2 -1
- package/templates/base/src/styles/main.scss.hbs +2 -1
- package/templates/base/tools/builder/builder.js.hbs +17 -27
- package/templates/base/tools/builder/core/copy-static.js.hbs +16 -20
- package/templates/base/tools/builder/core/esbuild.js.hbs +36 -0
- package/templates/base/tools/builder/core/finalize-build.js.hbs +24 -0
- package/templates/base/tools/builder/core/minify-html.js.hbs +60 -0
- package/templates/base/tools/builder/core/paths.js.hbs +19 -0
- package/templates/base/tools/builder/core/sass.config.js.hbs +16 -9
- package/templates/base/tools/builder/core/tsc.js.hbs +8 -0
- package/templates/base/tools/builder/core/validate-html.js.hbs +26 -11
- package/templates/base/tools/dev.js.hbs +43 -0
- package/templates/base/tools/generator/generator.js.hbs +9 -2
- package/templates/base/tools/generator/handle-file.js.hbs +23 -0
- package/templates/base/tools/generator/templates/index.js.hbs +3 -0
- package/templates/base/tools/serve.js.hbs +23 -0
- package/templates/base/tools/utils/export-flags.js.hbs +5 -0
- package/templates/base/tools/utils/get-files-recursive.js.hbs +1 -3
- package/templates/base/tools/utils/index.js.hbs +5 -1
- package/templates/base/tools/utils/prompt-boolean.js.hbs +15 -0
- package/templates/base/tools/utils/run-command.js.hbs +5 -0
- package/templates/base/tools/utils/run-script.js.hbs +22 -0
- package/templates/base/tools/watcher.js.hbs +44 -0
- package/templates/features/i18n/tools/builder/core/build-i18n.js.hbs +11 -16
- package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +1 -1
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +3 -0
- package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +4 -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 +13 -26
- package/templates/features/template/tools/watcher.js.hbs +0 -27
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## V1.0.1
|
|
4
|
+
|
|
5
|
+
- Fixed typo issue when using i18n feature
|
|
6
|
+
|
|
7
|
+
## V1.0.2
|
|
8
|
+
|
|
9
|
+
- Partially fixed `npm run dev` script when using internal templates. *Page still needs to be reload manually from time to time.*
|
|
10
|
+
|
|
11
|
+
## V1.1.0
|
|
12
|
+
|
|
13
|
+
- Added index access in CatalogConfig. Use with `config.index`.
|
|
14
|
+
```typescript
|
|
15
|
+
interface CatalogItemBase {
|
|
16
|
+
index: number;
|
|
17
|
+
}
|
|
18
|
+
type CatalogItemObject<T extends object> = T & CatalogItemBase;
|
|
19
|
+
interface CatalogItemPrimitive extends CatalogItemBase {
|
|
20
|
+
value: string | number | boolean | null | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// type safety
|
|
24
|
+
type CatalogItemConfig<T = any> =
|
|
25
|
+
T extends object ? CatalogItemObject<T> : CatalogItemPrimitive;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Notes:** *Primitive data arrays (string, number, etc) needs to be accessed with `config.value`.*
|
|
29
|
+
|
|
30
|
+
## V1.2.0
|
|
31
|
+
|
|
32
|
+
- Fixed `Cannot GET '/'` ponctual issue on changes with `npm run dev` command. Added middleware to reload nested routes.
|
|
33
|
+
|
|
34
|
+
- Improved `data-optional` tag:
|
|
35
|
+
> Can now specify which attribute to check with `data-optional="attrName"` (`src`, `href`, ...)
|
|
36
|
+
> Now works for `img`, `input`, etc
|
|
37
|
+
|
|
38
|
+
- Event Bus :
|
|
39
|
+
> Tracks both event name + callback.
|
|
40
|
+
> cleanupEventListeners now works correctly.
|
|
41
|
+
> New property: `once`. `once` subscriptions automatically remove themselves after first call.
|
|
42
|
+
|
|
43
|
+
- HTTP client :
|
|
44
|
+
> Optional request / response interceptors.
|
|
45
|
+
> Full timeout / abort controller support preserved.
|
|
46
|
+
> onDestroy now clears interceptors to prevent memory leaks.
|
|
47
|
+
|
|
48
|
+
- Improved builder
|
|
49
|
+
|
|
50
|
+
Implemented esbuild.
|
|
51
|
+
Added flags `--bundle` (for production-ready build - equivalent to `npm run build:prod`) and `--log` (verbose output).
|
|
52
|
+
Commands: `npm run build`, `npm run build:prod`, `npm run serve:only`, `npm run serve`, `npm run dev`.
|
|
53
|
+
|
|
54
|
+
*Note: `--prod` or `--production` will set NODE_ENV to production, and have the same effect as `--bundle`.* `const isProd = process.env.npm_config_bundle || process.env.NODE_ENV === 'production';`
|
|
55
|
+
|
|
56
|
+
- Improved default Dockerfile (nginx conf + gzip)
|
|
57
|
+
|
|
58
|
+
**NOTE :***Update nginx.conf as needed. Example: CSP connect-src if the app makes API calls.*
|
package/README.md
CHANGED
|
@@ -2,4 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
**A lightweight, highly tweakable frontend toolkit, crafted for building lean, dynamic single-page applications without the complexity of full-scale frameworks.**
|
|
4
4
|
Includes essential features out of the box, with zero runtime dependencies.
|
|
5
|
-
*TypeScript/Javascript,SASS,HTML*
|
|
5
|
+
*TypeScript / Javascript, SASS, HTML*
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# install package globally
|
|
11
|
+
npm install -g @nutin/cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## New App
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# create a new app
|
|
18
|
+
create-nutin-app # or
|
|
19
|
+
nutin-new
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Flags
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# argument: project-name
|
|
26
|
+
|
|
27
|
+
-d --defaults
|
|
28
|
+
-pm --package-manager <manager> # npm (default), yarn, pnpm
|
|
29
|
+
|
|
30
|
+
--template # Use external templates
|
|
31
|
+
--no-stylin-nutin # Do not use built-in SCSS utility
|
|
32
|
+
--testin-nutin # Use testin-nutin toolkit
|
|
33
|
+
--i18n # Use i18n & json-based content
|
|
34
|
+
--transition # Use animated view transitions. Note: May interfere with CSS `position: fixed`, `z-index`...
|
|
35
|
+
|
|
36
|
+
# Defaults
|
|
37
|
+
--no-template # (Default) Do not use external templates
|
|
38
|
+
--stylin-nutin # (Default) Use built-in SCSS utility classes & mixins
|
|
39
|
+
--no-i18n # (Default) Do not use i18n & json-based content
|
|
40
|
+
--no-transition # (Default) Do not use animated view transitions
|
|
41
|
+
--no-testin-nutin # (Default) Do not use testin-nutin toolkit
|
|
42
|
+
|
|
43
|
+
# Help
|
|
44
|
+
-h --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## [First app tutorial](https://www.nutin.org/tutorial)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
- Visit the [website](https://www.nutin.org)
|
|
51
|
+
- [Project repository](https://github.com/LoisKOUNINEF/nutin)
|
package/lib/index.mjs
CHANGED
|
@@ -6,20 +6,20 @@ import { displaySuccessMessage } from './src/utils.mjs';
|
|
|
6
6
|
|
|
7
7
|
export async function createApp() {
|
|
8
8
|
program
|
|
9
|
-
.version('1.
|
|
9
|
+
.version('1.1.0')
|
|
10
10
|
.argument('[projectName]', 'Name of the project')
|
|
11
11
|
.option('-d, --defaults', 'Use default options')
|
|
12
|
-
.option('-pm, --package-manager <manager>', 'Specify package manager (npm, yarn, pnpm)')
|
|
13
|
-
.option('--template', 'Use external templates')
|
|
14
|
-
.option('--no-template', '
|
|
12
|
+
.option('-pm, --package-manager <manager>', 'Specify package manager (npm, yarn, pnpm, bun)')
|
|
13
|
+
.option('--template', '(Default) Use external templates')
|
|
14
|
+
.option('--no-template', 'Do not use external templates')
|
|
15
15
|
.option('--stylin-nutin', '(Default) Use built-in SCSS utility classes & mixins')
|
|
16
16
|
.option('--no-stylin-nutin', 'Do not use built-in SCSS utility')
|
|
17
17
|
.option('--i18n', 'Use i18n & json-based content')
|
|
18
18
|
.option('--no-i18n', '(Default) Do not use i18n & json-based content')
|
|
19
|
+
.option('--testin-nutin', '(Default) Use testin-nutin toolkit')
|
|
20
|
+
.option('--no-testin-nutin', 'Do not use testin-nutin toolkit')
|
|
19
21
|
.option('--transition', 'Use animated view transitions\nNote: May interfere with CSS `position: fixed`, `z-index`...')
|
|
20
22
|
.option('--no-transition', '(Default) Do not use animated view transitions')
|
|
21
|
-
.option('--testin-nutin', 'Use testin-nutin toolkit')
|
|
22
|
-
.option('--no-testin-nutin', '(Default) Do not use testin-nutin toolkit')
|
|
23
23
|
.addHelpText('afterAll', '\nChaining options :\nExample: -d -pm npm --i18n --testinNutin')
|
|
24
24
|
.action(async (projectName, cliOptions) => {
|
|
25
25
|
print.blue('🚀 Welcome to nutin !');
|
|
@@ -4,7 +4,7 @@ import * as fsExtra from 'fs-extra';
|
|
|
4
4
|
import { TemplateCompiler } from './template-compiler.mjs';
|
|
5
5
|
import { print } from './print.mjs';
|
|
6
6
|
import { initializeGit } from './git-manager.mjs';
|
|
7
|
-
import { installDependencies, generatePackageJson, generateTsconfigJson } from './package-manager.mjs';
|
|
7
|
+
import { installDependencies, generatePackageJson, generateTsconfigJson, getCiCommand } from './package-manager.mjs';
|
|
8
8
|
|
|
9
9
|
const fs = fsExtra.default;
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -54,7 +54,10 @@ export class ProjectGenerator {
|
|
|
54
54
|
await this.processFeatureTemplates(projectPath, context);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
buildContext(answers) {
|
|
57
|
+
buildContext(answers) {
|
|
58
|
+
const version = '1.2.0';
|
|
59
|
+
const ciCommand = getCiCommand(answers.packageManager);
|
|
60
|
+
|
|
58
61
|
return {
|
|
59
62
|
projectName: answers.projectName,
|
|
60
63
|
description: answers.description || `A modern web application`,
|
|
@@ -68,7 +71,9 @@ export class ProjectGenerator {
|
|
|
68
71
|
hasFeatures: answers.template || answers.stylinNutin || answers.i18n || answers.transition || answers.testinNutin,
|
|
69
72
|
|
|
70
73
|
year: new Date().getFullYear(),
|
|
71
|
-
packageManager: answers.packageManager || 'npm'
|
|
74
|
+
packageManager: answers.packageManager || 'npm',
|
|
75
|
+
ciCommand: ciCommand,
|
|
76
|
+
version: version
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -108,7 +113,7 @@ export class ProjectGenerator {
|
|
|
108
113
|
if (BINARY_EXTENSIONS.has(fileExt)) {
|
|
109
114
|
const outputPath = path.join(outputDir, fileName);
|
|
110
115
|
await fs.copy(templatePath, outputPath);
|
|
111
|
-
print.info(`📄 Copied: ${fileName}`);
|
|
116
|
+
// print.info(`📄 Copied: ${fileName}`);
|
|
112
117
|
} else if (fileName.endsWith('.hbs')) {
|
|
113
118
|
|
|
114
119
|
const outputFileName = fileName.replace('.hbs', '');
|
|
@@ -117,7 +122,7 @@ export class ProjectGenerator {
|
|
|
117
122
|
try {
|
|
118
123
|
const compiledContent = await this.compiler.compileFile(templatePath, context);
|
|
119
124
|
await fs.writeFile(outputPath, compiledContent);
|
|
120
|
-
print.info(`📝 Generated: ${outputFileName}`);
|
|
125
|
+
// print.info(`📝 Generated: ${outputFileName}`);
|
|
121
126
|
} catch (error) {
|
|
122
127
|
print.boldError(`❌ Failed to process template: ${fileName}`);
|
|
123
128
|
throw error;
|
|
@@ -125,7 +130,7 @@ export class ProjectGenerator {
|
|
|
125
130
|
} else {
|
|
126
131
|
const outputPath = path.join(outputDir, fileName);
|
|
127
132
|
await fs.copy(templatePath, outputPath);
|
|
128
|
-
print.info(`📄 Copied: ${fileName}`);
|
|
133
|
+
// print.info(`📄 Copied: ${fileName}`);
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
|
|
@@ -12,88 +12,92 @@ export async function installDependencies(projectPath, packageManager) {
|
|
|
12
12
|
await promiseExec(installCommand, { cwd: projectPath });
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function
|
|
15
|
+
export function getCiCommand(packageManager) {
|
|
16
|
+
switch (packageManager) {
|
|
17
|
+
case 'yarn':
|
|
18
|
+
return 'yarn install --frozen-lockfile';
|
|
19
|
+
case 'pnpm':
|
|
20
|
+
return 'pnpm install --frozen-lockfile';
|
|
21
|
+
case 'bun':
|
|
22
|
+
return 'bun install --frozen-lockfile'
|
|
23
|
+
default:
|
|
24
|
+
return 'npm ci';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getInstallCommand(packageManager) {
|
|
16
29
|
switch (packageManager) {
|
|
17
30
|
case 'yarn':
|
|
18
31
|
return 'yarn install';
|
|
19
32
|
case 'pnpm':
|
|
20
33
|
return 'pnpm install';
|
|
34
|
+
case 'bun':
|
|
35
|
+
return 'bun install'
|
|
21
36
|
default:
|
|
22
37
|
return 'npm install';
|
|
23
38
|
}
|
|
24
39
|
}
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
const { testinNutin,
|
|
28
|
-
|
|
29
|
-
const baseDevDeps = {
|
|
30
|
-
"concurrently": "^9.1.2",
|
|
31
|
-
"live-server": "^1.2.2",
|
|
32
|
-
"sass": "^1.89.0",
|
|
33
|
-
"typescript": "^5.8.3"
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const devDependencies = testinNutin
|
|
37
|
-
? { ...baseDevDeps, "jsdom": "^26.1.0" }
|
|
38
|
-
: baseDevDeps;
|
|
41
|
+
function getScripts(answers) {
|
|
42
|
+
const { testinNutin, packageManager } = answers;
|
|
39
43
|
|
|
40
44
|
const baseScripts = {
|
|
41
|
-
"patch":
|
|
42
|
-
"minor":
|
|
43
|
-
"major":
|
|
44
|
-
"build
|
|
45
|
-
"build
|
|
46
|
-
"serve": "
|
|
45
|
+
"patch": `${packageManager} version patch -m 'CI/CD: Bump version to %s'`,
|
|
46
|
+
"minor": `${packageManager} version minor -m 'CI/CD: Bump version to %s'`,
|
|
47
|
+
"major": `${packageManager} version major -m 'CI/CD: Bump version to %s'`,
|
|
48
|
+
"build": "node tools/builder/builder.js",
|
|
49
|
+
"build:prod": "NODE_ENV=production node tools/builder/builder.js",
|
|
50
|
+
"serve:only": "node tools/serve.js",
|
|
51
|
+
"serve": `${packageManager} run build && ${packageManager} run serve:only`,
|
|
52
|
+
"dev": "node tools/dev.js",
|
|
47
53
|
"generate": "node tools/generator/generator.js"
|
|
48
|
-
}
|
|
54
|
+
};
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
? {
|
|
52
|
-
...baseScripts,
|
|
56
|
+
const testinNutinScripts = {
|
|
53
57
|
"test": "node test/runner.js",
|
|
54
|
-
"test
|
|
55
|
-
"test:watch":
|
|
58
|
+
"test:rebuild": `${packageManager} run build && ${packageManager} run test`,
|
|
59
|
+
"test:watch": `${packageManager} run build && node test/watch-tests.js`,
|
|
60
|
+
"serve": `${packageManager} run build && ${packageManager} run serve:only`
|
|
56
61
|
}
|
|
57
|
-
: baseScripts;
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
return testinNutin
|
|
60
64
|
? {
|
|
61
|
-
...
|
|
62
|
-
|
|
63
|
-
"build:finalize": "mkdir -p dist && mv dist-build/src dist/ && rm -rf dist-build",
|
|
64
|
-
"build": "npm run build:clear && npm run build:ts && npm run build-static && npm run build:finalize",
|
|
65
|
-
"serve:only": "live-server dist/src --port=9090 --entry-file=index.html --open",
|
|
66
|
-
"serve": "npm run build && npm run serve:only",
|
|
67
|
-
"dev": "concurrently \"node tools/watcher.js\" \"npm run build && npm run serve:only > /dev/null 2>&1\"",
|
|
65
|
+
...baseScripts,
|
|
66
|
+
...testinNutinScripts
|
|
68
67
|
}
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
: baseScripts;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function generatePackageJson(projectPath, answers) {
|
|
72
|
+
const { testinNutin, projectName, packageManager } = answers;
|
|
73
|
+
|
|
74
|
+
const devDependencies = {
|
|
75
|
+
"esbuild": "0.25.12",
|
|
76
|
+
"html-minifier-terser": "7.2.0",
|
|
77
|
+
"jsdom": "26.1.0",
|
|
78
|
+
"live-server": "1.2.2",
|
|
79
|
+
"sass": "1.89.0",
|
|
80
|
+
"typescript": "5.8.3"
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const scripts = getScripts(answers);
|
|
78
84
|
|
|
79
85
|
const packageJson = {
|
|
80
86
|
"name": projectName,
|
|
81
87
|
"version": "0.1.0",
|
|
82
|
-
"main": "index.js",
|
|
83
88
|
"type": "module",
|
|
84
89
|
"imports": testinNutin ? {
|
|
85
90
|
"#root/*.js": "./*.js"
|
|
86
91
|
} : {},
|
|
87
92
|
scripts,
|
|
88
|
-
devDependencies
|
|
93
|
+
devDependencies,
|
|
94
|
+
"packageManager": `${packageManager}@latest`
|
|
89
95
|
};
|
|
90
96
|
|
|
91
97
|
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
export async function generateTsconfigJson(projectPath, answers) {
|
|
95
|
-
const { template } = answers;
|
|
96
|
-
|
|
97
101
|
const tsconfig = {
|
|
98
102
|
"compilerOptions": {
|
|
99
103
|
"baseUrl": "./",
|
|
@@ -102,12 +106,13 @@ export async function generateTsconfigJson(projectPath, answers) {
|
|
|
102
106
|
"module": "NodeNext",
|
|
103
107
|
"moduleResolution": "NodeNext",
|
|
104
108
|
"rootDir": ".",
|
|
105
|
-
"outDir":
|
|
109
|
+
"outDir": "dist-build",
|
|
106
110
|
"strict": true,
|
|
107
111
|
"esModuleInterop": true,
|
|
108
112
|
"allowSyntheticDefaultImports": true,
|
|
109
113
|
"lib": ["es2022", "DOM"],
|
|
110
114
|
"types": [],
|
|
115
|
+
"removeComments": true,
|
|
111
116
|
"resolveJsonModule": true,
|
|
112
117
|
"typeRoots": ["src/types", "node_modules/@types"]
|
|
113
118
|
},
|
package/lib/src/prompts.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
22
22
|
type: 'list',
|
|
23
23
|
name: 'packageManager',
|
|
24
24
|
message: 'Package manager:',
|
|
25
|
-
choices: ['npm', 'yarn', 'pnpm'],
|
|
25
|
+
choices: ['npm', 'yarn', 'pnpm', 'bun'],
|
|
26
26
|
default: 'npm'
|
|
27
27
|
}
|
|
28
28
|
]);
|
|
@@ -34,10 +34,10 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
34
34
|
projectName,
|
|
35
35
|
packageManager,
|
|
36
36
|
stylinNutin: cliOptions.stylinNutin || true,
|
|
37
|
-
template: cliOptions.template ||
|
|
38
|
-
i18n: cliOptions.i18n ||
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
template: cliOptions.template || true,
|
|
38
|
+
i18n: cliOptions.i18n || true,
|
|
39
|
+
testinNutin: cliOptions.testinNutin || true,
|
|
40
|
+
transition: cliOptions.transitions || false
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -47,7 +47,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
47
47
|
questions.push({
|
|
48
48
|
type: 'confirm',
|
|
49
49
|
name: 'stylinNutin',
|
|
50
|
-
message: 'Use built-in SCSS utility classes?',
|
|
50
|
+
message: 'Use built-in SCSS utility classes ?',
|
|
51
51
|
default: true
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -56,8 +56,8 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
56
56
|
questions.push({
|
|
57
57
|
type: 'confirm',
|
|
58
58
|
name: 'template',
|
|
59
|
-
message: 'Use external templates?',
|
|
60
|
-
default:
|
|
59
|
+
message: 'Use external templates ?',
|
|
60
|
+
default: true
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -65,8 +65,8 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
65
65
|
questions.push({
|
|
66
66
|
type: 'confirm',
|
|
67
67
|
name: 'i18n',
|
|
68
|
-
message: 'Use i18n
|
|
69
|
-
default:
|
|
68
|
+
message: 'Use i18n with json-based content ?',
|
|
69
|
+
default: true
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -84,7 +84,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
84
84
|
type: 'confirm',
|
|
85
85
|
name: 'testinNutin',
|
|
86
86
|
message: 'Use testin-nutin toolkit?',
|
|
87
|
-
default:
|
|
87
|
+
default: true
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|
package/lib/src/utils.mjs
CHANGED
|
@@ -5,11 +5,12 @@ import { print } from './print.mjs';
|
|
|
5
5
|
export const promiseExec = promisify(exec);
|
|
6
6
|
|
|
7
7
|
export function displaySuccessMessage(answers) {
|
|
8
|
-
const { projectName
|
|
8
|
+
const { projectName } = answers;
|
|
9
9
|
|
|
10
10
|
print.boldSuccess('\n🎉 Your project is ready!');
|
|
11
11
|
print.info('\nNext steps:');
|
|
12
12
|
print.info(` cd ${projectName} && npm run serve`);
|
|
13
|
+
print.boldInfo(`\nDocumentation on https://www.nutin.org`);
|
|
13
14
|
|
|
14
15
|
print.success('\nHappy coding! 🚀\n');
|
|
15
16
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nutin/cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A lightweight, highly tweakable frontend toolkit, crafted for building lean, dynamic single-page applications without the complexity of full-scale frameworks.",
|
|
5
|
+
"preferGlobal": true,
|
|
5
6
|
"main": "lib/index.mjs",
|
|
6
7
|
"bin": {
|
|
7
8
|
"create-nutin-app": "./bin/create-app.mjs",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
node_modules
|
|
2
2
|
dist
|
|
3
|
+
dist-build
|
|
3
4
|
.github
|
|
4
5
|
.gitignore
|
|
5
6
|
.git
|
|
@@ -7,6 +8,14 @@ dist
|
|
|
7
8
|
*.md
|
|
8
9
|
.env
|
|
9
10
|
*.txt
|
|
10
|
-
{{#if
|
|
11
|
+
{{#if testinNutin}}
|
|
11
12
|
*.test
|
|
12
13
|
{{/if}}
|
|
14
|
+
*.log
|
|
15
|
+
|
|
16
|
+
npm-debug.log
|
|
17
|
+
.nyc_output
|
|
18
|
+
coverage
|
|
19
|
+
.DS_Store
|
|
20
|
+
.cache
|
|
21
|
+
.parcel-cache
|
|
@@ -1,13 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
ARG NODE_VERSION=22.3.0
|
|
2
|
+
ARG NGINX_VERSION=1.27-alpine
|
|
3
|
+
|
|
4
|
+
FROM node:${NODE_VERSION} AS builder
|
|
2
5
|
WORKDIR /usr/src/app
|
|
3
6
|
COPY . .
|
|
4
|
-
RUN
|
|
5
|
-
|
|
7
|
+
RUN {{ciCommand}} && \
|
|
8
|
+
{{packageManager}} run build:prod
|
|
9
|
+
|
|
10
|
+
FROM nginx:${NGINX_VERSION}
|
|
11
|
+
|
|
12
|
+
RUN rm /etc/nginx/conf.d/default.conf
|
|
13
|
+
COPY --from=builder /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf
|
|
14
|
+
|
|
15
|
+
RUN sed -i '/http {/a \
|
|
16
|
+
# Gzip compression\n\
|
|
17
|
+
gzip on;\n\
|
|
18
|
+
gzip_vary on;\n\
|
|
19
|
+
gzip_proxied any;\n\
|
|
20
|
+
gzip_comp_level 6;\n\
|
|
21
|
+
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;\n\
|
|
22
|
+
gzip_min_length 1000;\n\
|
|
23
|
+
' /etc/nginx/nginx.conf && \
|
|
24
|
+
touch /var/run/nginx.pid
|
|
6
25
|
|
|
7
|
-
FROM nginx:1.21-alpine
|
|
8
|
-
RUN sed -i 's/listen .*/listen 9090;/' /etc/nginx/conf.d/default.conf && \
|
|
9
|
-
sed -i 's|index index.html index.htm;|index index.html index.htm;\n try_files \$uri /index.html;|' /etc/nginx/conf.d/default.conf && \
|
|
10
|
-
mkdir -p /usr/share/nginx/html
|
|
11
26
|
COPY --from=builder /usr/src/app/dist/src /usr/share/nginx/html
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
EXPOSE 9090
|
|
29
|
+
|
|
30
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
31
|
+
CMD curl -f http://localhost:9090/health || exit 1
|
|
32
|
+
|
|
33
|
+
CMD ["nginx", "-g", "daemon off;"]
|
|
@@ -1 +1,24 @@
|
|
|
1
1
|
# {{projectName}} - Built with nutin
|
|
2
|
+
|
|
3
|
+
## Start
|
|
4
|
+
|
|
5
|
+
- `{{packageManager}} run serve` (rebuild), `{{packageManager}} run serve:only` (no rebuild), `{{packageManager}} run dev` (watch).
|
|
6
|
+
|
|
7
|
+
## Build
|
|
8
|
+
|
|
9
|
+
- `{{packageManager}} run build` (dev) or `{{packageManager}} run build:prod` (production)
|
|
10
|
+
|
|
11
|
+
Accepts `--bundle` (= build:prod) and `--log` (verbose output) flags
|
|
12
|
+
|
|
13
|
+
## Generate Component, View, Service
|
|
14
|
+
|
|
15
|
+
- `{{packageManager}} run generate ELEMENT ELEMENT_NAME`
|
|
16
|
+
|
|
17
|
+
Supports nested paths.
|
|
18
|
+
|
|
19
|
+
{{#if testinNutin}}
|
|
20
|
+
## Test
|
|
21
|
+
|
|
22
|
+
- `{{packageManager}} run test:rebuild`, `{{packageManager}} run test`, `{{packageManager}} run test:watch`.
|
|
23
|
+
|
|
24
|
+
{{/if}}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
server {
|
|
2
|
+
listen 9090;
|
|
3
|
+
server_name _;
|
|
4
|
+
root /usr/share/nginx/html;
|
|
5
|
+
index index.html;
|
|
6
|
+
|
|
7
|
+
# Security headers
|
|
8
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
9
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
10
|
+
add_header X-XSS-Protection "1; mode=block" always;
|
|
11
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
12
|
+
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
|
|
13
|
+
|
|
14
|
+
# Content Security Policy - ADJUST BASED ON YOUR APP'S NEEDS
|
|
15
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';" always;
|
|
16
|
+
|
|
17
|
+
location / {
|
|
18
|
+
try_files $uri $uri/ /index.html;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Static assets with long cache
|
|
22
|
+
location ~* \.(js|css|png|jpg|jpeg|gif|avif|ico|svg|woff|woff2|ttf|eot)$ {
|
|
23
|
+
expires 1y;
|
|
24
|
+
add_header Cache-Control "public, immutable";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# HTML files - no cache
|
|
28
|
+
location ~* \.html$ {
|
|
29
|
+
expires -1;
|
|
30
|
+
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
31
|
+
add_header Pragma "no-cache";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Health check endpoint
|
|
35
|
+
location /health {
|
|
36
|
+
access_log off;
|
|
37
|
+
return 200 "healthy\n";
|
|
38
|
+
add_header Content-Type text/plain;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Hide nginx version
|
|
42
|
+
server_tokens off;
|
|
43
|
+
}
|
|
@@ -23,7 +23,7 @@ const template = `{{#if template}}__TEMPLATE_PLACEHOLDER__{{else}}<div
|
|
|
23
23
|
{{else}}
|
|
24
24
|
style="font-style:italic;font-size:0.75rem;"
|
|
25
25
|
{{/if}}
|
|
26
|
-
>nutin
|
|
26
|
+
>nutin v{{version}}</span>
|
|
27
27
|
<img src="/assets/images/logo-trans.avif" width=250 height=250 alt="nutin's logo. A frame opened on the top left through which a plant grows.">
|
|
28
28
|
<div
|
|
29
29
|
{{#if i18n}}
|
|
@@ -6,6 +6,7 @@ import { I18nHelper } from './helpers/i18n.helper.js';
|
|
|
6
6
|
import { PipeHelper } from './helpers/pipe.helper.js';
|
|
7
7
|
import { ChildrenHelper } from './helpers/children.helper.js';
|
|
8
8
|
import { CatalogHelper, CatalogConfig } from './helpers/catalog.helper.js';
|
|
9
|
+
export { CatalogItemConfig } from './helpers/catalog.helper.js';
|
|
9
10
|
|
|
10
11
|
/**```typescript
|
|
11
12
|
* export interface ComponentConfig {
|
|
@@ -59,7 +60,7 @@ export abstract class BaseComponent<T extends HTMLElement = HTMLElement> {
|
|
|
59
60
|
/**
|
|
60
61
|
* ```typescript
|
|
61
62
|
interface CatalogConfig {
|
|
62
|
-
array:
|
|
63
|
+
array: CatalogItemConfig[];
|
|
63
64
|
elementName: string;
|
|
64
65
|
elementTag?: keyof HTMLElementTagNameMap;
|
|
65
66
|
selector: string;
|