openvibers/openvibe.shared crawlable.js

crawlable.js

main 2.6 KB · 33 lines · javascript

1'use strict';
2
3const seo = require('./seo');
4const catalog = require('./crawl-sites.json');
5const esc = (value) => String(value == null ? '' : value).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
6const absolute = (url, base) => new URL(url, base).href;
7
8/** Static navigation, network links and discovery metadata for HTML sent before JavaScript runs. */
9function render({ name, service, url, description = '', links, pages = [], searchUrl } = {}) {
10 if (!name || !url) throw new TypeError('crawlable.render requires name and url');
11 const base = new URL(url).origin;
12 const ownLinks = Array.isArray(links) ? links : [];
13 const ownItems = ownLinks.flatMap((item) => Array.isArray(item.items) ? item.items : [item])
14 .filter((item) => item && (item.href || item.url) && (item.label || item.name || item.title))
15 .map((item) => ({ label: item.label || item.name || item.title, href: item.href || item.url }));
16 const nav = `<nav aria-label="${esc(name)}"><ul><li><a href="/">Home</a></li>${ownItems.filter((item) => item.href !== '/').map((item) => `<li><a href="${esc(item.href)}">${esc(item.label)}</a></li>`).join('')}</ul></nav>`;
17 const network = catalog.sites.filter((site) => site.id !== service);
18 const footer = `<nav aria-label="OpenVibe">${catalog.groups.map((group) => {
19 const sites = network.filter((site) => site.group === group.id);
20 return sites.length ? `<section><h2>${esc(group.name)}</h2><ul>${sites.map((site) => `<li><a href="${esc(site.url)}">${esc(site.name)}</a> — ${esc(site.description)}</li>`).join('')}</ul></section>` : '';
21 }).join('')}</nav>`;
22 const sameAs = [...new Set(catalog.sites.map((site) => new URL(site.url).origin))];
23 const jsonLd = [seo.jsonLd.organization({ sameAs }), seo.jsonLd.website({ name, url: base + '/', description, searchUrl })];
24 const head = jsonLd.map((item) => `<script type="application/ld+json">${JSON.stringify(item).replace(/</g, '\\u003c')}</script>`).join('\n');
25 const llmsTxt = seo.llmsTxt({ name, summary: description || name, sections: [
26 { title: 'Main pages', links: [{ title: 'Home', url: base + '/' }, ...pages.map((page) => ({ title: page.title || page.name, url: absolute(page.url || page.href, base) }))] },
27 ...catalog.groups.map((group) => ({ title: group.name, links: network.filter((site) => site.group === group.id).map((site) => ({ title: site.name, url: site.url, note: site.description })) })).filter((section) => section.links.length),
28 ] });
29 return { nav, footer, head, jsonLd, llmsTxt };
30}
31
32module.exports = { render, catalog };
33