Skip to content

[7.1 API] Equivalent of BuilderProgram.getAllDependencies / batched module resolutionΒ #64920

Open

Description

@nicojs

πŸ” Search Terms

getAllDependencies, dependency graph, getResolvedModule, batched, 7.1 API, typescript/sync, typescript/async

βœ… Viability Checklist

⭐ Suggestion

In TypeScript ≀ 6, BuilderProgram.getAllDependencies(sourceFile) returned the transitive set of files a given source file depends on (module imports, /// <reference path>, type reference directives, and project-reference redirects).

With the 7.1 beta, builder/watch/solution-builder APIs are explicitly out of scope for stable (#64709), so there is no equivalent. The building blocks are mostly present (program.getSourceFileNames(), program.getSourceFile(), and the new program.getResolvedModule() / getResolvedTypeReferenceDirective() from #64247), but reconstructing the dependency graph requires one IPC round-trip per import per file, which gets expensive quickly on larger projects.

I'd like to request one (or both) of the following:

  1. A dependency-graph method on Program β€” e.g. program.getAllDependencies(sourceFile) or program.getDependencies(sourceFile) returning the direct/transitive dependency file names, computed server-side.
  2. A batched resolution method β€” e.g. program.getResolvedModules(sourceFile) returning all resolutions for a file in a single call, similar in spirit to the batched methods added in Add batched version for the several APIΒ #63904. This would let consumers build the graph themselves with one call per file instead of one call per import.

Option 1 is the ergonomic fit for the old API; option 2 is a smaller primitive and can be used independently.

πŸ“ƒ Motivating Example

I use getAllDependencies in StrykerJS to identify inrelated files and use that to implement mutant grouping (a roughly 50% performance improvement over single mutant validation). Migrating to 7.1 currently means re-implementing the traversal client-side.

Current client-side reconstruction (7.1 beta) (using AI)
import { API } from "typescript/sync";

function getAllDependencies(program: Program, rootFileName: string) {
  const seen = new Set<string>();
  const queue = [rootFileName];

  while (queue.length) {
    const fileName = queue.pop()!;
    if (seen.has(fileName)) continue;
    seen.add(fileName);

    const sf = program.getSourceFile(fileName);
    if (!sf) continue;

    // One round-trip per import
    for (const imp of sf.imports) {
      const r = program.getResolvedModule(sf, imp.text /*, mode */);
      if (r?.resolvedModule) queue.push(r.resolvedModule.resolvedFileName);
    }

    // One round-trip per type reference directive
    for (const ref of sf.typeReferenceDirectives) {
      const r = program.getResolvedTypeReferenceDirective(sf, ref.fileName);
      if (r?.resolvedTypeReferenceDirective?.resolvedFileName) {
        queue.push(r.resolvedTypeReferenceDirective.resolvedFileName);
      }
    }

    for (const ref of sf.referencedFiles) {
      queue.push(resolve(dirname(fileName), ref.fileName));
    }
  }

  seen.delete(rootFileName);
  return [...seen];
}

Issues with this approach:

  • Round-trip count scales with total import count, not file count.
  • Project-reference output redirects (handled by the old builder method) have to be reimplemented manually.
  • Resolution mode (ESNext vs CommonJS) must be derived client-side per import.

πŸ’» Use Cases

  1. What do you want to use this for?

Generate a list of unrelated files so we can validate multiple mutants in a single type-check run.

  1. What shortcomings exist with current approaches?

Per-import IPC calls make the client-side traversal slow on large graphs, and project-reference redirects are easy to get subtly wrong.

  1. What workarounds are you using in the meantime?

The reconstruction above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions