Add analyzers module (base, factory, Python, JavaScript)
Some checks failed
CI / test (push) Failing after 15s
CI / build (push) Has been skipped

This commit is contained in:
2026-02-03 06:57:52 +00:00
parent a2903f86b6
commit f44b194e59

View File

@@ -0,0 +1,206 @@
"""JavaScript analyzer for VibeGuard."""
import re
from pathlib import Path
from typing import Any
from vibeguard.analyzers.base import BaseAnalyzer
class JavaScriptAnalyzer(BaseAnalyzer):
"""Analyzer for JavaScript code."""
LANGUAGE_NAME = "javascript"
FILE_EXTENSIONS = [".js", ".jsx"]
MAGIC_STRING_PATTERN = re.compile(r'["\']{3,}[^"\']{20,}["\']{3,}')
HARD_CODED_NUMBER = re.compile(r"[^0-9a-zA-Z](?:1[0-9]{3}|[2-9][0-9]{3,})(?![0-9])")
def parse_file(self, path: Path) -> str | None:
"""Parse a JavaScript file and return the content."""
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except Exception:
return None
def analyze(self, content: str, path: Path) -> list[dict[str, Any]]:
"""Analyze JavaScript content for anti-patterns."""
issues: list[dict[str, Any]] = []
lines = content.split("\n")
for i, line in enumerate(lines, 1):
issues.extend(self._check_console_log(line, i, path))
issues.extend(self._check_var_usage(line, i, path))
issues.extend(self._check_magic_strings(line, i, path))
issues.extend(self._check_hardcoded_values(line, i, path))
issues.extend(self._check_inconsistent_error_handling(line, i, path))
issues.extend(self._check_async_await_without_await(line, i, path))
issues.extend(self._check_missing_await(content, path))
issues.extend(self._check_promise_wrapper(path, content))
return issues
def _check_console_log(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for console.log statements."""
issues: list[dict[str, Any]] = []
if "console.log" in line and "console" not in line.lower():
issues.append(
{
"pattern": "CONSOLE_LOG",
"severity": "warning",
"file": str(path),
"line": line_num,
"message": "Console.log statement detected - remove or use proper logging",
"suggestion": "Use a logging library instead of console.log",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_var_usage(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for var keyword usage."""
issues: list[dict[str, Any]] = []
if re.search(r"\bvar\s+", line):
issues.append(
{
"pattern": "VAR_KEYWORD",
"severity": "warning",
"file": str(path),
"line": line_num,
"message": "'var' keyword detected - use 'const' or 'let' instead",
"suggestion": "Replace 'var' with 'const' (preferred) or 'let' for block scoping",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_magic_strings(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for magic strings."""
issues: list[dict[str, Any]] = []
if self.MAGIC_STRING_PATTERN.search(line):
issues.append(
{
"pattern": "MAGIC_STRING",
"severity": "warning",
"file": str(path),
"line": line_num,
"message": "Long magic string detected - consider using constants",
"suggestion": "Extract to a named constant at module level",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_hardcoded_values(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for hardcoded numeric values."""
issues: list[dict[str, Any]] = []
if self.HARD_CODED_NUMBER.search(line):
issues.append(
{
"pattern": "HARDCODED_VALUE",
"severity": "warning",
"file": str(path),
"line": line_num,
"message": "Large hardcoded numeric value detected",
"suggestion": "Extract to a named constant with descriptive name",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_inconsistent_error_handling(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for inconsistent error handling patterns."""
issues: list[dict[str, Any]] = []
if "catch (e)" in line or "catch(err)" in line:
issues.append(
{
"pattern": "INCONSISTENT_ERROR_HANDLING",
"severity": "warning",
"file": str(path),
"line": line_num,
"message": "Generic error variable name - use more descriptive names",
"suggestion": "Use specific error names like 'error', 'err', or 'exception'",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_async_await_without_await(
self, line: str, line_num: int, path: Path
) -> list[dict[str, Any]]:
"""Check for async function without await usage."""
issues: list[dict[str, Any]] = []
if re.search(r"async\s+function.*await", line):
pass
elif re.search(r"const\s+\w+\s*=\s*async", line):
pass
elif "await" in line and line.strip().startswith("await"):
pass
return issues
def _check_missing_await(self, content: str, path: Path) -> list[dict[str, Any]]:
"""Check for potential missing await keywords."""
issues: list[dict[str, Any]] = []
async_pattern = re.compile(r"async\s+\([^)]*\)\s*=>(?!\s*await)")
matches = async_pattern.findall(content)
for match in matches:
issues.append(
{
"pattern": "POTENTIAL_MISSING_AWAIT",
"severity": "info",
"file": str(path),
"line": 0,
"message": "Arrow function with async but no await in body",
"suggestion": "Check if async keyword is needed or if await is missing",
"language": self.LANGUAGE_NAME,
}
)
return issues
def _check_promise_wrapper(self, path: Path, content: str) -> list[dict[str, Any]]:
"""Check for unnecessary Promise wrappers."""
issues: list[dict[str, Any]] = []
pattern = re.compile(r"new\s+Promise\s*\(\s*\(\s*resolve\s*\)\s*=>\s*{[^}]*resolve\([^)]+\)")
matches = pattern.findall(content)
for match in matches:
issues.append(
{
"pattern": "UNNECESSARY_PROMISE_WRAPPER",
"severity": "warning",
"file": str(path),
"line": 0,
"message": "Unnecessary Promise wrapper detected",
"suggestion": "Return the value directly instead of wrapping in Promise",
"language": self.LANGUAGE_NAME,
}
)
return issues