feat: Add AST-based Code Complexity Profiler component - #1750
feat: Add AST-based Code Complexity Profiler component#1750desireddymohithreddy0925 wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughAdds a React component that parses supplied JavaScript with Babel, counts ChangesJavaScript complexity profiler
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
actor User
participant CodeComplexityProfiler
participant BabelParser
participant BabelTraverse
User->>CodeComplexityProfiler: Click Analyze
CodeComplexityProfiler->>BabelParser: Parse supplied JavaScript
BabelParser->>BabelTraverse: Traverse parsed AST
BabelTraverse-->>CodeComplexityProfiler: Return loop counts and nesting depth
CodeComplexityProfiler-->>User: Display estimated complexity
Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment Warning |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/CodeComplexityProfiler.jsx`:
- Around line 21-32: Extend the loop visitors in the Babel traversal used by the
complexity profiler to include ForInStatement, ForOfStatement, and
DoWhileStatement alongside ForStatement and WhileStatement. Ensure each new
visitor increments loopCount and updates maxDepth using the same ancestry-based
depth calculation.
- Around line 45-48: Update the parse-error catch block in
CodeComplexityProfiler to clear the existing complexity result before setting
the parse error, ensuring stale analysis metrics are not displayed after invalid
code is submitted.
- Around line 13-21: Add `@babel/standalone` to frontend/package.json and the
lockfile, then update CodeComplexityProfiler to parse and traverse through
babel.packages.parser.parse and babel.packages.traverse. Extend its traversal
visitors to count ForInStatement, ForOfStatement, and DoWhileStatement, and
clear the complexity state in the error path so stale results are not retained.
- Line 2: Declare `@babel/standalone` in frontend/package.json and synchronize
frontend/package-lock.json. In CodeComplexityProfiler, replace unsupported
top-level babel.parse and babel.traverse calls with the supported
babel.packages.parser and babel.packages.traverse APIs.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ef67fdba-5a9f-4f91-9944-5604e01ce107
📒 Files selected for processing (1)
frontend/src/components/CodeComplexityProfiler.jsx
| @@ -0,0 +1,89 @@ | |||
| import React, { useState } from 'react'; | |||
| import * as babel from '@babel/standalone'; | |||
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
jq -e '
(.dependencies // {})["`@babel/standalone`"] //
(.devDependencies // {})["`@babel/standalone`"]
' frontend/package.jsonRepository: Canopus-Labs/PrepPilot
Length of output: 165
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- manifests and lockfiles ---'
git ls-files '*package.json' '*package-lock.json' '*yarn.lock' '*pnpm-lock.yaml' '*npm-shrinkwrap.json'
printf '%s\n' '--- frontend manifest ---'
cat frontend/package.json
printf '%s\n' '--- relevant source references ---'
rg -n -C 4 '`@babel/standalone`|babel\.(parse|traverse)|babel\.packages' frontend/src frontend/package.json
printf '%s\n' '--- lockfile references ---'
rg -n -C 2 '`@babel/standalone`|node_modules/@babel/standalone|babel/standalone' . \
-g 'package-lock.json' -g 'yarn.lock' -g 'pnpm-lock.yaml' -g 'npm-shrinkwrap.json' || trueRepository: Canopus-Labs/PrepPilot
Length of output: 3637
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- component ---'
cat -n frontend/src/components/CodeComplexityProfiler.jsx
printf '%s\n' '--- root package configuration ---'
cat package.json
printf '%s\n' '--- all lockfile package entries for the dependency ---'
for lockfile in package-lock.json frontend/package-lock.json backend/package-lock.json; do
if [ -f "$lockfile" ]; then
printf '%s\n' "--- $lockfile ---"
jq -r '
if .packages then
.packages | to_entries[] | select(.key | contains("`@babel/standalone`")) | .key + " " + (.value.version // "")
elif .dependencies then
paths(scalars) as $p | select(($p | join("/")) | contains("`@babel/standalone`")) | (($p | join("/")) + " " + (getpath($p) | tostring))
else empty end
' "$lockfile" 2>/dev/null || true
fi
doneRepository: Canopus-Labs/PrepPilot
Length of output: 4005
🌐 Web query:
@babel/standalone parse traverse API Babel.packages official documentation
💡 Result:
The @babel/standalone package provides access to internal Babel tooling packages, including parser and traverse, through the Babel.packages object [1][2]. When you load @babel/standalone in 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/standalone does 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:
- 1: https://babeljs.io/docs/babel-standalone
- 2: https://github.com/babel/website/blob/master/docs/standalone.md
- 3: Export babel tooling packages in
@babel/standalonebabel/babel#11696 - 4: No parse API exposed from @babel/standalone babel/babel#12482
Declare @babel/standalone and use its supported APIs.
Add @babel/standalone to frontend/package.json and update frontend/package-lock.json. The current babel.parse and babel.traverse calls are not top-level @babel/standalone APIs; use babel.packages.parser and babel.packages.traverse instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/CodeComplexityProfiler.jsx` at line 2, Declare
`@babel/standalone` in frontend/package.json and synchronize
frontend/package-lock.json. In CodeComplexityProfiler, replace unsupported
top-level babel.parse and babel.traverse calls with the supported
babel.packages.parser and babel.packages.traverse APIs.
| const ast = babel.parse(code, { | ||
| presets: ['env'], | ||
| }); | ||
|
|
||
| let loopCount = 0; | ||
| let maxDepth = 0; | ||
|
|
||
| // Simple custom traversal to detect loops | ||
| babel.traverse(ast, { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Official Babel documentation for @babel/standalone: how should an application access the parser and traversal APIs, and are Babel.parseandBabel.traverse supported top-level APIs?
💡 Result:
In @babel/standalone, the parser and traversal APIs are not directly exposed as top-level methods on the global Babel object [1][2]. Instead, they are accessible through the Babel.packages object [1][2]. To access these functionalities, you should use the following paths: - Parser: Babel.packages.parser [1][2] - Traversal: Babel.packages.traverse [1][2] The methods Babel.parse and Babel.traverse are not supported as top-level APIs on the Babel object [1][2]. You must access them via the namespaced properties shown above. For example, if you wish to use the parser, you would access it via Babel.packages.parser. Likewise, traversal is accessed via Babel.packages.traverse [1][2]. The full list of internal packages exposed on the Babel.packages object includes: - Babel.packages.generator [1][2] - Babel.packages.parser [1][2] - Babel.packages.template [1][2] - Babel.packages.traverse [1][2] - Babel.packages.types [1][2]
Citations:
- 1: https://github.com/babel/website/blob/master/docs/standalone.md
- 2: https://babeljs.io/docs/babel-standalone
🏁 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.
- Add
@babel/standalonetofrontend/package.jsonand its lockfile. - Use
babel.packages.parser.parseandbabel.packages.traverse. The top-level calls are unsupported. - Add visitors for
ForInStatement,ForOfStatement, andDoWhileStatementif all loops must count. - Clear
complexityin the error path to prevent stale results.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/CodeComplexityProfiler.jsx` around lines 13 - 21, Add
`@babel/standalone` to frontend/package.json and the lockfile, then update
CodeComplexityProfiler to parse and traverse through babel.packages.parser.parse
and babel.packages.traverse. Extend its traversal visitors to count
ForInStatement, ForOfStatement, and DoWhileStatement, and clear the complexity
state in the error path so stale results are not retained.
| babel.traverse(ast, { | ||
| 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); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Include all JavaScript loop node types.
for...in, for...of, and do...while loops are not counted. For example, a single for (const item of items) loop reports zero loops and O(1). Add visitors for ForInStatement, ForOfStatement, and DoWhileStatement.
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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/CodeComplexityProfiler.jsx` around lines 21 - 32,
Extend the loop visitors in the Babel traversal used by the complexity profiler
to include ForInStatement, ForOfStatement, and DoWhileStatement alongside
ForStatement and WhileStatement. Ensure each new visitor increments loopCount
and updates maxDepth using the same ancestry-based depth calculation.
| } catch (err) { | ||
| setError("Failed to parse code. Please ensure valid JavaScript."); | ||
| console.error(err); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Clear the previous result when parsing fails.
If a valid analysis is followed by invalid code, complexity retains the old result. The UI then shows stale loop metrics with the parse error. Clear complexity in the catch block.
Proposed fix
} catch (err) {
+ setComplexity(null);
setError("Failed to parse code. Please ensure valid JavaScript.");
console.error(err);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (err) { | |
| setError("Failed to parse code. Please ensure valid JavaScript."); | |
| console.error(err); | |
| } | |
| } catch (err) { | |
| setComplexity(null); | |
| setError("Failed to parse code. Please ensure valid JavaScript."); | |
| console.error(err); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/CodeComplexityProfiler.jsx` around lines 45 - 48,
Update the parse-error catch block in CodeComplexityProfiler to clear the
existing complexity result before setting the parse error, ensuring stale
analysis metrics are not displayed after invalid code is submitted.
Description
This PR implements the Dynamic AST-based Code Complexity Profiler as proposed. It evaluates users' code submissions dynamically on the client side to estimate algorithmic time complexity (Big-O notation).
Changes Made
CodeComplexityProfiler.jsxinfrontend/src/components/.@babel/standaloneto generate Abstract Syntax Trees (ASTs) in the browser.ForStatementandWhileStatement) to accurately predictO(n),O(n^2), etc.Checklist
Adds the
CodeComplexityProfilerReact component. The component parses JavaScript with@babel/standalone, detects nestedforandwhileloops, estimates Big-O complexity, and displays analysis results. It also reports parsing errors.