@nutin/cli 1.2.0 → 1.2.1
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 +63 -30
- package/README.md +9 -8
- package/lib/index.mjs +6 -6
- package/lib/src/file-generator.mjs +18 -5
- package/lib/src/package-manager.mjs +33 -21
- package/lib/src/prompts.mjs +2 -2
- package/lib/src/utils.mjs +2 -0
- package/package.json +4 -3
- package/templates/base/.dockerignore.hbs +2 -1
- package/templates/base/builder.config.js.hbs +28 -0
- package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +11 -12
- package/templates/base/src/app/dom.test.js.hbs +8 -1
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +3 -0
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.ts.hbs +3 -0
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +2 -2
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +3 -3
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.ts.hbs +2 -2
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +4 -3
- package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +1 -1
- package/templates/base/src/index.html.hbs +1 -0
- package/templates/base/src/styles/main.scss.hbs +2 -0
- package/templates/base/tools/builder/builder.js.hbs +10 -7
- package/templates/base/tools/builder/core/copy-static.js.hbs +27 -2
- package/templates/base/tools/builder/core/esbuild.js.hbs +5 -15
- package/templates/base/tools/builder/core/finalize-build.js.hbs +2 -2
- package/templates/base/tools/builder/core/minify-html.js.hbs +2 -2
- package/templates/base/tools/builder/core/{sass.config.js.hbs → sass.js.hbs} +5 -7
- package/templates/base/tools/builder/core/tsc.js.hbs +8 -4
- package/templates/base/tools/builder/core/validate-html.js.hbs +19 -14
- package/templates/base/tools/deployment/Dockerfile.hbs +22 -0
- package/templates/base/tools/deployment/gzip.conf.hbs +19 -0
- package/templates/base/tools/deployment/nginx.conf.hbs +78 -0
- package/templates/base/tools/dev/dev-serve.js.hbs +32 -0
- package/templates/base/tools/{watcher.js.hbs → dev/watcher.js.hbs} +1 -1
- package/templates/base/tools/generator/generator.js.hbs +13 -5
- package/templates/base/tools/generator/handle-file.js.hbs +15 -8
- package/templates/base/tools/utils/export-flags.js.hbs +2 -1
- package/templates/base/tools/utils/index.js.hbs +4 -1
- package/templates/base/tools/utils/run-command.js.hbs +10 -3
- package/templates/base/tools/utils/run-script.js.hbs +1 -1
- package/templates/features/i18n/src/core/config.ts.hbs +3 -0
- package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +2 -0
- package/templates/features/i18n/src/core/services/i18n/i18n.ts.hbs +10 -1
- package/templates/features/i18n/src/core/services/i18n/languages.ts.hbs +7 -0
- package/templates/features/i18n/tools/builder/core/build-i18n.js.hbs +2 -2
- package/templates/features/i18n/tools/utils/languages.js.hbs +7 -0
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
- package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +1 -1
- package/templates/features/testinNutin/{test → testin-nutin}/core/index.js.hbs +1 -1
- package/templates/features/testinNutin/testin-nutin/core/src/globals/assertion-lib.js.hbs +136 -0
- package/templates/features/testinNutin/testin-nutin/core/src/globals/jsdom-setup.js.hbs +101 -0
- package/templates/features/testinNutin/{test → testin-nutin}/core/src/globals/register-test-globals.js.hbs +15 -1
- package/templates/features/testinNutin/testin-nutin/core/src/globals/spyon.js.hbs +59 -0
- package/templates/features/testinNutin/{test → testin-nutin}/core/src/utils/print.js.hbs +3 -1
- package/templates/features/testinNutin/{test → testin-nutin}/core/src/utils/test-discovery.js.hbs +5 -1
- package/templates/features/testinNutin/{test → testin-nutin}/core/src/utils/test-queue.js.hbs +3 -1
- package/templates/features/testinNutin/{test → testin-nutin}/core/tests/assertions.test.js.hbs +26 -2
- package/templates/features/testinNutin/testin-nutin/runner.js.hbs +35 -0
- package/templates/features/testinNutin/{test → testin-nutin}/watch-tests.js.hbs +1 -1
- package/templates/features/testinNutin/testin-nutin.config.js.hbs +14 -0
- package/templates/base/Dockerfile.hbs +0 -33
- package/templates/base/nginx.conf.hbs +0 -43
- package/templates/base/tools/builder/docs.md.hbs +0 -9
- package/templates/base/tools/dev.js.hbs +0 -43
- package/templates/features/testinNutin/test/core/src/globals/assertion-lib.js.hbs +0 -118
- package/templates/features/testinNutin/test/core/src/globals/spyon.js.hbs +0 -22
- package/templates/features/testinNutin/test/core/src/jsdom-setup.js.hbs +0 -12
- package/templates/features/testinNutin/test/docs.md.hbs +0 -39
- package/templates/features/testinNutin/test/runner.js.hbs +0 -36
- /package/templates/base/tools/{serve.js.hbs → dev/serve.js.hbs} +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/core/src/utils/queue.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/core/tests/globals-each-all.test.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/create-mock-method.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/mock-event-bus.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/mock-http-client.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/mock-i18n.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/mock-router.js.hbs +0 -0
- /package/templates/features/testinNutin/{test → testin-nutin}/mocks/mock-store.js.hbs +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## V1.
|
|
3
|
+
## V1.2.1
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Builder
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Added config file `builder.config.js` file (esbuild, verbose)
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- I18N
|
|
10
|
+
|
|
11
|
+
- Added JSON config file for centralized languages (`config/languages.json`)
|
|
12
|
+
|
|
13
|
+
- testin-nutin
|
|
14
|
+
|
|
15
|
+
- Added config file `test.config.js` (origins array, verbose boolean)
|
|
16
|
+
- Added assertions + automated 'not' counterparts
|
|
17
|
+
- Improved spyOn
|
|
18
|
+
- Improved JSDOM setup & global registration
|
|
19
|
+
|
|
20
|
+
- Improved deployment tools Dockerfile (nginx conf + gzip)
|
|
21
|
+
|
|
22
|
+
- Fixed typo and added comments in `nginx.conf`.
|
|
23
|
+
- Extracted gzip config from Dockerfile (removed `sed` command)
|
|
24
|
+
- Moved Dockerfile and deployment config files (`nginx.conf`, `gzip.conf`) into their own folder `tools/deployment`.
|
|
25
|
+
- Added `docker:build` and `docker:run` scripts in package.json.
|
|
26
|
+
|
|
27
|
+
See [DEPLOYMENT.md file](../../docs/DEPLOYMENT_HELPERS.md) for details.
|
|
28
|
+
|
|
29
|
+
- Moved dev tools (`serve.js`, `dev-serve.js`, `watcher.js`) into their own `tools/dev` folder.
|
|
30
|
+
|
|
31
|
+
- Added explicit chokidar devDependency (sass & live-server transitive).
|
|
32
|
+
|
|
33
|
+
## V1.2.0
|
|
34
|
+
|
|
35
|
+
- Fixed `npm run dev` command. Added middleware to reload nested routes.
|
|
36
|
+
|
|
37
|
+
- Improved `data-optional` tag:
|
|
38
|
+
- Can now specify which attribute to check with `data-optional="attrName"` (`src`, `href`, ...)
|
|
39
|
+
- Now works for `img`, `input`, etc
|
|
40
|
+
|
|
41
|
+
- Event Bus :
|
|
42
|
+
- Tracks both event name + callback.
|
|
43
|
+
- cleanupEventListeners now works correctly.
|
|
44
|
+
- New property: `once`. `once` subscriptions automatically remove themselves after first call.
|
|
45
|
+
|
|
46
|
+
- HTTP client :
|
|
47
|
+
- Optional request / response interceptors.
|
|
48
|
+
- Full timeout / abort controller support preserved.
|
|
49
|
+
- onDestroy now clears interceptors to prevent memory leaks.
|
|
50
|
+
|
|
51
|
+
- Improved builder
|
|
52
|
+
|
|
53
|
+
- Implemented `esbuild` & `html-minifier-terser`.
|
|
54
|
+
- Added flags `--bundle` (for production-ready build. *`npm run build --bundle` is equivalent to `npm run build:prod`)* and `--log` (verbose output). **Doesn't work with yarn or bun**.
|
|
55
|
+
- Commands: `npm run build`, `npm run build:prod`, `npm run serve:only`, `npm run serve`, `npm run dev`.
|
|
56
|
+
|
|
57
|
+
*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';`
|
|
58
|
+
|
|
59
|
+
- Improved deployment tools Dockerfile (nginx conf + gzip)
|
|
60
|
+
|
|
61
|
+
See [DEPLOYMENT.md file](../../docs/DEPLOYMENT_HELPERS.md) for details.
|
|
62
|
+
|
|
63
|
+
- stylin-nutin (generator)
|
|
64
|
+
- When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `stymes/components/_index.scss`).
|
|
10
65
|
|
|
11
66
|
## V1.1.0
|
|
12
67
|
|
|
@@ -27,32 +82,10 @@ type CatalogItemConfig<T = any> =
|
|
|
27
82
|
|
|
28
83
|
**Notes:** *Primitive data arrays (string, number, etc) needs to be accessed with `config.value`.*
|
|
29
84
|
|
|
30
|
-
## V1.2
|
|
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`.
|
|
85
|
+
## V1.0.2
|
|
53
86
|
|
|
54
|
-
|
|
87
|
+
- Partially fixed `npm run dev` script when using internal templates. *Page still needs to be reload manually from time to time.*
|
|
55
88
|
|
|
56
|
-
|
|
89
|
+
## V1.0.1
|
|
57
90
|
|
|
58
|
-
|
|
91
|
+
- Fixed typo issue when using i18n feature
|
package/README.md
CHANGED
|
@@ -1,9 +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
4
|
Includes essential features out of the box, with zero runtime dependencies.
|
|
5
5
|
*TypeScript / Javascript, SASS, HTML*
|
|
6
6
|
|
|
7
|
+
- Required : Node >=18 **If using testin-nutin: Node >=20.19.0 (JSDOM requirement : `{ node: '^20.19.0 || ^22.12.0 || >=24.0.0' }`)**
|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -25,20 +27,20 @@ nutin-new
|
|
|
25
27
|
# argument: project-name
|
|
26
28
|
|
|
27
29
|
-d --defaults
|
|
28
|
-
-pm --package-manager <manager> # npm (
|
|
30
|
+
-pm --package-manager <manager> # npm (Default), yarn, pnpm, bun
|
|
29
31
|
|
|
30
|
-
--template # Use
|
|
32
|
+
--no-template # Use inline templates
|
|
31
33
|
--no-stylin-nutin # Do not use built-in SCSS utility
|
|
32
34
|
--testin-nutin # Use testin-nutin toolkit
|
|
33
|
-
--i18n #
|
|
35
|
+
--no-i18n # Do not use i18n & json-based content
|
|
34
36
|
--transition # Use animated view transitions. Note: May interfere with CSS `position: fixed`, `z-index`...
|
|
35
37
|
|
|
36
38
|
# Defaults
|
|
37
|
-
--
|
|
39
|
+
--template # (Default) Use external templates
|
|
38
40
|
--stylin-nutin # (Default) Use built-in SCSS utility classes & mixins
|
|
39
|
-
--
|
|
40
|
-
--no-transition # (Default) Do not use animated view transitions
|
|
41
|
+
--i18n # (Default) Use i18n & json-based content
|
|
41
42
|
--no-testin-nutin # (Default) Do not use testin-nutin toolkit
|
|
43
|
+
--no-transition # (Default) Do not use animated view transitions
|
|
42
44
|
|
|
43
45
|
# Help
|
|
44
46
|
-h --help
|
|
@@ -46,6 +48,5 @@ nutin-new
|
|
|
46
48
|
|
|
47
49
|
## [First app tutorial](https://www.nutin.org/tutorial)
|
|
48
50
|
|
|
49
|
-
|
|
50
51
|
- Visit the [website](https://www.nutin.org)
|
|
51
52
|
- [Project repository](https://github.com/LoisKOUNINEF/nutin)
|
package/lib/index.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { program } from 'commander';
|
|
|
2
2
|
import { print } from './src/print.mjs';
|
|
3
3
|
import { promptUser } from './src/prompts.mjs';
|
|
4
4
|
import { createProject } from './src/file-generator.mjs';
|
|
5
|
-
import { displaySuccessMessage } from './src/utils.mjs';
|
|
5
|
+
import { displaySuccessMessage, packageVersion } from './src/utils.mjs';
|
|
6
6
|
|
|
7
7
|
export async function createApp() {
|
|
8
8
|
program
|
|
9
|
-
.version(
|
|
9
|
+
.version(packageVersion)
|
|
10
10
|
.argument('[projectName]', 'Name of the project')
|
|
11
11
|
.option('-d, --defaults', 'Use default options')
|
|
12
12
|
.option('-pm, --package-manager <manager>', 'Specify package manager (npm, yarn, pnpm, bun)')
|
|
@@ -14,10 +14,10 @@ export async function createApp() {
|
|
|
14
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
|
-
.option('--i18n', 'Use i18n & json-based content')
|
|
18
|
-
.option('--no-i18n', '
|
|
19
|
-
.option('--testin-nutin', '
|
|
20
|
-
.option('--no-testin-nutin', 'Do not use testin-nutin toolkit')
|
|
17
|
+
.option('--i18n', '(Default) Use i18n & json-based content')
|
|
18
|
+
.option('--no-i18n', 'Do not use i18n & json-based content')
|
|
19
|
+
.option('--testin-nutin', 'Use testin-nutin toolkit')
|
|
20
|
+
.option('--no-testin-nutin', '(Default) Do not use testin-nutin toolkit')
|
|
21
21
|
.option('--transition', 'Use animated view transitions\nNote: May interfere with CSS `position: fixed`, `z-index`...')
|
|
22
22
|
.option('--no-transition', '(Default) Do not use animated view transitions')
|
|
23
23
|
.addHelpText('afterAll', '\nChaining options :\nExample: -d -pm npm --i18n --testinNutin')
|
|
@@ -5,6 +5,7 @@ import { TemplateCompiler } from './template-compiler.mjs';
|
|
|
5
5
|
import { print } from './print.mjs';
|
|
6
6
|
import { initializeGit } from './git-manager.mjs';
|
|
7
7
|
import { installDependencies, generatePackageJson, generateTsconfigJson, getCiCommand } from './package-manager.mjs';
|
|
8
|
+
import { packageVersion } from './utils.mjs';
|
|
8
9
|
|
|
9
10
|
const fs = fsExtra.default;
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -48,14 +49,14 @@ export class ProjectGenerator {
|
|
|
48
49
|
const context = this.buildContext(answers);
|
|
49
50
|
const templateDir = this.getTemplateDirectory(answers);
|
|
50
51
|
|
|
51
|
-
print.section('
|
|
52
|
+
print.section('📝 Processing templates...');
|
|
52
53
|
await this.processTemplateDirectory(templateDir, projectPath, context);
|
|
53
54
|
|
|
54
55
|
await this.processFeatureTemplates(projectPath, context);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
buildContext(answers) {
|
|
58
|
-
const version =
|
|
59
|
+
const version = packageVersion;
|
|
59
60
|
const ciCommand = getCiCommand(answers.packageManager);
|
|
60
61
|
|
|
61
62
|
return {
|
|
@@ -148,7 +149,7 @@ export class ProjectGenerator {
|
|
|
148
149
|
const featureTemplateDir = path.join(featuresDir, feature);
|
|
149
150
|
|
|
150
151
|
if (await fs.pathExists(featureTemplateDir)) {
|
|
151
|
-
print.info(
|
|
152
|
+
print.info(`🔧 Adding ${feature} feature...`);
|
|
152
153
|
await this.processTemplateDirectory(featureTemplateDir, projectPath, context);
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -158,6 +159,18 @@ export class ProjectGenerator {
|
|
|
158
159
|
async generateJsonFiles(projectPath, answers) {
|
|
159
160
|
await generatePackageJson(projectPath, answers);
|
|
160
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 });
|
|
161
174
|
}
|
|
162
175
|
|
|
163
176
|
async runPostSetupTasks(projectPath, answers) {
|
|
@@ -170,10 +183,10 @@ export class ProjectGenerator {
|
|
|
170
183
|
}
|
|
171
184
|
|
|
172
185
|
async runSetupScripts(projectPath, answers) {
|
|
173
|
-
print.section('
|
|
186
|
+
print.section('⚙️ Running setup scripts...');
|
|
174
187
|
|
|
175
188
|
try {
|
|
176
|
-
const setupScriptsTemplate = path.join(__dirname, '
|
|
189
|
+
const setupScriptsTemplate = path.join(__dirname, '..', '..', 'templates', 'scripts');
|
|
177
190
|
|
|
178
191
|
if (await fs.pathExists(setupScriptsTemplate)) {
|
|
179
192
|
const scriptsDir = path.join(projectPath, 'scripts');
|
|
@@ -39,7 +39,7 @@ function getInstallCommand(packageManager) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function getScripts(answers) {
|
|
42
|
-
const { testinNutin, packageManager } = answers;
|
|
42
|
+
const { testinNutin, packageManager, projectName } = answers;
|
|
43
43
|
|
|
44
44
|
const baseScripts = {
|
|
45
45
|
"patch": `${packageManager} version patch -m 'CI/CD: Bump version to %s'`,
|
|
@@ -47,17 +47,18 @@ function getScripts(answers) {
|
|
|
47
47
|
"major": `${packageManager} version major -m 'CI/CD: Bump version to %s'`,
|
|
48
48
|
"build": "node tools/builder/builder.js",
|
|
49
49
|
"build:prod": "NODE_ENV=production node tools/builder/builder.js",
|
|
50
|
-
"serve:only": "node tools/serve.js",
|
|
50
|
+
"serve:only": "node tools/dev/serve.js",
|
|
51
51
|
"serve": `${packageManager} run build && ${packageManager} run serve:only`,
|
|
52
|
-
"dev": "node tools/dev.js",
|
|
53
|
-
"generate": "node tools/generator/generator.js"
|
|
52
|
+
"dev": "node tools/dev/dev-serve.js",
|
|
53
|
+
"generate": "node tools/generator/generator.js",
|
|
54
|
+
"docker:build": `docker build -t ${projectName} -f tools/deployment/Dockerfile .`,
|
|
55
|
+
"docker:run": `docker run -p 9090:9090 ${projectName}:latest`
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
const testinNutinScripts = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"serve": `${packageManager} run build && ${packageManager} run serve:only`
|
|
59
|
+
"test": "node testin-nutin/runner.js",
|
|
60
|
+
"test:rebuild": `${packageManager} run build && ${packageManager} run test`,
|
|
61
|
+
"test:watch": `${packageManager} run build && node testin-nutin/watch-tests.js`
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
return testinNutin
|
|
@@ -72,12 +73,15 @@ export async function generatePackageJson(projectPath, answers) {
|
|
|
72
73
|
const { testinNutin, projectName, packageManager } = answers;
|
|
73
74
|
|
|
74
75
|
const devDependencies = {
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"live-server": "1.2.2",
|
|
79
|
-
"sass": "1.89.0",
|
|
80
|
-
"typescript": "5.8.3"
|
|
76
|
+
"chokidar": "^4.0.3",
|
|
77
|
+
"esbuild": "^0.25.12",
|
|
78
|
+
"html-minifier-terser": "^7.2.0",
|
|
79
|
+
"live-server": "^1.2.2",
|
|
80
|
+
"sass": "^1.89.0",
|
|
81
|
+
"typescript": "^5.8.3",
|
|
82
|
+
...(testinNutin && {
|
|
83
|
+
"jsdom": "^26.1.0",
|
|
84
|
+
})
|
|
81
85
|
};
|
|
82
86
|
|
|
83
87
|
const scripts = getScripts(answers);
|
|
@@ -86,12 +90,18 @@ export async function generatePackageJson(projectPath, answers) {
|
|
|
86
90
|
"name": projectName,
|
|
87
91
|
"version": "0.1.0",
|
|
88
92
|
"type": "module",
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
|
|
93
|
+
...(testinNutin && {
|
|
94
|
+
"imports": {
|
|
95
|
+
"#root/*.js": "./*.js"
|
|
96
|
+
}
|
|
97
|
+
}),
|
|
92
98
|
scripts,
|
|
93
99
|
devDependencies,
|
|
94
|
-
|
|
100
|
+
...(testinNutin && {
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=20.19.0"
|
|
103
|
+
}
|
|
104
|
+
})
|
|
95
105
|
};
|
|
96
106
|
|
|
97
107
|
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
@@ -105,13 +115,15 @@ export async function generateTsconfigJson(projectPath, answers) {
|
|
|
105
115
|
"target": "ESNext",
|
|
106
116
|
"module": "NodeNext",
|
|
107
117
|
"moduleResolution": "NodeNext",
|
|
108
|
-
"rootDir": "
|
|
109
|
-
"outDir": "dist-build",
|
|
118
|
+
"rootDir": "src",
|
|
119
|
+
"outDir": "dist-build/src",
|
|
110
120
|
"strict": true,
|
|
121
|
+
"skipLibCheck": true,
|
|
122
|
+
"forceConsistentCasingInFileNames": true,
|
|
123
|
+
"noUncheckedIndexedAccess": true,
|
|
111
124
|
"esModuleInterop": true,
|
|
112
125
|
"allowSyntheticDefaultImports": true,
|
|
113
126
|
"lib": ["es2022", "DOM"],
|
|
114
|
-
"types": [],
|
|
115
127
|
"removeComments": true,
|
|
116
128
|
"resolveJsonModule": true,
|
|
117
129
|
"typeRoots": ["src/types", "node_modules/@types"]
|
package/lib/src/prompts.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export async function promptUser(initialName, cliOptions = {}) {
|
|
|
36
36
|
stylinNutin: cliOptions.stylinNutin || true,
|
|
37
37
|
template: cliOptions.template || true,
|
|
38
38
|
i18n: cliOptions.i18n || true,
|
|
39
|
-
testinNutin: cliOptions.testinNutin ||
|
|
39
|
+
testinNutin: cliOptions.testinNutin || false,
|
|
40
40
|
transition: cliOptions.transitions || false
|
|
41
41
|
};
|
|
42
42
|
}
|
|
@@ -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: false
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|
package/lib/src/utils.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nutin/cli",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Build SPAs by understanding SPAs. A minimal frontend toolkit crafted for learning and lean applications.",
|
|
5
5
|
"preferGlobal": true,
|
|
6
6
|
"main": "lib/index.mjs",
|
|
7
7
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"nutin",
|
|
20
20
|
"Frontend",
|
|
21
21
|
"SPA",
|
|
22
|
+
"Learning",
|
|
22
23
|
"Minimalist",
|
|
23
24
|
"Lighweight",
|
|
24
25
|
"Customizable"
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"inquirer": "^9.0.0"
|
|
33
34
|
},
|
|
34
35
|
"engines": {
|
|
35
|
-
"node": ">=18
|
|
36
|
+
"node": ">=18"
|
|
36
37
|
},
|
|
37
38
|
"repository": {
|
|
38
39
|
"type": "git",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
esbuild: {
|
|
3
|
+
bundle: true,
|
|
4
|
+
minify: true,
|
|
5
|
+
sourcemap: false,
|
|
6
|
+
platform: 'browser',
|
|
7
|
+
format: 'esm',
|
|
8
|
+
target: ['es2015'],
|
|
9
|
+
legalComments: 'none',
|
|
10
|
+
loader: {
|
|
11
|
+
'.json': 'json',
|
|
12
|
+
},
|
|
13
|
+
define: {
|
|
14
|
+
'process.env.NODE_ENV': '"production"',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
sass: {
|
|
18
|
+
paths: [
|
|
19
|
+
'base',
|
|
20
|
+
'core',
|
|
21
|
+
{{#if stylinNutin}}
|
|
22
|
+
'components',
|
|
23
|
+
'views',
|
|
24
|
+
{{/if}}
|
|
25
|
+
], // in styles/
|
|
26
|
+
},
|
|
27
|
+
verbose: false,
|
|
28
|
+
}
|
|
@@ -2,21 +2,19 @@ import { AnchorManager, Component, IAnchorConfig } from '../../../../core/index.
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ```typescript
|
|
5
|
-
interface
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
* interface IAnchorConfig {
|
|
6
|
+
// prefix href with '#' for internal anchor
|
|
7
|
+
href: string;
|
|
8
|
+
target?: string;
|
|
9
9
|
textContent?: string;
|
|
10
|
+
{{#if i18n}}
|
|
11
|
+
i18nKey?: string;
|
|
12
|
+
{{/if}}
|
|
10
13
|
className?: string;
|
|
11
14
|
style?: string;
|
|
12
|
-
// use regular pipe syntax for arguments / chaining
|
|
15
|
+
// use regular pipe syntax for arguments / chaining
|
|
13
16
|
pipes?: string;
|
|
14
|
-
|
|
15
|
-
interface IAnchorConfig extends IAttributesConfig {
|
|
16
|
-
// prefix href with '#' for internal anchor
|
|
17
|
-
href: string;
|
|
18
|
-
// _blank, etc
|
|
19
|
-
target?: string;
|
|
17
|
+
tagName?: keyof HTMLElementTagNameMap;
|
|
20
18
|
}
|
|
21
19
|
```
|
|
22
20
|
*/
|
|
@@ -25,9 +23,10 @@ export class AnchorComponent extends Component<HTMLAnchorElement, IAnchorConfig>
|
|
|
25
23
|
constructor(mountTarget: HTMLElement, config: IAnchorConfig) {
|
|
26
24
|
super({
|
|
27
25
|
mountTarget,
|
|
26
|
+
tagName: config.tagName,
|
|
28
27
|
config,
|
|
29
28
|
normalizeKeys: ['style', 'pipes', 'className']
|
|
30
29
|
});
|
|
31
30
|
new AnchorManager(config, this.element);
|
|
32
31
|
}
|
|
33
|
-
}
|
|
32
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
describe('App container', () => {
|
|
2
|
-
it('should
|
|
2
|
+
it('should be defined', () => {
|
|
3
3
|
const appContainer = document.getElementById('app');
|
|
4
4
|
expect(appContainer).toBeDefined();
|
|
5
5
|
});
|
|
6
|
+
it('should have title', () => {
|
|
7
|
+
expect(document.title).toEqual('{{projectName}}');
|
|
8
|
+
});
|
|
9
|
+
it('should have meta description', () => {
|
|
10
|
+
const desc = document.querySelector('meta[name="description"]').content;
|
|
11
|
+
expect(desc).toEqual('{{projectName}}');
|
|
12
|
+
});
|
|
6
13
|
});
|
|
@@ -10,6 +10,9 @@ export class EventHelper {
|
|
|
10
10
|
element.querySelectorAll('[data-event]').forEach(el => {
|
|
11
11
|
const parts = el.getAttribute('data-event')!.split(':');
|
|
12
12
|
const [eventName, handlerName, argsString] = parts;
|
|
13
|
+
|
|
14
|
+
if (!handlerName || !eventName) return;
|
|
15
|
+
|
|
13
16
|
const handler = (component as any)[handlerName];
|
|
14
17
|
|
|
15
18
|
if (typeof handler === 'function') {
|
|
@@ -28,6 +28,9 @@ export class PipeHelper {
|
|
|
28
28
|
|
|
29
29
|
for (const pipe of pipes) {
|
|
30
30
|
const [pipeName, ...argParts] = pipe.split(':');
|
|
31
|
+
|
|
32
|
+
if(!pipeName) return;
|
|
33
|
+
|
|
31
34
|
const args = argParts.length ? argParts.join(':').split(',') : [];
|
|
32
35
|
value = AppPipeRegistry.apply(pipeName.trim(), value, args.map(a => a.trim()));
|
|
33
36
|
}
|
|
@@ -53,9 +53,9 @@ export class TokenHelper {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
private static resolvePrefixed(token: string, el: HTMLElement): any | null {
|
|
56
|
-
for (const prefix
|
|
56
|
+
for (const [prefix, resolver] of Object.entries(PREFIXED_TOKEN_RESOLVERS)) {
|
|
57
57
|
if (token.startsWith(prefix)) {
|
|
58
|
-
return
|
|
58
|
+
return resolver(token.slice(prefix.length), el);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
return null;
|
|
@@ -68,11 +68,11 @@ export class NavigationManager {
|
|
|
68
68
|
|
|
69
69
|
const params: Record<string, string> = {};
|
|
70
70
|
paramNames.forEach((name, i) => {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const value = match[i + 1];
|
|
72
|
+
if (value) {
|
|
73
|
+
params[name] = value;
|
|
73
74
|
}
|
|
74
75
|
});
|
|
75
|
-
|
|
76
76
|
return params;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -58,12 +58,12 @@ export class RouteGuardsManager {
|
|
|
58
58
|
const guardResult = await this.runGuards(guards, params);
|
|
59
59
|
|
|
60
60
|
if (guardResult === false) {
|
|
61
|
-
console.log(`Navigation to ${targetPath} blocked by route guard`);
|
|
61
|
+
// console.log(`Navigation to ${targetPath} blocked by route guard`);
|
|
62
62
|
return { allowed: false };
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (typeof guardResult === 'string') {
|
|
66
|
-
console.log(`Navigation to ${targetPath} redirected to ${guardResult} by route guard`);
|
|
66
|
+
// console.log(`Navigation to ${targetPath} redirected to ${guardResult} by route guard`);
|
|
67
67
|
return { allowed: false, redirectTo: guardResult };
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -3,9 +3,9 @@ import { AttributesHelper, IAttributesConfig } from "../helpers/attributes.helpe
|
|
|
3
3
|
/**
|
|
4
4
|
* ```typescript
|
|
5
5
|
interface IAttributesConfig {
|
|
6
|
-
|
|
6
|
+
{{#if i18n}}
|
|
7
7
|
i18nKey?: string;
|
|
8
|
-
|
|
8
|
+
{{/if}}
|
|
9
9
|
textContent?: string;
|
|
10
10
|
className?: string;
|
|
11
11
|
style?: string;
|
|
@@ -15,14 +15,15 @@ interface IAttributesConfig {
|
|
|
15
15
|
interface IAnchorConfig extends IAttributesConfig {
|
|
16
16
|
// prefix href with '#' for internal anchor
|
|
17
17
|
href: string;
|
|
18
|
-
// _blank, etc
|
|
19
18
|
target?: string;
|
|
19
|
+
tagName?: keyof HTMLElementTagNameMap;
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
*/
|
|
23
23
|
export interface IAnchorConfig extends IAttributesConfig {
|
|
24
24
|
href: string;
|
|
25
25
|
target?: string;
|
|
26
|
+
tagName?: keyof HTMLElementTagNameMap;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export class AnchorManager {
|
|
@@ -109,7 +109,7 @@ describe('ButtonManager', () => {
|
|
|
109
109
|
|
|
110
110
|
it('should set button className correctly', () => {
|
|
111
111
|
const mockComponent = {};
|
|
112
|
-
const manager = new ButtonManager(mockComponent, []
|
|
112
|
+
const manager = new ButtonManager(mockComponent, []);
|
|
113
113
|
const btn = {
|
|
114
114
|
i18nKey: 'save',
|
|
115
115
|
className: 'btn-primary'
|