UNPKG

@podium/client

Version:

Client for fetching podium component fragments over HTTP.

121 lines (111 loc) 4.04 kB
import { AssetJs, AssetCss, uriRelativeToAbsolute } from '@podium/utils'; /** * Checks if a header object has a header. * Will return true if the header exist and is not an empty * string or a string of whitespace. * * @param {object} headers * @param {string} header * @returns {boolean} */ export const isHeaderDefined = (headers, header) => { if (headers[header] === undefined || headers[header].trim() === '') { return false; } return true; }; /** * Check if there is a difference between two manifests in * the changelog event object emitted from the internal * cache registry. * * @param {object} item A changelog event object * @returns {boolean} */ export const hasManifestChange = (item) => { const oldVersion = item.oldVal ? item.oldVal.version : ''; const newVersion = item.newVal ? item.newVal.version : ''; return oldVersion !== newVersion; }; /** * Check if a value is a Podium HttpIncoming object or not. If not, it * assume the incoming value is a context * * @param {object} incoming * @returns {boolean} */ export const validateIncoming = (incoming = {}) => Object.prototype.toString.call(incoming) === '[object PodiumHttpIncoming]'; /** * Filter assets array based on scope. * If scope property is not present, asset will be included (backwards compatibility) * If scope property is set to "all", asset will be included. * If scope is set to "content" and asset scope property is set to "fallback", asset will not be included * If scope is set to "fallback" and asset scope property is set to "content", asset will not be included * @template {import("@podium/utils").AssetCss | import("@podium/utils").AssetJs} T[] * @param {"content" | "fallback" | "all"} scope * @param {T[]} assets * @returns {T[]} * * @example * ``` * // plain objects work * const assets = filterAssets("content", [{..., scope: "content"}, {..., scope: "fallback"}]); * // as do AssetJs and AssetCSS objects * const assets = filterAssets("content", [new AssetCss(), new AssetCss()]); * ``` */ export const filterAssets = (scope, assets) => { // if undefined or null, passthrough if (!assets) return assets; // if a non array value is given, throw if (!Array.isArray(assets)) throw new TypeError( `Asset definition must be of type array. Got ${typeof assets}`, ); // filter the array of asset definitions to matchin scope or anything with all. Treat no scope the same as "all" for backwards compatibility. return assets.filter( (asset) => !asset.scope || asset.scope === scope || asset.scope === 'all', ); }; // parse link headers in AssetCss and AssetJs objects export const parseLinkHeaders = (headers, manifestUri) => { const links = []; if (!headers) return links; headers.split(',').forEach((link) => { const parts = link.split(';'); const [href, ...rest] = parts; const value = uriRelativeToAbsolute( href.replace(/<|>|"/g, '').trim(), manifestUri, ); const asset = { value }; for (const key of rest) { const [keyName, keyValue] = key.split('='); asset[keyName.trim()] = keyValue.trim(); } if (asset['asset-type'] === 'script') { links.push(new AssetJs(asset)); } if (asset['asset-type'] === 'style') { links.push(new AssetCss(asset)); } }); return links; }; export const toPreloadAssetObjects = (assetObjects) => { return assetObjects.map((assetObject) => { return new AssetCss({ type: assetObject instanceof AssetJs ? 'application/javascript' : 'text/css', crossorigin: !!assetObject.crossorigin, rel: 'preload', as: assetObject instanceof AssetJs ? 'script' : 'style', media: assetObject.media || '', value: assetObject.value.trim(), }); }); };