shelving
Version:
Toolkit for using data in JavaScript.
90 lines (89 loc) • 3.8 kB
JavaScript
import { getDictionaryItems } from "../../util/dictionary.js";
import { requireLink } from "../../util/link.js";
import { withURIParams } from "../../util/uri.js";
import { requireURL } from "../../util/url.js";
/** Turn a deconstructed CSP into a string. */
export function joinMetaCSP(csp) {
if (typeof csp === "string")
return csp;
if (csp !== null && csp !== undefined)
return Object.entries(csp).map(_mapCSP).join("; ");
}
const _mapCSP = ([key, content]) => `${key} ${content.join(" ")}`;
/** Merge two page or site titles together, e.g. `Manchester Runners` + `Messages` becomes `Messages - Manchester Runners` */
export function joinTitles(...titles) {
return titles.filter(Boolean).join(" - ");
}
/**
* Merge two `MetaData` objects.
* - `title` is merged.
* - `URL` is resolved to an absolute URL, e.g. `./d/e/f` + `/a/b/c` becomes `https://d.com/a/b/c/d/e/f`
* - `stylesheets` and `links` hrefs newly set in `meta2` are absolutified against the merged `url`/`base`, so they stay correct no matter where they are later rendered.
*/
export function mergeMeta(meta1, meta2, caller = mergeMeta) {
const title = joinTitles(meta2.title, meta1.title);
const root = mergeMetaURL(undefined, meta1.root, meta2.root, undefined, caller);
const url = mergeMetaURL(root, meta1.url, meta2.url, meta2.params, caller);
return {
...meta1,
...meta2,
root,
url,
title,
tags: mergeMetaTags(meta1.tags, meta2.tags),
links: mergeMetaLinks(meta1.links, meta2.links, url, root, caller),
modules: mergeMetaAssets(meta1.modules, meta2.modules, url, root, caller),
scripts: mergeMetaAssets(meta1.scripts, meta2.scripts, url, root, caller),
stylesheets: mergeMetaAssets(meta1.stylesheets, meta2.stylesheets, url, root, caller),
};
}
/**
* Create a fully-formed `Meta` from a `PossibleMeta`.
* - Like `mergeMeta()` but with no previous `Meta` to merge into — initialises meta from scratch.
*/
export function createMeta(meta, caller = createMeta) {
return mergeMeta({}, meta, caller);
}
/**
* Merge two metadata URLs.
* - New URL is resolved relative to: current URL, new base URL, current base URL
*/
export function mergeMetaURL(base, current, next, params, caller = mergeMetaURL) {
const url = next ? requireURL(next, base, caller) : current;
return url && params ? withURIParams(url, params, caller) : url;
}
/**
* Merge two metadata tags.
* - New assets are resolved relative to current URL (relative paths) and root URL (absolute paths).
*/
export function mergeMetaTags(current, next) {
return current && next ? { ...current, ...next } : current || next;
}
/**
* Merge two metadata link lists.
* - New assets are resolved relative to current URL (relative paths) and root URL (absolute paths).
*/
export function mergeMetaLinks(current, next, url, root, caller = mergeMetaLinks) {
return next ? { ...current, ..._yieldMetaLinkEntries(next, url, root, caller) } : current;
}
function* _yieldMetaLinkEntries(links, url, root, caller) {
for (const [k, link] of getDictionaryItems(links))
if (link)
yield [k, requireLink(link, url, root, caller)];
}
/**
* Merge two metadata asset lists.
* - New assets are resolved relative to current URL (relative paths) and root URL (absolute paths).
*/
export function mergeMetaAssets(current, next, url, root, caller = mergeMetaAssets) {
if (next) {
const mapped = _yieldMetaAssets(next, url, root, caller);
return current ? [...current, ...mapped] : Array.from(mapped);
}
return current;
}
function* _yieldMetaAssets(assets, url, root, caller) {
for (const asset of assets)
if (asset)
yield requireLink(asset, url, root, caller);
}