fallow-type-aware 3.34.1 → 3.36.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.
@@ -0,0 +1,84 @@
1
+ import { ast } from "./backend.mjs";
2
+
3
+ const {
4
+ isExportDeclaration,
5
+ isImportDeclaration,
6
+ isImportEqualsDeclaration,
7
+ isModuleBlock,
8
+ isModuleDeclaration,
9
+ isNamedExports,
10
+ isNamespaceExportDeclaration,
11
+ isNamespaceImport,
12
+ } = ast;
13
+
14
+ const JAVASCRIPT_FILE = /\.[cm]?jsx?$/iu;
15
+
16
+ // In JavaScript, require bindings and export assignments also create aliases.
17
+ // The scan keeps every identifier of these files.
18
+ export const isJavaScriptFile = (sourceFile) => JAVASCRIPT_FILE.test(sourceFile.fileName);
19
+
20
+ const importNames = ({ importClause }) => {
21
+ const bindings = importClause?.namedBindings;
22
+ if (!bindings) return [importClause?.name];
23
+ if (isNamespaceImport(bindings)) return [importClause.name, bindings.name];
24
+ return [importClause.name, ...bindings.elements.map((element) => element.name)];
25
+ };
26
+
27
+ const exportNames = ({ exportClause }) => {
28
+ if (!exportClause) return [];
29
+ if (isNamedExports(exportClause)) return exportClause.elements.map((element) => element.name);
30
+ return [exportClause.name];
31
+ };
32
+
33
+ /** Returns the statements of a `namespace` or `declare module` body, also when nested. */
34
+ export const moduleBodyStatements = (body) => {
35
+ if (body && isModuleDeclaration(body)) return moduleBodyStatements(body.body);
36
+ return body && isModuleBlock(body) ? body.statements : [];
37
+ };
38
+
39
+ const statementAliasNames = (statement) => {
40
+ if (isImportDeclaration(statement)) return importNames(statement);
41
+ if (isImportEqualsDeclaration(statement) || isNamespaceExportDeclaration(statement)) {
42
+ return [statement.name];
43
+ }
44
+ if (isExportDeclaration(statement)) return exportNames(statement);
45
+ if (isModuleDeclaration(statement)) {
46
+ return moduleBodyStatements(statement.body).flatMap(statementAliasNames);
47
+ }
48
+ return [];
49
+ };
50
+
51
+ // Default-library and external-library files sit below node_modules. The
52
+ // path check skips them before the source file and its metadata, which each
53
+ // cost a checker round trip.
54
+ export const NODE_MODULES_SEGMENT = /[\\/]node_modules[\\/]/u;
55
+
56
+ const isOwnSourceFile = (project, sourceFile) =>
57
+ Boolean(sourceFile) &&
58
+ !project.program.isSourceFileDefaultLibrary(sourceFile) &&
59
+ !project.program.isSourceFileFromExternalLibrary(sourceFile);
60
+
61
+ /** Returns the program files outside node_modules and the default libraries. */
62
+ export const ownSourceFiles = (project) =>
63
+ project.program
64
+ .getSourceFileNames()
65
+ .filter((fileName) => !NODE_MODULES_SEGMENT.test(fileName))
66
+ .map((fileName) => project.program.getSourceFile(fileName))
67
+ .filter((sourceFile) => isOwnSourceFile(project, sourceFile));
68
+
69
+ const programAliasNames = (project) =>
70
+ ownSourceFiles(project)
71
+ .flatMap((sourceFile) => sourceFile.statements.flatMap(statementAliasNames))
72
+ .map((name) => name?.text)
73
+ .filter((text) => typeof text === "string");
74
+
75
+ /**
76
+ * Returns the identifier names that can reach a symbol-use target.
77
+ *
78
+ * An identifier resolves to a declaration with its own name, or to an alias
79
+ * that an import or export declares under that name. So the scan needs only
80
+ * the target names and the names that the alias declarations of the project
81
+ * files declare, project declaration files included.
82
+ */
83
+ export const referenceNames = (projects, targetNames) =>
84
+ new Set([...targetNames, "default", ...projects.flatMap(programAliasNames)]);
@@ -1,7 +1,11 @@
1
1
  import path from "node:path";
2
2
 
3
- import { SymbolFlags } from "typescript/unstable/sync";
4
- import {
3
+ import { sync, ast } from "./backend.mjs";
4
+
5
+ import { canonicalFileIdentity } from "./file-identity.mjs";
6
+
7
+ const { SymbolFlags } = sync;
8
+ const {
5
9
  isClassDeclaration,
6
10
  isClassExpression,
7
11
  isEnumDeclaration,
@@ -19,9 +23,7 @@ import {
19
23
  isSourceFile,
20
24
  isTypeAliasDeclaration,
21
25
  isVariableDeclaration,
22
- } from "typescript/unstable/ast/is";
23
-
24
- import { canonicalFileIdentity } from "./file-identity.mjs";
26
+ } = ast;
25
27
 
26
28
  const DECLARATION_KIND_RULES = [
27
29
  [[isClassDeclaration, isClassExpression], "class"],
package/src/semantic.mjs CHANGED
@@ -1,34 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
4
 
5
- import { API, SignatureKind, SymbolFlags } from "typescript/unstable/sync";
6
- import {
7
- isCallExpression,
8
- isClassDeclaration,
9
- isClassExpression,
10
- isDecorator,
11
- isElementAccessExpression,
12
- isExportDeclaration,
13
- isExportSpecifier,
14
- isFunctionBody,
15
- isGetAccessorDeclaration,
16
- isIdentifier,
17
- isImportDeclaration,
18
- isImportClause,
19
- isImportSpecifier,
20
- isImportTypeNode,
21
- isNamespaceImport,
22
- isPropertyAccessExpression,
23
- isQualifiedName,
24
- isPrivateIdentifier,
25
- isSetAccessorDeclaration,
26
- isStringLiteralLikeNode,
27
- isTypeNode,
28
- isTypeQueryNode,
29
- } from "typescript/unstable/ast/is";
5
+ import { ast, createApi as createBackendApi, heritageTypeName, sync } from "./backend.mjs";
30
6
 
31
7
  import { canonicalFileIdentity } from "./file-identity.mjs";
8
+ import { packagePathTargets, projectPackage } from "./project-package.mjs";
32
9
  import { projectResult, projectState } from "./project-state.mjs";
33
10
  import {
34
11
  declarationName,
@@ -51,8 +28,50 @@ import {
51
28
  symbolForDeclaration,
52
29
  } from "./semantic-identity.mjs";
53
30
  import { analyzeSymbolImpact } from "./symbol-impact.mjs";
31
+ import { isJavaScriptFile, referenceNames } from "./reference-names.mjs";
32
+ import { recordComputedMemberAccess } from "./computed-member-access.mjs";
33
+ import {
34
+ namespaceSourceIdentity,
35
+ recordDynamicImportUncertainty,
36
+ recordNamespaceUse,
37
+ } from "./namespace-uses.mjs";
38
+ import {
39
+ isLiteralMemberName,
40
+ qualifierExpression,
41
+ referenceValueSymbol,
42
+ shorthandReferenceSymbols,
43
+ } from "./shorthand-references.mjs";
44
+ import {
45
+ matchingAliasEntries,
46
+ matchingSymbolEntries,
47
+ recordSymbolUncertainty,
48
+ targetName,
49
+ } from "./symbol-targets.mjs";
54
50
  import { analyzeTypeCoupling } from "./type-coupling.mjs";
55
51
 
52
+ const { SignatureKind, SymbolFlags } = sync;
53
+ const {
54
+ isCallExpression,
55
+ isClassDeclaration,
56
+ isClassExpression,
57
+ isDecorator,
58
+ isExportDeclaration,
59
+ isExportSpecifier,
60
+ isFunctionBody,
61
+ isGetAccessorDeclaration,
62
+ isIdentifier,
63
+ isImportDeclaration,
64
+ isImportSpecifier,
65
+ isImportTypeNode,
66
+ isNamespaceImport,
67
+ isPropertyAccessExpression,
68
+ isPrivateIdentifier,
69
+ isSetAccessorDeclaration,
70
+ isStringLiteralLikeNode,
71
+ isTypeNode,
72
+ isTypeQueryNode,
73
+ } = ast;
74
+
56
75
  const ATTACHED_COMMENT_PATTERN = /\/\/|\/\*/u;
57
76
  const MAX_SYMBOL_USE_EVIDENCE = 1;
58
77
  const DECLARATION_KINDS = new Set([
@@ -352,23 +371,15 @@ const semanticUse = (root, node, namespace) => {
352
371
  };
353
372
  };
354
373
 
355
- const isDefaultImportReference = (node) => isImportClause(node.parent) && node.parent.name === node;
356
-
357
374
  const candidateNameMatches = (candidateNames, name) => !candidateNames || candidateNames.has(name);
358
375
 
359
- const isIdentifierReference = (node, candidateNames, includeDefaultImports) => {
360
- if (!isIdentifier(node)) return false;
361
- if (candidateNameMatches(candidateNames, node.text)) return true;
362
- return includeDefaultImports && isDefaultImportReference(node);
363
- };
364
-
365
- const isElementNameNode = (node) =>
366
- isStringLiteralLikeNode(node) &&
367
- isElementAccessExpression(node.parent) &&
368
- node.parent.argumentExpression === node;
376
+ const isIdentifierReference = (node, names) =>
377
+ isIdentifier(node) && candidateNameMatches(names, node.text);
369
378
 
370
379
  const isElementReference = (node, candidateNames) =>
371
- isElementNameNode(node) && candidateNameMatches(candidateNames, node.text);
380
+ isStringLiteralLikeNode(node) &&
381
+ candidateNameMatches(candidateNames, node.text) &&
382
+ isLiteralMemberName(node);
372
383
 
373
384
  const entriesByName = (entries) => {
374
385
  const byName = new Map();
@@ -396,8 +407,11 @@ const symbolUseBatchState = (resolvedQueries, evidenceLimit) => {
396
407
  uncertaintiesByQuery: new Map(resolvedQueries.map(({ query }) => [query.id, new Set()])),
397
408
  declarationLocationsByQuery: new Map(resolvedQueries.map(({ query }) => [query.id, new Set()])),
398
409
  targetsByKey: new Map(),
410
+ targetNames: new Set(),
411
+ aliasEntriesByProject: new Map(),
399
412
  exportEntriesByModule: new Map(),
400
413
  exportEntriesByProject: new Map(),
414
+ exportedNames: new Set(),
401
415
  candidateNames: new Set(),
402
416
  classMemberEntries,
403
417
  classMemberEntriesByName: entriesByName(classMemberEntries),
@@ -412,6 +426,7 @@ const addSymbolTarget = (root, state, entry, declaration, namespace) => {
412
426
  state.declarationLocationsByQuery
413
427
  .get(entry.query.id)
414
428
  .add(locationKey(location(root, declaration.name ?? declaration)));
429
+ state.targetNames.add(targetName(declaration));
415
430
  const key = stableDeclarationKey(declaration, namespace);
416
431
  const targets = state.targetsByKey.get(key) ?? [];
417
432
  targets.push(entry);
@@ -422,6 +437,7 @@ const registerSymbolTargets = (root, state, entry) => {
422
437
  state.candidateNames.add(entry.query.symbol.localName);
423
438
  state.candidateNames.add(entry.query.symbol.exportedName);
424
439
  if (entry.query.symbol.declarationKind === "export") {
440
+ state.exportedNames.add(entry.query.symbol.exportedName);
425
441
  const moduleIdentity = canonicalFileIdentity(entry.query.symbol.absolutePath);
426
442
  const entries = state.exportEntriesByModule.get(moduleIdentity) ?? new Set();
427
443
  entries.add(entry);
@@ -447,45 +463,6 @@ const registerSymbolTargets = (root, state, entry) => {
447
463
  state.contractRelationsByQuery.set(entry.query.id, entry.resolved.contractRelations);
448
464
  };
449
465
 
450
- const matchingSymbolEntries = (project, symbol, namespace, targetsByKey) => {
451
- const declarations = declarationsForSymbol(project, symbol);
452
- const matchingLane = declarations.filter((declaration) =>
453
- declarationNamespaces(declaration).has(namespace),
454
- );
455
- if (matchingLane.length > 0) {
456
- return new Set(
457
- matchingLane.flatMap(
458
- (declaration) => targetsByKey.get(stableDeclarationKey(declaration, namespace)) ?? [],
459
- ),
460
- );
461
- }
462
- return new Set(
463
- declarations.flatMap((declaration) =>
464
- [...declarationNamespaces(declaration)].flatMap(
465
- (declaredNamespace) =>
466
- targetsByKey.get(stableDeclarationKey(declaration, declaredNamespace)) ?? [],
467
- ),
468
- ),
469
- );
470
- };
471
-
472
- const matchingAliasEntries = (project, symbol, namespace, targetsByKey) => {
473
- const entries = new Set();
474
- const seen = new Set();
475
- let current = symbol;
476
- while (current && !seen.has(current)) {
477
- seen.add(current);
478
- matchingSymbolEntries(project, current, namespace, targetsByKey).forEach((entry) =>
479
- entries.add(entry),
480
- );
481
- if ((current.flags & SymbolFlags.Alias) === 0) break;
482
- const aliased = project.checker.getAliasedSymbol(current);
483
- if (project.checker.isUnknownSymbol(aliased) || aliased === current) break;
484
- current = aliased;
485
- }
486
- return entries;
487
- };
488
-
489
466
  const isDeclarationReference = (root, state, entry, node) => {
490
467
  const locations = state.declarationLocationsByQuery.get(entry.query.id);
491
468
  if (locations.has(locationKey(location(root, node)))) return true;
@@ -516,9 +493,6 @@ const importTypeSpecifier = (node) => {
516
493
  return literal && isStringLiteralLikeNode(literal) ? literal : undefined;
517
494
  };
518
495
 
519
- const isDynamicImportCall = (node) =>
520
- isCallExpression(node) && node.expression.getText(node.getSourceFile()) === "import";
521
-
522
496
  const MODULE_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
523
497
 
524
498
  const lexicalModuleIdentityForText = (project, sourceFile, specifierText) => {
@@ -554,19 +528,8 @@ const moduleIdentityForSpecifier = (project, specifier) => {
554
528
  };
555
529
 
556
530
  const namespaceImportDeclaration = (project, node) => {
557
- const parent = node.parent;
558
- if (!parent) return undefined;
559
- const namespace = isPropertyAccessExpression(parent)
560
- ? parent.name === node
561
- ? parent.expression
562
- : undefined
563
- : isElementAccessExpression(parent) && parent.argumentExpression === node
564
- ? parent.expression
565
- : isQualifiedName(parent) && parent.right === node
566
- ? parent.left
567
- : undefined;
568
- if (!namespace) return undefined;
569
- if (!isIdentifier(namespace)) return undefined;
531
+ const namespace = qualifierExpression(node);
532
+ if (!namespace || !isIdentifier(namespace)) return undefined;
570
533
  const symbol = project.checker.getSymbolAtLocation(namespace);
571
534
  return symbol?.declarations
572
535
  ?.map((declaration) => declaration.resolve(project))
@@ -574,7 +537,7 @@ const namespaceImportDeclaration = (project, node) => {
574
537
  };
575
538
 
576
539
  const localAliasModuleEdgeDeclaration = (project, node) => {
577
- const symbol = project.checker.getSymbolAtLocation(node);
540
+ const symbol = referenceValueSymbol(project, node);
578
541
  return symbol?.declarations
579
542
  ?.map((declaration) => moduleEdgeDeclaration(declaration.resolve(project)))
580
543
  .find(Boolean);
@@ -605,7 +568,8 @@ const referencedModuleIdentity = (project, node) => {
605
568
 
606
569
  const isExactExportReference = (project, entry, node) => {
607
570
  if (entry.query.symbol.declarationKind !== "export") return true;
608
- const referencedModule = referencedModuleIdentity(project, node);
571
+ const referencedModule =
572
+ referencedModuleIdentity(project, node) ?? namespaceSourceIdentity(project, node);
609
573
  if (!referencedModule) return false;
610
574
  const queryModule = canonicalFileIdentity(entry.query.symbol.absolutePath);
611
575
  return (
@@ -650,78 +614,37 @@ const classMemberEntrySetForProject = (state, project) => {
650
614
  return entries;
651
615
  };
652
616
 
653
- const recordSymbolUncertainty = (state, entry, reasonCode) => {
654
- state.uncertaintiesByQuery.get(entry.query.id).add(reasonCode);
655
- };
656
-
657
- const isExactElementReference = (node) => isElementNameNode(node) && typeof node.text === "string";
617
+ const isExactElementReference = (node) =>
618
+ isLiteralMemberName(node) && typeof node.text === "string";
658
619
 
659
- const recordStringDispatchedMemberAccess = (state, projectEntries, node) => {
660
- if (!isStringLiteralLikeNode(node) || isExactElementReference(node)) return;
620
+ const recordNameUncertainty = (state, projectEntries, node) =>
661
621
  (state.classMemberEntriesByName.get(node.text) ?? [])
662
622
  .filter((entry) => projectEntries.has(entry))
663
623
  .forEach((entry) => recordSymbolUncertainty(state, entry, "dynamic-member-access"));
664
- };
665
-
666
- const recordComputedMemberAccess = (state, project, entries, node) => {
667
- if (
668
- entries.size === 0 ||
669
- state.projectsWithUnknownDynamicAccess.has(project) ||
670
- !isElementAccessExpression(node) ||
671
- node.argumentExpression === undefined ||
672
- isStringLiteralLikeNode(node.argumentExpression)
673
- ) {
674
- return;
675
- }
676
- const receiverType = project.checker.getTypeAtLocation(node.expression);
677
- const receiverProperties = project.checker.getPropertiesOfType(receiverType);
678
- if (receiverProperties.length === 0) {
679
- entries.forEach((entry) => recordSymbolUncertainty(state, entry, "dynamic-member-access"));
680
- state.projectsWithUnknownDynamicAccess.add(project);
681
- return;
682
- }
683
- const matched = new Set(
684
- receiverProperties
685
- .filter((property) => state.candidateNames.has(property.name))
686
- .flatMap((property) => [
687
- ...matchingSymbolEntries(project, property, "value", state.targetsByKey),
688
- ]),
689
- );
690
- matched.forEach((entry) => recordSymbolUncertainty(state, entry, "dynamic-member-access"));
691
- };
692
624
 
693
- const recordDynamicImportUncertainty = (state, project, node) => {
694
- if (!isDynamicImportCall(node)) return;
695
- const specifier = node.arguments[0];
696
- if (!specifier || !isStringLiteralLikeNode(specifier)) {
697
- (state.exportEntriesByProject.get(project) ?? []).forEach((entry) =>
698
- recordSymbolUncertainty(state, entry, "dynamic-behavior"),
699
- );
700
- return;
701
- }
702
- const moduleIdentity = moduleIdentityForSpecifier(project, specifier);
703
- if (!moduleIdentity) return;
704
- (state.exportEntriesByModule.get(moduleIdentity) ?? []).forEach((entry) =>
705
- recordSymbolUncertainty(state, entry, "dynamic-behavior"),
706
- );
625
+ const recordStringDispatchedMemberAccess = (state, projectEntries, node) => {
626
+ if (!isStringLiteralLikeNode(node) || isExactElementReference(node)) return;
627
+ recordNameUncertainty(state, projectEntries, node);
707
628
  };
708
629
 
709
- const referenceNamespaces = (project, node, symbol) => {
630
+ const referenceNamespaces = (project, node, rawSymbol) => {
710
631
  const alias = aliasNode(node);
711
632
  if (!alias || !isExportSpecifier(alias)) {
712
633
  return [isTypePosition(node) ? "type" : "value"];
713
634
  }
714
635
  if (alias.isTypeOnly || alias.parent?.parent?.isTypeOnly) return ["type"];
715
636
  const namespaces = new Set(
716
- declarationsForSymbol(project, symbol).flatMap((declaration) => [
717
- ...declarationNamespaces(declaration),
718
- ]),
637
+ declarationsForSymbol(project, resolveAlias(project.checker, rawSymbol)).flatMap(
638
+ (declaration) => [...declarationNamespaces(declaration)],
639
+ ),
719
640
  );
720
641
  return namespaces.size > 0 ? [...namespaces] : [isTypePosition(node) ? "type" : "value"];
721
642
  };
722
643
 
723
644
  const surfaceEntriesForNode = (state, project, node, namespace) => {
724
- if (moduleEdgeDeclaration(node)) return new Set();
645
+ // Resolving the referenced module costs checker round trips, so skip every
646
+ // identifier that cannot name an export query.
647
+ if (!state.exportedNames.has(node.text) || moduleEdgeDeclaration(node)) return new Set();
725
648
  const moduleIdentity = referencedModuleIdentity(project, node);
726
649
  if (!moduleIdentity) return new Set();
727
650
  const direct = [...(state.exportEntriesByModule.get(moduleIdentity) ?? [])].filter(
@@ -732,7 +655,7 @@ const surfaceEntriesForNode = (state, project, node, namespace) => {
732
655
  for (const entry of direct) {
733
656
  for (const { declaration } of entry.resolved.ownerContexts) {
734
657
  const symbol = resolveAlias(project.checker, symbolForDeclaration(project, declaration));
735
- matchingSymbolEntries(project, symbol, namespace, state.targetsByKey).forEach((related) =>
658
+ matchingSymbolEntries(project, symbol, namespace, state).forEach((related) =>
736
659
  entries.add(related),
737
660
  );
738
661
  }
@@ -740,28 +663,32 @@ const surfaceEntriesForNode = (state, project, node, namespace) => {
740
663
  return entries;
741
664
  };
742
665
 
743
- const scanSymbolUseFile = (root, state, project, sourceFile, includeDefaultImports) => {
666
+ const scanSymbolUseFile = (root, state, project, sourceFile, names) => {
744
667
  const projectEntries = classMemberEntrySetForProject(state, project);
745
668
  const nodes = [];
746
669
  visit(sourceFile, (node) => {
747
- recordDynamicImportUncertainty(state, project, node);
670
+ recordDynamicImportUncertainty(state, project, node, moduleIdentityForSpecifier);
748
671
  if (projectEntries.size > 0) {
749
672
  recordStringDispatchedMemberAccess(state, projectEntries, node);
750
673
  recordComputedMemberAccess(state, project, projectEntries, node);
751
674
  }
752
- if (isIdentifierReference(node, undefined, includeDefaultImports)) {
675
+ if (isIdentifierReference(node, isJavaScriptFile(sourceFile) ? undefined : names)) {
753
676
  nodes.push(node);
754
677
  return;
755
678
  }
756
679
  if (isElementReference(node, state.candidateNames)) nodes.push(node);
757
680
  });
758
681
  const symbols = project.checker.getSymbolAtLocation(nodes);
682
+ const references = shorthandReferenceSymbols(project, nodes, symbols, state);
683
+ references.unresolved.forEach((node) => recordNameUncertainty(state, projectEntries, node));
759
684
  nodes.forEach((node, index) => {
760
685
  const rawSymbol = symbols[index];
761
- const symbol = resolveAlias(project.checker, rawSymbol);
762
- referenceNamespaces(project, node, symbol).forEach((namespace) => {
686
+ recordNamespaceUse(state, project, node, rawSymbol);
687
+ referenceNamespaces(project, node, rawSymbol).forEach((namespace) => {
763
688
  const entries = new Set([
764
- ...matchingAliasEntries(project, rawSymbol, namespace, state.targetsByKey),
689
+ ...references.symbols[index].flatMap((symbol) => [
690
+ ...matchingAliasEntries(project, symbol, namespace, state),
691
+ ]),
765
692
  ...surfaceEntriesForNode(state, project, node, namespace),
766
693
  ]);
767
694
  entries.forEach((entry) => recordSymbolUse(root, state, project, entry, node, namespace));
@@ -769,7 +696,7 @@ const scanSymbolUseFile = (root, state, project, sourceFile, includeDefaultImpor
769
696
  });
770
697
  };
771
698
 
772
- const scanSymbolUseProjects = (root, state, projects, includeDefaultImports) => {
699
+ const scanSymbolUseProjects = (root, state, projects, names) => {
773
700
  const scannedFiles = new Set();
774
701
  let sourceScanCount = 0;
775
702
  for (const project of projects) {
@@ -778,7 +705,7 @@ const scanSymbolUseProjects = (root, state, projects, includeDefaultImports) =>
778
705
  if (scannedFiles.has(fileIdentity)) continue;
779
706
  scannedFiles.add(fileIdentity);
780
707
  sourceScanCount += 1;
781
- scanSymbolUseFile(root, state, project, sourceFile, includeDefaultImports);
708
+ scanSymbolUseFile(root, state, project, sourceFile, names);
782
709
  }
783
710
  }
784
711
  return sourceScanCount;
@@ -787,10 +714,8 @@ const scanSymbolUseProjects = (root, state, projects, includeDefaultImports) =>
787
714
  const batchSymbolUseEvidence = (root, resolvedQueries, scanProjects, evidenceLimit) => {
788
715
  const state = symbolUseBatchState(resolvedQueries, evidenceLimit);
789
716
  resolvedQueries.forEach((entry) => registerSymbolTargets(root, state, entry));
790
- const includeDefaultImports = resolvedQueries.some(
791
- ({ query }) => query.symbol.exportedName === "default",
792
- );
793
- const sourceScanCount = scanSymbolUseProjects(root, state, scanProjects, includeDefaultImports);
717
+ const names = referenceNames(scanProjects, [...state.candidateNames, ...state.targetNames]);
718
+ const sourceScanCount = scanSymbolUseProjects(root, state, scanProjects, names);
794
719
  return {
795
720
  evidenceByQuery: state.evidenceByQuery,
796
721
  totalByQuery: state.totalByQuery,
@@ -1013,7 +938,7 @@ const frameworkContractRelationsFor = (root, project, declaration, contracts, me
1013
938
  : "extends";
1014
939
  for (const heritageType of clause.types ?? []) {
1015
940
  const rawSymbol =
1016
- project.checker.getSymbolAtLocation(heritageType.expression) ??
941
+ project.checker.getSymbolAtLocation(heritageTypeName(heritageType)) ??
1017
942
  project.checker.getTypeAtLocation(heritageType).aliasSymbol ??
1018
943
  project.checker.getTypeAtLocation(heritageType).symbol;
1019
944
  const symbol = resolveAlias(project.checker, rawSymbol);
@@ -1294,31 +1219,6 @@ const wildcardPattern = (target) => {
1294
1219
  return new RegExp(`^${escaped}$`, "u");
1295
1220
  };
1296
1221
 
1297
- const pathWithin = (root, candidate) =>
1298
- candidate === root || candidate.startsWith(`${root}${path.sep}`);
1299
-
1300
- const packageAt = (directory) => {
1301
- const packageFile = path.join(directory, "package.json");
1302
- if (!existsSync(packageFile)) return undefined;
1303
- try {
1304
- return { root: directory, json: JSON.parse(readFileSync(packageFile, "utf8")) };
1305
- } catch {
1306
- return { root: directory, json: {} };
1307
- }
1308
- };
1309
-
1310
- const projectPackage = (root, project) => {
1311
- const normalizedRoot = path.resolve(root);
1312
- let current = path.dirname(project.configFileName);
1313
- while (pathWithin(normalizedRoot, current)) {
1314
- const found = packageAt(current);
1315
- if (found) return found;
1316
- if (current === normalizedRoot) break;
1317
- current = path.dirname(current);
1318
- }
1319
- return { root: normalizedRoot, json: {} };
1320
- };
1321
-
1322
1222
  const ancestorRoots = (root, start) => {
1323
1223
  const normalizedRoot = path.resolve(root);
1324
1224
  const roots = [];
@@ -1386,12 +1286,6 @@ const addPathEntry = (entries, sourceFiles, roots, target) => {
1386
1286
  );
1387
1287
  };
1388
1288
 
1389
- const packagePathTargets = (project, packageJson) => {
1390
- const packageName = packageJson.name;
1391
- if (typeof packageName !== "string") return [];
1392
- return project.compilerOptions.paths?.[packageName] ?? [];
1393
- };
1394
-
1395
1289
  const discoverEntryPoints = (root, project, requested) => {
1396
1290
  const sourceFiles = sourceFileIndex(project);
1397
1291
  if (requested.length > 0) return requestedEntryPoints(requested, sourceFiles);
@@ -2205,7 +2099,7 @@ const executeDisposableSemanticAnalysis = (input) => {
2205
2099
 
2206
2100
  export const analyzeSemanticQueries = (
2207
2101
  { root, projects, queries, evidenceLimit },
2208
- { createApi = (cwd) => new API({ cwd }) } = {},
2102
+ { createApi = (cwd) => createBackendApi({ cwd }) } = {},
2209
2103
  ) => {
2210
2104
  if (queries.length === 0) return emptyRequestedAnalysis();
2211
2105
  const setupStartedAt = performance.now();
@@ -2224,7 +2118,10 @@ export const analyzeSemanticQueries = (
2224
2118
  }
2225
2119
  };
2226
2120
 
2227
- export const createSemanticSession = (root, { createApi = (cwd) => new API({ cwd }) } = {}) => {
2121
+ export const createSemanticSession = (
2122
+ root,
2123
+ { createApi = (cwd) => createBackendApi({ cwd }) } = {},
2124
+ ) => {
2228
2125
  const api = createApi(root);
2229
2126
  const state = {
2230
2127
  openFiles: new Set(),