jsc-typescript-ast-mcp 1.1.0 → 1.1.1
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.
|
@@ -1,16 +1,52 @@
|
|
|
1
|
-
import { SyntaxKind } from 'ts-morph';
|
|
2
|
-
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
const isComponentVariableDeclaration = (node) => {
|
|
3
|
+
if (!Node.isVariableDeclaration(node)) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
const initializer = node.getInitializer();
|
|
7
|
+
if (!initializer) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return (initializer.getKind() === SyntaxKind.ArrowFunction ||
|
|
11
|
+
initializer.getKind() === SyntaxKind.FunctionExpression);
|
|
12
|
+
};
|
|
13
|
+
const isComponentDeclaration = (node) => {
|
|
14
|
+
if (Node.isFunctionDeclaration(node)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return isComponentVariableDeclaration(node);
|
|
18
|
+
};
|
|
19
|
+
const getFirstExportedComponent = (sourceFile) => {
|
|
20
|
+
const exportedDeclarations = sourceFile.getExportedDeclarations();
|
|
21
|
+
for (const declarations of exportedDeclarations.values()) {
|
|
22
|
+
const componentDeclaration = declarations.find(isComponentDeclaration);
|
|
23
|
+
if (componentDeclaration) {
|
|
24
|
+
return componentDeclaration;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
3
29
|
export const getComponent = (sourceFile) => {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
|
|
30
|
+
const defaultExportDeclaration = sourceFile
|
|
31
|
+
.getDefaultExportSymbol()
|
|
32
|
+
?.getDeclarations()
|
|
33
|
+
?.find(isComponentDeclaration);
|
|
34
|
+
if (defaultExportDeclaration) {
|
|
35
|
+
return defaultExportDeclaration;
|
|
36
|
+
}
|
|
37
|
+
const firstExportedComponent = getFirstExportedComponent(sourceFile);
|
|
38
|
+
if (firstExportedComponent) {
|
|
39
|
+
return firstExportedComponent;
|
|
40
|
+
}
|
|
41
|
+
const localVariableComponent = sourceFile
|
|
42
|
+
.getVariableDeclarations()
|
|
43
|
+
.find(isComponentVariableDeclaration);
|
|
44
|
+
if (localVariableComponent) {
|
|
45
|
+
return localVariableComponent;
|
|
46
|
+
}
|
|
47
|
+
const localFunctionComponent = sourceFile.getFunctions()[0];
|
|
48
|
+
if (localFunctionComponent) {
|
|
49
|
+
return localFunctionComponent;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`Component not found in ${sourceFile.getFilePath()}`);
|
|
16
52
|
};
|
|
@@ -91,19 +91,64 @@ const resolveComponentFile = (node) => {
|
|
|
91
91
|
const defaultImport = imp.getDefaultImport();
|
|
92
92
|
if (defaultImport?.getText() === tagName) {
|
|
93
93
|
const file = imp.getModuleSpecifierSourceFile();
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
if (!file || file.getFilePath().includes('/node_modules/')) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const component = getComponent(file);
|
|
99
|
+
return component ?? null;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
96
104
|
}
|
|
97
105
|
for (const n of named) {
|
|
98
106
|
if (n.getName() === tagName) {
|
|
99
107
|
const file = imp.getModuleSpecifierSourceFile();
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
if (!file || file.getFilePath().includes('/node_modules/')) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const component = getComponent(file);
|
|
113
|
+
return component ?? null;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
return null;
|
|
106
122
|
};
|
|
123
|
+
const resolveLocalComponentFilePath = (node) => {
|
|
124
|
+
const tagName = getTagName(node);
|
|
125
|
+
const sourceFile = node.getSourceFile();
|
|
126
|
+
for (const imp of sourceFile.getImportDeclarations()) {
|
|
127
|
+
const matchesDefaultImport = imp.getDefaultImport()?.getText() === tagName;
|
|
128
|
+
const matchesNamedImport = imp
|
|
129
|
+
.getNamedImports()
|
|
130
|
+
.some((namedImport) => namedImport.getName() === tagName);
|
|
131
|
+
if (!matchesDefaultImport && !matchesNamedImport) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
135
|
+
const isLocalImport = moduleSpecifier.startsWith('.') || moduleSpecifier.startsWith('/');
|
|
136
|
+
if (!isLocalImport) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const importedSourceFile = imp.getModuleSpecifierSourceFile();
|
|
140
|
+
if (!importedSourceFile) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const importedFilePath = importedSourceFile.getFilePath();
|
|
144
|
+
if (importedFilePath.includes('/node_modules/')) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return importedFilePath;
|
|
148
|
+
}
|
|
149
|
+
// If there is no matching import, treat it as a component from the current local source file.
|
|
150
|
+
return sourceFile.getFilePath();
|
|
151
|
+
};
|
|
107
152
|
const buildNodeFromJSX = (node, project, options, depth) => {
|
|
108
153
|
const tagName = getTagName(node);
|
|
109
154
|
const isHtml = tagName[0] === tagName[0].toLowerCase();
|
|
@@ -113,7 +158,10 @@ const buildNodeFromJSX = (node, project, options, depth) => {
|
|
|
113
158
|
children: [],
|
|
114
159
|
};
|
|
115
160
|
if (!isHtml) {
|
|
116
|
-
|
|
161
|
+
const localComponentFilePath = resolveLocalComponentFilePath(node);
|
|
162
|
+
if (localComponentFilePath) {
|
|
163
|
+
treeNode.filePath = localComponentFilePath;
|
|
164
|
+
}
|
|
117
165
|
}
|
|
118
166
|
const props = extractPropsFromNode(node);
|
|
119
167
|
if (props) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsc-typescript-ast-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"mcpName": "io.github.jscoobyced/jsc-typescript-ast-mcp",
|
|
5
5
|
"description": "A Model Context Protocol (MCP) server that provides an abstract syntax tree (AST) representation of TypeScript code using the ts-morph library. It allows clients to analyze and manipulate TypeScript code structures, making it easier for AI models to understand and work with TypeScript projects. You can create a JSON representation of your React components, and use it for various purposes such as documentation, code analysis, or even generating new code based on existing components.",
|
|
6
6
|
"main": "dist/index.js",
|