flow-typed
Version:
A repository of high quality flow type definitions
191 lines (166 loc) • 5.52 kB
Flow
// @flow
import path from 'path';
import semver from 'semver';
import {getCacheRepoDir} from './cacheRepoUtils';
import {fs} from './node';
import {ValidationError} from './ValidationError';
import {
disjointVersionsAll as disjointFlowVersionsAll,
parseDirString as parseFlowDirString,
toSemverString as flowVersionToSemver,
type FlowVersion,
} from './flowVersion';
import {isExcludedFile} from './fileUtils';
import {TEST_FILE_NAME_RE} from './libDefs';
import {findLatestFileCommitHash} from './git';
type EnvLibDef = {
name: string,
flowVersion: FlowVersion,
path: string,
testFilePaths: Array<string>,
};
export const getEnvDefs = async (): Promise<Array<EnvLibDef>> => {
const definitionsDir = path.join(getCacheRepoDir(), 'definitions');
const envDefsDirPath = path.join(definitionsDir, 'environments');
const dirItems = await fs.readdir(envDefsDirPath);
const errors = [];
const proms = dirItems.map(async itemName => {
if (isExcludedFile(itemName)) return;
try {
return await getSingleEnvDef(itemName, envDefsDirPath);
} catch (e) {
errors.push(e);
}
});
const settled = await Promise.all(proms);
if (errors.length) {
throw errors;
}
return [...settled].filter(Boolean).flat();
};
const getSingleEnvDef = async (defName: string, envDefPath: string) => {
const itemPath = path.join(envDefPath, defName);
const itemStat = await fs.stat(itemPath);
if (itemStat.isDirectory()) {
// itemPath must be an env dir
return await extractEnvDefs(itemPath, defName);
} else {
throw new ValidationError(
`Expected only directories to be present in this directory.`,
);
}
};
async function extractEnvDefs(
envDirPath: string,
defName: string,
): Promise<Array<EnvLibDef>> {
const envDefFileName = `${defName}.js`;
const envDirItems = await fs.readdir(envDirPath);
const commonTestFiles = [];
const parsedFlowDirs: Array<[string, FlowVersion]> = [];
envDirItems.forEach(envDirItem => {
const envDirItemPath = path.join(envDirPath, envDirItem);
const envDirItemStat = fs.statSync(envDirItemPath);
if (envDirItemStat.isFile()) {
const isValidTestFile = TEST_FILE_NAME_RE.test(envDirItem);
if (isValidTestFile) commonTestFiles.push(envDirItemPath);
} else if (envDirItemStat.isDirectory()) {
const parsedFlowDir = parseFlowDirString(envDirItem);
parsedFlowDirs.push([envDirItemPath, parsedFlowDir]);
} else {
throw new ValidationError('Unexpected directory item');
}
});
if (!disjointFlowVersionsAll(parsedFlowDirs.map(([_, ver]) => ver))) {
throw new ValidationError(`Flow versions not disjoint on ${defName}!`);
}
if (parsedFlowDirs.length === 0) {
throw new ValidationError('No libdef files found!');
}
const envDefs = [];
await Promise.all(
parsedFlowDirs.map(async ([flowDirPath, flowVersion]) => {
const testFilePaths = [...commonTestFiles];
let libDefFilePath: null | string = null;
(await fs.readdir(flowDirPath)).forEach(flowDirItem => {
const flowDirItemPath = path.join(flowDirPath, flowDirItem);
const flowDirItemStat = fs.statSync(flowDirItemPath);
if (flowDirItemStat.isFile()) {
if (path.extname(flowDirItem) === '.swp') {
return;
}
// Is this the env def file?
if (flowDirItem === envDefFileName) {
libDefFilePath = path.join(flowDirPath, flowDirItem);
return;
}
// Is this a test file?
const isValidTestFile = TEST_FILE_NAME_RE.test(flowDirItem);
if (isValidTestFile) {
testFilePaths.push(flowDirItemPath);
return;
}
throw new ValidationError(
`Unexpected file: ${envDefFileName}. This directory can only contain test files ` +
`or a env def file named \`${envDefFileName}\`.`,
);
} else {
throw new ValidationError(
`Unexpected sub-directory. This directory can only contain test ` +
`files or a env def file named \`${envDefFileName}\`.`,
);
}
});
if (libDefFilePath === null) {
libDefFilePath = path.join(flowDirPath, envDefFileName);
throw new ValidationError(
`No env def file found. Looking for a file named ${envDefFileName}`,
);
}
envDefs.push({
name: defName,
flowVersion,
path: libDefFilePath,
testFilePaths,
});
}),
);
return envDefs;
}
export const findEnvDef = (
defName: string,
flowVersion: FlowVersion,
useCacheUntil: number,
envDefs: Array<EnvLibDef>,
): EnvLibDef | void => {
return envDefs.filter(def => {
let filterMatch = def.name === defName;
if (!filterMatch) {
return false;
}
switch (def.flowVersion.kind) {
case 'all':
return true;
case 'ranged':
case 'specific':
return semver.satisfies(
flowVersionToSemver(flowVersion),
flowVersionToSemver(def.flowVersion),
);
default:
return true;
}
})[0];
};
export async function getEnvDefVersionHash(
repoDirPath: string,
libDef: EnvLibDef,
): Promise<string> {
const latestCommitHash = await findLatestFileCommitHash(
repoDirPath,
path.relative(repoDirPath, libDef.path),
);
return `${latestCommitHash.substr(0, 10)}/${
libDef.name
}/flow_${flowVersionToSemver(libDef.flowVersion)}`;
}