@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
|
@@ -1,15 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectPackageManager
|
|
3
|
+
} from "./chunk-MQV3WNMH.js";
|
|
1
4
|
import {
|
|
2
5
|
checkPathExists,
|
|
3
|
-
|
|
6
|
+
createDefaultFsAdapter,
|
|
4
7
|
logger,
|
|
5
8
|
readFileContent,
|
|
6
9
|
readPackageJson,
|
|
7
10
|
readTsConfig,
|
|
8
11
|
writeFileContent
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HM2JWJOO.js";
|
|
13
|
+
|
|
14
|
+
// src/core/plugin-tracker.ts
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
var CONFIG_FILE_NAME = ".configjsrc";
|
|
17
|
+
var CONFIG_VERSION = "1.0.0";
|
|
18
|
+
var PluginTracker = class {
|
|
19
|
+
configPath;
|
|
20
|
+
config = null;
|
|
21
|
+
fsAdapter;
|
|
22
|
+
constructor(projectRoot, fsAdapter) {
|
|
23
|
+
this.configPath = join(projectRoot, CONFIG_FILE_NAME);
|
|
24
|
+
this.fsAdapter = fsAdapter;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Charge la configuration depuis le fichier
|
|
28
|
+
*/
|
|
29
|
+
async load() {
|
|
30
|
+
try {
|
|
31
|
+
const exists = await checkPathExists(this.configPath, this.fsAdapter);
|
|
32
|
+
if (!exists) {
|
|
33
|
+
this.config = {
|
|
34
|
+
version: CONFIG_VERSION,
|
|
35
|
+
installedPlugins: [],
|
|
36
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
37
|
+
};
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const content = await readFileContent(
|
|
41
|
+
this.configPath,
|
|
42
|
+
"utf-8",
|
|
43
|
+
this.fsAdapter
|
|
44
|
+
);
|
|
45
|
+
this.config = JSON.parse(content);
|
|
46
|
+
if (!this.config.version) {
|
|
47
|
+
logger.warn("Old config format detected, migrating...");
|
|
48
|
+
this.config.version = CONFIG_VERSION;
|
|
49
|
+
await this.save();
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error("Failed to load plugin configuration", error);
|
|
53
|
+
this.config = {
|
|
54
|
+
version: CONFIG_VERSION,
|
|
55
|
+
installedPlugins: [],
|
|
56
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sauvegarde la configuration dans le fichier
|
|
62
|
+
*/
|
|
63
|
+
async save() {
|
|
64
|
+
if (!this.config) {
|
|
65
|
+
throw new Error("Configuration not loaded");
|
|
66
|
+
}
|
|
67
|
+
this.config.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
68
|
+
try {
|
|
69
|
+
const content = JSON.stringify(this.config, null, 2);
|
|
70
|
+
await writeFileContent(this.configPath, content, "utf-8", this.fsAdapter);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error("Failed to save plugin configuration", error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Vérifie si un plugin est déjà installé
|
|
78
|
+
*/
|
|
79
|
+
isInstalled(pluginName) {
|
|
80
|
+
if (!this.config) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return this.config.installedPlugins.some((p) => p.name === pluginName);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Récupère les informations d'un plugin installé
|
|
87
|
+
*/
|
|
88
|
+
getPlugin(pluginName) {
|
|
89
|
+
if (!this.config) {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
return this.config.installedPlugins.find((p) => p.name === pluginName);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Récupère tous les plugins installés
|
|
96
|
+
*/
|
|
97
|
+
getInstalledPlugins() {
|
|
98
|
+
return this.config?.installedPlugins || [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Récupère tous les plugins installés d'une catégorie donnée
|
|
102
|
+
*/
|
|
103
|
+
getPluginsByCategory(category) {
|
|
104
|
+
if (!this.config) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return this.config.installedPlugins.filter((p) => p.category === category);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Ajoute un plugin à la liste des installés
|
|
111
|
+
*/
|
|
112
|
+
async addPlugin(plugin) {
|
|
113
|
+
if (!this.config) {
|
|
114
|
+
throw new Error("Configuration not loaded");
|
|
115
|
+
}
|
|
116
|
+
if (this.isInstalled(plugin.name)) {
|
|
117
|
+
logger.warn(`Plugin ${plugin.name} is already marked as installed`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const installedPlugin = {
|
|
121
|
+
...plugin,
|
|
122
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
123
|
+
};
|
|
124
|
+
this.config.installedPlugins.push(installedPlugin);
|
|
125
|
+
await this.save();
|
|
126
|
+
logger.debug(`Marked plugin ${plugin.name} as installed`);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Supprime un plugin de la liste des installés
|
|
130
|
+
*/
|
|
131
|
+
async removePlugin(pluginName) {
|
|
132
|
+
if (!this.config) {
|
|
133
|
+
throw new Error("Configuration not loaded");
|
|
134
|
+
}
|
|
135
|
+
const index = this.config.installedPlugins.findIndex(
|
|
136
|
+
(p) => p.name === pluginName
|
|
137
|
+
);
|
|
138
|
+
if (index === -1) {
|
|
139
|
+
logger.warn(`Plugin ${pluginName} not found in installed plugins`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.config.installedPlugins.splice(index, 1);
|
|
143
|
+
await this.save();
|
|
144
|
+
logger.debug(`Removed plugin ${pluginName} from installed plugins`);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Met à jour les informations d'un plugin installé
|
|
148
|
+
*/
|
|
149
|
+
async updatePlugin(pluginName, updates) {
|
|
150
|
+
if (!this.config) {
|
|
151
|
+
throw new Error("Configuration not loaded");
|
|
152
|
+
}
|
|
153
|
+
const plugin = this.config.installedPlugins.find(
|
|
154
|
+
(p) => p.name === pluginName
|
|
155
|
+
);
|
|
156
|
+
if (!plugin) {
|
|
157
|
+
throw new Error(`Plugin ${pluginName} not found`);
|
|
158
|
+
}
|
|
159
|
+
Object.assign(plugin, updates);
|
|
160
|
+
await this.save();
|
|
161
|
+
logger.debug(`Updated plugin ${pluginName}`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Efface toute la configuration
|
|
165
|
+
*/
|
|
166
|
+
async reset() {
|
|
167
|
+
this.config = {
|
|
168
|
+
version: CONFIG_VERSION,
|
|
169
|
+
installedPlugins: [],
|
|
170
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
+
};
|
|
172
|
+
await this.save();
|
|
173
|
+
logger.info("Reset plugin configuration");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Vérifie les conflits potentiels avec un nouveau plugin
|
|
177
|
+
*
|
|
178
|
+
* @param category - Catégorie du plugin à installer
|
|
179
|
+
* @param exclusiveCategories - Catégories qui ne peuvent avoir qu'un seul plugin
|
|
180
|
+
* @returns Liste des plugins en conflit
|
|
181
|
+
*/
|
|
182
|
+
checkCategoryConflicts(category, exclusiveCategories = ["routing", "state"]) {
|
|
183
|
+
if (!exclusiveCategories.includes(category)) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
return this.getPluginsByCategory(category);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Exporte la configuration pour affichage ou debug
|
|
190
|
+
*/
|
|
191
|
+
export() {
|
|
192
|
+
return this.config;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
10
195
|
|
|
11
196
|
// src/core/detector.ts
|
|
12
|
-
import { resolve, join } from "path";
|
|
197
|
+
import { resolve, join as join2 } from "path";
|
|
13
198
|
import { platform, version } from "process";
|
|
14
199
|
var detectionCache = /* @__PURE__ */ new Map();
|
|
15
200
|
var DetectionError = class extends Error {
|
|
@@ -24,6 +209,12 @@ function detectFramework(pkg) {
|
|
|
24
209
|
...pkg["dependencies"] || {},
|
|
25
210
|
...pkg["devDependencies"] || {}
|
|
26
211
|
};
|
|
212
|
+
if (deps["next"]) {
|
|
213
|
+
return {
|
|
214
|
+
framework: "nextjs",
|
|
215
|
+
version: deps["next"].replace(/[\^~]/, "")
|
|
216
|
+
};
|
|
217
|
+
}
|
|
27
218
|
if (deps["react"]) {
|
|
28
219
|
return {
|
|
29
220
|
framework: "react",
|
|
@@ -31,9 +222,17 @@ function detectFramework(pkg) {
|
|
|
31
222
|
};
|
|
32
223
|
}
|
|
33
224
|
if (deps["vue"]) {
|
|
225
|
+
const version2 = deps["vue"].replace(/[\^~]/, "");
|
|
226
|
+
const majorVersion = parseInt(version2.split(".")[0] || "0", 10);
|
|
227
|
+
if (majorVersion < 3) {
|
|
228
|
+
throw new DetectionError(
|
|
229
|
+
"Vue 2 is not supported. Please use Vue 3 (Vue 2 is not compatible with Vite).",
|
|
230
|
+
{ vueVersion: version2 }
|
|
231
|
+
);
|
|
232
|
+
}
|
|
34
233
|
return {
|
|
35
234
|
framework: "vue",
|
|
36
|
-
version:
|
|
235
|
+
version: version2
|
|
37
236
|
};
|
|
38
237
|
}
|
|
39
238
|
if (deps["svelte"]) {
|
|
@@ -43,17 +242,32 @@ function detectFramework(pkg) {
|
|
|
43
242
|
};
|
|
44
243
|
}
|
|
45
244
|
throw new DetectionError(
|
|
46
|
-
"No supported framework detected. Supported frameworks: React, Vue, Svelte",
|
|
245
|
+
"No supported framework detected. Supported frameworks: Next.js, React, Vue, Svelte",
|
|
47
246
|
{ dependencies: Object.keys(deps) }
|
|
48
247
|
);
|
|
49
248
|
}
|
|
50
|
-
async function detectBundler(projectRoot, pkg) {
|
|
249
|
+
async function detectBundler(projectRoot, pkg, fsAdapter) {
|
|
51
250
|
const deps = {
|
|
52
251
|
...pkg["dependencies"] || {},
|
|
53
252
|
...pkg["devDependencies"] || {}
|
|
54
253
|
};
|
|
254
|
+
if (deps["next"]) {
|
|
255
|
+
const nextConfigExists = await checkPathExists(join2(projectRoot, "next.config.js"), fsAdapter) || await checkPathExists(join2(projectRoot, "next.config.ts"), fsAdapter) || await checkPathExists(
|
|
256
|
+
join2(projectRoot, "next.config.mjs"),
|
|
257
|
+
fsAdapter
|
|
258
|
+
) || await checkPathExists(join2(projectRoot, "next.config.cjs"), fsAdapter);
|
|
259
|
+
if (nextConfigExists) {
|
|
260
|
+
return {
|
|
261
|
+
bundler: "nextjs",
|
|
262
|
+
version: deps["next"].replace(/[\^~]/, "")
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
55
266
|
if (deps["vite"]) {
|
|
56
|
-
const viteConfigExists = await checkPathExists(
|
|
267
|
+
const viteConfigExists = await checkPathExists(join2(projectRoot, "vite.config.js"), fsAdapter) || await checkPathExists(join2(projectRoot, "vite.config.ts"), fsAdapter) || await checkPathExists(
|
|
268
|
+
join2(projectRoot, "vite.config.mjs"),
|
|
269
|
+
fsAdapter
|
|
270
|
+
) || await checkPathExists(join2(projectRoot, "vite.config.cjs"), fsAdapter);
|
|
57
271
|
if (viteConfigExists) {
|
|
58
272
|
return {
|
|
59
273
|
bundler: "vite",
|
|
@@ -61,6 +275,19 @@ async function detectBundler(projectRoot, pkg) {
|
|
|
61
275
|
};
|
|
62
276
|
}
|
|
63
277
|
}
|
|
278
|
+
if (deps["vue"] && deps["@vue/cli-service"]) {
|
|
279
|
+
const vueConfigExists = await checkPathExists(
|
|
280
|
+
join2(projectRoot, "vue.config.js"),
|
|
281
|
+
fsAdapter
|
|
282
|
+
);
|
|
283
|
+
if (vueConfigExists) {
|
|
284
|
+
return {
|
|
285
|
+
bundler: "webpack",
|
|
286
|
+
// Vue CLI utilise Webpack
|
|
287
|
+
version: deps["@vue/cli-service"].replace(/[\^~]/, "")
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
64
291
|
if (deps["react-scripts"]) {
|
|
65
292
|
return {
|
|
66
293
|
bundler: "cra",
|
|
@@ -68,7 +295,10 @@ async function detectBundler(projectRoot, pkg) {
|
|
|
68
295
|
};
|
|
69
296
|
}
|
|
70
297
|
if (deps["webpack"]) {
|
|
71
|
-
const webpackConfigExists = await checkPathExists(
|
|
298
|
+
const webpackConfigExists = await checkPathExists(
|
|
299
|
+
join2(projectRoot, "webpack.config.js"),
|
|
300
|
+
fsAdapter
|
|
301
|
+
) || await checkPathExists(join2(projectRoot, "webpack.config.ts"), fsAdapter);
|
|
72
302
|
if (webpackConfigExists) {
|
|
73
303
|
return {
|
|
74
304
|
bundler: "webpack",
|
|
@@ -87,16 +317,16 @@ async function detectBundler(projectRoot, pkg) {
|
|
|
87
317
|
version: null
|
|
88
318
|
};
|
|
89
319
|
}
|
|
90
|
-
async function detectTypeScript(projectRoot) {
|
|
91
|
-
const tsConfig = await readTsConfig(projectRoot);
|
|
320
|
+
async function detectTypeScript(projectRoot, fsAdapter) {
|
|
321
|
+
const tsConfig = await readTsConfig(projectRoot, fsAdapter);
|
|
92
322
|
if (tsConfig) {
|
|
93
323
|
const possiblePaths = [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
324
|
+
join2(projectRoot, "tsconfig.json"),
|
|
325
|
+
join2(projectRoot, "tsconfig.app.json"),
|
|
326
|
+
join2(projectRoot, "tsconfig.node.json")
|
|
97
327
|
];
|
|
98
328
|
for (const path of possiblePaths) {
|
|
99
|
-
if (await checkPathExists(path)) {
|
|
329
|
+
if (await checkPathExists(path, fsAdapter)) {
|
|
100
330
|
return {
|
|
101
331
|
typescript: true,
|
|
102
332
|
tsconfigPath: path
|
|
@@ -105,47 +335,108 @@ async function detectTypeScript(projectRoot) {
|
|
|
105
335
|
}
|
|
106
336
|
return {
|
|
107
337
|
typescript: true,
|
|
108
|
-
tsconfigPath:
|
|
338
|
+
tsconfigPath: join2(projectRoot, "tsconfig.json")
|
|
109
339
|
};
|
|
110
340
|
}
|
|
111
341
|
return {
|
|
112
342
|
typescript: false
|
|
113
343
|
};
|
|
114
344
|
}
|
|
115
|
-
async function detectSrcDir(projectRoot) {
|
|
345
|
+
async function detectSrcDir(projectRoot, fsAdapter) {
|
|
116
346
|
const possibleDirs = ["src", "app", "source", "lib"];
|
|
117
347
|
for (const dir of possibleDirs) {
|
|
118
|
-
const dirPath =
|
|
119
|
-
if (await checkPathExists(dirPath)) {
|
|
348
|
+
const dirPath = join2(projectRoot, dir);
|
|
349
|
+
if (await checkPathExists(dirPath, fsAdapter)) {
|
|
120
350
|
return dir;
|
|
121
351
|
}
|
|
122
352
|
}
|
|
123
353
|
return "src";
|
|
124
354
|
}
|
|
125
|
-
async function detectPublicDir(projectRoot) {
|
|
355
|
+
async function detectPublicDir(projectRoot, fsAdapter) {
|
|
126
356
|
const possibleDirs = ["public", "static", "assets"];
|
|
127
357
|
for (const dir of possibleDirs) {
|
|
128
|
-
const dirPath =
|
|
129
|
-
if (await checkPathExists(dirPath)) {
|
|
358
|
+
const dirPath = join2(projectRoot, dir);
|
|
359
|
+
if (await checkPathExists(dirPath, fsAdapter)) {
|
|
130
360
|
return dir;
|
|
131
361
|
}
|
|
132
362
|
}
|
|
133
363
|
return "public";
|
|
134
364
|
}
|
|
135
|
-
async function
|
|
136
|
-
const
|
|
137
|
-
const
|
|
365
|
+
async function detectNextjsRouter(projectRoot, srcDir, fsAdapter) {
|
|
366
|
+
const appDirInSrc = join2(projectRoot, srcDir, "app");
|
|
367
|
+
const pagesDirInSrc = join2(projectRoot, srcDir, "pages");
|
|
368
|
+
const appDirAtRoot = join2(projectRoot, "app");
|
|
369
|
+
const pagesDirAtRoot = join2(projectRoot, "pages");
|
|
370
|
+
const appDirExists = await checkPathExists(appDirInSrc, fsAdapter) || await checkPathExists(appDirAtRoot, fsAdapter);
|
|
371
|
+
const pagesDirExists = await checkPathExists(pagesDirInSrc, fsAdapter) || await checkPathExists(pagesDirAtRoot, fsAdapter);
|
|
372
|
+
if (appDirExists) {
|
|
373
|
+
return "app";
|
|
374
|
+
}
|
|
375
|
+
if (pagesDirExists) {
|
|
376
|
+
return "pages";
|
|
377
|
+
}
|
|
378
|
+
return void 0;
|
|
379
|
+
}
|
|
380
|
+
async function detectVueApi(projectRoot, srcDir, fsAdapter) {
|
|
381
|
+
const adapter = fsAdapter || createDefaultFsAdapter();
|
|
382
|
+
const srcPath = join2(projectRoot, srcDir);
|
|
383
|
+
if (!await checkPathExists(srcPath, adapter)) {
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
const files = [];
|
|
388
|
+
async function findVueFiles(dir) {
|
|
389
|
+
const entries = await adapter.readdir(dir);
|
|
390
|
+
for (const entryName of entries) {
|
|
391
|
+
const fullPath = join2(dir, entryName);
|
|
392
|
+
const stat = await adapter.stat(fullPath);
|
|
393
|
+
if (stat.isDirectory()) {
|
|
394
|
+
await findVueFiles(fullPath);
|
|
395
|
+
} else if (entryName.endsWith(".vue")) {
|
|
396
|
+
files.push(fullPath);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
await findVueFiles(srcPath);
|
|
401
|
+
let hasCompositionApi = false;
|
|
402
|
+
let hasOptionsApi = false;
|
|
403
|
+
for (const file of files.slice(0, 10)) {
|
|
404
|
+
try {
|
|
405
|
+
const content = await adapter.readFile(file, "utf-8");
|
|
406
|
+
if (content.includes("<script setup>") || content.includes('<script setup lang="ts">') || content.includes("export default defineComponent") || content.includes("setup()")) {
|
|
407
|
+
hasCompositionApi = true;
|
|
408
|
+
}
|
|
409
|
+
if (content.includes("export default {") || content.includes("export default{")) {
|
|
410
|
+
hasOptionsApi = true;
|
|
411
|
+
}
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (hasCompositionApi) {
|
|
416
|
+
return "composition";
|
|
417
|
+
}
|
|
418
|
+
if (hasOptionsApi) {
|
|
419
|
+
return "options";
|
|
420
|
+
}
|
|
421
|
+
return void 0;
|
|
422
|
+
} catch {
|
|
423
|
+
return void 0;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async function detectGit(projectRoot, fsAdapter) {
|
|
427
|
+
const gitDir = join2(projectRoot, ".git");
|
|
428
|
+
const hasGit = await checkPathExists(gitDir, fsAdapter);
|
|
138
429
|
if (!hasGit) {
|
|
139
430
|
return { hasGit: false };
|
|
140
431
|
}
|
|
141
|
-
const hooksPath =
|
|
142
|
-
const hasHooks = await checkPathExists(hooksPath);
|
|
432
|
+
const hooksPath = join2(gitDir, "hooks");
|
|
433
|
+
const hasHooks = await checkPathExists(hooksPath, fsAdapter);
|
|
143
434
|
return {
|
|
144
435
|
hasGit: true,
|
|
145
436
|
gitHooksPath: hasHooks ? hooksPath : void 0
|
|
146
437
|
};
|
|
147
438
|
}
|
|
148
|
-
async function detectLockfile(projectRoot, packageManager) {
|
|
439
|
+
async function detectLockfile(projectRoot, packageManager, fsAdapter) {
|
|
149
440
|
const lockfiles = {
|
|
150
441
|
npm: "package-lock.json",
|
|
151
442
|
yarn: "yarn.lock",
|
|
@@ -153,13 +444,13 @@ async function detectLockfile(projectRoot, packageManager) {
|
|
|
153
444
|
bun: "bun.lockb"
|
|
154
445
|
};
|
|
155
446
|
const lockfile = lockfiles[packageManager];
|
|
156
|
-
const lockfilePath =
|
|
157
|
-
if (await checkPathExists(lockfilePath)) {
|
|
447
|
+
const lockfilePath = join2(projectRoot, lockfile);
|
|
448
|
+
if (await checkPathExists(lockfilePath, fsAdapter)) {
|
|
158
449
|
return lockfile;
|
|
159
450
|
}
|
|
160
451
|
return lockfile;
|
|
161
452
|
}
|
|
162
|
-
async function detectContext(projectRoot) {
|
|
453
|
+
async function detectContext(projectRoot, fsAdapter) {
|
|
163
454
|
const fullPath = resolve(projectRoot);
|
|
164
455
|
if (detectionCache.has(fullPath)) {
|
|
165
456
|
logger.debug(`Using cached context for ${fullPath}`);
|
|
@@ -171,7 +462,7 @@ async function detectContext(projectRoot) {
|
|
|
171
462
|
logger.debug(`Detecting context for project: ${fullPath}`);
|
|
172
463
|
let pkg;
|
|
173
464
|
try {
|
|
174
|
-
pkg = await readPackageJson(fullPath);
|
|
465
|
+
pkg = await readPackageJson(fullPath, fsAdapter);
|
|
175
466
|
} catch (error) {
|
|
176
467
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
177
468
|
throw new DetectionError(
|
|
@@ -189,14 +480,17 @@ async function detectContext(projectRoot) {
|
|
|
189
480
|
gitInfo
|
|
190
481
|
] = await Promise.all([
|
|
191
482
|
Promise.resolve(detectFramework(pkg)),
|
|
192
|
-
detectTypeScript(fullPath),
|
|
193
|
-
detectBundler(fullPath, pkg),
|
|
483
|
+
detectTypeScript(fullPath, fsAdapter),
|
|
484
|
+
detectBundler(fullPath, pkg, fsAdapter),
|
|
194
485
|
detectPackageManager(fullPath),
|
|
195
|
-
detectSrcDir(fullPath),
|
|
196
|
-
detectPublicDir(fullPath),
|
|
197
|
-
detectGit(fullPath)
|
|
486
|
+
detectSrcDir(fullPath, fsAdapter),
|
|
487
|
+
detectPublicDir(fullPath, fsAdapter),
|
|
488
|
+
detectGit(fullPath, fsAdapter)
|
|
198
489
|
]);
|
|
199
|
-
const lockfile = await detectLockfile(fullPath, packageManager);
|
|
490
|
+
const lockfile = await detectLockfile(fullPath, packageManager, fsAdapter);
|
|
491
|
+
const nextjsRouter = frameworkInfo.framework === "nextjs" ? await detectNextjsRouter(fullPath, srcDir, fsAdapter) : void 0;
|
|
492
|
+
const vueVersion = frameworkInfo.framework === "vue" ? "3" : void 0;
|
|
493
|
+
const vueApi = frameworkInfo.framework === "vue" ? await detectVueApi(fullPath, srcDir, fsAdapter) : void 0;
|
|
200
494
|
const dependencies = pkg["dependencies"] || {};
|
|
201
495
|
const devDependencies = pkg["devDependencies"] || {};
|
|
202
496
|
const context = {
|
|
@@ -224,7 +518,14 @@ async function detectContext(projectRoot) {
|
|
|
224
518
|
devDependencies,
|
|
225
519
|
// Git
|
|
226
520
|
hasGit: gitInfo.hasGit,
|
|
227
|
-
gitHooksPath: gitInfo.gitHooksPath
|
|
521
|
+
gitHooksPath: gitInfo.gitHooksPath,
|
|
522
|
+
// Next.js specific
|
|
523
|
+
nextjsRouter,
|
|
524
|
+
// Vue.js specific
|
|
525
|
+
vueVersion,
|
|
526
|
+
vueApi,
|
|
527
|
+
// Filesystem adapter (for testing with memfs)
|
|
528
|
+
fsAdapter
|
|
228
529
|
};
|
|
229
530
|
detectionCache.set(fullPath, context);
|
|
230
531
|
logger.debug(`Context detected successfully for ${fullPath}`, {
|
|
@@ -235,184 +536,8 @@ async function detectContext(projectRoot) {
|
|
|
235
536
|
return context;
|
|
236
537
|
}
|
|
237
538
|
|
|
238
|
-
// src/core/plugin-tracker.ts
|
|
239
|
-
import { join as join2 } from "path";
|
|
240
|
-
var CONFIG_FILE_NAME = ".configjsrc";
|
|
241
|
-
var CONFIG_VERSION = "1.0.0";
|
|
242
|
-
var PluginTracker = class {
|
|
243
|
-
configPath;
|
|
244
|
-
config = null;
|
|
245
|
-
constructor(projectRoot) {
|
|
246
|
-
this.configPath = join2(projectRoot, CONFIG_FILE_NAME);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Charge la configuration depuis le fichier
|
|
250
|
-
*/
|
|
251
|
-
async load() {
|
|
252
|
-
try {
|
|
253
|
-
const exists = await checkPathExists(this.configPath);
|
|
254
|
-
if (!exists) {
|
|
255
|
-
this.config = {
|
|
256
|
-
version: CONFIG_VERSION,
|
|
257
|
-
installedPlugins: [],
|
|
258
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
259
|
-
};
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const content = await readFileContent(this.configPath);
|
|
263
|
-
this.config = JSON.parse(content);
|
|
264
|
-
if (!this.config.version) {
|
|
265
|
-
logger.warn("Old config format detected, migrating...");
|
|
266
|
-
this.config.version = CONFIG_VERSION;
|
|
267
|
-
await this.save();
|
|
268
|
-
}
|
|
269
|
-
} catch (error) {
|
|
270
|
-
logger.error("Failed to load plugin configuration", error);
|
|
271
|
-
this.config = {
|
|
272
|
-
version: CONFIG_VERSION,
|
|
273
|
-
installedPlugins: [],
|
|
274
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Sauvegarde la configuration dans le fichier
|
|
280
|
-
*/
|
|
281
|
-
async save() {
|
|
282
|
-
if (!this.config) {
|
|
283
|
-
throw new Error("Configuration not loaded");
|
|
284
|
-
}
|
|
285
|
-
this.config.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
286
|
-
try {
|
|
287
|
-
const content = JSON.stringify(this.config, null, 2);
|
|
288
|
-
await writeFileContent(this.configPath, content);
|
|
289
|
-
} catch (error) {
|
|
290
|
-
logger.error("Failed to save plugin configuration", error);
|
|
291
|
-
throw error;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Vérifie si un plugin est déjà installé
|
|
296
|
-
*/
|
|
297
|
-
isInstalled(pluginName) {
|
|
298
|
-
if (!this.config) {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
return this.config.installedPlugins.some((p) => p.name === pluginName);
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Récupère les informations d'un plugin installé
|
|
305
|
-
*/
|
|
306
|
-
getPlugin(pluginName) {
|
|
307
|
-
if (!this.config) {
|
|
308
|
-
return void 0;
|
|
309
|
-
}
|
|
310
|
-
return this.config.installedPlugins.find((p) => p.name === pluginName);
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Récupère tous les plugins installés
|
|
314
|
-
*/
|
|
315
|
-
getInstalledPlugins() {
|
|
316
|
-
return this.config?.installedPlugins || [];
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Récupère tous les plugins installés d'une catégorie donnée
|
|
320
|
-
*/
|
|
321
|
-
getPluginsByCategory(category) {
|
|
322
|
-
if (!this.config) {
|
|
323
|
-
return [];
|
|
324
|
-
}
|
|
325
|
-
return this.config.installedPlugins.filter((p) => p.category === category);
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Ajoute un plugin à la liste des installés
|
|
329
|
-
*/
|
|
330
|
-
async addPlugin(plugin) {
|
|
331
|
-
if (!this.config) {
|
|
332
|
-
throw new Error("Configuration not loaded");
|
|
333
|
-
}
|
|
334
|
-
if (this.isInstalled(plugin.name)) {
|
|
335
|
-
logger.warn(`Plugin ${plugin.name} is already marked as installed`);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const installedPlugin = {
|
|
339
|
-
...plugin,
|
|
340
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
341
|
-
};
|
|
342
|
-
this.config.installedPlugins.push(installedPlugin);
|
|
343
|
-
await this.save();
|
|
344
|
-
logger.debug(`Marked plugin ${plugin.name} as installed`);
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Supprime un plugin de la liste des installés
|
|
348
|
-
*/
|
|
349
|
-
async removePlugin(pluginName) {
|
|
350
|
-
if (!this.config) {
|
|
351
|
-
throw new Error("Configuration not loaded");
|
|
352
|
-
}
|
|
353
|
-
const index = this.config.installedPlugins.findIndex(
|
|
354
|
-
(p) => p.name === pluginName
|
|
355
|
-
);
|
|
356
|
-
if (index === -1) {
|
|
357
|
-
logger.warn(`Plugin ${pluginName} not found in installed plugins`);
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
this.config.installedPlugins.splice(index, 1);
|
|
361
|
-
await this.save();
|
|
362
|
-
logger.debug(`Removed plugin ${pluginName} from installed plugins`);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Met à jour les informations d'un plugin installé
|
|
366
|
-
*/
|
|
367
|
-
async updatePlugin(pluginName, updates) {
|
|
368
|
-
if (!this.config) {
|
|
369
|
-
throw new Error("Configuration not loaded");
|
|
370
|
-
}
|
|
371
|
-
const plugin = this.config.installedPlugins.find(
|
|
372
|
-
(p) => p.name === pluginName
|
|
373
|
-
);
|
|
374
|
-
if (!plugin) {
|
|
375
|
-
throw new Error(`Plugin ${pluginName} not found`);
|
|
376
|
-
}
|
|
377
|
-
Object.assign(plugin, updates);
|
|
378
|
-
await this.save();
|
|
379
|
-
logger.debug(`Updated plugin ${pluginName}`);
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Efface toute la configuration
|
|
383
|
-
*/
|
|
384
|
-
async reset() {
|
|
385
|
-
this.config = {
|
|
386
|
-
version: CONFIG_VERSION,
|
|
387
|
-
installedPlugins: [],
|
|
388
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
389
|
-
};
|
|
390
|
-
await this.save();
|
|
391
|
-
logger.info("Reset plugin configuration");
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Vérifie les conflits potentiels avec un nouveau plugin
|
|
395
|
-
*
|
|
396
|
-
* @param category - Catégorie du plugin à installer
|
|
397
|
-
* @param exclusiveCategories - Catégories qui ne peuvent avoir qu'un seul plugin
|
|
398
|
-
* @returns Liste des plugins en conflit
|
|
399
|
-
*/
|
|
400
|
-
checkCategoryConflicts(category, exclusiveCategories = ["routing", "state"]) {
|
|
401
|
-
if (!exclusiveCategories.includes(category)) {
|
|
402
|
-
return [];
|
|
403
|
-
}
|
|
404
|
-
return this.getPluginsByCategory(category);
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Exporte la configuration pour affichage ou debug
|
|
408
|
-
*/
|
|
409
|
-
export() {
|
|
410
|
-
return this.config;
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
|
|
414
539
|
export {
|
|
540
|
+
PluginTracker,
|
|
415
541
|
DetectionError,
|
|
416
|
-
detectContext
|
|
417
|
-
PluginTracker
|
|
542
|
+
detectContext
|
|
418
543
|
};
|