@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.
@@ -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",
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
+ }
@@ -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
- };
@@ -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
- };