-
Notifications
You must be signed in to change notification settings - Fork 140
feat: Add AST-based Code Complexity Profiler component #1750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,89 @@ | ||||||||||||||||||||
| import React, { useState } from 'react'; | ||||||||||||||||||||
| import * as babel from '@babel/standalone'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const CodeComplexityProfiler = ({ code }) => { | ||||||||||||||||||||
| const [complexity, setComplexity] = useState(null); | ||||||||||||||||||||
| const [error, setError] = useState(null); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const analyzeCode = () => { | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| setError(null); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Parse code to AST | ||||||||||||||||||||
| const ast = babel.parse(code, { | ||||||||||||||||||||
| presets: ['env'], | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| let loopCount = 0; | ||||||||||||||||||||
| let maxDepth = 0; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Simple custom traversal to detect loops | ||||||||||||||||||||
| babel.traverse(ast, { | ||||||||||||||||||||
|
Comment on lines
+13
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In Citations:
🏁 Script executed: #!/bin/bash
set -eu
file="$(git ls-files | grep -F 'frontend/src/components/CodeComplexityProfiler.jsx' | head -n1)"
printf '%s\n' "FILE=$file"
wc -l "$file"
sed -n '1,180p' "$file"
printf '%s\n' '--- frontend/package.json ---'
cat frontend/package.json
printf '%s\n' '--- Babel API references ---'
rg -n --glob '!node_modules/**' '`@babel/standalone`|babel\.(parse|traverse)|babel\.packages\.(parser|traverse)' frontendRepository: Canopus-Labs/PrepPilot Length of output: 4869 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- package manifests and lockfiles ---'
git ls-files | grep -E '(^|/)(package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock)$' || true
printf '%s\n' '--- `@babel/standalone` declarations and lockfile entries ---'
rg -n --hidden --glob '!**/.git/**' --glob '!**/node_modules/**' \
'"`@babel/standalone`"|`@babel/standalone` frontend|`@babel/standalone`' . || true
printf '%s\n' '--- component call sites ---'
rg -n --glob '!**/node_modules/**' 'CodeComplexityProfiler|analyzeCode|setComplexity|setError' frontend/src frontend 2>/dev/null || true
printf '%s\n' '--- relevant source structure ---'
ast-grep outline frontend/src/components/CodeComplexityProfiler.jsxRepository: Canopus-Labs/PrepPilot Length of output: 20884 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
import json
from pathlib import Path
source = Path("frontend/src/components/CodeComplexityProfiler.jsx").read_text()
manifest = json.loads(Path("frontend/package.json").read_text())
lock = json.loads(Path("frontend/package-lock.json").read_text())
print("frontend_dependency_declared=", "`@babel/standalone`" in manifest.get("dependencies", {}) or "`@babel/standalone`" in manifest.get("devDependencies", {}))
print("frontend_lock_root_declared=", "`@babel/standalone`" in lock.get("packages", {}).get("", {}).get("dependencies", {}))
print("frontend_lock_package_present=", any(
key.endswith("/node_modules/@babel/standalone") or key == "node_modules/@babel/standalone"
for key in lock.get("packages", {})
))
print("top_level_parse_call=", "babel.parse(" in source)
print("namespaced_parse_call=", "babel.packages.parser.parse(" in source)
print("top_level_traverse_call=", "babel.traverse(" in source)
print("namespaced_traverse_call=", "babel.packages.traverse(" in source)
for node in ("ForStatement", "WhileStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement"):
print(f"visitor_{node}=", f"{node}(path)" in source)
print("complexity_cleared_on_error=", "setComplexity(null)" in source[source.index("} catch"):])
PY
printf '%s\n' '--- frontend lockfile package metadata ---'
python3 - <<'PY'
import json
from pathlib import Path
lock = json.loads(Path("frontend/package-lock.json").read_text())
for key, value in lock.get("packages", {}).items():
if "babel" in key.lower():
print(key, value.get("version"))
PY
printf '%s\n' '--- root package declarations ---'
cat package.jsonRepository: Canopus-Labs/PrepPilot Length of output: 1741 Fix the Babel dependency and analyzer state before merging.
🤖 Prompt for AI Agents |
||||||||||||||||||||
| ForStatement(path) { | ||||||||||||||||||||
| loopCount++; | ||||||||||||||||||||
| const depth = path.getAncestry().filter(p => p.isLoop()).length; | ||||||||||||||||||||
| maxDepth = Math.max(maxDepth, depth); | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| WhileStatement(path) { | ||||||||||||||||||||
| loopCount++; | ||||||||||||||||||||
| const depth = path.getAncestry().filter(p => p.isLoop()).length; | ||||||||||||||||||||
| maxDepth = Math.max(maxDepth, depth); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
Comment on lines
+21
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Include all JavaScript loop node types.
Proposed fix babel.packages.traverse(ast, {
ForStatement(path) {
loopCount++;
const depth = path.getAncestry().filter(p => p.isLoop()).length;
maxDepth = Math.max(maxDepth, depth);
},
+ ForInStatement(path) {
+ loopCount++;
+ const depth = path.getAncestry().filter(p => p.isLoop()).length;
+ maxDepth = Math.max(maxDepth, depth);
+ },
+ ForOfStatement(path) {
+ loopCount++;
+ const depth = path.getAncestry().filter(p => p.isLoop()).length;
+ maxDepth = Math.max(maxDepth, depth);
+ },
WhileStatement(path) {
loopCount++;
const depth = path.getAncestry().filter(p => p.isLoop()).length;
maxDepth = Math.max(maxDepth, depth);
+ },
+ DoWhileStatement(path) {
+ loopCount++;
+ const depth = path.getAncestry().filter(p => p.isLoop()).length;
+ maxDepth = Math.max(maxDepth, depth);
}
});🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| let bigO = 'O(1)'; | ||||||||||||||||||||
| if (maxDepth === 1) bigO = 'O(n)'; | ||||||||||||||||||||
| if (maxDepth === 2) bigO = 'O(n^2)'; | ||||||||||||||||||||
| if (maxDepth >= 3) bigO = 'O(n^3) or worse'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| setComplexity({ | ||||||||||||||||||||
| totalLoops: loopCount, | ||||||||||||||||||||
| maxNesting: maxDepth, | ||||||||||||||||||||
| estimatedTimeComplexity: bigO | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||
| setError("Failed to parse code. Please ensure valid JavaScript."); | ||||||||||||||||||||
| console.error(err); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+45
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Clear the previous result when parsing fails. If a valid analysis is followed by invalid code, Proposed fix } catch (err) {
+ setComplexity(null);
setError("Failed to parse code. Please ensure valid JavaScript.");
console.error(err);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return ( | ||||||||||||||||||||
| <div className="p-4 bg-gray-900 rounded-xl shadow-lg border border-gray-700 mt-4"> | ||||||||||||||||||||
| <div className="flex justify-between items-center mb-4"> | ||||||||||||||||||||
| <h2 className="text-xl font-bold text-white">AST Code Complexity Profiler</h2> | ||||||||||||||||||||
| <button | ||||||||||||||||||||
| onClick={analyzeCode} | ||||||||||||||||||||
| className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-500 transition-colors" | ||||||||||||||||||||
| > | ||||||||||||||||||||
| Analyze Complexity | ||||||||||||||||||||
| </button> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| {error && ( | ||||||||||||||||||||
| <div className="text-red-400 mb-2">{error}</div> | ||||||||||||||||||||
| )} | ||||||||||||||||||||
|
|
||||||||||||||||||||
| {complexity && ( | ||||||||||||||||||||
| <div className="bg-gray-800 p-4 rounded-lg"> | ||||||||||||||||||||
| <div className="grid grid-cols-3 gap-4 text-center"> | ||||||||||||||||||||
| <div className="p-2 border border-gray-600 rounded"> | ||||||||||||||||||||
| <div className="text-gray-400 text-sm">Total Loops</div> | ||||||||||||||||||||
| <div className="text-2xl font-bold text-white">{complexity.totalLoops}</div> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| <div className="p-2 border border-gray-600 rounded"> | ||||||||||||||||||||
| <div className="text-gray-400 text-sm">Max Nesting Depth</div> | ||||||||||||||||||||
| <div className="text-2xl font-bold text-white">{complexity.maxNesting}</div> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| <div className="p-2 border border-purple-500 rounded bg-purple-900 bg-opacity-20"> | ||||||||||||||||||||
| <div className="text-purple-300 text-sm">Estimated Big-O</div> | ||||||||||||||||||||
| <div className="text-2xl font-bold text-purple-400">{complexity.estimatedTimeComplexity}</div> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| )} | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| ); | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| export default CodeComplexityProfiler; | ||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Canopus-Labs/PrepPilot
Length of output: 165
🏁 Script executed:
Repository: Canopus-Labs/PrepPilot
Length of output: 3637
🏁 Script executed:
Repository: Canopus-Labs/PrepPilot
Length of output: 4005
🌐 Web query:
@babel/standalone parse traverse API Babel.packages official documentation💡 Result:
The
@babel/standalonepackage provides access to internal Babel tooling packages, including parser and traverse, through the Babel.packages object [1][2]. When you load@babel/standalonein a browser environment, it exposes a global Babel object that contains these packages [1]. You can access them as follows: Babel.packages.parser [1][2] Babel.packages.traverse [1][2] Other available packages include Babel.packages.generator, Babel.packages.template, and Babel.packages.types [1][2]. Important usage notes: - These internal packages are exposed specifically for use within non-Node.js (browser) environments where standard module imports are not available [3]. - While these packages are made available,@babel/standalonedoes not provide a direct top-level parse API outside of these namespaced packages [4]. - You should generally prefer importing these packages directly (e.g., import { parse } from "@babel/parser") if you are using a bundler or working in a Node.js environment [3].Citations:
@babel/standalonebabel/babel#11696Declare
@babel/standaloneand use its supported APIs.Add
@babel/standalonetofrontend/package.jsonand updatefrontend/package-lock.json. The currentbabel.parseandbabel.traversecalls are not top-level@babel/standaloneAPIs; usebabel.packages.parserandbabel.packages.traverseinstead.🤖 Prompt for AI Agents