capy-mcp 1.0.6 → 1.0.8
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.js +0 -0
- package/dist/component-discovery.d.ts +0 -1
- package/dist/component-discovery.js +5 -8
- package/dist/project.js +61 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -36,14 +36,12 @@ async function scanForComponents(projectRoot, projectFacts) {
|
|
|
36
36
|
...projectFacts.likelyComponentDirs,
|
|
37
37
|
...projectFacts.likelyUiDirs,
|
|
38
38
|
]);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
else {
|
|
44
|
-
// Fallback: scan everything if no dirs were discovered
|
|
45
|
-
patterns = ["**/*.{ts,tsx,js,jsx}"];
|
|
39
|
+
// If no component dirs were discovered, return empty instead of
|
|
40
|
+
// falling back to a repo-wide scan that reads every file's contents.
|
|
41
|
+
if (searchDirs.size === 0) {
|
|
42
|
+
return [];
|
|
46
43
|
}
|
|
44
|
+
const patterns = Array.from(searchDirs).map((dir) => `${dir}/**/*.{ts,tsx,js,jsx}`);
|
|
47
45
|
const sourceFiles = await glob(patterns, {
|
|
48
46
|
cwd: projectRoot,
|
|
49
47
|
nodir: true,
|
|
@@ -72,7 +70,6 @@ async function scanForComponents(projectRoot, projectFacts) {
|
|
|
72
70
|
path: file,
|
|
73
71
|
basename: basename(file).replace(/\.[^.]+$/, ""),
|
|
74
72
|
exports,
|
|
75
|
-
contents,
|
|
76
73
|
});
|
|
77
74
|
}
|
|
78
75
|
}
|
package/dist/project.js
CHANGED
|
@@ -28,14 +28,15 @@ export async function buildProjectFacts(projectRoot, framework) {
|
|
|
28
28
|
],
|
|
29
29
|
});
|
|
30
30
|
const normalized = sourceFiles.map((f) => toPosixPath(f));
|
|
31
|
-
//
|
|
32
|
-
const componentDirs = await findComponentDirs(projectRoot, normalized);
|
|
33
|
-
// Find page/route directories by looking for routing markers
|
|
31
|
+
// Find page/route directories FIRST — we need these to exclude from component dirs
|
|
34
32
|
const pageDirs = findPageDirs(normalized);
|
|
33
|
+
// Scan files for PascalCase exports to find component directories
|
|
34
|
+
// Exclude page dirs so route files don't inflate the component list
|
|
35
|
+
const componentDirs = await findComponentDirs(projectRoot, normalized, pageDirs);
|
|
35
36
|
// Find style files — already dynamic via glob
|
|
36
37
|
const styleFiles = await findStyleFiles(projectRoot);
|
|
37
|
-
// UI dirs =
|
|
38
|
-
const likelyUiDirs = Array.from(new Set([...componentDirs
|
|
38
|
+
// UI dirs = component dirs only (page dirs are routes, not reusable UI)
|
|
39
|
+
const likelyUiDirs = Array.from(new Set([...componentDirs]));
|
|
39
40
|
return {
|
|
40
41
|
framework: framework.kind,
|
|
41
42
|
routingStyle: framework.routingStyle,
|
|
@@ -49,42 +50,45 @@ export async function buildProjectFacts(projectRoot, framework) {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
|
-
* Find component directories by scanning source files for
|
|
53
|
-
*
|
|
54
|
-
*
|
|
53
|
+
* Find component directories by scanning .tsx/.jsx source files for
|
|
54
|
+
* PascalCase exports that look like React components.
|
|
55
|
+
*
|
|
56
|
+
* Key rules:
|
|
57
|
+
* - Only .tsx/.jsx files qualify (plain .ts/.js are utilities, not components)
|
|
58
|
+
* - Files must contain JSX or React signals (not just any PascalCase export)
|
|
59
|
+
* - Page/route directories are excluded (those are routes, not reusable UI)
|
|
55
60
|
*/
|
|
56
|
-
async function findComponentDirs(projectRoot, sourceFiles) {
|
|
61
|
+
async function findComponentDirs(projectRoot, sourceFiles, pageDirs) {
|
|
57
62
|
const dirComponentCount = new Map();
|
|
58
63
|
for (const file of sourceFiles) {
|
|
59
64
|
// Skip files at the root level (no directory)
|
|
60
65
|
if (!file.includes("/"))
|
|
61
66
|
continue;
|
|
67
|
+
// Skip files inside page/route dirs — those are routes, not components
|
|
68
|
+
const dir = dirname(file);
|
|
69
|
+
if (isInsidePageDir(dir, pageDirs))
|
|
70
|
+
continue;
|
|
62
71
|
const ext = file.split(".").pop() ?? "";
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
// ONLY check .tsx and .jsx files — plain .ts/.js are utilities/hooks/config
|
|
73
|
+
if (ext !== "tsx" && ext !== "jsx")
|
|
74
|
+
continue;
|
|
75
|
+
// Skip common non-component page/route files by name
|
|
66
76
|
const fileName = basename(file).replace(/\.[^.]+$/, "");
|
|
67
|
-
|
|
68
|
-
if (!isTsxJsx && !isPascalFileName)
|
|
77
|
+
if (isRouteFileName(fileName))
|
|
69
78
|
continue;
|
|
70
79
|
const contents = await readText(`${projectRoot}/${file}`);
|
|
71
80
|
if (!contents)
|
|
72
81
|
continue;
|
|
82
|
+
// For .tsx/.jsx files, a PascalCase export is sufficient signal —
|
|
83
|
+
// the file extension itself indicates component intent
|
|
73
84
|
if (hasPascalCaseExport(contents)) {
|
|
74
|
-
const dir = dirname(file);
|
|
75
85
|
dirComponentCount.set(dir, (dirComponentCount.get(dir) ?? 0) + 1);
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
if (dirComponentCount.size === 0)
|
|
79
89
|
return [];
|
|
80
|
-
// Collect all dirs that have components
|
|
81
90
|
const allDirs = Array.from(dirComponentCount.keys()).sort();
|
|
82
|
-
|
|
83
|
-
// keep the parent "src/components" (the child is already covered by glob patterns).
|
|
84
|
-
// But also keep the child if the parent has 0 direct components (e.g. parent is
|
|
85
|
-
// just an organizer directory).
|
|
86
|
-
const roots = deduplicateToRoots(allDirs);
|
|
87
|
-
return roots;
|
|
91
|
+
return deduplicateToRoots(allDirs);
|
|
88
92
|
}
|
|
89
93
|
/**
|
|
90
94
|
* Given a sorted list of directory paths, return the root-most directories.
|
|
@@ -135,6 +139,41 @@ function findPageDirs(sourceFiles) {
|
|
|
135
139
|
function hasPascalCaseExport(contents) {
|
|
136
140
|
return /export\s+(?:default\s+)?(?:function|const|class)\s+[A-Z][A-Za-z0-9_]*/.test(contents);
|
|
137
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Check if a file looks like a React component (contains JSX or React imports).
|
|
144
|
+
* This prevents pure utility files with PascalCase class exports from being
|
|
145
|
+
* treated as components.
|
|
146
|
+
*/
|
|
147
|
+
function looksLikeComponent(contents) {
|
|
148
|
+
// Contains JSX-like syntax: <Component, <div, <>, etc.
|
|
149
|
+
if (/<[A-Za-z][A-Za-z0-9.]*[\s/>]/.test(contents))
|
|
150
|
+
return true;
|
|
151
|
+
if (/<>/.test(contents))
|
|
152
|
+
return true;
|
|
153
|
+
// Imports React or uses React APIs
|
|
154
|
+
if (/from\s+['"]react['"]/.test(contents))
|
|
155
|
+
return true;
|
|
156
|
+
if (/React\.createElement/.test(contents))
|
|
157
|
+
return true;
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if a directory is inside (or equal to) any of the known page dirs.
|
|
162
|
+
*/
|
|
163
|
+
function isInsidePageDir(dir, pageDirs) {
|
|
164
|
+
return pageDirs.some((pageDir) => dir === pageDir || dir.startsWith(pageDir + "/"));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Common route/page filenames that should not be treated as components.
|
|
168
|
+
*/
|
|
169
|
+
function isRouteFileName(name) {
|
|
170
|
+
const routeNames = new Set([
|
|
171
|
+
"layout", "page", "loading", "error", "not-found",
|
|
172
|
+
"template", "default", "route", "middleware",
|
|
173
|
+
"_app", "_document", "_error",
|
|
174
|
+
]);
|
|
175
|
+
return routeNames.has(name);
|
|
176
|
+
}
|
|
138
177
|
async function findStyleFiles(projectRoot) {
|
|
139
178
|
const files = await glob(["**/*.{css,scss,sass,less}"], {
|
|
140
179
|
cwd: projectRoot,
|
package/package.json
CHANGED