@parseme/cli 0.0.4 → 0.0.6
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 +102 -58
- package/dist/cli/cli.js +25 -34
- package/dist/core/analyzers/ast-analyzer.d.ts +1 -1
- package/dist/core/analyzers/ast-analyzer.js +9 -23
- package/dist/core/analyzers/framework-detector.d.ts +4 -6
- package/dist/core/analyzers/framework-detector.js +154 -165
- package/dist/core/analyzers/pattern-detector.d.ts +2 -2
- package/dist/core/analyzers/pattern-detector.js +93 -13
- package/dist/core/analyzers/project-analyzer.d.ts +1 -1
- package/dist/core/analyzers/project-analyzer.js +7 -18
- package/dist/core/config.d.ts +0 -1
- package/dist/core/config.js +6 -30
- package/dist/core/context-builder.d.ts +1 -3
- package/dist/core/context-builder.js +97 -181
- package/dist/core/generator.js +7 -5
- package/dist/core/types/analyzer-types.d.ts +3 -4
- package/dist/core/types/config-types.d.ts +2 -3
- package/dist/core/types/generator-types.d.ts +0 -2
- package/dist/core/types/project-types.d.ts +1 -1
- package/dist/utils/file-collector.d.ts +23 -0
- package/dist/utils/file-collector.js +61 -0
- package/dist/utils/file-filter.d.ts +30 -0
- package/dist/utils/file-filter.js +99 -0
- package/dist/{core/analyzers/git-analyzer.d.ts → utils/git.d.ts} +1 -1
- package/package.json +9 -6
- /package/dist/{core/analyzers/git-analyzer.js → utils/git.js} +0 -0
- /package/dist/{cli → utils}/prompt.d.ts +0 -0
- /package/dist/{cli → utils}/prompt.js +0 -0
|
@@ -1,175 +1,164 @@
|
|
|
1
1
|
export class FrameworkDetector {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (deps['@nestjs/core'] || deps['@nestjs/common']) {
|
|
6
|
-
return this.detectNestJS(deps);
|
|
7
|
-
}
|
|
8
|
-
if (deps['fastify']) {
|
|
9
|
-
return this.detectFastify(deps);
|
|
10
|
-
}
|
|
11
|
-
if (deps['express']) {
|
|
12
|
-
return this.detectExpress(deps);
|
|
13
|
-
}
|
|
14
|
-
if (deps['koa']) {
|
|
15
|
-
return this.detectKoa(deps);
|
|
16
|
-
}
|
|
17
|
-
if (deps['@hapi/hapi']) {
|
|
18
|
-
return this.detectHapi(deps);
|
|
19
|
-
}
|
|
20
|
-
return {
|
|
21
|
-
name: 'unknown',
|
|
22
|
-
features: [],
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
detectExpress(deps) {
|
|
26
|
-
const features = [];
|
|
27
|
-
// Detect common Express features
|
|
28
|
-
if (deps['express-session']) {
|
|
29
|
-
features.push('sessions');
|
|
30
|
-
}
|
|
31
|
-
if (deps['passport']) {
|
|
32
|
-
features.push('authentication');
|
|
33
|
-
}
|
|
34
|
-
if (deps['express-rate-limit']) {
|
|
35
|
-
features.push('rate-limiting');
|
|
36
|
-
}
|
|
37
|
-
if (deps['helmet']) {
|
|
38
|
-
features.push('security');
|
|
39
|
-
}
|
|
40
|
-
if (deps['cors']) {
|
|
41
|
-
features.push('cors');
|
|
42
|
-
}
|
|
43
|
-
if (deps['body-parser']) {
|
|
44
|
-
features.push('body-parsing');
|
|
45
|
-
}
|
|
46
|
-
if (deps['express-validator']) {
|
|
47
|
-
features.push('validation');
|
|
48
|
-
}
|
|
49
|
-
if (deps['multer']) {
|
|
50
|
-
features.push('file-upload');
|
|
51
|
-
}
|
|
52
|
-
if (deps['express-static']) {
|
|
53
|
-
features.push('static-files');
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
name: 'express',
|
|
57
|
-
version: deps['express'],
|
|
58
|
-
features,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
detectFastify(deps) {
|
|
62
|
-
const features = [];
|
|
63
|
-
if (deps['@fastify/cors']) {
|
|
64
|
-
features.push('cors');
|
|
65
|
-
}
|
|
66
|
-
if (deps['@fastify/helmet']) {
|
|
67
|
-
features.push('security');
|
|
68
|
-
}
|
|
69
|
-
if (deps['@fastify/rate-limit']) {
|
|
70
|
-
features.push('rate-limiting');
|
|
71
|
-
}
|
|
72
|
-
if (deps['@fastify/multipart']) {
|
|
73
|
-
features.push('file-upload');
|
|
74
|
-
}
|
|
75
|
-
if (deps['@fastify/static']) {
|
|
76
|
-
features.push('static-files');
|
|
77
|
-
}
|
|
78
|
-
if (deps['@fastify/jwt']) {
|
|
79
|
-
features.push('jwt');
|
|
80
|
-
}
|
|
81
|
-
if (deps['@fastify/session']) {
|
|
82
|
-
features.push('sessions');
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
name: 'fastify',
|
|
86
|
-
version: deps['fastify'],
|
|
87
|
-
features,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
detectNestJS(deps) {
|
|
91
|
-
const features = [];
|
|
92
|
-
if (deps['@nestjs/typeorm'] || deps['@nestjs/mongoose']) {
|
|
93
|
-
features.push('orm');
|
|
94
|
-
}
|
|
95
|
-
if (deps['@nestjs/passport']) {
|
|
96
|
-
features.push('authentication');
|
|
97
|
-
}
|
|
98
|
-
if (deps['@nestjs/jwt']) {
|
|
99
|
-
features.push('jwt');
|
|
100
|
-
}
|
|
101
|
-
if (deps['@nestjs/swagger']) {
|
|
102
|
-
features.push('swagger');
|
|
103
|
-
}
|
|
104
|
-
if (deps['@nestjs/graphql']) {
|
|
105
|
-
features.push('graphql');
|
|
106
|
-
}
|
|
107
|
-
if (deps['@nestjs/websockets']) {
|
|
108
|
-
features.push('websockets');
|
|
109
|
-
}
|
|
110
|
-
if (deps['@nestjs/microservices']) {
|
|
111
|
-
features.push('microservices');
|
|
112
|
-
}
|
|
113
|
-
if (deps['@nestjs/testing']) {
|
|
114
|
-
features.push('testing');
|
|
115
|
-
}
|
|
116
|
-
// NestJS uses decorators by default
|
|
117
|
-
features.push('decorators', 'dependency-injection', 'modules');
|
|
118
|
-
return {
|
|
2
|
+
frameworks = [
|
|
3
|
+
// Backend frameworks
|
|
4
|
+
{
|
|
119
5
|
name: 'nestjs',
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
6
|
+
detectionKeys: ['@nestjs/core', '@nestjs/common'],
|
|
7
|
+
versionKey: '@nestjs/core',
|
|
8
|
+
builtInFeatures: ['decorators', 'dependency-injection', 'modules'],
|
|
9
|
+
featureMap: {
|
|
10
|
+
'@nestjs/typeorm': 'orm',
|
|
11
|
+
'@nestjs/mongoose': 'orm',
|
|
12
|
+
'@nestjs/passport': 'authentication',
|
|
13
|
+
'@nestjs/jwt': 'jwt',
|
|
14
|
+
'@nestjs/swagger': 'swagger',
|
|
15
|
+
'@nestjs/graphql': 'graphql',
|
|
16
|
+
'@nestjs/websockets': 'websockets',
|
|
17
|
+
'@nestjs/microservices': 'microservices',
|
|
18
|
+
'@nestjs/testing': 'testing',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'fastify',
|
|
23
|
+
detectionKeys: ['fastify'],
|
|
24
|
+
versionKey: 'fastify',
|
|
25
|
+
featureMap: {
|
|
26
|
+
'@fastify/cors': 'cors',
|
|
27
|
+
'@fastify/helmet': 'security',
|
|
28
|
+
'@fastify/rate-limit': 'rate-limiting',
|
|
29
|
+
'@fastify/multipart': 'file-upload',
|
|
30
|
+
'@fastify/static': 'static-files',
|
|
31
|
+
'@fastify/jwt': 'jwt',
|
|
32
|
+
'@fastify/session': 'sessions',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'express',
|
|
37
|
+
detectionKeys: ['express'],
|
|
38
|
+
versionKey: 'express',
|
|
39
|
+
featureMap: {
|
|
40
|
+
'express-session': 'sessions',
|
|
41
|
+
passport: 'authentication',
|
|
42
|
+
'express-rate-limit': 'rate-limiting',
|
|
43
|
+
helmet: 'security',
|
|
44
|
+
cors: 'cors',
|
|
45
|
+
'body-parser': 'body-parsing',
|
|
46
|
+
'express-validator': 'validation',
|
|
47
|
+
multer: 'file-upload',
|
|
48
|
+
'express-static': 'static-files',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
// Fullstack frameworks
|
|
52
|
+
{
|
|
53
|
+
name: 'next.js',
|
|
54
|
+
detectionKeys: ['next'],
|
|
55
|
+
versionKey: 'next',
|
|
56
|
+
builtInFeatures: ['ssr', 'routing', 'api-routes', 'file-based-routing'],
|
|
57
|
+
featureMap: {
|
|
58
|
+
'next-auth': 'authentication',
|
|
59
|
+
'@vercel/analytics': 'analytics',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'nuxt.js',
|
|
64
|
+
detectionKeys: ['nuxt'],
|
|
65
|
+
versionKey: 'nuxt',
|
|
66
|
+
builtInFeatures: ['ssr', 'routing', 'api-routes', 'file-based-routing', 'auto-imports'],
|
|
67
|
+
featureMap: {
|
|
68
|
+
'@nuxt/content': 'content-management',
|
|
69
|
+
'@nuxtjs/auth': 'authentication',
|
|
70
|
+
'@nuxtjs/auth-next': 'authentication',
|
|
71
|
+
'@pinia/nuxt': 'state-management-pinia',
|
|
72
|
+
'@nuxt/image': 'image-optimization',
|
|
73
|
+
'@nuxtjs/tailwindcss': 'tailwind',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
// Frontend frameworks
|
|
77
|
+
{
|
|
78
|
+
name: 'react',
|
|
79
|
+
detectionKeys: ['react', 'react-dom'],
|
|
80
|
+
versionKey: 'react',
|
|
81
|
+
featureMap: {
|
|
82
|
+
'react-router': 'routing',
|
|
83
|
+
'react-router-dom': 'routing',
|
|
84
|
+
redux: 'state-management-redux',
|
|
85
|
+
'@reduxjs/toolkit': 'state-management-redux',
|
|
86
|
+
zustand: 'state-management-zustand',
|
|
87
|
+
'react-query': 'data-fetching',
|
|
88
|
+
'@tanstack/react-query': 'data-fetching',
|
|
89
|
+
'@testing-library/react': 'testing',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'vue',
|
|
94
|
+
detectionKeys: (deps) => !!deps['vue'] || Object.keys(deps).some((dep) => dep.startsWith('@vue/')),
|
|
95
|
+
versionKey: 'vue',
|
|
96
|
+
featureMap: {
|
|
97
|
+
'vue-router': 'routing',
|
|
98
|
+
pinia: 'state-management-pinia',
|
|
99
|
+
vuex: 'state-management-vuex',
|
|
100
|
+
'@vue/test-utils': 'testing',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'angular',
|
|
105
|
+
detectionKeys: ['@angular/core'],
|
|
106
|
+
versionKey: '@angular/core',
|
|
107
|
+
builtInFeatures: ['decorators', 'dependency-injection', 'typescript'],
|
|
108
|
+
featureMap: {
|
|
109
|
+
'@angular/router': 'routing',
|
|
110
|
+
'@angular/forms': 'forms',
|
|
111
|
+
'@angular/common/http': 'http-client',
|
|
112
|
+
'@angular/common': 'http-client',
|
|
113
|
+
'@ngrx/store': 'state-management-ngrx',
|
|
114
|
+
'@angular/material': 'material-design',
|
|
115
|
+
'@angular/animations': 'animations',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'svelte',
|
|
120
|
+
detectionKeys: (deps) => !!deps['svelte'] || Object.keys(deps).some((dep) => dep.startsWith('@sveltejs/')),
|
|
121
|
+
versionKey: 'svelte',
|
|
122
|
+
featureMap: {
|
|
123
|
+
'@sveltejs/kit': 'sveltekit',
|
|
124
|
+
'@sveltejs/adapter-auto': 'sveltekit',
|
|
125
|
+
'svelte-routing': 'routing',
|
|
126
|
+
'@testing-library/svelte': 'testing',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
async detect(projectInfo) {
|
|
131
|
+
// Only check dependencies (not devDependencies) for framework detection
|
|
132
|
+
// This prevents false positives from libraries that have frameworks in devDependencies for testing
|
|
133
|
+
const deps = projectInfo.dependencies || {};
|
|
134
|
+
const detectedFrameworks = [];
|
|
135
|
+
for (const config of this.frameworks) {
|
|
136
|
+
if (this.shouldDetect(config, deps)) {
|
|
137
|
+
detectedFrameworks.push(this.buildFrameworkInfo(config, deps));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return detectedFrameworks;
|
|
123
141
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
features.push('cors');
|
|
128
|
-
}
|
|
129
|
-
if (deps['koa-helmet']) {
|
|
130
|
-
features.push('security');
|
|
131
|
-
}
|
|
132
|
-
if (deps['koa-ratelimit']) {
|
|
133
|
-
features.push('rate-limiting');
|
|
134
|
-
}
|
|
135
|
-
if (deps['koa-multer']) {
|
|
136
|
-
features.push('file-upload');
|
|
137
|
-
}
|
|
138
|
-
if (deps['koa-static']) {
|
|
139
|
-
features.push('static-files');
|
|
140
|
-
}
|
|
141
|
-
if (deps['koa-session']) {
|
|
142
|
-
features.push('sessions');
|
|
143
|
-
}
|
|
144
|
-
if (deps['koa-bodyparser']) {
|
|
145
|
-
features.push('body-parsing');
|
|
142
|
+
shouldDetect(config, deps) {
|
|
143
|
+
if (typeof config.detectionKeys === 'function') {
|
|
144
|
+
return config.detectionKeys(deps);
|
|
146
145
|
}
|
|
147
|
-
return
|
|
148
|
-
name: 'koa',
|
|
149
|
-
version: deps['koa'],
|
|
150
|
-
features,
|
|
151
|
-
};
|
|
146
|
+
return config.detectionKeys.some((key) => deps[key]);
|
|
152
147
|
}
|
|
153
|
-
|
|
154
|
-
const features = [];
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
if (deps['@hapi/vision']) {
|
|
165
|
-
features.push('templates');
|
|
166
|
-
}
|
|
167
|
-
if (deps['@hapi/boom']) {
|
|
168
|
-
features.push('error-handling');
|
|
148
|
+
buildFrameworkInfo(config, deps) {
|
|
149
|
+
const features = [...(config.builtInFeatures || [])];
|
|
150
|
+
// Detect features based on feature map
|
|
151
|
+
for (const [depKey, feature] of Object.entries(config.featureMap)) {
|
|
152
|
+
if (deps[depKey]) {
|
|
153
|
+
// Avoid duplicates
|
|
154
|
+
if (!features.includes(feature)) {
|
|
155
|
+
features.push(feature);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
169
158
|
}
|
|
170
159
|
return {
|
|
171
|
-
name:
|
|
172
|
-
version: deps[
|
|
160
|
+
name: config.name,
|
|
161
|
+
version: deps[config.versionKey],
|
|
173
162
|
features,
|
|
174
163
|
};
|
|
175
164
|
}
|
|
@@ -10,8 +10,6 @@ export interface PatternAnalysis {
|
|
|
10
10
|
utilities: UtilityInfo[];
|
|
11
11
|
}
|
|
12
12
|
export interface EndpointInfo extends RouteInfo {
|
|
13
|
-
type: 'rest' | 'graphql' | 'websocket' | 'rpc' | 'unknown';
|
|
14
|
-
framework?: string;
|
|
15
13
|
decorator?: string;
|
|
16
14
|
}
|
|
17
15
|
export interface ServiceInfo {
|
|
@@ -52,4 +50,6 @@ export interface UtilityInfo {
|
|
|
52
50
|
export declare class PatternDetector {
|
|
53
51
|
analyzePatterns(ast: t.File, filePath: string, _content: string): PatternAnalysis;
|
|
54
52
|
private hasJSXReturn;
|
|
53
|
+
private extractNextJSRoutePath;
|
|
54
|
+
private extractNuxtRoutePath;
|
|
55
55
|
}
|
|
@@ -11,7 +11,7 @@ export class PatternDetector {
|
|
|
11
11
|
middleware: [],
|
|
12
12
|
utilities: [],
|
|
13
13
|
};
|
|
14
|
-
//
|
|
14
|
+
// Analyze patterns in the AST
|
|
15
15
|
traverse.default(ast, {
|
|
16
16
|
// Detect decorator-based routes: @Get(), @Post(), etc.
|
|
17
17
|
ClassMethod: (path) => {
|
|
@@ -31,7 +31,6 @@ export class PatternDetector {
|
|
|
31
31
|
handler: t.isIdentifier(path.node.key) ? path.node.key.name : 'anonymous',
|
|
32
32
|
file: filePath,
|
|
33
33
|
line: path.node.loc?.start.line || 0,
|
|
34
|
-
type: 'rest',
|
|
35
34
|
decorator: callee.name,
|
|
36
35
|
});
|
|
37
36
|
}
|
|
@@ -40,7 +39,7 @@ export class PatternDetector {
|
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
41
|
},
|
|
43
|
-
// Detect Express/
|
|
42
|
+
// Detect Express/Fastify-style routes: app.get(), router.post(), fastify.get(), etc.
|
|
44
43
|
CallExpression: (path) => {
|
|
45
44
|
const { callee, arguments: args } = path.node;
|
|
46
45
|
if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
|
|
@@ -48,19 +47,47 @@ export class PatternDetector {
|
|
|
48
47
|
const methodName = callee.property.name;
|
|
49
48
|
if (httpMethods.includes(methodName) && args.length >= 2) {
|
|
50
49
|
const routePath = args[0];
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
// Only detect if:
|
|
51
|
+
// 1. First argument is a string literal (route path)
|
|
52
|
+
// 2. Object is a known route handler name
|
|
53
|
+
if (t.isStringLiteral(routePath) && t.isIdentifier(callee.object)) {
|
|
54
|
+
const objectName = callee.object.name;
|
|
55
|
+
const routeObjectNames = [
|
|
56
|
+
'app',
|
|
57
|
+
'router',
|
|
58
|
+
'server',
|
|
59
|
+
'fastify',
|
|
60
|
+
'express',
|
|
61
|
+
'route',
|
|
62
|
+
'api',
|
|
63
|
+
];
|
|
64
|
+
// Only detect if it's a common route object name
|
|
65
|
+
// This filters out axios.get(), client.get(), etc.
|
|
66
|
+
if (routeObjectNames.includes(objectName.toLowerCase())) {
|
|
67
|
+
analysis.endpoints.push({
|
|
68
|
+
method: methodName.toUpperCase(),
|
|
69
|
+
path: routePath.value,
|
|
70
|
+
handler: 'anonymous',
|
|
71
|
+
file: filePath,
|
|
72
|
+
line: path.node.loc?.start.line || 0,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
77
|
}
|
|
78
|
+
// Detect Nuxt.js server routes: defineEventHandler()
|
|
79
|
+
if (t.isIdentifier(callee) && callee.name === 'defineEventHandler') {
|
|
80
|
+
// Extract route path from file path
|
|
81
|
+
// Nuxt server routes are in server/api/ or server/routes/
|
|
82
|
+
const routePath = this.extractNuxtRoutePath(filePath);
|
|
83
|
+
analysis.endpoints.push({
|
|
84
|
+
method: 'GET/POST',
|
|
85
|
+
path: routePath,
|
|
86
|
+
handler: 'defineEventHandler',
|
|
87
|
+
file: filePath,
|
|
88
|
+
line: path.node.loc?.start.line || 0,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
64
91
|
},
|
|
65
92
|
// Detect TypeScript interfaces and type aliases
|
|
66
93
|
TSInterfaceDeclaration: (path) => {
|
|
@@ -92,6 +119,27 @@ export class PatternDetector {
|
|
|
92
119
|
type: 'type',
|
|
93
120
|
});
|
|
94
121
|
},
|
|
122
|
+
// Detect Next.js API route handlers: export function GET/POST/etc.
|
|
123
|
+
ExportNamedDeclaration: (path) => {
|
|
124
|
+
const declaration = path.node.declaration;
|
|
125
|
+
if (t.isFunctionDeclaration(declaration) && declaration.id) {
|
|
126
|
+
const functionName = declaration.id.name;
|
|
127
|
+
const httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
|
|
128
|
+
// Only detect if it's an HTTP method name and in an api directory
|
|
129
|
+
if (httpMethods.includes(functionName) && /\/api\//.test(filePath)) {
|
|
130
|
+
// Extract route path from file path
|
|
131
|
+
// Next.js API routes are in app/api/ or pages/api/
|
|
132
|
+
const routePath = this.extractNextJSRoutePath(filePath);
|
|
133
|
+
analysis.endpoints.push({
|
|
134
|
+
method: functionName,
|
|
135
|
+
path: routePath,
|
|
136
|
+
handler: functionName,
|
|
137
|
+
file: filePath,
|
|
138
|
+
line: declaration.loc?.start.line || 0,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
95
143
|
// Detect React components
|
|
96
144
|
FunctionDeclaration: (path) => {
|
|
97
145
|
const functionName = path.node.id?.name;
|
|
@@ -176,4 +224,36 @@ export class PatternDetector {
|
|
|
176
224
|
hasJSXReturn(body) {
|
|
177
225
|
return body.body.some((stmt) => t.isReturnStatement(stmt) && stmt.argument && t.isJSXElement(stmt.argument));
|
|
178
226
|
}
|
|
227
|
+
extractNextJSRoutePath(filePath) {
|
|
228
|
+
// Next.js API routes can be in:
|
|
229
|
+
// - app/api/[route]/route.ts (App Router)
|
|
230
|
+
// - pages/api/[route].ts (Pages Router)
|
|
231
|
+
// Try App Router pattern first
|
|
232
|
+
let match = filePath.match(/\/app\/api\/(.+)\/route\.[jt]sx?$/);
|
|
233
|
+
if (match) {
|
|
234
|
+
return `/api/${match[1]}`;
|
|
235
|
+
}
|
|
236
|
+
// Try Pages Router pattern
|
|
237
|
+
match = filePath.match(/\/pages\/api\/(.+)\.[jt]sx?$/);
|
|
238
|
+
if (match) {
|
|
239
|
+
return `/api/${match[1]}`;
|
|
240
|
+
}
|
|
241
|
+
// Fallback
|
|
242
|
+
return '/api/unknown';
|
|
243
|
+
}
|
|
244
|
+
extractNuxtRoutePath(filePath) {
|
|
245
|
+
// Nuxt.js server routes can be in:
|
|
246
|
+
// - server/api/[route].ts
|
|
247
|
+
// - server/routes/[route].ts
|
|
248
|
+
let match = filePath.match(/\/server\/api\/(.+)\.[jt]s$/);
|
|
249
|
+
if (match) {
|
|
250
|
+
return `/api/${match[1]}`;
|
|
251
|
+
}
|
|
252
|
+
match = filePath.match(/\/server\/routes\/(.+)\.[jt]s$/);
|
|
253
|
+
if (match) {
|
|
254
|
+
return `/${match[1]}`;
|
|
255
|
+
}
|
|
256
|
+
// Fallback
|
|
257
|
+
return '/api/unknown';
|
|
258
|
+
}
|
|
179
259
|
}
|
|
@@ -2,7 +2,7 @@ import type { ParsemeConfig } from '../config.js';
|
|
|
2
2
|
import type { ProjectInfo } from '../types.js';
|
|
3
3
|
export declare class ProjectAnalyzer {
|
|
4
4
|
private readonly config;
|
|
5
|
-
private readonly
|
|
5
|
+
private readonly fileCollector;
|
|
6
6
|
constructor(config: ParsemeConfig);
|
|
7
7
|
analyze(rootDir: string): Promise<ProjectInfo>;
|
|
8
8
|
private detectProjectType;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { readFile, access, readdir, stat } from 'fs/promises';
|
|
2
|
-
import { join, basename
|
|
3
|
-
import
|
|
2
|
+
import { join, basename } from 'path';
|
|
3
|
+
import { FileCollector } from '../../utils/file-collector.js';
|
|
4
4
|
export class ProjectAnalyzer {
|
|
5
5
|
config;
|
|
6
|
-
|
|
6
|
+
fileCollector;
|
|
7
7
|
constructor(config) {
|
|
8
8
|
this.config = config;
|
|
9
|
-
this.
|
|
10
|
-
const configData = this.config.get();
|
|
11
|
-
this.ig.add(configData.excludePatterns || []);
|
|
9
|
+
this.fileCollector = new FileCollector(config);
|
|
12
10
|
}
|
|
13
11
|
async analyze(rootDir) {
|
|
14
12
|
const packageJsonPath = join(rootDir, 'package.json');
|
|
@@ -88,11 +86,6 @@ export class ProjectAnalyzer {
|
|
|
88
86
|
const files = [];
|
|
89
87
|
for (const entry of entries) {
|
|
90
88
|
const fullPath = join(dir, entry);
|
|
91
|
-
const relativePath = relative(rootDir, fullPath);
|
|
92
|
-
// Skip if ignored by exclude patterns
|
|
93
|
-
if (this.ig.ignores(relativePath)) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
89
|
const stats = await stat(fullPath);
|
|
97
90
|
if (stats.isFile()) {
|
|
98
91
|
files.push(fullPath);
|
|
@@ -142,11 +135,7 @@ export class ProjectAnalyzer {
|
|
|
142
135
|
return 'frontend-web';
|
|
143
136
|
}
|
|
144
137
|
// Check for backend frameworks
|
|
145
|
-
if (deps['express'] ||
|
|
146
|
-
deps['fastify'] ||
|
|
147
|
-
deps['@nestjs/core'] ||
|
|
148
|
-
deps['koa'] ||
|
|
149
|
-
deps['@hapi/hapi']) {
|
|
138
|
+
if (deps['express'] || deps['fastify'] || deps['@nestjs/core']) {
|
|
150
139
|
return 'backend-api';
|
|
151
140
|
}
|
|
152
141
|
// Check if it's a library (has main/module/exports but no app dependencies)
|
|
@@ -213,7 +202,7 @@ export class ProjectAnalyzer {
|
|
|
213
202
|
return appIndicators.some((indicator) => deps[indicator]);
|
|
214
203
|
}
|
|
215
204
|
async getAllProjectFiles(rootDir) {
|
|
216
|
-
const
|
|
217
|
-
return
|
|
205
|
+
const result = await this.fileCollector.getAllProjectFiles(rootDir);
|
|
206
|
+
return result.files;
|
|
218
207
|
}
|
|
219
208
|
}
|
package/dist/core/config.d.ts
CHANGED
package/dist/core/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
2
|
import { readFile, writeFile } from 'fs/promises';
|
|
3
3
|
import { join, extname } from 'path';
|
|
4
4
|
export class ParsemeConfig {
|
|
@@ -123,6 +123,7 @@ export class ParsemeConfig {
|
|
|
123
123
|
analyzeFileTypes: fileTypes,
|
|
124
124
|
// Git
|
|
125
125
|
includeGitInfo: config.includeGitInfo ?? true,
|
|
126
|
+
useGitForFiles: config.useGitForFiles ?? true,
|
|
126
127
|
// Sections
|
|
127
128
|
sections: {
|
|
128
129
|
overview: true,
|
|
@@ -143,10 +144,7 @@ export class ParsemeConfig {
|
|
|
143
144
|
},
|
|
144
145
|
// Size limits
|
|
145
146
|
limits: {
|
|
146
|
-
|
|
147
|
-
maxCharsPerFile: config.limits?.maxCharsPerFile ?? 50000, // ~15k tokens
|
|
148
|
-
maxFilesPerContext: config.limits?.maxFilesPerContext ?? 20,
|
|
149
|
-
truncateStrategy: config.limits?.truncateStrategy ?? 'truncate',
|
|
147
|
+
maxFilesPerContext: config.limits?.maxFilesPerContext ?? 5000,
|
|
150
148
|
},
|
|
151
149
|
};
|
|
152
150
|
}
|
|
@@ -187,30 +185,8 @@ const config: ParsemeConfigFile = ${JSON.stringify(this.userConfig, null, 2)};
|
|
|
187
185
|
export default config;
|
|
188
186
|
`;
|
|
189
187
|
}
|
|
190
|
-
mergeExcludePatterns(configPatterns,
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
// Merge gitignore patterns with config patterns, they are added to gitignore patterns, not replacing them
|
|
194
|
-
const excludePatterns = [...gitignorePatterns];
|
|
195
|
-
if (configPatterns) {
|
|
196
|
-
excludePatterns.push(...configPatterns);
|
|
197
|
-
}
|
|
198
|
-
return excludePatterns;
|
|
199
|
-
}
|
|
200
|
-
readGitignorePatterns(rootDir) {
|
|
201
|
-
try {
|
|
202
|
-
const gitignorePath = join(rootDir, '.gitignore');
|
|
203
|
-
if (!existsSync(gitignorePath)) {
|
|
204
|
-
return [];
|
|
205
|
-
}
|
|
206
|
-
const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
|
|
207
|
-
return gitignoreContent
|
|
208
|
-
.split('\n')
|
|
209
|
-
.map((line) => line.trim())
|
|
210
|
-
.filter((line) => line && !line.startsWith('#'));
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
return [];
|
|
214
|
-
}
|
|
188
|
+
mergeExcludePatterns(configPatterns, _rootDir) {
|
|
189
|
+
// Only use config patterns - git ignore is now handled by FileFilterService
|
|
190
|
+
return configPatterns || [];
|
|
215
191
|
}
|
|
216
192
|
}
|
|
@@ -12,13 +12,11 @@ interface BuildContext {
|
|
|
12
12
|
export declare class ContextBuilder {
|
|
13
13
|
private readonly config;
|
|
14
14
|
constructor(config: ParsemeConfig);
|
|
15
|
-
private truncateContent;
|
|
16
|
-
private splitContentByLines;
|
|
17
|
-
private splitContentByChars;
|
|
18
15
|
build(context: BuildContext): ContextOutput;
|
|
19
16
|
private buildMultiFile;
|
|
20
17
|
private buildHeader;
|
|
21
18
|
private buildProjectOverview;
|
|
19
|
+
private formatFrameworksList;
|
|
22
20
|
private buildGitSection;
|
|
23
21
|
private buildSummarySection;
|
|
24
22
|
private buildFilesList;
|