@nutin/cli 1.2.3 → 1.3.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/README.md +25 -18
  3. package/lib/index.mjs +23 -20
  4. package/lib/src/context-builder.mjs +40 -0
  5. package/lib/src/file-generator.mjs +26 -126
  6. package/lib/src/git-manager.mjs +1 -1
  7. package/lib/src/json-generator.mjs +31 -0
  8. package/lib/src/package-manager.mjs +21 -18
  9. package/lib/src/print.mjs +1 -0
  10. package/lib/src/project-generator.mjs +53 -0
  11. package/lib/src/prompts.mjs +16 -75
  12. package/lib/src/template-compiler.mjs +1 -1
  13. package/lib/src/utils.mjs +9 -9
  14. package/lib/src/version.mjs +1 -0
  15. package/package.json +1 -1
  16. package/templates/base/.gitignore.hbs +1 -0
  17. package/templates/base/builder.config.js.hbs +8 -0
  18. package/templates/base/src/app/globals.d.ts.hbs +12 -8
  19. package/templates/base/src/app/main.ts.hbs +2 -1
  20. package/templates/base/src/app/views/home/home.view.test.js.hbs +26 -0
  21. package/templates/base/src/app/views/home/home.view.ts.hbs +2 -2
  22. package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +1 -1
  23. package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +46 -26
  24. package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +36 -22
  25. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +4 -4
  26. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +60 -13
  27. package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +16 -17
  28. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +40 -8
  29. package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +16 -16
  30. package/templates/base/src/core/base-classes/component/component.ts.hbs +20 -24
  31. package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +19 -2
  32. package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.ts.hbs +2 -2
  33. package/templates/base/src/core/base-classes/service/service.ts.hbs +35 -12
  34. package/templates/base/src/core/base-classes/view/view.ts.hbs +6 -6
  35. package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +13 -13
  36. package/templates/base/src/core/services/http-client/helpers/http-builder.helper.ts.hbs +18 -18
  37. package/templates/base/src/core/services/http-client/helpers/http-manager.helper.ts.hbs +24 -24
  38. package/templates/base/src/core/services/http-client/http-client.ts.hbs +19 -19
  39. package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +6 -6
  40. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +15 -14
  41. package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.ts.hbs +22 -22
  42. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +4 -36
  43. package/templates/base/src/core/services/router/router.ts.hbs +45 -46
  44. package/templates/base/src/core/services/store/store.ts.hbs +6 -6
  45. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +41 -15
  46. package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +40 -19
  47. package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +11 -5
  48. package/templates/base/src/libs/pipes/pipes.ts.hbs +1 -1
  49. package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +197 -0
  50. package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +75 -0
  51. package/templates/base/src/libs/popover/popover.ts.hbs +89 -96
  52. package/templates/base/src/libs/snackbar/snackbar.ts.hbs +7 -4
  53. package/templates/base/src/styles/base/_variables.scss.hbs +1 -0
  54. package/templates/base/src/styles/core/_anchors.scss.hbs +10 -0
  55. package/templates/base/src/styles/core/_index.scss.hbs +6 -0
  56. package/templates/base/src/styles/core/_popover.scss.hbs +96 -0
  57. package/templates/base/src/styles/core/_snackbar.scss.hbs +50 -0
  58. package/templates/base/src/styles/main.scss.hbs +1 -1
  59. package/templates/base/tools/builder/builder.js.hbs +9 -3
  60. package/templates/base/tools/builder/core/add-tags.js.hbs +46 -0
  61. package/templates/base/tools/builder/core/copy-static.js.hbs +35 -35
  62. package/templates/base/tools/builder/core/finalize-build.js.hbs +6 -6
  63. package/templates/base/tools/builder/core/hash-files.js.hbs +52 -0
  64. package/templates/base/tools/builder/core/validate-html.js.hbs +12 -32
  65. package/templates/base/tools/utils/get-files-recursive.js.hbs +17 -8
  66. package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +56 -0
  67. package/templates/{base → features/deployHelper}/tools/deployment/Dockerfile.hbs +12 -4
  68. package/templates/{base → features/deployHelper}/tools/deployment/nginx.conf.hbs +30 -2
  69. package/templates/features/i18n/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +4 -6
  70. package/templates/features/i18n/src/core/services/i18n/i18n.ts.hbs +76 -52
  71. package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +31 -0
  72. package/templates/features/template/src/app/views/home/home.view.html.hbs +1 -1
  73. package/templates/features/template/tools/builder/core/merge-templates.js.hbs +2 -2
  74. package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +2 -2
  75. package/templates/features/testinNutin/testin-nutin/runner.js.hbs +1 -0
  76. package/templates/features/testinNutin/testin-nutin.config.js.hbs +2 -2
  77. package/templates/base/src/styles/core/_libs.scss.hbs +0 -132
  78. package/templates/base/tools/deployment/gzip.conf.hbs +0 -19
  79. package/templates/features/i18n/src/app/views/home/locales/fr.json.hbs +0 -9
  80. /package/templates/{base → features/deployHelper}/.dockerignore.hbs +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # Changelog
2
2
 
3
+ ## V1.3.1
4
+
5
+ - Minor features:
6
+ - New BaseComponent protected method.
7
+ Register events that will trigger re-render. Call this in component's constructor.
8
+ ```ts
9
+ // force = true: calls forceRender() instead of render()
10
+ listenToRenderEvents(events: EventKey[], force: boolean = false): void
11
+ ```
12
+ - data-optional now supports JS values
13
+ ```html
14
+ data-optional="${myValue}"
15
+ ```
16
+ - PopoverView and AnchorComponents now have accessibility features
17
+ - ButtonManager now handles checkboxes as well
18
+ - i18nService now emits `language-changed` event and exposes related methods
19
+
20
+ - Fixes:
21
+ - CatalogConfig no longer renders multiple containers when render() or forceRender() are triggered by events
22
+ - Event Listeners are now destroyed properly before triggering a re-render
23
+ - Router now correctly redirects to 404 page when URL starts with two slashes `//`.
24
+ - Popover no longer flickers on close
25
+ - Snackbar messages are now properly sanitized
26
+
27
+ - CLI
28
+ - Rework: App creation flow
29
+ - No features enabled by default
30
+ - Presets `--preset <minimal|standard|full|cicd>`
31
+ Minimal: External templates
32
+ Standard: Minimal + i18n & built-in SCSS utilities.
33
+ Full: Standard + deployment helpers & built-in testing toolkit
34
+ CI/CD: Minimal + deployment helpers
35
+ - Remaining flags: `--i18n`, `--deploy-helper`, `--testin-nutin`, `--transition`
36
+
37
+ ## V1.3.0
38
+
39
+ Version 1.3.0 marks a stability milestone with various improvements and refinements, making this the recommended version for new projects.
40
+
41
+ - Global
42
+ - `Service` abstract class now auto-binds methods with `this`, preventing `"this" is undefined` potential warning on initial page load.
43
+ - Added HTML template sanitization with trustLevel that can be passed to component's `super()`.
44
+ - trusted : no template sanitization (returns original)
45
+ - normal (default) : remove scripts and inline event handlers
46
+ - strict : remove iframe, object, embed, href (javascript), data: protocol, scripts and inline event handlers
47
+
48
+ - I18n
49
+ - Removed `data-i18n-params` pipe : Default values (fallback) must now be inlined text content in component's HTML template.
50
+
51
+ - CLI :
52
+ - Deployment helpers is now optional (default : disabled). Flags : `--deploy-helper` `--no-deploy-helper`
53
+ - Fixed CLI prompts (no longer overrides answers)
54
+
55
+ - Builder / Deployment Helper
56
+ - Now adds stylesheet & script tags in `index.html` on build time (`add-tags.js`)
57
+ - (Production) Added file hashing (js & css) (`hash-files.js`)
58
+ - (Production) Added Gzip and Brotli compression (`compress-files.js`). Configurable in `builder.config.js`
59
+ - **Note : default nginx alpine image does NOT support Brotli compression.** If you want to use it :
60
+ - uncomment brotli-related sections (`BROTLI OPTIONAL`) in `builder.config.js` and `tools/builder/core/compress-files.js`
61
+ - enable brotli in nginx.conf
62
+ - You'll also need to use an existing Brotli-enabled nginx image or build your own from source.
63
+ - Enabled gzip globally in `nginx.conf` and removed `gzip.conf` (compress during build).
64
+
65
+ - StylinNutin
66
+ - Added utility classes `u-text-center`, `u-text-right`, `u-text-left`, `u-font-primary`
67
+ - box-shadow variables now use `$primary-color`
68
+
69
+ - TestinNutin
70
+ - Now applies `setupJsdom()` beforeAll (was beforeEach) and `teardownJsdom()` afterAll (was afterEach) in `test-queue.js` (improved efficiency / speed)
71
+
3
72
  ## V1.2.3
4
73
 
5
74
  - `nginx.conf` : fixed multi-line CSP map warning (single line map)
@@ -69,7 +138,7 @@
69
138
  - Improved deployment tools Dockerfile (nginx conf + gzip)
70
139
 
71
140
  - stylin-nutin (generator)
72
- - When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `stymes/components/_index.scss`).
141
+ - When generating a component, prompts (boolean) to generate a `_component-name.scss` file in `styles/components` (forwarded by `styles/components/_index.scss`).
73
142
 
74
143
  ## V1.1.0
75
144
 
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, with zero runtime dependencies.
5
- *TypeScript / Javascript, SASS, HTML*
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 **If using testin-nutin: Node >=20.19.0 (JSDOM requirement : `{ node: '^20.19.0 || ^22.12.0 || >=24.0.0' }`)**
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,37 @@ 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)
28
32
 
33
+ # Help
34
+ -h --help
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
 
32
- --no-template # Use inline templates
33
- --no-stylin-nutin # Do not use built-in SCSS utility
42
+ # Presets
43
+ --preset <minimal|standard|full|cicd>
44
+ # minimal: external templates
45
+ # standard: minimal + i18n & built-in SCSS classes
46
+ # full: standard + deployment helpers & built-in testing toolkit
47
+ # cicd: minimal + deployment helpers
48
+
49
+ # Features
50
+ --i18n # Use i18n & json-based content
51
+ --deploy-helper # Use Docker & deployment helpers
34
52
  --testin-nutin # Use testin-nutin toolkit
35
- --no-i18n # Do not use i18n & json-based content
36
53
  --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
- --no-testin-nutin # (Default) Do not use testin-nutin toolkit
43
- --no-transition # (Default) Do not use animated view transitions
44
-
45
- # Help
46
- -h --help
47
54
  ```
48
55
 
49
56
  ## [First app tutorial](https://www.nutin.org/tutorial)
@@ -51,4 +58,4 @@ nutin-new
51
58
  ## 📄 Docs
52
59
 
53
60
  - Docs on [website](https://www.nutin.org).
54
- - Docs on [Project repository](https://github.com/LoisKOUNINEF/nutin/tree/main/docs).
61
+ - Docs on [repository](https://github.com/LoisKOUNINEF/nutin/tree/main/docs).
package/lib/index.mjs CHANGED
@@ -1,38 +1,41 @@
1
1
  import { program } from 'commander';
2
- import { print } from './src/print.mjs';
2
+ import { print, chalk } from './src/print.mjs';
3
3
  import { promptUser } from './src/prompts.mjs';
4
- import { createProject } from './src/file-generator.mjs';
5
- import { displaySuccessMessage, packageVersion } from './src/utils.mjs';
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
9
10
  .version(packageVersion)
11
+ .configureOutput({
12
+ helpWidth: 100
13
+ })
10
14
  .argument('[projectName]', 'Name of the project')
11
- .option('-d, --defaults', 'Use default options')
12
15
  .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
- .option('--stylin-nutin', '(Default) Use built-in SCSS utility classes & mixins')
16
- .option('--no-stylin-nutin', 'Do not use built-in SCSS utility')
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
- .option('--transition', 'Use animated view transitions\nNote: May interfere with CSS `position: fixed`, `z-index`...')
22
- .option('--no-transition', '(Default) Do not use animated view transitions')
23
- .addHelpText('afterAll', '\nChaining options :\nExample: -d -pm npm --i18n --testinNutin')
16
+ .option('--preset <preset>',
17
+ `${chalk.yellow('Project preset configuration:')}
18
+ ${chalk.cyan('• minimal')}${chalk.boldGray(' - external templates')}
19
+ ${chalk.cyan('• standard')}${chalk.boldGray(' - minimal + i18n & built-in SCSS utilities')}
20
+ ${chalk.cyan('• full')}${chalk.boldGray(' - standard + deployment helpers & built-in testing toolkit')}
21
+ ${chalk.cyan('• cicd')}${chalk.boldGray(' - minimal + deployment helpers')}`
22
+ )
23
+ .option('--i18n', 'Use i18n & json-based content')
24
+ .option('--deploy-helper', 'Use Docker & deployment helpers')
25
+ .option('--testin-nutin', 'Use built-in testing toolkit')
26
+ .option('--transition', 'Use animated view transitions\nNote: may interfere with CSS `position: fixed`, `z-index`…')
24
27
  .action(async (projectName, cliOptions) => {
25
28
  print.blue('🚀 Welcome to nutin !');
26
-
27
29
  try {
28
- const answers = await promptUser(projectName, cliOptions);
29
- await createProject(answers);
30
- displaySuccessMessage(answers);
30
+ const preferences = await promptUser(projectName, cliOptions);
31
+ await createProject(preferences);
32
+ displaySuccessMessage(preferences);
31
33
  } catch (error) {
32
- print.boldError(`❌ Error creating project: ${error.message}`);
34
+ print.boldError(`❌ Error creating project: ${error.stack}`);
33
35
  process.exit(1);
34
36
  }
35
37
  });
36
38
 
37
39
  program.parse();
38
40
  }
41
+
@@ -0,0 +1,40 @@
1
+ import { getCiCommand } from './package-manager.mjs';
2
+ import { packageVersion } from './version.mjs';
3
+
4
+ const PRESET_MAP = {
5
+ default: { template: false, stylinNutin: false, i18n: false, deployHelper: false, testinNutin: false },
6
+ minimal: { template: true, stylinNutin: false, i18n: false, deployHelper: false, testinNutin: false },
7
+ standard: { template: true, stylinNutin: true, i18n: true, deployHelper: false, testinNutin: false },
8
+ full: { template: true, stylinNutin: true, i18n: true, deployHelper: true, testinNutin: true },
9
+ cicd: { template: true, stylinNutin: false, i18n: false, deployHelper: true, testinNutin: false },
10
+ };
11
+
12
+ export const defaults = {
13
+ projectName: 'my-nutin-app',
14
+ packageManager: 'npm',
15
+ ...PRESET_MAP['default']
16
+ };
17
+
18
+ export class ContextBuilder {
19
+ buildContext(preferences) {
20
+ const version = packageVersion;
21
+ const ciCommand = getCiCommand(preferences.packageManager);
22
+
23
+ const preset = PRESET_MAP[preferences.preset] ?? PRESET_MAP['default'];
24
+
25
+ return {
26
+ projectName: preferences.projectName,
27
+ packageManager: preferences.packageManager,
28
+
29
+ template: preset.template,
30
+ stylinNutin: preset.stylinNutin,
31
+ i18n: preferences.i18n ?? preset.i18n,
32
+ deployHelper: preferences.deployHelper ?? preset.deployHelper,
33
+ testinNutin: preferences.testinNutin ?? preset.testinNutin,
34
+ transition: preferences.transition,
35
+
36
+ ciCommand: ciCommand,
37
+ version: version
38
+ };
39
+ }
40
+ }
@@ -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 ProjectGenerator {
18
+ export class FileGenerator {
22
19
  constructor() {
23
20
  this.compiler = new TemplateCompiler();
24
21
  }
25
22
 
26
- async createProject(answers) {
27
- const { projectName } = answers;
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
- buildContext(answers) {
59
- const version = packageVersion;
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
  }
@@ -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('\n🔧 Initializing Git repository...');
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(`\n📦 Installing dependencies with ${packageManager}...`);
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(answers) {
42
- const { testinNutin, packageManager, projectName } = answers;
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 --name ${projectName}-container ${projectName}:latest`,
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
- return testinNutin
65
- ? {
66
- ...baseScripts,
67
- ...testinNutinScripts
68
- }
69
- : baseScripts;
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, answers) {
73
- const { testinNutin, projectName, packageManager } = answers;
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(answers);
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, answers) {
113
+ export async function generateTsconfigJson(projectPath, context) {
111
114
  const tsconfig = {
112
115
  "compilerOptions": {
113
116
  "baseUrl": "./",
package/lib/src/print.mjs CHANGED
@@ -12,6 +12,7 @@ export const chalk = {
12
12
  boldYellow: (s) => `\x1b[1;33m${s}\x1b[0m`,
13
13
  boldCyan: (s) => `\x1b[1;36m${s}\x1b[0m`,
14
14
  boldPurple: (s) => `\x1b[1;35m${s}\x1b[0m`,
15
+ boldGray: (s) => `\x1b[1;90m${s}\x1b[0m`,
15
16
  };
16
17
 
17
18
  export const print = {
@@ -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(preferences) {
20
+ const { projectName } = preferences;
21
+ const projectPath = path.join(process.cwd(), projectName);
22
+ const context = this.builder.buildContext(preferences);
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(preferences) {
52
+ return projectGenerator.createProject(preferences);
53
+ }