@mg21st/dev-assist 1.0.0

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.
Files changed (65) hide show
  1. package/.eslintrc.json +17 -0
  2. package/.github/workflows/ci.yml +42 -0
  3. package/.github/workflows/docs.yml +49 -0
  4. package/.github/workflows/publish.yml +49 -0
  5. package/README.md +117 -0
  6. package/bin/dev-assist.js +4 -0
  7. package/dev-assist.config.js +10 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +133 -0
  11. package/dist/cli/wizard.d.ts +5 -0
  12. package/dist/cli/wizard.d.ts.map +1 -0
  13. package/dist/cli/wizard.js +66 -0
  14. package/dist/config.d.ts +3 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +62 -0
  17. package/dist/generators/docsGenerator.d.ts +15 -0
  18. package/dist/generators/docsGenerator.d.ts.map +1 -0
  19. package/dist/generators/docsGenerator.js +186 -0
  20. package/dist/generators/testGenerator.d.ts +12 -0
  21. package/dist/generators/testGenerator.d.ts.map +1 -0
  22. package/dist/generators/testGenerator.js +185 -0
  23. package/dist/parser/astParser.d.ts +7 -0
  24. package/dist/parser/astParser.d.ts.map +1 -0
  25. package/dist/parser/astParser.js +194 -0
  26. package/dist/server/index.d.ts +5 -0
  27. package/dist/server/index.d.ts.map +1 -0
  28. package/dist/server/index.js +247 -0
  29. package/dist/shared/types.d.ts +77 -0
  30. package/dist/shared/types.d.ts.map +1 -0
  31. package/dist/shared/types.js +3 -0
  32. package/docs/_config.yml +22 -0
  33. package/docs/api-reference.md +173 -0
  34. package/docs/architecture.md +90 -0
  35. package/docs/configuration.md +52 -0
  36. package/docs/contributing.md +101 -0
  37. package/docs/index.md +50 -0
  38. package/docs/installation.md +95 -0
  39. package/docs/usage.md +107 -0
  40. package/package.json +58 -0
  41. package/src/cli/index.ts +108 -0
  42. package/src/cli/wizard.ts +63 -0
  43. package/src/config.ts +29 -0
  44. package/src/generators/docsGenerator.ts +192 -0
  45. package/src/generators/testGenerator.ts +174 -0
  46. package/src/parser/astParser.ts +172 -0
  47. package/src/server/index.ts +238 -0
  48. package/src/shared/types.ts +83 -0
  49. package/tsconfig.build.json +8 -0
  50. package/tsconfig.json +19 -0
  51. package/ui/index.html +13 -0
  52. package/ui/package-lock.json +3086 -0
  53. package/ui/package.json +31 -0
  54. package/ui/postcss.config.js +6 -0
  55. package/ui/src/App.tsx +36 -0
  56. package/ui/src/components/ApiDocsTab.tsx +184 -0
  57. package/ui/src/components/ApiTestingTab.tsx +363 -0
  58. package/ui/src/components/Dashboard.tsx +128 -0
  59. package/ui/src/components/Layout.tsx +76 -0
  60. package/ui/src/components/TestsTab.tsx +149 -0
  61. package/ui/src/main.tsx +10 -0
  62. package/ui/src/styles/index.css +41 -0
  63. package/ui/tailwind.config.js +20 -0
  64. package/ui/tsconfig.json +19 -0
  65. package/ui/vite.config.ts +19 -0
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.DocsGenerator = void 0;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const ora_1 = __importDefault(require("ora"));
44
+ const astParser_1 = require("../parser/astParser");
45
+ class DocsGenerator {
46
+ constructor() {
47
+ this.parser = new astParser_1.AstParser();
48
+ }
49
+ async generate(options) {
50
+ const spinner = (0, ora_1.default)('Scanning source files for API routes...').start();
51
+ const parsedFiles = await this.parser.parseDirectory(options.sourceDir);
52
+ const endpoints = [];
53
+ for (const parsedFile of parsedFiles) {
54
+ for (const route of parsedFile.routes) {
55
+ endpoints.push(this.buildEndpoint(route));
56
+ }
57
+ }
58
+ spinner.text = `Generating documentation for ${endpoints.length} endpoints...`;
59
+ const doc = {
60
+ title: options.title || 'API Documentation',
61
+ version: '1.0.0',
62
+ baseUrl: options.baseUrl || 'http://localhost:3000',
63
+ endpoints,
64
+ generatedAt: new Date().toISOString(),
65
+ };
66
+ fs.mkdirSync(options.outputDir, { recursive: true });
67
+ fs.writeFileSync(path.join(options.outputDir, 'api-docs.json'), JSON.stringify(doc, null, 2));
68
+ fs.writeFileSync(path.join(options.outputDir, 'api-docs.md'), this.generateMarkdown(doc));
69
+ fs.writeFileSync(path.join(options.outputDir, 'openapi.json'), JSON.stringify(this.generateOpenApiSpec(doc), null, 2));
70
+ spinner.succeed(chalk_1.default.green(`Generated API docs for ${endpoints.length} endpoints`));
71
+ return doc;
72
+ }
73
+ buildEndpoint(route) {
74
+ const pathParams = route.params.map(p => ({
75
+ name: p,
76
+ type: 'string',
77
+ required: true,
78
+ }));
79
+ const exampleRequest = {};
80
+ const exampleResponse = {
81
+ success: true,
82
+ data: {},
83
+ message: 'Operation completed successfully',
84
+ };
85
+ if (['POST', 'PUT', 'PATCH'].includes(route.method)) {
86
+ exampleRequest.body = { key: 'value' };
87
+ }
88
+ return {
89
+ method: route.method,
90
+ path: route.path,
91
+ params: pathParams,
92
+ queryParams: [],
93
+ description: `${route.method} ${route.path}`,
94
+ exampleRequest,
95
+ exampleResponse,
96
+ };
97
+ }
98
+ generateMarkdown(doc) {
99
+ const lines = [
100
+ `# ${doc.title}`,
101
+ '',
102
+ `**Version:** ${doc.version}`,
103
+ `**Base URL:** ${doc.baseUrl}`,
104
+ `**Generated:** ${doc.generatedAt}`,
105
+ '',
106
+ '## Endpoints',
107
+ '',
108
+ ];
109
+ for (const endpoint of doc.endpoints) {
110
+ lines.push(`### ${endpoint.method} \`${endpoint.path}\``);
111
+ lines.push('');
112
+ if (endpoint.description) {
113
+ lines.push(endpoint.description);
114
+ lines.push('');
115
+ }
116
+ if (endpoint.params.length > 0) {
117
+ lines.push('**Path Parameters:**');
118
+ lines.push('');
119
+ lines.push('| Name | Type | Required |');
120
+ lines.push('|------|------|----------|');
121
+ for (const p of endpoint.params) {
122
+ lines.push(`| ${p.name} | ${p.type} | ${p.required ? 'Yes' : 'No'} |`);
123
+ }
124
+ lines.push('');
125
+ }
126
+ if (endpoint.exampleRequest && Object.keys(endpoint.exampleRequest).length > 0) {
127
+ lines.push('**Example Request:**');
128
+ lines.push('```json');
129
+ lines.push(JSON.stringify(endpoint.exampleRequest, null, 2));
130
+ lines.push('```');
131
+ lines.push('');
132
+ }
133
+ if (endpoint.exampleResponse) {
134
+ lines.push('**Example Response:**');
135
+ lines.push('```json');
136
+ lines.push(JSON.stringify(endpoint.exampleResponse, null, 2));
137
+ lines.push('```');
138
+ lines.push('');
139
+ }
140
+ lines.push('---');
141
+ lines.push('');
142
+ }
143
+ return lines.join('\n');
144
+ }
145
+ generateOpenApiSpec(doc) {
146
+ const paths = {};
147
+ for (const endpoint of doc.endpoints) {
148
+ const openApiPath = endpoint.path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');
149
+ if (!paths[openApiPath]) {
150
+ paths[openApiPath] = {};
151
+ }
152
+ const pathItem = paths[openApiPath];
153
+ const method = endpoint.method.toLowerCase();
154
+ pathItem[method] = {
155
+ summary: endpoint.description,
156
+ parameters: endpoint.params.map(p => ({
157
+ name: p.name,
158
+ in: 'path',
159
+ required: p.required,
160
+ schema: { type: p.type },
161
+ })),
162
+ responses: {
163
+ '200': {
164
+ description: 'Success',
165
+ content: {
166
+ 'application/json': {
167
+ example: endpoint.exampleResponse,
168
+ },
169
+ },
170
+ },
171
+ },
172
+ };
173
+ }
174
+ return {
175
+ openapi: '3.0.0',
176
+ info: {
177
+ title: doc.title,
178
+ version: doc.version,
179
+ },
180
+ servers: [{ url: doc.baseUrl }],
181
+ paths,
182
+ };
183
+ }
184
+ }
185
+ exports.DocsGenerator = DocsGenerator;
186
+ //# sourceMappingURL=docsGenerator.js.map
@@ -0,0 +1,12 @@
1
+ import { GeneratedTest } from '../shared/types';
2
+ export declare class TestGenerator {
3
+ private parser;
4
+ constructor();
5
+ generate(options: {
6
+ sourceDir: string;
7
+ outputDir: string;
8
+ framework: 'jest' | 'vitest';
9
+ }): Promise<GeneratedTest[]>;
10
+ private generateTestContent;
11
+ }
12
+ //# sourceMappingURL=testGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testGenerator.d.ts","sourceRoot":"","sources":["../../src/generators/testGenerator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAA+B,MAAM,iBAAiB,CAAC;AAE7E,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAY;;IAMpB,QAAQ,CAAC,OAAO,EAAE;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC;KAC9B,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAsC5B,OAAO,CAAC,mBAAmB;CAqG5B"}
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TestGenerator = void 0;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const ora_1 = __importDefault(require("ora"));
44
+ const astParser_1 = require("../parser/astParser");
45
+ class TestGenerator {
46
+ constructor() {
47
+ this.parser = new astParser_1.AstParser();
48
+ }
49
+ async generate(options) {
50
+ const spinner = (0, ora_1.default)('Scanning source files...').start();
51
+ const parsedFiles = await this.parser.parseDirectory(options.sourceDir);
52
+ spinner.text = `Generating tests for ${parsedFiles.length} files...`;
53
+ const generated = [];
54
+ for (const parsedFile of parsedFiles) {
55
+ if (parsedFile.functions.length === 0 && parsedFile.routes.length === 0)
56
+ continue;
57
+ const testContent = this.generateTestContent(parsedFile.filePath, {
58
+ functions: parsedFile.functions,
59
+ routes: parsedFile.routes,
60
+ framework: options.framework,
61
+ sourceDir: options.sourceDir,
62
+ outputDir: options.outputDir,
63
+ });
64
+ const relativePath = path.relative(options.sourceDir, parsedFile.filePath);
65
+ const testFileName = relativePath.replace(/\.(js|ts|jsx|tsx)$/, `.test.${parsedFile.filePath.endsWith('.ts') || parsedFile.filePath.endsWith('.tsx') ? 'ts' : 'js'}`);
66
+ const outputPath = path.join(options.outputDir, testFileName);
67
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
68
+ fs.writeFileSync(outputPath, testContent);
69
+ generated.push({
70
+ filePath: outputPath,
71
+ sourceFile: parsedFile.filePath,
72
+ framework: options.framework,
73
+ content: testContent,
74
+ });
75
+ }
76
+ spinner.succeed(chalk_1.default.green(`Generated ${generated.length} test files`));
77
+ return generated;
78
+ }
79
+ generateTestContent(filePath, options) {
80
+ const { functions, routes, framework } = options;
81
+ const relativeImport = path.relative(path.dirname(path.join(options.outputDir, path.relative(options.sourceDir, filePath))), filePath).replace(/\.(ts|tsx)$/, '');
82
+ const lines = [];
83
+ if (framework === 'vitest') {
84
+ lines.push(`import { describe, it, expect, vi, beforeEach } from 'vitest';`);
85
+ }
86
+ else {
87
+ lines.push(`import { describe, it, expect, jest, beforeEach } from '@jest/globals';`);
88
+ }
89
+ lines.push(`import * as module from '${relativeImport.startsWith('.') ? relativeImport : './' + relativeImport}';`);
90
+ lines.push('');
91
+ for (const fn of functions) {
92
+ lines.push(`describe('${fn.name}', () => {`);
93
+ lines.push(` it('should be defined', () => {`);
94
+ lines.push(` expect(module.${fn.name}).toBeDefined();`);
95
+ lines.push(` });`);
96
+ lines.push('');
97
+ if (fn.isAsync) {
98
+ lines.push(` it('should return a promise', async () => {`);
99
+ const mockArgs = fn.params.map(() => 'undefined').join(', ');
100
+ lines.push(` const result = module.${fn.name}(${mockArgs});`);
101
+ lines.push(` expect(result).toBeInstanceOf(Promise);`);
102
+ lines.push(` });`);
103
+ }
104
+ else {
105
+ lines.push(` it('should execute without throwing', () => {`);
106
+ const mockArgs = generateMockArgs(fn.params);
107
+ lines.push(` expect(() => module.${fn.name}(${mockArgs})).not.toThrow();`);
108
+ lines.push(` });`);
109
+ }
110
+ lines.push('');
111
+ lines.push(` it('should handle edge case: null/undefined inputs', ${fn.isAsync ? 'async ' : ''}() => {`);
112
+ lines.push(` // Edge case: test with null/undefined inputs`);
113
+ const nullArgs = fn.params.map(() => 'null').join(', ');
114
+ if (fn.isAsync) {
115
+ lines.push(` try {`);
116
+ lines.push(` await module.${fn.name}(${nullArgs});`);
117
+ lines.push(` } catch (e) {`);
118
+ lines.push(` expect(e).toBeDefined();`);
119
+ lines.push(` }`);
120
+ }
121
+ else {
122
+ lines.push(` try {`);
123
+ lines.push(` module.${fn.name}(${nullArgs});`);
124
+ lines.push(` } catch (e) {`);
125
+ lines.push(` expect(e).toBeDefined();`);
126
+ lines.push(` }`);
127
+ }
128
+ lines.push(` });`);
129
+ lines.push(`});`);
130
+ lines.push('');
131
+ }
132
+ if (routes.length > 0) {
133
+ lines.push(`// Express Route Tests`);
134
+ lines.push(`describe('Express Routes', () => {`);
135
+ for (const route of routes) {
136
+ lines.push(` describe('${route.method} ${route.path}', () => {`);
137
+ lines.push(` it('should handle request correctly', () => {`);
138
+ lines.push(` const mockReq = {`);
139
+ lines.push(` params: { ${route.params.map(p => `${p}: 'test'`).join(', ')} },`);
140
+ lines.push(` query: {},`);
141
+ lines.push(` body: {},`);
142
+ lines.push(` headers: {},`);
143
+ lines.push(` };`);
144
+ lines.push(` const mockRes = {`);
145
+ lines.push(` status: ${framework === 'jest' ? 'jest' : 'vi'}.fn().mockReturnThis(),`);
146
+ lines.push(` json: ${framework === 'jest' ? 'jest' : 'vi'}.fn().mockReturnThis(),`);
147
+ lines.push(` send: ${framework === 'jest' ? 'jest' : 'vi'}.fn().mockReturnThis(),`);
148
+ lines.push(` };`);
149
+ lines.push(` expect(mockReq).toBeDefined();`);
150
+ lines.push(` expect(mockRes).toBeDefined();`);
151
+ lines.push(` });`);
152
+ lines.push(` });`);
153
+ lines.push('');
154
+ }
155
+ lines.push(`});`);
156
+ }
157
+ return lines.join('\n');
158
+ }
159
+ }
160
+ exports.TestGenerator = TestGenerator;
161
+ function generateMockArgs(params) {
162
+ return params.map(param => {
163
+ if (param.includes('...'))
164
+ return '[]';
165
+ const lower = param.toLowerCase();
166
+ if (lower.includes('id'))
167
+ return "'test-id'";
168
+ if (lower.includes('name'))
169
+ return "'test-name'";
170
+ if (lower.includes('email'))
171
+ return "'test@example.com'";
172
+ if (lower.includes('num') || lower.includes('count') || lower.includes('age'))
173
+ return '0';
174
+ if (lower.includes('flag') || lower.includes('bool') || lower.includes('is') || lower.includes('has'))
175
+ return 'false';
176
+ if (lower.includes('arr') || lower.includes('list') || lower.includes('items'))
177
+ return '[]';
178
+ if (lower.includes('obj') || lower.includes('data') || lower.includes('config'))
179
+ return '{}';
180
+ if (lower.includes('fn') || lower.includes('callback') || lower.includes('cb'))
181
+ return '() => {}';
182
+ return "''";
183
+ }).join(', ');
184
+ }
185
+ //# sourceMappingURL=testGenerator.js.map
@@ -0,0 +1,7 @@
1
+ import { ParsedFile } from '../shared/types';
2
+ export declare class AstParser {
3
+ parseFile(filePath: string): Promise<ParsedFile>;
4
+ parseDirectory(dirPath: string): Promise<ParsedFile[]>;
5
+ private getFiles;
6
+ }
7
+ //# sourceMappingURL=astParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astParser.d.ts","sourceRoot":"","sources":["../../src/parser/astParser.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAA+B,MAAM,iBAAiB,CAAC;AAE1E,qBAAa,SAAS;IACd,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAuFhD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAgB5D,OAAO,CAAC,QAAQ;CAwBjB"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AstParser = void 0;
40
+ const parser = __importStar(require("@babel/parser"));
41
+ const traverse_1 = __importDefault(require("@babel/traverse"));
42
+ const t = __importStar(require("@babel/types"));
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ class AstParser {
46
+ async parseFile(filePath) {
47
+ const content = fs.readFileSync(filePath, 'utf-8');
48
+ const ext = path.extname(filePath);
49
+ const plugins = ['jsx'];
50
+ if (ext === '.ts' || ext === '.tsx') {
51
+ plugins.push('typescript');
52
+ }
53
+ let ast;
54
+ try {
55
+ ast = parser.parse(content, {
56
+ sourceType: 'module',
57
+ plugins,
58
+ errorRecovery: true,
59
+ });
60
+ }
61
+ catch {
62
+ return { filePath, functions: [], routes: [], imports: [] };
63
+ }
64
+ const functions = [];
65
+ const routes = [];
66
+ const imports = [];
67
+ (0, traverse_1.default)(ast, {
68
+ ImportDeclaration(nodePath) {
69
+ imports.push(nodePath.node.source.value);
70
+ },
71
+ ExportNamedDeclaration(nodePath) {
72
+ const declaration = nodePath.node.declaration;
73
+ if (!declaration)
74
+ return;
75
+ if (t.isFunctionDeclaration(declaration) && declaration.id) {
76
+ functions.push(extractFunction(declaration));
77
+ }
78
+ else if (t.isVariableDeclaration(declaration)) {
79
+ declaration.declarations.forEach(decl => {
80
+ if (t.isVariableDeclarator(decl) &&
81
+ t.isIdentifier(decl.id) &&
82
+ (t.isArrowFunctionExpression(decl.init) || t.isFunctionExpression(decl.init))) {
83
+ functions.push(extractVariableFunction(decl.id.name, decl.init));
84
+ }
85
+ });
86
+ }
87
+ },
88
+ ExportDefaultDeclaration(nodePath) {
89
+ const declaration = nodePath.node.declaration;
90
+ if (t.isFunctionDeclaration(declaration)) {
91
+ const fn = extractFunction(declaration);
92
+ fn.name = fn.name || 'default';
93
+ functions.push(fn);
94
+ }
95
+ },
96
+ CallExpression(nodePath) {
97
+ const node = nodePath.node;
98
+ if (t.isMemberExpression(node.callee) &&
99
+ t.isIdentifier(node.callee.property)) {
100
+ const method = node.callee.property.name.toUpperCase();
101
+ const httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
102
+ if (httpMethods.includes(method) && node.arguments.length >= 2) {
103
+ const firstArg = node.arguments[0];
104
+ if (t.isStringLiteral(firstArg)) {
105
+ const routePath = firstArg.value;
106
+ const params = extractPathParams(routePath);
107
+ routes.push({
108
+ method,
109
+ path: routePath,
110
+ handler: `handler_${method.toLowerCase()}_${routePath.replace(/[^a-zA-Z0-9]/g, '_')}`,
111
+ params,
112
+ middleware: [],
113
+ });
114
+ }
115
+ }
116
+ }
117
+ },
118
+ });
119
+ return { filePath, functions, routes, imports };
120
+ }
121
+ async parseDirectory(dirPath) {
122
+ const results = [];
123
+ const files = this.getFiles(dirPath);
124
+ for (const file of files) {
125
+ try {
126
+ const parsed = await this.parseFile(file);
127
+ results.push(parsed);
128
+ }
129
+ catch {
130
+ // Skip unparseable files
131
+ }
132
+ }
133
+ return results;
134
+ }
135
+ getFiles(dirPath) {
136
+ const files = [];
137
+ if (!fs.existsSync(dirPath))
138
+ return files;
139
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
140
+ for (const entry of entries) {
141
+ const fullPath = path.join(dirPath, entry.name);
142
+ if (entry.isDirectory()) {
143
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
144
+ files.push(...this.getFiles(fullPath));
145
+ }
146
+ }
147
+ else if (entry.isFile()) {
148
+ const ext = path.extname(entry.name);
149
+ if (['.js', '.ts', '.jsx', '.tsx'].includes(ext)) {
150
+ files.push(fullPath);
151
+ }
152
+ }
153
+ }
154
+ return files;
155
+ }
156
+ }
157
+ exports.AstParser = AstParser;
158
+ function extractFunction(node) {
159
+ return {
160
+ name: node.id?.name || 'anonymous',
161
+ params: node.params.map(p => {
162
+ if (t.isIdentifier(p))
163
+ return p.name;
164
+ if (t.isAssignmentPattern(p) && t.isIdentifier(p.left))
165
+ return p.left.name;
166
+ if (t.isRestElement(p) && t.isIdentifier(p.argument))
167
+ return `...${p.argument.name}`;
168
+ return 'param';
169
+ }),
170
+ isAsync: node.async,
171
+ isExported: true,
172
+ };
173
+ }
174
+ function extractVariableFunction(name, node) {
175
+ return {
176
+ name,
177
+ params: node.params.map(p => {
178
+ if (t.isIdentifier(p))
179
+ return p.name;
180
+ if (t.isAssignmentPattern(p) && t.isIdentifier(p.left))
181
+ return p.left.name;
182
+ if (t.isRestElement(p) && t.isIdentifier(p.argument))
183
+ return `...${p.argument.name}`;
184
+ return 'param';
185
+ }),
186
+ isAsync: node.async,
187
+ isExported: true,
188
+ };
189
+ }
190
+ function extractPathParams(routePath) {
191
+ const matches = routePath.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
192
+ return matches ? matches.map(m => m.slice(1)) : [];
193
+ }
194
+ //# sourceMappingURL=astParser.js.map
@@ -0,0 +1,5 @@
1
+ export declare function startServer(options: {
2
+ port: number;
3
+ openBrowser: boolean;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAeA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8NhG"}