UNPKG

myst-cli

Version:
109 lines (108 loc) 3.72 kB
import { RuleId, plural, fileError, toText } from 'myst-common'; import { selectAll } from 'unist-util-select'; import { computeHash, tic } from 'myst-cli-utils'; import { loadFromCache, writeToCache } from '../session/cache.js'; const ROR_MAX_AGE = 30; // in days /** * Build a path to the cache-file for the given ROR * * @param session: CLI session * @param ror: normalized ROR ID */ function rorCacheFilename(ror) { return `ror-${computeHash(ror)}.json`; } /** * Resolve the given ror.org ID into JSON data about the organization * * @param session - CLI session * @param ror - ror.org ID */ export async function resolveRORAsJSON(session, ror) { const url = `https://api.ror.org/v2/organizations/${ror}`; session.log.debug(`Fetching ROR JSON from ${url}`); const response = await session.fetch(url).catch(() => { session.log.debug(`Request to ${url} failed.`); return undefined; }); if (!response || !response.ok) { session.log.debug(`ROR fetch failed for ${url}`); return undefined; } const data = (await response.json()); return data; } /** * Fetch organization data for the given ROR ID in JSON * * @param session - CLI session * @param vfile * @param node * @param ror - ror ID (does not include the https://ror.org) */ export async function resolveROR(session, vfile, node, ror) { if (!ror) return undefined; // Cache ROR resolution as JSON const filename = rorCacheFilename(ror); const cached = loadFromCache(session, filename, { maxAge: ROR_MAX_AGE }); if (cached) return JSON.parse(cached); const toc = tic(); let data; try { data = await resolveRORAsJSON(session, ror); if (data) { session.log.debug(toc(`Fetched ROR JSON for ror:${ror} in %s`)); } else { fileError(vfile, `Could not find ROR "${ror}" from https://ror.org/${ror}`, { node, ruleId: RuleId.rorLinkValid, note: `Please check the ROR is correct and has a page at https://ror.org/${ror}`, }); session.log.debug(`JSON not available from ror.org for ror:${ror}`); } } catch (error) { session.log.debug(`JSON from ror.org was malformed for ror:${ror}`); } if (!data) return undefined; writeToCache(session, filename, JSON.stringify(data)); return data; } /** * Find in-line RORs and add default text */ export async function transformLinkedRORs(session, vfile, mdast, path) { const toc = tic(); const linkedRORs = selectAll('link[protocol=ror]', mdast); if (linkedRORs.length === 0) return; session.log.debug(`Found ${plural('%s ROR(s)', linkedRORs.length)} to auto link.`); const statuses = await Promise.all(linkedRORs.map(async (node) => { const ror = node.data?.ror; if (!ror) return false; const rorData = await resolveROR(session, vfile, node, ror); if (rorData === undefined) { return false; } if (toText(node.children) !== ror) { return true; } const displayEntry = rorData.names.find((entry) => entry.types.includes('ror_display')); if (displayEntry === undefined) { return true; } // If the link text is the ROR, update with a organization name node.children = [{ type: 'text', value: displayEntry.value }]; return true; })); const nLinked = statuses.reduce((total, current) => total + +current, 0); if (nLinked > 0) { session.log.info(toc(`🪄 Linked ${plural('%s ROR(s)', nLinked)} in %s for ${path}`)); } return; }