UNPKG

single-spa-web-server-utils

Version:

Utility functions for single-spa's recommended setup

124 lines (111 loc) 3.41 kB
import _ from "lodash"; import { applyOverrides, getOverridesFromCookies } from "import-map-overrides"; let importMapPromises = {}; let intervals = []; /** * * @typedef {{ * url: string; * pollInterval?: number; * req?: import('http').IncomingMessage; * allowOverrides?: boolean; * nodeKeyFilter?(importSpecifier: string): boolean; * }} GetImportMapOptions * * @param {GetImportMapOptions} options */ export function getImportMaps({ url, pollInterval = 30000, req, allowOverrides, nodeKeyFilter, }) { if (!importMapPromises[url]) { importMapPromises[url] = fetchImportMap(); const intervalId = setInterval(() => { importMapPromises[url] = fetchImportMap(); }, pollInterval); intervals.push(intervalId); } return importMapPromises[url].then((originalMap) => { // We initially resolve Error objects rather than reject them, // to avoid unhandled promise rejection crashes. So this check // is to verify whether this resolution was a "real resolve" // or just one to avoid an unhandled promise rejection. // https://github.com/single-spa/create-single-spa/issues/343 if (originalMap instanceof Error) { throw originalMap; } const browserImportMap = allowOverrides ? applyOverrides(originalMap, getOverridesFromCookies(req)) : originalMap; const nodeImportMap = _.cloneDeep(browserImportMap); if (nodeKeyFilter) { Object.keys(nodeImportMap.imports).forEach((key) => { if (!nodeKeyFilter(key)) { delete nodeImportMap.imports[key]; } }); } return { browserImportMap, nodeImportMap, }; }); function fetchImportMap() { return ( fetch(url) .then( (r) => { if (r.ok) { if ( r.headers.get("content-type") && r.headers.get("content-type").includes("json") ) { return r.json(); } else { throw Error( `Import Map at ${url} did not respond with correct content-type response header. Should be application/importmap+json, but was ${r.headers.get( "content-type" )}` ); } } else { throw Error( `Import Map at ${url} responded with HTTP status ${r.status}` ); } }, (err) => { console.error(err); throw Error(`Failed to fetch import map at url ${url}`); } ) // If we do not catch promise rejections here, they won't necessarily be caught at all, // which causes NodeJS to kill the entire program. // So we instead catch the error to turn it into a resolved promise, but // then check the promise result later on to see if it's an error // before proceeding // https://github.com/single-spa/create-single-spa/issues/343 .catch((err) => { return err; }) ); } } /** * This will stop all polling of import maps */ export function clearAllIntervals() { intervals.forEach(clearInterval); intervals = []; } /** * This will stop all polling of import maps, and also * discard any cached import maps that are still in memory */ export function reset() { clearAllIntervals(); importMapPromises = {}; }