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