nestcraftx 0.2.6 → 0.3.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/utils/shell.js CHANGED
@@ -1,26 +1,75 @@
1
1
  const { execSync } = require("child_process");
2
2
  const fs = require("fs");
3
3
  const { logError } = require("./loggers/logError");
4
+ const { logWarning } = require("./loggers/logWarning");
4
5
  const { spinner } = require("./spinner");
6
+ const { warning, info } = require("./colors");
7
+
8
+ /**
9
+ * Accumulated warnings collected during a generation session.
10
+ * Displayed as a summary at the end via printSetupWarnings().
11
+ */
12
+ const _warnings = [];
13
+
14
+ /**
15
+ * Runs a shell command synchronously.
16
+ *
17
+ * @param {string} command - The shell command to execute.
18
+ * @param {string} errorMessage - Human-readable error message shown on failure.
19
+ * @param {object} [options] - Optional configuration.
20
+ * @param {boolean} [options.critical=true] - If true (default), a failure calls process.exit(1).
21
+ * If false, the failure is logged as a warning and execution continues.
22
+ * @param {string|null} [options.spinner] - Optional spinner label shown during execution.
23
+ * @returns {Promise<boolean>} Resolves to true on success, false on non-critical failure.
24
+ */
25
+ async function runCommand(command, errorMessage, options = {}) {
26
+ // Support old call signature: runCommand(cmd, msg, spinnerText)
27
+ // New signature: runCommand(cmd, msg, { critical, spinner })
28
+ let critical = true;
29
+ let spinnerText = null;
30
+
31
+ if (typeof options === "string") {
32
+ // Legacy: third arg was spinnerText string
33
+ spinnerText = options;
34
+ } else if (typeof options === "object" && options !== null) {
35
+ critical = options.critical !== false; // default true
36
+ spinnerText = options.spinner || null;
37
+ }
5
38
 
6
- async function runCommand(command, errorMessage, spinnerText = null) {
7
39
  const spin = spinnerText ? spinner(spinnerText) : null;
8
40
 
9
41
  try {
10
42
  if (spin) spin.start();
11
43
  execSync(command, { stdio: spinnerText ? "pipe" : "inherit" });
12
44
  if (spin) spin.succeed(spinnerText);
45
+ return true;
13
46
  } catch (error) {
14
47
  if (spin) spin.fail(errorMessage);
15
- logError(errorMessage);
16
- fs.appendFileSync(
17
- "setup.log",
18
- `[Erreur] ${errorMessage}: ${error.message}\n`,
19
- );
20
- process.exit(1);
48
+
49
+ const logLine = `[${new Date().toISOString()}] ${errorMessage}: ${error.message}\n`;
50
+ fs.appendFileSync("setup.log", logLine);
51
+
52
+ if (critical) {
53
+ // Fatal — stop everything
54
+ logError(`${errorMessage}`);
55
+ logError(`Run: ${command}`);
56
+ process.exit(1);
57
+ } else {
58
+ // Non-critical — warn and continue
59
+ logWarning(`${errorMessage} (non-fatal, continuing...)`);
60
+ logWarning(` Command: ${command}`);
61
+ logWarning(` See setup.log for details`);
62
+ _warnings.push({ command, errorMessage });
63
+ return false;
64
+ }
21
65
  }
22
66
  }
23
67
 
68
+ /**
69
+ * Silent command — returns stdout string or null on failure. Never exits.
70
+ * @param {string} command
71
+ * @returns {string|null}
72
+ */
24
73
  async function runCommandSilent(command) {
25
74
  try {
26
75
  return execSync(command, { stdio: "pipe" }).toString();
@@ -29,4 +78,35 @@ async function runCommandSilent(command) {
29
78
  }
30
79
  }
31
80
 
32
- module.exports = { runCommand, runCommandSilent };
81
+ /**
82
+ * Prints a summary of all non-critical warnings accumulated during the session.
83
+ * Call this at the very end of the generation pipeline.
84
+ */
85
+ function printSetupWarnings() {
86
+ if (_warnings.length === 0) return;
87
+
88
+ console.log(
89
+ `\n${warning("⚠️ Setup completed with")} ${warning(String(_warnings.length))} ${warning("warning(s):")}`,
90
+ );
91
+ _warnings.forEach((w, i) => {
92
+ console.log(` ${info(`${i + 1}.`)} ${w.errorMessage}`);
93
+ console.log(` ${info("Command:")} ${w.command}`);
94
+ });
95
+ console.log(
96
+ `\n ${info("➜")} Full details in: ${info("setup.log")}\n`,
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Clears the warnings accumulator (useful between test runs).
102
+ */
103
+ function clearSetupWarnings() {
104
+ _warnings.length = 0;
105
+ }
106
+
107
+ module.exports = {
108
+ runCommand,
109
+ runCommandSilent,
110
+ printSetupWarnings,
111
+ clearSetupWarnings,
112
+ };
@@ -1,8 +1,18 @@
1
- import * as readline from "readline-sync";
2
- import * as fs from "fs";
3
- import { logInfo } from "./loggers/logInfo.js";
4
-
5
- export async function getUserInputs2() {
1
+ const readline = require("readline-sync");
2
+ const fs = require("fs");
3
+ const { logInfo } = require("./loggers/logInfo");
4
+
5
+ // ── Modules extraits (Phase 1A) ────────────────────────────────────────────────
6
+ const {
7
+ createDirectory,
8
+ createFile,
9
+ updateFile,
10
+ } = require("./file-system");
11
+ const { safeUpdateAppModule } = require("./app-module.updater");
12
+ const { capitalize, decapitalize } = require("./helpers");
13
+ // ──────────────────────────────────────────────────────────────────────────────
14
+
15
+ async function getUserInputs2() {
6
16
  console.log("\n🔹🔹🔹 Configuration du projet 🔹🔹🔹\n");
7
17
 
8
18
  const dataBases = [
@@ -299,7 +309,7 @@ export async function getUserInputs2() {
299
309
  };
300
310
  }
301
311
 
302
- export function getUserInputsSwagger() {
312
+ function getUserInputsSwagger() {
303
313
  console.log("\n🔹 Configuration de Swagger 🔹");
304
314
 
305
315
  const title = readline.question("Titre de l'API ? (ex: Mon API) ", {
@@ -324,98 +334,24 @@ export function getUserInputsSwagger() {
324
334
  return { title, description, version, endpoint };
325
335
  }
326
336
 
327
- export async function createDirectory(directoryPath) {
328
- try {
329
- if (!fs.existsSync(directoryPath)) {
330
- fs.mkdirSync(directoryPath, { recursive: true });
331
- // console.log(`Dossier créé : ${directoryPath}`);
332
- }
333
- } catch (error) {
334
- console.error(
335
- `Erreur lors de la création du dossier ${directoryPath}:`,
336
- error,
337
- );
338
- }
339
- }
340
-
341
- export async function createFile(fileData) {
342
- try {
343
- if (!fs.existsSync(fileData.path)) {
344
- fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
345
- } else {
346
- console.log(`Existing file : ${fileData.path}`);
347
- fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
348
- }
349
- } catch (error) {
350
- console.error(`Erreur creating file ${fileData.path}:`, error);
351
- }
352
- }
353
-
354
- export async function updateFile({ path, pattern, replacement }) {
355
- try {
356
- let mainTs = fs.readFileSync(path, "utf8");
357
- const updatedContent = mainTs.replace(pattern, replacement);
358
- fs.writeFileSync(path, updatedContent, "utf-8");
359
- // console.log(` Updated file: ${path}`);
360
- } catch (error) {
361
- console.error(` Error updating file ${path}:`, error);
362
- }
363
- }
364
-
365
- export async function safeUpdateAppModule(entity) {
366
- const filePath = "src/app.module.ts";
367
- const moduleName = `${capitalize(entity)}Module`;
368
- const importLine = `import { ${moduleName} } from 'src/${entity}/${entity}.module';`;
369
-
370
- let content = fs.readFileSync(filePath, "utf-8");
371
-
372
- // Étape 1 : Ajout de l'import si nécessaire
373
- if (!content.includes(importLine)) {
374
- const importMarker = `import { ConfigModule } from '@nestjs/config';`;
375
-
376
- if (content.includes(importMarker)) {
377
- await updateFile({
378
- path: filePath,
379
- pattern: importMarker,
380
- replacement: `${importMarker}\n${importLine}`,
381
- });
382
- content = fs.readFileSync(filePath, "utf-8");
383
- } else {
384
- logInfo(
385
- " Impossible de trouver le point d'insertion de l'import (ConfigModule manquant)",
386
- );
387
- }
388
- }
389
-
390
- // Étape 2 : Vérifier le bloc des imports du @Module
391
- const importsBlockRegex = /imports:\s*\[((.|\n)*?)\]/m;
392
- const match = content.match(importsBlockRegex);
393
-
394
- if (!match) {
395
- logInfo(" Impossible de trouver le bloc 'imports' dans AppModule.");
396
- return;
397
- }
398
-
399
- const currentImportsBlock = match[1];
400
- const isAlreadyImportedInModule = currentImportsBlock.includes(moduleName);
401
-
402
- if (!isAlreadyImportedInModule) {
403
- const updatedBlock = currentImportsBlock.trim().endsWith(",")
404
- ? `${currentImportsBlock.trim()} ${moduleName},`
405
- : `${currentImportsBlock.trim()}, ${moduleName},`;
406
-
407
- const newContent = content.replace(
408
- importsBlockRegex,
409
- `imports: [${updatedBlock}]`,
410
- );
411
- fs.writeFileSync(filePath, newContent, "utf-8");
412
- }
413
- }
414
-
415
- export function capitalize(str) {
416
- return str.charAt(0).toUpperCase() + str.slice(1);
417
- }
418
-
419
- export function decapitalize(str) {
420
- return str.charAt(0).toLowerCase() + str.slice(1);
421
- }
337
+ // ── Fonctions déléguées aux modules extraits (Phase 1A) ───────────────────────
338
+ // createDirectory, createFile, updateFile → ./file-system.js
339
+ // safeUpdateAppModule → ./app-module.updater.js
340
+ // capitalize, decapitalize → ./helpers.js
341
+ //
342
+ // Les implémentations inline ont été supprimées. Toutes sont importées en haut
343
+ // du fichier et ré-exportées dans module.exports ci-dessous.
344
+ // ──────────────────────────────────────────────────────────────────────────────
345
+
346
+ module.exports = {
347
+ // Fonctions locales
348
+ getUserInputs2,
349
+ getUserInputsSwagger,
350
+ // Ré-exports des modules extraits (compatibilité ascendante 100%)
351
+ createDirectory,
352
+ createFile,
353
+ updateFile,
354
+ safeUpdateAppModule,
355
+ capitalize,
356
+ decapitalize,
357
+ };