@mcpc-tech/unplugin-dev-inspector-mcp 0.0.10 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_chunk = require('./chunk.cjs');
3
+ const require_vue_transform = require('./vue-transform.cjs');
4
+
5
+ //#region src/loader.ts
6
+ function devInspectorLoader(source) {
7
+ if (!((this.getOptions() || {}).enabled ?? process.env.NODE_ENV !== "production")) return source;
8
+ const resourcePath = this.resourcePath;
9
+ if (resourcePath.match(/\.(jsx|tsx)$/)) try {
10
+ const result = require_vue_transform.transformJSX({
11
+ code: source,
12
+ id: resourcePath
13
+ });
14
+ if (result) {
15
+ if (result.map && this.sourceMap) {
16
+ this.callback(null, result.code, result.map);
17
+ return "";
18
+ }
19
+ return result.code;
20
+ }
21
+ } catch (error) {
22
+ console.error(`[dev-inspector-loader] Failed to transform ${resourcePath}:`, error);
23
+ }
24
+ if (resourcePath.match(/\.vue$/)) try {
25
+ const result = require_vue_transform.compileVue({
26
+ code: source,
27
+ id: resourcePath
28
+ });
29
+ if (result) {
30
+ if (result.map && this.sourceMap) {
31
+ this.callback(null, result.code, result.map);
32
+ return "";
33
+ }
34
+ return result.code;
35
+ }
36
+ } catch (error) {
37
+ console.error(`[dev-inspector-loader] Failed to transform ${resourcePath}:`, error);
38
+ }
39
+ return source;
40
+ }
41
+ const raw = false;
42
+
43
+ //#endregion
44
+ exports.default = devInspectorLoader;
45
+ exports.raw = raw;
@@ -0,0 +1,15 @@
1
+ import { LoaderContext } from "webpack";
2
+
3
+ //#region src/loader.d.ts
4
+
5
+ interface DevInspectorLoaderOptions {
6
+ /**
7
+ * Enable/disable the loader
8
+ * @default true in development, false in production
9
+ */
10
+ enabled?: boolean;
11
+ }
12
+ declare function devInspectorLoader(this: LoaderContext<DevInspectorLoaderOptions>, source: string): string;
13
+ declare const raw = false;
14
+ //#endregion
15
+ export { DevInspectorLoaderOptions, devInspectorLoader as default, raw };
@@ -0,0 +1,15 @@
1
+ import { LoaderContext } from "webpack";
2
+
3
+ //#region src/loader.d.ts
4
+
5
+ interface DevInspectorLoaderOptions {
6
+ /**
7
+ * Enable/disable the loader
8
+ * @default true in development, false in production
9
+ */
10
+ enabled?: boolean;
11
+ }
12
+ declare function devInspectorLoader(this: LoaderContext<DevInspectorLoaderOptions>, source: string): string;
13
+ declare const raw = false;
14
+ //#endregion
15
+ export { DevInspectorLoaderOptions, devInspectorLoader as default, raw };
package/dist/loader.js ADDED
@@ -0,0 +1,42 @@
1
+ import { n as transformJSX, t as compileVue } from "./vue-transform.js";
2
+
3
+ //#region src/loader.ts
4
+ function devInspectorLoader(source) {
5
+ if (!((this.getOptions() || {}).enabled ?? process.env.NODE_ENV !== "production")) return source;
6
+ const resourcePath = this.resourcePath;
7
+ if (resourcePath.match(/\.(jsx|tsx)$/)) try {
8
+ const result = transformJSX({
9
+ code: source,
10
+ id: resourcePath
11
+ });
12
+ if (result) {
13
+ if (result.map && this.sourceMap) {
14
+ this.callback(null, result.code, result.map);
15
+ return "";
16
+ }
17
+ return result.code;
18
+ }
19
+ } catch (error) {
20
+ console.error(`[dev-inspector-loader] Failed to transform ${resourcePath}:`, error);
21
+ }
22
+ if (resourcePath.match(/\.vue$/)) try {
23
+ const result = compileVue({
24
+ code: source,
25
+ id: resourcePath
26
+ });
27
+ if (result) {
28
+ if (result.map && this.sourceMap) {
29
+ this.callback(null, result.code, result.map);
30
+ return "";
31
+ }
32
+ return result.code;
33
+ }
34
+ } catch (error) {
35
+ console.error(`[dev-inspector-loader] Failed to transform ${resourcePath}:`, error);
36
+ }
37
+ return source;
38
+ }
39
+ const raw = false;
40
+
41
+ //#endregion
42
+ export { devInspectorLoader as default, raw };
@@ -1,104 +1,4 @@
1
1
  const require_chunk = require('./chunk.cjs');
2
- let node_http = require("node:http");
3
- node_http = require_chunk.__toESM(node_http);
2
+ const require_cli = require('./cli.cjs');
4
3
 
5
- //#region src/utils/standalone-server.ts
6
- var StandaloneServer = class {
7
- server;
8
- middlewares = [];
9
- port = 0;
10
- host = "localhost";
11
- stack = [];
12
- constructor() {
13
- this.server = node_http.default.createServer(async (req, res) => {
14
- let index = 0;
15
- const next = async () => {
16
- if (index >= this.middlewares.length) {
17
- if (!res.writableEnded) {
18
- res.statusCode = 404;
19
- res.end("Not Found");
20
- }
21
- return;
22
- }
23
- const layer = this.middlewares[index++];
24
- if ((req.url || "/").startsWith(layer.route)) try {
25
- const originalUrl = req.url;
26
- if (layer.route !== "/" && req.url) {}
27
- await layer.handle(req, res, next);
28
- if (layer.route !== "/") req.url = originalUrl;
29
- } catch (error) {
30
- console.error("Middleware error:", error);
31
- if (!res.writableEnded) {
32
- res.statusCode = 500;
33
- res.end("Internal Server Error");
34
- }
35
- }
36
- else next();
37
- };
38
- await next();
39
- });
40
- }
41
- use(routeOrHandle, handle) {
42
- let route = "/";
43
- let handler;
44
- if (typeof routeOrHandle === "string") {
45
- route = routeOrHandle;
46
- if (!handle) throw new Error("Handler is required when route is provided");
47
- handler = handle;
48
- } else handler = routeOrHandle;
49
- this.middlewares.push({
50
- route,
51
- handle: handler
52
- });
53
- return this;
54
- }
55
- listen(...args) {
56
- return this.server.listen(...args);
57
- }
58
- async start(options = {}) {
59
- const startPort = options.port || 8888;
60
- this.host = options.host || "localhost";
61
- for (let port = startPort; port < startPort + 100; port++) try {
62
- await new Promise((resolve, reject) => {
63
- this.server.listen(port, this.host, () => {
64
- this.port = port;
65
- resolve();
66
- });
67
- this.server.on("error", (err) => {
68
- if (err.code === "EADDRINUSE") {
69
- this.server.close();
70
- reject(err);
71
- } else reject(err);
72
- });
73
- });
74
- return {
75
- host: this.host,
76
- port: this.port
77
- };
78
- } catch (error) {
79
- if (error.code !== "EADDRINUSE") throw error;
80
- }
81
- throw new Error(`Could not find a free port starting from ${startPort}`);
82
- }
83
- close() {
84
- this.server.close();
85
- }
86
- };
87
- let globalServer = null;
88
- async function startStandaloneServer(options = {}) {
89
- if (globalServer) return {
90
- server: globalServer,
91
- host: globalServer.host,
92
- port: globalServer.port
93
- };
94
- globalServer = new StandaloneServer();
95
- const { host, port } = await globalServer.start(options);
96
- return {
97
- server: globalServer,
98
- host,
99
- port
100
- };
101
- }
102
-
103
- //#endregion
104
- exports.startStandaloneServer = startStandaloneServer;
4
+ exports.startStandaloneServer = require_cli.startStandaloneServer;
@@ -1,102 +1,3 @@
1
- import http from "node:http";
1
+ import { n as startStandaloneServer, t as StandaloneServer } from "./cli.js";
2
2
 
3
- //#region src/utils/standalone-server.ts
4
- var StandaloneServer = class {
5
- server;
6
- middlewares = [];
7
- port = 0;
8
- host = "localhost";
9
- stack = [];
10
- constructor() {
11
- this.server = http.createServer(async (req, res) => {
12
- let index = 0;
13
- const next = async () => {
14
- if (index >= this.middlewares.length) {
15
- if (!res.writableEnded) {
16
- res.statusCode = 404;
17
- res.end("Not Found");
18
- }
19
- return;
20
- }
21
- const layer = this.middlewares[index++];
22
- if ((req.url || "/").startsWith(layer.route)) try {
23
- const originalUrl = req.url;
24
- if (layer.route !== "/" && req.url) {}
25
- await layer.handle(req, res, next);
26
- if (layer.route !== "/") req.url = originalUrl;
27
- } catch (error) {
28
- console.error("Middleware error:", error);
29
- if (!res.writableEnded) {
30
- res.statusCode = 500;
31
- res.end("Internal Server Error");
32
- }
33
- }
34
- else next();
35
- };
36
- await next();
37
- });
38
- }
39
- use(routeOrHandle, handle) {
40
- let route = "/";
41
- let handler;
42
- if (typeof routeOrHandle === "string") {
43
- route = routeOrHandle;
44
- if (!handle) throw new Error("Handler is required when route is provided");
45
- handler = handle;
46
- } else handler = routeOrHandle;
47
- this.middlewares.push({
48
- route,
49
- handle: handler
50
- });
51
- return this;
52
- }
53
- listen(...args) {
54
- return this.server.listen(...args);
55
- }
56
- async start(options = {}) {
57
- const startPort = options.port || 8888;
58
- this.host = options.host || "localhost";
59
- for (let port = startPort; port < startPort + 100; port++) try {
60
- await new Promise((resolve, reject) => {
61
- this.server.listen(port, this.host, () => {
62
- this.port = port;
63
- resolve();
64
- });
65
- this.server.on("error", (err) => {
66
- if (err.code === "EADDRINUSE") {
67
- this.server.close();
68
- reject(err);
69
- } else reject(err);
70
- });
71
- });
72
- return {
73
- host: this.host,
74
- port: this.port
75
- };
76
- } catch (error) {
77
- if (error.code !== "EADDRINUSE") throw error;
78
- }
79
- throw new Error(`Could not find a free port starting from ${startPort}`);
80
- }
81
- close() {
82
- this.server.close();
83
- }
84
- };
85
- let globalServer = null;
86
- async function startStandaloneServer(options = {}) {
87
- if (globalServer) return {
88
- server: globalServer,
89
- host: globalServer.host,
90
- port: globalServer.port
91
- };
92
- globalServer = new StandaloneServer();
93
- const { host, port } = await globalServer.start(options);
94
- return {
95
- server: globalServer,
96
- host,
97
- port
98
- };
99
- }
100
-
101
- //#endregion
102
3
  export { startStandaloneServer };
@@ -0,0 +1,140 @@
1
+ const require_chunk = require('./chunk.cjs');
2
+ let node_module = require("node:module");
3
+ let node_path = require("node:path");
4
+ node_path = require_chunk.__toESM(node_path);
5
+ let magic_string = require("magic-string");
6
+ magic_string = require_chunk.__toESM(magic_string);
7
+ let __babel_parser = require("@babel/parser");
8
+ let __vue_compiler_sfc = require("@vue/compiler-sfc");
9
+
10
+ //#region src/compiler/jsx-transform.ts
11
+ const traverse = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("@babel/traverse").default;
12
+ function normalizePath$1(id) {
13
+ return id.split(node_path.default.sep).join("/");
14
+ }
15
+ const DATA_SOURCE_ATTR$1 = "data-source";
16
+ /**
17
+ * Check if JSX element name is a Fragment (React.Fragment, Fragment, or <>)
18
+ */
19
+ function isFragment(name) {
20
+ if (name.type === "JSXIdentifier") return name.name === "Fragment";
21
+ if (name.type === "JSXMemberExpression") {
22
+ const object = name.object;
23
+ const property = name.property;
24
+ return object.type === "JSXIdentifier" && object.name === "React" && property.type === "JSXIdentifier" && property.name === "Fragment";
25
+ }
26
+ return false;
27
+ }
28
+ /**
29
+ * Transform JSX/TSX code to inject data-source attributes for dev inspection.
30
+ *
31
+ * Handles:
32
+ * - Regular components: <Div>, <Button>
33
+ * - Member expressions: <Foo.Bar>, <Layout.Content>
34
+ * - Generic components: <Table<T>>, <Select<Option>>
35
+ * - Namespaced: <svg:rect>
36
+ *
37
+ * Skips:
38
+ * - Fragments: <>, <Fragment>, <React.Fragment>
39
+ * - Elements that already have data-source
40
+ * - Parse errors (returns null gracefully)
41
+ */
42
+ function transformJSX({ code, id }) {
43
+ if (!code.includes("<")) return null;
44
+ let ast;
45
+ try {
46
+ ast = (0, __babel_parser.parse)(code, {
47
+ sourceType: "module",
48
+ plugins: [
49
+ "jsx",
50
+ "typescript",
51
+ "decorators-legacy",
52
+ "classProperties",
53
+ "importMeta"
54
+ ],
55
+ errorRecovery: true
56
+ });
57
+ } catch {
58
+ return null;
59
+ }
60
+ const relativePath = normalizePath$1(node_path.default.relative(process.cwd(), id));
61
+ const s = new magic_string.default(code);
62
+ let hasModifications = false;
63
+ try {
64
+ traverse(ast, { JSXOpeningElement(path$2) {
65
+ try {
66
+ const node = path$2.node;
67
+ if (!node.name || isFragment(node.name)) return;
68
+ if (!node.loc?.start || !node.name.end) return;
69
+ if (node.attributes?.some((attr$1) => attr$1.type === "JSXAttribute" && attr$1.name?.type === "JSXIdentifier" && attr$1.name.name === DATA_SOURCE_ATTR$1)) return;
70
+ const { line, column } = node.loc.start;
71
+ const sourceValue = `${relativePath}:${line}:${column}`;
72
+ let insertPos;
73
+ if (node.typeParameters?.end) insertPos = node.typeParameters.end;
74
+ else if (node.name.end) insertPos = node.name.end;
75
+ else return;
76
+ if (insertPos < 0 || insertPos > code.length) return;
77
+ const attr = ` ${DATA_SOURCE_ATTR$1}="${sourceValue}"`;
78
+ s.appendLeft(insertPos, attr);
79
+ hasModifications = true;
80
+ } catch {}
81
+ } });
82
+ } catch {
83
+ return null;
84
+ }
85
+ if (!hasModifications) return null;
86
+ return {
87
+ code: s.toString(),
88
+ map: s.generateMap({ hires: true })
89
+ };
90
+ }
91
+
92
+ //#endregion
93
+ //#region src/compiler/vue-transform.ts
94
+ function normalizePath(id) {
95
+ return id.split(node_path.default.sep).join("/");
96
+ }
97
+ const DATA_SOURCE_ATTR = "data-source";
98
+ function compileVue({ code, id }) {
99
+ const relativePath = normalizePath(node_path.default.relative(process.cwd(), id));
100
+ const { descriptor } = (0, __vue_compiler_sfc.parse)(code, {
101
+ filename: id,
102
+ sourceMap: true
103
+ });
104
+ if (!descriptor.template || !descriptor.template.ast) return null;
105
+ const s = new magic_string.default(code);
106
+ let hasModifications = false;
107
+ function traverse$1(node) {
108
+ if (node.type === 1) {
109
+ if (!node.props.some((prop) => prop.type === 6 && prop.name === DATA_SOURCE_ATTR)) {
110
+ const { line, column } = node.loc.start;
111
+ const sourceValue = `${relativePath}:${line}:${column}`;
112
+ const tagName = node.tag;
113
+ const insertPos = node.loc.start.offset + 1 + tagName.length;
114
+ s.prependLeft(insertPos, ` ${DATA_SOURCE_ATTR}="${sourceValue}"`);
115
+ hasModifications = true;
116
+ }
117
+ }
118
+ if (node.children) node.children.forEach(traverse$1);
119
+ }
120
+ traverse$1(descriptor.template.ast);
121
+ if (!hasModifications) return null;
122
+ return {
123
+ code: s.toString(),
124
+ map: s.generateMap({ hires: true })
125
+ };
126
+ }
127
+
128
+ //#endregion
129
+ Object.defineProperty(exports, 'compileVue', {
130
+ enumerable: true,
131
+ get: function () {
132
+ return compileVue;
133
+ }
134
+ });
135
+ Object.defineProperty(exports, 'transformJSX', {
136
+ enumerable: true,
137
+ get: function () {
138
+ return transformJSX;
139
+ }
140
+ });
@@ -0,0 +1,126 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import MagicString from "magic-string";
4
+ import { parse } from "@babel/parser";
5
+ import { parse as parse$1 } from "@vue/compiler-sfc";
6
+
7
+ //#region src/compiler/jsx-transform.ts
8
+ const traverse = createRequire(import.meta.url)("@babel/traverse").default;
9
+ function normalizePath$1(id) {
10
+ return id.split(path.sep).join("/");
11
+ }
12
+ const DATA_SOURCE_ATTR$1 = "data-source";
13
+ /**
14
+ * Check if JSX element name is a Fragment (React.Fragment, Fragment, or <>)
15
+ */
16
+ function isFragment(name) {
17
+ if (name.type === "JSXIdentifier") return name.name === "Fragment";
18
+ if (name.type === "JSXMemberExpression") {
19
+ const object = name.object;
20
+ const property = name.property;
21
+ return object.type === "JSXIdentifier" && object.name === "React" && property.type === "JSXIdentifier" && property.name === "Fragment";
22
+ }
23
+ return false;
24
+ }
25
+ /**
26
+ * Transform JSX/TSX code to inject data-source attributes for dev inspection.
27
+ *
28
+ * Handles:
29
+ * - Regular components: <Div>, <Button>
30
+ * - Member expressions: <Foo.Bar>, <Layout.Content>
31
+ * - Generic components: <Table<T>>, <Select<Option>>
32
+ * - Namespaced: <svg:rect>
33
+ *
34
+ * Skips:
35
+ * - Fragments: <>, <Fragment>, <React.Fragment>
36
+ * - Elements that already have data-source
37
+ * - Parse errors (returns null gracefully)
38
+ */
39
+ function transformJSX({ code, id }) {
40
+ if (!code.includes("<")) return null;
41
+ let ast;
42
+ try {
43
+ ast = parse(code, {
44
+ sourceType: "module",
45
+ plugins: [
46
+ "jsx",
47
+ "typescript",
48
+ "decorators-legacy",
49
+ "classProperties",
50
+ "importMeta"
51
+ ],
52
+ errorRecovery: true
53
+ });
54
+ } catch {
55
+ return null;
56
+ }
57
+ const relativePath = normalizePath$1(path.relative(process.cwd(), id));
58
+ const s = new MagicString(code);
59
+ let hasModifications = false;
60
+ try {
61
+ traverse(ast, { JSXOpeningElement(path$1) {
62
+ try {
63
+ const node = path$1.node;
64
+ if (!node.name || isFragment(node.name)) return;
65
+ if (!node.loc?.start || !node.name.end) return;
66
+ if (node.attributes?.some((attr$1) => attr$1.type === "JSXAttribute" && attr$1.name?.type === "JSXIdentifier" && attr$1.name.name === DATA_SOURCE_ATTR$1)) return;
67
+ const { line, column } = node.loc.start;
68
+ const sourceValue = `${relativePath}:${line}:${column}`;
69
+ let insertPos;
70
+ if (node.typeParameters?.end) insertPos = node.typeParameters.end;
71
+ else if (node.name.end) insertPos = node.name.end;
72
+ else return;
73
+ if (insertPos < 0 || insertPos > code.length) return;
74
+ const attr = ` ${DATA_SOURCE_ATTR$1}="${sourceValue}"`;
75
+ s.appendLeft(insertPos, attr);
76
+ hasModifications = true;
77
+ } catch {}
78
+ } });
79
+ } catch {
80
+ return null;
81
+ }
82
+ if (!hasModifications) return null;
83
+ return {
84
+ code: s.toString(),
85
+ map: s.generateMap({ hires: true })
86
+ };
87
+ }
88
+
89
+ //#endregion
90
+ //#region src/compiler/vue-transform.ts
91
+ function normalizePath(id) {
92
+ return id.split(path.sep).join("/");
93
+ }
94
+ const DATA_SOURCE_ATTR = "data-source";
95
+ function compileVue({ code, id }) {
96
+ const relativePath = normalizePath(path.relative(process.cwd(), id));
97
+ const { descriptor } = parse$1(code, {
98
+ filename: id,
99
+ sourceMap: true
100
+ });
101
+ if (!descriptor.template || !descriptor.template.ast) return null;
102
+ const s = new MagicString(code);
103
+ let hasModifications = false;
104
+ function traverse$1(node) {
105
+ if (node.type === 1) {
106
+ if (!node.props.some((prop) => prop.type === 6 && prop.name === DATA_SOURCE_ATTR)) {
107
+ const { line, column } = node.loc.start;
108
+ const sourceValue = `${relativePath}:${line}:${column}`;
109
+ const tagName = node.tag;
110
+ const insertPos = node.loc.start.offset + 1 + tagName.length;
111
+ s.prependLeft(insertPos, ` ${DATA_SOURCE_ATTR}="${sourceValue}"`);
112
+ hasModifications = true;
113
+ }
114
+ }
115
+ if (node.children) node.children.forEach(traverse$1);
116
+ }
117
+ traverse$1(descriptor.template.ast);
118
+ if (!hasModifications) return null;
119
+ return {
120
+ code: s.toString(),
121
+ map: s.generateMap({ hires: true })
122
+ };
123
+ }
124
+
125
+ //#endregion
126
+ export { transformJSX as n, compileVue as t };
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@mcpc-tech/unplugin-dev-inspector-mcp",
3
- "version": "0.0.10",
3
+ "version": "0.0.13",
4
4
  "description": "Universal dev inspector plugin for React/Vue - inspect component sources and API calls in any bundler",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "bin": {
8
+ "dev-inspector-server": "./dist/cli.js"
9
+ },
7
10
  "files": [
8
11
  "dist",
9
12
  "client/dist",
@@ -36,6 +39,16 @@
36
39
  "./client": {
37
40
  "types": "./client.d.ts"
38
41
  },
42
+ "./loader": {
43
+ "import": {
44
+ "types": "./dist/loader.d.ts",
45
+ "default": "./dist/loader.js"
46
+ },
47
+ "require": {
48
+ "types": "./dist/loader.d.cts",
49
+ "default": "./dist/loader.cjs"
50
+ }
51
+ },
39
52
  "./package.json": "./package.json"
40
53
  },
41
54
  "scripts": {
@@ -98,6 +111,7 @@
98
111
  "@radix-ui/react-slot": "^1.2.3",
99
112
  "@radix-ui/react-tooltip": "^1.2.8",
100
113
  "@radix-ui/react-use-controllable-state": "^1.2.2",
114
+ "@radix-ui/react-visually-hidden": "^1.2.4",
101
115
  "@vue/compiler-sfc": "^3.5.24",
102
116
  "@xyflow/react": "^12.9.3",
103
117
  "ai": "^5.0.95",