flow-typed
Version:
A repository of high quality flow type definitions
167 lines (150 loc) • 5.11 kB
Flow
// @flow
import {mkdirp} from './fileUtils';
import {cloneInto, rebaseRepoMainline} from './git';
import {fs, os, path} from './node';
import semver from 'semver';
export const CACHE_REPO_EXPIRY = 1000 * 60; // 1 minute
const REMOTE_REPO_URL = 'https://github.com/flowtype/flow-typed.git';
async function cloneCacheRepo() {
await mkdirp(getCacheRepoDir());
try {
await cloneInto(REMOTE_REPO_URL, getCacheRepoDir());
} catch (e) {
console.error('ERROR: Unable to clone local cache repo!');
throw e;
}
await fs.writeFile(getLastUpdatedFile(), String(Date.now()));
}
let customCacheDir = null;
function getCacheDir() {
return customCacheDir === null
? path.join(os.homedir(), '.flow-typed')
: customCacheDir;
}
function clearCustomCacheDir(): void {
customCacheDir = null;
}
function setCustomCacheDir(dir: string): void {
customCacheDir = dir;
}
function getCacheRepoGitDir(): string {
return path.join(getCacheRepoDir(), '.git');
}
function getLastUpdatedFile(): string {
return path.join(getCacheRepoDir(), 'lastUpdated');
}
async function rebaseCacheRepo() {
if (
(await fs.exists(getCacheRepoDir())) &&
(await fs.exists(getCacheRepoGitDir()))
) {
try {
await rebaseRepoMainline(getCacheRepoDir());
} catch (e) {
console.error(
'ERROR: Unable to rebase the local cache repo. ' + e.message,
);
return false;
}
await fs.writeFile(getLastUpdatedFile(), String(Date.now()));
return true;
} else {
await cloneCacheRepo();
return true;
}
}
/**
* Ensure that the CACHE_REPO_DIR exists and is recently rebased.
* (else: create/rebase it)
*/
const cacheRepoEnsureToken: {
lastEnsured: number,
pendingEnsurance: Promise<*>,
} = {
lastEnsured: 0,
pendingEnsurance: Promise.resolve(),
};
export async function ensureCacheRepo(
cacheRepoExpiry: number = CACHE_REPO_EXPIRY,
): Promise<void> {
// Only re-run rebase checks if a check hasn't been run in the last 5 minutes
if (cacheRepoEnsureToken.lastEnsured + 5 * 1000 * 60 >= Date.now()) {
return cacheRepoEnsureToken.pendingEnsurance;
}
cacheRepoEnsureToken.lastEnsured = Date.now();
const prevEnsurance = cacheRepoEnsureToken.pendingEnsurance;
return (cacheRepoEnsureToken.pendingEnsurance = prevEnsurance.then(() =>
(async function() {
const repoDirExists = fs.exists(getCacheRepoDir());
const repoGitDirExists = fs.exists(getCacheRepoGitDir());
if (!(await repoDirExists) || !(await repoGitDirExists)) {
console.log(`• flow-typed cache not found, fetching from GitHub...`);
await cloneCacheRepo();
} else {
let lastUpdated = 0;
if (await fs.exists(getLastUpdatedFile())) {
// If the LAST_UPDATED_FILE has anything other than just a number in
// it, just assume we need to update.
const lastUpdatedRaw = await fs.readFile(getLastUpdatedFile());
const lastUpdatedNum = parseInt(lastUpdatedRaw, 10);
if (String(lastUpdatedNum) === String(lastUpdatedRaw)) {
lastUpdated = lastUpdatedNum;
}
}
if (lastUpdated + cacheRepoExpiry < Date.now()) {
console.log('• rebasing flow-typed cache...');
const rebaseSuccessful = await rebaseCacheRepo();
if (!rebaseSuccessful) {
console.log(
"\nNOTE: Unable to rebase local cache! If you don't currently " +
"have internet connectivity, no worries -- we'll update the " +
'local cache the next time you do.\n',
);
}
}
}
})(),
));
}
export function getCacheRepoDir(): string {
return path.join(getCacheDir(), 'repo');
}
export async function verifyCLIVersion(): Promise<void> {
const metadataPath = path.join(
getCacheRepoDir(),
'definitions',
'.cli-metadata.json',
);
const metadata = await fs.readJson(metadataPath);
const compatibleCLIRange = metadata.compatibleCLIRange;
if (!compatibleCLIRange) {
throw new Error(
`Unable to find the 'compatibleCLIRange' property in ${metadataPath}. ` +
`You might need to update your flow-typed CLI to the latest version.`,
);
}
const thisCLIPkgJsonPath = path.join(__dirname, '..', '..', 'package.json');
const thisCLIPkgJson = await fs.readJson(thisCLIPkgJsonPath);
const thisCLIVersion = thisCLIPkgJson.version;
if (
!semver.satisfies(
semver.coerce(thisCLIVersion) ?? thisCLIVersion,
compatibleCLIRange,
)
) {
throw new Error(
`Please upgrade your flow-typed CLI! This CLI is version ` +
`${thisCLIVersion}, but the latest flow-typed definitions are only ` +
`compatible with flow-typed@${compatibleCLIRange}`,
);
}
}
export {
CACHE_REPO_EXPIRY as _CACHE_REPO_EXPIRY,
cacheRepoEnsureToken as _cacheRepoEnsureToken,
clearCustomCacheDir as _clearCustomCacheDir,
getCacheRepoGitDir as _getCacheRepoGitDir,
getLastUpdatedFile as _getLastUpdatedFile,
REMOTE_REPO_URL as _REMOTE_REPO_URL,
setCustomCacheDir as _setCustomCacheDir,
};