UNPKG

myst-cli

Version:
118 lines (117 loc) 5.23 kB
import { selectAll } from 'unist-util-select'; import { RuleId, fileWarn, plural, selectMdastNodes } from 'myst-common'; import { computeHash, tic } from 'myst-cli-utils'; import { addChildrenFromTargetNode } from 'myst-transforms'; import { loadFromCache, writeToCache } from '../session/cache.js'; export const XREF_MAX_AGE = 1; // in days function mystDataFilename(dataUrl) { return `myst-${computeHash(dataUrl)}.json`; } async function fetchMystData(session, dataUrl, urlSource, vfile) { const onError = (note) => { var _a; fileWarn(vfile, `Unable to resolve link text from external MyST reference: ${(_a = urlSource !== null && urlSource !== void 0 ? urlSource : dataUrl) !== null && _a !== void 0 ? _a : ''}`, { ruleId: RuleId.mystLinkValid, note, }); }; if (!dataUrl) { return onError('Data source URL unavailable'); } const filename = mystDataFilename(dataUrl); const cacheData = loadFromCache(session, filename, { maxAge: XREF_MAX_AGE }); if (cacheData) { return JSON.parse(cacheData); } let data; try { const resp = await session.fetch(dataUrl); if (!resp.ok) { return onError('Could not fetch data from URL'); } data = (await resp.json()); } catch { return onError('Could not load fetched data'); // data is unset } writeToCache(session, filename, JSON.stringify(data)); return data; } export async function fetchMystLinkData(session, node, vfile) { return fetchMystData(session, node.dataUrl, node.urlSource, vfile); } export async function fetchMystXRefData(session, node, vfile) { let dataUrl; if (node.remoteBaseUrl && node.dataUrl) { dataUrl = `${node.remoteBaseUrl}${node.dataUrl}`; } return fetchMystData(session, dataUrl, node.urlSource, vfile); } export function nodesFromMystXRefData(data, identifier, vfile, opts) { var _a, _b; let targetNodes; [data, ...Object.values((_b = (_a = data.frontmatter) === null || _a === void 0 ? void 0 : _a.parts) !== null && _b !== void 0 ? _b : {})].forEach(({ mdast }) => { if (!mdast || (targetNodes === null || targetNodes === void 0 ? void 0 : targetNodes.length)) return; targetNodes = selectMdastNodes(mdast, identifier, opts === null || opts === void 0 ? void 0 : opts.maxNodes).nodes; }); if (!(targetNodes === null || targetNodes === void 0 ? void 0 : targetNodes.length)) { fileWarn(vfile, `Unable to resolve content from external MyST reference: ${opts === null || opts === void 0 ? void 0 : opts.urlSource}`, { ruleId: RuleId.mystLinkValid, note: `Could not locate identifier ${identifier} in page content`, }); return; } return targetNodes; } /** * Load external MyST project data to update link text for MyST xrefs */ export async function transformMystXRefs(session, vfile, mdast, frontmatter) { const toc = tic(); const nodes = selectAll('link,crossReference', mdast) .filter((node) => { // Only handle MyST xrefs return node.protocol === 'xref:myst' && node.dataUrl; }) .filter((node) => { var _a; // If no link text, load the target to compute children if (!((_a = node.children) === null || _a === void 0 ? void 0 : _a.length)) return true; // If `identifier` is present, we may need to update existing children (e.g. enumerator) return !!node.identifier; }); if (nodes.length === 0) return; session.log.debug(`Updating link text for ${plural('%s external MyST reference(s)', nodes)}`); let number = 0; await Promise.all([ ...nodes.map(async (node) => { var _a, _b, _c, _d, _e; if (!node.identifier) { // Page references without specific node identifier const data = await fetchMystLinkData(session, node, vfile); if (!data) return; node.children = [{ type: 'text', value: (_c = (_b = (_a = data.frontmatter) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : data.slug) !== null && _c !== void 0 ? _c : '' }]; } else { const data = await fetchMystXRefData(session, node, vfile); if (!data) return; const targets = nodesFromMystXRefData(data, node.identifier, vfile, { urlSource: node.urlSource, maxNodes: 1, }); if (targets === null || targets === void 0 ? void 0 : targets.length) { addChildrenFromTargetNode(node, targets[0], frontmatter.numbering, vfile, (_e = (_d = frontmatter.numbering) === null || _d === void 0 ? void 0 : _d.title) === null || _e === void 0 ? void 0 : _e.offset); } } number += 1; }), ]); const denominator = number === nodes.length ? '' : `/${nodes.length}`; session.log.info(toc(`🪄 Updated link text for ${plural(`%s${denominator} external MyST reference(s)`, number)} in %s seconds`)); }