@configjs/cli 1.1.3 → 1.1.5
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/README.fr.md +1 -1
- package/README.md +40 -6
- package/dist/{check-7PYMMMZK.js → check-AJCBQBFD.js} +6 -5
- package/dist/chunk-3V72QFFY.js +383 -0
- package/dist/{install-7LTMBLVZ.js → chunk-CNAEKUBH.js} +310 -434
- package/dist/chunk-D37FHAGO.js +569 -0
- package/dist/chunk-HM2JWJOO.js +418 -0
- package/dist/chunk-MQV3WNMH.js +114 -0
- package/dist/{chunk-ZSDLWQSS.js → chunk-TVZWTKJU.js} +341 -216
- package/dist/{chunk-OJGTPK6N.js → chunk-V75HW2AM.js} +8347 -4788
- package/dist/cli.js +32 -7
- package/dist/{installed-Y76PWTXI.js → installed-WA6I2IFD.js} +4 -3
- package/dist/{list-NW6ENYK6.js → list-Y7I5NUWT.js} +3 -2
- package/dist/nextjs-command-AUVNEBHG.js +59 -0
- package/dist/nextjs-installer-RWBGWW23.js +80 -0
- package/dist/nextjs-setup-7LWMEC5I.js +100 -0
- package/dist/react-command-DTIKCCPO.js +59 -0
- package/dist/{remove-7HJKNAEX.js → remove-JBICRDXX.js} +4 -3
- package/dist/vite-installer-XIJFJ4CN.js +49 -0
- package/dist/vite-setup-3U22LJFG.js +51 -0
- package/dist/vue-command-R4XETTRT.js +73 -0
- package/dist/vue-installer-SFROF4N3.js +74 -0
- package/dist/vue-setup-X65P3CZX.js +91 -0
- package/package.json +5 -2
- package/dist/chunk-QRFLHLFE.js +0 -300
- package/dist/chunk-VJ254HJY.js +0 -214
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTranslations
|
|
3
|
+
} from "./chunk-3V72QFFY.js";
|
|
4
|
+
import "./chunk-QGM4M3NI.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/prompts/vue-setup.ts
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
async function promptVueSetup(language) {
|
|
9
|
+
const t = getTranslations(language);
|
|
10
|
+
const answers = await inquirer.prompt([
|
|
11
|
+
{
|
|
12
|
+
type: "confirm",
|
|
13
|
+
name: "shouldCreate",
|
|
14
|
+
message: t.vue.proposeSetup,
|
|
15
|
+
default: true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: "input",
|
|
19
|
+
name: "projectName",
|
|
20
|
+
message: t.vue.projectName,
|
|
21
|
+
default: t.vue.projectNamePlaceholder,
|
|
22
|
+
when: (answers2) => answers2.shouldCreate === true,
|
|
23
|
+
validate: (input) => {
|
|
24
|
+
if (!input || input.trim().length === 0) {
|
|
25
|
+
return t.vue.validation.empty;
|
|
26
|
+
}
|
|
27
|
+
if (!/^[a-z0-9-_]+$/i.test(input)) {
|
|
28
|
+
return t.vue.validation.invalid;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: "confirm",
|
|
35
|
+
name: "typescript",
|
|
36
|
+
message: t.vue.typescript,
|
|
37
|
+
default: true,
|
|
38
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: "confirm",
|
|
42
|
+
name: "router",
|
|
43
|
+
message: t.vue.router,
|
|
44
|
+
default: true,
|
|
45
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "confirm",
|
|
49
|
+
name: "pinia",
|
|
50
|
+
message: t.vue.pinia,
|
|
51
|
+
default: true,
|
|
52
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "confirm",
|
|
56
|
+
name: "vitest",
|
|
57
|
+
message: t.vue.vitest,
|
|
58
|
+
default: true,
|
|
59
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: "confirm",
|
|
63
|
+
name: "eslint",
|
|
64
|
+
message: t.vue.eslint,
|
|
65
|
+
default: true,
|
|
66
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "confirm",
|
|
70
|
+
name: "prettier",
|
|
71
|
+
message: t.vue.prettier,
|
|
72
|
+
default: true,
|
|
73
|
+
when: (answers2) => answers2.shouldCreate === true
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
if (!answers.shouldCreate) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
projectName: answers.projectName.trim(),
|
|
81
|
+
typescript: answers.typescript,
|
|
82
|
+
router: answers.router,
|
|
83
|
+
pinia: answers.pinia,
|
|
84
|
+
vitest: answers.vitest,
|
|
85
|
+
eslint: answers.eslint,
|
|
86
|
+
prettier: answers.prettier
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
promptVueSetup
|
|
91
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configjs/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Configure your frontend stack, instantly - Utilitaire CLI d'installation modulaire de bibliothèques frontend",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"cli",
|
|
8
8
|
"react",
|
|
9
|
+
"nextjs",
|
|
10
|
+
"vue",
|
|
9
11
|
"installer",
|
|
10
12
|
"configuration",
|
|
11
13
|
"setup",
|
|
@@ -77,6 +79,7 @@
|
|
|
77
79
|
"eslint-plugin-prettier": "^5.5.4",
|
|
78
80
|
"husky": "^9.1.7",
|
|
79
81
|
"lint-staged": "^16.2.7",
|
|
82
|
+
"memfs": "^4.51.1",
|
|
80
83
|
"prettier": "^3.7.4",
|
|
81
84
|
"tsup": "^8.5.1",
|
|
82
85
|
"typescript": "^5.9.3",
|
|
@@ -92,4 +95,4 @@
|
|
|
92
95
|
"prettier --write"
|
|
93
96
|
]
|
|
94
97
|
}
|
|
95
|
-
}
|
|
98
|
+
}
|
package/dist/chunk-QRFLHLFE.js
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
// src/utils/logger.ts
|
|
2
|
-
import pc from "picocolors";
|
|
3
|
-
var Logger = class {
|
|
4
|
-
level = 1 /* INFO */;
|
|
5
|
-
setLevel(level) {
|
|
6
|
-
this.level = level;
|
|
7
|
-
}
|
|
8
|
-
debug(message, ...args) {
|
|
9
|
-
if (this.level <= 0 /* DEBUG */) {
|
|
10
|
-
console.log(pc.gray(`[DEBUG] ${message}`), ...args);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
info(message, ...args) {
|
|
14
|
-
if (this.level <= 1 /* INFO */) {
|
|
15
|
-
console.log(pc.cyan(`\u2139 ${message}`), ...args);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
success(message, ...args) {
|
|
19
|
-
if (this.level <= 1 /* INFO */) {
|
|
20
|
-
console.log(pc.green(`\u2713 ${message}`), ...args);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
warn(message, ...args) {
|
|
24
|
-
if (this.level <= 2 /* WARN */) {
|
|
25
|
-
console.warn(pc.yellow(`\u26A0\uFE0F ${message}`), ...args);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
error(message, ...args) {
|
|
29
|
-
if (this.level <= 3 /* ERROR */) {
|
|
30
|
-
console.error(pc.red(`\u2716 ${message}`), ...args);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
header(message) {
|
|
34
|
-
if (this.level <= 1 /* INFO */) {
|
|
35
|
-
console.log();
|
|
36
|
-
console.log(pc.bold(pc.magenta(`\u25C6 ${message}`)));
|
|
37
|
-
console.log();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
section(title) {
|
|
41
|
-
if (this.level <= 1 /* INFO */) {
|
|
42
|
-
console.log();
|
|
43
|
-
console.log(pc.bold(pc.cyan(`\u25B8 ${title}`)));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
item(message, color = "gray") {
|
|
47
|
-
if (this.level <= 1 /* INFO */) {
|
|
48
|
-
const colorFn = pc[color];
|
|
49
|
-
console.log(colorFn(` \u2022 ${message}`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
dim(message) {
|
|
53
|
-
if (this.level <= 1 /* INFO */) {
|
|
54
|
-
console.log(pc.gray(` ${message}`));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
step(message) {
|
|
58
|
-
if (this.level <= 1 /* INFO */) {
|
|
59
|
-
console.log(pc.cyan(`
|
|
60
|
-
\u2192 ${message}`));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
box(title, content) {
|
|
64
|
-
if (this.level <= 1 /* INFO */) {
|
|
65
|
-
const maxLength = Math.max(
|
|
66
|
-
title.length,
|
|
67
|
-
...content.map((line) => line.length)
|
|
68
|
-
);
|
|
69
|
-
const border = "\u2500".repeat(maxLength + 4);
|
|
70
|
-
console.log(pc.cyan(`\u250C${border}\u2510`));
|
|
71
|
-
console.log(pc.cyan(`\u2502 ${title.padEnd(maxLength)} \u2502`));
|
|
72
|
-
console.log(pc.cyan(`\u251C${border}\u2524`));
|
|
73
|
-
content.forEach((line) => {
|
|
74
|
-
console.log(pc.cyan(`\u2502 ${line.padEnd(maxLength)} \u2502`));
|
|
75
|
-
});
|
|
76
|
-
console.log(pc.cyan(`\u2514${border}\u2518`));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
var logger = new Logger();
|
|
81
|
-
|
|
82
|
-
// src/utils/fs-helpers.ts
|
|
83
|
-
import fs from "fs-extra";
|
|
84
|
-
import { resolve, dirname, extname } from "path";
|
|
85
|
-
function normalizePath(path) {
|
|
86
|
-
return path.replace(/\\/g, "/");
|
|
87
|
-
}
|
|
88
|
-
async function readPackageJson(root) {
|
|
89
|
-
const packageJsonPath = resolve(root, "package.json");
|
|
90
|
-
if (!await fs.pathExists(packageJsonPath)) {
|
|
91
|
-
throw new Error(`package.json not found at ${packageJsonPath}`);
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const pkg = await fs.readJson(packageJsonPath);
|
|
95
|
-
logger.debug(`Read package.json from ${packageJsonPath}`);
|
|
96
|
-
return pkg;
|
|
97
|
-
} catch (error) {
|
|
98
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
-
throw new Error(
|
|
100
|
-
`Failed to read package.json: ${errorMessage}. File may be invalid JSON.`
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
async function writePackageJson(root, pkg) {
|
|
105
|
-
const packageJsonPath = resolve(root, "package.json");
|
|
106
|
-
try {
|
|
107
|
-
await fs.writeJson(packageJsonPath, pkg, {
|
|
108
|
-
spaces: 2,
|
|
109
|
-
EOL: "\n"
|
|
110
|
-
});
|
|
111
|
-
logger.debug(`Wrote package.json to ${packageJsonPath}`);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
114
|
-
throw new Error(`Failed to write package.json: ${errorMessage}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
async function readTsConfig(root) {
|
|
118
|
-
const possiblePaths = [
|
|
119
|
-
resolve(root, "tsconfig.json"),
|
|
120
|
-
resolve(root, "tsconfig.app.json"),
|
|
121
|
-
resolve(root, "tsconfig.node.json")
|
|
122
|
-
];
|
|
123
|
-
for (const tsconfigPath of possiblePaths) {
|
|
124
|
-
if (await fs.pathExists(tsconfigPath)) {
|
|
125
|
-
try {
|
|
126
|
-
const config = await fs.readJson(tsconfigPath);
|
|
127
|
-
logger.debug(`Read tsconfig.json from ${tsconfigPath}`);
|
|
128
|
-
return config;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
131
|
-
logger.warn(
|
|
132
|
-
`Failed to parse tsconfig.json at ${tsconfigPath}: ${errorMessage}`
|
|
133
|
-
);
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
logger.debug("No tsconfig.json found");
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
async function checkPathExists(path) {
|
|
142
|
-
const fullPath = resolve(path);
|
|
143
|
-
return fs.pathExists(fullPath);
|
|
144
|
-
}
|
|
145
|
-
async function ensureDirectory(path) {
|
|
146
|
-
const fullPath = resolve(path);
|
|
147
|
-
try {
|
|
148
|
-
await fs.ensureDir(fullPath);
|
|
149
|
-
logger.debug(`Ensured directory exists: ${fullPath}`);
|
|
150
|
-
} catch (error) {
|
|
151
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
152
|
-
throw new Error(`Failed to create directory ${fullPath}: ${errorMessage}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async function readFileContent(filePath, encoding = "utf-8") {
|
|
156
|
-
const fullPath = resolve(filePath);
|
|
157
|
-
if (!await fs.pathExists(fullPath)) {
|
|
158
|
-
throw new Error(`File not found: ${fullPath}`);
|
|
159
|
-
}
|
|
160
|
-
try {
|
|
161
|
-
const content = await fs.readFile(fullPath, encoding);
|
|
162
|
-
logger.debug(`Read file: ${fullPath}`);
|
|
163
|
-
return content;
|
|
164
|
-
} catch (error) {
|
|
165
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
166
|
-
throw new Error(`Failed to read file ${fullPath}: ${errorMessage}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
async function writeFileContent(filePath, content, encoding = "utf-8") {
|
|
170
|
-
const fullPath = resolve(filePath);
|
|
171
|
-
const parentDir = dirname(fullPath);
|
|
172
|
-
await ensureDirectory(parentDir);
|
|
173
|
-
try {
|
|
174
|
-
await fs.writeFile(fullPath, content, encoding);
|
|
175
|
-
logger.debug(`Wrote file: ${fullPath}`);
|
|
176
|
-
} catch (error) {
|
|
177
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
178
|
-
throw new Error(`Failed to write file ${fullPath}: ${errorMessage}`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// src/utils/package-manager.ts
|
|
183
|
-
import { execa } from "execa";
|
|
184
|
-
import fs2 from "fs-extra";
|
|
185
|
-
import { resolve as resolve2, join } from "path";
|
|
186
|
-
async function detectPackageManager(projectRoot) {
|
|
187
|
-
const root = resolve2(projectRoot);
|
|
188
|
-
const lockfiles = [
|
|
189
|
-
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
190
|
-
{ file: "yarn.lock", manager: "yarn" },
|
|
191
|
-
{ file: "package-lock.json", manager: "npm" },
|
|
192
|
-
{ file: "bun.lockb", manager: "bun" }
|
|
193
|
-
];
|
|
194
|
-
for (const { file, manager } of lockfiles) {
|
|
195
|
-
const lockfilePath = join(root, file);
|
|
196
|
-
if (await fs2.pathExists(lockfilePath)) {
|
|
197
|
-
logger.debug(`Detected package manager: ${manager} (found ${file})`);
|
|
198
|
-
return manager;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
logger.debug("No lockfile found, defaulting to npm");
|
|
202
|
-
return "npm";
|
|
203
|
-
}
|
|
204
|
-
async function installPackages(packages, options) {
|
|
205
|
-
if (packages.length === 0) {
|
|
206
|
-
logger.warn("No packages to install");
|
|
207
|
-
return { success: true, packages: [] };
|
|
208
|
-
}
|
|
209
|
-
const {
|
|
210
|
-
packageManager,
|
|
211
|
-
projectRoot,
|
|
212
|
-
dev = false,
|
|
213
|
-
exact = false,
|
|
214
|
-
silent = false
|
|
215
|
-
} = options;
|
|
216
|
-
logger.info(
|
|
217
|
-
`Installing ${packages.length} package(s) with ${packageManager}...`
|
|
218
|
-
);
|
|
219
|
-
try {
|
|
220
|
-
const command = getInstallCommand(packageManager, packages, { dev, exact });
|
|
221
|
-
const cwd = resolve2(projectRoot);
|
|
222
|
-
logger.debug(`Executing: ${command.join(" ")} in ${cwd}`);
|
|
223
|
-
const [cmd, ...args] = command;
|
|
224
|
-
if (!cmd) {
|
|
225
|
-
throw new Error("Command is empty");
|
|
226
|
-
}
|
|
227
|
-
const result = await execa(cmd, args, {
|
|
228
|
-
cwd,
|
|
229
|
-
stdio: silent ? "pipe" : "inherit",
|
|
230
|
-
env: {
|
|
231
|
-
...process.env,
|
|
232
|
-
// Désactiver les prompts interactifs
|
|
233
|
-
npm_config_yes: "true",
|
|
234
|
-
YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
if (result.exitCode !== 0) {
|
|
238
|
-
throw new Error(`Installation failed with exit code ${result.exitCode}`);
|
|
239
|
-
}
|
|
240
|
-
logger.success(`Successfully installed ${packages.length} package(s)`);
|
|
241
|
-
return {
|
|
242
|
-
success: true,
|
|
243
|
-
packages
|
|
244
|
-
};
|
|
245
|
-
} catch (error) {
|
|
246
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
247
|
-
logger.error(`Failed to install packages: ${errorMessage}`);
|
|
248
|
-
return {
|
|
249
|
-
success: false,
|
|
250
|
-
packages,
|
|
251
|
-
error: errorMessage
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function getInstallCommand(packageManager, packages, options) {
|
|
256
|
-
const { dev, exact } = options;
|
|
257
|
-
switch (packageManager) {
|
|
258
|
-
case "pnpm":
|
|
259
|
-
return [
|
|
260
|
-
"pnpm",
|
|
261
|
-
"add",
|
|
262
|
-
...dev ? ["-D"] : [],
|
|
263
|
-
...exact ? ["--save-exact"] : [],
|
|
264
|
-
...packages
|
|
265
|
-
];
|
|
266
|
-
case "yarn":
|
|
267
|
-
return [
|
|
268
|
-
"yarn",
|
|
269
|
-
"add",
|
|
270
|
-
...dev ? ["--dev"] : [],
|
|
271
|
-
...exact ? ["--exact"] : [],
|
|
272
|
-
...packages
|
|
273
|
-
];
|
|
274
|
-
case "bun":
|
|
275
|
-
return ["bun", "add", ...dev ? ["--dev"] : [], ...packages];
|
|
276
|
-
case "npm":
|
|
277
|
-
default:
|
|
278
|
-
return [
|
|
279
|
-
"npm",
|
|
280
|
-
"install",
|
|
281
|
-
...dev ? ["--save-dev"] : [],
|
|
282
|
-
...exact ? ["--save-exact"] : [],
|
|
283
|
-
...packages
|
|
284
|
-
];
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export {
|
|
289
|
-
logger,
|
|
290
|
-
normalizePath,
|
|
291
|
-
readPackageJson,
|
|
292
|
-
writePackageJson,
|
|
293
|
-
readTsConfig,
|
|
294
|
-
checkPathExists,
|
|
295
|
-
ensureDirectory,
|
|
296
|
-
readFileContent,
|
|
297
|
-
writeFileContent,
|
|
298
|
-
detectPackageManager,
|
|
299
|
-
installPackages
|
|
300
|
-
};
|
package/dist/chunk-VJ254HJY.js
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
logger
|
|
3
|
-
} from "./chunk-QRFLHLFE.js";
|
|
4
|
-
|
|
5
|
-
// src/core/validator.ts
|
|
6
|
-
var CompatibilityValidator = class {
|
|
7
|
-
/**
|
|
8
|
-
* @param rules - Règles de compatibilité à appliquer
|
|
9
|
-
*/
|
|
10
|
-
constructor(rules) {
|
|
11
|
-
this.rules = rules;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Valide la compatibilité d'un ensemble de plugins
|
|
15
|
-
*
|
|
16
|
-
* @param plugins - Liste des plugins à valider
|
|
17
|
-
* @returns Résultat de la validation avec erreurs, warnings et suggestions
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const result = validator.validate([plugin1, plugin2, plugin3])
|
|
22
|
-
* if (!result.valid) {
|
|
23
|
-
* // Gérer les erreurs
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
validate(plugins) {
|
|
28
|
-
logger.debug(`Validating ${plugins.length} plugin(s)`);
|
|
29
|
-
const pluginNames = new Set(plugins.map((p) => p.name));
|
|
30
|
-
const allConflicts = this.checkConflicts(plugins, pluginNames);
|
|
31
|
-
const conflictErrors = [];
|
|
32
|
-
const conflictWarnings = [];
|
|
33
|
-
for (const conflict of allConflicts) {
|
|
34
|
-
const rule = this.rules.find(
|
|
35
|
-
(r) => r.type === "CONFLICT" && r.plugins?.every((p) => conflict.plugins?.includes(p))
|
|
36
|
-
);
|
|
37
|
-
if (rule?.severity === "error") {
|
|
38
|
-
conflictErrors.push(conflict);
|
|
39
|
-
} else {
|
|
40
|
-
conflictWarnings.push(conflict);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const errors = [
|
|
44
|
-
...this.checkExclusivity(plugins, pluginNames),
|
|
45
|
-
...conflictErrors,
|
|
46
|
-
...this.checkDependencies(plugins, pluginNames)
|
|
47
|
-
];
|
|
48
|
-
const warnings = conflictWarnings;
|
|
49
|
-
const suggestions = this.checkRecommendations(plugins, pluginNames);
|
|
50
|
-
const valid = errors.length === 0;
|
|
51
|
-
logger.debug(`Validation result: ${valid ? "valid" : "invalid"}`, {
|
|
52
|
-
errors: errors.length,
|
|
53
|
-
warnings: warnings.length,
|
|
54
|
-
suggestions: suggestions.length
|
|
55
|
-
});
|
|
56
|
-
return {
|
|
57
|
-
valid,
|
|
58
|
-
errors,
|
|
59
|
-
warnings,
|
|
60
|
-
suggestions
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Vérifie les règles d'exclusivité (EXCLUSIVE)
|
|
65
|
-
*
|
|
66
|
-
* @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
|
|
67
|
-
* @param pluginNames - Set des noms de plugins pour lookup rapide
|
|
68
|
-
* @returns Liste des erreurs d'exclusivité
|
|
69
|
-
*
|
|
70
|
-
* @internal
|
|
71
|
-
*/
|
|
72
|
-
checkExclusivity(_plugins, pluginNames) {
|
|
73
|
-
const errors = [];
|
|
74
|
-
for (const rule of this.rules) {
|
|
75
|
-
if (rule.type !== "EXCLUSIVE" || !rule.plugins) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
const selectedExclusivePlugins = rule.plugins.filter(
|
|
79
|
-
(pluginName) => pluginNames.has(pluginName)
|
|
80
|
-
);
|
|
81
|
-
if (selectedExclusivePlugins.length > 1) {
|
|
82
|
-
errors.push({
|
|
83
|
-
type: "EXCLUSIVE",
|
|
84
|
-
plugins: selectedExclusivePlugins,
|
|
85
|
-
message: rule.reason,
|
|
86
|
-
canOverride: rule.allowOverride ?? false
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return errors;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Vérifie les conflits entre plugins (CONFLICT)
|
|
94
|
-
*
|
|
95
|
-
* @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
|
|
96
|
-
* @param pluginNames - Set des noms de plugins pour lookup rapide
|
|
97
|
-
* @returns Liste des warnings/erreurs de conflit
|
|
98
|
-
*
|
|
99
|
-
* @internal
|
|
100
|
-
*/
|
|
101
|
-
checkConflicts(_plugins, pluginNames) {
|
|
102
|
-
const conflicts = [];
|
|
103
|
-
for (const rule of this.rules) {
|
|
104
|
-
if (rule.type !== "CONFLICT" || !rule.plugins) {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
const conflictingPlugins = rule.plugins.filter(
|
|
108
|
-
(pluginName) => pluginNames.has(pluginName)
|
|
109
|
-
);
|
|
110
|
-
if (conflictingPlugins.length > 1) {
|
|
111
|
-
conflicts.push({
|
|
112
|
-
type: "CONFLICT",
|
|
113
|
-
plugins: conflictingPlugins,
|
|
114
|
-
message: rule.reason,
|
|
115
|
-
canOverride: rule.allowOverride ?? true
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return conflicts;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Vérifie les dépendances requises (REQUIRES)
|
|
123
|
-
*
|
|
124
|
-
* @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
|
|
125
|
-
* @param pluginNames - Set des noms de plugins pour lookup rapide
|
|
126
|
-
* @returns Liste des erreurs de dépendances manquantes
|
|
127
|
-
*
|
|
128
|
-
* @internal
|
|
129
|
-
*/
|
|
130
|
-
checkDependencies(_plugins, pluginNames) {
|
|
131
|
-
const errors = [];
|
|
132
|
-
for (const rule of this.rules) {
|
|
133
|
-
if (rule.type !== "REQUIRES" || !rule.plugin || !rule.requires) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (!pluginNames.has(rule.plugin)) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const missingDependencies = rule.requires.filter(
|
|
140
|
-
(dep) => !pluginNames.has(dep)
|
|
141
|
-
);
|
|
142
|
-
if (missingDependencies.length > 0) {
|
|
143
|
-
errors.push({
|
|
144
|
-
type: "REQUIRES",
|
|
145
|
-
plugin: rule.plugin,
|
|
146
|
-
required: missingDependencies.join(", "),
|
|
147
|
-
message: `${rule.plugin} requires: ${missingDependencies.join(", ")}. ${rule.reason}`,
|
|
148
|
-
canOverride: rule.allowOverride ?? false
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return errors;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Vérifie les recommandations (RECOMMENDS)
|
|
156
|
-
*
|
|
157
|
-
* @param _plugins - Liste des plugins (non utilisée, conservée pour cohérence)
|
|
158
|
-
* @param pluginNames - Set des noms de plugins pour lookup rapide
|
|
159
|
-
* @returns Liste des suggestions de plugins recommandés
|
|
160
|
-
*
|
|
161
|
-
* @internal
|
|
162
|
-
*/
|
|
163
|
-
checkRecommendations(_plugins, pluginNames) {
|
|
164
|
-
const suggestions = [];
|
|
165
|
-
for (const rule of this.rules) {
|
|
166
|
-
if (rule.type !== "RECOMMENDS" || !rule.plugin || !rule.recommends) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (!pluginNames.has(rule.plugin)) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
const missingRecommendations = rule.recommends.filter(
|
|
173
|
-
(rec) => !pluginNames.has(rec)
|
|
174
|
-
);
|
|
175
|
-
if (missingRecommendations.length > 0) {
|
|
176
|
-
suggestions.push(
|
|
177
|
-
`${rule.plugin} recommends: ${missingRecommendations.join(", ")}. ${rule.reason}`
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return suggestions;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
var compatibilityRules = [
|
|
185
|
-
// Exclusivités - State Management
|
|
186
|
-
{
|
|
187
|
-
type: "EXCLUSIVE",
|
|
188
|
-
plugins: ["@reduxjs/toolkit", "zustand", "jotai"],
|
|
189
|
-
reason: "Une seule solution de state management est recommand\xE9e",
|
|
190
|
-
severity: "error",
|
|
191
|
-
allowOverride: false
|
|
192
|
-
},
|
|
193
|
-
// Conflits - CSS Frameworks
|
|
194
|
-
{
|
|
195
|
-
type: "CONFLICT",
|
|
196
|
-
plugins: ["tailwindcss", "bootstrap"],
|
|
197
|
-
reason: "Approches CSS potentiellement conflictuelles",
|
|
198
|
-
severity: "warning",
|
|
199
|
-
allowOverride: true
|
|
200
|
-
},
|
|
201
|
-
// Recommandations - React Router
|
|
202
|
-
{
|
|
203
|
-
type: "RECOMMENDS",
|
|
204
|
-
plugin: "react-router-dom",
|
|
205
|
-
recommends: ["@types/react-router-dom"],
|
|
206
|
-
reason: "Types TypeScript recommand\xE9s pour une meilleure exp\xE9rience de d\xE9veloppement",
|
|
207
|
-
severity: "info"
|
|
208
|
-
}
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
export {
|
|
212
|
-
CompatibilityValidator,
|
|
213
|
-
compatibilityRules
|
|
214
|
-
};
|