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()];
}