This commit is contained in:
124
src/docgen/detectors/javascript.py
Normal file
124
src/docgen/detectors/javascript.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""JavaScript endpoint detector for Express and Fastify."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from docgen.models import Endpoint, HTTPMethod
|
||||
from docgen.detectors.base import BaseDetector
|
||||
|
||||
|
||||
class JavaScriptDetector(BaseDetector):
|
||||
"""Detector for JavaScript web frameworks."""
|
||||
|
||||
extensions = [".js", ".ts", ".jsx", ".tsx"]
|
||||
framework_name = "javascript"
|
||||
|
||||
EXPRESS_PATTERN = re.compile(
|
||||
r'(app|router)\.(get|post|put|patch|delete|options|head)\s*\(\s*["\']([^"\']+)["\']',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
EXPRESS_APP_ROUTE_PATTERN = re.compile(
|
||||
r'(app|router)\.(route)\s*\(\s*["\']([^"\']+)["\'][\s\S]*?\.((?:get|post|put|patch|delete|options|head))\s*\(',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
FASTIFY_PATTERN = re.compile(
|
||||
r'(?:fastify|instance)\s*\.\s*(get|post|put|patch|delete|options|head)\s*\(\s*["\']([^"\']+)["\']',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
FASTIFY_DECORATOR_PATTERN = re.compile(
|
||||
r'@?(?:get|post|put|patch|delete|options|head)\s*\(\s*["\']([^"\']+)["\']',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
METHOD_MAP = {
|
||||
"get": HTTPMethod.GET,
|
||||
"post": HTTPMethod.POST,
|
||||
"put": HTTPMethod.PUT,
|
||||
"patch": HTTPMethod.PATCH,
|
||||
"delete": HTTPMethod.DELETE,
|
||||
"options": HTTPMethod.OPTIONS,
|
||||
"head": HTTPMethod.HEAD,
|
||||
}
|
||||
|
||||
def detect_endpoints(self, file_path: Path) -> list[Endpoint]:
|
||||
"""Detect endpoints in a JavaScript/TypeScript file."""
|
||||
content = file_path.read_text()
|
||||
endpoints = []
|
||||
|
||||
framework = self._detect_framework(content)
|
||||
|
||||
if framework == "fastify":
|
||||
endpoints.extend(self._detect_fastify(content, file_path))
|
||||
else:
|
||||
endpoints.extend(self._detect_express(content, file_path))
|
||||
|
||||
return endpoints
|
||||
|
||||
def _detect_framework(self, content: str) -> Optional[str]:
|
||||
"""Auto-detect the JavaScript framework."""
|
||||
if "from '@fastify/" in content or "from 'fastify'" in content or "import fastify" in content:
|
||||
return "fastify"
|
||||
if 'require("fastify")' in content or "require('fastify')" in content:
|
||||
return "fastify"
|
||||
if "from 'express'" in content or "import express" in content:
|
||||
return "express"
|
||||
return None
|
||||
|
||||
def _detect_express(self, content: str, file_path: Path) -> list[Endpoint]:
|
||||
"""Detect Express endpoints."""
|
||||
endpoints = []
|
||||
|
||||
for match in self.EXPRESS_PATTERN.finditer(content):
|
||||
router, method, path = match.groups()
|
||||
endpoint = Endpoint(
|
||||
path=path,
|
||||
method=self.METHOD_MAP.get(method.lower(), HTTPMethod.GET),
|
||||
summary=f"{method.upper()} {path}",
|
||||
file_path=str(file_path),
|
||||
line_number=content[:match.start()].count("\n") + 1,
|
||||
)
|
||||
endpoints.append(endpoint)
|
||||
|
||||
for match in self.EXPRESS_APP_ROUTE_PATTERN.finditer(content):
|
||||
router, path, sub_method = match.groups()
|
||||
endpoint = Endpoint(
|
||||
path=path,
|
||||
method=self.METHOD_MAP.get(sub_method.lower(), HTTPMethod.GET),
|
||||
summary=f"{sub_method.upper()} {path}",
|
||||
file_path=str(file_path),
|
||||
line_number=content[:match.start()].count("\n") + 1,
|
||||
)
|
||||
endpoints.append(endpoint)
|
||||
|
||||
return endpoints
|
||||
|
||||
def _detect_fastify(self, content: str, file_path: Path) -> list[Endpoint]:
|
||||
"""Detect Fastify endpoints."""
|
||||
endpoints = []
|
||||
|
||||
for match in self.FASTIFY_PATTERN.finditer(content):
|
||||
method, path = match.groups()
|
||||
endpoint = Endpoint(
|
||||
path=path,
|
||||
method=self.METHOD_MAP.get(method.lower(), HTTPMethod.GET),
|
||||
summary=f"{method.upper()} {path}",
|
||||
file_path=str(file_path),
|
||||
line_number=content[:match.start()].count("\n") + 1,
|
||||
)
|
||||
endpoints.append(endpoint)
|
||||
|
||||
for match in self.FASTIFY_DECORATOR_PATTERN.finditer(content):
|
||||
path = match.group(1)
|
||||
endpoint = Endpoint(
|
||||
path=path,
|
||||
method=HTTPMethod.GET,
|
||||
summary=f"GET {path}",
|
||||
file_path=str(file_path),
|
||||
line_number=content[:match.start()].count("\n") + 1,
|
||||
)
|
||||
endpoints.append(endpoint)
|
||||
|
||||
return endpoints
|
||||
Reference in New Issue
Block a user