@uniweb/runtime 1.4.3 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ssr.js CHANGED
@@ -1220,6 +1220,12 @@ function parseIconReference(ref) {
1220
1220
  const library = ref.slice(0, dash);
1221
1221
  return ICON_FAMILY_MAP[library] ? { library, name: ref.slice(dash + 1) } : null;
1222
1222
  }
1223
+ const ICON_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1224
+ function iconValue(value) {
1225
+ if (value.length > 64) return null;
1226
+ const ref = parseIconReference(value);
1227
+ return ref && ICON_FAMILY_MAP[ref.library.toLowerCase()] && ICON_NAME.test(ref.name) ? ref : null;
1228
+ }
1223
1229
  function visit(node, found) {
1224
1230
  if (!node || typeof node !== "object") return;
1225
1231
  if (node.type === "image" && node.attrs?.role === "icon") found.push(node.attrs);
@@ -1229,9 +1235,18 @@ function visit(node, found) {
1229
1235
  }
1230
1236
  if (Array.isArray(node.content)) for (const child of node.content) visit(child, found);
1231
1237
  }
1232
- function pageIcons(blocks) {
1233
- const named = [];
1234
- for (const block of blocks) visit(block.rawContent?.doc ?? block.rawContent, named);
1238
+ function visitValue(value, found, seen = /* @__PURE__ */ new WeakSet()) {
1239
+ if (typeof value === "string") {
1240
+ const ref = iconValue(value);
1241
+ if (ref) found.push(ref);
1242
+ return;
1243
+ }
1244
+ if (!value || typeof value !== "object" || seen.has(value)) return;
1245
+ seen.add(value);
1246
+ if (value.type === "image" && value.attrs?.role === "icon") found.push(value.attrs);
1247
+ for (const child of Array.isArray(value) ? value : Object.values(value)) visitValue(child, found, seen);
1248
+ }
1249
+ function addressed(named) {
1235
1250
  const icons = /* @__PURE__ */ new Map();
1236
1251
  for (const { library, name } of named) {
1237
1252
  const family = ICON_FAMILY_MAP[String(library).toLowerCase()];
@@ -1239,27 +1254,51 @@ function pageIcons(blocks) {
1239
1254
  }
1240
1255
  return icons;
1241
1256
  }
1242
- async function loadPageIcons({ blocks, website, fetch: fetch2, cache }) {
1257
+ function pageIcons(blocks) {
1258
+ const named = [];
1259
+ for (const block of blocks) visit(block.rawContent?.doc ?? block.rawContent, named);
1260
+ for (const block of blocks) {
1261
+ visitValue(block.properties, named);
1262
+ visitValue(getComponentMeta(block.type)?.defaults, named);
1263
+ visitValue(block.heldData, named);
1264
+ }
1265
+ return addressed(named);
1266
+ }
1267
+ function dataIcons(values) {
1268
+ const named = [];
1269
+ visitValue(values, named);
1270
+ return addressed(named);
1271
+ }
1272
+ function iconRequests({ website, fetch: fetch2, cache }) {
1243
1273
  const config = website.config?.icons;
1244
- if (config?.cdn === false) return;
1245
1274
  const base = config?.base || DEFAULT_ICON_BASE;
1246
- const wanted = [...pageIcons(blocks)].filter(([key]) => !cache.has(key)).slice(0, MAX_ICON_REQUESTS);
1247
- if (wanted.length === 0) return;
1248
- const all = Promise.all(wanted.map(async ([key, { family, name }]) => {
1249
- try {
1250
- const response = await fetch2(iconUrl(family, name, base), { method: "GET" });
1251
- if (!response.ok) return;
1252
- const svg = await response.text();
1253
- if (/<svg[\s>]/i.test(svg)) cache.set(key, svg);
1254
- } catch {
1275
+ const asked = /* @__PURE__ */ new Set();
1276
+ return async function ask(icons) {
1277
+ if (config?.cdn === false) return;
1278
+ const wanted = [];
1279
+ for (const [key, address] of icons) {
1280
+ if (asked.size === MAX_ICON_REQUESTS) break;
1281
+ if (cache.has(key) || asked.has(key)) continue;
1282
+ asked.add(key);
1283
+ wanted.push([key, address]);
1255
1284
  }
1256
- }));
1257
- let timer;
1258
- const waited = new Promise((resolve) => {
1259
- timer = setTimeout(resolve, ICON_WAIT_MS);
1260
- });
1261
- await Promise.race([all, waited]);
1262
- clearTimeout(timer);
1285
+ if (wanted.length === 0) return;
1286
+ const all = Promise.all(wanted.map(async ([key, { family, name }]) => {
1287
+ try {
1288
+ const response = await fetch2(iconUrl(family, name, base), { method: "GET" });
1289
+ if (!response.ok) return;
1290
+ const svg = await response.text();
1291
+ if (/<svg[\s>]/i.test(svg)) cache.set(key, svg);
1292
+ } catch {
1293
+ }
1294
+ }));
1295
+ let timer;
1296
+ const waited = new Promise((resolve) => {
1297
+ timer = setTimeout(resolve, ICON_WAIT_MS);
1298
+ });
1299
+ await Promise.race([all, waited]);
1300
+ clearTimeout(timer);
1301
+ };
1263
1302
  }
1264
1303
  function whileReadingIcons(uniweb, read, draw) {
1265
1304
  const cache = uniweb?.iconCache;
@@ -1328,13 +1367,15 @@ async function loadPageData({ website, route, fetch: fetch2, dev = false, locale
1328
1367
  };
1329
1368
  const store = website.entityStore;
1330
1369
  const blocks = blocksOf(page);
1370
+ const askIcons = icons && iconCache ? iconRequests({ website, fetch: fetch2, cache: iconCache }) : null;
1331
1371
  await Promise.all([
1332
1372
  // The page's own record: asked whether or not a section declares the route key, because the
1333
1373
  // page names itself from it and a URL naming no record is a 404.
1334
1374
  store.fetchPageRecord(page, { dispatcher: recording }),
1335
1375
  ...blocks.map((block) => store.fetch(block, getComponentMeta(block.type), { dispatcher: recording })),
1336
- icons && iconCache && loadPageIcons({ blocks, website, fetch: fetch2, cache: iconCache })
1376
+ askIcons?.(pageIcons(blocks))
1337
1377
  ]);
1378
+ if (askIcons) await askIcons(dataIcons([...entries.values()].map((entry) => entry.data)));
1338
1379
  return [...entries.values()];
1339
1380
  }
1340
1381
  function createPageRenderer({ website, shell }) {