@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.
- package/README.md +19 -1
- package/client/dist/inspector.iife.js +1 -5
- package/dist/cli.cjs +180 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +177 -0
- package/dist/config-updater.cjs +88171 -0
- package/dist/config-updater.js +88183 -0
- package/dist/index.cjs +79 -88252
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +71 -88279
- package/dist/loader.cjs +45 -0
- package/dist/loader.d.cts +15 -0
- package/dist/loader.d.ts +15 -0
- package/dist/loader.js +42 -0
- package/dist/standalone-server.cjs +2 -102
- package/dist/standalone-server.js +1 -100
- package/dist/vue-transform.cjs +140 -0
- package/dist/vue-transform.js +126 -0
- package/package.json +15 -1
package/dist/loader.cjs
ADDED
|
@@ -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 };
|
package/dist/loader.d.ts
ADDED
|
@@ -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
|
-
|
|
3
|
-
node_http = require_chunk.__toESM(node_http);
|
|
2
|
+
const require_cli = require('./cli.cjs');
|
|
4
3
|
|
|
5
|
-
|
|
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
|
|
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.
|
|
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",
|