@cerema/cadriciel 1.7.7 → 1.8.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.
@@ -11,6 +11,157 @@ module.exports = (args) => {
11
11
  const CACHE_DIR = path.join(os.homedir(), '.cadriciel', 'starters-cache');
12
12
  const PACKAGE_NAME = '@cadriciel/starters';
13
13
 
14
+ // Helper: Parse CLI arguments
15
+ const parseArgs = (cmdArgs) => {
16
+ const options = {
17
+ name: null,
18
+ template: null,
19
+ features: null,
20
+ allFeatures: false,
21
+ defaults: false,
22
+ list: false,
23
+ listFeatures: null,
24
+ yes: false,
25
+ test: false,
26
+ help: false
27
+ };
28
+
29
+ for (let i = 0; i < cmdArgs.length; i++) {
30
+ const arg = cmdArgs[i];
31
+ const nextArg = cmdArgs[i + 1];
32
+
33
+ switch (arg) {
34
+ case '-n':
35
+ case '--name':
36
+ options.name = nextArg;
37
+ i++;
38
+ break;
39
+ case '-t':
40
+ case '--template':
41
+ options.template = nextArg;
42
+ i++;
43
+ break;
44
+ case '-f':
45
+ case '--features':
46
+ options.features = nextArg ? nextArg.split(',').map(f => f.trim()) : [];
47
+ i++;
48
+ break;
49
+ case '-a':
50
+ case '--all-features':
51
+ options.allFeatures = true;
52
+ break;
53
+ case '-d':
54
+ case '--defaults':
55
+ options.defaults = true;
56
+ break;
57
+ case '-l':
58
+ case '--list':
59
+ options.list = true;
60
+ break;
61
+ case '--list-features':
62
+ options.listFeatures = nextArg;
63
+ i++;
64
+ break;
65
+ case '-y':
66
+ case '--yes':
67
+ options.yes = true;
68
+ break;
69
+ case '--test':
70
+ options.test = true;
71
+ break;
72
+ case '-h':
73
+ case '--help':
74
+ options.help = true;
75
+ break;
76
+ default:
77
+ // Positional argument (project name if not already set)
78
+ if (!arg.startsWith('-') && !options.name && arg !== 'create') {
79
+ options.name = arg;
80
+ }
81
+ break;
82
+ }
83
+ }
84
+
85
+ return options;
86
+ };
87
+
88
+ // Helper: Display help
89
+ const showHelp = () => {
90
+ console.log(chalk.bold.cyan('\nUsage: cad create [options] [project-name]\n'));
91
+ console.log(chalk.bold('Options:'));
92
+ console.log(' -n, --name <name> Project name');
93
+ console.log(' -t, --template <id> Template ID (skip template selection)');
94
+ console.log(' -f, --features <list> Comma-separated list of features to enable');
95
+ console.log(' -a, --all-features Enable all available features');
96
+ console.log(' -d, --defaults Use default feature selection');
97
+ console.log(' -l, --list List available templates');
98
+ console.log(' --list-features <id> List features for a specific template');
99
+ console.log(' -y, --yes Auto-confirm prompts (e.g., overwrite)');
100
+ console.log(' --test Use current directory for starters (dev mode)');
101
+ console.log(' -h, --help Show this help message');
102
+ console.log(chalk.bold('\nExamples:'));
103
+ console.log(chalk.gray(' # Interactive mode (default)'));
104
+ console.log(' cad create');
105
+ console.log(' cad create my-project');
106
+ console.log(chalk.gray('\n # Automated mode'));
107
+ console.log(' cad create -n my-project -t basic -d');
108
+ console.log(' cad create -n my-project -t basic -f auth,api');
109
+ console.log(' cad create -n my-project -t basic -a -y');
110
+ console.log(chalk.gray('\n # List templates and features'));
111
+ console.log(' cad create --list');
112
+ console.log(' cad create --list-features basic');
113
+ };
114
+
115
+ // Helper: List templates
116
+ const listTemplates = (starters) => {
117
+ console.log(chalk.bold.cyan('\nAvailable templates:\n'));
118
+ starters.forEach(s => {
119
+ console.log(` ${chalk.bold.green(s.id)}`);
120
+ console.log(` Name: ${s.name}`);
121
+ console.log(` Description: ${s.description}`);
122
+ console.log('');
123
+ });
124
+ };
125
+
126
+ // Helper: List features for a template
127
+ const listFeatures = (starter) => {
128
+ const optionsConfig = starter.config.options || {};
129
+ const coreFeatures = [];
130
+ const optionalFeatures = [];
131
+
132
+ Object.keys(optionsConfig).forEach(key => {
133
+ const opt = optionsConfig[key];
134
+ if (opt.type === 'core') {
135
+ coreFeatures.push({ id: key, ...opt });
136
+ } else {
137
+ optionalFeatures.push({ id: key, ...opt });
138
+ }
139
+ });
140
+
141
+ console.log(chalk.bold.cyan(`\nFeatures for template: ${starter.name}\n`));
142
+
143
+ if (coreFeatures.length > 0) {
144
+ console.log(chalk.bold('Core (always included):'));
145
+ coreFeatures.forEach(f => {
146
+ console.log(` ${chalk.gray('•')} ${f.id}${f.description ? ` - ${f.description}` : ''}`);
147
+ });
148
+ console.log('');
149
+ }
150
+
151
+ if (optionalFeatures.length > 0) {
152
+ console.log(chalk.bold('Optional:'));
153
+ optionalFeatures.forEach(f => {
154
+ const defaultTag = f.default ? chalk.green(' [default]') : '';
155
+ console.log(` ${chalk.gray('•')} ${chalk.bold(f.id)}${f.description ? ` - ${f.description}` : ''}${defaultTag}`);
156
+ });
157
+ console.log('');
158
+ }
159
+
160
+ if (optionalFeatures.length === 0 && coreFeatures.length === 0) {
161
+ console.log(chalk.yellow(' No features defined for this template.'));
162
+ }
163
+ };
164
+
14
165
  // Helper: Fetch/Update the starters package
15
166
  const fetchStarters = async () => {
16
167
  const spinner = ora('Fetching latest starters...').start();
@@ -71,19 +222,23 @@ module.exports = (args) => {
71
222
  };
72
223
 
73
224
  // Helper: Generate Project
74
- const generateProject = async (starter, options, targetName) => {
225
+ const generateProject = async (starter, options, targetName, autoConfirm = false) => {
75
226
  const targetPath = path.resolve(process.cwd(), targetName);
76
227
 
77
228
  if (fs.existsSync(targetPath)) {
78
- const { replace } = await inquirer.prompt([
79
- {
80
- type: 'confirm',
81
- name: 'replace',
82
- message: `Directory ${targetName} already exists. Overwrite?`,
83
- default: false
84
- }
85
- ]);
86
- if (!replace) return;
229
+ if (autoConfirm) {
230
+ console.log(chalk.yellow(`Directory ${targetName} already exists. Overwriting...`));
231
+ } else {
232
+ const { replace } = await inquirer.prompt([
233
+ {
234
+ type: 'confirm',
235
+ name: 'replace',
236
+ message: `Directory ${targetName} already exists. Overwrite?`,
237
+ default: false
238
+ }
239
+ ]);
240
+ if (!replace) return;
241
+ }
87
242
  }
88
243
 
89
244
  const spinner = ora(`Generating project ${targetName}...`).start();
@@ -182,16 +337,23 @@ module.exports = (args) => {
182
337
  description: 'Create a new project from Cadriciel templates',
183
338
  },
184
339
  start: async (cmdArgs) => {
340
+ // Parse CLI arguments
341
+ const opts = parseArgs(cmdArgs);
342
+
343
+ // Show help if requested
344
+ if (opts.help) {
345
+ showHelp();
346
+ return;
347
+ }
348
+
185
349
  console.log(chalk.bold.cyan('Cadriciel Project Generator'));
186
350
 
187
- // Check for --test flag
188
- const isTestMode = cmdArgs.includes('--test');
189
- // Remove --test from args so it doesn't interfere with project name
190
- const filteredArgs = cmdArgs.filter(arg => arg !== '--test');
351
+ // Determine if running in automated mode
352
+ const isAutomated = opts.template && opts.name && (opts.features || opts.allFeatures || opts.defaults);
191
353
 
192
354
  // 1. Fetch Starters
193
355
  let startersPath;
194
- if (isTestMode) {
356
+ if (opts.test) {
195
357
  console.log(chalk.yellow('Running in TEST mode: Using current directory for starters.'));
196
358
  startersPath = process.cwd();
197
359
  } else {
@@ -206,12 +368,26 @@ module.exports = (args) => {
206
368
  return;
207
369
  }
208
370
 
209
- // 2. Ask for Project Name (if not provided)
210
- let args = [...filteredArgs];
211
- if (args.length > 0 && args[0] === 'create') {
212
- args.shift();
371
+ // Handle --list: show available templates and exit
372
+ if (opts.list) {
373
+ listTemplates(starters);
374
+ return;
375
+ }
376
+
377
+ // Handle --list-features: show features for a template and exit
378
+ if (opts.listFeatures) {
379
+ const starter = starters.find(s => s.id === opts.listFeatures);
380
+ if (!starter) {
381
+ console.log(chalk.red(`Template '${opts.listFeatures}' not found.`));
382
+ console.log(chalk.gray('Use --list to see available templates.'));
383
+ return;
384
+ }
385
+ listFeatures(starter);
386
+ return;
213
387
  }
214
- let targetName = args[0];
388
+
389
+ // 2. Get Project Name
390
+ let targetName = opts.name;
215
391
  if (!targetName) {
216
392
  const answers = await inquirer.prompt([
217
393
  {
@@ -225,25 +401,38 @@ module.exports = (args) => {
225
401
  }
226
402
 
227
403
  // 3. Select Starter
228
- const { selectedStarterId } = await inquirer.prompt([
229
- {
230
- type: 'list',
231
- name: 'selectedStarterId',
232
- message: 'Select a template:',
233
- choices: starters.map(s => ({
234
- name: `${chalk.bold(s.name)} - ${s.description}${isTestMode ? ` (${s.path})` : ''}`,
235
- value: s.id
236
- }))
404
+ let starter;
405
+ if (opts.template) {
406
+ starter = starters.find(s => s.id === opts.template);
407
+ if (!starter) {
408
+ console.log(chalk.red(`Template '${opts.template}' not found.`));
409
+ console.log(chalk.gray('Use --list to see available templates.'));
410
+ return;
237
411
  }
238
- ]);
239
-
240
- const starter = starters.find(s => s.id === selectedStarterId);
412
+ console.log(chalk.gray(`Using template: ${starter.name}`));
413
+ } else if (opts.defaults || opts.allFeatures || opts.features) {
414
+ // In automated mode without explicit template, use first available
415
+ starter = starters[0];
416
+ console.log(chalk.gray(`Using default template: ${starter.name}`));
417
+ } else {
418
+ const { selectedStarterId } = await inquirer.prompt([
419
+ {
420
+ type: 'list',
421
+ name: 'selectedStarterId',
422
+ message: 'Select a template:',
423
+ choices: starters.map(s => ({
424
+ name: `${chalk.bold(s.name)} - ${s.description}${opts.test ? ` (${s.path})` : ''}`,
425
+ value: s.id
426
+ }))
427
+ }
428
+ ]);
429
+ starter = starters.find(s => s.id === selectedStarterId);
430
+ }
241
431
 
242
432
  // 4. Select Features
243
- // Determine selectable features from config
244
433
  const optionsConfig = starter.config.options || {};
245
434
  const selectableFeatures = Object.keys(optionsConfig)
246
- .filter(key => optionsConfig[key].type !== 'core') // Exclude core
435
+ .filter(key => optionsConfig[key].type !== 'core')
247
436
  .map(key => ({
248
437
  name: optionsConfig[key].description || key,
249
438
  value: key,
@@ -251,7 +440,26 @@ module.exports = (args) => {
251
440
  }));
252
441
 
253
442
  let selectedFeatures = [];
254
- if (selectableFeatures.length > 0) {
443
+
444
+ if (opts.allFeatures) {
445
+ // Enable all features
446
+ selectedFeatures = selectableFeatures.map(f => f.value);
447
+ console.log(chalk.gray(`Enabling all features: ${selectedFeatures.join(', ') || 'none'}`));
448
+ } else if (opts.features) {
449
+ // Use specified features
450
+ const validFeatures = selectableFeatures.map(f => f.value);
451
+ const invalidFeatures = opts.features.filter(f => !validFeatures.includes(f));
452
+ if (invalidFeatures.length > 0) {
453
+ console.log(chalk.yellow(`Warning: Unknown features ignored: ${invalidFeatures.join(', ')}`));
454
+ }
455
+ selectedFeatures = opts.features.filter(f => validFeatures.includes(f));
456
+ console.log(chalk.gray(`Selected features: ${selectedFeatures.join(', ') || 'none'}`));
457
+ } else if (opts.defaults) {
458
+ // Use default features
459
+ selectedFeatures = selectableFeatures.filter(f => f.checked).map(f => f.value);
460
+ console.log(chalk.gray(`Using default features: ${selectedFeatures.join(', ') || 'none'}`));
461
+ } else if (selectableFeatures.length > 0) {
462
+ // Interactive mode
255
463
  const featureAnswers = await inquirer.prompt([
256
464
  {
257
465
  type: 'checkbox',
@@ -264,9 +472,9 @@ module.exports = (args) => {
264
472
  }
265
473
 
266
474
  // 5. Generate
267
- await generateProject(starter, { selectedFeatures }, targetName);
475
+ await generateProject(starter, { selectedFeatures }, targetName, opts.yes);
268
476
 
269
- if (isTestMode) process.exit(0);
477
+ if (opts.test) process.exit(0);
270
478
  },
271
479
  };
272
480
  };
package/cli.js CHANGED
@@ -10,6 +10,7 @@ const userHomeDir = os.homedir(); // Fetching the user's home directory.
10
10
  const boxen = require('boxen'); // For creating boxes in the console.
11
11
  const axios = require('axios'); // For making HTTP requests.
12
12
 
13
+
13
14
  // Locating the '.cadriciel' directory starting from the current working directory.
14
15
  const CADRICIEL_PATH = findCadricielDir(process.cwd());
15
16
  const CADRICIEL_COMMAND = 'cad';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerema/cadriciel",
3
- "version": "1.7.7",
3
+ "version": "1.8.1",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "npm": ">=8.0.0",
package/.gitlab-ci.yml DELETED
@@ -1,16 +0,0 @@
1
- image: node:20
2
-
3
- stages:
4
- - publish
5
-
6
- publish:
7
- stage: publish
8
- tags:
9
- - k8-runner
10
- script:
11
- - npm install
12
- - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
13
- - npm publish
14
- rules:
15
- - if: '$CI_COMMIT_TAG'
16
- - if: '$CI_COMMIT_BRANCH == "main"'
@@ -1,27 +0,0 @@
1
-
2
- MINIO_URL=localhost
3
- MINIO_ROOT_USER=minioadmin
4
- MINIO_ROOT_PASSWORD=minioadmin
5
-
6
- POSTGRES_DB=sysadyp
7
- POSTGRES_PASSWORD=password
8
- PG_USER=postgres
9
-
10
- REDIS_PASSWORD=password
11
- KEYCLOAK_ADMIN_USER=admin
12
- KEYCLOAK_ADMIN_PASSWORD=dreammotion
13
-
14
- DOCKER_REGISTRY=cerema
15
- PROJECT_NAME=sysadyp
16
- VERSION=latest
17
-
18
- # CADRICIEL
19
- CADRICIEL_PG_DB="postgres://postgres:password@postgres:5432/sysadyp"
20
- CADRICIEL_PG_DBAPI="postgres://postgres:password@postgres:5432/sysadyp"
21
- CADRICIEL_PG_DB_SCHEMA="sysadyp"
22
- CADRICIEL_API_PORT=3000
23
- CADRICIEL_APP_PORT=4200
24
- CADRICIEL_AUTH_STRATEGY="guest"
25
- CADRICIEL_ORION_URI="http://localhost:9090"
26
- CADRICIEL_ORION_REALM="62540d37-7443-44e2-be54-2b402e899660"
27
-