ucn 3.8.13 → 3.8.15
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.
- package/.claude/skills/ucn/SKILL.md +3 -1
- package/.github/workflows/ci.yml +13 -1
- package/README.md +1 -0
- package/cli/index.js +165 -246
- package/core/analysis.js +1400 -0
- package/core/build-worker.js +194 -0
- package/core/cache.js +105 -7
- package/core/callers.js +194 -64
- package/core/deadcode.js +22 -66
- package/core/discovery.js +9 -54
- package/core/execute.js +139 -54
- package/core/graph.js +615 -0
- package/core/imports.js +50 -16
- package/core/output/analysis-ext.js +271 -0
- package/core/output/analysis.js +491 -0
- package/core/output/extraction.js +188 -0
- package/core/output/find.js +355 -0
- package/core/output/graph.js +399 -0
- package/core/output/refactoring.js +293 -0
- package/core/output/reporting.js +331 -0
- package/core/output/search.js +307 -0
- package/core/output/shared.js +271 -0
- package/core/output/tracing.js +416 -0
- package/core/output.js +15 -3293
- package/core/parallel-build.js +165 -0
- package/core/project.js +299 -3633
- package/core/registry.js +59 -0
- package/core/reporting.js +258 -0
- package/core/search.js +890 -0
- package/core/stacktrace.js +1 -1
- package/core/tracing.js +631 -0
- package/core/verify.js +10 -13
- package/eslint.config.js +43 -0
- package/jsconfig.json +10 -0
- package/languages/go.js +21 -2
- package/languages/html.js +8 -0
- package/languages/index.js +102 -40
- package/languages/java.js +13 -0
- package/languages/javascript.js +17 -1
- package/languages/python.js +14 -0
- package/languages/rust.js +13 -0
- package/languages/utils.js +1 -1
- package/mcp/server.js +45 -28
- package/package.json +8 -3
package/core/verify.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const { detectLanguage, getParser, getLanguageModule, safeParse } = require('../languages');
|
|
9
|
+
const { detectLanguage, getParser, getLanguageModule, safeParse, langTraits } = require('../languages');
|
|
10
10
|
const { escapeRegExp } = require('./shared');
|
|
11
11
|
const { extractImports } = require('./imports');
|
|
12
12
|
|
|
@@ -164,8 +164,9 @@ function identifyCallPatterns(callSites, funcName) {
|
|
|
164
164
|
if (new RegExp('\\.' + escapeRegExp(funcName) + '\\s*\\(').test(expr)) patterns.chainedCalls++;
|
|
165
165
|
|
|
166
166
|
if (site.args && site.args.length > 0) {
|
|
167
|
+
const literalPattern = /^[\d'"{\[]/; // eslint-disable-line no-useless-escape
|
|
167
168
|
const hasLiteral = site.args.some(a =>
|
|
168
|
-
|
|
169
|
+
literalPattern.test(a) || a === 'true' || a === 'false' || a === 'null'
|
|
169
170
|
);
|
|
170
171
|
if (hasLiteral) patterns.constantArgs++;
|
|
171
172
|
if (site.hasVariable) patterns.variableArgs++;
|
|
@@ -194,11 +195,9 @@ function verify(index, name, options = {}) {
|
|
|
194
195
|
const fileEntry = index.files.get(def.file);
|
|
195
196
|
const lang = fileEntry?.language;
|
|
196
197
|
let params = def.paramsStructured || [];
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
params = params.slice(1);
|
|
201
|
-
}
|
|
198
|
+
const selfParams = langTraits(lang)?.selfParam;
|
|
199
|
+
if (selfParams && params.length > 0 && selfParams.includes(params[0].name)) {
|
|
200
|
+
params = params.slice(1);
|
|
202
201
|
}
|
|
203
202
|
const hasRest = params.some(p => p.rest);
|
|
204
203
|
// Rest params don't count toward expected/min — they accept 0+ extra args
|
|
@@ -345,7 +344,7 @@ function verify(index, name, options = {}) {
|
|
|
345
344
|
const importedNames = getImportedNames(call.file);
|
|
346
345
|
if (!importedNames.has(callReceiver)) continue;
|
|
347
346
|
// Receiver matches target module and is imported — keep it
|
|
348
|
-
} else if (callReceiver && defLang
|
|
347
|
+
} else if (callReceiver && langTraits(defLang)?.hasReceiverPackageCalls) {
|
|
349
348
|
// Go: receiver is package alias (last segment of import path, e.g., "controller"
|
|
350
349
|
// from "k8s.io/.../pkg/controller"), not the filename ("controller_utils").
|
|
351
350
|
// Check if receiver matches the directory name of the target file.
|
|
@@ -588,11 +587,9 @@ function plan(index, name, options = {}) {
|
|
|
588
587
|
const fileEntry = index.files.get(def.file);
|
|
589
588
|
const lang = fileEntry?.language;
|
|
590
589
|
let selfOffset = 0;
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
selfOffset = 1;
|
|
595
|
-
}
|
|
590
|
+
const planSelfParams = langTraits(lang)?.selfParam;
|
|
591
|
+
if (planSelfParams && currentParams.length > 0 && planSelfParams.includes(currentParams[0].name)) {
|
|
592
|
+
selfOffset = 1;
|
|
596
593
|
}
|
|
597
594
|
const callerArgIndex = paramIndex - selfOffset;
|
|
598
595
|
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const js = require('@eslint/js');
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
js.configs.recommended,
|
|
7
|
+
{
|
|
8
|
+
languageOptions: {
|
|
9
|
+
ecmaVersion: 2020,
|
|
10
|
+
sourceType: 'commonjs',
|
|
11
|
+
globals: {
|
|
12
|
+
// Node.js globals
|
|
13
|
+
require: 'readonly',
|
|
14
|
+
module: 'readonly',
|
|
15
|
+
exports: 'readonly',
|
|
16
|
+
__dirname: 'readonly',
|
|
17
|
+
__filename: 'readonly',
|
|
18
|
+
process: 'readonly',
|
|
19
|
+
console: 'readonly',
|
|
20
|
+
Buffer: 'readonly',
|
|
21
|
+
setTimeout: 'readonly',
|
|
22
|
+
setInterval: 'readonly',
|
|
23
|
+
clearTimeout: 'readonly',
|
|
24
|
+
clearInterval: 'readonly',
|
|
25
|
+
setImmediate: 'readonly',
|
|
26
|
+
URL: 'readonly',
|
|
27
|
+
URLSearchParams: 'readonly',
|
|
28
|
+
TextEncoder: 'readonly',
|
|
29
|
+
TextDecoder: 'readonly',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
rules: {
|
|
33
|
+
'no-undef': 'error',
|
|
34
|
+
'no-unused-vars': ['warn', { args: 'none', caughtErrors: 'none' }],
|
|
35
|
+
'no-redeclare': 'error',
|
|
36
|
+
'eqeqeq': ['warn', 'smart'],
|
|
37
|
+
'no-constant-condition': ['error', { checkLoops: false }],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
ignores: ['node_modules/', 'test/', '.ucn-cache/', 'demo/'],
|
|
42
|
+
},
|
|
43
|
+
];
|
package/jsconfig.json
ADDED
package/languages/go.js
CHANGED
|
@@ -1124,8 +1124,15 @@ function findImportsInCode(code, parser) {
|
|
|
1124
1124
|
}
|
|
1125
1125
|
|
|
1126
1126
|
if (modulePath) {
|
|
1127
|
-
// Package name is last segment of path
|
|
1128
|
-
|
|
1127
|
+
// Package name is last segment of path, skipping Go version suffixes (v2, v3, etc.)
|
|
1128
|
+
let pkgName = alias;
|
|
1129
|
+
if (!pkgName) {
|
|
1130
|
+
const parts = modulePath.split('/');
|
|
1131
|
+
// Go convention: if last segment matches /^v\d+$/, use the previous segment
|
|
1132
|
+
// e.g., k8s.io/klog/v2 → klog, github.com/foo/bar/v3 → bar
|
|
1133
|
+
const last = parts[parts.length - 1];
|
|
1134
|
+
pkgName = (/^v\d+$/.test(last) && parts.length > 1) ? parts[parts.length - 2] : last;
|
|
1135
|
+
}
|
|
1129
1136
|
imports.push({
|
|
1130
1137
|
module: modulePath,
|
|
1131
1138
|
names: [pkgName],
|
|
@@ -1345,6 +1352,17 @@ function findUsagesInCode(code, name, parser) {
|
|
|
1345
1352
|
return usages;
|
|
1346
1353
|
}
|
|
1347
1354
|
|
|
1355
|
+
/**
|
|
1356
|
+
* Check if a symbol is a Go-convention entry point.
|
|
1357
|
+
* These are invoked by the Go runtime or test runner, not user code.
|
|
1358
|
+
*/
|
|
1359
|
+
function isEntryPoint(symbol) {
|
|
1360
|
+
const { name } = symbol;
|
|
1361
|
+
if (name === 'main' || name === 'init') return true;
|
|
1362
|
+
if (/^(Test|Benchmark|Example|Fuzz)[A-Z_]/.test(name)) return true;
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1348
1366
|
module.exports = {
|
|
1349
1367
|
findFunctions,
|
|
1350
1368
|
findClasses,
|
|
@@ -1353,5 +1371,6 @@ module.exports = {
|
|
|
1353
1371
|
findImportsInCode,
|
|
1354
1372
|
findExportsInCode,
|
|
1355
1373
|
findUsagesInCode,
|
|
1374
|
+
isEntryPoint,
|
|
1356
1375
|
parse
|
|
1357
1376
|
};
|
package/languages/html.js
CHANGED
|
@@ -311,6 +311,13 @@ function findUsagesInCode(code, name, parser) {
|
|
|
311
311
|
return jsUsages.concat(handlerUsages);
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
/**
|
|
315
|
+
* HTML has no entry points of its own.
|
|
316
|
+
*/
|
|
317
|
+
function isEntryPoint() {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
314
321
|
module.exports = {
|
|
315
322
|
parse,
|
|
316
323
|
findFunctions,
|
|
@@ -322,6 +329,7 @@ module.exports = {
|
|
|
322
329
|
findImportsInCode,
|
|
323
330
|
findExportsInCode,
|
|
324
331
|
findUsagesInCode,
|
|
332
|
+
isEntryPoint,
|
|
325
333
|
// Exported for testing
|
|
326
334
|
extractScriptBlocks,
|
|
327
335
|
buildVirtualJSContent,
|
package/languages/index.js
CHANGED
|
@@ -12,55 +12,133 @@ let TreeSitter = null;
|
|
|
12
12
|
// Cached parser instances
|
|
13
13
|
const parsers = {};
|
|
14
14
|
|
|
15
|
+
// Shared trait presets for languages with the same type-system characteristics
|
|
16
|
+
const STRUCTURAL_TRAITS = {
|
|
17
|
+
typeSystem: 'structural',
|
|
18
|
+
methodCallInclusion: 'explicit',
|
|
19
|
+
packageScope: 'file',
|
|
20
|
+
hasReceiverPackageCalls: false,
|
|
21
|
+
exportVisibility: 'keyword',
|
|
22
|
+
hasDynamicImports: true,
|
|
23
|
+
testDirs: [],
|
|
24
|
+
};
|
|
25
|
+
const NOMINAL_TRAITS = {
|
|
26
|
+
typeSystem: 'nominal',
|
|
27
|
+
methodCallInclusion: 'auto',
|
|
28
|
+
packageScope: 'file',
|
|
29
|
+
hasReceiverPackageCalls: false,
|
|
30
|
+
exportVisibility: 'keyword',
|
|
31
|
+
hasDynamicImports: true,
|
|
32
|
+
testDirs: [],
|
|
33
|
+
};
|
|
34
|
+
|
|
15
35
|
// Language configurations
|
|
16
36
|
const LANGUAGES = {
|
|
17
37
|
javascript: {
|
|
18
38
|
name: 'javascript',
|
|
19
39
|
extensions: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
20
40
|
treeSitterLang: 'javascript',
|
|
21
|
-
module: () => require('./javascript')
|
|
41
|
+
module: () => require('./javascript'),
|
|
42
|
+
treeSitterModule: () => require('tree-sitter-javascript'),
|
|
43
|
+
traits: {
|
|
44
|
+
...STRUCTURAL_TRAITS,
|
|
45
|
+
selfParam: ['this'],
|
|
46
|
+
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
47
|
+
testDirs: ['__tests__'],
|
|
48
|
+
},
|
|
22
49
|
},
|
|
23
50
|
typescript: {
|
|
24
51
|
name: 'typescript',
|
|
25
52
|
extensions: ['.ts'],
|
|
26
53
|
treeSitterLang: 'typescript',
|
|
27
|
-
module: () => require('./javascript') // Same module, different parser
|
|
54
|
+
module: () => require('./javascript'), // Same module, different parser
|
|
55
|
+
treeSitterModule: () => require('tree-sitter-typescript').typescript,
|
|
56
|
+
traits: {
|
|
57
|
+
...STRUCTURAL_TRAITS,
|
|
58
|
+
selfParam: ['this'],
|
|
59
|
+
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
60
|
+
testDirs: ['__tests__'],
|
|
61
|
+
},
|
|
28
62
|
},
|
|
29
63
|
tsx: {
|
|
30
64
|
name: 'tsx',
|
|
31
65
|
extensions: ['.tsx'],
|
|
32
66
|
treeSitterLang: 'tsx',
|
|
33
|
-
module: () => require('./javascript')
|
|
67
|
+
module: () => require('./javascript'),
|
|
68
|
+
treeSitterModule: () => require('tree-sitter-typescript').tsx,
|
|
69
|
+
traits: {
|
|
70
|
+
...STRUCTURAL_TRAITS,
|
|
71
|
+
selfParam: ['this'],
|
|
72
|
+
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
73
|
+
testDirs: ['__tests__'],
|
|
74
|
+
},
|
|
34
75
|
},
|
|
35
76
|
python: {
|
|
36
77
|
name: 'python',
|
|
37
78
|
extensions: ['.py', '.pyi'],
|
|
38
79
|
treeSitterLang: 'python',
|
|
39
|
-
module: () => require('./python')
|
|
80
|
+
module: () => require('./python'),
|
|
81
|
+
treeSitterModule: () => require('tree-sitter-python'),
|
|
82
|
+
traits: {
|
|
83
|
+
...STRUCTURAL_TRAITS,
|
|
84
|
+
selfParam: ['self', 'cls'],
|
|
85
|
+
testFileCandidates: (base, ext) => [`test_${base}.py`, `${base}_test.py`],
|
|
86
|
+
testDirs: ['tests'],
|
|
87
|
+
},
|
|
40
88
|
},
|
|
41
89
|
go: {
|
|
42
90
|
name: 'go',
|
|
43
91
|
extensions: ['.go'],
|
|
44
92
|
treeSitterLang: 'go',
|
|
45
|
-
module: () => require('./go')
|
|
93
|
+
module: () => require('./go'),
|
|
94
|
+
treeSitterModule: () => require('tree-sitter-go'),
|
|
95
|
+
traits: {
|
|
96
|
+
...NOMINAL_TRAITS,
|
|
97
|
+
selfParam: null,
|
|
98
|
+
packageScope: 'directory',
|
|
99
|
+
hasReceiverPackageCalls: true,
|
|
100
|
+
exportVisibility: 'capitalization',
|
|
101
|
+
hasDynamicImports: false,
|
|
102
|
+
testFileCandidates: (base, ext) => [`${base}_test.go`],
|
|
103
|
+
},
|
|
46
104
|
},
|
|
47
105
|
rust: {
|
|
48
106
|
name: 'rust',
|
|
49
107
|
extensions: ['.rs'],
|
|
50
108
|
treeSitterLang: 'rust',
|
|
51
|
-
module: () => require('./rust')
|
|
109
|
+
module: () => require('./rust'),
|
|
110
|
+
treeSitterModule: () => require('tree-sitter-rust'),
|
|
111
|
+
traits: {
|
|
112
|
+
...NOMINAL_TRAITS,
|
|
113
|
+
selfParam: ['self', '&self', '&mut self', 'mut self'],
|
|
114
|
+
hasDynamicImports: false,
|
|
115
|
+
testFileCandidates: (base, ext) => [`${base}_test.rs`],
|
|
116
|
+
testDirs: ['tests'],
|
|
117
|
+
},
|
|
52
118
|
},
|
|
53
119
|
java: {
|
|
54
120
|
name: 'java',
|
|
55
121
|
extensions: ['.java'],
|
|
56
122
|
treeSitterLang: 'java',
|
|
57
|
-
module: () => require('./java')
|
|
123
|
+
module: () => require('./java'),
|
|
124
|
+
treeSitterModule: () => require('tree-sitter-java'),
|
|
125
|
+
traits: {
|
|
126
|
+
...NOMINAL_TRAITS,
|
|
127
|
+
selfParam: ['this'],
|
|
128
|
+
testFileCandidates: (base, ext) => [`${base}Test.java`, `${base}Tests.java`, `${base}TestCase.java`],
|
|
129
|
+
},
|
|
58
130
|
},
|
|
59
131
|
html: {
|
|
60
132
|
name: 'html',
|
|
61
133
|
extensions: ['.html', '.htm'],
|
|
62
134
|
treeSitterLang: 'html',
|
|
63
|
-
module: () => require('./html')
|
|
135
|
+
module: () => require('./html'),
|
|
136
|
+
treeSitterModule: () => require('tree-sitter-html'),
|
|
137
|
+
traits: {
|
|
138
|
+
...STRUCTURAL_TRAITS,
|
|
139
|
+
selfParam: ['this'],
|
|
140
|
+
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`],
|
|
141
|
+
},
|
|
64
142
|
}
|
|
65
143
|
};
|
|
66
144
|
|
|
@@ -83,7 +161,8 @@ function loadTreeSitter() {
|
|
|
83
161
|
} catch (e) {
|
|
84
162
|
throw new Error(
|
|
85
163
|
'tree-sitter is required but not installed.\n' +
|
|
86
|
-
'Install with: npm install'
|
|
164
|
+
'Install with: npm install',
|
|
165
|
+
{ cause: e }
|
|
87
166
|
);
|
|
88
167
|
}
|
|
89
168
|
}
|
|
@@ -107,41 +186,14 @@ function getParser(language) {
|
|
|
107
186
|
}
|
|
108
187
|
|
|
109
188
|
try {
|
|
110
|
-
|
|
111
|
-
switch (language) {
|
|
112
|
-
case 'javascript':
|
|
113
|
-
lang = require('tree-sitter-javascript');
|
|
114
|
-
break;
|
|
115
|
-
case 'typescript':
|
|
116
|
-
lang = require('tree-sitter-typescript').typescript;
|
|
117
|
-
break;
|
|
118
|
-
case 'tsx':
|
|
119
|
-
lang = require('tree-sitter-typescript').tsx;
|
|
120
|
-
break;
|
|
121
|
-
case 'python':
|
|
122
|
-
lang = require('tree-sitter-python');
|
|
123
|
-
break;
|
|
124
|
-
case 'go':
|
|
125
|
-
lang = require('tree-sitter-go');
|
|
126
|
-
break;
|
|
127
|
-
case 'java':
|
|
128
|
-
lang = require('tree-sitter-java');
|
|
129
|
-
break;
|
|
130
|
-
case 'rust':
|
|
131
|
-
lang = require('tree-sitter-rust');
|
|
132
|
-
break;
|
|
133
|
-
case 'html':
|
|
134
|
-
lang = require('tree-sitter-html');
|
|
135
|
-
break;
|
|
136
|
-
default:
|
|
137
|
-
throw new Error(`No tree-sitter grammar for: ${language}`);
|
|
138
|
-
}
|
|
189
|
+
const lang = config.treeSitterModule();
|
|
139
190
|
parser.setLanguage(lang);
|
|
140
191
|
} catch (e) {
|
|
141
192
|
throw new Error(
|
|
142
|
-
`Failed to load tree-sitter
|
|
193
|
+
`Failed to load tree-sitter grammar for ${language}.\n` +
|
|
143
194
|
`Install with: npm install tree-sitter-${language}\n` +
|
|
144
|
-
`Original error: ${e.message}
|
|
195
|
+
`Original error: ${e.message}`,
|
|
196
|
+
{ cause: e }
|
|
145
197
|
);
|
|
146
198
|
}
|
|
147
199
|
|
|
@@ -282,6 +334,15 @@ function safeParse(parser, content, oldTree = undefined, options = {}) {
|
|
|
282
334
|
throw lastError;
|
|
283
335
|
}
|
|
284
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Get trait object for a language.
|
|
339
|
+
* @param {string} language - Language name (e.g. 'go', 'python')
|
|
340
|
+
* @returns {object|undefined} Trait object or undefined if unknown language
|
|
341
|
+
*/
|
|
342
|
+
function langTraits(language) {
|
|
343
|
+
return LANGUAGES[language]?.traits;
|
|
344
|
+
}
|
|
345
|
+
|
|
285
346
|
module.exports = {
|
|
286
347
|
detectLanguage,
|
|
287
348
|
getParser,
|
|
@@ -293,6 +354,7 @@ module.exports = {
|
|
|
293
354
|
PARSE_OPTIONS,
|
|
294
355
|
getParseOptions,
|
|
295
356
|
safeParse,
|
|
357
|
+
langTraits,
|
|
296
358
|
DEFAULT_BUFFER_SIZE,
|
|
297
359
|
MAX_BUFFER_SIZE
|
|
298
360
|
};
|
package/languages/java.js
CHANGED
|
@@ -1173,6 +1173,18 @@ function findUsagesInCode(code, name, parser) {
|
|
|
1173
1173
|
return usages;
|
|
1174
1174
|
}
|
|
1175
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* Check if a symbol is a Java-convention entry point.
|
|
1178
|
+
* These are invoked by the JVM runtime, test runners, or required by type system.
|
|
1179
|
+
*/
|
|
1180
|
+
function isEntryPoint(symbol) {
|
|
1181
|
+
const m = symbol.modifiers || [];
|
|
1182
|
+
if (symbol.name === 'main' && m.includes('public') && m.includes('static')) return true;
|
|
1183
|
+
if (m.includes('test')) return true;
|
|
1184
|
+
if (m.includes('override')) return true;
|
|
1185
|
+
return false;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1176
1188
|
module.exports = {
|
|
1177
1189
|
findFunctions,
|
|
1178
1190
|
findClasses,
|
|
@@ -1181,5 +1193,6 @@ module.exports = {
|
|
|
1181
1193
|
findImportsInCode,
|
|
1182
1194
|
findExportsInCode,
|
|
1183
1195
|
findUsagesInCode,
|
|
1196
|
+
isEntryPoint,
|
|
1184
1197
|
parse
|
|
1185
1198
|
};
|
package/languages/javascript.js
CHANGED
|
@@ -1776,7 +1776,7 @@ function findImportsInCode(code, parser) {
|
|
|
1776
1776
|
const firstArg = argsNode.namedChild(0);
|
|
1777
1777
|
const line = node.startPosition.row + 1;
|
|
1778
1778
|
const names = [];
|
|
1779
|
-
let modulePath
|
|
1779
|
+
let modulePath;
|
|
1780
1780
|
let dynamic = false;
|
|
1781
1781
|
|
|
1782
1782
|
if (firstArg && firstArg.type === 'string') {
|
|
@@ -2141,6 +2141,21 @@ function findUsagesInCode(code, name, parser) {
|
|
|
2141
2141
|
return usages;
|
|
2142
2142
|
}
|
|
2143
2143
|
|
|
2144
|
+
const _JS_LIFECYCLE_METHODS = new Set([
|
|
2145
|
+
'render', 'componentDidMount', 'componentDidUpdate', 'componentWillUnmount',
|
|
2146
|
+
'getDerivedStateFromProps', 'getDerivedStateFromError', 'componentDidCatch',
|
|
2147
|
+
'getSnapshotBeforeUpdate', 'shouldComponentUpdate',
|
|
2148
|
+
'connectedCallback', 'disconnectedCallback', 'attributeChangedCallback', 'adoptedCallback'
|
|
2149
|
+
]);
|
|
2150
|
+
|
|
2151
|
+
/**
|
|
2152
|
+
* Check if a symbol is a JS/TS-convention entry point.
|
|
2153
|
+
* These are framework lifecycle methods invoked by React or Web Components.
|
|
2154
|
+
*/
|
|
2155
|
+
function isEntryPoint(symbol) {
|
|
2156
|
+
return !!(symbol.isMethod && _JS_LIFECYCLE_METHODS.has(symbol.name));
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2144
2159
|
module.exports = {
|
|
2145
2160
|
findFunctions,
|
|
2146
2161
|
findClasses,
|
|
@@ -2151,5 +2166,6 @@ module.exports = {
|
|
|
2151
2166
|
findImportsInCode,
|
|
2152
2167
|
findExportsInCode,
|
|
2153
2168
|
findUsagesInCode,
|
|
2169
|
+
isEntryPoint,
|
|
2154
2170
|
parse
|
|
2155
2171
|
};
|
package/languages/python.js
CHANGED
|
@@ -1198,6 +1198,19 @@ function extractConstructorName(node) {
|
|
|
1198
1198
|
return null;
|
|
1199
1199
|
}
|
|
1200
1200
|
|
|
1201
|
+
/**
|
|
1202
|
+
* Check if a symbol is a Python-convention entry point.
|
|
1203
|
+
* These are invoked by the Python runtime, test runners, or frameworks.
|
|
1204
|
+
*/
|
|
1205
|
+
function isEntryPoint(symbol) {
|
|
1206
|
+
const { name } = symbol;
|
|
1207
|
+
if (/^__\w+__$/.test(name)) return true;
|
|
1208
|
+
if (/^test_/.test(name)) return true;
|
|
1209
|
+
if (/^(setUp|tearDown)(Class|Module)?$/.test(name)) return true;
|
|
1210
|
+
if (/^pytest_/.test(name)) return true;
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1201
1214
|
module.exports = {
|
|
1202
1215
|
findFunctions,
|
|
1203
1216
|
findClasses,
|
|
@@ -1207,5 +1220,6 @@ module.exports = {
|
|
|
1207
1220
|
findExportsInCode,
|
|
1208
1221
|
findUsagesInCode,
|
|
1209
1222
|
findInstanceAttributeTypes,
|
|
1223
|
+
isEntryPoint,
|
|
1210
1224
|
parse
|
|
1211
1225
|
};
|
package/languages/rust.js
CHANGED
|
@@ -1459,6 +1459,18 @@ function findUsagesInCode(code, name, parser) {
|
|
|
1459
1459
|
return usages;
|
|
1460
1460
|
}
|
|
1461
1461
|
|
|
1462
|
+
/**
|
|
1463
|
+
* Check if a symbol is a Rust-convention entry point.
|
|
1464
|
+
* These are invoked by the Rust runtime, test harness, or required by trait contracts.
|
|
1465
|
+
*/
|
|
1466
|
+
function isEntryPoint(symbol) {
|
|
1467
|
+
const m = symbol.modifiers || [];
|
|
1468
|
+
if (symbol.name === 'main') return true;
|
|
1469
|
+
if (m.includes('test') || m.includes('bench')) return true;
|
|
1470
|
+
if (symbol.isMethod && symbol.className && symbol.traitImpl) return true;
|
|
1471
|
+
return false;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1462
1474
|
module.exports = {
|
|
1463
1475
|
findFunctions,
|
|
1464
1476
|
findClasses,
|
|
@@ -1467,5 +1479,6 @@ module.exports = {
|
|
|
1467
1479
|
findImportsInCode,
|
|
1468
1480
|
findExportsInCode,
|
|
1469
1481
|
findUsagesInCode,
|
|
1482
|
+
isEntryPoint,
|
|
1470
1483
|
parse
|
|
1471
1484
|
};
|
package/languages/utils.js
CHANGED
|
@@ -372,7 +372,7 @@ function extractRustDocstring(codeOrLines, startLine) {
|
|
|
372
372
|
commentStart--;
|
|
373
373
|
}
|
|
374
374
|
// Return first line of comment block
|
|
375
|
-
const firstLine = lines[commentStart].trim().replace(/^\/\/[
|
|
375
|
+
const firstLine = lines[commentStart].trim().replace(/^\/\/[/!]\s?/, '');
|
|
376
376
|
if (firstLine) return firstLine;
|
|
377
377
|
}
|
|
378
378
|
return null;
|