@mcpc-tech/unplugin-dev-inspector-mcp 0.1.9 → 0.1.10

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/dist/cli.cjs CHANGED
@@ -1,206 +1,111 @@
1
1
  #!/usr/bin/env node
2
2
  const require_chunk = require('./chunk.cjs');
3
3
  const require_config_updater = require('./config-updater.cjs');
4
- let node_http = require("node:http");
5
- node_http = require_chunk.__toESM(node_http);
6
- let child_process = require("child_process");
7
4
  let fs = require("fs");
8
5
  let path = require("path");
6
+ let child_process = require("child_process");
9
7
  let _babel_parser = require("@babel/parser");
10
8
  let _babel_traverse = require("@babel/traverse");
11
9
  _babel_traverse = require_chunk.__toESM(_babel_traverse);
12
10
  let magic_string = require("magic-string");
13
11
  magic_string = require_chunk.__toESM(magic_string);
12
+ let node_http = require("node:http");
13
+ node_http = require_chunk.__toESM(node_http);
14
14
 
15
- //#region src/utils/standalone-server.ts
16
- var StandaloneServer = class {
17
- server;
18
- middlewares = [];
19
- port = 0;
20
- host = "localhost";
21
- stack = [];
22
- constructor() {
23
- this.server = node_http.default.createServer(async (req, res) => {
24
- let index = 0;
25
- const next = async () => {
26
- if (index >= this.middlewares.length) {
27
- if (!res.writableEnded) {
28
- res.statusCode = 404;
29
- res.end("Not Found");
30
- }
31
- return;
32
- }
33
- const layer = this.middlewares[index++];
34
- if ((req.url || "/").startsWith(layer.route)) try {
35
- const originalUrl = req.url;
36
- if (layer.route !== "/" && req.url) {}
37
- await layer.handle(req, res, next);
38
- if (layer.route !== "/") req.url = originalUrl;
39
- } catch (error) {
40
- console.error("Middleware error:", error);
41
- if (!res.writableEnded) {
42
- res.statusCode = 500;
43
- res.end("Internal Server Error");
44
- }
45
- }
46
- else next();
47
- };
48
- await next();
49
- });
50
- }
51
- use(routeOrHandle, handle) {
52
- let route = "/";
53
- let handler;
54
- if (typeof routeOrHandle === "string") {
55
- route = routeOrHandle;
56
- if (!handle) throw new Error("Handler is required when route is provided");
57
- handler = handle;
58
- } else handler = routeOrHandle;
59
- this.middlewares.push({
60
- route,
61
- handle: handler
62
- });
63
- return this;
15
+ //#region src/utils/package-manager.ts
16
+ function detectPackageManager(cwd = process.cwd()) {
17
+ let current = cwd;
18
+ const root = (0, path.dirname)(cwd) === cwd ? cwd : "/";
19
+ while (current !== root) {
20
+ if ((0, fs.existsSync)((0, path.join)(current, "pnpm-lock.yaml"))) return "pnpm";
21
+ if ((0, fs.existsSync)((0, path.join)(current, "yarn.lock"))) return "yarn";
22
+ if ((0, fs.existsSync)((0, path.join)(current, "bun.lockb"))) return "bun";
23
+ if ((0, fs.existsSync)((0, path.join)(current, "package-lock.json"))) return "npm";
24
+ const parent = (0, path.dirname)(current);
25
+ if (parent === current) break;
26
+ current = parent;
64
27
  }
65
- listen(...args) {
66
- return this.server.listen(...args);
28
+ const userAgent = process.env.npm_config_user_agent;
29
+ if (userAgent) {
30
+ if (userAgent.startsWith("pnpm")) return "pnpm";
31
+ if (userAgent.startsWith("yarn")) return "yarn";
32
+ if (userAgent.startsWith("bun")) return "bun";
67
33
  }
68
- async start(options = {}) {
69
- const startPort = options.port || 8888;
70
- this.host = options.host || "localhost";
71
- for (let port = startPort; port < startPort + 100; port++) try {
72
- await new Promise((resolve$1, reject) => {
73
- this.server.listen(port, this.host, () => {
74
- this.port = port;
75
- resolve$1();
76
- });
77
- this.server.on("error", (err) => {
78
- if (err.code === "EADDRINUSE") {
79
- this.server.close();
80
- reject(err);
81
- } else reject(err);
82
- });
83
- });
84
- return {
85
- host: this.host,
86
- port: this.port
87
- };
88
- } catch (error) {
89
- if (error.code !== "EADDRINUSE") throw error;
90
- }
91
- throw new Error(`Could not find a free port starting from ${startPort}`);
34
+ return "npm";
35
+ }
36
+ function getInstallCommand(pm, packageName, dev = true) {
37
+ const flags = dev ? "-D" : "";
38
+ switch (pm) {
39
+ case "npm": return `npm install ${flags} ${packageName}`;
40
+ case "yarn": return `yarn add ${flags} ${packageName}`;
41
+ case "pnpm": return `pnpm add ${flags} ${packageName}`;
42
+ case "bun": return `bun add ${flags} ${packageName}`;
43
+ default: return `npm install ${flags} ${packageName}`;
92
44
  }
93
- close() {
94
- this.server.close();
45
+ }
46
+ function installPackage(packageName, dev = true) {
47
+ const pm = detectPackageManager();
48
+ const command = getInstallCommand(pm, packageName, dev);
49
+ console.log(`\n📦 Installing ${packageName} with ${pm}...`);
50
+ console.log(` Running: ${command}`);
51
+ try {
52
+ (0, child_process.execSync)(command, { stdio: "inherit" });
53
+ console.log(`✅ Successfully installed ${packageName}`);
54
+ return true;
55
+ } catch (error) {
56
+ console.error(`❌ Failed to install ${packageName}`);
57
+ return false;
95
58
  }
96
- };
97
- let globalServer = null;
98
- async function startStandaloneServer(options = {}) {
99
- if (globalServer) return {
100
- server: globalServer,
101
- host: globalServer.host,
102
- port: globalServer.port,
103
- isNew: false
104
- };
105
- globalServer = new StandaloneServer();
106
- const { host, port } = await globalServer.start(options);
107
- return {
108
- server: globalServer,
109
- host,
110
- port,
111
- isNew: true
112
- };
113
59
  }
114
60
 
115
61
  //#endregion
116
- //#region src/utils/config-detector.ts
117
- require_config_updater.init_helpers();
118
- const CONFIG_PATTERNS = {
119
- vite: [
120
- "vite.config.ts",
121
- "vite.config.js",
122
- "vite.config.mjs"
123
- ],
124
- webpack: ["webpack.config.ts", "webpack.config.js"],
125
- nextjs: [
126
- "next.config.ts",
127
- "next.config.js",
128
- "next.config.mjs"
129
- ]
130
- };
131
- /**
132
- * Auto-detect bundler configuration files in the given directory
133
- * @param cwd - Current working directory (defaults to process.cwd())
134
- * @returns Array of detected configurations
135
- */
136
- function detectConfigs(cwd = process.cwd()) {
137
- const detected = [];
138
- for (const [bundler, patterns] of Object.entries(CONFIG_PATTERNS)) for (const pattern of patterns) {
139
- const configPath = (0, path.resolve)(cwd, pattern);
140
- if ((0, fs.existsSync)(configPath)) {
141
- detected.push({
142
- path: configPath,
143
- bundler,
144
- exists: true
145
- });
146
- break;
147
- }
148
- }
149
- return detected;
150
- }
62
+ //#region src/commands/setup/utils.ts
151
63
  /**
152
- * Detect a specific bundler configuration
153
- * @param bundler - Bundler type to detect
154
- * @param cwd - Current working directory
155
- * @returns Detected config or null
64
+ * Detects a configuration file in the given directory based on patterns.
156
65
  */
157
- function detectConfig(bundler, cwd = process.cwd()) {
158
- const patterns = CONFIG_PATTERNS[bundler];
66
+ function detectConfigFile(cwd, patterns) {
159
67
  for (const pattern of patterns) {
160
68
  const configPath = (0, path.resolve)(cwd, pattern);
161
- if ((0, fs.existsSync)(configPath)) return {
162
- path: configPath,
163
- bundler,
164
- exists: true
165
- };
69
+ if ((0, fs.existsSync)(configPath)) return configPath;
166
70
  }
167
71
  return null;
168
72
  }
169
73
  /**
170
- * Find config file by explicit path
171
- * @param configPath - Explicit path to config file
172
- * @returns Detected config or null
74
+ * Calculates the character position to insert an import, based on the last import line.
173
75
  */
174
- function detectConfigByPath(configPath) {
175
- if (!(0, fs.existsSync)(configPath)) return null;
176
- const fileName = configPath.split("/").pop() || "";
177
- for (const [bundler, patterns] of Object.entries(CONFIG_PATTERNS)) if (patterns.some((pattern) => fileName.includes(pattern.split(".")[0]))) return {
178
- path: configPath,
179
- bundler,
180
- exists: true
181
- };
182
- return null;
76
+ function getInsertPosition(code, lastImportLine) {
77
+ const lines = code.split("\n");
78
+ let insertPos = 0;
79
+ for (let i = 0; i < lastImportLine; i++) insertPos += lines[i].length + 1;
80
+ return insertPos;
183
81
  }
184
-
185
- //#endregion
186
- //#region src/utils/codemod-transformer.ts
187
- const traverse = _babel_traverse.default.default || _babel_traverse.default;
188
- const PLUGIN_IMPORT = "@mcpc-tech/unplugin-dev-inspector-mcp";
189
- const PLUGIN_VAR_NAME = "DevInspector";
190
82
  /**
191
- * Check if DevInspector is already imported in the code
83
+ * Sanitizes a path string to prevent syntax errors in generated code.
192
84
  */
193
- function hasDevInspectorImport(code) {
194
- return code.includes(PLUGIN_IMPORT);
85
+ function sanitizePath(path$1) {
86
+ return path$1.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "");
195
87
  }
196
88
  /**
197
- * Transform Vite configuration to add DevInspector
89
+ * Generates formatted plugin options string.
198
90
  */
199
- function transformViteConfig(options) {
200
- const { configPath } = options;
91
+ function getPluginOptions(options, indent = 6) {
92
+ if (!options.entryPath) return "{ enabled: true }";
93
+ const baseIndent = " ".repeat(indent);
94
+ return `{
95
+ ${baseIndent}enabled: true,
96
+ ${baseIndent}entry: '${sanitizePath(options.entryPath)}',
97
+ ${baseIndent}autoInject: false,
98
+ ${baseIndent.slice(2)}}`;
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/commands/setup/frameworks/vite.ts
103
+ const traverse$2 = _babel_traverse.default.default || _babel_traverse.default;
104
+ const PLUGIN_IMPORT$2 = "@mcpc-tech/unplugin-dev-inspector-mcp";
105
+ const PLUGIN_VAR_NAME$2 = "DevInspector";
106
+ function transformViteConfig(code, options) {
201
107
  try {
202
- const code = (0, fs.readFileSync)(configPath, "utf-8");
203
- if (hasDevInspectorImport(code)) return {
108
+ if (code.includes(PLUGIN_IMPORT$2)) return {
204
109
  success: true,
205
110
  modified: false,
206
111
  message: "DevInspector is already configured in this file"
@@ -214,7 +119,7 @@ function transformViteConfig(options) {
214
119
  let pluginsArrayStart = -1;
215
120
  let firstPluginStart = -1;
216
121
  let hasPluginsArray = false;
217
- traverse(ast, {
122
+ traverse$2(ast, {
218
123
  ImportDeclaration(path$1) {
219
124
  if (path$1.node.loc) lastImportEnd = Math.max(lastImportEnd, path$1.node.loc.end.line);
220
125
  },
@@ -229,18 +134,16 @@ function transformViteConfig(options) {
229
134
  }
230
135
  }
231
136
  });
232
- const lines = code.split("\n");
233
- const importLine = `import ${PLUGIN_VAR_NAME} from '${PLUGIN_IMPORT}';\n`;
137
+ const importLine = `import ${PLUGIN_VAR_NAME$2} from '${PLUGIN_IMPORT$2}';\n`;
234
138
  if (lastImportEnd > 0) {
235
- let insertPos = 0;
236
- for (let i = 0; i < lastImportEnd; i++) insertPos += lines[i].length + 1;
139
+ const insertPos = getInsertPosition(code, lastImportEnd);
237
140
  s.appendLeft(insertPos, importLine);
238
141
  } else s.prepend(importLine);
239
142
  if (hasPluginsArray && firstPluginStart > -1) {
240
- const pluginCall = `${PLUGIN_VAR_NAME}.vite({\n enabled: true,\n }),\n `;
143
+ const pluginCall = `${PLUGIN_VAR_NAME$2}.vite(${getPluginOptions(options, 6)}),\n `;
241
144
  s.appendLeft(firstPluginStart, pluginCall);
242
145
  } else if (hasPluginsArray && pluginsArrayStart > -1) {
243
- const pluginCall = `\n ${PLUGIN_VAR_NAME}.vite({\n enabled: true,\n }),\n `;
146
+ const pluginCall = `\n ${PLUGIN_VAR_NAME$2}.vite(${getPluginOptions(options, 6)}),\n `;
244
147
  s.appendLeft(pluginsArrayStart + 1, pluginCall);
245
148
  } else return {
246
149
  success: false,
@@ -263,14 +166,22 @@ function transformViteConfig(options) {
263
166
  };
264
167
  }
265
168
  }
266
- /**
267
- * Transform Webpack configuration to add DevInspector
268
- */
269
- function transformWebpackConfig(options) {
270
- const { configPath } = options;
169
+ function detectViteConfig(cwd) {
170
+ return detectConfigFile(cwd, [
171
+ "vite.config.ts",
172
+ "vite.config.js",
173
+ "vite.config.mjs"
174
+ ]);
175
+ }
176
+
177
+ //#endregion
178
+ //#region src/commands/setup/frameworks/webpack.ts
179
+ const traverse$1 = _babel_traverse.default.default || _babel_traverse.default;
180
+ const PLUGIN_IMPORT$1 = "@mcpc-tech/unplugin-dev-inspector-mcp";
181
+ const PLUGIN_VAR_NAME$1 = "DevInspector";
182
+ function transformWebpackConfig(code, options) {
271
183
  try {
272
- const code = (0, fs.readFileSync)(configPath, "utf-8");
273
- if (hasDevInspectorImport(code)) return {
184
+ if (code.includes(PLUGIN_IMPORT$1)) return {
274
185
  success: true,
275
186
  modified: false,
276
187
  message: "DevInspector is already configured in this file"
@@ -283,7 +194,7 @@ function transformWebpackConfig(options) {
283
194
  });
284
195
  let lastImportEnd = 0;
285
196
  let pluginsArrayStart = -1;
286
- traverse(ast, {
197
+ traverse$1(ast, {
287
198
  ImportDeclaration(path$1) {
288
199
  if (path$1.node.loc) lastImportEnd = Math.max(lastImportEnd, path$1.node.loc.end.line);
289
200
  },
@@ -296,15 +207,13 @@ function transformWebpackConfig(options) {
296
207
  }
297
208
  }
298
209
  });
299
- const lines = code.split("\n");
300
- const importLine = isESM ? `import ${PLUGIN_VAR_NAME} from '${PLUGIN_IMPORT}';\n` : `const ${PLUGIN_VAR_NAME} = require('${PLUGIN_IMPORT}');\n`;
210
+ const importLine = isESM ? `import ${PLUGIN_VAR_NAME$1} from '${PLUGIN_IMPORT$1}';\n` : `const ${PLUGIN_VAR_NAME$1} = require('${PLUGIN_IMPORT$1}');\n`;
301
211
  if (lastImportEnd > 0) {
302
- let insertPos = 0;
303
- for (let i = 0; i < lastImportEnd; i++) insertPos += lines[i].length + 1;
212
+ const insertPos = getInsertPosition(code, lastImportEnd);
304
213
  s.appendLeft(insertPos, importLine);
305
214
  } else s.prepend(importLine);
306
215
  if (pluginsArrayStart > -1) {
307
- const pluginCall = `\n ${PLUGIN_VAR_NAME}.webpack({\n enabled: true,\n }),`;
216
+ const pluginCall = `\n ${PLUGIN_VAR_NAME$1}.webpack(${getPluginOptions(options, 6)}),`;
308
217
  s.appendLeft(pluginsArrayStart + 1, pluginCall);
309
218
  } else return {
310
219
  success: false,
@@ -327,14 +236,23 @@ function transformWebpackConfig(options) {
327
236
  };
328
237
  }
329
238
  }
330
- /**
331
- * Transform Next.js configuration to add DevInspector
332
- */
333
- function transformNextConfig(options) {
334
- const { configPath } = options;
239
+ function detectWebpackConfig(cwd) {
240
+ return detectConfigFile(cwd, [
241
+ "webpack.config.ts",
242
+ "webpack.config.js",
243
+ "webpack.config.cjs",
244
+ "webpack.config.mjs"
245
+ ]);
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/commands/setup/frameworks/nextjs.ts
250
+ const traverse = _babel_traverse.default.default || _babel_traverse.default;
251
+ const PLUGIN_IMPORT = "@mcpc-tech/unplugin-dev-inspector-mcp";
252
+ const PLUGIN_VAR_NAME = "DevInspector";
253
+ function transformNextConfig(code, _options) {
335
254
  try {
336
- const code = (0, fs.readFileSync)(configPath, "utf-8");
337
- if (hasDevInspectorImport(code)) return {
255
+ if (code.includes(PLUGIN_IMPORT)) return {
338
256
  success: true,
339
257
  modified: false,
340
258
  message: "DevInspector is already configured in this file"
@@ -347,6 +265,7 @@ function transformNextConfig(options) {
347
265
  let lastImportEnd = 0;
348
266
  let nextConfigStart = -1;
349
267
  let hasWebpackProperty = false;
268
+ let hasTurbopackProperty = false;
350
269
  traverse(ast, {
351
270
  ImportDeclaration(path$1) {
352
271
  if (path$1.node.loc) lastImportEnd = Math.max(lastImportEnd, path$1.node.loc.end.line);
@@ -355,25 +274,36 @@ function transformNextConfig(options) {
355
274
  if (path$1.node.id.type === "Identifier" && path$1.node.id.name === "nextConfig" && path$1.node.init?.type === "ObjectExpression") {
356
275
  if (path$1.node.init.start !== null && path$1.node.init.start !== void 0) nextConfigStart = path$1.node.init.start;
357
276
  path$1.node.init.properties.forEach((prop) => {
358
- if (prop.type === "ObjectProperty" && prop.key.type === "Identifier" && prop.key.name === "webpack") hasWebpackProperty = true;
277
+ if (prop.type === "ObjectProperty" && prop.key.type === "Identifier") {
278
+ if (prop.key.name === "webpack") hasWebpackProperty = true;
279
+ if (prop.key.name === "turbopack") hasTurbopackProperty = true;
280
+ }
359
281
  });
360
282
  }
361
283
  }
362
284
  });
363
- const lines = code.split("\n");
364
- const importLine = `import ${PLUGIN_VAR_NAME} from '${PLUGIN_IMPORT}';\n`;
285
+ const importLine = `import ${PLUGIN_VAR_NAME}, { turbopackDevInspector } from '${PLUGIN_IMPORT}';\n`;
365
286
  if (lastImportEnd > 0) {
366
- let insertPos = 0;
367
- for (let i = 0; i < lastImportEnd; i++) insertPos += lines[i].length + 1;
287
+ const insertPos = getInsertPosition(code, lastImportEnd);
368
288
  s.appendLeft(insertPos, importLine);
369
289
  } else s.prepend(importLine);
370
- if (nextConfigStart > -1 && !hasWebpackProperty) {
371
- const webpackConfig = `\n webpack: (config) => {\n config.plugins.push(\n ${PLUGIN_VAR_NAME}.webpack({\n enabled: true,\n })\n );\n return config;\n },`;
290
+ if (nextConfigStart > -1 && !hasWebpackProperty && !hasTurbopackProperty) {
291
+ const pluginOptions = "{ enabled: true }";
292
+ const webpackConfig = `
293
+ webpack: (config) => {
294
+ config.plugins.push(
295
+ ${PLUGIN_VAR_NAME}.webpack(${pluginOptions})
296
+ );
297
+ return config;
298
+ },
299
+ turbopack: {
300
+ rules: turbopackDevInspector(${pluginOptions}),
301
+ },`;
372
302
  s.appendLeft(nextConfigStart + 1, webpackConfig);
373
- } else if (hasWebpackProperty) return {
303
+ } else if (hasWebpackProperty || hasTurbopackProperty) return {
374
304
  success: false,
375
305
  modified: false,
376
- message: "Webpack property already exists. Please add DevInspector manually to avoid conflicts"
306
+ message: `${hasWebpackProperty ? "webpack" : "turbopack"} property already exists. Please add DevInspector manually to avoid conflicts`
377
307
  };
378
308
  else return {
379
309
  success: false,
@@ -396,33 +326,13 @@ function transformNextConfig(options) {
396
326
  };
397
327
  }
398
328
  }
399
- /**
400
- * Main transform function that routes to the appropriate transformer
401
- */
402
- function transformConfig(options) {
403
- switch (options.bundler) {
404
- case "vite": return transformViteConfig(options);
405
- case "webpack": return transformWebpackConfig(options);
406
- case "nextjs": return transformNextConfig(options);
407
- default: return {
408
- success: false,
409
- modified: false,
410
- error: `Unknown bundler type: ${options.bundler}`,
411
- message: "Unsupported bundler type"
412
- };
413
- }
414
- }
415
- /**
416
- * Transform entry file to add DevInspector import
417
- */
418
- function transformEntryFile(options) {
419
- const { entryPath } = options;
329
+ function transformNextLayout(code) {
420
330
  try {
421
- const code = (0, fs.readFileSync)(entryPath, "utf-8");
422
- if (code.includes("virtual:dev-inspector-mcp")) return {
331
+ const COMPONENT_IMPORT = "@mcpc-tech/unplugin-dev-inspector-mcp/next";
332
+ if (code.includes("DevInspector") || code.includes(COMPONENT_IMPORT)) return {
423
333
  success: true,
424
334
  modified: false,
425
- message: "DevInspector is already imported in this file"
335
+ message: "DevInspector component is already in this file"
426
336
  };
427
337
  const s = new magic_string.default(code);
428
338
  const ast = (0, _babel_parser.parse)(code, {
@@ -430,88 +340,55 @@ function transformEntryFile(options) {
430
340
  plugins: ["typescript", "jsx"]
431
341
  });
432
342
  let lastImportEnd = 0;
433
- traverse(ast, { ImportDeclaration(path$1) {
434
- if (path$1.node.loc) lastImportEnd = Math.max(lastImportEnd, path$1.node.loc.end.line);
435
- } });
436
- const lines = code.split("\n");
437
- const importLine = "import 'virtual:dev-inspector-mcp';\n";
343
+ let bodyStart = -1;
344
+ traverse(ast, {
345
+ ImportDeclaration(path$1) {
346
+ if (path$1.node.loc) lastImportEnd = Math.max(lastImportEnd, path$1.node.loc.end.line);
347
+ },
348
+ JSXElement(path$1) {
349
+ if (path$1.node.openingElement.name.type === "JSXIdentifier" && path$1.node.openingElement.name.name === "body" && path$1.node.start !== null) {
350
+ const openingEnd = path$1.node.openingElement.end;
351
+ if (openingEnd !== null && openingEnd !== void 0) bodyStart = openingEnd;
352
+ }
353
+ }
354
+ });
355
+ const importLine = `import { DevInspector } from "${COMPONENT_IMPORT}";\n`;
438
356
  if (lastImportEnd > 0) {
439
- let insertPos = 0;
440
- for (let i = 0; i < lastImportEnd; i++) insertPos += lines[i].length + 1;
357
+ const insertPos = getInsertPosition(code, lastImportEnd);
441
358
  s.appendLeft(insertPos, importLine);
442
359
  } else s.prepend(importLine);
360
+ if (bodyStart > -1) s.appendLeft(bodyStart, "\n <DevInspector />");
361
+ else return {
362
+ success: false,
363
+ modified: false,
364
+ error: "Could not find <body> element",
365
+ message: "Please add <DevInspector /> manually to your layout"
366
+ };
443
367
  return {
444
368
  success: true,
445
369
  modified: true,
446
370
  code: s.toString(),
447
- message: "Successfully added DevInspector import to entry file"
371
+ message: "Successfully added DevInspector component to layout"
448
372
  };
449
373
  } catch (error) {
450
374
  return {
451
375
  success: false,
452
376
  modified: false,
453
377
  error: error instanceof Error ? error.message : String(error),
454
- message: "Failed to transform entry file"
378
+ message: "Failed to transform layout file"
455
379
  };
456
380
  }
457
381
  }
458
-
459
- //#endregion
460
- //#region src/utils/package-manager.ts
461
- function detectPackageManager(cwd = process.cwd()) {
462
- let current = cwd;
463
- const root = (0, path.dirname)(cwd) === cwd ? cwd : "/";
464
- while (current !== root) {
465
- if ((0, fs.existsSync)((0, path.join)(current, "pnpm-lock.yaml"))) return "pnpm";
466
- if ((0, fs.existsSync)((0, path.join)(current, "yarn.lock"))) return "yarn";
467
- if ((0, fs.existsSync)((0, path.join)(current, "bun.lockb"))) return "bun";
468
- if ((0, fs.existsSync)((0, path.join)(current, "package-lock.json"))) return "npm";
469
- const parent = (0, path.dirname)(current);
470
- if (parent === current) break;
471
- current = parent;
472
- }
473
- const userAgent = process.env.npm_config_user_agent;
474
- if (userAgent) {
475
- if (userAgent.startsWith("pnpm")) return "pnpm";
476
- if (userAgent.startsWith("yarn")) return "yarn";
477
- if (userAgent.startsWith("bun")) return "bun";
478
- }
479
- return "npm";
480
- }
481
- function getInstallCommand(pm, packageName, dev = true) {
482
- const flags = dev ? "-D" : "";
483
- switch (pm) {
484
- case "npm": return `npm install ${flags} ${packageName}`;
485
- case "yarn": return `yarn add ${flags} ${packageName}`;
486
- case "pnpm": return `pnpm add ${flags} ${packageName}`;
487
- case "bun": return `bun add ${flags} ${packageName}`;
488
- default: return `npm install ${flags} ${packageName}`;
489
- }
490
- }
491
- function installPackage(packageName, dev = true) {
492
- const pm = detectPackageManager();
493
- const command = getInstallCommand(pm, packageName, dev);
494
- console.log(`\n📦 Installing ${packageName} with ${pm}...`);
495
- console.log(` Running: ${command}`);
496
- try {
497
- (0, child_process.execSync)(command, { stdio: "inherit" });
498
- console.log(`✅ Successfully installed ${packageName}`);
499
- return true;
500
- } catch (error) {
501
- console.error(`❌ Failed to install ${packageName}`);
502
- return false;
503
- }
382
+ function detectNextConfig(cwd) {
383
+ return detectConfigFile(cwd, [
384
+ "next.config.ts",
385
+ "next.config.js",
386
+ "next.config.mjs"
387
+ ]);
504
388
  }
505
389
 
506
390
  //#endregion
507
- //#region src/cli.ts
508
- /**
509
- * CLI for dev-inspector
510
- *
511
- * Commands:
512
- * setup - Add DevInspector to your bundler config
513
- * server - Start standalone MCP server (default)
514
- */
391
+ //#region src/commands/setup/index.ts
515
392
  async function runSetupCommand() {
516
393
  const args = process.argv.slice(3);
517
394
  let dryRun = false;
@@ -529,31 +406,7 @@ async function runSetupCommand() {
529
406
  bundlerType = args[i + 1];
530
407
  i++;
531
408
  } else if (args[i] === "--help" || args[i] === "-h") {
532
- console.log(`
533
- ╔══════════════════════════════════════════════════════════╗
534
- ║ 🔧 DevInspector Setup Command ║
535
- ╚══════════════════════════════════════════════════════════╝
536
-
537
- Usage:
538
- npx @mcpc-tech/unplugin-dev-inspector-mcp setup [options]
539
-
540
- Options:
541
- --config <path> Specify config file path (auto-detect by default)
542
- --entry <path> Specify entry file path to add import (optional)
543
- --bundler <type> Specify bundler type: vite, webpack, nextjs
544
- --dry-run Preview changes without applying them
545
- --help, -h Show this help message
546
-
547
- Examples:
548
- # Auto-detect and setup
549
- npx @mcpc-tech/unplugin-dev-inspector-mcp setup
550
-
551
- # Setup defining entry file (for React Router v7 / non-HTML apps)
552
- npx @mcpc-tech/unplugin-dev-inspector-mcp setup --entry src/main.tsx
553
-
554
- # Preview changes without applying
555
- npx @mcpc-tech/unplugin-dev-inspector-mcp setup --dry-run
556
- `);
409
+ printHelp();
557
410
  process.exit(0);
558
411
  }
559
412
  console.log(`
@@ -562,107 +415,287 @@ Examples:
562
415
  ╚══════════════════════════════════════════════════════════╝
563
416
  `);
564
417
  try {
565
- let targetConfig;
418
+ const cwd = process.cwd();
419
+ let selectedConfigPath = null;
420
+ let selectedBundler = null;
421
+ const frameworks = [
422
+ {
423
+ type: "vite",
424
+ detect: detectViteConfig,
425
+ transform: transformViteConfig
426
+ },
427
+ {
428
+ type: "webpack",
429
+ detect: detectWebpackConfig,
430
+ transform: transformWebpackConfig
431
+ },
432
+ {
433
+ type: "nextjs",
434
+ detect: detectNextConfig,
435
+ transform: transformNextConfig
436
+ }
437
+ ];
438
+ const validBundlers = [
439
+ "vite",
440
+ "webpack",
441
+ "nextjs"
442
+ ];
566
443
  if (configPath) {
567
- targetConfig = detectConfigByPath(configPath);
568
- if (!targetConfig) {
569
- console.error(`❌ Config file not found: ${configPath}`);
444
+ if (!(0, fs.existsSync)(configPath)) {
445
+ console.error(`❌ Provided config file does not exist: ${configPath}`);
446
+ process.exit(1);
447
+ }
448
+ selectedConfigPath = configPath;
449
+ const filename = configPath.toLowerCase();
450
+ if (/vite\.config\.(ts|js|mjs)$/i.test(filename)) selectedBundler = "vite";
451
+ else if (/webpack\.config\.(ts|js|cjs|mjs)$/i.test(filename)) selectedBundler = "webpack";
452
+ else if (/next\.config\.(ts|js|mjs)$/i.test(filename)) selectedBundler = "nextjs";
453
+ if (!selectedBundler) {
454
+ console.error(`❌ Could not determine bundler type from config path: ${configPath}`);
455
+ console.error(`💡 Use --bundler flag to specify: ${validBundlers.join(", ")}`);
570
456
  process.exit(1);
571
457
  }
572
458
  } else if (bundlerType) {
573
- targetConfig = detectConfig(bundlerType);
574
- if (!targetConfig) {
575
- console.error(`❌ No ${bundlerType} config file found in current directory`);
459
+ if (!validBundlers.includes(bundlerType)) {
460
+ console.error(`❌ Invalid bundler type: ${bundlerType}`);
461
+ console.error(`💡 Valid options: ${validBundlers.join(", ")}`);
576
462
  process.exit(1);
577
463
  }
464
+ selectedBundler = bundlerType;
465
+ const fw = frameworks.find((f) => f.type === bundlerType);
466
+ if (fw) selectedConfigPath = fw.detect(cwd);
578
467
  } else {
579
- const detected = detectConfigs();
468
+ const detected = frameworks.map((f) => ({
469
+ type: f.type,
470
+ path: f.detect(cwd)
471
+ })).filter((d) => d.path !== null);
580
472
  if (detected.length === 0) {
581
473
  console.error("❌ No bundler config files found in current directory");
582
- console.log("\nSupported configs: vite.config.{ts,js,mjs}, webpack.config.{ts,js}, next.config.{ts,js,mjs}");
583
474
  process.exit(1);
584
475
  }
585
476
  if (detected.length > 1) {
586
477
  console.log("📦 Multiple configs detected:");
587
- detected.forEach((config, i) => {
588
- console.log(` ${i + 1}. ${config.bundler}: ${config.path}`);
589
- });
478
+ detected.forEach((d, i) => console.log(` ${i + 1}. ${d.type}: ${d.path}`));
590
479
  console.log("\n💡 Tip: Use --bundler or --config to specify which one to transform");
591
- targetConfig = detected[0];
592
- console.log(`\n🎯 Using: ${targetConfig.bundler} (${targetConfig.path})`);
593
- } else {
594
- targetConfig = detected[0];
595
- console.log(`🎯 Detected: ${targetConfig.bundler} config at ${targetConfig.path}`);
596
480
  }
481
+ selectedBundler = detected[0].type;
482
+ selectedConfigPath = detected[0].path;
483
+ console.log(`🎯 Using: ${selectedBundler} (${selectedConfigPath})`);
597
484
  }
598
- console.log(`\n${dryRun ? "🔍 Previewing" : "🔧 Transforming"} ${targetConfig.bundler} config...`);
599
- const result = transformConfig({
600
- configPath: targetConfig.path,
601
- bundler: targetConfig.bundler,
602
- dryRun
603
- });
485
+ if (!selectedConfigPath || !selectedBundler) {
486
+ console.error(`❌ Could not find or detect ${bundlerType || "any"} configuration`);
487
+ process.exit(1);
488
+ }
489
+ console.log(`\n${dryRun ? "🔍 Previewing" : "🔧 Transforming"} ${selectedBundler} config...`);
490
+ const code = (0, fs.readFileSync)(selectedConfigPath, "utf-8");
491
+ const options = {
492
+ dryRun,
493
+ configPath: selectedConfigPath,
494
+ entryPath
495
+ };
496
+ const framework = frameworks.find((f) => f.type === selectedBundler);
497
+ if (!framework) {
498
+ console.error(`❌ Unsupported bundler: ${selectedBundler}`);
499
+ process.exit(1);
500
+ }
501
+ const result = framework.transform(code, options);
604
502
  if (!result.success) {
605
503
  console.error(`\n❌ ${result.message}`);
606
504
  if (result.error) console.error(` Error: ${result.error}`);
607
505
  process.exit(1);
608
506
  }
609
- let entryResult;
610
- if (entryPath) {
611
- console.log(`\n${dryRun ? "🔍 Previewing" : "🔧 Transforming"} entry file: ${entryPath}...`);
612
- const { transformEntryFile: transformEntryFile$1 } = await Promise.resolve().then(() => require("./codemod-transformer.cjs"));
613
- entryResult = transformEntryFile$1({
614
- entryPath,
615
- dryRun
616
- });
617
- if (!entryResult.success) {
618
- console.error(`\n❌ ${entryResult.message}`);
619
- if (entryResult.error) console.error(` Error: ${entryResult.error}`);
620
- }
621
- }
622
507
  if (dryRun) {
623
- if (result.modified) {
624
- console.log("\n📄 Preview of config changes:");
625
- console.log("─".repeat(60));
626
- console.log(result.code);
627
- console.log("─".repeat(60));
628
- } else console.log(`\n✅ Config: ${result.message}`);
629
- if (entryPath && entryResult) {
630
- if (entryResult.modified) {
631
- console.log("\n📄 Preview of entry file changes:");
632
- console.log("─".repeat(60));
633
- console.log(entryResult.code);
634
- console.log("─".repeat(60));
635
- } else if (entryResult.success) console.log(`\n✅ Entry file: ${entryResult.message}`);
636
- }
637
- console.log("\n💡 Run without --dry-run to apply these changes");
508
+ showPreview(result);
638
509
  process.exit(0);
639
510
  }
640
511
  const installed = installPackage("@mcpc-tech/unplugin-dev-inspector-mcp", true);
512
+ if (!installed) console.warn("⚠️ Package installation failed, but setup will continue with config transformation.");
641
513
  if (result.modified) {
642
- (0, fs.writeFileSync)(targetConfig.path, result.code, "utf-8");
514
+ (0, fs.writeFileSync)(selectedConfigPath, result.code, "utf-8");
643
515
  console.log(`\n✅ ${result.message}`);
644
516
  } else console.log(`\n✅ ${result.message}`);
645
- if (entryPath && entryResult && entryResult.success) if (entryResult.modified) {
646
- (0, fs.writeFileSync)(entryPath, entryResult.code, "utf-8");
647
- console.log(`✅ ${entryResult.message}`);
648
- } else console.log(`✅ ${entryResult.message}`);
649
- console.log(`\n📝 Next steps:`);
650
- console.log(` 1. Review the changes in ${targetConfig.path}${entryPath ? ` and ${entryPath}` : ""} and package.json`);
651
- if (!installed) {
652
- console.log(` 2. Install the package: npm i -D @mcpc-tech/unplugin-dev-inspector-mcp`);
653
- console.log(` 3. Start your dev server`);
654
- } else console.log(` 2. Start your dev server`);
655
- if (targetConfig.bundler === "vite") {
656
- console.log(`\n⚠️ Important: DevInspector should be placed BEFORE framework plugins (react/vue/svelte)`);
657
- console.log(` Please verify the plugin order in your config.`);
517
+ if (selectedBundler === "nextjs" && entryPath) {
518
+ const layoutPath = (0, path.resolve)(cwd, entryPath);
519
+ if (!(0, fs.existsSync)(layoutPath)) {
520
+ console.warn(`\n⚠️ Layout file not found: ${layoutPath}`);
521
+ console.warn(` Skipping layout transformation. Please add <DevInspector /> manually.`);
522
+ } else {
523
+ console.log(`\n🔧 Transforming layout file...`);
524
+ const layoutResult = transformNextLayout((0, fs.readFileSync)(layoutPath, "utf-8"));
525
+ if (layoutResult.success && layoutResult.modified) {
526
+ if (!dryRun) (0, fs.writeFileSync)(layoutPath, layoutResult.code, "utf-8");
527
+ console.log(`✅ ${layoutResult.message}`);
528
+ } else if (!layoutResult.success) {
529
+ console.warn(`⚠️ ${layoutResult.message}`);
530
+ if (layoutResult.error) console.warn(` ${layoutResult.error}`);
531
+ } else console.log(`ℹ️ ${layoutResult.message}`);
532
+ }
658
533
  }
534
+ printNextSteps(selectedConfigPath, entryPath, selectedBundler, installed);
659
535
  } catch (error) {
660
536
  console.error("❌ Setup failed:", error instanceof Error ? error.message : error);
661
537
  process.exit(1);
662
538
  }
663
539
  }
540
+ function printHelp() {
541
+ console.log(`
542
+ Usage:
543
+ npx @mcpc-tech/unplugin-dev-inspector-mcp setup [options]
544
+
545
+ Options:
546
+ --config <path> Specify config file path (auto-detect by default)
547
+ --entry <path> Specify entry file path to add import (optional)
548
+ --bundler <type> Specify bundler type: vite, webpack, nextjs
549
+ --dry-run Preview changes without applying them
550
+ --help, -h Show this help message
551
+ `);
552
+ }
553
+ function showPreview(result) {
554
+ if (result.modified) {
555
+ console.log("\n📄 Preview of config changes:");
556
+ console.log("─".repeat(60));
557
+ console.log(result.code);
558
+ console.log("─".repeat(60));
559
+ } else console.log(`\n✅ Config: ${result.message}`);
560
+ console.log("\n💡 Run without --dry-run to apply these changes");
561
+ }
562
+ function printNextSteps(configPath, entryPath, bundler, installed) {
563
+ console.log(`\n📝 Next steps:`);
564
+ if (!installed) {
565
+ console.log(` 1. Install the package manually: npm install -D @mcpc-tech/unplugin-dev-inspector-mcp`);
566
+ console.log(` 2. Review the changes in ${configPath} and package.json`);
567
+ console.log(` 3. Start your dev server`);
568
+ } else {
569
+ console.log(` 1. Review the changes in ${configPath} and package.json`);
570
+ console.log(` 2. Start your dev server`);
571
+ }
572
+ if (entryPath) {
573
+ console.log(`\n💡 Entry file specified: ${entryPath}`);
574
+ console.log(` This has been added to your config with autoInject: false`);
575
+ console.log(` No modifications were made to your entry file`);
576
+ }
577
+ if (bundler === "nextjs") {
578
+ console.log(`\n⚠️ Next.js 16+ uses Turbopack by default`);
579
+ console.log(` Turbopack mode requires running a standalone MCP server:`);
580
+ console.log(` `);
581
+ console.log(` Terminal 1: npm run dev`);
582
+ console.log(` Terminal 2: npx dev-inspector-server`);
583
+ console.log(` `);
584
+ console.log(` Or use concurrently: npx concurrently "npm run dev" "npx dev-inspector-server"`);
585
+ console.log(` (Webpack mode works without standalone server: npm run dev -- --webpack)`);
586
+ }
587
+ if (bundler === "vite") {
588
+ console.log(`\n⚠️ Important: DevInspector should be placed BEFORE framework plugins (react/vue/svelte)`);
589
+ console.log(` Please verify the plugin order in your config.`);
590
+ }
591
+ }
592
+
593
+ //#endregion
594
+ //#region src/utils/standalone-server.ts
595
+ var StandaloneServer = class {
596
+ server;
597
+ middlewares = [];
598
+ port = 0;
599
+ host = "localhost";
600
+ stack = [];
601
+ constructor() {
602
+ this.server = node_http.default.createServer(async (req, res) => {
603
+ let index = 0;
604
+ const next = async () => {
605
+ if (index >= this.middlewares.length) {
606
+ if (!res.writableEnded) {
607
+ res.statusCode = 404;
608
+ res.end("Not Found");
609
+ }
610
+ return;
611
+ }
612
+ const layer = this.middlewares[index++];
613
+ if ((req.url || "/").startsWith(layer.route)) try {
614
+ const originalUrl = req.url;
615
+ if (layer.route !== "/" && req.url) {}
616
+ await layer.handle(req, res, next);
617
+ if (layer.route !== "/") req.url = originalUrl;
618
+ } catch (error) {
619
+ console.error("Middleware error:", error);
620
+ if (!res.writableEnded) {
621
+ res.statusCode = 500;
622
+ res.end("Internal Server Error");
623
+ }
624
+ }
625
+ else next();
626
+ };
627
+ await next();
628
+ });
629
+ }
630
+ use(routeOrHandle, handle) {
631
+ let route = "/";
632
+ let handler;
633
+ if (typeof routeOrHandle === "string") {
634
+ route = routeOrHandle;
635
+ if (!handle) throw new Error("Handler is required when route is provided");
636
+ handler = handle;
637
+ } else handler = routeOrHandle;
638
+ this.middlewares.push({
639
+ route,
640
+ handle: handler
641
+ });
642
+ return this;
643
+ }
644
+ listen(...args) {
645
+ return this.server.listen(...args);
646
+ }
647
+ async start(options = {}) {
648
+ const startPort = options.port || 8888;
649
+ this.host = options.host || "localhost";
650
+ for (let port = startPort; port < startPort + 100; port++) try {
651
+ await new Promise((resolve$2, reject) => {
652
+ this.server.listen(port, this.host, () => {
653
+ this.port = port;
654
+ resolve$2();
655
+ });
656
+ this.server.on("error", (err) => {
657
+ if (err.code === "EADDRINUSE") {
658
+ this.server.close();
659
+ reject(err);
660
+ } else reject(err);
661
+ });
662
+ });
663
+ return {
664
+ host: this.host,
665
+ port: this.port
666
+ };
667
+ } catch (error) {
668
+ if (error.code !== "EADDRINUSE") throw error;
669
+ }
670
+ throw new Error(`Could not find a free port starting from ${startPort}`);
671
+ }
672
+ close() {
673
+ this.server.close();
674
+ }
675
+ };
676
+ let globalServer = null;
677
+ async function startStandaloneServer(options = {}) {
678
+ if (globalServer) return {
679
+ server: globalServer,
680
+ host: globalServer.host,
681
+ port: globalServer.port,
682
+ isNew: false
683
+ };
684
+ globalServer = new StandaloneServer();
685
+ const { host, port } = await globalServer.start(options);
686
+ return {
687
+ server: globalServer,
688
+ host,
689
+ port,
690
+ isNew: true
691
+ };
692
+ }
693
+
694
+ //#endregion
695
+ //#region src/commands/server/index.ts
696
+ require_config_updater.init_helpers();
664
697
  async function runServerCommand() {
665
- const args = process.argv.slice(2);
698
+ const args = process.argv.slice(3);
666
699
  let port = 8888;
667
700
  let host = "localhost";
668
701
  for (let i = 0; i < args.length; i++) if (args[i] === "--port" && args[i + 1]) {
@@ -725,6 +758,16 @@ Example:
725
758
  process.exit(1);
726
759
  }
727
760
  }
761
+
762
+ //#endregion
763
+ //#region src/cli.ts
764
+ /**
765
+ * CLI for dev-inspector
766
+ *
767
+ * Commands:
768
+ * setup - Add DevInspector to your bundler config
769
+ * server - Start standalone MCP server (default)
770
+ */
728
771
  async function main() {
729
772
  const command = process.argv[2];
730
773
  if (command === "setup") await runSetupCommand();
@@ -748,9 +791,4 @@ main();
748
791
 
749
792
  //#endregion
750
793
  exports.StandaloneServer = StandaloneServer;
751
- exports.startStandaloneServer = startStandaloneServer;
752
- exports.transformConfig = transformConfig;
753
- exports.transformEntryFile = transformEntryFile;
754
- exports.transformNextConfig = transformNextConfig;
755
- exports.transformViteConfig = transformViteConfig;
756
- exports.transformWebpackConfig = transformWebpackConfig;
794
+ exports.startStandaloneServer = startStandaloneServer;