@configjs/cli 1.0.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/LICENSE +22 -0
- package/README.md +431 -0
- package/dist/check-JF56SNQC.js +126 -0
- package/dist/chunk-5T664O5A.js +8402 -0
- package/dist/chunk-PQLKGF6I.js +224 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +41 -0
- package/dist/install-X7G2IXXX.js +1127 -0
- package/dist/list-J4E7JFII.js +75 -0
- package/package.json +95 -0
|
@@ -0,0 +1,1127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompatibilityValidator,
|
|
3
|
+
compatibilityRules
|
|
4
|
+
} from "./chunk-PQLKGF6I.js";
|
|
5
|
+
import {
|
|
6
|
+
BackupManager,
|
|
7
|
+
ConfigWriter,
|
|
8
|
+
checkPathExists,
|
|
9
|
+
detectPackageManager,
|
|
10
|
+
getPluginsByCategory,
|
|
11
|
+
installPackages,
|
|
12
|
+
logger,
|
|
13
|
+
pluginRegistry,
|
|
14
|
+
readPackageJson,
|
|
15
|
+
readTsConfig
|
|
16
|
+
} from "./chunk-5T664O5A.js";
|
|
17
|
+
|
|
18
|
+
// src/core/detector.ts
|
|
19
|
+
import { resolve, join } from "path";
|
|
20
|
+
import { platform, version } from "process";
|
|
21
|
+
var detectionCache = /* @__PURE__ */ new Map();
|
|
22
|
+
var DetectionError = class extends Error {
|
|
23
|
+
constructor(message, context) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.context = context;
|
|
26
|
+
this.name = "DetectionError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
function detectFramework(pkg) {
|
|
30
|
+
const deps = {
|
|
31
|
+
...pkg["dependencies"] || {},
|
|
32
|
+
...pkg["devDependencies"] || {}
|
|
33
|
+
};
|
|
34
|
+
if (deps["react"]) {
|
|
35
|
+
return {
|
|
36
|
+
framework: "react",
|
|
37
|
+
version: deps["react"].replace(/[\^~]/, "")
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (deps["vue"]) {
|
|
41
|
+
return {
|
|
42
|
+
framework: "vue",
|
|
43
|
+
version: deps["vue"].replace(/[\^~]/, "")
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (deps["svelte"]) {
|
|
47
|
+
return {
|
|
48
|
+
framework: "svelte",
|
|
49
|
+
version: deps["svelte"].replace(/[\^~]/, "")
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
throw new DetectionError(
|
|
53
|
+
"No supported framework detected. Supported frameworks: React, Vue, Svelte",
|
|
54
|
+
{ dependencies: Object.keys(deps) }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
async function detectBundler(projectRoot, pkg) {
|
|
58
|
+
const deps = {
|
|
59
|
+
...pkg["dependencies"] || {},
|
|
60
|
+
...pkg["devDependencies"] || {}
|
|
61
|
+
};
|
|
62
|
+
if (deps["vite"]) {
|
|
63
|
+
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"));
|
|
64
|
+
if (viteConfigExists) {
|
|
65
|
+
return {
|
|
66
|
+
bundler: "vite",
|
|
67
|
+
version: deps["vite"].replace(/[\^~]/, "")
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (deps["react-scripts"]) {
|
|
72
|
+
return {
|
|
73
|
+
bundler: "cra",
|
|
74
|
+
version: deps["react-scripts"].replace(/[\^~]/, "")
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (deps["webpack"]) {
|
|
78
|
+
const webpackConfigExists = await checkPathExists(join(projectRoot, "webpack.config.js")) || await checkPathExists(join(projectRoot, "webpack.config.ts"));
|
|
79
|
+
if (webpackConfigExists) {
|
|
80
|
+
return {
|
|
81
|
+
bundler: "webpack",
|
|
82
|
+
version: deps["webpack"].replace(/[\^~]/, "")
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (deps["@rspack/core"]) {
|
|
87
|
+
return {
|
|
88
|
+
bundler: "rspack",
|
|
89
|
+
version: deps["@rspack/core"].replace(/[\^~]/, "")
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
bundler: null,
|
|
94
|
+
version: null
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function detectTypeScript(projectRoot) {
|
|
98
|
+
const tsConfig = await readTsConfig(projectRoot);
|
|
99
|
+
if (tsConfig) {
|
|
100
|
+
const possiblePaths = [
|
|
101
|
+
join(projectRoot, "tsconfig.json"),
|
|
102
|
+
join(projectRoot, "tsconfig.app.json"),
|
|
103
|
+
join(projectRoot, "tsconfig.node.json")
|
|
104
|
+
];
|
|
105
|
+
for (const path of possiblePaths) {
|
|
106
|
+
if (await checkPathExists(path)) {
|
|
107
|
+
return {
|
|
108
|
+
typescript: true,
|
|
109
|
+
tsconfigPath: path
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
typescript: true,
|
|
115
|
+
tsconfigPath: join(projectRoot, "tsconfig.json")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
typescript: false
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function detectSrcDir(projectRoot) {
|
|
123
|
+
const possibleDirs = ["src", "app", "source", "lib"];
|
|
124
|
+
for (const dir of possibleDirs) {
|
|
125
|
+
const dirPath = join(projectRoot, dir);
|
|
126
|
+
if (await checkPathExists(dirPath)) {
|
|
127
|
+
return dir;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return "src";
|
|
131
|
+
}
|
|
132
|
+
async function detectPublicDir(projectRoot) {
|
|
133
|
+
const possibleDirs = ["public", "static", "assets"];
|
|
134
|
+
for (const dir of possibleDirs) {
|
|
135
|
+
const dirPath = join(projectRoot, dir);
|
|
136
|
+
if (await checkPathExists(dirPath)) {
|
|
137
|
+
return dir;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return "public";
|
|
141
|
+
}
|
|
142
|
+
async function detectGit(projectRoot) {
|
|
143
|
+
const gitDir = join(projectRoot, ".git");
|
|
144
|
+
const hasGit = await checkPathExists(gitDir);
|
|
145
|
+
if (!hasGit) {
|
|
146
|
+
return { hasGit: false };
|
|
147
|
+
}
|
|
148
|
+
const hooksPath = join(gitDir, "hooks");
|
|
149
|
+
const hasHooks = await checkPathExists(hooksPath);
|
|
150
|
+
return {
|
|
151
|
+
hasGit: true,
|
|
152
|
+
gitHooksPath: hasHooks ? hooksPath : void 0
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function detectLockfile(projectRoot, packageManager) {
|
|
156
|
+
const lockfiles = {
|
|
157
|
+
npm: "package-lock.json",
|
|
158
|
+
yarn: "yarn.lock",
|
|
159
|
+
pnpm: "pnpm-lock.yaml",
|
|
160
|
+
bun: "bun.lockb"
|
|
161
|
+
};
|
|
162
|
+
const lockfile = lockfiles[packageManager];
|
|
163
|
+
const lockfilePath = join(projectRoot, lockfile);
|
|
164
|
+
if (await checkPathExists(lockfilePath)) {
|
|
165
|
+
return lockfile;
|
|
166
|
+
}
|
|
167
|
+
return lockfile;
|
|
168
|
+
}
|
|
169
|
+
async function detectContext(projectRoot) {
|
|
170
|
+
const fullPath = resolve(projectRoot);
|
|
171
|
+
if (detectionCache.has(fullPath)) {
|
|
172
|
+
logger.debug(`Using cached context for ${fullPath}`);
|
|
173
|
+
const cached = detectionCache.get(fullPath);
|
|
174
|
+
if (cached) {
|
|
175
|
+
return cached;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
logger.debug(`Detecting context for project: ${fullPath}`);
|
|
179
|
+
let pkg;
|
|
180
|
+
try {
|
|
181
|
+
pkg = await readPackageJson(fullPath);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
184
|
+
throw new DetectionError(
|
|
185
|
+
`Invalid project: package.json not found or invalid. ${errorMessage}`,
|
|
186
|
+
{ projectRoot: fullPath }
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const [
|
|
190
|
+
frameworkInfo,
|
|
191
|
+
typescriptInfo,
|
|
192
|
+
bundlerInfo,
|
|
193
|
+
packageManager,
|
|
194
|
+
srcDir,
|
|
195
|
+
publicDir,
|
|
196
|
+
gitInfo
|
|
197
|
+
] = await Promise.all([
|
|
198
|
+
Promise.resolve(detectFramework(pkg)),
|
|
199
|
+
detectTypeScript(fullPath),
|
|
200
|
+
detectBundler(fullPath, pkg),
|
|
201
|
+
detectPackageManager(fullPath),
|
|
202
|
+
detectSrcDir(fullPath),
|
|
203
|
+
detectPublicDir(fullPath),
|
|
204
|
+
detectGit(fullPath)
|
|
205
|
+
]);
|
|
206
|
+
const lockfile = await detectLockfile(fullPath, packageManager);
|
|
207
|
+
const dependencies = pkg["dependencies"] || {};
|
|
208
|
+
const devDependencies = pkg["devDependencies"] || {};
|
|
209
|
+
const context = {
|
|
210
|
+
// Framework
|
|
211
|
+
framework: frameworkInfo.framework,
|
|
212
|
+
frameworkVersion: frameworkInfo.version,
|
|
213
|
+
// Bundler
|
|
214
|
+
bundler: bundlerInfo.bundler,
|
|
215
|
+
bundlerVersion: bundlerInfo.version,
|
|
216
|
+
// Language
|
|
217
|
+
typescript: typescriptInfo.typescript,
|
|
218
|
+
tsconfigPath: typescriptInfo.tsconfigPath,
|
|
219
|
+
// Package Manager
|
|
220
|
+
packageManager,
|
|
221
|
+
lockfile,
|
|
222
|
+
// Structure
|
|
223
|
+
projectRoot: fullPath,
|
|
224
|
+
srcDir,
|
|
225
|
+
publicDir,
|
|
226
|
+
// Environment
|
|
227
|
+
os: platform,
|
|
228
|
+
nodeVersion: version,
|
|
229
|
+
// Dependencies
|
|
230
|
+
dependencies,
|
|
231
|
+
devDependencies,
|
|
232
|
+
// Git
|
|
233
|
+
hasGit: gitInfo.hasGit,
|
|
234
|
+
gitHooksPath: gitInfo.gitHooksPath
|
|
235
|
+
};
|
|
236
|
+
detectionCache.set(fullPath, context);
|
|
237
|
+
logger.debug(`Context detected successfully for ${fullPath}`, {
|
|
238
|
+
framework: context.framework,
|
|
239
|
+
bundler: context.bundler,
|
|
240
|
+
typescript: context.typescript
|
|
241
|
+
});
|
|
242
|
+
return context;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/cli/prompts/language.ts
|
|
246
|
+
import inquirer from "inquirer";
|
|
247
|
+
|
|
248
|
+
// src/cli/i18n/fr.ts
|
|
249
|
+
var fr = {
|
|
250
|
+
language: {
|
|
251
|
+
select: "Choisissez votre langue",
|
|
252
|
+
options: [
|
|
253
|
+
{ value: "fr", name: "Fran\xE7ais" },
|
|
254
|
+
{ value: "en", name: "English" },
|
|
255
|
+
{ value: "es", name: "Espa\xF1ol" }
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
common: {
|
|
259
|
+
continue: "Continuer",
|
|
260
|
+
cancel: "Annuler",
|
|
261
|
+
back: "Retour",
|
|
262
|
+
none: "Aucun",
|
|
263
|
+
selected: (count) => count === 0 ? "Aucune biblioth\xE8que s\xE9lectionn\xE9e" : count === 1 ? "1 biblioth\xE8que s\xE9lectionn\xE9e" : `${count} biblioth\xE8ques s\xE9lectionn\xE9es`
|
|
264
|
+
},
|
|
265
|
+
plugins: {
|
|
266
|
+
selectCategory: (category) => `S\xE9lectionnez vos biblioth\xE8ques : ${category}`,
|
|
267
|
+
selectMultiple: "S\xE9lection multiple",
|
|
268
|
+
pressSpace: "Appuyez sur <espace> pour s\xE9lectionner",
|
|
269
|
+
pressEnter: "Appuyez sur <entr\xE9e> pour valider",
|
|
270
|
+
description: "Description"
|
|
271
|
+
},
|
|
272
|
+
detection: {
|
|
273
|
+
detecting: "\u{1F50D} D\xE9tection du contexte...",
|
|
274
|
+
framework: "Framework",
|
|
275
|
+
typescript: "TypeScript",
|
|
276
|
+
bundler: "Bundler",
|
|
277
|
+
packageManager: "Gestionnaire de paquets"
|
|
278
|
+
},
|
|
279
|
+
confirmation: {
|
|
280
|
+
summary: "\u{1F4CB} R\xE9sum\xE9 de l'installation",
|
|
281
|
+
packagesToInstall: "\u{1F4E6} Packages \xE0 installer",
|
|
282
|
+
filesToCreate: "\u{1F4DD} Fichiers qui seront cr\xE9\xE9s",
|
|
283
|
+
filesToModify: "\u{1F4DD} Fichiers qui seront modifi\xE9s",
|
|
284
|
+
continueQuestion: "Continuer avec l'installation ?"
|
|
285
|
+
},
|
|
286
|
+
installation: {
|
|
287
|
+
installing: "Installation en cours...",
|
|
288
|
+
configuring: "Configuration en cours...",
|
|
289
|
+
success: "\u2728 Installation termin\xE9e !",
|
|
290
|
+
error: "\u274C Erreur lors de l'installation",
|
|
291
|
+
rollback: "\u21BA Rollback en cours..."
|
|
292
|
+
},
|
|
293
|
+
report: {
|
|
294
|
+
title: "\u2728 Installation termin\xE9e !",
|
|
295
|
+
packagesInstalled: "\u{1F4E6} Packages install\xE9s",
|
|
296
|
+
filesCreated: "\u{1F4DD} Fichiers cr\xE9\xE9s",
|
|
297
|
+
filesModified: "\u{1F4DD} Fichiers modifi\xE9s",
|
|
298
|
+
nextSteps: "\u{1F680} Prochaines \xE9tapes"
|
|
299
|
+
},
|
|
300
|
+
errors: {
|
|
301
|
+
detectionFailed: "\xC9chec de la d\xE9tection du contexte",
|
|
302
|
+
installationFailed: "\xC9chec de l'installation",
|
|
303
|
+
validationFailed: "\xC9chec de la validation",
|
|
304
|
+
incompatiblePlugins: (plugins) => `Plugins incompatibles d\xE9tect\xE9s : ${plugins.join(", ")}`
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/cli/i18n/en.ts
|
|
309
|
+
var en = {
|
|
310
|
+
language: {
|
|
311
|
+
select: "Choose your language",
|
|
312
|
+
options: [
|
|
313
|
+
{ value: "fr", name: "Fran\xE7ais" },
|
|
314
|
+
{ value: "en", name: "English" },
|
|
315
|
+
{ value: "es", name: "Espa\xF1ol" }
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
common: {
|
|
319
|
+
continue: "Continue",
|
|
320
|
+
cancel: "Cancel",
|
|
321
|
+
back: "Back",
|
|
322
|
+
none: "None",
|
|
323
|
+
selected: (count) => count === 0 ? "No library selected" : count === 1 ? "1 library selected" : `${count} libraries selected`
|
|
324
|
+
},
|
|
325
|
+
plugins: {
|
|
326
|
+
selectCategory: (category) => `Select your libraries: ${category}`,
|
|
327
|
+
selectMultiple: "Multiple selection",
|
|
328
|
+
pressSpace: "Press <space> to select",
|
|
329
|
+
pressEnter: "Press <enter> to confirm",
|
|
330
|
+
description: "Description"
|
|
331
|
+
},
|
|
332
|
+
detection: {
|
|
333
|
+
detecting: "\u{1F50D} Detecting context...",
|
|
334
|
+
framework: "Framework",
|
|
335
|
+
typescript: "TypeScript",
|
|
336
|
+
bundler: "Bundler",
|
|
337
|
+
packageManager: "Package manager"
|
|
338
|
+
},
|
|
339
|
+
confirmation: {
|
|
340
|
+
summary: "\u{1F4CB} Installation Summary",
|
|
341
|
+
packagesToInstall: "\u{1F4E6} Packages to install",
|
|
342
|
+
filesToCreate: "\u{1F4DD} Files that will be created",
|
|
343
|
+
filesToModify: "\u{1F4DD} Files that will be modified",
|
|
344
|
+
continueQuestion: "Continue with installation?"
|
|
345
|
+
},
|
|
346
|
+
installation: {
|
|
347
|
+
installing: "Installing...",
|
|
348
|
+
configuring: "Configuring...",
|
|
349
|
+
success: "\u2728 Installation completed!",
|
|
350
|
+
error: "\u274C Installation error",
|
|
351
|
+
rollback: "\u21BA Rolling back..."
|
|
352
|
+
},
|
|
353
|
+
report: {
|
|
354
|
+
title: "\u2728 Installation completed!",
|
|
355
|
+
packagesInstalled: "\u{1F4E6} Installed packages",
|
|
356
|
+
filesCreated: "\u{1F4DD} Created files",
|
|
357
|
+
filesModified: "\u{1F4DD} Modified files",
|
|
358
|
+
nextSteps: "\u{1F680} Next steps"
|
|
359
|
+
},
|
|
360
|
+
errors: {
|
|
361
|
+
detectionFailed: "Context detection failed",
|
|
362
|
+
installationFailed: "Installation failed",
|
|
363
|
+
validationFailed: "Validation failed",
|
|
364
|
+
incompatiblePlugins: (plugins) => `Incompatible plugins detected: ${plugins.join(", ")}`
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// src/cli/i18n/es.ts
|
|
369
|
+
var es = {
|
|
370
|
+
language: {
|
|
371
|
+
select: "Elige tu idioma",
|
|
372
|
+
options: [
|
|
373
|
+
{ value: "fr", name: "Fran\xE7ais" },
|
|
374
|
+
{ value: "en", name: "English" },
|
|
375
|
+
{ value: "es", name: "Espa\xF1ol" }
|
|
376
|
+
]
|
|
377
|
+
},
|
|
378
|
+
common: {
|
|
379
|
+
continue: "Continuar",
|
|
380
|
+
cancel: "Cancelar",
|
|
381
|
+
back: "Volver",
|
|
382
|
+
none: "Ninguno",
|
|
383
|
+
selected: (count) => count === 0 ? "Ninguna biblioteca seleccionada" : count === 1 ? "1 biblioteca seleccionada" : `${count} bibliotecas seleccionadas`
|
|
384
|
+
},
|
|
385
|
+
plugins: {
|
|
386
|
+
selectCategory: (category) => `Selecciona tus bibliotecas: ${category}`,
|
|
387
|
+
selectMultiple: "Selecci\xF3n m\xFAltiple",
|
|
388
|
+
pressSpace: "Presiona <espacio> para seleccionar",
|
|
389
|
+
pressEnter: "Presiona <entrar> para confirmar",
|
|
390
|
+
description: "Descripci\xF3n"
|
|
391
|
+
},
|
|
392
|
+
detection: {
|
|
393
|
+
detecting: "\u{1F50D} Detectando contexto...",
|
|
394
|
+
framework: "Framework",
|
|
395
|
+
typescript: "TypeScript",
|
|
396
|
+
bundler: "Bundler",
|
|
397
|
+
packageManager: "Gestor de paquetes"
|
|
398
|
+
},
|
|
399
|
+
confirmation: {
|
|
400
|
+
summary: "\u{1F4CB} Resumen de la instalaci\xF3n",
|
|
401
|
+
packagesToInstall: "\u{1F4E6} Paquetes a instalar",
|
|
402
|
+
filesToCreate: "\u{1F4DD} Archivos que se crear\xE1n",
|
|
403
|
+
filesToModify: "\u{1F4DD} Archivos que se modificar\xE1n",
|
|
404
|
+
continueQuestion: "\xBFContinuar con la instalaci\xF3n?"
|
|
405
|
+
},
|
|
406
|
+
installation: {
|
|
407
|
+
installing: "Instalando...",
|
|
408
|
+
configuring: "Configurando...",
|
|
409
|
+
success: "\u2728 \xA1Instalaci\xF3n completada!",
|
|
410
|
+
error: "\u274C Error en la instalaci\xF3n",
|
|
411
|
+
rollback: "\u21BA Revirtiendo..."
|
|
412
|
+
},
|
|
413
|
+
report: {
|
|
414
|
+
title: "\u2728 \xA1Instalaci\xF3n completada!",
|
|
415
|
+
packagesInstalled: "\u{1F4E6} Paquetes instalados",
|
|
416
|
+
filesCreated: "\u{1F4DD} Archivos creados",
|
|
417
|
+
filesModified: "\u{1F4DD} Archivos modificados",
|
|
418
|
+
nextSteps: "\u{1F680} Pr\xF3ximos pasos"
|
|
419
|
+
},
|
|
420
|
+
errors: {
|
|
421
|
+
detectionFailed: "Fallo en la detecci\xF3n del contexto",
|
|
422
|
+
installationFailed: "Fallo en la instalaci\xF3n",
|
|
423
|
+
validationFailed: "Fallo en la validaci\xF3n",
|
|
424
|
+
incompatiblePlugins: (plugins) => `Plugins incompatibles detectados: ${plugins.join(", ")}`
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// src/cli/i18n/index.ts
|
|
429
|
+
function getTranslations(lang) {
|
|
430
|
+
switch (lang) {
|
|
431
|
+
case "fr":
|
|
432
|
+
return fr;
|
|
433
|
+
case "en":
|
|
434
|
+
return en;
|
|
435
|
+
case "es":
|
|
436
|
+
return es;
|
|
437
|
+
default:
|
|
438
|
+
return en;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/cli/prompts/language.ts
|
|
443
|
+
async function promptLanguage() {
|
|
444
|
+
const defaultTranslations = getTranslations("en");
|
|
445
|
+
const { language } = await inquirer.prompt([
|
|
446
|
+
{
|
|
447
|
+
type: "list",
|
|
448
|
+
name: "language",
|
|
449
|
+
message: defaultTranslations.language.select,
|
|
450
|
+
choices: defaultTranslations.language.options.map((opt) => ({
|
|
451
|
+
name: opt.name,
|
|
452
|
+
value: opt.value
|
|
453
|
+
})),
|
|
454
|
+
default: "en"
|
|
455
|
+
}
|
|
456
|
+
]);
|
|
457
|
+
return language;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/cli/prompts/select-plugins.ts
|
|
461
|
+
import inquirer2 from "inquirer";
|
|
462
|
+
function getCategoryName(category, _lang) {
|
|
463
|
+
const categoryMap = {
|
|
464
|
+
["routing" /* ROUTING */]: "Routing",
|
|
465
|
+
["state" /* STATE */]: "State Management",
|
|
466
|
+
["http" /* HTTP */]: "HTTP Client",
|
|
467
|
+
["css" /* CSS */]: "CSS / Styling",
|
|
468
|
+
["ui" /* UI */]: "UI Components",
|
|
469
|
+
["forms" /* FORMS */]: "Forms",
|
|
470
|
+
["tooling" /* TOOLING */]: "Tooling",
|
|
471
|
+
["testing" /* TESTING */]: "Testing",
|
|
472
|
+
["i18n" /* I18N */]: "Internationalization",
|
|
473
|
+
["animation" /* ANIMATION */]: "Animation",
|
|
474
|
+
["utils" /* UTILS */]: "Utilities"
|
|
475
|
+
};
|
|
476
|
+
return categoryMap[category] || category;
|
|
477
|
+
}
|
|
478
|
+
function formatPluginChoice(plugin, _lang) {
|
|
479
|
+
return {
|
|
480
|
+
name: `${plugin.displayName}
|
|
481
|
+
${plugin.description}`,
|
|
482
|
+
value: plugin.name,
|
|
483
|
+
short: plugin.displayName
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
async function promptPluginSelection(ctx, availablePlugins, lang) {
|
|
487
|
+
const translations = getTranslations(lang);
|
|
488
|
+
const selectedPlugins = [];
|
|
489
|
+
const pluginsByCategory = /* @__PURE__ */ new Map();
|
|
490
|
+
for (const plugin of availablePlugins) {
|
|
491
|
+
if (!plugin.frameworks.includes(ctx.framework)) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (plugin.requiresTypeScript === true && !ctx.typescript) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (plugin.bundlers && ctx.bundler && !plugin.bundlers.includes(ctx.bundler)) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const category = plugin.category;
|
|
501
|
+
if (!pluginsByCategory.has(category)) {
|
|
502
|
+
pluginsByCategory.set(category, []);
|
|
503
|
+
}
|
|
504
|
+
const categoryPluginsList = pluginsByCategory.get(category);
|
|
505
|
+
if (categoryPluginsList !== void 0) {
|
|
506
|
+
categoryPluginsList.push(plugin);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const categories = Array.from(pluginsByCategory.keys()).sort();
|
|
510
|
+
for (const category of categories) {
|
|
511
|
+
const plugins = pluginsByCategory.get(category);
|
|
512
|
+
if (plugins === void 0 || plugins.length === 0) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const categoryName = getCategoryName(category, lang);
|
|
516
|
+
const categoryPlugins = getPluginsByCategory(category).filter(
|
|
517
|
+
(p) => plugins.some((ap) => ap.name === p.name)
|
|
518
|
+
);
|
|
519
|
+
const choices = [
|
|
520
|
+
...categoryPlugins.map((plugin) => formatPluginChoice(plugin, lang)),
|
|
521
|
+
new inquirer2.Separator(),
|
|
522
|
+
{
|
|
523
|
+
name: translations.common.none,
|
|
524
|
+
value: "__none__",
|
|
525
|
+
short: translations.common.none
|
|
526
|
+
}
|
|
527
|
+
];
|
|
528
|
+
const { selected } = await inquirer2.prompt([
|
|
529
|
+
{
|
|
530
|
+
type: "checkbox",
|
|
531
|
+
name: "selected",
|
|
532
|
+
message: `${translations.plugins.selectCategory(categoryName)}
|
|
533
|
+
${translations.plugins.pressSpace} | ${translations.plugins.pressEnter}`,
|
|
534
|
+
choices,
|
|
535
|
+
pageSize: 10,
|
|
536
|
+
loop: false
|
|
537
|
+
}
|
|
538
|
+
]);
|
|
539
|
+
const pluginNames = selected.filter((name) => name !== "__none__");
|
|
540
|
+
for (const pluginName of pluginNames) {
|
|
541
|
+
const plugin = categoryPlugins.find((p) => p.name === pluginName);
|
|
542
|
+
if (plugin !== void 0) {
|
|
543
|
+
selectedPlugins.push(plugin);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return selectedPlugins;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/cli/prompts/confirm.ts
|
|
551
|
+
import inquirer3 from "inquirer";
|
|
552
|
+
async function promptConfirmation(selectedPlugins, lang) {
|
|
553
|
+
const translations = getTranslations(lang);
|
|
554
|
+
console.log(`
|
|
555
|
+
${translations.confirmation.summary}
|
|
556
|
+
`);
|
|
557
|
+
console.log(`${translations.confirmation.packagesToInstall}:`);
|
|
558
|
+
for (const plugin of selectedPlugins) {
|
|
559
|
+
console.log(
|
|
560
|
+
` \u2022 ${plugin.displayName}${plugin.version ? ` (${plugin.version})` : ""}`
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
const { confirm } = await inquirer3.prompt([
|
|
564
|
+
{
|
|
565
|
+
type: "confirm",
|
|
566
|
+
name: "confirm",
|
|
567
|
+
message: translations.confirmation.continueQuestion,
|
|
568
|
+
default: true
|
|
569
|
+
}
|
|
570
|
+
]);
|
|
571
|
+
return confirm;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/core/installer.ts
|
|
575
|
+
var Installer = class {
|
|
576
|
+
/**
|
|
577
|
+
* @param ctx - Contexte du projet détecté
|
|
578
|
+
* @param validator - Validateur de compatibilité
|
|
579
|
+
* @param writer - Writer de configuration
|
|
580
|
+
* @param backupManager - Gestionnaire de backups
|
|
581
|
+
*/
|
|
582
|
+
constructor(ctx, validator, writer, backupManager) {
|
|
583
|
+
this.ctx = ctx;
|
|
584
|
+
this.validator = validator;
|
|
585
|
+
this.writer = writer;
|
|
586
|
+
this.backupManager = backupManager;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Installe un ensemble de plugins
|
|
590
|
+
*
|
|
591
|
+
* @param plugins - Liste des plugins à installer
|
|
592
|
+
* @param options - Options d'installation (skipPackageInstall pour --no-install)
|
|
593
|
+
* @returns Rapport d'installation avec détails
|
|
594
|
+
* @throws {Error} Si l'installation échoue (après rollback)
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* const report = await installer.install([plugin1, plugin2])
|
|
599
|
+
* console.log(`Installed ${report.installed.length} plugin(s)`)
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
async install(plugins, options) {
|
|
603
|
+
const startTime = Date.now();
|
|
604
|
+
logger.info(`Starting installation of ${plugins.length} plugin(s)`);
|
|
605
|
+
try {
|
|
606
|
+
logger.debug("Validating plugins compatibility...");
|
|
607
|
+
const validationResult = this.validator.validate(plugins);
|
|
608
|
+
if (!validationResult.valid) {
|
|
609
|
+
const errorMessages = validationResult.errors.map((e) => e.message).join("; ");
|
|
610
|
+
throw new Error(
|
|
611
|
+
`Validation failed: ${errorMessages}. Please fix compatibility issues before installing.`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
if (validationResult.warnings.length > 0) {
|
|
615
|
+
logger.warn(
|
|
616
|
+
`Found ${validationResult.warnings.length} warning(s):`,
|
|
617
|
+
validationResult.warnings.map((w) => w.message)
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
logger.debug("Resolving dependencies...");
|
|
621
|
+
const resolved = this.resolveDependencies(plugins);
|
|
622
|
+
const allPlugins = resolved.plugins;
|
|
623
|
+
if (resolved.autoInstalled.length > 0) {
|
|
624
|
+
logger.info(
|
|
625
|
+
`Auto-installing ${resolved.autoInstalled.length} required dependency(ies): ${resolved.autoInstalled.join(", ")}`
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
logger.debug("Running pre-install hooks...");
|
|
629
|
+
await this.runPreInstallHooks(allPlugins);
|
|
630
|
+
if (options?.skipPackageInstall) {
|
|
631
|
+
logger.info("Skipping package installation (--no-install mode)");
|
|
632
|
+
} else {
|
|
633
|
+
logger.debug("Installing packages...");
|
|
634
|
+
const installResults = await this.installPackages(allPlugins);
|
|
635
|
+
const failedInstalls = installResults.filter((r) => !r.success);
|
|
636
|
+
if (failedInstalls.length > 0) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
`Failed to install packages for ${failedInstalls.length} plugin(s)`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
logger.debug("Configuring plugins...");
|
|
643
|
+
const configResults = [];
|
|
644
|
+
const filesCreated = [];
|
|
645
|
+
for (const plugin of allPlugins) {
|
|
646
|
+
try {
|
|
647
|
+
logger.debug(`Configuring ${plugin.displayName}...`);
|
|
648
|
+
const configResult = await plugin.configure(this.ctx);
|
|
649
|
+
configResults.push(configResult);
|
|
650
|
+
filesCreated.push(...configResult.files || []);
|
|
651
|
+
if (!configResult.success) {
|
|
652
|
+
throw new Error(
|
|
653
|
+
`Configuration failed for ${plugin.displayName}: ${configResult.message || "Unknown error"}`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
} catch (error) {
|
|
657
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
658
|
+
logger.error(
|
|
659
|
+
`Configuration failed for ${plugin.displayName}: ${errorMessage}`
|
|
660
|
+
);
|
|
661
|
+
throw error;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
logger.debug("Running post-install hooks...");
|
|
665
|
+
await this.runPostInstallHooks(allPlugins);
|
|
666
|
+
const duration = Date.now() - startTime;
|
|
667
|
+
const installed = allPlugins.map((p) => p.name);
|
|
668
|
+
logger.info(
|
|
669
|
+
`Successfully installed ${installed.length} plugin(s) in ${duration}ms`
|
|
670
|
+
);
|
|
671
|
+
return {
|
|
672
|
+
success: true,
|
|
673
|
+
duration,
|
|
674
|
+
installed,
|
|
675
|
+
warnings: validationResult.warnings,
|
|
676
|
+
filesCreated
|
|
677
|
+
};
|
|
678
|
+
} catch (error) {
|
|
679
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
680
|
+
logger.error(`Installation failed: ${errorMessage}`);
|
|
681
|
+
logger.debug("Rolling back changes...");
|
|
682
|
+
try {
|
|
683
|
+
await this.rollback(plugins);
|
|
684
|
+
} catch (rollbackError) {
|
|
685
|
+
const rollbackMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
686
|
+
logger.error(`Rollback failed: ${rollbackMessage}`);
|
|
687
|
+
}
|
|
688
|
+
const duration = Date.now() - startTime;
|
|
689
|
+
return {
|
|
690
|
+
success: false,
|
|
691
|
+
duration,
|
|
692
|
+
installed: [],
|
|
693
|
+
warnings: [],
|
|
694
|
+
filesCreated: []
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Résout les dépendances requises et recommandées
|
|
700
|
+
*
|
|
701
|
+
* @param plugins - Liste des plugins initiaux
|
|
702
|
+
* @returns Plugins avec dépendances résolues
|
|
703
|
+
*
|
|
704
|
+
* @internal
|
|
705
|
+
*/
|
|
706
|
+
resolveDependencies(plugins) {
|
|
707
|
+
const pluginMap = /* @__PURE__ */ new Map();
|
|
708
|
+
const autoInstalled = [];
|
|
709
|
+
for (const plugin of plugins) {
|
|
710
|
+
pluginMap.set(plugin.name, plugin);
|
|
711
|
+
}
|
|
712
|
+
let changed = true;
|
|
713
|
+
while (changed) {
|
|
714
|
+
changed = false;
|
|
715
|
+
for (const plugin of Array.from(pluginMap.values())) {
|
|
716
|
+
if (!plugin.requires) {
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
for (const required of plugin.requires) {
|
|
720
|
+
if (!pluginMap.has(required)) {
|
|
721
|
+
logger.warn(
|
|
722
|
+
`Plugin ${plugin.name} requires ${required}, but it's not available in the registry`
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return {
|
|
729
|
+
plugins: Array.from(pluginMap.values()),
|
|
730
|
+
autoInstalled
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Installe les packages pour tous les plugins
|
|
735
|
+
*
|
|
736
|
+
* @param plugins - Liste des plugins
|
|
737
|
+
* @returns Résultats d'installation pour chaque plugin
|
|
738
|
+
*
|
|
739
|
+
* @internal
|
|
740
|
+
*/
|
|
741
|
+
async installPackages(plugins) {
|
|
742
|
+
const results = [];
|
|
743
|
+
const installPromises = plugins.map(async (plugin) => {
|
|
744
|
+
try {
|
|
745
|
+
if (plugin.detect && await plugin.detect(this.ctx)) {
|
|
746
|
+
logger.debug(`${plugin.displayName} is already installed`);
|
|
747
|
+
return {
|
|
748
|
+
packages: {},
|
|
749
|
+
success: true,
|
|
750
|
+
message: "Already installed"
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
if (plugin.preInstall) {
|
|
754
|
+
await plugin.preInstall(this.ctx);
|
|
755
|
+
}
|
|
756
|
+
const result = await plugin.install(this.ctx);
|
|
757
|
+
if (plugin.postInstall) {
|
|
758
|
+
await plugin.postInstall(this.ctx);
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
} catch (error) {
|
|
762
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
763
|
+
logger.error(`Failed to install ${plugin.displayName}: ${errorMessage}`);
|
|
764
|
+
return {
|
|
765
|
+
packages: {},
|
|
766
|
+
success: false,
|
|
767
|
+
message: errorMessage
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
const installResults = await Promise.all(installPromises);
|
|
772
|
+
results.push(...installResults);
|
|
773
|
+
const allDependencies = [];
|
|
774
|
+
const allDevDependencies = [];
|
|
775
|
+
for (const result of installResults) {
|
|
776
|
+
if (result.success && result.packages) {
|
|
777
|
+
if (result.packages.dependencies) {
|
|
778
|
+
allDependencies.push(...result.packages.dependencies);
|
|
779
|
+
}
|
|
780
|
+
if (result.packages.devDependencies) {
|
|
781
|
+
allDevDependencies.push(...result.packages.devDependencies);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (allDependencies.length > 0) {
|
|
786
|
+
await installPackages(allDependencies, {
|
|
787
|
+
packageManager: this.ctx.packageManager,
|
|
788
|
+
projectRoot: this.ctx.projectRoot,
|
|
789
|
+
dev: false,
|
|
790
|
+
silent: false
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
if (allDevDependencies.length > 0) {
|
|
794
|
+
await installPackages(allDevDependencies, {
|
|
795
|
+
packageManager: this.ctx.packageManager,
|
|
796
|
+
projectRoot: this.ctx.projectRoot,
|
|
797
|
+
dev: true,
|
|
798
|
+
silent: false
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
return results;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Exécute les hooks pre-install de tous les plugins
|
|
805
|
+
*
|
|
806
|
+
* @param plugins - Liste des plugins
|
|
807
|
+
*
|
|
808
|
+
* @internal
|
|
809
|
+
*/
|
|
810
|
+
async runPreInstallHooks(plugins) {
|
|
811
|
+
for (const plugin of plugins) {
|
|
812
|
+
if (plugin.preInstall) {
|
|
813
|
+
try {
|
|
814
|
+
await plugin.preInstall(this.ctx);
|
|
815
|
+
} catch (error) {
|
|
816
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
817
|
+
logger.warn(
|
|
818
|
+
`Pre-install hook failed for ${plugin.displayName}: ${errorMessage}`
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Exécute les hooks post-install de tous les plugins
|
|
826
|
+
*
|
|
827
|
+
* @param plugins - Liste des plugins
|
|
828
|
+
*
|
|
829
|
+
* @internal
|
|
830
|
+
*/
|
|
831
|
+
async runPostInstallHooks(plugins) {
|
|
832
|
+
for (const plugin of plugins) {
|
|
833
|
+
if (plugin.postInstall) {
|
|
834
|
+
try {
|
|
835
|
+
await plugin.postInstall(this.ctx);
|
|
836
|
+
} catch (error) {
|
|
837
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
838
|
+
logger.warn(
|
|
839
|
+
`Post-install hook failed for ${plugin.displayName}: ${errorMessage}`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Effectue un rollback complet en cas d'erreur
|
|
847
|
+
*
|
|
848
|
+
* @param plugins - Liste des plugins installés
|
|
849
|
+
*
|
|
850
|
+
* @internal
|
|
851
|
+
*/
|
|
852
|
+
async rollback(plugins) {
|
|
853
|
+
logger.debug("Starting rollback...");
|
|
854
|
+
for (const plugin of plugins.reverse()) {
|
|
855
|
+
if (plugin.rollback) {
|
|
856
|
+
try {
|
|
857
|
+
await plugin.rollback(this.ctx);
|
|
858
|
+
logger.debug(`Rolled back ${plugin.displayName}`);
|
|
859
|
+
} catch (error) {
|
|
860
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
861
|
+
logger.error(
|
|
862
|
+
`Rollback failed for ${plugin.displayName}: ${errorMessage}`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
try {
|
|
868
|
+
await this.backupManager.restoreAll();
|
|
869
|
+
logger.debug("Restored all file backups");
|
|
870
|
+
} catch (error) {
|
|
871
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
872
|
+
logger.error(`Failed to restore backups: ${errorMessage}`);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
// src/cli/ui/spinner.ts
|
|
878
|
+
import ora from "ora";
|
|
879
|
+
var SpinnerManager = class {
|
|
880
|
+
spinner = null;
|
|
881
|
+
constructor() {
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Démarre un spinner avec un message
|
|
885
|
+
*
|
|
886
|
+
* @param message - Message à afficher (peut être une clé de traduction)
|
|
887
|
+
* @param text - Texte personnalisé (optionnel, remplace le message traduit)
|
|
888
|
+
*/
|
|
889
|
+
start(message, text) {
|
|
890
|
+
this.stop();
|
|
891
|
+
const displayText = text || message;
|
|
892
|
+
this.spinner = ora({
|
|
893
|
+
text: displayText,
|
|
894
|
+
spinner: "dots"
|
|
895
|
+
}).start();
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Met à jour le message du spinner
|
|
899
|
+
*
|
|
900
|
+
* @param message - Nouveau message
|
|
901
|
+
*/
|
|
902
|
+
update(message) {
|
|
903
|
+
if (this.spinner) {
|
|
904
|
+
this.spinner.text = message;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Arrête le spinner avec succès
|
|
909
|
+
*
|
|
910
|
+
* @param message - Message de succès (optionnel)
|
|
911
|
+
*/
|
|
912
|
+
succeed(message) {
|
|
913
|
+
if (this.spinner) {
|
|
914
|
+
this.spinner.succeed(message);
|
|
915
|
+
this.spinner = null;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Arrête le spinner avec une erreur
|
|
920
|
+
*
|
|
921
|
+
* @param message - Message d'erreur (optionnel)
|
|
922
|
+
*/
|
|
923
|
+
fail(message) {
|
|
924
|
+
if (this.spinner) {
|
|
925
|
+
this.spinner.fail(message);
|
|
926
|
+
this.spinner = null;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Arrête le spinner avec un avertissement
|
|
931
|
+
*
|
|
932
|
+
* @param message - Message d'avertissement (optionnel)
|
|
933
|
+
*/
|
|
934
|
+
warn(message) {
|
|
935
|
+
if (this.spinner) {
|
|
936
|
+
this.spinner.warn(message);
|
|
937
|
+
this.spinner = null;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Arrête le spinner avec un message d'information
|
|
942
|
+
*
|
|
943
|
+
* @param message - Message d'information (optionnel)
|
|
944
|
+
*/
|
|
945
|
+
info(message) {
|
|
946
|
+
if (this.spinner) {
|
|
947
|
+
this.spinner.info(message);
|
|
948
|
+
this.spinner = null;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Arrête le spinner sans message
|
|
953
|
+
*/
|
|
954
|
+
stop() {
|
|
955
|
+
if (this.spinner) {
|
|
956
|
+
this.spinner.stop();
|
|
957
|
+
this.spinner = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Vérifie si un spinner est actif
|
|
962
|
+
*/
|
|
963
|
+
isSpinning() {
|
|
964
|
+
return this.spinner !== null && this.spinner.isSpinning;
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
// src/cli/ui/report.ts
|
|
969
|
+
function displayInstallationReport(report, plugins, lang) {
|
|
970
|
+
const t = getTranslations(lang);
|
|
971
|
+
console.log(`
|
|
972
|
+
${t.report.title}`);
|
|
973
|
+
if (report.duration > 0) {
|
|
974
|
+
const durationSeconds = (report.duration / 1e3).toFixed(1);
|
|
975
|
+
console.log(` (${durationSeconds}s)
|
|
976
|
+
`);
|
|
977
|
+
} else {
|
|
978
|
+
console.log("");
|
|
979
|
+
}
|
|
980
|
+
if (report.installed.length > 0) {
|
|
981
|
+
console.log(`${t.report.packagesInstalled}:`);
|
|
982
|
+
for (const pluginName of report.installed) {
|
|
983
|
+
const plugin = plugins.find((p) => p.name === pluginName);
|
|
984
|
+
const version2 = plugin?.version ? ` (${plugin.version})` : "";
|
|
985
|
+
console.log(` \u2713 ${plugin?.displayName || pluginName}${version2}`);
|
|
986
|
+
}
|
|
987
|
+
console.log("");
|
|
988
|
+
}
|
|
989
|
+
const createdFiles = report.filesCreated.filter((f) => f.type === "create");
|
|
990
|
+
if (createdFiles.length > 0) {
|
|
991
|
+
console.log(`${t.report.filesCreated}:`);
|
|
992
|
+
for (const file of createdFiles) {
|
|
993
|
+
console.log(` \u2022 ${file.path}`);
|
|
994
|
+
}
|
|
995
|
+
console.log("");
|
|
996
|
+
}
|
|
997
|
+
const modifiedFiles = report.filesCreated.filter((f) => f.type === "modify");
|
|
998
|
+
if (modifiedFiles.length > 0) {
|
|
999
|
+
console.log(`${t.report.filesModified}:`);
|
|
1000
|
+
for (const file of modifiedFiles) {
|
|
1001
|
+
console.log(` \u2022 ${file.path}`);
|
|
1002
|
+
}
|
|
1003
|
+
console.log("");
|
|
1004
|
+
}
|
|
1005
|
+
if (report.warnings.length > 0) {
|
|
1006
|
+
console.log(`\u26A0\uFE0F ${report.warnings.length} warning(s):`);
|
|
1007
|
+
for (const warning of report.warnings) {
|
|
1008
|
+
console.log(` \u26A0 ${warning.message}`);
|
|
1009
|
+
if (warning.plugins && warning.plugins.length > 0) {
|
|
1010
|
+
console.log(` Plugins: ${warning.plugins.join(", ")}`);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
console.log("");
|
|
1014
|
+
}
|
|
1015
|
+
displayNextSteps(lang);
|
|
1016
|
+
}
|
|
1017
|
+
function displayNextSteps(lang) {
|
|
1018
|
+
const t = getTranslations(lang);
|
|
1019
|
+
console.log(`${t.report.nextSteps}:`);
|
|
1020
|
+
console.log(" 1. npm run dev");
|
|
1021
|
+
console.log(" 2. Visitez http://localhost:5173");
|
|
1022
|
+
console.log(" 3. Consultez la documentation dans src/");
|
|
1023
|
+
console.log("");
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/cli/commands/install.ts
|
|
1027
|
+
async function installReact(options) {
|
|
1028
|
+
try {
|
|
1029
|
+
const language = await promptLanguage();
|
|
1030
|
+
const t = getTranslations(language);
|
|
1031
|
+
console.log(`
|
|
1032
|
+
${t.detection.detecting}`);
|
|
1033
|
+
const projectRoot = process.cwd();
|
|
1034
|
+
const ctx = await detectContext(projectRoot);
|
|
1035
|
+
console.log(
|
|
1036
|
+
` \u2713 ${t.detection.framework}: ${ctx.framework} ${ctx.frameworkVersion}`
|
|
1037
|
+
);
|
|
1038
|
+
console.log(
|
|
1039
|
+
` \u2713 ${t.detection.typescript}: ${ctx.typescript ? "Oui" : "Non"}`
|
|
1040
|
+
);
|
|
1041
|
+
if (ctx.bundler) {
|
|
1042
|
+
console.log(
|
|
1043
|
+
` \u2713 ${t.detection.bundler}: ${ctx.bundler} ${ctx.bundlerVersion || ""}`
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
console.log(` \u2713 ${t.detection.packageManager}: ${ctx.packageManager}
|
|
1047
|
+
`);
|
|
1048
|
+
let selectedPlugins = [];
|
|
1049
|
+
if (options.yes) {
|
|
1050
|
+
logger.info("Using default recommendations (--yes mode)");
|
|
1051
|
+
} else {
|
|
1052
|
+
selectedPlugins = await promptPluginSelection(
|
|
1053
|
+
ctx,
|
|
1054
|
+
pluginRegistry,
|
|
1055
|
+
language
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
if (selectedPlugins.length === 0) {
|
|
1059
|
+
console.log(`
|
|
1060
|
+
${t.common.selected(0)}`);
|
|
1061
|
+
console.log("Exiting...");
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
console.log(`
|
|
1065
|
+
${t.common.selected(selectedPlugins.length)}`);
|
|
1066
|
+
if (!options.yes && !options.silent) {
|
|
1067
|
+
const confirmed = await promptConfirmation(selectedPlugins, language);
|
|
1068
|
+
if (!confirmed) {
|
|
1069
|
+
console.log(t.common.cancel);
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (options.dryRun) {
|
|
1074
|
+
console.log("\n\u{1F50D} MODE DRY-RUN (simulation uniquement)");
|
|
1075
|
+
console.log("\u2501".repeat(50));
|
|
1076
|
+
console.log("\n\u{1F4E6} Packages \xE0 installer :");
|
|
1077
|
+
for (const plugin of selectedPlugins) {
|
|
1078
|
+
console.log(
|
|
1079
|
+
` ${plugin.name}${plugin.version ? `@${plugin.version}` : ""}`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
console.log("\n\u{1F4DD} Fichiers qui seraient cr\xE9\xE9s/modifi\xE9s :");
|
|
1083
|
+
for (const plugin of selectedPlugins) {
|
|
1084
|
+
console.log(` ${plugin.displayName} configuration`);
|
|
1085
|
+
}
|
|
1086
|
+
console.log("\n\u26A0\uFE0F Aucune modification n'a \xE9t\xE9 effectu\xE9e (dry-run)");
|
|
1087
|
+
console.log("\u{1F4A1} Ex\xE9cutez sans --dry-run pour appliquer les changements");
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const backupManager = new BackupManager();
|
|
1091
|
+
const configWriter = new ConfigWriter(backupManager);
|
|
1092
|
+
const validator = new CompatibilityValidator(compatibilityRules);
|
|
1093
|
+
const installer = new Installer(ctx, validator, configWriter, backupManager);
|
|
1094
|
+
if (options.install === false) {
|
|
1095
|
+
console.log("\n\u2699\uFE0F Mode configuration uniquement (--no-install)");
|
|
1096
|
+
console.log("Les packages ne seront PAS install\xE9s\n");
|
|
1097
|
+
}
|
|
1098
|
+
const spinner = new SpinnerManager();
|
|
1099
|
+
spinner.start(t.installation.installing);
|
|
1100
|
+
try {
|
|
1101
|
+
const result = await installer.install(selectedPlugins, {
|
|
1102
|
+
skipPackageInstall: options.install === false
|
|
1103
|
+
});
|
|
1104
|
+
spinner.succeed(t.installation.success);
|
|
1105
|
+
if (result.success) {
|
|
1106
|
+
displayInstallationReport(result, selectedPlugins, language);
|
|
1107
|
+
} else {
|
|
1108
|
+
console.error(`
|
|
1109
|
+
${t.installation.error}`);
|
|
1110
|
+
process.exit(1);
|
|
1111
|
+
}
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
spinner.fail(t.installation.error);
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
logger.error("Installation failed:", error);
|
|
1118
|
+
if (error instanceof Error) {
|
|
1119
|
+
console.error(`
|
|
1120
|
+
\u274C ${error.message}`);
|
|
1121
|
+
}
|
|
1122
|
+
process.exit(1);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
export {
|
|
1126
|
+
installReact
|
|
1127
|
+
};
|