@unhead/shared 1.5.5 → 1.6.1

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/index.cjs CHANGED
@@ -180,72 +180,145 @@ function unpackToString(value, options) {
180
180
  }).join(options.entrySeparator || "");
181
181
  }
182
182
 
183
+ const p = (p2) => ({ keyValue: p2, metaKey: "property" });
184
+ const k = (p2) => ({ keyValue: p2 });
183
185
  const MetaPackingSchema = {
184
- robots: {
186
+ appleItunesApp: {
185
187
  unpack: {
186
- keyValueSeparator: ":"
188
+ entrySeparator: ", ",
189
+ resolve({ key, value }) {
190
+ return `${fixKeyCase(key)}=${value}`;
191
+ }
187
192
  }
188
193
  },
189
- // Pragma directives
194
+ articleAuthor: p("article:author"),
195
+ articleExpirationTime: p("article:expiration_time"),
196
+ articleModifiedTime: p("article:modified_time"),
197
+ articlePublishedTime: p("article:published_time"),
198
+ articleSection: p("article:section"),
199
+ articleTag: p("article:tag"),
200
+ bookAuthor: p("book:author"),
201
+ bookIsbn: p("book:isbn"),
202
+ bookReleaseDate: p("book:release_date"),
203
+ bookTag: p("book:tag"),
204
+ charset: {
205
+ metaKey: "charset"
206
+ },
190
207
  contentSecurityPolicy: {
191
208
  unpack: {
192
- keyValueSeparator: " ",
193
- entrySeparator: "; "
209
+ entrySeparator: "; ",
210
+ resolve({ key, value }) {
211
+ return `${fixKeyCase(key)} ${value}`;
212
+ }
194
213
  },
195
214
  metaKey: "http-equiv"
196
215
  },
197
- fbAppId: {
198
- keyValue: "fb:app_id",
199
- metaKey: "property"
200
- },
201
- ogSiteName: {
202
- keyValue: "og:site_name"
203
- },
204
- msapplicationTileImage: {
205
- keyValue: "msapplication-TileImage"
206
- },
207
- /**
208
- * Tile colour for windows
209
- */
210
- msapplicationTileColor: {
211
- keyValue: "msapplication-TileColor"
212
- },
213
- /**
214
- * URL of a config for windows tile.
215
- */
216
- msapplicationConfig: {
217
- keyValue: "msapplication-Config"
218
- },
219
- charset: {
220
- metaKey: "charset"
221
- },
222
216
  contentType: {
223
217
  metaKey: "http-equiv"
224
218
  },
225
219
  defaultStyle: {
226
220
  metaKey: "http-equiv"
227
221
  },
228
- xUaCompatible: {
229
- metaKey: "http-equiv"
230
- },
222
+ fbAppId: p("fb:app_id"),
223
+ msapplicationConfig: k("msapplication-Config"),
224
+ msapplicationTileColor: k("msapplication-TileColor"),
225
+ msapplicationTileImage: k("msapplication-TileImage"),
226
+ ogAudioSecureUrl: p("og:audio:secure_url"),
227
+ ogAudioType: p("og:audio:type"),
228
+ ogAudioUrl: p("og:audio"),
229
+ ogDescription: p("og:description"),
230
+ ogDeterminer: p("og:determiner"),
231
+ ogImage: p("og:image"),
232
+ ogImageAlt: p("og:image:alt"),
233
+ ogImageHeight: p("og:image:height"),
234
+ ogImageSecureUrl: p("og:image:secure_url"),
235
+ ogImageType: p("og:image:type"),
236
+ ogImageUrl: p("og:image"),
237
+ ogImageWidth: p("og:image:width"),
238
+ ogLocale: p("og:locale"),
239
+ ogLocaleAlternate: p("og:locale:alternate"),
240
+ ogSiteName: p("og:site_name"),
241
+ ogTitle: p("og:title"),
242
+ ogType: p("og:type"),
243
+ ogUrl: p("og:url"),
244
+ ogVideo: p("og:video"),
245
+ ogVideoAlt: p("og:video:alt"),
246
+ ogVideoHeight: p("og:video:height"),
247
+ ogVideoSecureUrl: p("og:video:secure_url"),
248
+ ogVideoType: p("og:video:type"),
249
+ ogVideoUrl: p("og:video"),
250
+ ogVideoWidth: p("og:video:width"),
251
+ profileFirstName: p("profile:first_name"),
252
+ profileGender: p("profile:gender"),
253
+ profileLastName: p("profile:last_name"),
254
+ profileUsername: p("profile:username"),
231
255
  refresh: {
256
+ metaKey: "http-equiv",
257
+ unpack: {
258
+ entrySeparator: ";",
259
+ keyValueSeparator: "=",
260
+ resolve({ key, value }) {
261
+ if (key === "seconds")
262
+ return `${value}`;
263
+ }
264
+ }
265
+ },
266
+ robots: {
267
+ unpack: {
268
+ entrySeparator: ", ",
269
+ resolve({ key, value }) {
270
+ if (typeof value === "boolean")
271
+ return `${fixKeyCase(key)}`;
272
+ else
273
+ return `${fixKeyCase(key)}:${value}`;
274
+ }
275
+ }
276
+ },
277
+ twitterAppIdGoogleplay: k("twitter:app:id:googleplay"),
278
+ twitterAppIdIpad: k("twitter:app:id:ipad"),
279
+ twitterAppIdIphone: k("twitter:app:id:iphone"),
280
+ twitterAppNameGoogleplay: k("twitter:app:name:googleplay"),
281
+ twitterAppNameIpad: k("twitter:app:name:ipad"),
282
+ twitterAppNameIphone: k("twitter:app:name:iphone"),
283
+ twitterAppUrlGoogleplay: k("twitter:app:url:googleplay"),
284
+ twitterAppUrlIpad: k("twitter:app:url:ipad"),
285
+ twitterAppUrlIphone: k("twitter:app:url:iphone"),
286
+ twitterCard: k("twitter:card"),
287
+ twitterCreator: k("twitter:creator"),
288
+ twitterCreatorId: k("twitter:creator:id"),
289
+ twitterData1: k("twitter:data1"),
290
+ twitterData2: k("twitter:data2"),
291
+ twitterDescription: k("twitter:description"),
292
+ twitterImage: k("twitter:image"),
293
+ twitterImageAlt: k("twitter:image:alt"),
294
+ /*************************************************/
295
+ // not part of Twitter's card specification anymore
296
+ twitterImageHeight: k("twitter:image:height"),
297
+ twitterImageType: k("twitter:image:type"),
298
+ twitterImageUrl: k("twitter:image"),
299
+ twitterImageWidth: k("twitter:image:width"),
300
+ /**************************************************/
301
+ twitterLabel1: k("twitter:label1"),
302
+ twitterLabel2: k("twitter:label2"),
303
+ twitterPlayer: k("twitter:player"),
304
+ twitterPlayerHeight: k("twitter:player:height"),
305
+ twitterPlayerStream: k("twitter:player:stream"),
306
+ twitterPlayerWidth: k("twitter:player:width"),
307
+ twitterSite: k("twitter:site"),
308
+ twitterSiteId: k("twitter:site:id"),
309
+ twitterTitle: k("twitter:title"),
310
+ xUaCompatible: {
232
311
  metaKey: "http-equiv"
233
312
  }
234
313
  };
235
- const ColonPrefixKeys = /^(og|twitter|fb)/;
236
- const PropertyPrefixKeys = /^(og|fb)/;
237
314
  function resolveMetaKeyType(key) {
238
- return PropertyPrefixKeys.test(key) ? "property" : MetaPackingSchema[key]?.metaKey || "name";
315
+ return MetaPackingSchema[key]?.metaKey || "name";
239
316
  }
240
317
  function resolveMetaKeyValue(key) {
241
318
  return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
242
319
  }
243
320
  function fixKeyCase(key) {
244
- key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
245
- if (ColonPrefixKeys.test(key)) {
246
- key = key.replace("secure-url", "secure_url").replace(/-/g, ":");
247
- }
248
- return key;
321
+ return key.replace(/([A-Z])/g, "-$1").toLowerCase();
249
322
  }
250
323
  function changeKeyCasingDeep(input) {
251
324
  if (Array.isArray(input)) {
@@ -266,7 +339,6 @@ function resolvePackedMetaObjectValue(value, key) {
266
339
  changeKeyCasingDeep(value),
267
340
  {
268
341
  entrySeparator: ", ",
269
- keyValueSeparator: "=",
270
342
  resolve({ value: value2, key: key2 }) {
271
343
  if (value2 === null)
272
344
  return "";
@@ -277,35 +349,47 @@ function resolvePackedMetaObjectValue(value, key) {
277
349
  }
278
350
  );
279
351
  }
280
- const OpenGraphInputs = ["og:Image", "og:Video", "og:Audio", "twitter:Image"];
281
352
  const SimpleArrayUnpackMetas = ["themeColor"];
282
- function unpackMeta(input) {
353
+ function getMeta(key, value) {
354
+ const meta = {};
355
+ const metaKeyType = resolveMetaKeyType(key);
356
+ if (metaKeyType === "charset") {
357
+ meta[metaKeyType] = value;
358
+ } else {
359
+ meta[metaKeyType] = resolveMetaKeyValue(key);
360
+ meta.content = value;
361
+ }
362
+ return meta;
363
+ }
364
+ function flattenMetaObjects(input, prefix = "") {
283
365
  const extras = [];
284
- OpenGraphInputs.forEach((key) => {
285
- const propKey = key.toLowerCase();
286
- const inputKey = `${key.replace(":", "")}`;
287
- const val = input[inputKey];
288
- if (typeof val === "object") {
289
- (Array.isArray(val) ? val : [val]).forEach((entry) => {
290
- if (!entry)
291
- return;
292
- const unpackedEntry = unpackToArray(entry, {
293
- key: key.startsWith("og") ? "property" : "name",
294
- value: "content",
295
- resolveKeyData({ key: key2 }) {
296
- return fixKeyCase(`${propKey}${key2 !== "url" ? `:${key2}` : ""}`);
297
- },
298
- resolveValueData({ value }) {
299
- return typeof value === "number" ? value.toString() : value;
300
- }
301
- });
302
- extras.push(
303
- ...unpackedEntry.sort((a, b) => a.property === propKey ? -1 : b.property === propKey ? 1 : 0)
304
- );
305
- });
306
- delete input[inputKey];
366
+ for (const [key, value] of Object.entries(input)) {
367
+ const fullkey = `${prefix}${prefix === "" ? key : key.charAt(0).toUpperCase() + key.slice(1)}`;
368
+ const unpacker = MetaPackingSchema[key]?.unpack;
369
+ if (unpacker) {
370
+ extras.push(getMeta(fullkey, unpackToString(value, unpacker)));
371
+ delete input[key];
372
+ continue;
307
373
  }
308
- });
374
+ if (typeof value === "object") {
375
+ const children = Array.isArray(value) ? value : [value];
376
+ for (const child of children) {
377
+ if (typeof child === "object")
378
+ extras.push(...flattenMetaObjects(child, fullkey));
379
+ else
380
+ extras.push(getMeta(fullkey, child));
381
+ }
382
+ delete input[key];
383
+ } else {
384
+ extras.push(getMeta(fullkey, value));
385
+ if (typeof input === "object")
386
+ delete input[key];
387
+ }
388
+ }
389
+ return extras;
390
+ }
391
+ function unpackMeta(input) {
392
+ const extras = [];
309
393
  SimpleArrayUnpackMetas.forEach((meta2) => {
310
394
  if (input[meta2] && typeof input[meta2] !== "string") {
311
395
  const val = Array.isArray(input[meta2]) ? input[meta2] : [input[meta2]];
@@ -318,6 +402,19 @@ function unpackMeta(input) {
318
402
  });
319
403
  }
320
404
  });
405
+ extras.push(
406
+ ...flattenMetaObjects(input).sort((a, b) => {
407
+ if (a.property?.startsWith("og:image")) {
408
+ if (b.property?.startsWith("og:image"))
409
+ return 0;
410
+ else
411
+ return -1;
412
+ }
413
+ if (b.property?.startsWith("og:image"))
414
+ return 1;
415
+ return 0;
416
+ })
417
+ );
321
418
  const meta = unpackToArray(input, {
322
419
  key({ key }) {
323
420
  return resolveMetaKeyType(key);
@@ -343,8 +440,8 @@ function packMeta(inputs) {
343
440
  return packArray(inputs, {
344
441
  key: ["name", "property", "httpEquiv", "http-equiv", "charset"],
345
442
  value: ["content", "charset"],
346
- resolveKey(k) {
347
- let key = mappedPackingSchema.filter((sk) => sk[1] === k)?.[0]?.[0] || k;
443
+ resolveKey(k2) {
444
+ let key = mappedPackingSchema.filter((sk) => sk[1] === k2)?.[0]?.[0] || k2;
348
445
  const replacer = (_, letter) => letter?.toUpperCase();
349
446
  key = key.replace(/:([a-z])/g, replacer).replace(/-([a-z])/g, replacer);
350
447
  return key;
@@ -554,27 +651,27 @@ async function normaliseEntryTags(e) {
554
651
 
555
652
  const TAG_WEIGHTS = {
556
653
  // tags
557
- base: -1,
558
- title: 1
654
+ base: -10,
655
+ title: 10
559
656
  };
560
657
  const TAG_ALIASES = {
561
658
  // relative scores to their default values
562
- critical: -8,
563
- high: -1,
564
- low: 2
659
+ critical: -80,
660
+ high: -10,
661
+ low: 20
565
662
  };
566
663
  function tagWeight(tag) {
567
- let weight = 10;
664
+ let weight = 100;
568
665
  const priority = tag.tagPriority;
569
666
  if (typeof priority === "number")
570
667
  return priority;
571
668
  if (tag.tag === "meta") {
572
669
  if (tag.props.charset)
573
- weight = -2;
670
+ weight = -20;
574
671
  if (tag.props["http-equiv"] === "content-security-policy")
575
672
  weight = 0;
576
673
  } else if (tag.tag === "link" && tag.props.rel === "preconnect") {
577
- weight = 2;
674
+ weight = 20;
578
675
  } else if (tag.tag in TAG_WEIGHTS) {
579
676
  weight = TAG_WEIGHTS[tag.tag];
580
677
  }
package/dist/index.d.cts CHANGED
@@ -45,13 +45,13 @@ declare const TagEntityBits = 10;
45
45
  declare function normaliseEntryTags<T extends {} = Head>(e: HeadEntry<T>): Promise<HeadTag[]>;
46
46
 
47
47
  declare const TAG_WEIGHTS: {
48
- readonly base: -1;
49
- readonly title: 1;
48
+ readonly base: -10;
49
+ readonly title: 10;
50
50
  };
51
51
  declare const TAG_ALIASES: {
52
- readonly critical: -8;
53
- readonly high: -1;
54
- readonly low: 2;
52
+ readonly critical: -80;
53
+ readonly high: -10;
54
+ readonly low: 20;
55
55
  };
56
56
  declare function tagWeight<T extends HeadTag>(tag: T): any;
57
57
  declare const SortModifiers: {
package/dist/index.d.mts CHANGED
@@ -45,13 +45,13 @@ declare const TagEntityBits = 10;
45
45
  declare function normaliseEntryTags<T extends {} = Head>(e: HeadEntry<T>): Promise<HeadTag[]>;
46
46
 
47
47
  declare const TAG_WEIGHTS: {
48
- readonly base: -1;
49
- readonly title: 1;
48
+ readonly base: -10;
49
+ readonly title: 10;
50
50
  };
51
51
  declare const TAG_ALIASES: {
52
- readonly critical: -8;
53
- readonly high: -1;
54
- readonly low: 2;
52
+ readonly critical: -80;
53
+ readonly high: -10;
54
+ readonly low: 20;
55
55
  };
56
56
  declare function tagWeight<T extends HeadTag>(tag: T): any;
57
57
  declare const SortModifiers: {
package/dist/index.d.ts CHANGED
@@ -45,13 +45,13 @@ declare const TagEntityBits = 10;
45
45
  declare function normaliseEntryTags<T extends {} = Head>(e: HeadEntry<T>): Promise<HeadTag[]>;
46
46
 
47
47
  declare const TAG_WEIGHTS: {
48
- readonly base: -1;
49
- readonly title: 1;
48
+ readonly base: -10;
49
+ readonly title: 10;
50
50
  };
51
51
  declare const TAG_ALIASES: {
52
- readonly critical: -8;
53
- readonly high: -1;
54
- readonly low: 2;
52
+ readonly critical: -80;
53
+ readonly high: -10;
54
+ readonly low: 20;
55
55
  };
56
56
  declare function tagWeight<T extends HeadTag>(tag: T): any;
57
57
  declare const SortModifiers: {
package/dist/index.mjs CHANGED
@@ -178,72 +178,145 @@ function unpackToString(value, options) {
178
178
  }).join(options.entrySeparator || "");
179
179
  }
180
180
 
181
+ const p = (p2) => ({ keyValue: p2, metaKey: "property" });
182
+ const k = (p2) => ({ keyValue: p2 });
181
183
  const MetaPackingSchema = {
182
- robots: {
184
+ appleItunesApp: {
183
185
  unpack: {
184
- keyValueSeparator: ":"
186
+ entrySeparator: ", ",
187
+ resolve({ key, value }) {
188
+ return `${fixKeyCase(key)}=${value}`;
189
+ }
185
190
  }
186
191
  },
187
- // Pragma directives
192
+ articleAuthor: p("article:author"),
193
+ articleExpirationTime: p("article:expiration_time"),
194
+ articleModifiedTime: p("article:modified_time"),
195
+ articlePublishedTime: p("article:published_time"),
196
+ articleSection: p("article:section"),
197
+ articleTag: p("article:tag"),
198
+ bookAuthor: p("book:author"),
199
+ bookIsbn: p("book:isbn"),
200
+ bookReleaseDate: p("book:release_date"),
201
+ bookTag: p("book:tag"),
202
+ charset: {
203
+ metaKey: "charset"
204
+ },
188
205
  contentSecurityPolicy: {
189
206
  unpack: {
190
- keyValueSeparator: " ",
191
- entrySeparator: "; "
207
+ entrySeparator: "; ",
208
+ resolve({ key, value }) {
209
+ return `${fixKeyCase(key)} ${value}`;
210
+ }
192
211
  },
193
212
  metaKey: "http-equiv"
194
213
  },
195
- fbAppId: {
196
- keyValue: "fb:app_id",
197
- metaKey: "property"
198
- },
199
- ogSiteName: {
200
- keyValue: "og:site_name"
201
- },
202
- msapplicationTileImage: {
203
- keyValue: "msapplication-TileImage"
204
- },
205
- /**
206
- * Tile colour for windows
207
- */
208
- msapplicationTileColor: {
209
- keyValue: "msapplication-TileColor"
210
- },
211
- /**
212
- * URL of a config for windows tile.
213
- */
214
- msapplicationConfig: {
215
- keyValue: "msapplication-Config"
216
- },
217
- charset: {
218
- metaKey: "charset"
219
- },
220
214
  contentType: {
221
215
  metaKey: "http-equiv"
222
216
  },
223
217
  defaultStyle: {
224
218
  metaKey: "http-equiv"
225
219
  },
226
- xUaCompatible: {
227
- metaKey: "http-equiv"
228
- },
220
+ fbAppId: p("fb:app_id"),
221
+ msapplicationConfig: k("msapplication-Config"),
222
+ msapplicationTileColor: k("msapplication-TileColor"),
223
+ msapplicationTileImage: k("msapplication-TileImage"),
224
+ ogAudioSecureUrl: p("og:audio:secure_url"),
225
+ ogAudioType: p("og:audio:type"),
226
+ ogAudioUrl: p("og:audio"),
227
+ ogDescription: p("og:description"),
228
+ ogDeterminer: p("og:determiner"),
229
+ ogImage: p("og:image"),
230
+ ogImageAlt: p("og:image:alt"),
231
+ ogImageHeight: p("og:image:height"),
232
+ ogImageSecureUrl: p("og:image:secure_url"),
233
+ ogImageType: p("og:image:type"),
234
+ ogImageUrl: p("og:image"),
235
+ ogImageWidth: p("og:image:width"),
236
+ ogLocale: p("og:locale"),
237
+ ogLocaleAlternate: p("og:locale:alternate"),
238
+ ogSiteName: p("og:site_name"),
239
+ ogTitle: p("og:title"),
240
+ ogType: p("og:type"),
241
+ ogUrl: p("og:url"),
242
+ ogVideo: p("og:video"),
243
+ ogVideoAlt: p("og:video:alt"),
244
+ ogVideoHeight: p("og:video:height"),
245
+ ogVideoSecureUrl: p("og:video:secure_url"),
246
+ ogVideoType: p("og:video:type"),
247
+ ogVideoUrl: p("og:video"),
248
+ ogVideoWidth: p("og:video:width"),
249
+ profileFirstName: p("profile:first_name"),
250
+ profileGender: p("profile:gender"),
251
+ profileLastName: p("profile:last_name"),
252
+ profileUsername: p("profile:username"),
229
253
  refresh: {
254
+ metaKey: "http-equiv",
255
+ unpack: {
256
+ entrySeparator: ";",
257
+ keyValueSeparator: "=",
258
+ resolve({ key, value }) {
259
+ if (key === "seconds")
260
+ return `${value}`;
261
+ }
262
+ }
263
+ },
264
+ robots: {
265
+ unpack: {
266
+ entrySeparator: ", ",
267
+ resolve({ key, value }) {
268
+ if (typeof value === "boolean")
269
+ return `${fixKeyCase(key)}`;
270
+ else
271
+ return `${fixKeyCase(key)}:${value}`;
272
+ }
273
+ }
274
+ },
275
+ twitterAppIdGoogleplay: k("twitter:app:id:googleplay"),
276
+ twitterAppIdIpad: k("twitter:app:id:ipad"),
277
+ twitterAppIdIphone: k("twitter:app:id:iphone"),
278
+ twitterAppNameGoogleplay: k("twitter:app:name:googleplay"),
279
+ twitterAppNameIpad: k("twitter:app:name:ipad"),
280
+ twitterAppNameIphone: k("twitter:app:name:iphone"),
281
+ twitterAppUrlGoogleplay: k("twitter:app:url:googleplay"),
282
+ twitterAppUrlIpad: k("twitter:app:url:ipad"),
283
+ twitterAppUrlIphone: k("twitter:app:url:iphone"),
284
+ twitterCard: k("twitter:card"),
285
+ twitterCreator: k("twitter:creator"),
286
+ twitterCreatorId: k("twitter:creator:id"),
287
+ twitterData1: k("twitter:data1"),
288
+ twitterData2: k("twitter:data2"),
289
+ twitterDescription: k("twitter:description"),
290
+ twitterImage: k("twitter:image"),
291
+ twitterImageAlt: k("twitter:image:alt"),
292
+ /*************************************************/
293
+ // not part of Twitter's card specification anymore
294
+ twitterImageHeight: k("twitter:image:height"),
295
+ twitterImageType: k("twitter:image:type"),
296
+ twitterImageUrl: k("twitter:image"),
297
+ twitterImageWidth: k("twitter:image:width"),
298
+ /**************************************************/
299
+ twitterLabel1: k("twitter:label1"),
300
+ twitterLabel2: k("twitter:label2"),
301
+ twitterPlayer: k("twitter:player"),
302
+ twitterPlayerHeight: k("twitter:player:height"),
303
+ twitterPlayerStream: k("twitter:player:stream"),
304
+ twitterPlayerWidth: k("twitter:player:width"),
305
+ twitterSite: k("twitter:site"),
306
+ twitterSiteId: k("twitter:site:id"),
307
+ twitterTitle: k("twitter:title"),
308
+ xUaCompatible: {
230
309
  metaKey: "http-equiv"
231
310
  }
232
311
  };
233
- const ColonPrefixKeys = /^(og|twitter|fb)/;
234
- const PropertyPrefixKeys = /^(og|fb)/;
235
312
  function resolveMetaKeyType(key) {
236
- return PropertyPrefixKeys.test(key) ? "property" : MetaPackingSchema[key]?.metaKey || "name";
313
+ return MetaPackingSchema[key]?.metaKey || "name";
237
314
  }
238
315
  function resolveMetaKeyValue(key) {
239
316
  return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
240
317
  }
241
318
  function fixKeyCase(key) {
242
- key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
243
- if (ColonPrefixKeys.test(key)) {
244
- key = key.replace("secure-url", "secure_url").replace(/-/g, ":");
245
- }
246
- return key;
319
+ return key.replace(/([A-Z])/g, "-$1").toLowerCase();
247
320
  }
248
321
  function changeKeyCasingDeep(input) {
249
322
  if (Array.isArray(input)) {
@@ -264,7 +337,6 @@ function resolvePackedMetaObjectValue(value, key) {
264
337
  changeKeyCasingDeep(value),
265
338
  {
266
339
  entrySeparator: ", ",
267
- keyValueSeparator: "=",
268
340
  resolve({ value: value2, key: key2 }) {
269
341
  if (value2 === null)
270
342
  return "";
@@ -275,35 +347,47 @@ function resolvePackedMetaObjectValue(value, key) {
275
347
  }
276
348
  );
277
349
  }
278
- const OpenGraphInputs = ["og:Image", "og:Video", "og:Audio", "twitter:Image"];
279
350
  const SimpleArrayUnpackMetas = ["themeColor"];
280
- function unpackMeta(input) {
351
+ function getMeta(key, value) {
352
+ const meta = {};
353
+ const metaKeyType = resolveMetaKeyType(key);
354
+ if (metaKeyType === "charset") {
355
+ meta[metaKeyType] = value;
356
+ } else {
357
+ meta[metaKeyType] = resolveMetaKeyValue(key);
358
+ meta.content = value;
359
+ }
360
+ return meta;
361
+ }
362
+ function flattenMetaObjects(input, prefix = "") {
281
363
  const extras = [];
282
- OpenGraphInputs.forEach((key) => {
283
- const propKey = key.toLowerCase();
284
- const inputKey = `${key.replace(":", "")}`;
285
- const val = input[inputKey];
286
- if (typeof val === "object") {
287
- (Array.isArray(val) ? val : [val]).forEach((entry) => {
288
- if (!entry)
289
- return;
290
- const unpackedEntry = unpackToArray(entry, {
291
- key: key.startsWith("og") ? "property" : "name",
292
- value: "content",
293
- resolveKeyData({ key: key2 }) {
294
- return fixKeyCase(`${propKey}${key2 !== "url" ? `:${key2}` : ""}`);
295
- },
296
- resolveValueData({ value }) {
297
- return typeof value === "number" ? value.toString() : value;
298
- }
299
- });
300
- extras.push(
301
- ...unpackedEntry.sort((a, b) => a.property === propKey ? -1 : b.property === propKey ? 1 : 0)
302
- );
303
- });
304
- delete input[inputKey];
364
+ for (const [key, value] of Object.entries(input)) {
365
+ const fullkey = `${prefix}${prefix === "" ? key : key.charAt(0).toUpperCase() + key.slice(1)}`;
366
+ const unpacker = MetaPackingSchema[key]?.unpack;
367
+ if (unpacker) {
368
+ extras.push(getMeta(fullkey, unpackToString(value, unpacker)));
369
+ delete input[key];
370
+ continue;
305
371
  }
306
- });
372
+ if (typeof value === "object") {
373
+ const children = Array.isArray(value) ? value : [value];
374
+ for (const child of children) {
375
+ if (typeof child === "object")
376
+ extras.push(...flattenMetaObjects(child, fullkey));
377
+ else
378
+ extras.push(getMeta(fullkey, child));
379
+ }
380
+ delete input[key];
381
+ } else {
382
+ extras.push(getMeta(fullkey, value));
383
+ if (typeof input === "object")
384
+ delete input[key];
385
+ }
386
+ }
387
+ return extras;
388
+ }
389
+ function unpackMeta(input) {
390
+ const extras = [];
307
391
  SimpleArrayUnpackMetas.forEach((meta2) => {
308
392
  if (input[meta2] && typeof input[meta2] !== "string") {
309
393
  const val = Array.isArray(input[meta2]) ? input[meta2] : [input[meta2]];
@@ -316,6 +400,19 @@ function unpackMeta(input) {
316
400
  });
317
401
  }
318
402
  });
403
+ extras.push(
404
+ ...flattenMetaObjects(input).sort((a, b) => {
405
+ if (a.property?.startsWith("og:image")) {
406
+ if (b.property?.startsWith("og:image"))
407
+ return 0;
408
+ else
409
+ return -1;
410
+ }
411
+ if (b.property?.startsWith("og:image"))
412
+ return 1;
413
+ return 0;
414
+ })
415
+ );
319
416
  const meta = unpackToArray(input, {
320
417
  key({ key }) {
321
418
  return resolveMetaKeyType(key);
@@ -341,8 +438,8 @@ function packMeta(inputs) {
341
438
  return packArray(inputs, {
342
439
  key: ["name", "property", "httpEquiv", "http-equiv", "charset"],
343
440
  value: ["content", "charset"],
344
- resolveKey(k) {
345
- let key = mappedPackingSchema.filter((sk) => sk[1] === k)?.[0]?.[0] || k;
441
+ resolveKey(k2) {
442
+ let key = mappedPackingSchema.filter((sk) => sk[1] === k2)?.[0]?.[0] || k2;
346
443
  const replacer = (_, letter) => letter?.toUpperCase();
347
444
  key = key.replace(/:([a-z])/g, replacer).replace(/-([a-z])/g, replacer);
348
445
  return key;
@@ -552,27 +649,27 @@ async function normaliseEntryTags(e) {
552
649
 
553
650
  const TAG_WEIGHTS = {
554
651
  // tags
555
- base: -1,
556
- title: 1
652
+ base: -10,
653
+ title: 10
557
654
  };
558
655
  const TAG_ALIASES = {
559
656
  // relative scores to their default values
560
- critical: -8,
561
- high: -1,
562
- low: 2
657
+ critical: -80,
658
+ high: -10,
659
+ low: 20
563
660
  };
564
661
  function tagWeight(tag) {
565
- let weight = 10;
662
+ let weight = 100;
566
663
  const priority = tag.tagPriority;
567
664
  if (typeof priority === "number")
568
665
  return priority;
569
666
  if (tag.tag === "meta") {
570
667
  if (tag.props.charset)
571
- weight = -2;
668
+ weight = -20;
572
669
  if (tag.props["http-equiv"] === "content-security-policy")
573
670
  weight = 0;
574
671
  } else if (tag.tag === "link" && tag.props.rel === "preconnect") {
575
- weight = 2;
672
+ weight = 20;
576
673
  } else if (tag.tag in TAG_WEIGHTS) {
577
674
  weight = TAG_WEIGHTS[tag.tag];
578
675
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unhead/shared",
3
3
  "type": "module",
4
- "version": "1.5.5",
4
+ "version": "1.6.1",
5
5
  "author": "Harlan Wilton <harlan@harlanzw.com>",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -34,7 +34,7 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@unhead/schema": "1.5.5"
37
+ "@unhead/schema": "1.6.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "packrup": "^0.1.0"