|
1
|
""" |
|
2
|
Express.js framework enricher. |
|
3
|
|
|
4
|
Promotes generic Function/Class nodes to semantic Express types: |
|
5
|
- Route — app.get / app.post / app.put / app.delete / app.patch calls |
|
6
|
- Middleware — app.use calls |
|
7
|
- Controller — functions/classes defined in a controllers/ directory |
|
8
|
- Router — Router() instantiations / express.Router() |
|
9
|
""" |
|
10
|
|
|
11
|
from navegador.enrichment.base import EnrichmentResult, FrameworkEnricher |
|
12
|
|
|
13
|
# HTTP method prefixes that indicate a route definition |
|
14
|
_ROUTE_PREFIXES = ( |
|
15
|
"app.get", |
|
16
|
"app.post", |
|
17
|
"app.put", |
|
18
|
"app.delete", |
|
19
|
"app.patch", |
|
20
|
"router.get", |
|
21
|
"router.post", |
|
22
|
"router.put", |
|
23
|
"router.delete", |
|
24
|
"router.patch", |
|
25
|
) |
|
26
|
|
|
27
|
|
|
28
|
class ExpressEnricher(FrameworkEnricher): |
|
29
|
"""Enricher for Express.js codebases.""" |
|
30
|
|
|
31
|
@property |
|
32
|
def framework_name(self) -> str: |
|
33
|
return "express" |
|
34
|
|
|
35
|
@property |
|
36
|
def detection_patterns(self) -> list[str]: |
|
37
|
return ["express"] |
|
38
|
|
|
39
|
def enrich(self) -> EnrichmentResult: |
|
40
|
result = EnrichmentResult() |
|
41
|
|
|
42
|
# ── Routes: app.<method> or router.<method> patterns ───────────────── |
|
43
|
route_rows = ( |
|
44
|
self.store.query( |
|
45
|
"MATCH (n) WHERE (n.name STARTS WITH 'app.get' " |
|
46
|
"OR n.name STARTS WITH 'app.post' " |
|
47
|
"OR n.name STARTS WITH 'app.put' " |
|
48
|
"OR n.name STARTS WITH 'app.delete' " |
|
49
|
"OR n.name STARTS WITH 'app.patch' " |
|
50
|
"OR n.name STARTS WITH 'router.get' " |
|
51
|
"OR n.name STARTS WITH 'router.post' " |
|
52
|
"OR n.name STARTS WITH 'router.put' " |
|
53
|
"OR n.name STARTS WITH 'router.delete' " |
|
54
|
"OR n.name STARTS WITH 'router.patch') " |
|
55
|
"AND n.file_path IS NOT NULL " |
|
56
|
"RETURN n.name, n.file_path", |
|
57
|
).result_set |
|
58
|
or [] |
|
59
|
) |
|
60
|
for name, file_path in route_rows: |
|
61
|
self._promote_node(name, file_path, "ExpressRoute") |
|
62
|
result.promoted += 1 |
|
63
|
result.patterns_found["routes"] = len(route_rows) |
|
64
|
|
|
65
|
# ── Middleware: app.use calls ───────────────────────────────────────── |
|
66
|
middleware_rows = ( |
|
67
|
self.store.query( |
|
68
|
"MATCH (n) WHERE (n.name STARTS WITH 'app.use' " |
|
69
|
"OR n.name STARTS WITH 'router.use') " |
|
70
|
"AND n.file_path IS NOT NULL " |
|
71
|
"RETURN n.name, n.file_path", |
|
72
|
).result_set |
|
73
|
or [] |
|
74
|
) |
|
75
|
for name, file_path in middleware_rows: |
|
76
|
self._promote_node(name, file_path, "ExpressMiddleware") |
|
77
|
result.promoted += 1 |
|
78
|
result.patterns_found["middleware"] = len(middleware_rows) |
|
79
|
|
|
80
|
# ── Controllers: nodes whose file_path contains /controllers/ ───────── |
|
81
|
controller_rows = ( |
|
82
|
self.store.query( |
|
83
|
"MATCH (n) WHERE n.file_path CONTAINS '/controllers/' " |
|
84
|
"AND n.name IS NOT NULL " |
|
85
|
"RETURN n.name, n.file_path", |
|
86
|
).result_set |
|
87
|
or [] |
|
88
|
) |
|
89
|
for name, file_path in controller_rows: |
|
90
|
self._promote_node(name, file_path, "ExpressController") |
|
91
|
result.promoted += 1 |
|
92
|
result.patterns_found["controllers"] = len(controller_rows) |
|
93
|
|
|
94
|
# ── Routers: Router() / express.Router() instantiations ────────────── |
|
95
|
router_rows = ( |
|
96
|
self.store.query( |
|
97
|
"MATCH (n) WHERE (n.name = 'Router' OR n.name CONTAINS 'Router()' " |
|
98
|
"OR n.name CONTAINS 'express.Router') " |
|
99
|
"AND n.file_path IS NOT NULL " |
|
100
|
"RETURN n.name, n.file_path", |
|
101
|
).result_set |
|
102
|
or [] |
|
103
|
) |
|
104
|
for name, file_path in router_rows: |
|
105
|
self._promote_node(name, file_path, "ExpressRouter") |
|
106
|
result.promoted += 1 |
|
107
|
result.patterns_found["routers"] = len(router_rows) |
|
108
|
|
|
109
|
return result |
|
110
|
|