@configjs/cli 1.1.3 → 1.1.4

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.
@@ -24,6 +24,12 @@ function detectFramework(pkg) {
24
24
  ...pkg["dependencies"] || {},
25
25
  ...pkg["devDependencies"] || {}
26
26
  };
27
+ if (deps["next"]) {
28
+ return {
29
+ framework: "nextjs",
30
+ version: deps["next"].replace(/[\^~]/, "")
31
+ };
32
+ }
27
33
  if (deps["react"]) {
28
34
  return {
29
35
  framework: "react",
@@ -43,7 +49,7 @@ function detectFramework(pkg) {
43
49
  };
44
50
  }
45
51
  throw new DetectionError(
46
- "No supported framework detected. Supported frameworks: React, Vue, Svelte",
52
+ "No supported framework detected. Supported frameworks: Next.js, React, Vue, Svelte",
47
53
  { dependencies: Object.keys(deps) }
48
54
  );
49
55
  }
@@ -52,6 +58,15 @@ async function detectBundler(projectRoot, pkg) {
52
58
  ...pkg["dependencies"] || {},
53
59
  ...pkg["devDependencies"] || {}
54
60
  };
61
+ if (deps["next"]) {
62
+ const nextConfigExists = await checkPathExists(join(projectRoot, "next.config.js")) || await checkPathExists(join(projectRoot, "next.config.ts")) || await checkPathExists(join(projectRoot, "next.config.mjs")) || await checkPathExists(join(projectRoot, "next.config.cjs"));
63
+ if (nextConfigExists) {
64
+ return {
65
+ bundler: "nextjs",
66
+ version: deps["next"].replace(/[\^~]/, "")
67
+ };
68
+ }
69
+ }
55
70
  if (deps["vite"]) {
56
71
  const viteConfigExists = await checkPathExists(join(projectRoot, "vite.config.js")) || await checkPathExists(join(projectRoot, "vite.config.ts")) || await checkPathExists(join(projectRoot, "vite.config.mjs")) || await checkPathExists(join(projectRoot, "vite.config.cjs"));
57
72
  if (viteConfigExists) {
@@ -132,6 +147,21 @@ async function detectPublicDir(projectRoot) {
132
147
  }
133
148
  return "public";
134
149
  }
150
+ async function detectNextjsRouter(projectRoot, srcDir) {
151
+ const appDirInSrc = join(projectRoot, srcDir, "app");
152
+ const pagesDirInSrc = join(projectRoot, srcDir, "pages");
153
+ const appDirAtRoot = join(projectRoot, "app");
154
+ const pagesDirAtRoot = join(projectRoot, "pages");
155
+ const appDirExists = await checkPathExists(appDirInSrc) || await checkPathExists(appDirAtRoot);
156
+ const pagesDirExists = await checkPathExists(pagesDirInSrc) || await checkPathExists(pagesDirAtRoot);
157
+ if (appDirExists) {
158
+ return "app";
159
+ }
160
+ if (pagesDirExists) {
161
+ return "pages";
162
+ }
163
+ return void 0;
164
+ }
135
165
  async function detectGit(projectRoot) {
136
166
  const gitDir = join(projectRoot, ".git");
137
167
  const hasGit = await checkPathExists(gitDir);
@@ -197,6 +227,7 @@ async function detectContext(projectRoot) {
197
227
  detectGit(fullPath)
198
228
  ]);
199
229
  const lockfile = await detectLockfile(fullPath, packageManager);
230
+ const nextjsRouter = frameworkInfo.framework === "nextjs" ? await detectNextjsRouter(fullPath, srcDir) : void 0;
200
231
  const dependencies = pkg["dependencies"] || {};
201
232
  const devDependencies = pkg["devDependencies"] || {};
202
233
  const context = {
@@ -224,7 +255,9 @@ async function detectContext(projectRoot) {
224
255
  devDependencies,
225
256
  // Git
226
257
  hasGit: gitInfo.hasGit,
227
- gitHooksPath: gitInfo.gitHooksPath
258
+ gitHooksPath: gitInfo.gitHooksPath,
259
+ // Next.js specific
260
+ nextjsRouter
228
261
  };
229
262
  detectionCache.set(fullPath, context);
230
263
  logger.debug(`Context detected successfully for ${fullPath}`, {
@@ -1,27 +1,13 @@
1
1
  import {
2
- CompatibilityValidator,
3
- compatibilityRules
4
- } from "./chunk-VJ254HJY.js";
2
+ getPluginsByCategory
3
+ } from "./chunk-4VHPGJVU.js";
5
4
  import {
6
- BackupManager,
7
- ConfigWriter,
8
- getPluginsByCategory,
9
- pluginRegistry
10
- } from "./chunk-OJGTPK6N.js";
5
+ PluginTracker
6
+ } from "./chunk-BVXGN3AC.js";
11
7
  import {
12
- DetectionError,
13
- PluginTracker,
14
- detectContext
15
- } from "./chunk-ZSDLWQSS.js";
16
- import {
17
- checkPathExists,
18
8
  installPackages,
19
9
  logger
20
10
  } from "./chunk-QRFLHLFE.js";
21
- import "./chunk-QGM4M3NI.js";
22
-
23
- // src/cli/prompts/language.ts
24
- import inquirer from "inquirer";
25
11
 
26
12
  // src/cli/i18n/fr.ts
27
13
  var fr = {
@@ -100,6 +86,27 @@ var fr = {
100
86
  invalid: "Le nom du projet ne peut contenir que des lettres, chiffres, tirets et underscores"
101
87
  },
102
88
  folderExists: (name) => `Le dossier "${name}" existe d\xE9j\xE0. Veuillez choisir un autre nom.`
89
+ },
90
+ nextjs: {
91
+ noNextjsDetected: "\u26A0\uFE0F Aucun projet Next.js d\xE9tect\xE9 dans le r\xE9pertoire actuel.",
92
+ proposeSetup: "Souhaitez-vous cr\xE9er un nouveau projet Next.js ?",
93
+ projectName: "Nom du projet",
94
+ projectNamePlaceholder: "mon-projet-nextjs",
95
+ typescript: "Utiliser TypeScript ?",
96
+ eslint: "Utiliser ESLint ?",
97
+ tailwind: "Utiliser TailwindCSS ?",
98
+ srcDir: "Utiliser le dossier src/ ?",
99
+ appRouter: "Utiliser App Router (recommand\xE9) ?",
100
+ importAlias: "Alias d'import (ex: @/*)",
101
+ creating: "Cr\xE9ation du projet Next.js...",
102
+ success: "\u2705 Projet cr\xE9\xE9 avec succ\xE8s !",
103
+ error: "\u274C Erreur lors de la cr\xE9ation du projet",
104
+ changingDirectory: "Changement vers le r\xE9pertoire du projet...",
105
+ validation: {
106
+ empty: "Le nom du projet ne peut pas \xEAtre vide",
107
+ invalid: "Le nom du projet ne peut contenir que des lettres, chiffres, tirets et underscores"
108
+ },
109
+ folderExists: (name) => `Le dossier "${name}" existe d\xE9j\xE0. Veuillez choisir un autre nom.`
103
110
  }
104
111
  };
105
112
 
@@ -180,6 +187,27 @@ var en = {
180
187
  invalid: "Project name can only contain letters, numbers, dashes and underscores"
181
188
  },
182
189
  folderExists: (name) => `Folder "${name}" already exists. Please choose another name.`
190
+ },
191
+ nextjs: {
192
+ noNextjsDetected: "\u26A0\uFE0F No Next.js project detected in the current directory.",
193
+ proposeSetup: "Would you like to create a new Next.js project?",
194
+ projectName: "Project name",
195
+ projectNamePlaceholder: "my-nextjs-project",
196
+ typescript: "Use TypeScript?",
197
+ eslint: "Use ESLint?",
198
+ tailwind: "Use TailwindCSS?",
199
+ srcDir: "Use src/ directory?",
200
+ appRouter: "Use App Router (recommended)?",
201
+ importAlias: "Import alias (e.g. @/*)",
202
+ creating: "Creating Next.js project...",
203
+ success: "\u2705 Project created successfully!",
204
+ error: "\u274C Error creating project",
205
+ changingDirectory: "Changing to project directory...",
206
+ validation: {
207
+ empty: "Project name cannot be empty",
208
+ invalid: "Project name can only contain letters, numbers, dashes and underscores"
209
+ },
210
+ folderExists: (name) => `Folder "${name}" already exists. Please choose another name.`
183
211
  }
184
212
  };
185
213
 
@@ -260,6 +288,27 @@ var es = {
260
288
  invalid: "El nombre del proyecto solo puede contener letras, n\xFAmeros, guiones y guiones bajos"
261
289
  },
262
290
  folderExists: (name) => `La carpeta "${name}" ya existe. Por favor, elija otro nombre.`
291
+ },
292
+ nextjs: {
293
+ noNextjsDetected: "\u26A0\uFE0F No se detect\xF3 ning\xFAn proyecto Next.js en el directorio actual.",
294
+ proposeSetup: "\xBFDesea crear un nuevo proyecto Next.js?",
295
+ projectName: "Nombre del proyecto",
296
+ projectNamePlaceholder: "mi-proyecto-nextjs",
297
+ typescript: "\xBFUsar TypeScript?",
298
+ eslint: "\xBFUsar ESLint?",
299
+ tailwind: "\xBFUsar TailwindCSS?",
300
+ srcDir: "\xBFUsar directorio src/?",
301
+ appRouter: "\xBFUsar App Router (recomendado)?",
302
+ importAlias: "Alias de importaci\xF3n (ej: @/*)",
303
+ creating: "Creando proyecto Next.js...",
304
+ success: "\u2705 \xA1Proyecto creado con \xE9xito!",
305
+ error: "\u274C Error al crear el proyecto",
306
+ changingDirectory: "Cambiando al directorio del proyecto...",
307
+ validation: {
308
+ empty: "El nombre del proyecto no puede estar vac\xEDo",
309
+ invalid: "El nombre del proyecto solo puede contener letras, n\xFAmeros, guiones y guiones bajos"
310
+ },
311
+ folderExists: (name) => `La carpeta "${name}" ya existe. Por favor, elija otro nombre.`
263
312
  }
264
313
  };
265
314
 
@@ -278,6 +327,7 @@ function getTranslations(lang) {
278
327
  }
279
328
 
280
329
  // src/cli/prompts/language.ts
330
+ import inquirer from "inquirer";
281
331
  async function promptLanguage() {
282
332
  const defaultTranslations = getTranslations("en");
283
333
  const { language } = await inquirer.prompt([
@@ -329,6 +379,9 @@ async function promptPluginSelection(ctx, availablePlugins, lang) {
329
379
  if (!plugin.frameworks.includes(ctx.framework)) {
330
380
  continue;
331
381
  }
382
+ if (ctx.framework === "nextjs" && plugin.name === "react-router-dom") {
383
+ continue;
384
+ }
332
385
  if (plugin.requiresTypeScript === true && !ctx.typescript) {
333
386
  continue;
334
387
  }
@@ -409,88 +462,6 @@ ${translations.confirmation.summary}
409
462
  return confirm;
410
463
  }
411
464
 
412
- // src/cli/prompts/vite-setup.ts
413
- import inquirer4 from "inquirer";
414
- async function promptViteSetup(language) {
415
- const t = getTranslations(language);
416
- const answers = await inquirer4.prompt([
417
- {
418
- type: "confirm",
419
- name: "shouldCreate",
420
- message: t.vite.proposeSetup,
421
- default: true
422
- },
423
- {
424
- type: "input",
425
- name: "projectName",
426
- message: t.vite.projectName,
427
- default: t.vite.projectNamePlaceholder,
428
- when: (answers2) => answers2.shouldCreate === true,
429
- validate: (input) => {
430
- if (!input || input.trim().length === 0) {
431
- return t.vite.validation.empty;
432
- }
433
- if (!/^[a-z0-9-_]+$/i.test(input)) {
434
- return t.vite.validation.invalid;
435
- }
436
- return true;
437
- }
438
- },
439
- {
440
- type: "list",
441
- name: "template",
442
- message: t.vite.template,
443
- choices: t.vite.templateOptions,
444
- when: (answers2) => answers2.shouldCreate === true
445
- }
446
- ]);
447
- if (!answers.shouldCreate) {
448
- return null;
449
- }
450
- return {
451
- projectName: answers.projectName.trim(),
452
- template: answers.template
453
- };
454
- }
455
-
456
- // src/cli/utils/vite-installer.ts
457
- import { resolve } from "path";
458
- import { execa } from "execa";
459
- async function createViteProject(options, currentDir, language) {
460
- const t = getTranslations(language);
461
- const { projectName, template } = options;
462
- const projectPath = resolve(currentDir, projectName);
463
- if (await checkPathExists(projectPath)) {
464
- throw new Error(t.vite.folderExists(projectName));
465
- }
466
- logger.info(t.vite.creating);
467
- try {
468
- const result = await execa(
469
- "npm",
470
- ["create", "vite@latest", projectName, "--", "--template", template],
471
- {
472
- cwd: currentDir,
473
- stdio: "inherit",
474
- env: {
475
- ...process.env,
476
- // Désactiver les prompts interactifs de Vite
477
- npm_config_yes: "true"
478
- }
479
- }
480
- );
481
- if (result.exitCode !== 0) {
482
- throw new Error(`${t.vite.error}: exit code ${result.exitCode}`);
483
- }
484
- logger.success(t.vite.success);
485
- logger.info(t.vite.changingDirectory);
486
- return projectPath;
487
- } catch (error) {
488
- const errorMessage = error instanceof Error ? error.message : String(error);
489
- logger.error(`${t.vite.error}: ${errorMessage}`);
490
- throw error;
491
- }
492
- }
493
-
494
465
  // src/core/installer.ts
495
466
  var Installer = class {
496
467
  /**
@@ -581,7 +552,7 @@ var Installer = class {
581
552
  }
582
553
  }
583
554
  logger.debug("Validating plugins compatibility...");
584
- const validationResult = this.validator.validate(notInstalled);
555
+ const validationResult = this.validator.validate(notInstalled, this.ctx);
585
556
  if (!validationResult.valid) {
586
557
  const errorMessages = validationResult.errors.map((e) => e.message).join("; ");
587
558
  throw new Error(
@@ -1024,151 +995,12 @@ function displayNextSteps(lang) {
1024
995
  console.log();
1025
996
  }
1026
997
 
1027
- // src/cli/commands/install.ts
1028
- import pc2 from "picocolors";
1029
- async function installReact(options) {
1030
- try {
1031
- const language = await promptLanguage();
1032
- const t = getTranslations(language);
1033
- console.log();
1034
- console.log(pc2.bold(pc2.cyan(`\u{1F50D} ${t.detection.detecting}`)));
1035
- let projectRoot = process.cwd();
1036
- let ctx;
1037
- try {
1038
- ctx = await detectContext(projectRoot);
1039
- } catch (error) {
1040
- if (error instanceof DetectionError) {
1041
- console.log();
1042
- console.log(pc2.yellow(t.vite.noReactDetected));
1043
- console.log();
1044
- const viteOptions = await promptViteSetup(language);
1045
- if (!viteOptions) {
1046
- console.log();
1047
- console.log(pc2.gray(t.common.cancel));
1048
- return;
1049
- }
1050
- const newProjectPath = await createViteProject(
1051
- viteOptions,
1052
- projectRoot,
1053
- language
1054
- );
1055
- process.chdir(newProjectPath);
1056
- projectRoot = newProjectPath;
1057
- console.log();
1058
- ctx = await detectContext(projectRoot);
1059
- } else {
1060
- throw error;
1061
- }
1062
- }
1063
- console.log(
1064
- pc2.green(` \u2713 ${t.detection.framework}: `) + pc2.bold(`${ctx.framework} ${pc2.gray(ctx.frameworkVersion)}`)
1065
- );
1066
- console.log(
1067
- pc2.green(` \u2713 ${t.detection.typescript}: `) + pc2.bold(ctx.typescript ? "Oui" : "Non")
1068
- );
1069
- if (ctx.bundler) {
1070
- console.log(
1071
- pc2.green(` \u2713 ${t.detection.bundler}: `) + pc2.bold(`${ctx.bundler} ${pc2.gray(ctx.bundlerVersion || "")}`)
1072
- );
1073
- }
1074
- console.log(
1075
- pc2.green(` \u2713 ${t.detection.packageManager}: `) + pc2.bold(ctx.packageManager)
1076
- );
1077
- console.log();
1078
- let selectedPlugins = [];
1079
- if (options.yes) {
1080
- logger.info("Using default recommendations (--yes mode)");
1081
- } else {
1082
- selectedPlugins = await promptPluginSelection(
1083
- ctx,
1084
- pluginRegistry,
1085
- language
1086
- );
1087
- }
1088
- if (selectedPlugins.length === 0) {
1089
- console.log();
1090
- console.log(pc2.yellow(`\u26A0\uFE0F ${t.common.selected(0)}`));
1091
- console.log(pc2.gray("Exiting..."));
1092
- return;
1093
- }
1094
- console.log();
1095
- console.log(
1096
- pc2.bold(pc2.green(`\u2713 ${t.common.selected(selectedPlugins.length)}`))
1097
- );
1098
- console.log();
1099
- if (!options.yes && !options.silent) {
1100
- const confirmed = await promptConfirmation(selectedPlugins, language);
1101
- if (!confirmed) {
1102
- console.log(t.common.cancel);
1103
- return;
1104
- }
1105
- }
1106
- if (options.dryRun) {
1107
- console.log();
1108
- console.log(pc2.bold(pc2.yellow("\u2501".repeat(60))));
1109
- console.log(pc2.bold(pc2.yellow("\u{1F50D} MODE DRY-RUN (simulation uniquement)")));
1110
- console.log(pc2.bold(pc2.yellow("\u2501".repeat(60))));
1111
- console.log();
1112
- console.log(pc2.bold(pc2.cyan("\u{1F4E6} Packages \xE0 installer :")));
1113
- for (const plugin of selectedPlugins) {
1114
- console.log(
1115
- pc2.blue(` \u2022 ${plugin.displayName}`) + pc2.gray(
1116
- ` (${plugin.name}${plugin.version ? `@${plugin.version}` : ""})`
1117
- )
1118
- );
1119
- }
1120
- console.log();
1121
- console.log(pc2.bold(pc2.cyan("\u{1F4DD} Fichiers qui seraient cr\xE9\xE9s/modifi\xE9s :")));
1122
- for (const plugin of selectedPlugins) {
1123
- console.log(pc2.gray(` \u2022 ${plugin.displayName} configuration`));
1124
- }
1125
- console.log();
1126
- console.log(
1127
- pc2.yellow("\u26A0\uFE0F Aucune modification n'a \xE9t\xE9 effectu\xE9e (dry-run)")
1128
- );
1129
- console.log(
1130
- pc2.cyan("\u{1F4A1} Ex\xE9cutez sans --dry-run pour appliquer les changements")
1131
- );
1132
- console.log();
1133
- return;
1134
- }
1135
- const backupManager = new BackupManager();
1136
- const configWriter = new ConfigWriter(backupManager);
1137
- const validator = new CompatibilityValidator(compatibilityRules);
1138
- const installer = new Installer(ctx, validator, configWriter, backupManager);
1139
- if (options.install === false) {
1140
- console.log();
1141
- console.log(pc2.yellow("\u2699\uFE0F Mode configuration uniquement (--no-install)"));
1142
- console.log(pc2.gray("Les packages ne seront PAS install\xE9s"));
1143
- console.log();
1144
- }
1145
- const spinner = new SpinnerManager();
1146
- spinner.start(t.installation.installing);
1147
- try {
1148
- const result = await installer.install(selectedPlugins, {
1149
- skipPackageInstall: options.install === false
1150
- });
1151
- spinner.succeed(t.installation.success);
1152
- if (result.success) {
1153
- displayInstallationReport(result, selectedPlugins, language);
1154
- } else {
1155
- console.error(`
1156
- ${t.installation.error}`);
1157
- process.exit(1);
1158
- }
1159
- } catch (error) {
1160
- spinner.fail(t.installation.error);
1161
- throw error;
1162
- }
1163
- } catch (error) {
1164
- logger.error("Installation failed:", error);
1165
- if (error instanceof Error) {
1166
- console.error(`
1167
- \u274C ${error.message}`);
1168
- }
1169
- process.exit(1);
1170
- }
1171
- }
1172
998
  export {
1173
- installReact
999
+ getTranslations,
1000
+ promptLanguage,
1001
+ promptPluginSelection,
1002
+ promptConfirmation,
1003
+ Installer,
1004
+ SpinnerManager,
1005
+ displayInstallationReport
1174
1006
  };
@@ -14,24 +14,32 @@ var CompatibilityValidator = class {
14
14
  * Valide la compatibilité d'un ensemble de plugins
15
15
  *
16
16
  * @param plugins - Liste des plugins à valider
17
+ * @param ctx - Contexte du projet (optionnel, pour règles spécifiques framework)
17
18
  * @returns Résultat de la validation avec erreurs, warnings et suggestions
18
19
  *
19
20
  * @example
20
21
  * ```typescript
21
- * const result = validator.validate([plugin1, plugin2, plugin3])
22
+ * const result = validator.validate([plugin1, plugin2, plugin3], ctx)
22
23
  * if (!result.valid) {
23
24
  * // Gérer les erreurs
24
25
  * }
25
26
  * ```
26
27
  */
27
- validate(plugins) {
28
+ validate(plugins, ctx) {
28
29
  logger.debug(`Validating ${plugins.length} plugin(s)`);
29
30
  const pluginNames = new Set(plugins.map((p) => p.name));
30
- const allConflicts = this.checkConflicts(plugins, pluginNames);
31
+ const applicableRules = ctx ? this.rules.filter(
32
+ (rule) => !rule.framework || rule.framework === ctx.framework
33
+ ) : this.rules.filter((rule) => !rule.framework);
34
+ const allConflicts = this.checkConflicts(
35
+ plugins,
36
+ pluginNames,
37
+ applicableRules
38
+ );
31
39
  const conflictErrors = [];
32
40
  const conflictWarnings = [];
33
41
  for (const conflict of allConflicts) {
34
- const rule = this.rules.find(
42
+ const rule = applicableRules.find(
35
43
  (r) => r.type === "CONFLICT" && r.plugins?.every((p) => conflict.plugins?.includes(p))
36
44
  );
37
45
  if (rule?.severity === "error") {
@@ -40,13 +48,31 @@ var CompatibilityValidator = class {
40
48
  conflictWarnings.push(conflict);
41
49
  }
42
50
  }
51
+ const frameworkConflicts = ctx ? this.checkFrameworkConflicts(plugins, pluginNames, ctx, applicableRules) : [];
52
+ const frameworkErrors = [];
53
+ const frameworkWarnings = [];
54
+ for (const conflict of frameworkConflicts) {
55
+ const rule = applicableRules.find(
56
+ (r) => r.type === "CONFLICT" && r.framework === ctx?.framework && r.plugins?.some((p) => conflict.plugins?.includes(p))
57
+ );
58
+ if (rule?.severity === "error") {
59
+ frameworkErrors.push(conflict);
60
+ } else {
61
+ frameworkWarnings.push(conflict);
62
+ }
63
+ }
43
64
  const errors = [
44
- ...this.checkExclusivity(plugins, pluginNames),
65
+ ...this.checkExclusivity(plugins, pluginNames, applicableRules),
45
66
  ...conflictErrors,
46
- ...this.checkDependencies(plugins, pluginNames)
67
+ ...this.checkDependencies(plugins, pluginNames, applicableRules),
68
+ ...frameworkErrors
47
69
  ];
48
- const warnings = conflictWarnings;
49
- const suggestions = this.checkRecommendations(plugins, pluginNames);
70
+ const warnings = [...conflictWarnings, ...frameworkWarnings];
71
+ const suggestions = this.checkRecommendations(
72
+ plugins,
73
+ pluginNames,
74
+ applicableRules
75
+ );
50
76
  const valid = errors.length === 0;
51
77
  logger.debug(`Validation result: ${valid ? "valid" : "invalid"}`, {
52
78
  errors: errors.length,
@@ -60,18 +86,52 @@ var CompatibilityValidator = class {
60
86
  suggestions
61
87
  };
62
88
  }
89
+ /**
90
+ * Vérifie les conflits avec le framework
91
+ *
92
+ * @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
93
+ * @param pluginNames - Set des noms de plugins pour lookup rapide
94
+ * @param ctx - Contexte du projet
95
+ * @param rules - Règles applicables
96
+ * @returns Liste des erreurs/warnings de conflit framework
97
+ *
98
+ * @internal
99
+ */
100
+ checkFrameworkConflicts(_plugins, pluginNames, ctx, rules) {
101
+ const conflicts = [];
102
+ for (const rule of rules) {
103
+ if (rule.type !== "CONFLICT" || !rule.framework || rule.framework !== ctx.framework) {
104
+ continue;
105
+ }
106
+ if (rule.plugins && rule.plugins.length > 0) {
107
+ const conflictingPlugins = rule.plugins.filter(
108
+ (pluginName) => pluginNames.has(pluginName)
109
+ );
110
+ if (conflictingPlugins.length > 0) {
111
+ conflicts.push({
112
+ type: "CONFLICT",
113
+ plugins: conflictingPlugins,
114
+ message: rule.reason,
115
+ canOverride: rule.allowOverride ?? true
116
+ });
117
+ }
118
+ }
119
+ }
120
+ return conflicts;
121
+ }
63
122
  /**
64
123
  * Vérifie les règles d'exclusivité (EXCLUSIVE)
65
124
  *
66
125
  * @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
67
126
  * @param pluginNames - Set des noms de plugins pour lookup rapide
127
+ * @param rules - Règles applicables
68
128
  * @returns Liste des erreurs d'exclusivité
69
129
  *
70
130
  * @internal
71
131
  */
72
- checkExclusivity(_plugins, pluginNames) {
132
+ checkExclusivity(_plugins, pluginNames, rules = this.rules) {
73
133
  const errors = [];
74
- for (const rule of this.rules) {
134
+ for (const rule of rules) {
75
135
  if (rule.type !== "EXCLUSIVE" || !rule.plugins) {
76
136
  continue;
77
137
  }
@@ -94,13 +154,17 @@ var CompatibilityValidator = class {
94
154
  *
95
155
  * @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
96
156
  * @param pluginNames - Set des noms de plugins pour lookup rapide
157
+ * @param rules - Règles applicables
97
158
  * @returns Liste des warnings/erreurs de conflit
98
159
  *
99
160
  * @internal
100
161
  */
101
- checkConflicts(_plugins, pluginNames) {
162
+ checkConflicts(_plugins, pluginNames, rules = this.rules) {
102
163
  const conflicts = [];
103
- for (const rule of this.rules) {
164
+ for (const rule of rules) {
165
+ if (rule.framework) {
166
+ continue;
167
+ }
104
168
  if (rule.type !== "CONFLICT" || !rule.plugins) {
105
169
  continue;
106
170
  }
@@ -123,13 +187,14 @@ var CompatibilityValidator = class {
123
187
  *
124
188
  * @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
125
189
  * @param pluginNames - Set des noms de plugins pour lookup rapide
190
+ * @param rules - Règles applicables
126
191
  * @returns Liste des erreurs de dépendances manquantes
127
192
  *
128
193
  * @internal
129
194
  */
130
- checkDependencies(_plugins, pluginNames) {
195
+ checkDependencies(_plugins, pluginNames, rules = this.rules) {
131
196
  const errors = [];
132
- for (const rule of this.rules) {
197
+ for (const rule of rules) {
133
198
  if (rule.type !== "REQUIRES" || !rule.plugin || !rule.requires) {
134
199
  continue;
135
200
  }
@@ -156,13 +221,14 @@ var CompatibilityValidator = class {
156
221
  *
157
222
  * @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
158
223
  * @param pluginNames - Set des noms de plugins pour lookup rapide
224
+ * @param rules - Règles applicables
159
225
  * @returns Liste des suggestions de plugins recommandés
160
226
  *
161
227
  * @internal
162
228
  */
163
- checkRecommendations(_plugins, pluginNames) {
229
+ checkRecommendations(_plugins, pluginNames, rules = this.rules) {
164
230
  const suggestions = [];
165
- for (const rule of this.rules) {
231
+ for (const rule of rules) {
166
232
  if (rule.type !== "RECOMMENDS" || !rule.plugin || !rule.recommends) {
167
233
  continue;
168
234
  }
@@ -205,6 +271,34 @@ var compatibilityRules = [
205
271
  recommends: ["@types/react-router-dom"],
206
272
  reason: "Types TypeScript recommand\xE9s pour une meilleure exp\xE9rience de d\xE9veloppement",
207
273
  severity: "info"
274
+ },
275
+ // Règles spécifiques Next.js
276
+ // React Router incompatible avec Next.js
277
+ {
278
+ type: "CONFLICT",
279
+ plugins: ["react-router-dom"],
280
+ framework: "nextjs",
281
+ reason: "React Router est incompatible avec Next.js. Next.js a son propre syst\xE8me de routing int\xE9gr\xE9.",
282
+ severity: "error",
283
+ allowOverride: false
284
+ },
285
+ // Framer Motion peut causer des problèmes SSR avec Next.js
286
+ {
287
+ type: "CONFLICT",
288
+ plugins: ["framer-motion"],
289
+ framework: "nextjs",
290
+ reason: "Framer Motion peut causer des probl\xE8mes avec le Server-Side Rendering (SSR) de Next.js. Utilisez des alternatives compatibles SSR ou configurez correctement le dynamic import.",
291
+ severity: "warning",
292
+ allowOverride: true
293
+ },
294
+ // Shadcn/ui nécessite une configuration spéciale pour Next.js
295
+ {
296
+ type: "RECOMMENDS",
297
+ plugin: "shadcn-ui",
298
+ recommends: ["shadcn-ui-nextjs"],
299
+ framework: "nextjs",
300
+ reason: "Pour Next.js, utilisez la variante shadcn-ui-nextjs qui est optimis\xE9e pour React Server Components.",
301
+ severity: "info"
208
302
  }
209
303
  ];
210
304