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