export async function buildState()

in eng/tools/lint-diff/src/processChanges.ts [62:150]


export async function buildState(
  changedSpecFiles: string[],
  rootPath: string,
): Promise<[Map<string, string[]>, string[]]> {
  // Filter changed files to include only those that exist in the rootPath
  const existingChangedFiles = [];
  for (const file of changedSpecFiles) {
    if (await pathExists(join(rootPath, file))) {
      existingChangedFiles.push(file);
    }
  }

  // Get affected services from changed files
  // e.g. specification/service1/readme.md -> specification/service1
  const affectedServiceDirectories = await getAffectedServices(existingChangedFiles);

  // Get a map of a service's swagger files and their dependencies
  // TODO: Use set or array?
  const affectedSwaggerMap = new Map<string, string[]>();
  for (const serviceDir of affectedServiceDirectories) {
    const changedServiceFiles = existingChangedFiles.filter((file) => file.startsWith(serviceDir));
    const serviceDependencyMap = await getSwaggerDependenciesMap(rootPath, serviceDir);

    affectedSwaggerMap.set(
      serviceDir,
      await getAffectedSwaggers(changedServiceFiles, serviceDependencyMap),
    );
  }

  // Use changedSpecFiles (which might not be present in the branch) to get
  // a set of affected readme files.
  const affectedReadmes = await getAffectedReadmes(changedSpecFiles, rootPath);

  const readmeTags = new Map<string, Set<string>>();
  for (const readme of affectedReadmes) {
    const readmeService = await getService(readme);
    if (!affectedSwaggerMap.has(readmeService)) {
      continue;
    }

    // TODO: The parser is used twice to get tags and input files. This can be
    // made more efficient.
    const readmeContent = await readFile(join(rootPath, readme), { encoding: "utf-8" });
    for (const tag of getAllTags(readmeContent)) {
      const inputFiles = getInputFiles(readmeContent, tag).map((file) =>
        join(dirname(readme), file),
      );
      if (inputFiles === undefined || inputFiles.length === 0) {
        continue;
      }

      // Readme + Tag Combo

      // TODO: ensure ! is correct to do here
      for (const swagger of affectedSwaggerMap.get(readmeService)!) {
        if (inputFiles.includes(swagger)) {
          if (!readmeTags.has(readme)) {
            readmeTags.set(readme, new Set<string>());
          }
          readmeTags.get(readme)?.add(tag);
        }
      }
    }
  }

  // TODO: Deduplicate inside or outside state building? It's possible that
  // later processing like that in reconcileChangedFilesAndTags
  const changedFileAndTagsMap = new Map<string, string[]>();
  for (const [readme, tags] of readmeTags.entries()) {
    const dedupedTags = deduplicateTags(
      await getTagsAndInputFiles(
        [...tags],
        await readFile(join(rootPath, readme), { encoding: "utf-8" }),
      ),
    );

    changedFileAndTagsMap.set(readme, dedupedTags);
  }

  // For readme files that have changed but there are no affected swaggers,
  // add them to the map with no tags
  for (const changedReadme of affectedReadmes) {
    if (!changedFileAndTagsMap.has(changedReadme)) {
      changedFileAndTagsMap.set(changedReadme, []);
    }
  }

  return [changedFileAndTagsMap, Array.from(affectedSwaggerMap.values()).flat()];
}