projscan 1.1.0 → 1.2.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 (73) hide show
  1. package/README.md +32 -5
  2. package/dist/analyzers/unusedDependencyCheck.js +6 -0
  3. package/dist/analyzers/unusedDependencyCheck.js.map +1 -1
  4. package/dist/cli/commands/coverage.js +4 -0
  5. package/dist/cli/commands/coverage.js.map +1 -1
  6. package/dist/cli/commands/prDiff.js +4 -0
  7. package/dist/cli/commands/prDiff.js.map +1 -1
  8. package/dist/core/ast.d.ts +14 -0
  9. package/dist/core/ast.js +30 -8
  10. package/dist/core/ast.js.map +1 -1
  11. package/dist/core/codeGraph.js +68 -3
  12. package/dist/core/codeGraph.js.map +1 -1
  13. package/dist/core/languages/LanguageAdapter.d.ts +1 -1
  14. package/dist/core/languages/csharpAdapter.d.ts +2 -0
  15. package/dist/core/languages/csharpAdapter.js +148 -0
  16. package/dist/core/languages/csharpAdapter.js.map +1 -0
  17. package/dist/core/languages/csharpCallSites.d.ts +22 -0
  18. package/dist/core/languages/csharpCallSites.js +73 -0
  19. package/dist/core/languages/csharpCallSites.js.map +1 -0
  20. package/dist/core/languages/csharpCyclomatic.d.ts +23 -0
  21. package/dist/core/languages/csharpCyclomatic.js +61 -0
  22. package/dist/core/languages/csharpCyclomatic.js.map +1 -0
  23. package/dist/core/languages/csharpExports.d.ts +13 -0
  24. package/dist/core/languages/csharpExports.js +69 -0
  25. package/dist/core/languages/csharpExports.js.map +1 -0
  26. package/dist/core/languages/csharpFunctions.d.ts +24 -0
  27. package/dist/core/languages/csharpFunctions.js +170 -0
  28. package/dist/core/languages/csharpFunctions.js.map +1 -0
  29. package/dist/core/languages/csharpImports.d.ts +30 -0
  30. package/dist/core/languages/csharpImports.js +90 -0
  31. package/dist/core/languages/csharpImports.js.map +1 -0
  32. package/dist/core/languages/goFunctions.js +31 -7
  33. package/dist/core/languages/goFunctions.js.map +1 -1
  34. package/dist/core/languages/javaFunctions.js +12 -5
  35. package/dist/core/languages/javaFunctions.js.map +1 -1
  36. package/dist/core/languages/phpAdapter.d.ts +2 -0
  37. package/dist/core/languages/phpAdapter.js +149 -0
  38. package/dist/core/languages/phpAdapter.js.map +1 -0
  39. package/dist/core/languages/phpCallSites.d.ts +21 -0
  40. package/dist/core/languages/phpCallSites.js +56 -0
  41. package/dist/core/languages/phpCallSites.js.map +1 -0
  42. package/dist/core/languages/phpCyclomatic.d.ts +27 -0
  43. package/dist/core/languages/phpCyclomatic.js +69 -0
  44. package/dist/core/languages/phpCyclomatic.js.map +1 -0
  45. package/dist/core/languages/phpExports.d.ts +13 -0
  46. package/dist/core/languages/phpExports.js +58 -0
  47. package/dist/core/languages/phpExports.js.map +1 -0
  48. package/dist/core/languages/phpFunctions.d.ts +25 -0
  49. package/dist/core/languages/phpFunctions.js +144 -0
  50. package/dist/core/languages/phpFunctions.js.map +1 -0
  51. package/dist/core/languages/phpImports.d.ts +31 -0
  52. package/dist/core/languages/phpImports.js +142 -0
  53. package/dist/core/languages/phpImports.js.map +1 -0
  54. package/dist/core/languages/phpManifests.d.ts +31 -0
  55. package/dist/core/languages/phpManifests.js +89 -0
  56. package/dist/core/languages/phpManifests.js.map +1 -0
  57. package/dist/core/languages/pythonFunctions.js +34 -8
  58. package/dist/core/languages/pythonFunctions.js.map +1 -1
  59. package/dist/core/languages/registry.js +3 -1
  60. package/dist/core/languages/registry.js.map +1 -1
  61. package/dist/core/languages/rubyFunctions.js +37 -5
  62. package/dist/core/languages/rubyFunctions.js.map +1 -1
  63. package/dist/core/languages/rustFunctions.js +44 -8
  64. package/dist/core/languages/rustFunctions.js.map +1 -1
  65. package/dist/core/languages/treeSitterLoader.js +3 -1
  66. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  67. package/dist/grammars/tree-sitter-c_sharp.wasm +0 -0
  68. package/dist/grammars/tree-sitter-php.wasm +0 -0
  69. package/dist/reporters/htmlReporter.d.ts +3 -1
  70. package/dist/reporters/htmlReporter.js +86 -0
  71. package/dist/reporters/htmlReporter.js.map +1 -1
  72. package/dist/tool-manifest.json +2 -2
  73. package/package.json +4 -2
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Top-level PHP declarations are always public to anything that imports the
3
+ * file's namespace via `use` — there's no `private` modifier at the file /
4
+ * namespace scope (only inside classes). So all top-level
5
+ * function_definition / class_declaration / interface_declaration /
6
+ * trait_declaration / enum_declaration nodes count as exports.
7
+ *
8
+ * Method-level visibility is enforced inside `phpFunctions.ts` (where
9
+ * we surface per-method CC); it doesn't matter at the file-export level.
10
+ */
11
+ const TOP_LEVEL_EXPORTS = {
12
+ function_definition: 'function',
13
+ class_declaration: 'class',
14
+ interface_declaration: 'interface',
15
+ trait_declaration: 'class',
16
+ enum_declaration: 'enum',
17
+ };
18
+ export function extractPhpExports(root) {
19
+ const exports = [];
20
+ // Only walk top-level (program -> direct children). Nested classes are
21
+ // method-level and addressed by phpFunctions, not file exports.
22
+ for (const child of root.namedChildren) {
23
+ visit(child, exports, /* depth */ 0);
24
+ }
25
+ return exports;
26
+ }
27
+ function visit(node, out, depth) {
28
+ const kind = TOP_LEVEL_EXPORTS[node.type];
29
+ if (kind && depth === 0) {
30
+ const name = nameOf(node);
31
+ if (name) {
32
+ out.push({ name, kind, typeOnly: false, line: node.startPosition.row + 1 });
33
+ }
34
+ return;
35
+ }
36
+ // namespace_definition wraps further declarations; descend with the same
37
+ // depth so we still classify its top-level children as exports.
38
+ if (node.type === 'namespace_definition') {
39
+ const body = node.namedChildren.find((c) => c.type === 'declaration_list' || c.type === 'compound_statement');
40
+ if (body) {
41
+ for (const c of body.namedChildren)
42
+ visit(c, out, depth);
43
+ }
44
+ }
45
+ }
46
+ function nameOf(node) {
47
+ if (node.childForFieldName) {
48
+ const id = node.childForFieldName('name');
49
+ if (id)
50
+ return id.text;
51
+ }
52
+ for (const c of node.namedChildren) {
53
+ if (c.type === 'name')
54
+ return c.text;
55
+ }
56
+ return null;
57
+ }
58
+ //# sourceMappingURL=phpExports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpExports.js","sourceRoot":"","sources":["../../../src/core/languages/phpExports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,MAAM,iBAAiB,GAA+B;IACpD,mBAAmB,EAAE,UAAU;IAC/B,iBAAiB,EAAE,OAAO;IAC1B,qBAAqB,EAAE,WAAW;IAClC,iBAAiB,EAAE,OAAO;IAC1B,gBAAgB,EAAE,MAAM;CACzB,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,uEAAuE;IACvE,gEAAgE;IAChE,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,GAAgB,EAAE,KAAa;IAC1D,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO;IACT,CAAC;IACD,yEAAyE;IACzE,gEAAgE;IAChE,IAAI,IAAI,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC;QAC9G,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa;gBAAE,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,EAAE;YAAE,OAAO,EAAE,CAAC,IAAI,CAAC;IACzB,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { FunctionInfo } from '../ast.js';
2
+ interface TsNode {
3
+ type: string;
4
+ text: string;
5
+ startPosition: {
6
+ row: number;
7
+ column: number;
8
+ };
9
+ endPosition: {
10
+ row: number;
11
+ column: number;
12
+ };
13
+ namedChildren: TsNode[];
14
+ childForFieldName?: (name: string) => TsNode | null;
15
+ }
16
+ /**
17
+ * Per-function McCabe CC for PHP. Walks `function_definition` (free
18
+ * functions) and `method_declaration` nodes. Methods inside `class Foo`
19
+ * are named `Foo.method`; methods in interfaces/traits/enums use the
20
+ * declaring type's name. Anonymous functions and closures are not
21
+ * extracted as separate functions; their CC folds into the enclosing
22
+ * function.
23
+ */
24
+ export declare function extractPhpFunctions(root: TsNode): FunctionInfo[];
25
+ export {};
@@ -0,0 +1,144 @@
1
+ const PHP_DECISION_NODES = new Set([
2
+ 'if_statement',
3
+ 'else_if_clause',
4
+ 'elseif_clause',
5
+ 'for_statement',
6
+ 'foreach_statement',
7
+ 'while_statement',
8
+ 'do_statement',
9
+ 'catch_clause',
10
+ 'conditional_expression',
11
+ ]);
12
+ /**
13
+ * Per-function McCabe CC for PHP. Walks `function_definition` (free
14
+ * functions) and `method_declaration` nodes. Methods inside `class Foo`
15
+ * are named `Foo.method`; methods in interfaces/traits/enums use the
16
+ * declaring type's name. Anonymous functions and closures are not
17
+ * extracted as separate functions; their CC folds into the enclosing
18
+ * function.
19
+ */
20
+ export function extractPhpFunctions(root) {
21
+ const out = [];
22
+ walk(root, null, out);
23
+ return out;
24
+ }
25
+ function walk(node, ownerName, out) {
26
+ // Containers that scope methods by their type name.
27
+ if (node.type === 'class_declaration' ||
28
+ node.type === 'interface_declaration' ||
29
+ node.type === 'trait_declaration' ||
30
+ node.type === 'enum_declaration') {
31
+ const nameNode = node.childForFieldName ? node.childForFieldName('name') : findChild(node, 'name');
32
+ const cls = nameNode?.text ?? ownerName;
33
+ const body = node.childForFieldName ? node.childForFieldName('body') : findChild(node, 'declaration_list');
34
+ if (body) {
35
+ for (const child of body.namedChildren)
36
+ walk(child, cls, out);
37
+ }
38
+ return;
39
+ }
40
+ // namespace_definition wraps everything in a namespace { ... } block; descend.
41
+ if (node.type === 'namespace_definition') {
42
+ for (const child of node.namedChildren)
43
+ walk(child, ownerName, out);
44
+ return;
45
+ }
46
+ if (node.type === 'declaration_list' || node.type === 'compound_statement') {
47
+ for (const child of node.namedChildren)
48
+ walk(child, ownerName, out);
49
+ return;
50
+ }
51
+ if (node.type === 'function_definition' || node.type === 'method_declaration') {
52
+ const nameNode = node.childForFieldName ? node.childForFieldName('name') : findChild(node, 'name');
53
+ const baseName = nameNode?.text ?? '<anonymous>';
54
+ const fnName = ownerName && node.type === 'method_declaration' ? `${ownerName}.${baseName}` : baseName;
55
+ const line = node.startPosition.row + 1;
56
+ const endLine = node.endPosition.row + 1;
57
+ const { cc, callSites } = analyzeBody(node);
58
+ out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc, callSites });
59
+ return;
60
+ }
61
+ for (const child of node.namedChildren)
62
+ walk(child, ownerName, out);
63
+ }
64
+ function analyzeBody(fnNode) {
65
+ let count = 0;
66
+ const calls = new Set();
67
+ const body = fnNode.childForFieldName ? fnNode.childForFieldName('body') : findChild(fnNode, 'compound_statement');
68
+ if (!body)
69
+ return { cc: 1, callSites: [] };
70
+ walkSkipNested(body, (n) => {
71
+ if (PHP_DECISION_NODES.has(n.type)) {
72
+ count++;
73
+ return;
74
+ }
75
+ if (n.type === 'case_statement') {
76
+ if (!/^\s*default\b/.test(n.text))
77
+ count++;
78
+ return;
79
+ }
80
+ if (n.type === 'match_condition_list') {
81
+ if (!/^\s*default\b/.test(n.text))
82
+ count++;
83
+ return;
84
+ }
85
+ if (n.type === 'binary_expression') {
86
+ const t = n.text;
87
+ if (/(\s|^)(\|\||&&|\?\?)(\s|$)/.test(t))
88
+ count++;
89
+ else if (/\b(and|or)\b/.test(t))
90
+ count++;
91
+ return;
92
+ }
93
+ if (n.type === 'function_call_expression' ||
94
+ n.type === 'member_call_expression' ||
95
+ n.type === 'nullsafe_member_call_expression' ||
96
+ n.type === 'scoped_call_expression') {
97
+ const name = phpCalleeName(n);
98
+ if (name)
99
+ calls.add(name);
100
+ }
101
+ });
102
+ return { cc: count + 1, callSites: [...calls] };
103
+ }
104
+ function phpCalleeName(callNode) {
105
+ const target = callNode.type === 'function_call_expression'
106
+ ? (callNode.childForFieldName ? callNode.childForFieldName('function') : null)
107
+ : (callNode.childForFieldName ? callNode.childForFieldName('name') : null);
108
+ if (!target)
109
+ return null;
110
+ switch (target.type) {
111
+ case 'name':
112
+ case 'variable_name':
113
+ return target.text.replace(/^\$/, '');
114
+ case 'qualified_name': {
115
+ let last = null;
116
+ for (const c of target.namedChildren)
117
+ if (c.type === 'name')
118
+ last = c;
119
+ return last ? last.text : null;
120
+ }
121
+ default:
122
+ return null;
123
+ }
124
+ }
125
+ function walkSkipNested(node, visit) {
126
+ visit(node);
127
+ for (const child of node.namedChildren) {
128
+ if (child.type === 'function_definition' ||
129
+ child.type === 'method_declaration' ||
130
+ child.type === 'anonymous_function_creation_expression' ||
131
+ child.type === 'arrow_function') {
132
+ // Nested fn: skip so its decisions don't double-count into the parent.
133
+ continue;
134
+ }
135
+ walkSkipNested(child, visit);
136
+ }
137
+ }
138
+ function findChild(node, type) {
139
+ for (const c of node.namedChildren)
140
+ if (c.type === type)
141
+ return c;
142
+ return null;
143
+ }
144
+ //# sourceMappingURL=phpFunctions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/phpFunctions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { AstImport } from '../ast.js';
2
+ interface TsNode {
3
+ type: string;
4
+ text: string;
5
+ startPosition: {
6
+ row: number;
7
+ column: number;
8
+ };
9
+ namedChildren: TsNode[];
10
+ childForFieldName?: (name: string) => TsNode | null;
11
+ }
12
+ /**
13
+ * Extract PHP `use` statements from a tree-sitter-php AST.
14
+ *
15
+ * Handled forms:
16
+ * use Foo\Bar; → "Foo\\Bar"
17
+ * use Foo\Bar as Baz; → "Foo\\Bar", alias "Baz"
18
+ * use Foo\{Bar, Baz, Qux as Q}; → three imports
19
+ * use function Foo\some_fn; → "Foo\\some_fn", function-only
20
+ * use const Foo\SOME_CONST; → "Foo\\SOME_CONST", const-only
21
+ *
22
+ * Also handled in a lenient way:
23
+ * require / require_once / include / include_once 'path/to/file.php';
24
+ * → "path/to/file.php" (the resolver tries to land it on a repo file).
25
+ *
26
+ * NOT handled:
27
+ * - Dynamic require / include (`require $var`)
28
+ * - `extract()` / `class_exists()` reflective imports
29
+ */
30
+ export declare function extractPhpImports(root: TsNode): AstImport[];
31
+ export {};
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Extract PHP `use` statements from a tree-sitter-php AST.
3
+ *
4
+ * Handled forms:
5
+ * use Foo\Bar; → "Foo\\Bar"
6
+ * use Foo\Bar as Baz; → "Foo\\Bar", alias "Baz"
7
+ * use Foo\{Bar, Baz, Qux as Q}; → three imports
8
+ * use function Foo\some_fn; → "Foo\\some_fn", function-only
9
+ * use const Foo\SOME_CONST; → "Foo\\SOME_CONST", const-only
10
+ *
11
+ * Also handled in a lenient way:
12
+ * require / require_once / include / include_once 'path/to/file.php';
13
+ * → "path/to/file.php" (the resolver tries to land it on a repo file).
14
+ *
15
+ * NOT handled:
16
+ * - Dynamic require / include (`require $var`)
17
+ * - `extract()` / `class_exists()` reflective imports
18
+ */
19
+ export function extractPhpImports(root) {
20
+ const imports = [];
21
+ walk(root, (n) => {
22
+ if (n.type === 'namespace_use_declaration') {
23
+ collectUseDeclaration(n, imports);
24
+ }
25
+ else if (n.type === 'expression_statement') {
26
+ // require / include statements are expression statements wrapping an
27
+ // include_expression / require_expression node.
28
+ for (const child of n.namedChildren) {
29
+ if (child.type === 'include_expression' ||
30
+ child.type === 'include_once_expression' ||
31
+ child.type === 'require_expression' ||
32
+ child.type === 'require_once_expression') {
33
+ collectIncludeStatement(child, imports);
34
+ }
35
+ }
36
+ }
37
+ });
38
+ return imports;
39
+ }
40
+ function collectUseDeclaration(node, out) {
41
+ // Some grammars expose `function` / `const` as a child token; we don't
42
+ // distinguish those in the AstImport shape but we do parse the path.
43
+ const line = node.startPosition.row + 1;
44
+ // Find the qualifier (the `Foo\Bar` prefix) and any use-clauses or use-list.
45
+ // The grammar can produce one of:
46
+ // namespace_use_declaration > namespace_use_clause > qualified_name
47
+ // namespace_use_declaration > namespace_name (prefix) + namespace_use_group
48
+ // The group's prefix is a sibling of the group, not a child.
49
+ let groupPrefix = '';
50
+ for (const child of node.namedChildren) {
51
+ if (child.type === 'namespace_name' || child.type === 'qualified_name') {
52
+ groupPrefix = readPath(child);
53
+ }
54
+ }
55
+ for (const child of node.namedChildren) {
56
+ if (child.type === 'namespace_use_clause') {
57
+ const { source, alias } = extractClause(child);
58
+ if (source)
59
+ out.push(makeImport(source, alias, line));
60
+ }
61
+ else if (child.type === 'namespace_use_group') {
62
+ for (const groupChild of child.namedChildren) {
63
+ if (groupChild.type === 'namespace_use_clause' ||
64
+ groupChild.type === 'namespace_use_group_clause') {
65
+ const { source, alias } = extractClause(groupChild);
66
+ if (!source)
67
+ continue;
68
+ const full = groupPrefix ? `${groupPrefix}\\${source}` : source;
69
+ out.push(makeImport(full, alias, line));
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ function extractClause(node) {
76
+ let source = '';
77
+ let alias;
78
+ for (const c of node.namedChildren) {
79
+ if (c.type === 'qualified_name' || c.type === 'namespace_name' || c.type === 'name') {
80
+ if (!source)
81
+ source = readPath(c);
82
+ }
83
+ else if (c.type === 'namespace_aliasing_clause' || c.type === 'name') {
84
+ // alias clauses look like `as Baz` — the `name` after `as` is the alias.
85
+ // This branch fires for `name` only when source is already set.
86
+ const aliasName = node.childForFieldName ? node.childForFieldName('alias') : null;
87
+ if (aliasName)
88
+ alias = aliasName.text;
89
+ }
90
+ }
91
+ // Field-based alias (the grammar's preferred shape).
92
+ if (!alias && node.childForFieldName) {
93
+ const a = node.childForFieldName('alias');
94
+ if (a)
95
+ alias = a.text;
96
+ }
97
+ return { source, alias };
98
+ }
99
+ function collectIncludeStatement(expr, out) {
100
+ // include_expression : `include 'path'` ; first non-include child is the
101
+ // path expression. Grammar variants put it as a `string` literal child.
102
+ const line = expr.startPosition.row + 1;
103
+ for (const c of expr.namedChildren) {
104
+ if (c.type === 'string' || c.type === 'encapsed_string') {
105
+ const stringContent = c.namedChildren.find((s) => s.type === 'string_content');
106
+ const raw = stringContent ? stringContent.text : c.text.replace(/^['"]|['"]$/g, '');
107
+ if (raw)
108
+ out.push({ source: raw, kind: 'static', specifiers: [], typeOnly: false, line });
109
+ return;
110
+ }
111
+ }
112
+ }
113
+ function readPath(node) {
114
+ // qualified_name has children: namespace_name (or names) + name.
115
+ // Concatenate with backslashes.
116
+ const parts = [];
117
+ collectName(node, parts);
118
+ return parts.join('\\');
119
+ }
120
+ function collectName(node, out) {
121
+ if (node.type === 'name') {
122
+ out.push(node.text);
123
+ return;
124
+ }
125
+ for (const c of node.namedChildren)
126
+ collectName(c, out);
127
+ }
128
+ function makeImport(source, alias, line) {
129
+ return {
130
+ source,
131
+ kind: 'static',
132
+ specifiers: alias ? [alias] : [],
133
+ typeOnly: false,
134
+ line,
135
+ };
136
+ }
137
+ function walk(node, visit) {
138
+ visit(node);
139
+ for (const child of node.namedChildren)
140
+ walk(child, visit);
141
+ }
142
+ //# sourceMappingURL=phpImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpImports.js","sourceRoot":"","sources":["../../../src/core/languages/phpImports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { FileEntry } from '../../types.js';
2
+ export interface PhpProjectInfo {
3
+ /** Absolute directory containing composer.json. Imports resolve relative to its autoload roots. */
4
+ projectRoot: string;
5
+ /**
6
+ * PSR-4 namespace prefixes mapped to a relative source root. Example:
7
+ * `[{ prefix: 'App\\', root: 'src' }, { prefix: 'App\\Tests\\', root: 'tests' }]`.
8
+ * Resolution strategy: longest prefix match wins.
9
+ */
10
+ psr4: Array<{
11
+ prefix: string;
12
+ root: string;
13
+ }>;
14
+ /**
15
+ * `autoload.classmap` entries (relative dirs / files). PSR-4 covers most
16
+ * modern PHP; classmap is older but still common in legacy code.
17
+ */
18
+ classmap: string[];
19
+ }
20
+ /**
21
+ * Find the closest composer.json and read its autoload section. Tries the
22
+ * repo root first, then walks up from any directory containing a `.php`
23
+ * file. Returns null when no composer.json exists; .php files outside any
24
+ * Composer project are valid (snippets, scratch files) but their `use`
25
+ * clauses can't be resolved to local files.
26
+ *
27
+ * Both `autoload` and `autoload-dev` are read so that test files importing
28
+ * from `App\Tests\` resolve. PSR-4 always wins over classmap when both
29
+ * could resolve a path.
30
+ */
31
+ export declare function detectPhpProject(rootPath: string, files: FileEntry[]): Promise<PhpProjectInfo | null>;
@@ -0,0 +1,89 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ /**
4
+ * Find the closest composer.json and read its autoload section. Tries the
5
+ * repo root first, then walks up from any directory containing a `.php`
6
+ * file. Returns null when no composer.json exists; .php files outside any
7
+ * Composer project are valid (snippets, scratch files) but their `use`
8
+ * clauses can't be resolved to local files.
9
+ *
10
+ * Both `autoload` and `autoload-dev` are read so that test files importing
11
+ * from `App\Tests\` resolve. PSR-4 always wins over classmap when both
12
+ * could resolve a path.
13
+ */
14
+ export async function detectPhpProject(rootPath, files) {
15
+ // 1) Try repo root.
16
+ const fromRoot = await readComposer(rootPath);
17
+ if (fromRoot)
18
+ return fromRoot;
19
+ // 2) Walk up from .php file directories.
20
+ const candidates = new Set();
21
+ for (const f of files) {
22
+ if (!f.relativePath.endsWith('.php'))
23
+ continue;
24
+ let dir = path.posix.dirname(f.relativePath);
25
+ while (dir && dir !== '.' && dir !== '/') {
26
+ candidates.add(dir);
27
+ dir = path.posix.dirname(dir);
28
+ }
29
+ }
30
+ const sorted = [...candidates].sort((a, b) => a.length - b.length);
31
+ for (const dir of sorted) {
32
+ const project = await readComposer(path.join(rootPath, dir));
33
+ if (project)
34
+ return project;
35
+ }
36
+ return null;
37
+ }
38
+ async function readComposer(dir) {
39
+ const p = path.join(dir, 'composer.json');
40
+ let raw;
41
+ try {
42
+ raw = await fs.readFile(p, 'utf-8');
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(raw);
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ const psr4 = [];
55
+ const classmap = [];
56
+ for (const key of ['autoload', 'autoload-dev']) {
57
+ const block = parsed[key];
58
+ if (!block || typeof block !== 'object')
59
+ continue;
60
+ const psr4Block = block['psr-4'];
61
+ if (psr4Block && typeof psr4Block === 'object') {
62
+ for (const [prefix, target] of Object.entries(psr4Block)) {
63
+ if (typeof target === 'string') {
64
+ psr4.push({ prefix, root: stripTrailingSlash(target) });
65
+ }
66
+ else if (Array.isArray(target)) {
67
+ for (const t of target) {
68
+ if (typeof t === 'string')
69
+ psr4.push({ prefix, root: stripTrailingSlash(t) });
70
+ }
71
+ }
72
+ }
73
+ }
74
+ const cm = block.classmap;
75
+ if (Array.isArray(cm)) {
76
+ for (const entry of cm)
77
+ if (typeof entry === 'string')
78
+ classmap.push(stripTrailingSlash(entry));
79
+ }
80
+ }
81
+ // Sort PSR-4 entries by prefix length descending so longest-match resolution
82
+ // is a single pass.
83
+ psr4.sort((a, b) => b.prefix.length - a.prefix.length);
84
+ return { projectRoot: dir, psr4, classmap };
85
+ }
86
+ function stripTrailingSlash(s) {
87
+ return s.endsWith('/') ? s.slice(0, -1) : s;
88
+ }
89
+ //# sourceMappingURL=phpManifests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpManifests.js","sourceRoot":"","sources":["../../../src/core/languages/phpManifests.ts"],"names":[],"mappings":"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"}
@@ -29,8 +29,8 @@ function walk(node, className, out) {
29
29
  const qualifiedName = className ? `${className}.${fnName}` : fnName;
30
30
  const line = node.startPosition.row + 1;
31
31
  const endLine = node.endPosition.row + 1;
32
- const cc = countDecisions(node);
33
- out.push({ name: qualifiedName, line, endLine, cyclomaticComplexity: cc });
32
+ const { cc, callSites } = analyzeBody(node);
33
+ out.push({ name: qualifiedName, line, endLine, cyclomaticComplexity: cc, callSites });
34
34
  // Recurse into the body to find nested functions only - the CC for THIS
35
35
  // function already correctly excluded them via the body-walker.
36
36
  const body = node.childForFieldName ? node.childForFieldName('body') : null;
@@ -56,19 +56,45 @@ function walk(node, className, out) {
56
56
  walk(child, className, out);
57
57
  }
58
58
  /**
59
- * Count decision points inside a function body, skipping nested function /
60
- * class definitions so they don't pollute the enclosing function's CC.
59
+ * Count decision points and collect call sites inside a function body,
60
+ * skipping nested function / class definitions.
61
61
  */
62
- function countDecisions(fnNode) {
62
+ function analyzeBody(fnNode) {
63
63
  let count = 0;
64
+ const calls = new Set();
64
65
  const body = fnNode.childForFieldName ? fnNode.childForFieldName('body') : null;
65
66
  if (!body)
66
- return 1;
67
+ return { cc: 1, callSites: [] };
67
68
  walkSkipNested(body, (n) => {
68
- if (PY_DECISION_NODES.has(n.type))
69
+ if (PY_DECISION_NODES.has(n.type)) {
69
70
  count++;
71
+ return;
72
+ }
73
+ if (n.type === 'call') {
74
+ const fn = n.childForFieldName ? n.childForFieldName('function') : (n.namedChildren[0] ?? null);
75
+ const name = pyCalleeName(fn);
76
+ if (name)
77
+ calls.add(name);
78
+ }
70
79
  });
71
- return count + 1;
80
+ return { cc: count + 1, callSites: [...calls] };
81
+ }
82
+ function pyCalleeName(node) {
83
+ if (!node)
84
+ return null;
85
+ switch (node.type) {
86
+ case 'identifier':
87
+ return node.text;
88
+ case 'attribute': {
89
+ const attr = node.childForFieldName ? node.childForFieldName('attribute') : null;
90
+ if (attr)
91
+ return pyCalleeName(attr);
92
+ const named = node.namedChildren;
93
+ return named.length > 0 ? pyCalleeName(named[named.length - 1]) : null;
94
+ }
95
+ default:
96
+ return null;
97
+ }
72
98
  }
73
99
  function walkSkipNested(node, visit) {
74
100
  visit(node);
@@ -1 +1 @@
1
- {"version":3,"file":"pythonFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/pythonFunctions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pythonFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/pythonFunctions.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,9 @@ import { goAdapter } from './goAdapter.js';
5
5
  import { javaAdapter } from './javaAdapter.js';
6
6
  import { rubyAdapter } from './rubyAdapter.js';
7
7
  import { rustAdapter } from './rustAdapter.js';
8
- const adapters = [javascriptAdapter, pythonAdapter, goAdapter, javaAdapter, rubyAdapter, rustAdapter];
8
+ import { phpAdapter } from './phpAdapter.js';
9
+ import { csharpAdapter } from './csharpAdapter.js';
10
+ const adapters = [javascriptAdapter, pythonAdapter, goAdapter, javaAdapter, rubyAdapter, rustAdapter, phpAdapter, csharpAdapter];
9
11
  const byExtension = new Map();
10
12
  function rebuildIndex() {
11
13
  byExtension.clear();