osscodeiq 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. osscodeiq/__init__.py +0 -0
  2. osscodeiq/analyzer.py +467 -0
  3. osscodeiq/cache/__init__.py +0 -0
  4. osscodeiq/cache/hasher.py +23 -0
  5. osscodeiq/cache/store.py +300 -0
  6. osscodeiq/classifiers/__init__.py +0 -0
  7. osscodeiq/classifiers/layer_classifier.py +69 -0
  8. osscodeiq/cli.py +721 -0
  9. osscodeiq/config.py +113 -0
  10. osscodeiq/detectors/__init__.py +0 -0
  11. osscodeiq/detectors/auth/__init__.py +0 -0
  12. osscodeiq/detectors/auth/certificate_auth.py +139 -0
  13. osscodeiq/detectors/auth/ldap_auth.py +89 -0
  14. osscodeiq/detectors/auth/session_header_auth.py +120 -0
  15. osscodeiq/detectors/base.py +41 -0
  16. osscodeiq/detectors/config/__init__.py +0 -0
  17. osscodeiq/detectors/config/batch_structure.py +128 -0
  18. osscodeiq/detectors/config/cloudformation.py +183 -0
  19. osscodeiq/detectors/config/docker_compose.py +179 -0
  20. osscodeiq/detectors/config/github_actions.py +150 -0
  21. osscodeiq/detectors/config/gitlab_ci.py +216 -0
  22. osscodeiq/detectors/config/helm_chart.py +187 -0
  23. osscodeiq/detectors/config/ini_structure.py +101 -0
  24. osscodeiq/detectors/config/json_structure.py +72 -0
  25. osscodeiq/detectors/config/kubernetes.py +305 -0
  26. osscodeiq/detectors/config/kubernetes_rbac.py +212 -0
  27. osscodeiq/detectors/config/openapi.py +194 -0
  28. osscodeiq/detectors/config/package_json.py +99 -0
  29. osscodeiq/detectors/config/properties_detector.py +108 -0
  30. osscodeiq/detectors/config/pyproject_toml.py +169 -0
  31. osscodeiq/detectors/config/sql_structure.py +155 -0
  32. osscodeiq/detectors/config/toml_structure.py +93 -0
  33. osscodeiq/detectors/config/tsconfig_json.py +105 -0
  34. osscodeiq/detectors/config/yaml_structure.py +82 -0
  35. osscodeiq/detectors/cpp/__init__.py +0 -0
  36. osscodeiq/detectors/cpp/cpp_structures.py +192 -0
  37. osscodeiq/detectors/csharp/__init__.py +0 -0
  38. osscodeiq/detectors/csharp/csharp_efcore.py +184 -0
  39. osscodeiq/detectors/csharp/csharp_minimal_apis.py +156 -0
  40. osscodeiq/detectors/csharp/csharp_structures.py +317 -0
  41. osscodeiq/detectors/docs/__init__.py +0 -0
  42. osscodeiq/detectors/docs/markdown_structure.py +117 -0
  43. osscodeiq/detectors/frontend/__init__.py +0 -0
  44. osscodeiq/detectors/frontend/angular_components.py +177 -0
  45. osscodeiq/detectors/frontend/frontend_routes.py +259 -0
  46. osscodeiq/detectors/frontend/react_components.py +148 -0
  47. osscodeiq/detectors/frontend/svelte_components.py +84 -0
  48. osscodeiq/detectors/frontend/vue_components.py +150 -0
  49. osscodeiq/detectors/generic/__init__.py +1 -0
  50. osscodeiq/detectors/generic/imports_detector.py +413 -0
  51. osscodeiq/detectors/go/__init__.py +0 -0
  52. osscodeiq/detectors/go/go_orm.py +202 -0
  53. osscodeiq/detectors/go/go_structures.py +162 -0
  54. osscodeiq/detectors/go/go_web.py +157 -0
  55. osscodeiq/detectors/iac/__init__.py +0 -0
  56. osscodeiq/detectors/iac/bicep.py +135 -0
  57. osscodeiq/detectors/iac/dockerfile.py +182 -0
  58. osscodeiq/detectors/iac/terraform.py +188 -0
  59. osscodeiq/detectors/java/__init__.py +0 -0
  60. osscodeiq/detectors/java/azure_functions.py +424 -0
  61. osscodeiq/detectors/java/azure_messaging.py +350 -0
  62. osscodeiq/detectors/java/class_hierarchy.py +349 -0
  63. osscodeiq/detectors/java/config_def.py +82 -0
  64. osscodeiq/detectors/java/cosmos_db.py +105 -0
  65. osscodeiq/detectors/java/graphql_resolver.py +188 -0
  66. osscodeiq/detectors/java/grpc_service.py +142 -0
  67. osscodeiq/detectors/java/ibm_mq.py +178 -0
  68. osscodeiq/detectors/java/jaxrs.py +160 -0
  69. osscodeiq/detectors/java/jdbc.py +196 -0
  70. osscodeiq/detectors/java/jms.py +116 -0
  71. osscodeiq/detectors/java/jpa_entity.py +143 -0
  72. osscodeiq/detectors/java/kafka.py +113 -0
  73. osscodeiq/detectors/java/kafka_protocol.py +70 -0
  74. osscodeiq/detectors/java/micronaut.py +248 -0
  75. osscodeiq/detectors/java/module_deps.py +191 -0
  76. osscodeiq/detectors/java/public_api.py +206 -0
  77. osscodeiq/detectors/java/quarkus.py +176 -0
  78. osscodeiq/detectors/java/rabbitmq.py +150 -0
  79. osscodeiq/detectors/java/raw_sql.py +136 -0
  80. osscodeiq/detectors/java/repository.py +131 -0
  81. osscodeiq/detectors/java/rmi.py +129 -0
  82. osscodeiq/detectors/java/spring_events.py +117 -0
  83. osscodeiq/detectors/java/spring_rest.py +168 -0
  84. osscodeiq/detectors/java/spring_security.py +212 -0
  85. osscodeiq/detectors/java/tibco_ems.py +193 -0
  86. osscodeiq/detectors/java/websocket.py +188 -0
  87. osscodeiq/detectors/kotlin/__init__.py +0 -0
  88. osscodeiq/detectors/kotlin/kotlin_structures.py +124 -0
  89. osscodeiq/detectors/kotlin/ktor_routes.py +163 -0
  90. osscodeiq/detectors/proto/__init__.py +0 -0
  91. osscodeiq/detectors/proto/proto_structure.py +153 -0
  92. osscodeiq/detectors/python/__init__.py +0 -0
  93. osscodeiq/detectors/python/celery_tasks.py +88 -0
  94. osscodeiq/detectors/python/django_auth.py +132 -0
  95. osscodeiq/detectors/python/django_models.py +157 -0
  96. osscodeiq/detectors/python/django_views.py +74 -0
  97. osscodeiq/detectors/python/fastapi_auth.py +143 -0
  98. osscodeiq/detectors/python/fastapi_routes.py +68 -0
  99. osscodeiq/detectors/python/flask_routes.py +67 -0
  100. osscodeiq/detectors/python/kafka_python.py +175 -0
  101. osscodeiq/detectors/python/pydantic_models.py +115 -0
  102. osscodeiq/detectors/python/python_structures.py +234 -0
  103. osscodeiq/detectors/python/sqlalchemy_models.py +82 -0
  104. osscodeiq/detectors/registry.py +100 -0
  105. osscodeiq/detectors/rust/__init__.py +0 -0
  106. osscodeiq/detectors/rust/actix_web.py +234 -0
  107. osscodeiq/detectors/rust/rust_structures.py +174 -0
  108. osscodeiq/detectors/scala/__init__.py +0 -0
  109. osscodeiq/detectors/scala/scala_structures.py +128 -0
  110. osscodeiq/detectors/shell/__init__.py +0 -0
  111. osscodeiq/detectors/shell/bash_detector.py +127 -0
  112. osscodeiq/detectors/shell/powershell_detector.py +118 -0
  113. osscodeiq/detectors/typescript/__init__.py +0 -0
  114. osscodeiq/detectors/typescript/express_routes.py +55 -0
  115. osscodeiq/detectors/typescript/fastify_routes.py +156 -0
  116. osscodeiq/detectors/typescript/graphql_resolvers.py +100 -0
  117. osscodeiq/detectors/typescript/kafka_js.py +164 -0
  118. osscodeiq/detectors/typescript/mongoose_orm.py +151 -0
  119. osscodeiq/detectors/typescript/nestjs_controllers.py +99 -0
  120. osscodeiq/detectors/typescript/nestjs_guards.py +138 -0
  121. osscodeiq/detectors/typescript/passport_jwt.py +133 -0
  122. osscodeiq/detectors/typescript/prisma_orm.py +96 -0
  123. osscodeiq/detectors/typescript/remix_routes.py +160 -0
  124. osscodeiq/detectors/typescript/sequelize_orm.py +136 -0
  125. osscodeiq/detectors/typescript/typeorm_entities.py +86 -0
  126. osscodeiq/detectors/typescript/typescript_structures.py +185 -0
  127. osscodeiq/detectors/utils.py +49 -0
  128. osscodeiq/discovery/__init__.py +11 -0
  129. osscodeiq/discovery/change_detector.py +97 -0
  130. osscodeiq/discovery/file_discovery.py +342 -0
  131. osscodeiq/flow/__init__.py +0 -0
  132. osscodeiq/flow/engine.py +78 -0
  133. osscodeiq/flow/models.py +72 -0
  134. osscodeiq/flow/renderer.py +127 -0
  135. osscodeiq/flow/templates/interactive.html +252 -0
  136. osscodeiq/flow/vendor/cytoscape-dagre.min.js +8 -0
  137. osscodeiq/flow/vendor/cytoscape.min.js +32 -0
  138. osscodeiq/flow/vendor/dagre.min.js +3809 -0
  139. osscodeiq/flow/views.py +357 -0
  140. osscodeiq/graph/__init__.py +0 -0
  141. osscodeiq/graph/backend.py +52 -0
  142. osscodeiq/graph/backends/__init__.py +23 -0
  143. osscodeiq/graph/backends/kuzu.py +576 -0
  144. osscodeiq/graph/backends/networkx.py +135 -0
  145. osscodeiq/graph/backends/sqlite_backend.py +406 -0
  146. osscodeiq/graph/builder.py +297 -0
  147. osscodeiq/graph/query.py +228 -0
  148. osscodeiq/graph/store.py +183 -0
  149. osscodeiq/graph/views.py +231 -0
  150. osscodeiq/models/__init__.py +17 -0
  151. osscodeiq/models/graph.py +116 -0
  152. osscodeiq/output/__init__.py +0 -0
  153. osscodeiq/output/dot.py +171 -0
  154. osscodeiq/output/mermaid.py +160 -0
  155. osscodeiq/output/safety.py +58 -0
  156. osscodeiq/output/serializers.py +42 -0
  157. osscodeiq/parsing/__init__.py +5 -0
  158. osscodeiq/parsing/languages/__init__.py +0 -0
  159. osscodeiq/parsing/languages/base.py +23 -0
  160. osscodeiq/parsing/languages/java.py +68 -0
  161. osscodeiq/parsing/languages/python.py +57 -0
  162. osscodeiq/parsing/languages/typescript.py +95 -0
  163. osscodeiq/parsing/parser_manager.py +125 -0
  164. osscodeiq/parsing/structured/__init__.py +0 -0
  165. osscodeiq/parsing/structured/gradle_parser.py +78 -0
  166. osscodeiq/parsing/structured/json_parser.py +24 -0
  167. osscodeiq/parsing/structured/properties_parser.py +56 -0
  168. osscodeiq/parsing/structured/sql_parser.py +54 -0
  169. osscodeiq/parsing/structured/xml_parser.py +148 -0
  170. osscodeiq/parsing/structured/yaml_parser.py +38 -0
  171. osscodeiq/server/__init__.py +7 -0
  172. osscodeiq/server/app.py +53 -0
  173. osscodeiq/server/mcp_server.py +174 -0
  174. osscodeiq/server/middleware.py +16 -0
  175. osscodeiq/server/routes.py +184 -0
  176. osscodeiq/server/service.py +445 -0
  177. osscodeiq/server/templates/welcome.html +56 -0
  178. osscodeiq-0.0.0.dist-info/METADATA +30 -0
  179. osscodeiq-0.0.0.dist-info/RECORD +183 -0
  180. osscodeiq-0.0.0.dist-info/WHEEL +5 -0
  181. osscodeiq-0.0.0.dist-info/entry_points.txt +2 -0
  182. osscodeiq-0.0.0.dist-info/licenses/LICENSE +21 -0
  183. osscodeiq-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,148 @@
1
+ """React component and hook detector."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ from osscodeiq.detectors.base import DetectorContext, DetectorResult
8
+ from osscodeiq.detectors.utils import decode_text
9
+ from osscodeiq.models.graph import EdgeKind, GraphEdge, GraphNode, NodeKind, SourceLocation
10
+
11
+
12
+ class ReactComponentDetector:
13
+ """Detects React function/class components, custom hooks, and child rendering."""
14
+
15
+ name: str = "frontend.react_components"
16
+ supported_languages: tuple[str, ...] = ("typescript", "javascript")
17
+
18
+ # Function component patterns
19
+ _EXPORT_DEFAULT_FUNC = re.compile(
20
+ r"export\s+default\s+function\s+([A-Z]\w*)\s*\("
21
+ )
22
+ _EXPORT_CONST_ARROW = re.compile(
23
+ r"export\s+const\s+([A-Z]\w*)\s*=\s*\("
24
+ )
25
+ _EXPORT_CONST_FC = re.compile(
26
+ r"export\s+const\s+([A-Z]\w*)\s*:\s*React\.FC"
27
+ )
28
+
29
+ # Class component patterns
30
+ _CLASS_EXTENDS_REACT_COMPONENT = re.compile(
31
+ r"class\s+([A-Z]\w*)\s+extends\s+React\.Component"
32
+ )
33
+ _CLASS_EXTENDS_COMPONENT = re.compile(
34
+ r"class\s+([A-Z]\w*)\s+extends\s+Component\b"
35
+ )
36
+
37
+ # Custom hook patterns (exported functions starting with "use")
38
+ _EXPORT_FUNC_HOOK = re.compile(
39
+ r"export\s+function\s+(use[A-Z]\w*)\s*\("
40
+ )
41
+ _EXPORT_CONST_HOOK = re.compile(
42
+ r"export\s+const\s+(use[A-Z]\w*)\s*=\s*"
43
+ )
44
+
45
+ # JSX child reference: <SomeComponent (uppercase tag names are components)
46
+ _JSX_TAG = re.compile(
47
+ r"<([A-Z]\w*)\b"
48
+ )
49
+
50
+ def detect(self, ctx: DetectorContext) -> DetectorResult:
51
+ result = DetectorResult()
52
+ text = decode_text(ctx)
53
+
54
+ component_names: list[str] = []
55
+
56
+ # --- Function components ---
57
+ for pattern in (self._EXPORT_DEFAULT_FUNC, self._EXPORT_CONST_ARROW, self._EXPORT_CONST_FC):
58
+ for match in pattern.finditer(text):
59
+ name = match.group(1)
60
+ if name in [c for c, _ in [(n, None) for n in component_names]]:
61
+ continue
62
+ line = text[: match.start()].count("\n") + 1
63
+ node_id = f"react:{ctx.file_path}:component:{name}"
64
+ result.nodes.append(
65
+ GraphNode(
66
+ id=node_id,
67
+ kind=NodeKind.COMPONENT,
68
+ label=name,
69
+ fqn=f"{ctx.file_path}::{name}",
70
+ module=ctx.module_name,
71
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
72
+ properties={
73
+ "framework": "react",
74
+ "component_type": "function",
75
+ },
76
+ )
77
+ )
78
+ component_names.append(name)
79
+
80
+ # --- Class components ---
81
+ for pattern in (self._CLASS_EXTENDS_REACT_COMPONENT, self._CLASS_EXTENDS_COMPONENT):
82
+ for match in pattern.finditer(text):
83
+ name = match.group(1)
84
+ if name in component_names:
85
+ continue
86
+ line = text[: match.start()].count("\n") + 1
87
+ node_id = f"react:{ctx.file_path}:component:{name}"
88
+ result.nodes.append(
89
+ GraphNode(
90
+ id=node_id,
91
+ kind=NodeKind.COMPONENT,
92
+ label=name,
93
+ fqn=f"{ctx.file_path}::{name}",
94
+ module=ctx.module_name,
95
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
96
+ properties={
97
+ "framework": "react",
98
+ "component_type": "class",
99
+ },
100
+ )
101
+ )
102
+ component_names.append(name)
103
+
104
+ # --- Custom hooks ---
105
+ hook_names: list[str] = []
106
+ for pattern in (self._EXPORT_FUNC_HOOK, self._EXPORT_CONST_HOOK):
107
+ for match in pattern.finditer(text):
108
+ name = match.group(1)
109
+ if name in hook_names:
110
+ continue
111
+ line = text[: match.start()].count("\n") + 1
112
+ node_id = f"react:{ctx.file_path}:hook:{name}"
113
+ result.nodes.append(
114
+ GraphNode(
115
+ id=node_id,
116
+ kind=NodeKind.HOOK,
117
+ label=name,
118
+ fqn=f"{ctx.file_path}::{name}",
119
+ module=ctx.module_name,
120
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
121
+ properties={
122
+ "framework": "react",
123
+ },
124
+ )
125
+ )
126
+ hook_names.append(name)
127
+
128
+ # --- RENDERS edges (JSX child components) ---
129
+ all_detected = set(component_names) | set(hook_names)
130
+ child_names: set[str] = set()
131
+ for match in self._JSX_TAG.finditer(text):
132
+ tag = match.group(1)
133
+ if tag not in all_detected:
134
+ child_names.add(tag)
135
+
136
+ for comp in component_names:
137
+ source_id = f"react:{ctx.file_path}:component:{comp}"
138
+ for child in sorted(child_names):
139
+ result.edges.append(
140
+ GraphEdge(
141
+ source=source_id,
142
+ target=child,
143
+ kind=EdgeKind.RENDERS,
144
+ label=f"{comp} renders {child}",
145
+ )
146
+ )
147
+
148
+ return result
@@ -0,0 +1,84 @@
1
+ """Svelte component detector."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import PurePosixPath
7
+
8
+ from osscodeiq.detectors.base import DetectorContext, DetectorResult
9
+ from osscodeiq.detectors.utils import decode_text
10
+ from osscodeiq.models.graph import GraphNode, NodeKind, SourceLocation
11
+
12
+
13
+ class SvelteComponentDetector:
14
+ """Detects Svelte component patterns: props, reactive statements, script blocks."""
15
+
16
+ name: str = "frontend.svelte_components"
17
+ supported_languages: tuple[str, ...] = ("typescript", "javascript", "svelte")
18
+
19
+ # export let propName or export let propName = defaultValue
20
+ _PROP_PATTERN = re.compile(r"export\s+let\s+(\w+)")
21
+
22
+ # $: reactive statement
23
+ _REACTIVE_PATTERN = re.compile(r"^\s*\$:", re.MULTILINE)
24
+
25
+ # Detect Svelte script blocks (not used for HTML sanitization — structural detection only)
26
+ _SCRIPT_PATTERN = re.compile(r"^<script\b", re.MULTILINE) # nosec
27
+
28
+ # Detect template content — any HTML tag that isn't script/style
29
+ _HTML_TEMPLATE_PATTERN = re.compile(r"^<(?!script\b|style\b|/)[a-zA-Z]\w*[\s>]", re.MULTILINE)
30
+
31
+ def detect(self, ctx: DetectorContext) -> DetectorResult:
32
+ result = DetectorResult()
33
+ text = decode_text(ctx)
34
+
35
+ has_props = bool(self._PROP_PATTERN.search(text))
36
+ has_reactive = bool(self._REACTIVE_PATTERN.search(text))
37
+ has_script = bool(self._SCRIPT_PATTERN.search(text))
38
+ has_template = bool(self._HTML_TEMPLATE_PATTERN.search(text))
39
+
40
+ # A file is a Svelte component if it has export let (props) or reactive
41
+ # statements, or if it has both a <script> block and HTML template content.
42
+ is_svelte = has_props or has_reactive or (has_script and has_template)
43
+
44
+ if not is_svelte:
45
+ return result
46
+
47
+ # Derive component name from filename (without extension)
48
+ component_name = PurePosixPath(ctx.file_path).stem
49
+
50
+ # Collect props
51
+ props: list[str] = []
52
+ for match in self._PROP_PATTERN.finditer(text):
53
+ props.append(match.group(1))
54
+
55
+ # Count reactive statements
56
+ reactive_count = len(self._REACTIVE_PATTERN.findall(text))
57
+
58
+ # Find the first significant line for location
59
+ first_line = 1
60
+ for pattern in (self._SCRIPT_PATTERN, self._PROP_PATTERN, self._REACTIVE_PATTERN):
61
+ m = pattern.search(text)
62
+ if m:
63
+ candidate = text[: m.start()].count("\n") + 1
64
+ first_line = min(first_line, candidate) if first_line > 1 else candidate
65
+ break
66
+
67
+ node_id = f"svelte:{ctx.file_path}:component:{component_name}"
68
+ result.nodes.append(
69
+ GraphNode(
70
+ id=node_id,
71
+ kind=NodeKind.COMPONENT,
72
+ label=component_name,
73
+ fqn=f"{ctx.file_path}::{component_name}",
74
+ module=ctx.module_name,
75
+ location=SourceLocation(file_path=ctx.file_path, line_start=first_line),
76
+ properties={
77
+ "framework": "svelte",
78
+ "props": props,
79
+ "reactive_statements": reactive_count,
80
+ },
81
+ )
82
+ )
83
+
84
+ return result
@@ -0,0 +1,150 @@
1
+ """Vue component and composable detector."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ from osscodeiq.detectors.base import DetectorContext, DetectorResult
8
+ from osscodeiq.detectors.utils import decode_text
9
+ from osscodeiq.models.graph import GraphNode, NodeKind, SourceLocation
10
+
11
+
12
+ class VueComponentDetector:
13
+ """Detects Vue components (Options API, Composition API, <script setup>) and composables."""
14
+
15
+ name: str = "frontend.vue_components"
16
+ supported_languages: tuple[str, ...] = ("typescript", "javascript", "vue")
17
+
18
+ # defineComponent with name property
19
+ _DEFINE_COMPONENT_NAME = re.compile(
20
+ r"export\s+default\s+defineComponent\s*\(\s*\{[^}]*?name\s*:\s*['\"](\w+)['\"]",
21
+ re.DOTALL,
22
+ )
23
+
24
+ # Options API: export default { name: 'CompName'
25
+ _OPTIONS_API_NAME = re.compile(
26
+ r"export\s+default\s+\{\s*name\s*:\s*['\"](\w+)['\"]"
27
+ )
28
+
29
+ # <script setup> block detection
30
+ _SCRIPT_SETUP = re.compile(
31
+ r"<script\s+setup(?:\s+lang\s*=\s*['\"](?:ts|js)['\"])?\s*>"
32
+ )
33
+
34
+ # Composable patterns (functions starting with "use")
35
+ _EXPORT_FUNC_COMPOSABLE = re.compile(
36
+ r"export\s+function\s+(use[A-Z]\w*)\s*\("
37
+ )
38
+ _EXPORT_CONST_COMPOSABLE = re.compile(
39
+ r"export\s+const\s+(use[A-Z]\w*)\s*=\s*"
40
+ )
41
+
42
+ def _extract_script_setup_name(self, file_path: str) -> str | None:
43
+ """Derive component name from file path for <script setup> SFCs."""
44
+ # e.g. "components/UserProfile.vue" -> "UserProfile"
45
+ parts = file_path.replace("\\", "/").split("/")
46
+ filename = parts[-1]
47
+ if filename.endswith(".vue"):
48
+ return filename[: -len(".vue")]
49
+ return None
50
+
51
+ def detect(self, ctx: DetectorContext) -> DetectorResult:
52
+ result = DetectorResult()
53
+ text = decode_text(ctx)
54
+
55
+ component_names: list[str] = []
56
+
57
+ # --- defineComponent with name ---
58
+ for match in self._DEFINE_COMPONENT_NAME.finditer(text):
59
+ name = match.group(1)
60
+ if name in component_names:
61
+ continue
62
+ line = text[: match.start()].count("\n") + 1
63
+ node_id = f"vue:{ctx.file_path}:component:{name}"
64
+ result.nodes.append(
65
+ GraphNode(
66
+ id=node_id,
67
+ kind=NodeKind.COMPONENT,
68
+ label=name,
69
+ fqn=f"{ctx.file_path}::{name}",
70
+ module=ctx.module_name,
71
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
72
+ properties={
73
+ "framework": "vue",
74
+ "api_style": "composition",
75
+ },
76
+ )
77
+ )
78
+ component_names.append(name)
79
+
80
+ # --- Options API ---
81
+ for match in self._OPTIONS_API_NAME.finditer(text):
82
+ name = match.group(1)
83
+ if name in component_names:
84
+ continue
85
+ line = text[: match.start()].count("\n") + 1
86
+ node_id = f"vue:{ctx.file_path}:component:{name}"
87
+ result.nodes.append(
88
+ GraphNode(
89
+ id=node_id,
90
+ kind=NodeKind.COMPONENT,
91
+ label=name,
92
+ fqn=f"{ctx.file_path}::{name}",
93
+ module=ctx.module_name,
94
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
95
+ properties={
96
+ "framework": "vue",
97
+ "api_style": "options",
98
+ },
99
+ )
100
+ )
101
+ component_names.append(name)
102
+
103
+ # --- <script setup> ---
104
+ for match in self._SCRIPT_SETUP.finditer(text):
105
+ comp_name = self._extract_script_setup_name(ctx.file_path)
106
+ if comp_name is None or comp_name in component_names:
107
+ continue
108
+ line = text[: match.start()].count("\n") + 1
109
+ node_id = f"vue:{ctx.file_path}:component:{comp_name}"
110
+ result.nodes.append(
111
+ GraphNode(
112
+ id=node_id,
113
+ kind=NodeKind.COMPONENT,
114
+ label=comp_name,
115
+ fqn=f"{ctx.file_path}::{comp_name}",
116
+ module=ctx.module_name,
117
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
118
+ properties={
119
+ "framework": "vue",
120
+ "api_style": "script_setup",
121
+ },
122
+ )
123
+ )
124
+ component_names.append(comp_name)
125
+
126
+ # --- Composables (hooks) ---
127
+ hook_names: list[str] = []
128
+ for pattern in (self._EXPORT_FUNC_COMPOSABLE, self._EXPORT_CONST_COMPOSABLE):
129
+ for match in pattern.finditer(text):
130
+ name = match.group(1)
131
+ if name in hook_names:
132
+ continue
133
+ line = text[: match.start()].count("\n") + 1
134
+ node_id = f"vue:{ctx.file_path}:hook:{name}"
135
+ result.nodes.append(
136
+ GraphNode(
137
+ id=node_id,
138
+ kind=NodeKind.HOOK,
139
+ label=name,
140
+ fqn=f"{ctx.file_path}::{name}",
141
+ module=ctx.module_name,
142
+ location=SourceLocation(file_path=ctx.file_path, line_start=line),
143
+ properties={
144
+ "framework": "vue",
145
+ },
146
+ )
147
+ )
148
+ hook_names.append(name)
149
+
150
+ return result
@@ -0,0 +1 @@
1
+