@pharaoh-so/local-analyzer 0.1.0 → 0.1.2

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/dist/analyzer.mjs CHANGED
@@ -27577,6 +27577,8 @@ function buildFunctionMetadata(node2, source, name) {
27577
27577
  let signature = buildSignature2(node2, source, name), bodyHash = computeBodyHash3(node2), complexity = computeComplexity3(node2), paramCount = countParams3(node2), errorFlow = detectErrorFlow(node2), jsdoc = extractJSDoc(node2), isAsync = source.slice(node2.startIndex, node2.endIndex).startsWith("async");
27578
27578
  return {
27579
27579
  signature,
27580
+ sourceStart: node2.startIndex,
27581
+ sourceEnd: node2.endIndex,
27580
27582
  lineStart: node2.startPosition.row + 1,
27581
27583
  lineEnd: node2.endPosition.row + 1,
27582
27584
  loc: node2.endPosition.row - node2.startPosition.row + 1,
@@ -27645,6 +27647,8 @@ function pushFunctionValue(holder, name, valueNode, source, functions, className
27645
27647
  let fn = {
27646
27648
  name,
27647
27649
  signature,
27650
+ sourceStart: holder.startIndex,
27651
+ sourceEnd: valueNode.endIndex,
27648
27652
  lineStart: holder.startPosition.row + 1,
27649
27653
  lineEnd: valueNode.endPosition.row + 1,
27650
27654
  loc: valueNode.endPosition.row - holder.startPosition.row + 1,
@@ -27746,9 +27750,11 @@ function extractVariableDeclaration(node2, source, functions, exports) {
27746
27750
  functions.push({
27747
27751
  name,
27748
27752
  signature: buildArrowSignature(nameNode, valueNode, source),
27749
- lineStart: node2.startPosition.row + 1,
27753
+ sourceStart: declarator.startIndex,
27754
+ sourceEnd: valueNode.endIndex,
27755
+ lineStart: declarator.startPosition.row + 1,
27750
27756
  lineEnd: valueNode.endPosition.row + 1,
27751
- loc: valueNode.endPosition.row - node2.startPosition.row + 1,
27757
+ loc: valueNode.endPosition.row - declarator.startPosition.row + 1,
27752
27758
  complexity: computeComplexity3(valueNode),
27753
27759
  isExported,
27754
27760
  isAsync,
@@ -27904,7 +27910,7 @@ function extractExportStatement(node2, source, functions, classes, imports, expo
27904
27910
  function extractImport(node2, imports) {
27905
27911
  let sourceNode = node2.childForFieldName("source");
27906
27912
  if (!sourceNode) return;
27907
- let source = sourceNode.text.replace(/['"]/g, ""), symbols2 = [], isDefault = !1, isNamespace = !1, aliases = [], isTypeOnly = node2.children.some((c) => c.type === "type");
27913
+ let source = sourceNode.text.replace(/['"]/g, ""), symbols2 = [], isDefault = !1, isNamespace = !1, aliases = [], isTypeOnly2 = node2.children.some((c) => c.type === "type");
27908
27914
  for (let child of node2.children)
27909
27915
  if (child.type === "import_clause") {
27910
27916
  for (let clauseChild of child.children)
@@ -27933,7 +27939,7 @@ function extractImport(node2, imports) {
27933
27939
  isDefault,
27934
27940
  isNamespace,
27935
27941
  line: node2.startPosition.row + 1,
27936
- ...isTypeOnly ? { isTypeOnly } : {},
27942
+ ...isTypeOnly2 ? { isTypeOnly: isTypeOnly2 } : {},
27937
27943
  ...aliases.length > 0 ? { aliases } : {}
27938
27944
  });
27939
27945
  }
@@ -28080,20 +28086,29 @@ function readDynamicImportCandidates(rootNode) {
28080
28086
  importPath,
28081
28087
  rootRelative: wrapper?.rootRelative ?? !1,
28082
28088
  line: importCallNode.startPosition.row + 1,
28083
- extracted
28089
+ extracted,
28090
+ ...extracted.names.length > 0 && moduleScopeBinding(importCallNode) && { moduleScope: !0 }
28084
28091
  });
28085
28092
  }
28086
28093
  return candidates;
28087
28094
  }
28095
+ function moduleScopeBinding(call) {
28096
+ let expression = call.parent, awaited = expression?.type === "array" ? expression.parent?.parent?.parent : expression;
28097
+ if (awaited?.type !== "await_expression" || awaited.parent?.type !== "variable_declarator")
28098
+ return !1;
28099
+ let scope = awaited.parent.parent?.parent;
28100
+ return scope?.type === "program" || scope?.type === "export_statement" && scope.parent?.type === "program";
28101
+ }
28088
28102
  function bindDynamicImportCandidates(candidates, filePath, resolveSpecifier2) {
28089
28103
  let sites = [];
28090
- for (let { importPath, rootRelative, line, extracted } of candidates) {
28104
+ for (let { importPath, rootRelative, line, extracted, moduleScope } of candidates) {
28091
28105
  let extensionPath = rootRelative && importPath ? extensionRootPath(importPath) : null, specifier = rootRelative ? extensionPath ? resolveSpecifier2(extensionPath, filePath, "extension-root") : void 0 : importPath && !importPath.startsWith(".") ? resolveSpecifier2(importPath, filePath) : void 0;
28092
28106
  specifier?.external || sites.push({
28093
28107
  importPath,
28094
28108
  resolvedTarget: importPath ? rootRelative ? specifier?.target ?? null : specifier ? specifier.target ?? null : resolveRelativeImport(filePath, importPath) : null,
28095
28109
  line,
28096
- extracted
28110
+ extracted,
28111
+ ...moduleScope && { moduleScope }
28097
28112
  });
28098
28113
  }
28099
28114
  return sites;
@@ -28176,7 +28191,7 @@ function resolveDynamicImportSites(sites, filePath, parsedFiles) {
28176
28191
  importLine: site.line
28177
28192
  });
28178
28193
  }
28179
- extracted.namespaceId && (symbols2.set(extracted.namespaceId, targetFile), extracted.namespaceMethods.length > 0 && imports.push({
28194
+ extracted.namespaceId && (symbols2.set(extracted.namespaceId, targetFile), renames.set(extracted.namespaceId, "*"), extracted.namespaceMethods.length > 0 && imports.push({
28180
28195
  fromFile: filePath,
28181
28196
  toFile: targetFile,
28182
28197
  symbols: extracted.namespaceMethods,
@@ -28408,6 +28423,7 @@ var ExportRegistry = class {
28408
28423
  isExported: fn.isExported,
28409
28424
  lineStart: fn.lineStart,
28410
28425
  lineEnd: fn.lineEnd,
28426
+ ...fn.sourceStart !== void 0 && fn.sourceEnd !== void 0 ? { sourceStart: fn.sourceStart, sourceEnd: fn.sourceEnd } : {},
28411
28427
  loc: fn.loc,
28412
28428
  signature: fn.signature,
28413
28429
  complexity: fn.complexity,
@@ -28476,6 +28492,11 @@ var ExportRegistry = class {
28476
28492
  let entry = this.files.get(filePath);
28477
28493
  entry && returnTypes.size > 0 && (entry.returnTypes = returnTypes);
28478
28494
  }
28495
+ /** Retain a file's explicit export bindings after releasing its syntax tree. */
28496
+ addNamedReExports(filePath, exports) {
28497
+ let entry = this.files.get(filePath);
28498
+ entry && (entry.namedReExports = exports);
28499
+ }
28479
28500
  /**
28480
28501
  * Get the set of all file paths stored in the registry across all chunks.
28481
28502
  *
@@ -28586,7 +28607,7 @@ var ExportRegistry = class {
28586
28607
  importLine: item.importLine
28587
28608
  });
28588
28609
  }
28589
- item.namespaceId && (entry.symbols.has(item.namespaceId) || entry.symbols.set(item.namespaceId, targetFile), item.namespaceMethods.length > 0 && entry.imports.push({
28610
+ item.namespaceId && (entry.symbols.has(item.namespaceId) || (entry.symbols.set(item.namespaceId, targetFile), entry.renames.set(item.namespaceId, "*")), item.namespaceMethods.length > 0 && entry.imports.push({
28590
28611
  fromFile: filePath,
28591
28612
  toFile: targetFile,
28592
28613
  symbols: item.namespaceMethods,
@@ -28654,6 +28675,7 @@ var ExportRegistry = class {
28654
28675
  signature: fn.signature,
28655
28676
  lineStart: fn.lineStart,
28656
28677
  lineEnd: fn.lineEnd,
28678
+ ...fn.sourceStart !== void 0 && fn.sourceEnd !== void 0 ? { sourceStart: fn.sourceStart, sourceEnd: fn.sourceEnd } : {},
28657
28679
  loc: fn.loc,
28658
28680
  complexity: fn.complexity,
28659
28681
  isExported: fn.isExported,
@@ -28775,6 +28797,237 @@ function starExportTargets(fileImports) {
28775
28797
  return targets;
28776
28798
  }
28777
28799
 
28800
+ // src/resolver/re-exports.ts
28801
+ function isTypeOnly(node2) {
28802
+ return node2.children.some(
28803
+ (child) => child.type === "type" || child.type === "ERROR" && child.text === "type"
28804
+ );
28805
+ }
28806
+ function moduleSpecifier(node2) {
28807
+ return node2.childForFieldName("source")?.text.slice(1, -1);
28808
+ }
28809
+ function nonCallableBinding(file, name = "*") {
28810
+ return { file, name, local: !0 };
28811
+ }
28812
+ function importedBindings(root2, file, names, resolveSpecifier2, dynamicSites, knownFiles) {
28813
+ let bindings = /* @__PURE__ */ new Map(), add = (local, binding2) => {
28814
+ names.has(local) && bindings.set(local, bindings.has(local) ? null : binding2);
28815
+ };
28816
+ for (let statement of root2.namedChildren) {
28817
+ if (statement.type !== "import_statement") continue;
28818
+ let source = moduleSpecifier(statement), clause = statement.namedChildren.find((child) => child.type === "import_clause");
28819
+ if (source === void 0 || !clause) continue;
28820
+ let typeOnly = isTypeOnly(statement), resolution, bind = (local, original, typeOnly2, namespace2 = !1) => {
28821
+ if (!names.has(local)) return;
28822
+ if (typeOnly2) {
28823
+ add(local, null);
28824
+ return;
28825
+ }
28826
+ resolution ??= resolveSpecifier2(source, file, void 0, statement.startPosition.row + 1);
28827
+ let target = resolution.target;
28828
+ add(
28829
+ local,
28830
+ target ? namespace2 ? nonCallableBinding(target) : { file: target, name: original } : nonCallableBinding(file, `*:${local}`)
28831
+ );
28832
+ };
28833
+ for (let child of clause.namedChildren) {
28834
+ if (child.type === "identifier" && bind(child.text, "default", typeOnly), child.type === "namespace_import") {
28835
+ let name = child.namedChildren.find((member) => member.type === "identifier")?.text;
28836
+ name && bind(name, "*", typeOnly, !0);
28837
+ }
28838
+ if (child.type === "named_imports")
28839
+ for (let specifier of child.namedChildren) {
28840
+ let name = specifier.childForFieldName("name")?.text, alias = specifier.childForFieldName("alias")?.text;
28841
+ name && bind(alias ?? name, name, typeOnly || isTypeOnly(specifier));
28842
+ }
28843
+ }
28844
+ }
28845
+ for (let site of dynamicSites) {
28846
+ if (!site.moduleScope || !site.extracted.names.some(({ local }) => names.has(local))) continue;
28847
+ let target = site.resolvedTarget && knownFiles && findParsedFilePath(knownFiles, site.resolvedTarget);
28848
+ for (let { local, original } of site.extracted.names)
28849
+ add(
28850
+ local,
28851
+ target ? { file: target, name: original } : nonCallableBinding(file, `*:${local}`)
28852
+ );
28853
+ }
28854
+ return bindings;
28855
+ }
28856
+ var CALLABLE_DECLARATIONS = /* @__PURE__ */ new Set([
28857
+ "function_declaration",
28858
+ "generator_function_declaration",
28859
+ "class_declaration",
28860
+ "abstract_class_declaration"
28861
+ ]), CALLABLE_EXPRESSIONS = /* @__PURE__ */ new Set([
28862
+ "arrow_function",
28863
+ "function_expression",
28864
+ "generator_function",
28865
+ "class"
28866
+ ]);
28867
+ function recordPattern(node2, record2) {
28868
+ if (node2.type === "identifier" || node2.type === "shorthand_property_identifier_pattern") {
28869
+ record2(node2.text);
28870
+ return;
28871
+ }
28872
+ if (node2.type === "pair_pattern" || node2.type === "assignment_pattern" || node2.type === "object_assignment_pattern") {
28873
+ let value = node2.childForFieldName(node2.type === "pair_pattern" ? "value" : "left");
28874
+ value && recordPattern(value, record2);
28875
+ return;
28876
+ }
28877
+ for (let child of node2.namedChildren) recordPattern(child, record2);
28878
+ }
28879
+ function recordDeclaration(node2, record2) {
28880
+ if (node2.type === "lexical_declaration" || node2.type === "variable_declaration") {
28881
+ for (let declarator of node2.namedChildren) {
28882
+ if (declarator.type !== "variable_declarator") continue;
28883
+ let pattern = declarator.childForFieldName("name"), value = declarator.childForFieldName("value");
28884
+ pattern && recordPattern(pattern, (name2) => record2(name2, CALLABLE_EXPRESSIONS.has(value?.type ?? "")));
28885
+ }
28886
+ return;
28887
+ }
28888
+ let callable = CALLABLE_DECLARATIONS.has(node2.type);
28889
+ if (!callable && node2.type !== "enum_declaration" && node2.type !== "internal_module") return;
28890
+ let name = node2.childForFieldName("name");
28891
+ name && record2(name.text, callable);
28892
+ }
28893
+ function commonJsAssignment(statement) {
28894
+ if (statement.type !== "expression_statement") return !1;
28895
+ let expression = statement.namedChildren[0];
28896
+ if (expression?.type !== "assignment_expression") return !1;
28897
+ let left = expression.childForFieldName("left");
28898
+ return !!left && /^(?:module\.exports|exports)(?:\.|\[|$)/.test(left.text);
28899
+ }
28900
+ function readNamedReExports(root2, file, resolveSpecifier2, dynamicSites = [], parsedExports = [], knownFiles) {
28901
+ let named = /* @__PURE__ */ new Map(), stars = /* @__PURE__ */ new Set(), localValues = /* @__PURE__ */ new Set(), callable = /* @__PURE__ */ new Set(), types2 = /* @__PURE__ */ new Set(), localExports = [], commonJs = !1, hasExportStatement = !1, local = (name) => ({ file, name, local: !0, topLevel: !0 }), recordLocal = (name, isCallable) => {
28902
+ localValues.add(name), isCallable && callable.add(name);
28903
+ }, addNamed = (name, binding2) => named.set(name, named.has(name) ? null : binding2);
28904
+ for (let statement of root2.namedChildren) {
28905
+ if (statement.type !== "export_statement") {
28906
+ recordDeclaration(statement, recordLocal), commonJs || (commonJs = commonJsAssignment(statement));
28907
+ continue;
28908
+ }
28909
+ hasExportStatement = !0;
28910
+ let source = moduleSpecifier(statement), typeOnly = isTypeOnly(statement), clause = statement.namedChildren.find((child) => child.type === "export_clause");
28911
+ if (clause) {
28912
+ let target = source !== void 0 && !typeOnly ? resolveSpecifier2(source, file, void 0, statement.startPosition.row + 1).target : void 0;
28913
+ for (let specifier of clause.namedChildren) {
28914
+ let original = specifier.childForFieldName("name")?.text;
28915
+ if (!original) continue;
28916
+ let name = specifier.childForFieldName("alias")?.text ?? original;
28917
+ typeOnly || isTypeOnly(specifier) ? types2.add(name) : source === void 0 ? localExports.push({ name, original }) : addNamed(
28918
+ name,
28919
+ target ? { file: target, name: original } : nonCallableBinding(file, `*:${name}`)
28920
+ );
28921
+ }
28922
+ continue;
28923
+ }
28924
+ if (typeOnly) continue;
28925
+ if (source !== void 0) {
28926
+ if (statement.children.some((child) => child.type === "*")) {
28927
+ let target = resolveSpecifier2(
28928
+ source,
28929
+ file,
28930
+ void 0,
28931
+ statement.startPosition.row + 1
28932
+ ).target;
28933
+ target && stars.add(target);
28934
+ } else {
28935
+ let name = statement.namedChildren.find(
28936
+ (child) => child.type === "namespace_export"
28937
+ )?.namedChildren[0]?.text;
28938
+ if (name) {
28939
+ let target = resolveSpecifier2(
28940
+ source,
28941
+ file,
28942
+ void 0,
28943
+ statement.startPosition.row + 1
28944
+ ).target;
28945
+ addNamed(
28946
+ name,
28947
+ target ? nonCallableBinding(target) : nonCallableBinding(file, `*:${name}`)
28948
+ );
28949
+ }
28950
+ }
28951
+ continue;
28952
+ }
28953
+ let isDefault = statement.children.some((child) => child.type === "default"), declaration2 = statement.childForFieldName("declaration"), value = statement.childForFieldName("value");
28954
+ if (declaration2)
28955
+ recordDeclaration(declaration2, (name, isCallable) => {
28956
+ recordLocal(name, isCallable), addNamed(isDefault ? "default" : name, local(name));
28957
+ });
28958
+ else if (isDefault && value?.type === "identifier")
28959
+ localExports.push({ name: "default", original: value.text });
28960
+ else if (isDefault) {
28961
+ let name = parsedExports.find((entry) => entry.isDefault)?.name ?? "default";
28962
+ recordLocal(name, CALLABLE_EXPRESSIONS.has(value?.type ?? "")), addNamed("default", local(name));
28963
+ }
28964
+ }
28965
+ if (localExports.length > 0) {
28966
+ let names = new Set(localExports.map(({ original }) => original)), imported = importedBindings(
28967
+ root2,
28968
+ file,
28969
+ names,
28970
+ resolveSpecifier2,
28971
+ dynamicSites,
28972
+ knownFiles
28973
+ );
28974
+ for (let { name, original } of localExports) {
28975
+ let binding2 = imported.get(original);
28976
+ addNamed(
28977
+ name,
28978
+ binding2 === void 0 ? localValues.has(original) ? local(original) : null : binding2
28979
+ );
28980
+ }
28981
+ }
28982
+ for (let name of types2) named.has(name) || named.set(name, null);
28983
+ return {
28984
+ named,
28985
+ stars: [...stars],
28986
+ callable,
28987
+ ...commonJs && !hasExportStatement && { commonJs: !0 }
28988
+ };
28989
+ }
28990
+ var MAX_EXPORT_STATES = 4096;
28991
+ function createImportedSymbolResolver(imports, functions, reExports, defaultName, onExhausted) {
28992
+ return (startFile, startName) => {
28993
+ let start = reExports(startFile), binding2 = start?.named.get(startName);
28994
+ if (binding2 === null) return;
28995
+ if (binding2?.local)
28996
+ return start?.callable.has(binding2.name) && functions.get(startFile)?.has(binding2.name) ? { file: startFile, name: binding2.name, topLevel: !0 } : void 0;
28997
+ if (!binding2 && !start?.stars.length && functions.get(startFile)?.has(startName))
28998
+ return { file: startFile, name: startName };
28999
+ let queue2 = [{ file: startFile, name: startName }], visited = /* @__PURE__ */ new Map([[startFile, /* @__PURE__ */ new Set([startName])]]), enqueue = (file, name) => {
29000
+ let names = visited.get(file) ?? /* @__PURE__ */ new Set();
29001
+ return names.has(name) ? !0 : queue2.length >= MAX_EXPORT_STATES ? (onExhausted?.(startFile), !1) : (names.add(name), visited.set(file, names), queue2.push({ file, name }), !0);
29002
+ }, result2;
29003
+ for (let index = 0; index < queue2.length; index++) {
29004
+ let { file, name } = queue2[index], facts = reExports(file), explicit = facts?.named.get(name), declaration2;
29005
+ if (explicit !== null) {
29006
+ if (explicit?.local)
29007
+ declaration2 = { file: explicit.file, name: explicit.name, topLevel: !0 };
29008
+ else if (explicit) {
29009
+ if (!enqueue(explicit.file, explicit.name)) return;
29010
+ } else {
29011
+ let legacy = !facts || facts.commonJs || index === 0, actualName = name === "default" ? defaultName(file) ?? name : name;
29012
+ if (legacy && functions.get(file)?.has(actualName) && !facts?.stars.length)
29013
+ declaration2 = { file, name: actualName };
29014
+ else if (name !== "default") {
29015
+ let fileImports = imports.get(file), named = fileImports?.get(name), targets = facts && !facts.commonJs ? facts.stars : named ? [named] : starExportTargets(fileImports);
29016
+ for (let target of targets) if (!enqueue(target, name)) return;
29017
+ }
29018
+ }
29019
+ if (declaration2) {
29020
+ if (result2 && (result2.file !== declaration2.file || result2.name !== declaration2.name))
29021
+ return;
29022
+ result2 = declaration2;
29023
+ }
29024
+ }
29025
+ }
29026
+ if (!(!result2 || !functions.get(result2.file)?.has(result2.name)) && !(result2.topLevel && !reExports(result2.file)?.callable.has(result2.name)))
29027
+ return result2;
29028
+ };
29029
+ }
29030
+
28778
29031
  // src/resolver/router-registration.ts
28779
29032
  var ROUTER_PACKAGES = /* @__PURE__ */ new Set([
28780
29033
  "express",
@@ -28899,6 +29152,7 @@ function resolveRegistryImports(registry, fileIndex, tsConfig, opts) {
28899
29152
  stats.resolved++, resolved.push({
28900
29153
  fromFile: filePath,
28901
29154
  toFile: target,
29155
+ source: imp.source,
28902
29156
  symbols: imp.symbols,
28903
29157
  isTypeOnly: imp.isTypeOnly,
28904
29158
  importLine: imp.line,
@@ -29280,7 +29534,7 @@ var tsParser2 = new Parser();
29280
29534
  tsParser2.setLanguage(tsLanguage);
29281
29535
  var tsxParser2 = new Parser();
29282
29536
  tsxParser2.setLanguage(tsxLanguage);
29283
- var pyParser2 = new Parser();
29537
+ var jsParser = tsxLanguage === tsxLanguage ? tsxParser2 : new Parser().setLanguage(tsxLanguage), pyParser2 = new Parser();
29284
29538
  pyParser2.setLanguage(pyLanguage);
29285
29539
  var BUILT_INS = /* @__PURE__ */ new Set([
29286
29540
  "console",
@@ -29473,27 +29727,39 @@ function classifyCallContext(callNode) {
29473
29727
  }
29474
29728
  return "production";
29475
29729
  }
29476
- function resolveReExport(symbolName, immediateTarget, importSymbolMap, fileFunctionNames) {
29477
- let queue2 = [immediateTarget], visited = /* @__PURE__ */ new Set();
29478
- for (let currentFile = queue2.shift(); currentFile; currentFile = queue2.shift()) {
29479
- if (visited.has(currentFile)) continue;
29480
- visited.add(currentFile);
29481
- let fileImports = importSymbolMap.get(currentFile), named = fileImports?.get(symbolName);
29482
- for (let nextFile of named ? [named] : starExportTargets(fileImports)) {
29483
- if (fileFunctionNames.get(nextFile)?.has(symbolName)) return nextFile;
29484
- queue2.push(nextFile);
29485
- }
29730
+ function resolvedFunctionUid(repoName, target, classNames) {
29731
+ return target ? buildFunctionUid(repoName, target.file, {
29732
+ name: target.name,
29733
+ className: target.topLevel ? void 0 : classNames.get(target.file)?.get(target.name)
29734
+ }) : void 0;
29735
+ }
29736
+ function namespaceBindings(imports) {
29737
+ let names = /* @__PURE__ */ new Set();
29738
+ for (let statement of imports) {
29739
+ if (!statement.isNamespace || statement.isTypeOnly) continue;
29740
+ let name = statement.symbols.at(-1);
29741
+ name && name !== "*" && names.add(name);
29486
29742
  }
29487
- return null;
29743
+ return names;
29488
29744
  }
29489
- function memberTarget(name, member, targetFile, importSymbolMap, fileFunctionNames, instanceClasses, hasMember) {
29490
- let [className2, classFrom] = instanceClassOf(
29491
- name,
29492
- targetFile,
29493
- importSymbolMap,
29494
- instanceClasses
29495
- ) ?? [name, targetFile], file = fileFunctionNames.get(classFrom)?.has(className2) ? classFrom : resolveReExport(className2, classFrom, importSymbolMap, fileFunctionNames);
29496
- return file && hasMember(file, `${className2}.${member}`) ? { file, className: className2 } : void 0;
29745
+ function isPythonModuleReceiver(file, binding2, functions) {
29746
+ return file.endsWith(".py") && pythonModuleName(file) === binding2 && !functions.get(file)?.has(binding2);
29747
+ }
29748
+ function functionReceiverMember(member) {
29749
+ return member === "call" || member === "apply" || member === "bind";
29750
+ }
29751
+ function pythonFactoryReceiverUid(repo, classes, file, name) {
29752
+ return file.endsWith(".py") && classes.byName.get(name)?.some((cls) => cls.file === file) ? buildFunctionUid(repo, file, { name }) : void 0;
29753
+ }
29754
+ function memberTarget(name, member, targetFile, importSymbolMap, aliases, resolveImported, instanceClasses, hasMember) {
29755
+ let instance = instanceClassOf(name, targetFile, importSymbolMap, instanceClasses), target;
29756
+ if (instance) {
29757
+ let [className2, file] = instance;
29758
+ if (hasMember(file, `${className2}.${member}`)) return { file, className: className2 };
29759
+ let imported = importSymbolMap.get(file)?.get(className2);
29760
+ imported && (target = resolveImported(imported, aliases.get(file)?.get(className2) ?? className2));
29761
+ } else target = resolveImported(targetFile, name);
29762
+ return target && hasMember(target.file, `${target.name}.${member}`) ? { file: target.file, className: target.name } : void 0;
29497
29763
  }
29498
29764
  function instanceClassOf(name, file, importSymbolMap, instanceClasses) {
29499
29765
  let visited = /* @__PURE__ */ new Set(), current = file;
@@ -29536,8 +29802,16 @@ function functionNodesByRow(root2) {
29536
29802
  let name = child.childForFieldName("name"), value = child.childForFieldName("value");
29537
29803
  if (name?.type !== "identifier" || !value || !FUNCTION_VALUE_NODE_TYPES.has(value.type))
29538
29804
  continue;
29539
- let key = declarationKey("binding", row, value.endPosition.row, name.text);
29540
- byDeclaration.has(key) || byDeclaration.set(key, value);
29805
+ let key = declarationKey(
29806
+ "binding",
29807
+ child.startPosition.row,
29808
+ value.endPosition.row,
29809
+ name.text
29810
+ );
29811
+ byDeclaration.has(key) || byDeclaration.set(key, value), byDeclaration.set(
29812
+ declarationKey("binding-range", child.startIndex, value.endIndex, name.text),
29813
+ value
29814
+ );
29541
29815
  }
29542
29816
  if (type === "function_declaration" || type === "function_definition") {
29543
29817
  let name = node2.childForFieldName("name");
@@ -29557,7 +29831,12 @@ function functionNodesByRow(root2) {
29557
29831
  function indexedFunctionNode(index, fn) {
29558
29832
  let row = fn.lineStart - 1;
29559
29833
  if (!fn.className) {
29560
- let kind = /^(?:async )?const /.test(fn.signature) ? "binding" : "named", exact = index.byDeclaration.get(declarationKey(kind, row, fn.lineEnd - 1, fn.name));
29834
+ let kind = /^(?:async )?const /.test(fn.signature) ? "binding" : "named";
29835
+ if (kind === "binding" && fn.sourceStart !== void 0 && fn.sourceEnd !== void 0)
29836
+ return index.byDeclaration.get(
29837
+ declarationKey("binding-range", fn.sourceStart, fn.sourceEnd, fn.name)
29838
+ );
29839
+ let exact = index.byDeclaration.get(declarationKey(kind, row, fn.lineEnd - 1, fn.name));
29561
29840
  if (exact) return exact;
29562
29841
  }
29563
29842
  return index.byRow.get(row);
@@ -29956,7 +30235,15 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
29956
30235
  let fnMap = fileFnClassNames.get(path5);
29957
30236
  fnMap || (fnMap = /* @__PURE__ */ new Map(), fileFnClassNames.set(path5, fnMap)), fnMap.set(fn.name, fn.className);
29958
30237
  }
29959
- let effectiveAliasMap = aliasMap ?? /* @__PURE__ */ new Map(), instanceClasses = /* @__PURE__ */ new Map();
30238
+ let effectiveAliasMap = aliasMap ?? /* @__PURE__ */ new Map(), namespaceNames = new Map(
30239
+ [...registry.entries()].map(([file, entry]) => [file, namespaceBindings(entry.imports)])
30240
+ ), exhausted = /* @__PURE__ */ new Set(), tracingFile, resolveImported = createImportedSymbolResolver(
30241
+ importSymbolMap,
30242
+ fileFunctionNames,
30243
+ (file) => registry.getFileEntry(file)?.namedReExports,
30244
+ (file) => registry.getFileEntry(file)?.defaultExportName,
30245
+ (file) => exhausted.add(tracingFile ?? file)
30246
+ ), instanceClasses = /* @__PURE__ */ new Map();
29960
30247
  for (let [path5, entry] of registry.entries())
29961
30248
  entry.instanceClasses && instanceClasses.set(path5, new Map(Object.entries(entry.instanceClasses)));
29962
30249
  let classMembers = /* @__PURE__ */ new Map();
@@ -29968,6 +30255,7 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
29968
30255
  importSymbolMap,
29969
30256
  aliasMap: effectiveAliasMap,
29970
30257
  fileFunctionNames,
30258
+ resolveImported,
29971
30259
  classes: buildClassIndex([...registry.entries()].map(([, entry]) => entry)),
29972
30260
  hasMember: (file, classMember) => classMembers.get(file)?.has(classMember) ?? !1,
29973
30261
  returnTypes: (file) => registry.getFileEntry(file)?.returnTypes,
@@ -29976,29 +30264,28 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
29976
30264
  }, resolveReceiverCall = (call, filePath) => {
29977
30265
  if (!call.receiver) return;
29978
30266
  let receiverFile = importSymbolMap.get(filePath)?.get(call.receiver);
29979
- if (receiverFile) {
29980
- let target = memberTarget(
29981
- effectiveAliasMap.get(filePath)?.get(call.receiver) ?? call.receiver,
29982
- call.calledName,
29983
- receiverFile,
29984
- importSymbolMap,
29985
- fileFunctionNames,
29986
- instanceClasses,
29987
- (f, key) => registry.getFileEntry(f)?.functions.some((fn) => `${fn.className}.${fn.name}` === key)
30267
+ if (!receiverFile) return;
30268
+ let alias = effectiveAliasMap.get(filePath)?.get(call.receiver);
30269
+ if (alias === "*" || namespaceNames.get(filePath)?.has(call.receiver) || isPythonModuleReceiver(receiverFile, alias ?? call.receiver, fileFunctionNames))
30270
+ return resolvedFunctionUid(
30271
+ repoName,
30272
+ resolveImported(receiverFile, call.calledName),
30273
+ fileFnClassNames
29988
30274
  );
29989
- if (target)
29990
- return buildFunctionUid(repoName, target.file, {
29991
- name: call.calledName,
29992
- className: target.className
29993
- });
29994
- }
29995
- let names = receiverFile ? fileFunctionNames.get(receiverFile) : void 0;
29996
- if (!receiverFile || !names) return;
29997
- let name = names.has(call.calledName) ? call.calledName : names.has(call.receiver) ? call.receiver : void 0;
29998
- return name ? buildFunctionUid(repoName, receiverFile, {
30275
+ let name = alias ?? call.receiver, target = memberTarget(
29999
30276
  name,
30000
- className: fileFnClassNames.get(receiverFile)?.get(name)
30001
- }) : void 0;
30277
+ call.calledName,
30278
+ receiverFile,
30279
+ importSymbolMap,
30280
+ effectiveAliasMap,
30281
+ resolveImported,
30282
+ instanceClasses,
30283
+ (f, key) => classMembers.get(f)?.has(key)
30284
+ );
30285
+ return target ? buildFunctionUid(repoName, target.file, {
30286
+ name: call.calledName,
30287
+ className: target.className
30288
+ }) : pythonFactoryReceiverUid(repoName, typedContext.classes, receiverFile, name) ?? (functionReceiverMember(call.calledName) ? resolvedFunctionUid(repoName, resolveImported(receiverFile, name), fileFnClassNames) : void 0);
30002
30289
  };
30003
30290
  registry.resolvePendingDynamicImports();
30004
30291
  let registryDynamicImports = [], perFileDynImportCounts = /* @__PURE__ */ new Map();
@@ -30043,15 +30330,11 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
30043
30330
  }
30044
30331
  let viaReceiver = resolveReceiverCall(call, filePath);
30045
30332
  if (viaReceiver) return [viaReceiver];
30333
+ if (call.receiver && importSymbolMap.get(filePath)?.has(call.receiver)) return [];
30046
30334
  let targetFile = !!call.receiver && isSelfReceiver(call.receiver) ? void 0 : importSymbolMap.get(filePath)?.get(calledName);
30047
30335
  if (targetFile) {
30048
- let originalName = effectiveAliasMap.get(filePath)?.get(calledName) ?? calledName, definingFile = fileFunctionNames.get(targetFile)?.has(originalName) ? targetFile : resolveReExport(originalName, targetFile, importSymbolMap, fileFunctionNames);
30049
- return definingFile ? [
30050
- buildFunctionUid(repoName, definingFile, {
30051
- name: originalName,
30052
- className: fileFnClassNames.get(definingFile)?.get(originalName)
30053
- })
30054
- ] : [];
30336
+ let originalName = effectiveAliasMap.get(filePath)?.get(calledName) ?? calledName, target = resolveImported(targetFile, originalName), uid = resolvedFunctionUid(repoName, target, fileFnClassNames);
30337
+ return uid ? [uid] : [];
30055
30338
  }
30056
30339
  return calledName !== callerName && fileFunctionNames.get(filePath)?.has(calledName) ? [
30057
30340
  buildFunctionUid(repoName, filePath, {
@@ -30073,8 +30356,10 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
30073
30356
  });
30074
30357
  callerName.startsWith("$module_init") && resolved.length > resolvedBefore && !topLevelCallerUids.includes(callerUid) && topLevelCallerUids.push(callerUid);
30075
30358
  };
30076
- for (let [filePath, entry] of registry.entries())
30359
+ for (let [filePath, entry] of registry.entries()) {
30360
+ tracingFile = filePath;
30077
30361
  for (let fn of entry.functions) resolveCallsOf(fn.uid, fn.name, filePath, fn.calls);
30362
+ }
30078
30363
  for (let [callerUid, calls] of registry.getAllCallSites()) {
30079
30364
  let firstColon = callerUid.indexOf(":"), lastColon = callerUid.lastIndexOf(":");
30080
30365
  if (firstColon < 0 || lastColon <= firstColon) continue;
@@ -30088,8 +30373,8 @@ function traceCallChainsFromRegistry(registry, importSymbolMap, repoName, aliasM
30088
30373
  dynamicImports: registryDynamicImports,
30089
30374
  topLevelCallerUids,
30090
30375
  perFileDynImportCounts,
30091
- untraced: []
30092
- // it parses nothing: the chunked pipeline adds the files it couldn't trace
30376
+ untraced: [...exhausted]
30377
+ // the chunked pipeline adds files it could not parse or read
30093
30378
  };
30094
30379
  }
30095
30380
  function referenceSites(node2, callerName, skip) {
@@ -30421,8 +30706,8 @@ function declaredClass(ctx, typeName, filePath, reachable) {
30421
30706
  if (name = typeName.slice(dot + 1), !(module && pythonModuleName(module) === head && !ctx.fileFunctionNames.get(module)?.has(head)) || name.includes(".")) return;
30422
30707
  typeFile = module;
30423
30708
  }
30424
- let named = (ctx.classes.byName.get(name) ?? []).filter(reachable), importedFrom = ctx.fileFunctionNames.get(typeFile)?.has(name) ? typeFile : resolveReExport(name, typeFile, ctx.importSymbolMap, ctx.fileFunctionNames);
30425
- return named.find((c) => c.file === importedFrom) ?? (dot < 0 && named.length === 1 ? named[0] : void 0);
30709
+ let target = typeFile === filePath ? { file: typeFile, name } : ctx.resolveImported(typeFile, name), named = (ctx.classes.byName.get(target?.name ?? name) ?? []).filter(reachable);
30710
+ return named.find((c) => c.file === target?.file) ?? (dot < 0 && named.length === 1 ? named[0] : void 0);
30426
30711
  }
30427
30712
  function methodOrder(ctx, cls, fromTest) {
30428
30713
  let memo = fromTest ? ctx.methodOrders.test : ctx.methodOrders.production, reachable = (c) => fromTest || !c.test, ordering = /* @__PURE__ */ new Set(), external = (name) => {
@@ -30508,8 +30793,10 @@ function typedReceiverTargets(ctx, typeName, member, filePath, via = {}) {
30508
30793
  return targets.map((c) => ({ file: c.file, className: c.name }));
30509
30794
  }
30510
30795
  function returnedClass(ctx, fn, awaited, filePath) {
30511
- let name = ctx.aliasMap.get(filePath)?.get(fn) ?? fn, imported = ctx.importSymbolMap.get(filePath)?.get(fn), file = imported ? ctx.fileFunctionNames.get(imported)?.has(name) ? imported : resolveReExport(name, imported, ctx.importSymbolMap, ctx.fileFunctionNames) : filePath, declared = file ? ctx.returnTypes(file)?.get(name) : void 0;
30512
- if (!file || !declared || declared.promised && !awaited) return;
30796
+ let name = ctx.aliasMap.get(filePath)?.get(fn) ?? fn, imported = ctx.importSymbolMap.get(filePath)?.get(fn), target = imported ? ctx.resolveImported(imported, name) : { file: filePath, name };
30797
+ if (!target) return;
30798
+ let { file } = target, declared = ctx.returnTypes(file)?.get(target.name);
30799
+ if (!declared || declared.promised && !awaited) return;
30513
30800
  let fromTest = isTestFile(file);
30514
30801
  return declaredClass(ctx, declared.name, file, (c) => fromTest || !c.test) ? { name: declared.name, file } : void 0;
30515
30802
  }
@@ -30839,28 +31126,35 @@ function queryLocalBlastRadius(graph, request) {
30839
31126
 
30840
31127
  // src/analysis/local-analyzer.ts
30841
31128
  function analyzeSavedCallers(input2, request, parser3) {
30842
- let registry = new ExportRegistry(), files = [], dynamic = /* @__PURE__ */ new Map(), incomplete = input2.incomplete;
31129
+ let registry = new ExportRegistry(), files = [], dynamic = /* @__PURE__ */ new Map(), knownFiles = new Set(input2.sources.keys()), resolver = createCapturedTypeScriptResolver(knownFiles, input2.configFiles), incomplete = input2.incomplete;
30843
31130
  for (let [filePath, bytes] of input2.sources)
30844
31131
  try {
30845
- let python = filePath.endsWith(".py"), parse2 = python ? parser3.parsePythonSource : parser3.parseTypeScriptSource, callFacts, pythonClasses, dynamicImports, parsed = parse2(bytes, filePath, (tree, file) => {
30846
- tree.hasError && (incomplete = !0), callFacts = readRegistryCallFacts(tree, file, input2.repoName), python ? pythonClasses = readPythonClasses(tree) : dynamicImports = readDynamicImportCandidates(tree);
31132
+ let python = filePath.endsWith(".py"), parse2 = python ? parser3.parsePythonSource : parser3.parseTypeScriptSource, callFacts, pythonClasses, dynamicImports, namedReExports, parsed = parse2(bytes, filePath, (tree, file) => {
31133
+ tree.hasError && (incomplete = !0), callFacts = readRegistryCallFacts(tree, file, input2.repoName), python ? pythonClasses = readPythonClasses(tree) : (dynamicImports = bindDynamicImportCandidates(
31134
+ readDynamicImportCandidates(tree),
31135
+ filePath,
31136
+ resolver.resolveSpecifier
31137
+ ), namedReExports = readNamedReExports(
31138
+ tree,
31139
+ filePath,
31140
+ resolver.resolveSpecifier,
31141
+ dynamicImports,
31142
+ file.exports,
31143
+ knownFiles
31144
+ ));
30847
31145
  });
30848
31146
  if (files.push(parsed), registry.addFromParsed([parsed], input2.repoName), callFacts) {
30849
31147
  for (let [uid, sites] of callFacts.callSites)
30850
31148
  registry.addCallSites(uid, parsed.path, sites);
30851
31149
  registry.addReturnTypes(parsed.path, callFacts.returnTypes);
30852
31150
  }
30853
- pythonClasses && registry.addPythonClasses(parsed.path, pythonClasses), dynamicImports && dynamic.set(parsed.path, dynamicImports);
31151
+ pythonClasses && registry.addPythonClasses(parsed.path, pythonClasses), namedReExports && registry.addNamedReExports(parsed.path, namedReExports), dynamicImports && dynamic.set(parsed.path, dynamicImports);
30854
31152
  } catch (error2) {
30855
31153
  if (isWasmAbort(error2)) throw error2;
30856
31154
  incomplete = !0;
30857
31155
  }
30858
- let resolver = createCapturedTypeScriptResolver(input2.sources.keys(), input2.configFiles);
30859
- for (let [filePath, candidates] of dynamic) {
30860
- if (candidates.length === 0) continue;
30861
- let sites = bindDynamicImportCandidates(candidates, filePath, resolver.resolveSpecifier);
30862
- registry.addDynamicImports(filePath, resolveDynamicImportSites(sites, filePath, files));
30863
- }
31156
+ for (let [filePath, sites] of dynamic)
31157
+ sites.length !== 0 && registry.addDynamicImports(filePath, resolveDynamicImportSites(sites, filePath, files));
30864
31158
  let stats = {
30865
31159
  resolvedCount: 0,
30866
31160
  unresolvedCount: 0,
@@ -30895,7 +31189,7 @@ function analyzeSavedCallers(input2, request, parser3) {
30895
31189
  functions: file.functions
30896
31190
  })),
30897
31191
  trace,
30898
- incomplete: incomplete || unresolvedDynamic || stats.unresolvedCount > 0
31192
+ incomplete: incomplete || !!trace.untraced?.length || unresolvedDynamic || stats.unresolvedCount > 0
30899
31193
  },
30900
31194
  request
30901
31195
  );
Binary file
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@pharaoh-so/local-analyzer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Pharaoh-so/pharaoh.git"
9
+ },
6
10
  "engines": {
7
11
  "node": ">=18"
8
12
  },