nestgen-cli 0.0.7 → 0.1.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/dist/engine/architecture-profile.js +10 -0
- package/dist/engine/module-generator.js +142 -0
- package/dist/engine/project-config.js +39 -0
- package/dist/engine/resource-generator.js +117 -0
- package/dist/engine/resource-spec.js +44 -0
- package/nestgen.js +318 -83
- package/nestjs-generator/features/add_module.sh +68 -24
- package/nestjs-generator/features/configure_bootstrap.mjs +39 -0
- package/nestjs-generator/features/docker.sh +169 -36
- package/nestjs-generator/features/git.sh +4 -2
- package/nestjs-generator/features/inject_module_to_app.sh +22 -69
- package/nestjs-generator/features/preflight.mjs +55 -0
- package/nestjs-generator/features/prisma.sh +48 -8
- package/nestjs-generator/features/resource_name.mjs +57 -0
- package/nestjs-generator/features/swagger.sh +7 -20
- package/nestjs-generator/features/typeorm.sh +68 -7
- package/nestjs-generator/features/update_app_module.mjs +146 -0
- package/nestjs-generator/features/utils.sh +52 -14
- package/nestjs-generator/generate_project.sh +41 -71
- package/package.json +56 -31
- package/readme.md +90 -133
package/nestgen.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
+
import { inspectProject } from './nestjs-generator/features/preflight.mjs';
|
|
10
|
+
import { generateModule } from './dist/engine/module-generator.js';
|
|
11
|
+
import { generateResource } from './dist/engine/resource-generator.js';
|
|
12
|
+
import { parseResourceFields } from './dist/engine/resource-spec.js';
|
|
13
|
+
import { configFileName, defaultConfig, loadConfigIfPresent, writeConfig } from './dist/engine/project-config.js';
|
|
14
|
+
import { parseArchitectureProfile } from './dist/engine/architecture-profile.js';
|
|
9
15
|
|
|
10
16
|
// ────── Resolve __dirname compatible ES Modules
|
|
11
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -15,30 +21,155 @@ const __dirname = path.dirname(__filename);
|
|
|
15
21
|
const ROOT_PATH = process.env.NESTGEN_ROOT || path.resolve(__dirname, './nestjs-generator');
|
|
16
22
|
const GENERATE_SCRIPT = path.join(ROOT_PATH, 'generate_project.sh');
|
|
17
23
|
const ADD_MODULE_SCRIPT = path.join(ROOT_PATH, './features/add_module.sh');
|
|
24
|
+
const SUPPORTED_ORMS = new Set(['typeorm', 'prisma']);
|
|
25
|
+
const MODULE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i;
|
|
18
26
|
|
|
19
27
|
// ────── Logo CLI
|
|
20
28
|
function printLogo() {
|
|
21
|
-
console.log(
|
|
29
|
+
console.log(
|
|
30
|
+
chalk.magentaBright(`
|
|
22
31
|
███╗ ██╗███████╗███████╗████████╗ ██████╗ ███████╗███╗ ██╗
|
|
23
32
|
████╗ ██║██╔════╝██╔════╝╚══██╔══╝██╔════╝ ██╔════╝████╗ ██║
|
|
24
33
|
██╔██╗ ██║█████╗ ███████╗ ██║ ██║ ███╗█████╗ ██╔██╗ ██║
|
|
25
34
|
██║╚██╗██║██╔══╝ ╚════██║ ██║ ██║ ██║██╔══╝ ██║╚██╗██║
|
|
26
35
|
██║ ╚████║███████╗███████║ ██║ ╚██████╔╝███████╗██║ ╚████║
|
|
27
36
|
╚═╝ ╚═══╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝
|
|
28
|
-
`)
|
|
37
|
+
`),
|
|
38
|
+
);
|
|
29
39
|
console.log(chalk.cyan.bold('✨ NestGen CLI — Générateur modulaire NestJS'));
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
// ────── Helpers
|
|
33
|
-
function isNestProject() {
|
|
43
|
+
export function isNestProject() {
|
|
34
44
|
return fs.existsSync(path.resolve('./src/app.module.ts'));
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
function
|
|
38
|
-
const moduleName =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
export function validateModuleName(value) {
|
|
48
|
+
const moduleName = String(value ?? '').trim();
|
|
49
|
+
|
|
50
|
+
if (!MODULE_NAME_PATTERN.test(moduleName)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'Le nom du module doit commencer par une lettre et ne contenir que des lettres, chiffres, tirets ou underscores.',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return moduleName.toLowerCase();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function validateOrm(value) {
|
|
60
|
+
const orm = String(value ?? '')
|
|
61
|
+
.trim()
|
|
62
|
+
.toLowerCase();
|
|
63
|
+
|
|
64
|
+
if (!SUPPORTED_ORMS.has(orm)) {
|
|
65
|
+
throw new Error(`ORM non supporté : ${value}. Valeurs acceptées : ${[...SUPPORTED_ORMS].join(', ')}.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return orm;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resolveProjectPath(value) {
|
|
72
|
+
const projectPath = String(value ?? '').trim();
|
|
73
|
+
|
|
74
|
+
if (!projectPath || projectPath.includes('\0')) {
|
|
75
|
+
throw new Error('Le dossier cible doit être un chemin non vide valide.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return path.resolve(projectPath);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function parseModuleArgs(args) {
|
|
82
|
+
const parsed = parseCliArgs(args);
|
|
83
|
+
if (parsed.command !== 'module' || !parsed.positionals[0])
|
|
84
|
+
throw new Error('La commande module requiert un nom de module.');
|
|
85
|
+
return { moduleName: validateModuleName(parsed.positionals[0]), orm: validateOrm(parsed.options.orm ?? 'typeorm') };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function parseCliArgs(args) {
|
|
89
|
+
const options = {
|
|
90
|
+
orm: undefined,
|
|
91
|
+
profile: undefined,
|
|
92
|
+
packageManager: undefined,
|
|
93
|
+
noInteractive: false,
|
|
94
|
+
quiet: false,
|
|
95
|
+
verbose: false,
|
|
96
|
+
color: true,
|
|
97
|
+
help: false,
|
|
98
|
+
version: false,
|
|
99
|
+
dryRun: false,
|
|
100
|
+
};
|
|
101
|
+
const positionals = [];
|
|
102
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
103
|
+
const argument = args[index];
|
|
104
|
+
if (argument === '--orm') {
|
|
105
|
+
const value = args[++index];
|
|
106
|
+
if (!value || value.startsWith('-')) throw new Error('--orm requiert une valeur.');
|
|
107
|
+
options.orm = value;
|
|
108
|
+
} else if (argument.startsWith('--orm=')) options.orm = argument.slice(6);
|
|
109
|
+
else if (argument === '--profile') options.profile = args[++index];
|
|
110
|
+
else if (argument.startsWith('--profile=')) options.profile = argument.slice(10);
|
|
111
|
+
else if (argument === '--package-manager') options.packageManager = args[++index];
|
|
112
|
+
else if (argument.startsWith('--package-manager=')) options.packageManager = argument.slice(18);
|
|
113
|
+
else if (argument === '--fields') options.fields = args[++index];
|
|
114
|
+
else if (argument.startsWith('--fields=')) options.fields = argument.slice(9);
|
|
115
|
+
else if (argument === '--route') options.route = args[++index];
|
|
116
|
+
else if (argument.startsWith('--route=')) options.route = argument.slice(8);
|
|
117
|
+
else if (argument === '--table') options.table = args[++index];
|
|
118
|
+
else if (argument.startsWith('--table=')) options.table = argument.slice(8);
|
|
119
|
+
else if (argument === '--no-interactive') options.noInteractive = true;
|
|
120
|
+
else if (argument === '--quiet') options.quiet = true;
|
|
121
|
+
else if (argument === '--verbose') options.verbose = true;
|
|
122
|
+
else if (argument === '--dry-run') options.dryRun = true;
|
|
123
|
+
else if (argument === '--no-color') options.color = false;
|
|
124
|
+
else if (argument === '--help' || argument === '-h') options.help = true;
|
|
125
|
+
else if (argument === '--version' || argument === '-V') options.version = true;
|
|
126
|
+
else if (argument.startsWith('-')) throw new Error(`Option inconnue : ${argument}`);
|
|
127
|
+
else positionals.push(argument);
|
|
128
|
+
}
|
|
129
|
+
return { command: positionals.shift(), positionals, options };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function createModulePlan(projectRoot, moduleName, orm) {
|
|
133
|
+
const normalizedName = validateModuleName(moduleName);
|
|
134
|
+
const normalizedOrm = validateOrm(orm);
|
|
135
|
+
const resourceRoot = `src/app/${normalizedName}`;
|
|
136
|
+
return {
|
|
137
|
+
operation: 'module',
|
|
138
|
+
module: normalizedName,
|
|
139
|
+
orm: normalizedOrm,
|
|
140
|
+
projectRoot,
|
|
141
|
+
files: [
|
|
142
|
+
`${resourceRoot}/core/domain/entities/${normalizedName}.entity.ts`,
|
|
143
|
+
`${resourceRoot}/core/domain/ports/${normalizedName}.repository.ts`,
|
|
144
|
+
`${resourceRoot}/core/application/commands/create-${normalizedName}.command.ts`,
|
|
145
|
+
`${resourceRoot}/core/application/commands/create-${normalizedName}.handler.ts`,
|
|
146
|
+
`${resourceRoot}/interfaces/dtos/create-${normalizedName}.dto.ts`,
|
|
147
|
+
`${resourceRoot}/interfaces/controllers/${normalizedName}.controller.ts`,
|
|
148
|
+
`${resourceRoot}/${normalizedName}.module.ts`,
|
|
149
|
+
],
|
|
150
|
+
mutations: [
|
|
151
|
+
'Ajoute le module dans @Module({ imports }) de src/app.module.ts.',
|
|
152
|
+
'Ajoute CqrsModule dans src/app.module.ts si nécessaire.',
|
|
153
|
+
...(normalizedOrm === 'typeorm'
|
|
154
|
+
? ['Ajoute la configuration TypeORM racine seulement si elle est absente.']
|
|
155
|
+
: []),
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function runBashScript(scriptPath, args = [], env = {}, quiet = false) {
|
|
161
|
+
const result = spawnSync('bash', [scriptPath, ...args], {
|
|
162
|
+
env: { ...process.env, ...env },
|
|
163
|
+
stdio: quiet ? 'pipe' : 'inherit',
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (result.error) {
|
|
167
|
+
throw result.error;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (result.status !== 0) {
|
|
171
|
+
throw new Error(`Le script ${path.basename(scriptPath)} a échoué avec le code ${result.status ?? 'inconnu'}.`);
|
|
172
|
+
}
|
|
42
173
|
}
|
|
43
174
|
|
|
44
175
|
// ────── Commande : INIT
|
|
@@ -49,12 +180,14 @@ async function askInitQuestions() {
|
|
|
49
180
|
name: 'projectName',
|
|
50
181
|
message: '📛 Nom du projet :',
|
|
51
182
|
default: 'my-app',
|
|
183
|
+
validate: validatePromptValue(validateModuleName),
|
|
52
184
|
},
|
|
53
185
|
{
|
|
54
186
|
type: 'input',
|
|
55
187
|
name: 'projectPath',
|
|
56
188
|
message: '📁 Dossier cible :',
|
|
57
189
|
default: './',
|
|
190
|
+
validate: validatePromptValue(resolveProjectPath),
|
|
58
191
|
},
|
|
59
192
|
{
|
|
60
193
|
type: 'list',
|
|
@@ -93,76 +226,73 @@ async function askInitQuestions() {
|
|
|
93
226
|
name: 'modules',
|
|
94
227
|
message: '📦 Modules à générer (séparés par des espaces) :',
|
|
95
228
|
default: 'user',
|
|
96
|
-
filter: (input) =>
|
|
97
|
-
|
|
229
|
+
filter: (input) =>
|
|
230
|
+
input
|
|
231
|
+
.split(' ')
|
|
232
|
+
.map((s) => s.trim())
|
|
233
|
+
.filter(Boolean),
|
|
234
|
+
validate: validatePromptValue((modules) => modules.forEach(validateModuleName)),
|
|
235
|
+
},
|
|
98
236
|
]);
|
|
99
237
|
}
|
|
100
238
|
|
|
101
|
-
|
|
102
|
-
|
|
239
|
+
function validatePromptValue(validator) {
|
|
240
|
+
return (value) => {
|
|
241
|
+
try {
|
|
242
|
+
validator(value);
|
|
243
|
+
return true;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return error.message;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function runInteractiveInit(options) {
|
|
251
|
+
if (!options.quiet) printLogo();
|
|
252
|
+
if (options.noInteractive)
|
|
253
|
+
throw new Error(
|
|
254
|
+
'init --no-interactive requiert des options de projet qui ne sont pas encore prises en charge.',
|
|
255
|
+
);
|
|
103
256
|
|
|
104
257
|
if (!fs.existsSync(GENERATE_SCRIPT)) {
|
|
105
|
-
|
|
106
|
-
process.exit(1);
|
|
258
|
+
throw new Error(`Script introuvable : ${GENERATE_SCRIPT}`);
|
|
107
259
|
}
|
|
108
260
|
|
|
109
261
|
const answers = await askInitQuestions();
|
|
110
|
-
const {
|
|
111
|
-
projectName,
|
|
112
|
-
projectPath,
|
|
113
|
-
packageManager,
|
|
114
|
-
orm,
|
|
115
|
-
withSwagger,
|
|
116
|
-
withDocker,
|
|
117
|
-
withGit,
|
|
118
|
-
modules,
|
|
119
|
-
} = answers;
|
|
262
|
+
const { projectName, projectPath, packageManager, orm, withSwagger, withDocker, withGit, modules } = answers;
|
|
120
263
|
|
|
121
264
|
const env = {
|
|
122
|
-
APP_NAME: projectName,
|
|
123
|
-
PROJECT_PATH:
|
|
265
|
+
APP_NAME: validateModuleName(projectName),
|
|
266
|
+
PROJECT_PATH: resolveProjectPath(projectPath),
|
|
124
267
|
PM: packageManager,
|
|
125
|
-
ORM: orm,
|
|
268
|
+
ORM: validateOrm(orm),
|
|
126
269
|
WITH_SWAGGER: withSwagger ? 'y' : 'n',
|
|
127
270
|
WITH_DOCKER: withDocker ? 'y' : 'n',
|
|
128
271
|
WITH_GIT: withGit ? 'y' : 'n',
|
|
129
|
-
MODULES: modules.join(' '),
|
|
272
|
+
MODULES: modules.map(validateModuleName).join(' '),
|
|
130
273
|
};
|
|
131
274
|
|
|
132
|
-
const envExport = Object.entries(env)
|
|
133
|
-
.map(([key, val]) => `${key}="${val}"`)
|
|
134
|
-
.join(' ');
|
|
135
|
-
|
|
136
275
|
console.log('\n🚀 Lancement de la génération du projet...\n');
|
|
137
|
-
|
|
138
|
-
execSync(`env ${envExport} bash "${GENERATE_SCRIPT}"`, {
|
|
139
|
-
stdio: 'inherit',
|
|
140
|
-
});
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error(chalk.red('❌ Une erreur est survenue pendant la génération.'));
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
276
|
+
runBashScript(GENERATE_SCRIPT, [], env);
|
|
145
277
|
}
|
|
146
278
|
|
|
147
279
|
// ────── Commande : MODULE
|
|
148
|
-
async function runModuleGeneration(
|
|
149
|
-
printLogo();
|
|
280
|
+
async function runModuleGeneration(parsed) {
|
|
281
|
+
if (!parsed.options.quiet) printLogo();
|
|
150
282
|
|
|
151
283
|
let moduleName, orm;
|
|
152
284
|
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
console.log(chalk.red('❌ Tu dois fournir un nom de module.'));
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
285
|
+
if (parsed.positionals[0]) {
|
|
286
|
+
moduleName = validateModuleName(parsed.positionals[0]);
|
|
287
|
+
orm = validateOrm(parsed.options.orm ?? 'typeorm');
|
|
159
288
|
} else {
|
|
289
|
+
if (parsed.options.noInteractive) throw new Error('module --no-interactive requiert un nom de module.');
|
|
160
290
|
const answers = await inquirer.prompt([
|
|
161
291
|
{
|
|
162
292
|
type: 'input',
|
|
163
293
|
name: 'moduleName',
|
|
164
294
|
message: '📦 Nom du module :',
|
|
165
|
-
validate:
|
|
295
|
+
validate: validatePromptValue(validateModuleName),
|
|
166
296
|
},
|
|
167
297
|
{
|
|
168
298
|
type: 'list',
|
|
@@ -172,47 +302,152 @@ async function runModuleGeneration(args) {
|
|
|
172
302
|
default: 'typeorm',
|
|
173
303
|
},
|
|
174
304
|
]);
|
|
175
|
-
moduleName = answers.moduleName;
|
|
176
|
-
orm = answers.orm;
|
|
305
|
+
moduleName = validateModuleName(answers.moduleName);
|
|
306
|
+
orm = validateOrm(answers.orm);
|
|
177
307
|
}
|
|
178
308
|
|
|
179
309
|
if (!isNestProject()) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
310
|
+
throw new Error('Aucun projet NestJS détecté dans ce dossier. Lance cette commande depuis un projet NestJS.');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (parsed.options.dryRun) {
|
|
314
|
+
inspectProject(process.cwd(), orm);
|
|
315
|
+
console.log(JSON.stringify(createModulePlan(process.cwd(), moduleName, orm), null, 2));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!parsed.options.quiet) console.log(chalk.cyan(`\n⚙️ Génération du module ${moduleName}...\n`));
|
|
320
|
+
if (process.env.NESTGEN_ROOT) {
|
|
321
|
+
runBashScript(ADD_MODULE_SCRIPT, [moduleName, orm], {}, parsed.options.quiet);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
generateModule(process.cwd(), moduleName, orm);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function runResourceGeneration(parsed) {
|
|
329
|
+
const name = validateModuleName(parsed.positionals[0] ?? '');
|
|
330
|
+
if (!parsed.options.fields) throw new Error('resource requiert --fields champ:type[,champ:type].');
|
|
331
|
+
const config = loadConfigIfPresent(process.cwd()) ?? defaultConfig;
|
|
332
|
+
const orm = validateOrm(parsed.options.orm ?? config.orm);
|
|
333
|
+
const profile = parseArchitectureProfile(parsed.options.profile ?? config.profile);
|
|
334
|
+
const fields = parseResourceFields(parsed.options.fields.split(',').filter(Boolean));
|
|
335
|
+
const route = parsed.options.route ?? `${name}s`;
|
|
336
|
+
const table = parsed.options.table ?? `${name}s`;
|
|
337
|
+
if (!/^[a-z][a-z0-9/-]*$/.test(route) || !/^[a-z][a-z0-9_]*$/.test(table))
|
|
338
|
+
throw new Error('Route ou table invalide.');
|
|
339
|
+
if (parsed.options.dryRun) {
|
|
340
|
+
console.log(JSON.stringify({ operation: 'resource', name, route, table, orm, profile, fields }, null, 2));
|
|
341
|
+
return;
|
|
183
342
|
}
|
|
343
|
+
generateResource(process.cwd(), { name, route, table, fields, orm, profile });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function printUsage() {
|
|
347
|
+
console.log(
|
|
348
|
+
`Usage: nestgen <commande> [options]\n\nCommandes:\n init Génère un projet NestJS en mode interactif\n module <nom> [--orm <orm>] Génère un module\n resource <nom> --fields ... Génère un CRUD TypeORM
|
|
349
|
+
config init|show Gère nestgen.config.json
|
|
350
|
+
doctor Vérifie l'installation\n\nOptions:\n -h, --help Affiche cette aide\n -V, --version Affiche la version\n --no-interactive Refuse les prompts\n --quiet Supprime les sorties non essentielles\n --verbose Active les diagnostics\n --no-color Désactive les couleurs\n --profile <simple|advanced> Choisit le profil d'architecture
|
|
351
|
+
--package-manager <pm> Définit le package manager du config init
|
|
352
|
+
--dry-run Affiche le plan sans écrire`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
184
355
|
|
|
356
|
+
function runDoctor() {
|
|
357
|
+
const checks = [
|
|
358
|
+
[
|
|
359
|
+
'Node.js',
|
|
360
|
+
process.versions.node,
|
|
361
|
+
Number(process.versions.node.split('.')[0]) >= 24,
|
|
362
|
+
'Installe Node.js 24 LTS ou une version supportée.',
|
|
363
|
+
],
|
|
364
|
+
[
|
|
365
|
+
'Scripts NestGen',
|
|
366
|
+
`${GENERATE_SCRIPT}, ${ADD_MODULE_SCRIPT}`,
|
|
367
|
+
fs.existsSync(GENERATE_SCRIPT) && fs.existsSync(ADD_MODULE_SCRIPT),
|
|
368
|
+
'Réinstalle NestGen.',
|
|
369
|
+
],
|
|
370
|
+
];
|
|
371
|
+
for (const [name, detail, valid, advice] of checks)
|
|
372
|
+
console.log(`${valid ? 'OK' : 'ERREUR'} ${name}: ${detail}${valid ? '' : ` — ${advice}`}`);
|
|
185
373
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
} catch (
|
|
191
|
-
console.
|
|
192
|
-
|
|
374
|
+
const project = inspectProject(process.cwd(), 'typeorm');
|
|
375
|
+
console.log(
|
|
376
|
+
`OK Projet Nest: ${project.packageManager ?? 'lockfile absent'}; connexion TypeORM racine: ${project.hasRootTypeOrmConnection ? 'présente' : 'absente'}.`,
|
|
377
|
+
);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.log(
|
|
380
|
+
`INFO Projet Nest: ${error.message} — Lance doctor depuis un projet Nest compatible pour analyser son intégration.`,
|
|
381
|
+
);
|
|
193
382
|
}
|
|
383
|
+
if (checks.some(([, , valid]) => !valid)) throw new Error('Des prérequis NestGen sont manquants.');
|
|
194
384
|
}
|
|
195
385
|
|
|
196
386
|
// ────── Entrée CLI
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
387
|
+
export async function main(args = process.argv.slice(2)) {
|
|
388
|
+
const parsed = parseCliArgs(args);
|
|
389
|
+
if (!parsed.options.color) chalk.level = 0;
|
|
390
|
+
if (parsed.options.version) {
|
|
391
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
392
|
+
console.log(packageJson.version);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (parsed.options.help || !parsed.command) {
|
|
396
|
+
printUsage();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
switch (parsed.command) {
|
|
401
|
+
case 'init':
|
|
402
|
+
await runInteractiveInit(parsed.options);
|
|
403
|
+
break;
|
|
404
|
+
|
|
405
|
+
case 'module':
|
|
406
|
+
await runModuleGeneration(parsed);
|
|
407
|
+
break;
|
|
408
|
+
|
|
409
|
+
case 'resource':
|
|
410
|
+
runResourceGeneration(parsed);
|
|
411
|
+
break;
|
|
412
|
+
|
|
413
|
+
case 'config': {
|
|
414
|
+
const action = parsed.positionals[0];
|
|
415
|
+
if (action === 'show') {
|
|
416
|
+
console.log(JSON.stringify(loadConfigIfPresent(process.cwd()) ?? defaultConfig, null, 2));
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
if (action === 'init') {
|
|
420
|
+
const config = {
|
|
421
|
+
...defaultConfig,
|
|
422
|
+
orm: validateOrm(parsed.options.orm ?? defaultConfig.orm),
|
|
423
|
+
profile: parseArchitectureProfile(parsed.options.profile),
|
|
424
|
+
packageManager: parsed.options.packageManager ?? defaultConfig.packageManager,
|
|
425
|
+
};
|
|
426
|
+
if (!['npm', 'pnpm', 'yarn'].includes(config.packageManager))
|
|
427
|
+
throw new Error('Package manager invalide.');
|
|
428
|
+
if (parsed.options.dryRun) {
|
|
429
|
+
console.log(JSON.stringify({ operation: 'config-init', file: configFileName, config }, null, 2));
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
writeConfig(process.cwd(), config);
|
|
433
|
+
console.log(configFileName);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
throw new Error('config requiert init ou show.');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
case 'doctor':
|
|
440
|
+
runDoctor();
|
|
441
|
+
break;
|
|
442
|
+
|
|
443
|
+
default:
|
|
444
|
+
throw new Error(`Commande inconnue : ${parsed.command}. Utilise --help.`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (process.argv[1] && fs.realpathSync(process.argv[1]) === __filename) {
|
|
449
|
+
main().catch((error) => {
|
|
450
|
+
console.error(chalk.red(`❌ ${error.message}`));
|
|
451
|
+
process.exitCode = 1;
|
|
452
|
+
});
|
|
218
453
|
}
|