mcp-test-generator 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.
- package/README.md +123 -0
- package/dist/analyzers/function-extractor.d.ts +3 -0
- package/dist/analyzers/function-extractor.js +18 -0
- package/dist/analyzers/function-extractor.js.map +1 -0
- package/dist/analyzers/project-analyzer.d.ts +2 -0
- package/dist/analyzers/project-analyzer.js +70 -0
- package/dist/analyzers/project-analyzer.js.map +1 -0
- package/dist/analyzers/python-analyzer.d.ts +2 -0
- package/dist/analyzers/python-analyzer.js +259 -0
- package/dist/analyzers/python-analyzer.js.map +1 -0
- package/dist/analyzers/typescript-analyzer.d.ts +2 -0
- package/dist/analyzers/typescript-analyzer.js +386 -0
- package/dist/analyzers/typescript-analyzer.js.map +1 -0
- package/dist/generators/test-generator.d.ts +4 -0
- package/dist/generators/test-generator.js +223 -0
- package/dist/generators/test-generator.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/models/function-info.d.ts +66 -0
- package/dist/models/function-info.js +2 -0
- package/dist/models/function-info.js.map +1 -0
- package/dist/models/project-info.d.ts +53 -0
- package/dist/models/project-info.js +2 -0
- package/dist/models/project-info.js.map +1 -0
- package/dist/models/test-case.d.ts +32 -0
- package/dist/models/test-case.js +2 -0
- package/dist/models/test-case.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +159 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/analyze-project.d.ts +9 -0
- package/dist/tools/analyze-project.js +24 -0
- package/dist/tools/analyze-project.js.map +1 -0
- package/dist/tools/generate-single-test.d.ts +12 -0
- package/dist/tools/generate-single-test.js +58 -0
- package/dist/tools/generate-single-test.js.map +1 -0
- package/dist/tools/generate-tests.d.ts +17 -0
- package/dist/tools/generate-tests.js +82 -0
- package/dist/tools/generate-tests.js.map +1 -0
- package/dist/utils/config-parser.d.ts +8 -0
- package/dist/utils/config-parser.js +27 -0
- package/dist/utils/config-parser.js.map +1 -0
- package/dist/utils/file-utils.d.ts +7 -0
- package/dist/utils/file-utils.js +157 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/language-detector.d.ts +6 -0
- package/dist/utils/language-detector.js +148 -0
- package/dist/utils/language-detector.js.map +1 -0
- package/dist/utils/test-suite-renderer.d.ts +3 -0
- package/dist/utils/test-suite-renderer.js +62 -0
- package/dist/utils/test-suite-renderer.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# MCP Test Generator
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server that **generates test cases** for your project. Supports TypeScript, JavaScript, Python, and Java. Works with Cursor, Claude Desktop, Google Antigravity Studio, and any MCP client.
|
|
4
|
+
|
|
5
|
+
**Note:** This tool only **generates** test files; it does not run them. Use your usual test runner (Jest, Vitest, pytest, etc.) to execute tests.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g mcp-test-generator
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or use without global install:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx mcp-test-generator
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Configure your MCP client
|
|
24
|
+
|
|
25
|
+
Add the server to your client’s MCP config. Use **one** of the following.
|
|
26
|
+
|
|
27
|
+
### If installed globally
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"mcp-test-generator": {
|
|
33
|
+
"command": "mcp-test-generator"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Using npx (no global install)
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"mcp-test-generator": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "mcp-test-generator"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Config file locations
|
|
53
|
+
|
|
54
|
+
- **Cursor:** `~/.cursor/mcp.json` (macOS/Linux) or `%USERPROFILE%\.cursor\mcp.json` (Windows)
|
|
55
|
+
- **Claude Desktop:** See [Claude MCP docs](https://docs.anthropic.com/en/docs/build-with-claude/mcp)
|
|
56
|
+
- **Other clients:** Check your client’s docs for “MCP” or “Model Context Protocol” configuration.
|
|
57
|
+
|
|
58
|
+
Restart (or reload) your client after changing the config.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Tools
|
|
63
|
+
|
|
64
|
+
| Tool | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| **`analyze_project`** | Analyzes a project and returns structure, language, framework, and testable files. Does not generate tests. |
|
|
67
|
+
| **`generate_single_test`** | Generates a test file for one source file. |
|
|
68
|
+
| **`generate_all_tests`** | Analyzes the project and generates test files for all testable source files. |
|
|
69
|
+
|
|
70
|
+
All paths must be **absolute** (e.g. `/Users/me/my-project` or `C:\Users\me\my-project`).
|
|
71
|
+
|
|
72
|
+
### Example usage (in chat)
|
|
73
|
+
|
|
74
|
+
- “Analyze the project at `/path/to/my-app`”
|
|
75
|
+
- “Generate a test for `/path/to/my-app/src/utils.ts`”
|
|
76
|
+
- “Generate tests for the whole project at `/path/to/my-app`”
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Build from source
|
|
81
|
+
|
|
82
|
+
If you prefer to run from the repo instead of npm:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/your-username/mcp-test-generator.git
|
|
86
|
+
cd mcp-test-generator
|
|
87
|
+
npm install
|
|
88
|
+
npm run build
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then point your MCP config at the built entrypoint:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"mcp-test-generator": {
|
|
97
|
+
"command": "node",
|
|
98
|
+
"args": ["/absolute/path/to/mcp-test-generator/dist/index.js"]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See [TESTING.md](./TESTING.md) for more detailed testing steps.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Publish to npm (for maintainers)
|
|
109
|
+
|
|
110
|
+
1. Create an [npm account](https://www.npmjs.com/signup) if needed.
|
|
111
|
+
2. Log in: `npm login`
|
|
112
|
+
3. Update `repository.url` in `package.json` to your GitHub repo (e.g. `https://github.com/your-username/mcp-test-generator.git`).
|
|
113
|
+
4. Publish: `npm publish`
|
|
114
|
+
|
|
115
|
+
The `prepublishOnly` script runs `npm run build` automatically, so the published package includes the built `dist/` folder.
|
|
116
|
+
|
|
117
|
+
If the name `mcp-test-generator` is taken, use a scoped package: set `"name": "@your-username/mcp-test-generator"` and publish with `npm publish --access public`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { analyzeTypeScriptFile } from "./typescript-analyzer.js";
|
|
2
|
+
import { analyzePythonFile } from "./python-analyzer.js";
|
|
3
|
+
export function analyzeFile(filePath, language) {
|
|
4
|
+
switch (language) {
|
|
5
|
+
case "typescript":
|
|
6
|
+
case "javascript":
|
|
7
|
+
return analyzeTypeScriptFile(filePath);
|
|
8
|
+
case "python":
|
|
9
|
+
return analyzePythonFile(filePath);
|
|
10
|
+
case "java":
|
|
11
|
+
// For Java, we'd use java-analyzer.ts
|
|
12
|
+
// Simplified for now - returning basic structure
|
|
13
|
+
return analyzeTypeScriptFile(filePath); // Simplified fallback
|
|
14
|
+
default:
|
|
15
|
+
throw new Error(`Unsupported language: ${language}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=function-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-extractor.js","sourceRoot":"","sources":["../../src/analyzers/function-extractor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,QAA6B;IAE7B,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,YAAY,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACzC,KAAK,QAAQ;YACX,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACrC,KAAK,MAAM;YACT,sCAAsC;YACtC,iDAAiD;YACjD,OAAO,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB;QAChE;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { detectLanguage, detectFramework, detectTestFramework, } from "../utils/language-detector.js";
|
|
4
|
+
import { findSourceFiles } from "../utils/file-utils.js";
|
|
5
|
+
export async function analyzeProject(projectPath, includePatterns, excludePatterns) {
|
|
6
|
+
// Validate project path
|
|
7
|
+
if (!fs.existsSync(projectPath)) {
|
|
8
|
+
throw new Error(`Project path does not exist: ${projectPath}`);
|
|
9
|
+
}
|
|
10
|
+
if (!fs.statSync(projectPath).isDirectory()) {
|
|
11
|
+
throw new Error(`Project path is not a directory: ${projectPath}`);
|
|
12
|
+
}
|
|
13
|
+
const language = detectLanguage(projectPath);
|
|
14
|
+
const framework = detectFramework(projectPath);
|
|
15
|
+
const testFramework = detectTestFramework(projectPath, language);
|
|
16
|
+
const sourceFiles = await findSourceFiles(projectPath, language, includePatterns, excludePatterns);
|
|
17
|
+
// Find config files
|
|
18
|
+
const configFiles = findConfigFiles(projectPath);
|
|
19
|
+
// Detect package manager
|
|
20
|
+
const packageManager = detectPackageManager(projectPath);
|
|
21
|
+
return {
|
|
22
|
+
rootPath: projectPath,
|
|
23
|
+
language,
|
|
24
|
+
framework,
|
|
25
|
+
testFramework,
|
|
26
|
+
sourceFiles,
|
|
27
|
+
packageManager,
|
|
28
|
+
configFiles,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function findConfigFiles(projectPath) {
|
|
32
|
+
const configFileNames = [
|
|
33
|
+
"package.json",
|
|
34
|
+
"tsconfig.json",
|
|
35
|
+
"jest.config.js",
|
|
36
|
+
"jest.config.ts",
|
|
37
|
+
"vitest.config.ts",
|
|
38
|
+
".babelrc",
|
|
39
|
+
"babel.config.js",
|
|
40
|
+
"pyproject.toml",
|
|
41
|
+
"setup.py",
|
|
42
|
+
"setup.cfg",
|
|
43
|
+
"pytest.ini",
|
|
44
|
+
"pom.xml",
|
|
45
|
+
"build.gradle",
|
|
46
|
+
];
|
|
47
|
+
return configFileNames.filter((f) => fs.existsSync(path.join(projectPath, f)));
|
|
48
|
+
}
|
|
49
|
+
function detectPackageManager(projectPath) {
|
|
50
|
+
if (fs.existsSync(path.join(projectPath, "pnpm-lock.yaml")))
|
|
51
|
+
return "pnpm";
|
|
52
|
+
if (fs.existsSync(path.join(projectPath, "yarn.lock")))
|
|
53
|
+
return "yarn";
|
|
54
|
+
if (fs.existsSync(path.join(projectPath, "package-lock.json")))
|
|
55
|
+
return "npm";
|
|
56
|
+
if (fs.existsSync(path.join(projectPath, "bun.lockb")))
|
|
57
|
+
return "bun";
|
|
58
|
+
if (fs.existsSync(path.join(projectPath, "requirements.txt")))
|
|
59
|
+
return "pip";
|
|
60
|
+
if (fs.existsSync(path.join(projectPath, "Pipfile")))
|
|
61
|
+
return "pipenv";
|
|
62
|
+
if (fs.existsSync(path.join(projectPath, "poetry.lock")))
|
|
63
|
+
return "poetry";
|
|
64
|
+
if (fs.existsSync(path.join(projectPath, "pom.xml")))
|
|
65
|
+
return "maven";
|
|
66
|
+
if (fs.existsSync(path.join(projectPath, "build.gradle")))
|
|
67
|
+
return "gradle";
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=project-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/project-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
export function analyzePythonFile(filePath) {
|
|
3
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
4
|
+
return {
|
|
5
|
+
filePath,
|
|
6
|
+
imports: extractPythonImports(content),
|
|
7
|
+
exports: extractPythonExports(content),
|
|
8
|
+
functions: extractPythonFunctions(content),
|
|
9
|
+
classes: extractPythonClasses(content),
|
|
10
|
+
constants: [],
|
|
11
|
+
interfaces: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function extractPythonImports(content) {
|
|
15
|
+
const imports = [];
|
|
16
|
+
// from x import y
|
|
17
|
+
const fromImportRegex = /from\s+([\w.]+)\s+import\s+(.+)/g;
|
|
18
|
+
let match;
|
|
19
|
+
while ((match = fromImportRegex.exec(content)) !== null) {
|
|
20
|
+
const namedImports = match[2]
|
|
21
|
+
.split(",")
|
|
22
|
+
.map((s) => s.trim().split(/\s+as\s+/)[0].trim())
|
|
23
|
+
.filter((s) => s.length > 0);
|
|
24
|
+
imports.push({
|
|
25
|
+
module: match[1],
|
|
26
|
+
namedImports,
|
|
27
|
+
defaultImport: null,
|
|
28
|
+
isTypeOnly: false,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// import x
|
|
32
|
+
const importRegex = /^import\s+([\w.]+)(?:\s+as\s+(\w+))?/gm;
|
|
33
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
34
|
+
imports.push({
|
|
35
|
+
module: match[1],
|
|
36
|
+
namedImports: [],
|
|
37
|
+
defaultImport: match[2] || match[1],
|
|
38
|
+
isTypeOnly: false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return imports;
|
|
42
|
+
}
|
|
43
|
+
function extractPythonExports(content) {
|
|
44
|
+
// Python doesn't have explicit exports, but we look for __all__
|
|
45
|
+
const allMatch = content.match(/__all__\s*=\s*\[([^\]]+)\]/);
|
|
46
|
+
if (allMatch) {
|
|
47
|
+
return allMatch[1]
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((s) => s.trim().replace(/['"]/g, ""))
|
|
50
|
+
.filter((s) => s.length > 0);
|
|
51
|
+
}
|
|
52
|
+
// Return all top-level functions and classes (non-private)
|
|
53
|
+
const exports = [];
|
|
54
|
+
const funcRegex = /^(?:async\s+)?def\s+(\w+)/gm;
|
|
55
|
+
const classRegex = /^class\s+(\w+)/gm;
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = funcRegex.exec(content)) !== null) {
|
|
58
|
+
if (!match[1].startsWith("_"))
|
|
59
|
+
exports.push(match[1]);
|
|
60
|
+
}
|
|
61
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
62
|
+
if (!match[1].startsWith("_"))
|
|
63
|
+
exports.push(match[1]);
|
|
64
|
+
}
|
|
65
|
+
return exports;
|
|
66
|
+
}
|
|
67
|
+
function extractPythonFunctions(content) {
|
|
68
|
+
const functions = [];
|
|
69
|
+
const funcRegex = /^(async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^\n:]+))?\s*:/gm;
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = funcRegex.exec(content)) !== null) {
|
|
72
|
+
const isAsync = !!match[1];
|
|
73
|
+
const name = match[2];
|
|
74
|
+
const paramsStr = match[3];
|
|
75
|
+
const returnType = match[4]?.trim() || null;
|
|
76
|
+
// Extract docstring
|
|
77
|
+
const afterDef = content.substring(match.index + match[0].length);
|
|
78
|
+
const docstringMatch = afterDef.match(/^\s*(?:"""([\s\S]*?)"""|'''([\s\S]*?)''')/);
|
|
79
|
+
const jsDoc = docstringMatch
|
|
80
|
+
? docstringMatch[1] || docstringMatch[2]
|
|
81
|
+
: null;
|
|
82
|
+
// Extract body by indentation
|
|
83
|
+
const body = extractPythonBody(content, match.index + match[0].length);
|
|
84
|
+
// Extract decorators
|
|
85
|
+
const decorators = extractPythonDecorators(content, match.index);
|
|
86
|
+
functions.push({
|
|
87
|
+
name,
|
|
88
|
+
type: "function",
|
|
89
|
+
isAsync,
|
|
90
|
+
isExported: !name.startsWith("_"),
|
|
91
|
+
isStatic: false,
|
|
92
|
+
parameters: parsePythonParameters(paramsStr),
|
|
93
|
+
returnType,
|
|
94
|
+
className: null,
|
|
95
|
+
decorators,
|
|
96
|
+
jsDoc,
|
|
97
|
+
complexity: calculatePythonComplexity(body),
|
|
98
|
+
startLine: getLineNumber(content, match.index),
|
|
99
|
+
endLine: getLineNumber(content, match.index + match[0].length + body.length),
|
|
100
|
+
body,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return functions;
|
|
104
|
+
}
|
|
105
|
+
function extractPythonClasses(content) {
|
|
106
|
+
const classes = [];
|
|
107
|
+
const classRegex = /^class\s+(\w+)(?:\(([^)]*)\))?\s*:/gm;
|
|
108
|
+
let match;
|
|
109
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
110
|
+
const name = match[1];
|
|
111
|
+
const parentStr = match[2] || "";
|
|
112
|
+
const parents = parentStr
|
|
113
|
+
.split(",")
|
|
114
|
+
.map((s) => s.trim())
|
|
115
|
+
.filter((s) => s.length > 0);
|
|
116
|
+
const classBody = extractPythonBody(content, match.index + match[0].length);
|
|
117
|
+
// Extract methods from class body
|
|
118
|
+
const methods = extractMethodsFromPythonClass(classBody, name);
|
|
119
|
+
const constructorInfo = methods.find((m) => m.name === "__init__") || null;
|
|
120
|
+
classes.push({
|
|
121
|
+
name,
|
|
122
|
+
isExported: !name.startsWith("_"),
|
|
123
|
+
constructorInfo,
|
|
124
|
+
methods: methods.filter((m) => m.name !== "__init__"),
|
|
125
|
+
properties: [],
|
|
126
|
+
decorators: extractPythonDecorators(content, match.index),
|
|
127
|
+
extendsClass: parents.length > 0 ? parents[0] : null,
|
|
128
|
+
implementsInterfaces: [],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return classes;
|
|
132
|
+
}
|
|
133
|
+
function extractMethodsFromPythonClass(classBody, className) {
|
|
134
|
+
const methods = [];
|
|
135
|
+
const methodRegex = /(?:^|\n)\s+(async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^\n:]+))?\s*:/g;
|
|
136
|
+
let match;
|
|
137
|
+
while ((match = methodRegex.exec(classBody)) !== null) {
|
|
138
|
+
const isAsync = !!match[1];
|
|
139
|
+
const name = match[2];
|
|
140
|
+
const paramsStr = match[3];
|
|
141
|
+
const returnType = match[4]?.trim() || null;
|
|
142
|
+
const decorators = extractPythonDecorators(classBody, match.index);
|
|
143
|
+
const isStatic = decorators.includes("staticmethod");
|
|
144
|
+
methods.push({
|
|
145
|
+
name,
|
|
146
|
+
type: name === "__init__" ? "constructor" : "method",
|
|
147
|
+
isAsync,
|
|
148
|
+
isExported: !name.startsWith("_") || name === "__init__",
|
|
149
|
+
isStatic,
|
|
150
|
+
parameters: parsePythonParameters(paramsStr).filter((p) => p.name !== "self" && p.name !== "cls"),
|
|
151
|
+
returnType,
|
|
152
|
+
className,
|
|
153
|
+
decorators,
|
|
154
|
+
jsDoc: null,
|
|
155
|
+
complexity: 1,
|
|
156
|
+
startLine: 0,
|
|
157
|
+
endLine: 0,
|
|
158
|
+
body: "",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return methods;
|
|
162
|
+
}
|
|
163
|
+
function parsePythonParameters(paramsStr) {
|
|
164
|
+
if (!paramsStr.trim())
|
|
165
|
+
return [];
|
|
166
|
+
return paramsStr
|
|
167
|
+
.split(",")
|
|
168
|
+
.map((p) => p.trim())
|
|
169
|
+
.filter((p) => p.length > 0 && p !== "self" && p !== "cls")
|
|
170
|
+
.map((p) => {
|
|
171
|
+
let defaultValue = null;
|
|
172
|
+
const defaultMatch = p.match(/\s*=\s*(.+)$/);
|
|
173
|
+
if (defaultMatch) {
|
|
174
|
+
defaultValue = defaultMatch[1].trim();
|
|
175
|
+
p = p.substring(0, p.indexOf("=")).trim();
|
|
176
|
+
}
|
|
177
|
+
let type = null;
|
|
178
|
+
const typeMatch = p.match(/:\s*(.+)$/);
|
|
179
|
+
if (typeMatch) {
|
|
180
|
+
type = typeMatch[1].trim();
|
|
181
|
+
p = p.substring(0, p.indexOf(":")).trim();
|
|
182
|
+
}
|
|
183
|
+
const isRest = p.startsWith("*") || p.startsWith("**");
|
|
184
|
+
if (isRest)
|
|
185
|
+
p = p.replace(/^\*+/, "");
|
|
186
|
+
return {
|
|
187
|
+
name: p,
|
|
188
|
+
type,
|
|
189
|
+
isOptional: defaultValue !== null,
|
|
190
|
+
defaultValue,
|
|
191
|
+
isRest,
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function extractPythonBody(content, startIndex) {
|
|
196
|
+
const lines = content.substring(startIndex).split("\n");
|
|
197
|
+
if (lines.length === 0)
|
|
198
|
+
return "";
|
|
199
|
+
// Find the indentation level of the first non-empty line
|
|
200
|
+
let baseIndent = -1;
|
|
201
|
+
const bodyLines = [];
|
|
202
|
+
for (let i = 1; i < lines.length; i++) {
|
|
203
|
+
const line = lines[i];
|
|
204
|
+
if (line.trim().length === 0) {
|
|
205
|
+
bodyLines.push("");
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const indent = line.match(/^(\s*)/)?.[1].length || 0;
|
|
209
|
+
if (baseIndent === -1) {
|
|
210
|
+
baseIndent = indent;
|
|
211
|
+
}
|
|
212
|
+
if (indent >= baseIndent) {
|
|
213
|
+
bodyLines.push(line);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return bodyLines.join("\n");
|
|
220
|
+
}
|
|
221
|
+
function extractPythonDecorators(content, defIndex) {
|
|
222
|
+
const before = content.substring(0, defIndex).trimEnd();
|
|
223
|
+
const lines = before.split("\n").reverse();
|
|
224
|
+
const decorators = [];
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
const trimmed = line.trim();
|
|
227
|
+
if (trimmed.startsWith("@")) {
|
|
228
|
+
const decoratorName = trimmed.match(/@(\w+)/)?.[1];
|
|
229
|
+
if (decoratorName)
|
|
230
|
+
decorators.push(decoratorName);
|
|
231
|
+
}
|
|
232
|
+
else if (trimmed.length > 0) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return decorators.reverse();
|
|
237
|
+
}
|
|
238
|
+
function calculatePythonComplexity(body) {
|
|
239
|
+
let complexity = 1;
|
|
240
|
+
const keywords = [
|
|
241
|
+
/\bif\b/g,
|
|
242
|
+
/\belif\b/g,
|
|
243
|
+
/\bfor\b/g,
|
|
244
|
+
/\bwhile\b/g,
|
|
245
|
+
/\bexcept\b/g,
|
|
246
|
+
/\band\b/g,
|
|
247
|
+
/\bor\b/g,
|
|
248
|
+
];
|
|
249
|
+
for (const regex of keywords) {
|
|
250
|
+
const matches = body.match(regex);
|
|
251
|
+
if (matches)
|
|
252
|
+
complexity += matches.length;
|
|
253
|
+
}
|
|
254
|
+
return complexity;
|
|
255
|
+
}
|
|
256
|
+
function getLineNumber(content, index) {
|
|
257
|
+
return content.substring(0, index).split("\n").length;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=python-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/python-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AASzB,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEnD,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC;QACtC,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC;QACtC,SAAS,EAAE,sBAAsB,CAAC,OAAO,CAAC;QAC1C,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC;QACtC,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,EAAE;KACf,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAe;IAC3C,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,kBAAkB;IAClB,MAAM,eAAe,GAAG,kCAAkC,CAAC;IAC3D,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC;aAC1B,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aAChD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAChB,YAAY;YACZ,aAAa,EAAE,IAAI;YACnB,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;IACL,CAAC;IAED,WAAW;IACX,MAAM,WAAW,GAAG,wCAAwC,CAAC;IAC7D,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAChB,YAAY,EAAE,EAAE;YAChB,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACnC,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAe;IAC3C,gEAAgE;IAChE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC7D,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC,CAAC,CAAC;aACf,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;aACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,2DAA2D;IAC3D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAG,6BAA6B,CAAC;IAChD,MAAM,UAAU,GAAG,kBAAkB,CAAC;IAEtC,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAClD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACnD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAe;IAC7C,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,MAAM,SAAS,GACb,mEAAmE,CAAC;IAEtE,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;QAE5C,oBAAoB;QACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAClE,MAAM,cAAc,GAAG,QAAQ,CAAC,KAAK,CACnC,2CAA2C,CAC5C,CAAC;QACF,MAAM,KAAK,GAAG,cAAc;YAC1B,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC;YACxC,CAAC,CAAC,IAAI,CAAC;QAET,8BAA8B;QAC9B,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAEvE,qBAAqB;QACrB,MAAM,UAAU,GAAG,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAEjE,SAAS,CAAC,IAAI,CAAC;YACb,IAAI;YACJ,IAAI,EAAE,UAAU;YAChB,OAAO;YACP,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACjC,QAAQ,EAAE,KAAK;YACf,UAAU,EAAE,qBAAqB,CAAC,SAAS,CAAC;YAC5C,UAAU;YACV,SAAS,EAAE,IAAI;YACf,UAAU;YACV,KAAK;YACL,UAAU,EAAE,yBAAyB,CAAC,IAAI,CAAC;YAC3C,SAAS,EAAE,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC;YAC9C,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC5E,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAe;IAC3C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,UAAU,GAAG,sCAAsC,CAAC;IAE1D,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,SAAS;aACtB,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE/B,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAE5E,kCAAkC;QAClC,MAAM,OAAO,GAAG,6BAA6B,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC/D,MAAM,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,IAAI,CAAC;QAErD,OAAO,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACjC,eAAe;YACf,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;YACrD,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC;YACzD,YAAY,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;YACpD,oBAAoB,EAAE,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,6BAA6B,CACpC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,MAAM,WAAW,GACf,4EAA4E,CAAC;IAE/E,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;QAE5C,MAAM,UAAU,GAAG,uBAAuB,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAErD,OAAO,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,IAAI,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ;YACpD,OAAO;YACP,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,UAAU;YACxD,QAAQ;YACR,UAAU,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC,MAAM,CACjD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAC7C;YACD,UAAU;YACV,SAAS;YACT,UAAU;YACV,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,qBAAqB,CAAC,SAAiB;IAC9C,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAEjC,OAAO,SAAS;SACb,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,YAAY,GAAkB,IAAI,CAAC;QACvC,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC7C,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC;QAED,IAAI,IAAI,GAAkB,IAAI,CAAC;QAC/B,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3B,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,MAAM;YAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAEtC,OAAO;YACL,IAAI,EAAE,CAAC;YACP,IAAI;YACJ,UAAU,EAAE,YAAY,KAAK,IAAI;YACjC,YAAY;YACZ,MAAM;SACP,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe,EAAE,UAAkB;IAC5D,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,yDAAyD;IACzD,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;IACpB,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QAErD,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,UAAU,GAAG,MAAM,CAAC;QACtB,CAAC;QAED,IAAI,MAAM,IAAI,UAAU,EAAE,CAAC;YACzB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,uBAAuB,CAC9B,OAAe,EACf,QAAgB;IAEhB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,aAAa;gBAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,yBAAyB,CAAC,IAAY;IAC7C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,QAAQ,GAAG;QACf,SAAS;QACT,WAAW;QACX,UAAU;QACV,YAAY;QACZ,aAAa;QACb,UAAU;QACV,SAAS;KACV,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,OAAO;YAAE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAC5C,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,aAAa,CAAC,OAAe,EAAE,KAAa;IACnD,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACxD,CAAC"}
|