ucn 3.7.8 → 3.7.9

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/core/project.js CHANGED
@@ -3206,7 +3206,7 @@ class ProjectIndex {
3206
3206
  sharedCallees: []
3207
3207
  };
3208
3208
 
3209
- // 1. Same file functions
3209
+ // 1. Same file functions (sorted by proximity to target)
3210
3210
  const fileEntry = this.files.get(def.file);
3211
3211
  if (fileEntry) {
3212
3212
  for (const sym of fileEntry.symbols) {
@@ -3218,6 +3218,10 @@ class ProjectIndex {
3218
3218
  });
3219
3219
  }
3220
3220
  }
3221
+ // Sort by distance from target function (nearest first)
3222
+ related.sameFile.sort((a, b) =>
3223
+ Math.abs(a.line - def.startLine) - Math.abs(b.line - def.startLine)
3224
+ );
3221
3225
  }
3222
3226
 
3223
3227
  // 2. Similar names (shared prefix/suffix, camelCase similarity)
@@ -4097,14 +4101,25 @@ class ProjectIndex {
4097
4101
  const language = detectLanguage(call.file);
4098
4102
  if (!language) return { args: null, argCount: 0 };
4099
4103
 
4100
- const parser = getParser(language);
4101
- if (!parser) return { args: null, argCount: 0 };
4102
-
4103
4104
  // Use tree cache to avoid re-parsing the same file in batch operations
4104
4105
  let tree = this._treeCache?.get(call.file);
4105
4106
  if (!tree) {
4106
4107
  const content = this._readFile(call.file);
4107
- tree = safeParse(parser, content);
4108
+ // HTML files need special handling: parse script blocks as JS
4109
+ if (language === 'html') {
4110
+ const htmlModule = getLanguageModule('html');
4111
+ const htmlParser = getParser('html');
4112
+ const jsParser = getParser('javascript');
4113
+ if (!htmlParser || !jsParser) return { args: null, argCount: 0 };
4114
+ const blocks = htmlModule.extractScriptBlocks(content, htmlParser);
4115
+ if (blocks.length === 0) return { args: null, argCount: 0 };
4116
+ const virtualJS = htmlModule.buildVirtualJSContent(content, blocks);
4117
+ tree = safeParse(jsParser, virtualJS);
4118
+ } else {
4119
+ const parser = getParser(language);
4120
+ if (!parser) return { args: null, argCount: 0 };
4121
+ tree = safeParse(parser, content);
4122
+ }
4108
4123
  if (!tree) return { args: null, argCount: 0 };
4109
4124
  if (!this._treeCache) this._treeCache = new Map();
4110
4125
  this._treeCache.set(call.file, tree);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "3.7.8",
3
+ "version": "3.7.9",
4
4
  "description": "Universal Code Navigator — AST-based call graph analysis for AI agents. Find callers, trace impact, detect dead code across JS/TS, Python, Go, Rust, Java, and HTML. CLI, MCP server, and agent skill.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12871,6 +12871,32 @@ function unusedFn() { return 0; }
12871
12871
  fs.rmSync(tmpDir, { recursive: true, force: true });
12872
12872
  });
12873
12873
 
12874
+ it('verify/impact analyzeCallSite works for HTML inline scripts', () => {
12875
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ucn-html-verify-'));
12876
+ fs.writeFileSync(path.join(tmpDir, 'game.html'), `<html><body>
12877
+ <script>
12878
+ function checkCollision(objA, objB, threshX, threshZ) { return true; }
12879
+ function update() {
12880
+ var hitbox = { x: 1, z: 2 };
12881
+ if (checkCollision(p, player, hitbox.x, hitbox.z)) { return; }
12882
+ }
12883
+ </script>
12884
+ </body></html>`);
12885
+ fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name": "test"}');
12886
+
12887
+ const { ProjectIndex } = require('../core/project');
12888
+ const index = new ProjectIndex(tmpDir);
12889
+ index.build();
12890
+
12891
+ const result = index.verify('checkCollision');
12892
+ assert.ok(result.found);
12893
+ assert.strictEqual(result.totalCalls, 1);
12894
+ assert.strictEqual(result.valid, 1, `Expected 1 valid call, got ${result.valid} valid, ${result.uncertain} uncertain`);
12895
+ assert.strictEqual(result.uncertain, 0, 'dot-access args should not be uncertain');
12896
+
12897
+ fs.rmSync(tmpDir, { recursive: true, force: true });
12898
+ });
12899
+
12874
12900
  it('findCallers detects callers from HTML event handlers', () => {
12875
12901
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ucn-html-callers-'));
12876
12902
  fs.writeFileSync(path.join(tmpDir, 'page.html'), `<html><body>