circle-ir 3.1.0

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 (194) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +200 -0
  3. package/configs/sinks/code_injection.yaml +672 -0
  4. package/configs/sinks/command.yaml +917 -0
  5. package/configs/sinks/deserialization.yaml +105 -0
  6. package/configs/sinks/ldap.yaml +136 -0
  7. package/configs/sinks/nodejs.json +629 -0
  8. package/configs/sinks/path.yaml +715 -0
  9. package/configs/sinks/python.json +501 -0
  10. package/configs/sinks/rust.json +339 -0
  11. package/configs/sinks/sql.yaml +233 -0
  12. package/configs/sinks/ssrf.yaml +160 -0
  13. package/configs/sinks/xpath.yaml +121 -0
  14. package/configs/sinks/xss.yaml +727 -0
  15. package/configs/sources/db_sources.yaml +90 -0
  16. package/configs/sources/env_sources.yaml +94 -0
  17. package/configs/sources/express.json +197 -0
  18. package/configs/sources/file_sources.yaml +164 -0
  19. package/configs/sources/http_sources.yaml +379 -0
  20. package/configs/sources/io_sources.yaml +519 -0
  21. package/configs/sources/network_sources.yaml +99 -0
  22. package/configs/sources/python.json +230 -0
  23. package/configs/sources/rust.json +286 -0
  24. package/configs/sources/spring.yaml +70 -0
  25. package/dist/analysis/advisory-db.d.ts +86 -0
  26. package/dist/analysis/advisory-db.js +104 -0
  27. package/dist/analysis/advisory-db.js.map +1 -0
  28. package/dist/analysis/cargo-parser.d.ts +42 -0
  29. package/dist/analysis/cargo-parser.js +102 -0
  30. package/dist/analysis/cargo-parser.js.map +1 -0
  31. package/dist/analysis/config-loader.d.ts +37 -0
  32. package/dist/analysis/config-loader.js +1561 -0
  33. package/dist/analysis/config-loader.js.map +1 -0
  34. package/dist/analysis/constant-propagation/ast-utils.d.ts +25 -0
  35. package/dist/analysis/constant-propagation/ast-utils.js +34 -0
  36. package/dist/analysis/constant-propagation/ast-utils.js.map +1 -0
  37. package/dist/analysis/constant-propagation/evaluator.d.ts +32 -0
  38. package/dist/analysis/constant-propagation/evaluator.js +296 -0
  39. package/dist/analysis/constant-propagation/evaluator.js.map +1 -0
  40. package/dist/analysis/constant-propagation/index.d.ts +62 -0
  41. package/dist/analysis/constant-propagation/index.js +152 -0
  42. package/dist/analysis/constant-propagation/index.js.map +1 -0
  43. package/dist/analysis/constant-propagation/patterns.d.ts +8 -0
  44. package/dist/analysis/constant-propagation/patterns.js +126 -0
  45. package/dist/analysis/constant-propagation/patterns.js.map +1 -0
  46. package/dist/analysis/constant-propagation/propagator.d.ts +180 -0
  47. package/dist/analysis/constant-propagation/propagator.js +1985 -0
  48. package/dist/analysis/constant-propagation/propagator.js.map +1 -0
  49. package/dist/analysis/constant-propagation/types.d.ts +63 -0
  50. package/dist/analysis/constant-propagation/types.js +5 -0
  51. package/dist/analysis/constant-propagation/types.js.map +1 -0
  52. package/dist/analysis/constant-propagation.d.ts +9 -0
  53. package/dist/analysis/constant-propagation.js +18 -0
  54. package/dist/analysis/constant-propagation.js.map +1 -0
  55. package/dist/analysis/dependency-scanner.d.ts +79 -0
  56. package/dist/analysis/dependency-scanner.js +122 -0
  57. package/dist/analysis/dependency-scanner.js.map +1 -0
  58. package/dist/analysis/dfg-verifier.d.ts +116 -0
  59. package/dist/analysis/dfg-verifier.js +399 -0
  60. package/dist/analysis/dfg-verifier.js.map +1 -0
  61. package/dist/analysis/findings.d.ts +11 -0
  62. package/dist/analysis/findings.js +228 -0
  63. package/dist/analysis/findings.js.map +1 -0
  64. package/dist/analysis/index.d.ts +16 -0
  65. package/dist/analysis/index.js +18 -0
  66. package/dist/analysis/index.js.map +1 -0
  67. package/dist/analysis/interprocedural.d.ts +99 -0
  68. package/dist/analysis/interprocedural.js +526 -0
  69. package/dist/analysis/interprocedural.js.map +1 -0
  70. package/dist/analysis/path-finder.d.ts +133 -0
  71. package/dist/analysis/path-finder.js +354 -0
  72. package/dist/analysis/path-finder.js.map +1 -0
  73. package/dist/analysis/rules.d.ts +75 -0
  74. package/dist/analysis/rules.js +332 -0
  75. package/dist/analysis/rules.js.map +1 -0
  76. package/dist/analysis/semver.d.ts +27 -0
  77. package/dist/analysis/semver.js +127 -0
  78. package/dist/analysis/semver.js.map +1 -0
  79. package/dist/analysis/taint-matcher.d.ts +15 -0
  80. package/dist/analysis/taint-matcher.js +634 -0
  81. package/dist/analysis/taint-matcher.js.map +1 -0
  82. package/dist/analysis/taint-propagation.d.ts +67 -0
  83. package/dist/analysis/taint-propagation.js +298 -0
  84. package/dist/analysis/taint-propagation.js.map +1 -0
  85. package/dist/analysis/unresolved.d.ts +14 -0
  86. package/dist/analysis/unresolved.js +202 -0
  87. package/dist/analysis/unresolved.js.map +1 -0
  88. package/dist/analyzer.d.ts +43 -0
  89. package/dist/analyzer.js +1010 -0
  90. package/dist/analyzer.js.map +1 -0
  91. package/dist/browser/circle-ir.js +16576 -0
  92. package/dist/browser.d.ts +38 -0
  93. package/dist/browser.js +38 -0
  94. package/dist/browser.js.map +1 -0
  95. package/dist/core/circle-ir-core.cjs +13626 -0
  96. package/dist/core/circle-ir-core.d.ts +59 -0
  97. package/dist/core/circle-ir-core.js +13591 -0
  98. package/dist/core/extractors/calls.d.ts +13 -0
  99. package/dist/core/extractors/calls.js +1429 -0
  100. package/dist/core/extractors/calls.js.map +1 -0
  101. package/dist/core/extractors/cfg.d.ts +9 -0
  102. package/dist/core/extractors/cfg.js +519 -0
  103. package/dist/core/extractors/cfg.js.map +1 -0
  104. package/dist/core/extractors/dfg.d.ts +12 -0
  105. package/dist/core/extractors/dfg.js +1081 -0
  106. package/dist/core/extractors/dfg.js.map +1 -0
  107. package/dist/core/extractors/exports.d.ts +14 -0
  108. package/dist/core/extractors/exports.js +80 -0
  109. package/dist/core/extractors/exports.js.map +1 -0
  110. package/dist/core/extractors/imports.d.ts +9 -0
  111. package/dist/core/extractors/imports.js +739 -0
  112. package/dist/core/extractors/imports.js.map +1 -0
  113. package/dist/core/extractors/index.d.ts +10 -0
  114. package/dist/core/extractors/index.js +11 -0
  115. package/dist/core/extractors/index.js.map +1 -0
  116. package/dist/core/extractors/meta.d.ts +10 -0
  117. package/dist/core/extractors/meta.js +109 -0
  118. package/dist/core/extractors/meta.js.map +1 -0
  119. package/dist/core/extractors/types.d.ts +10 -0
  120. package/dist/core/extractors/types.js +1479 -0
  121. package/dist/core/extractors/types.js.map +1 -0
  122. package/dist/core/index.d.ts +5 -0
  123. package/dist/core/index.js +8 -0
  124. package/dist/core/index.js.map +1 -0
  125. package/dist/core/parser.d.ts +84 -0
  126. package/dist/core/parser.js +250 -0
  127. package/dist/core/parser.js.map +1 -0
  128. package/dist/core-lib.d.ts +59 -0
  129. package/dist/core-lib.js +62 -0
  130. package/dist/core-lib.js.map +1 -0
  131. package/dist/index.d.ts +15 -0
  132. package/dist/index.js +20 -0
  133. package/dist/index.js.map +1 -0
  134. package/dist/languages/index.d.ts +11 -0
  135. package/dist/languages/index.js +14 -0
  136. package/dist/languages/index.js.map +1 -0
  137. package/dist/languages/plugins/base.d.ts +44 -0
  138. package/dist/languages/plugins/base.js +82 -0
  139. package/dist/languages/plugins/base.js.map +1 -0
  140. package/dist/languages/plugins/index.d.ts +14 -0
  141. package/dist/languages/plugins/index.js +25 -0
  142. package/dist/languages/plugins/index.js.map +1 -0
  143. package/dist/languages/plugins/java.d.ts +49 -0
  144. package/dist/languages/plugins/java.js +402 -0
  145. package/dist/languages/plugins/java.js.map +1 -0
  146. package/dist/languages/plugins/javascript.d.ts +48 -0
  147. package/dist/languages/plugins/javascript.js +445 -0
  148. package/dist/languages/plugins/javascript.js.map +1 -0
  149. package/dist/languages/plugins/python.d.ts +47 -0
  150. package/dist/languages/plugins/python.js +480 -0
  151. package/dist/languages/plugins/python.js.map +1 -0
  152. package/dist/languages/plugins/rust.d.ts +47 -0
  153. package/dist/languages/plugins/rust.js +405 -0
  154. package/dist/languages/plugins/rust.js.map +1 -0
  155. package/dist/languages/registry.d.ts +30 -0
  156. package/dist/languages/registry.js +80 -0
  157. package/dist/languages/registry.js.map +1 -0
  158. package/dist/languages/types.d.ts +184 -0
  159. package/dist/languages/types.js +8 -0
  160. package/dist/languages/types.js.map +1 -0
  161. package/dist/resolution/cross-file.d.ts +146 -0
  162. package/dist/resolution/cross-file.js +439 -0
  163. package/dist/resolution/cross-file.js.map +1 -0
  164. package/dist/resolution/index.d.ts +12 -0
  165. package/dist/resolution/index.js +10 -0
  166. package/dist/resolution/index.js.map +1 -0
  167. package/dist/resolution/symbol-table.d.ts +136 -0
  168. package/dist/resolution/symbol-table.js +336 -0
  169. package/dist/resolution/symbol-table.js.map +1 -0
  170. package/dist/resolution/type-hierarchy.d.ts +124 -0
  171. package/dist/resolution/type-hierarchy.js +515 -0
  172. package/dist/resolution/type-hierarchy.js.map +1 -0
  173. package/dist/types/config.d.ts +45 -0
  174. package/dist/types/config.js +5 -0
  175. package/dist/types/config.js.map +1 -0
  176. package/dist/types/index.d.ts +392 -0
  177. package/dist/types/index.js +7 -0
  178. package/dist/types/index.js.map +1 -0
  179. package/dist/utils/logger.d.ts +85 -0
  180. package/dist/utils/logger.js +198 -0
  181. package/dist/utils/logger.js.map +1 -0
  182. package/dist/wasm/tree-sitter-java.wasm +0 -0
  183. package/dist/wasm/tree-sitter-javascript.wasm +0 -0
  184. package/dist/wasm/tree-sitter-python.wasm +0 -0
  185. package/dist/wasm/tree-sitter-rust.wasm +0 -0
  186. package/dist/wasm/web-tree-sitter.wasm +0 -0
  187. package/docs/SPEC.md +1021 -0
  188. package/examples/browser-example.html +610 -0
  189. package/examples/node-example.ts +215 -0
  190. package/package.json +107 -0
  191. package/wasm/tree-sitter-java.wasm +0 -0
  192. package/wasm/tree-sitter-javascript.wasm +0 -0
  193. package/wasm/tree-sitter-python.wasm +0 -0
  194. package/wasm/tree-sitter-rust.wasm +0 -0
@@ -0,0 +1,739 @@
1
+ /**
2
+ * Import extractor - extracts import declarations
3
+ */
4
+ import { findNodes, getNodeText } from '../parser.js';
5
+ /**
6
+ * Detect language from tree structure.
7
+ */
8
+ function detectLanguage(tree) {
9
+ const root = tree.rootNode;
10
+ // Check for JavaScript-specific nodes
11
+ const jsNodeTypes = new Set([
12
+ 'arrow_function', 'lexical_declaration', 'function_declaration',
13
+ 'export_statement', 'import_statement'
14
+ ]);
15
+ // Check for Java-specific nodes
16
+ const javaNodeTypes = new Set([
17
+ 'package_declaration', 'import_declaration', 'class_declaration',
18
+ 'method_declaration', 'annotation'
19
+ ]);
20
+ // Check for Python-specific nodes
21
+ const pythonNodeTypes = new Set([
22
+ 'class_definition', 'function_definition', 'decorated_definition',
23
+ 'import_from_statement'
24
+ ]);
25
+ // Check for Rust-specific nodes
26
+ const rustNodeTypes = new Set([
27
+ 'struct_item', 'impl_item', 'function_item', 'use_declaration',
28
+ 'mod_item', 'trait_item', 'enum_item'
29
+ ]);
30
+ let jsScore = 0;
31
+ let javaScore = 0;
32
+ let pythonScore = 0;
33
+ let rustScore = 0;
34
+ for (let i = 0; i < Math.min(root.childCount, 20); i++) {
35
+ const child = root.child(i);
36
+ if (!child)
37
+ continue;
38
+ if (jsNodeTypes.has(child.type))
39
+ jsScore++;
40
+ if (javaNodeTypes.has(child.type))
41
+ javaScore++;
42
+ if (pythonNodeTypes.has(child.type))
43
+ pythonScore++;
44
+ if (rustNodeTypes.has(child.type))
45
+ rustScore++;
46
+ }
47
+ if (rustScore > jsScore && rustScore > javaScore && rustScore > pythonScore)
48
+ return 'rust';
49
+ if (pythonScore > jsScore && pythonScore > javaScore)
50
+ return 'python';
51
+ return jsScore > javaScore ? 'javascript' : 'java';
52
+ }
53
+ /**
54
+ * Extract all imports from the tree.
55
+ */
56
+ export function extractImports(tree, language) {
57
+ const effectiveLanguage = language ?? detectLanguage(tree);
58
+ const isJavaScript = effectiveLanguage === 'javascript' || effectiveLanguage === 'typescript';
59
+ const isPython = effectiveLanguage === 'python';
60
+ const isRust = effectiveLanguage === 'rust';
61
+ if (isRust) {
62
+ return extractRustImports(tree);
63
+ }
64
+ if (isPython) {
65
+ return extractPythonImports(tree);
66
+ }
67
+ if (isJavaScript) {
68
+ return extractJavaScriptImports(tree);
69
+ }
70
+ return extractJavaImports(tree);
71
+ }
72
+ /**
73
+ * Extract JavaScript/TypeScript imports.
74
+ */
75
+ function extractJavaScriptImports(tree) {
76
+ const imports = [];
77
+ // Find all ES6 import statements
78
+ const importStatements = findNodes(tree.rootNode, 'import_statement');
79
+ for (const importStmt of importStatements) {
80
+ const importInfos = extractJSImportInfo(importStmt);
81
+ imports.push(...importInfos);
82
+ }
83
+ // Find CommonJS require calls
84
+ const requireCalls = findRequireCalls(tree);
85
+ imports.push(...requireCalls);
86
+ return imports;
87
+ }
88
+ /**
89
+ * Extract Java imports.
90
+ */
91
+ function extractJavaImports(tree) {
92
+ const imports = [];
93
+ // Find all import declarations
94
+ const importDecls = findNodes(tree.rootNode, 'import_declaration');
95
+ for (const importDecl of importDecls) {
96
+ const importInfo = extractJavaImportInfo(importDecl);
97
+ if (importInfo) {
98
+ imports.push(importInfo);
99
+ }
100
+ }
101
+ return imports;
102
+ }
103
+ /**
104
+ * Extract import information from a JavaScript import_statement node.
105
+ */
106
+ function extractJSImportInfo(node) {
107
+ const imports = [];
108
+ const lineNumber = node.startPosition.row + 1;
109
+ // Get the module source (from 'module')
110
+ const sourceNode = node.childForFieldName('source');
111
+ const fromPackage = sourceNode ? getNodeText(sourceNode).replace(/['"]/g, '') : null;
112
+ // Side-effect import: import 'module'
113
+ if (!node.childForFieldName('import_clause') && !node.childForFieldName('namespace_import')) {
114
+ // Check for named imports directly in the node
115
+ let hasImportClause = false;
116
+ for (let i = 0; i < node.childCount; i++) {
117
+ const child = node.child(i);
118
+ if (child && (child.type === 'import_clause' || child.type === 'namespace_import' ||
119
+ child.type === 'named_imports' || child.type === 'identifier')) {
120
+ hasImportClause = true;
121
+ break;
122
+ }
123
+ }
124
+ if (!hasImportClause && fromPackage) {
125
+ imports.push({
126
+ imported_name: '*',
127
+ from_package: fromPackage,
128
+ alias: null,
129
+ is_wildcard: true,
130
+ line_number: lineNumber,
131
+ });
132
+ return imports;
133
+ }
134
+ }
135
+ // Look through all children for import components
136
+ for (let i = 0; i < node.childCount; i++) {
137
+ const child = node.child(i);
138
+ if (!child)
139
+ continue;
140
+ // Default import: import foo from 'module'
141
+ if (child.type === 'identifier') {
142
+ const name = getNodeText(child);
143
+ // Skip 'import' keyword and module source
144
+ if (name !== 'import' && name !== 'from') {
145
+ imports.push({
146
+ imported_name: 'default',
147
+ from_package: fromPackage,
148
+ alias: name,
149
+ is_wildcard: false,
150
+ line_number: lineNumber,
151
+ });
152
+ }
153
+ }
154
+ // Namespace import: import * as ns from 'module'
155
+ if (child.type === 'namespace_import') {
156
+ // Try field name first, then look for identifier child
157
+ let aliasNode = child.childForFieldName('alias');
158
+ if (!aliasNode) {
159
+ // In tree-sitter-javascript, the alias is just an identifier child
160
+ for (let j = 0; j < child.childCount; j++) {
161
+ const subChild = child.child(j);
162
+ if (subChild && subChild.type === 'identifier') {
163
+ aliasNode = subChild;
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ const alias = aliasNode ? getNodeText(aliasNode) : null;
169
+ imports.push({
170
+ imported_name: '*',
171
+ from_package: fromPackage,
172
+ alias,
173
+ is_wildcard: true,
174
+ line_number: lineNumber,
175
+ });
176
+ }
177
+ // Named imports: import { foo, bar as baz } from 'module'
178
+ if (child.type === 'named_imports') {
179
+ for (let j = 0; j < child.namedChildCount; j++) {
180
+ const specifier = child.namedChild(j);
181
+ if (!specifier)
182
+ continue;
183
+ if (specifier.type === 'import_specifier') {
184
+ const nameNode = specifier.childForFieldName('name');
185
+ const aliasNode = specifier.childForFieldName('alias');
186
+ const importedName = nameNode ? getNodeText(nameNode) : null;
187
+ const alias = aliasNode ? getNodeText(aliasNode) : null;
188
+ if (importedName) {
189
+ imports.push({
190
+ imported_name: importedName,
191
+ from_package: fromPackage,
192
+ alias,
193
+ is_wildcard: false,
194
+ line_number: lineNumber,
195
+ });
196
+ }
197
+ }
198
+ }
199
+ }
200
+ // Import clause might contain default + named imports
201
+ if (child.type === 'import_clause') {
202
+ for (let j = 0; j < child.childCount; j++) {
203
+ const subChild = child.child(j);
204
+ if (!subChild)
205
+ continue;
206
+ if (subChild.type === 'identifier') {
207
+ const name = getNodeText(subChild);
208
+ imports.push({
209
+ imported_name: 'default',
210
+ from_package: fromPackage,
211
+ alias: name,
212
+ is_wildcard: false,
213
+ line_number: lineNumber,
214
+ });
215
+ }
216
+ if (subChild.type === 'namespace_import') {
217
+ let aliasNode = subChild.childForFieldName('alias');
218
+ if (!aliasNode) {
219
+ for (let k = 0; k < subChild.childCount; k++) {
220
+ const nsChild = subChild.child(k);
221
+ if (nsChild && nsChild.type === 'identifier') {
222
+ aliasNode = nsChild;
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ const alias = aliasNode ? getNodeText(aliasNode) : null;
228
+ imports.push({
229
+ imported_name: '*',
230
+ from_package: fromPackage,
231
+ alias,
232
+ is_wildcard: true,
233
+ line_number: lineNumber,
234
+ });
235
+ }
236
+ if (subChild.type === 'named_imports') {
237
+ for (let k = 0; k < subChild.namedChildCount; k++) {
238
+ const specifier = subChild.namedChild(k);
239
+ if (!specifier || specifier.type !== 'import_specifier')
240
+ continue;
241
+ const nameNode = specifier.childForFieldName('name');
242
+ const aliasNode = specifier.childForFieldName('alias');
243
+ const importedName = nameNode ? getNodeText(nameNode) : null;
244
+ const alias = aliasNode ? getNodeText(aliasNode) : null;
245
+ if (importedName) {
246
+ imports.push({
247
+ imported_name: importedName,
248
+ from_package: fromPackage,
249
+ alias,
250
+ is_wildcard: false,
251
+ line_number: lineNumber,
252
+ });
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ return imports;
260
+ }
261
+ /**
262
+ * Find CommonJS require calls: const x = require('module')
263
+ */
264
+ function findRequireCalls(tree) {
265
+ const imports = [];
266
+ // Find all call expressions
267
+ const callExpressions = findNodes(tree.rootNode, 'call_expression');
268
+ for (const call of callExpressions) {
269
+ const funcNode = call.childForFieldName('function');
270
+ if (!funcNode || getNodeText(funcNode) !== 'require')
271
+ continue;
272
+ const argsNode = call.childForFieldName('arguments');
273
+ if (!argsNode)
274
+ continue;
275
+ // Get the module path from first argument
276
+ const firstArg = argsNode.namedChild(0);
277
+ if (!firstArg || (firstArg.type !== 'string' && firstArg.type !== 'template_string'))
278
+ continue;
279
+ const fromPackage = getNodeText(firstArg).replace(/['"]/g, '');
280
+ const lineNumber = call.startPosition.row + 1;
281
+ // Try to find the variable being assigned
282
+ let alias = null;
283
+ const parent = call.parent;
284
+ if (parent?.type === 'variable_declarator') {
285
+ const nameNode = parent.childForFieldName('name');
286
+ if (nameNode) {
287
+ if (nameNode.type === 'identifier') {
288
+ alias = getNodeText(nameNode);
289
+ }
290
+ else if (nameNode.type === 'object_pattern') {
291
+ // Destructuring: const { foo, bar } = require('module')
292
+ for (let i = 0; i < nameNode.namedChildCount; i++) {
293
+ const prop = nameNode.namedChild(i);
294
+ if (!prop)
295
+ continue;
296
+ if (prop.type === 'shorthand_property_identifier_pattern') {
297
+ const name = getNodeText(prop);
298
+ imports.push({
299
+ imported_name: name,
300
+ from_package: fromPackage,
301
+ alias: null,
302
+ is_wildcard: false,
303
+ line_number: lineNumber,
304
+ });
305
+ }
306
+ else if (prop.type === 'pair_pattern') {
307
+ const keyNode = prop.childForFieldName('key');
308
+ const valueNode = prop.childForFieldName('value');
309
+ const importedName = keyNode ? getNodeText(keyNode) : null;
310
+ const propAlias = valueNode && valueNode.type === 'identifier' ? getNodeText(valueNode) : null;
311
+ if (importedName) {
312
+ imports.push({
313
+ imported_name: importedName,
314
+ from_package: fromPackage,
315
+ alias: propAlias,
316
+ is_wildcard: false,
317
+ line_number: lineNumber,
318
+ });
319
+ }
320
+ }
321
+ }
322
+ continue; // Continue to next call expression instead of returning
323
+ }
324
+ }
325
+ }
326
+ imports.push({
327
+ imported_name: '*',
328
+ from_package: fromPackage,
329
+ alias,
330
+ is_wildcard: true,
331
+ line_number: lineNumber,
332
+ });
333
+ }
334
+ return imports;
335
+ }
336
+ /**
337
+ * Extract import information from a Java import_declaration node.
338
+ */
339
+ function extractJavaImportInfo(node) {
340
+ // Check for static import
341
+ const isStatic = hasStaticModifier(node);
342
+ // Get the full import path
343
+ const scopedId = findScopedIdentifier(node);
344
+ if (!scopedId)
345
+ return null;
346
+ const fullPath = getNodeText(scopedId);
347
+ // Check for wildcard import
348
+ const isWildcard = fullPath.endsWith('.*') || hasWildcard(node);
349
+ // Parse the import path
350
+ const { importedName, fromPackage } = parseImportPath(fullPath, isWildcard);
351
+ return {
352
+ imported_name: importedName,
353
+ from_package: fromPackage,
354
+ alias: null, // Java doesn't support import aliases
355
+ is_wildcard: isWildcard,
356
+ line_number: node.startPosition.row + 1,
357
+ };
358
+ }
359
+ /**
360
+ * Check if the import has the static modifier.
361
+ */
362
+ function hasStaticModifier(node) {
363
+ for (let i = 0; i < node.childCount; i++) {
364
+ const child = node.child(i);
365
+ if (child && child.type === 'static') {
366
+ return true;
367
+ }
368
+ }
369
+ return false;
370
+ }
371
+ /**
372
+ * Check if the import has a wildcard asterisk.
373
+ */
374
+ function hasWildcard(node) {
375
+ for (let i = 0; i < node.childCount; i++) {
376
+ const child = node.child(i);
377
+ if (child && child.type === 'asterisk') {
378
+ return true;
379
+ }
380
+ }
381
+ return false;
382
+ }
383
+ /**
384
+ * Find the scoped identifier in an import declaration.
385
+ */
386
+ function findScopedIdentifier(node) {
387
+ for (let i = 0; i < node.childCount; i++) {
388
+ const child = node.child(i);
389
+ if (!child)
390
+ continue;
391
+ if (child.type === 'scoped_identifier' || child.type === 'identifier') {
392
+ return child;
393
+ }
394
+ }
395
+ return null;
396
+ }
397
+ /**
398
+ * Parse an import path into imported name and package.
399
+ */
400
+ function parseImportPath(fullPath, isWildcard) {
401
+ if (isWildcard) {
402
+ // For "java.util.*", package is "java.util", name is "*"
403
+ const cleanPath = fullPath.replace('.*', '').replace('*', '');
404
+ return {
405
+ importedName: '*',
406
+ fromPackage: cleanPath || null,
407
+ };
408
+ }
409
+ // For "java.util.ArrayList", package is "java.util", name is "ArrayList"
410
+ const lastDot = fullPath.lastIndexOf('.');
411
+ if (lastDot === -1) {
412
+ return {
413
+ importedName: fullPath,
414
+ fromPackage: null,
415
+ };
416
+ }
417
+ return {
418
+ importedName: fullPath.substring(lastDot + 1),
419
+ fromPackage: fullPath.substring(0, lastDot),
420
+ };
421
+ }
422
+ // =============================================================================
423
+ // Python Import Extraction
424
+ // =============================================================================
425
+ /**
426
+ * Extract Python imports.
427
+ */
428
+ function extractPythonImports(tree) {
429
+ const imports = [];
430
+ // Find all import statements: import os, sys
431
+ const importStatements = findNodes(tree.rootNode, 'import_statement');
432
+ for (const stmt of importStatements) {
433
+ const importInfos = extractPythonImportStatement(stmt);
434
+ imports.push(...importInfos);
435
+ }
436
+ // Find all from-import statements: from os import path
437
+ const importFromStatements = findNodes(tree.rootNode, 'import_from_statement');
438
+ for (const stmt of importFromStatements) {
439
+ const importInfos = extractPythonFromImportStatement(stmt);
440
+ imports.push(...importInfos);
441
+ }
442
+ return imports;
443
+ }
444
+ /**
445
+ * Extract import information from a Python import_statement node.
446
+ * Handles: import os, import os.path, import os as operating_system
447
+ */
448
+ function extractPythonImportStatement(node) {
449
+ const imports = [];
450
+ const lineNumber = node.startPosition.row + 1;
451
+ for (let i = 0; i < node.childCount; i++) {
452
+ const child = node.child(i);
453
+ if (!child)
454
+ continue;
455
+ if (child.type === 'dotted_name') {
456
+ const fullName = getNodeText(child);
457
+ const parts = fullName.split('.');
458
+ imports.push({
459
+ imported_name: parts[parts.length - 1],
460
+ from_package: parts.length > 1 ? parts.slice(0, -1).join('.') : null,
461
+ alias: null,
462
+ is_wildcard: false,
463
+ line_number: lineNumber,
464
+ });
465
+ }
466
+ else if (child.type === 'aliased_import') {
467
+ const nameNode = child.childForFieldName('name');
468
+ const aliasNode = child.childForFieldName('alias');
469
+ if (nameNode) {
470
+ const fullName = getNodeText(nameNode);
471
+ const parts = fullName.split('.');
472
+ imports.push({
473
+ imported_name: parts[parts.length - 1],
474
+ from_package: parts.length > 1 ? parts.slice(0, -1).join('.') : null,
475
+ alias: aliasNode ? getNodeText(aliasNode) : null,
476
+ is_wildcard: false,
477
+ line_number: lineNumber,
478
+ });
479
+ }
480
+ }
481
+ }
482
+ return imports;
483
+ }
484
+ /**
485
+ * Extract import information from a Python import_from_statement node.
486
+ * Handles: from os import path, from os import path as p, from os import *
487
+ */
488
+ function extractPythonFromImportStatement(node) {
489
+ const imports = [];
490
+ const lineNumber = node.startPosition.row + 1;
491
+ // Get the module name
492
+ const moduleNode = node.childForFieldName('module_name');
493
+ let fromPackage = moduleNode ? getNodeText(moduleNode) : null;
494
+ // Handle relative imports: from . import x, from .. import x
495
+ if (!fromPackage) {
496
+ // Check for relative import dots
497
+ let relativeDots = '';
498
+ for (let i = 0; i < node.childCount; i++) {
499
+ const child = node.child(i);
500
+ if (child && child.type === 'relative_import') {
501
+ relativeDots = getNodeText(child);
502
+ break;
503
+ }
504
+ }
505
+ if (relativeDots) {
506
+ fromPackage = relativeDots;
507
+ }
508
+ }
509
+ // Extract imported names
510
+ for (let i = 0; i < node.childCount; i++) {
511
+ const child = node.child(i);
512
+ if (!child)
513
+ continue;
514
+ // Wildcard import: from os import *
515
+ if (child.type === 'wildcard_import') {
516
+ imports.push({
517
+ imported_name: '*',
518
+ from_package: fromPackage,
519
+ alias: null,
520
+ is_wildcard: true,
521
+ line_number: lineNumber,
522
+ });
523
+ }
524
+ // Simple name: from os import path
525
+ else if (child.type === 'dotted_name' && child !== moduleNode) {
526
+ imports.push({
527
+ imported_name: getNodeText(child),
528
+ from_package: fromPackage,
529
+ alias: null,
530
+ is_wildcard: false,
531
+ line_number: lineNumber,
532
+ });
533
+ }
534
+ // Aliased import: from os import path as p
535
+ else if (child.type === 'aliased_import') {
536
+ const nameNode = child.childForFieldName('name');
537
+ const aliasNode = child.childForFieldName('alias');
538
+ if (nameNode) {
539
+ imports.push({
540
+ imported_name: getNodeText(nameNode),
541
+ from_package: fromPackage,
542
+ alias: aliasNode ? getNodeText(aliasNode) : null,
543
+ is_wildcard: false,
544
+ line_number: lineNumber,
545
+ });
546
+ }
547
+ }
548
+ }
549
+ return imports;
550
+ }
551
+ // =============================================================================
552
+ // Rust Import Extraction
553
+ // =============================================================================
554
+ /**
555
+ * Extract Rust use declarations.
556
+ */
557
+ function extractRustImports(tree) {
558
+ const imports = [];
559
+ // Find all use declarations
560
+ const useDecls = findNodes(tree.rootNode, 'use_declaration');
561
+ for (const useDecl of useDecls) {
562
+ const useImports = extractRustUseDecl(useDecl);
563
+ imports.push(...useImports);
564
+ }
565
+ return imports;
566
+ }
567
+ /**
568
+ * Extract imports from a Rust use declaration.
569
+ * Handles various forms:
570
+ * - use std::io;
571
+ * - use std::collections::HashMap;
572
+ * - use actix_web::{web, App, HttpServer};
573
+ * - use foo::bar::*;
574
+ * - use crate::module::Type as Alias;
575
+ */
576
+ function extractRustUseDecl(node) {
577
+ const imports = [];
578
+ const lineNumber = node.startPosition.row + 1;
579
+ const text = getNodeText(node);
580
+ // Find the use_list or scoped_identifier or identifier inside
581
+ for (let i = 0; i < node.childCount; i++) {
582
+ const child = node.child(i);
583
+ if (!child)
584
+ continue;
585
+ if (child.type === 'scoped_identifier') {
586
+ // Simple use: use std::io;
587
+ const parts = getNodeText(child).split('::');
588
+ const importedName = parts.pop() || '';
589
+ const fromPackage = parts.join('::');
590
+ imports.push({
591
+ imported_name: importedName,
592
+ from_package: fromPackage || null,
593
+ alias: null,
594
+ is_wildcard: importedName === '*',
595
+ line_number: lineNumber,
596
+ });
597
+ }
598
+ else if (child.type === 'scoped_use_list') {
599
+ // Grouped use: use foo::{Bar, Baz};
600
+ const scopedImports = extractRustScopedUseList(child, lineNumber);
601
+ imports.push(...scopedImports);
602
+ }
603
+ else if (child.type === 'use_as_clause') {
604
+ // Aliased import: use foo::Bar as B;
605
+ const pathNode = child.childForFieldName('path');
606
+ const aliasNode = child.childForFieldName('alias');
607
+ if (pathNode) {
608
+ const parts = getNodeText(pathNode).split('::');
609
+ const importedName = parts.pop() || '';
610
+ const fromPackage = parts.join('::');
611
+ imports.push({
612
+ imported_name: importedName,
613
+ from_package: fromPackage || null,
614
+ alias: aliasNode ? getNodeText(aliasNode) : null,
615
+ is_wildcard: false,
616
+ line_number: lineNumber,
617
+ });
618
+ }
619
+ }
620
+ else if (child.type === 'use_wildcard') {
621
+ // Wildcard: use foo::*;
622
+ const pathNode = child.childForFieldName('path');
623
+ const fromPackage = pathNode ? getNodeText(pathNode) : null;
624
+ imports.push({
625
+ imported_name: '*',
626
+ from_package: fromPackage,
627
+ alias: null,
628
+ is_wildcard: true,
629
+ line_number: lineNumber,
630
+ });
631
+ }
632
+ else if (child.type === 'identifier') {
633
+ // Single identifier: use std; (rare)
634
+ imports.push({
635
+ imported_name: getNodeText(child),
636
+ from_package: null,
637
+ alias: null,
638
+ is_wildcard: false,
639
+ line_number: lineNumber,
640
+ });
641
+ }
642
+ }
643
+ return imports;
644
+ }
645
+ /**
646
+ * Extract imports from a Rust scoped_use_list.
647
+ * e.g., use actix_web::{web, App, HttpServer};
648
+ */
649
+ function extractRustScopedUseList(node, lineNumber) {
650
+ const imports = [];
651
+ // Find the base path (before the {})
652
+ let basePath = '';
653
+ for (let i = 0; i < node.childCount; i++) {
654
+ const child = node.child(i);
655
+ if (!child)
656
+ continue;
657
+ if (child.type === 'scoped_identifier' || child.type === 'identifier') {
658
+ basePath = getNodeText(child);
659
+ break;
660
+ }
661
+ }
662
+ // Find the use_list (inside {})
663
+ for (let i = 0; i < node.childCount; i++) {
664
+ const child = node.child(i);
665
+ if (!child || child.type !== 'use_list')
666
+ continue;
667
+ // Extract each item in the use list
668
+ for (let j = 0; j < child.childCount; j++) {
669
+ const item = child.child(j);
670
+ if (!item)
671
+ continue;
672
+ if (item.type === 'identifier') {
673
+ imports.push({
674
+ imported_name: getNodeText(item),
675
+ from_package: basePath || null,
676
+ alias: null,
677
+ is_wildcard: false,
678
+ line_number: lineNumber,
679
+ });
680
+ }
681
+ else if (item.type === 'scoped_identifier') {
682
+ // Nested path in use list
683
+ const parts = getNodeText(item).split('::');
684
+ const importedName = parts.pop() || '';
685
+ const nestedPath = parts.join('::');
686
+ const fullPath = basePath ? `${basePath}::${nestedPath}` : nestedPath;
687
+ imports.push({
688
+ imported_name: importedName,
689
+ from_package: fullPath || null,
690
+ alias: null,
691
+ is_wildcard: false,
692
+ line_number: lineNumber,
693
+ });
694
+ }
695
+ else if (item.type === 'use_as_clause') {
696
+ const pathNode = item.childForFieldName('path');
697
+ const aliasNode = item.childForFieldName('alias');
698
+ if (pathNode) {
699
+ const pathText = getNodeText(pathNode);
700
+ // Handle both simple identifier and scoped path
701
+ if (pathText.includes('::')) {
702
+ const parts = pathText.split('::');
703
+ const importedName = parts.pop() || '';
704
+ const nestedPath = parts.join('::');
705
+ const fullPath = basePath ? `${basePath}::${nestedPath}` : nestedPath;
706
+ imports.push({
707
+ imported_name: importedName,
708
+ from_package: fullPath || null,
709
+ alias: aliasNode ? getNodeText(aliasNode) : null,
710
+ is_wildcard: false,
711
+ line_number: lineNumber,
712
+ });
713
+ }
714
+ else {
715
+ imports.push({
716
+ imported_name: pathText,
717
+ from_package: basePath || null,
718
+ alias: aliasNode ? getNodeText(aliasNode) : null,
719
+ is_wildcard: false,
720
+ line_number: lineNumber,
721
+ });
722
+ }
723
+ }
724
+ }
725
+ else if (item.type === 'self') {
726
+ // use foo::{self}; imports the module itself
727
+ imports.push({
728
+ imported_name: 'self',
729
+ from_package: basePath || null,
730
+ alias: null,
731
+ is_wildcard: false,
732
+ line_number: lineNumber,
733
+ });
734
+ }
735
+ }
736
+ }
737
+ return imports;
738
+ }
739
+ //# sourceMappingURL=imports.js.map