@unhead/shared 1.5.5 → 1.6.0
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 +175 -78
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +175 -78
- package/package.json +2 -2
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
|
-
|
|
186
|
+
appleItunesApp: {
|
|
185
187
|
unpack: {
|
|
186
|
-
|
|
188
|
+
entrySeparator: ", ",
|
|
189
|
+
resolve({ key, value }) {
|
|
190
|
+
return `${fixKeyCase(key)}=${value}`;
|
|
191
|
+
}
|
|
187
192
|
}
|
|
188
193
|
},
|
|
189
|
-
|
|
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
|
-
|
|
193
|
-
|
|
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
|
-
|
|
229
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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(
|
|
347
|
-
let key = mappedPackingSchema.filter((sk) => sk[1] ===
|
|
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: -
|
|
558
|
-
title:
|
|
654
|
+
base: -10,
|
|
655
|
+
title: 10
|
|
559
656
|
};
|
|
560
657
|
const TAG_ALIASES = {
|
|
561
658
|
// relative scores to their default values
|
|
562
|
-
critical: -
|
|
563
|
-
high: -
|
|
564
|
-
low:
|
|
659
|
+
critical: -80,
|
|
660
|
+
high: -10,
|
|
661
|
+
low: 20
|
|
565
662
|
};
|
|
566
663
|
function tagWeight(tag) {
|
|
567
|
-
let weight =
|
|
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 = -
|
|
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 =
|
|
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: -
|
|
49
|
-
readonly title:
|
|
48
|
+
readonly base: -10;
|
|
49
|
+
readonly title: 10;
|
|
50
50
|
};
|
|
51
51
|
declare const TAG_ALIASES: {
|
|
52
|
-
readonly critical: -
|
|
53
|
-
readonly high: -
|
|
54
|
-
readonly low:
|
|
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: -
|
|
49
|
-
readonly title:
|
|
48
|
+
readonly base: -10;
|
|
49
|
+
readonly title: 10;
|
|
50
50
|
};
|
|
51
51
|
declare const TAG_ALIASES: {
|
|
52
|
-
readonly critical: -
|
|
53
|
-
readonly high: -
|
|
54
|
-
readonly low:
|
|
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: -
|
|
49
|
-
readonly title:
|
|
48
|
+
readonly base: -10;
|
|
49
|
+
readonly title: 10;
|
|
50
50
|
};
|
|
51
51
|
declare const TAG_ALIASES: {
|
|
52
|
-
readonly critical: -
|
|
53
|
-
readonly high: -
|
|
54
|
-
readonly low:
|
|
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
|
-
|
|
184
|
+
appleItunesApp: {
|
|
183
185
|
unpack: {
|
|
184
|
-
|
|
186
|
+
entrySeparator: ", ",
|
|
187
|
+
resolve({ key, value }) {
|
|
188
|
+
return `${fixKeyCase(key)}=${value}`;
|
|
189
|
+
}
|
|
185
190
|
}
|
|
186
191
|
},
|
|
187
|
-
|
|
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
|
-
|
|
191
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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(
|
|
345
|
-
let key = mappedPackingSchema.filter((sk) => sk[1] ===
|
|
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: -
|
|
556
|
-
title:
|
|
652
|
+
base: -10,
|
|
653
|
+
title: 10
|
|
557
654
|
};
|
|
558
655
|
const TAG_ALIASES = {
|
|
559
656
|
// relative scores to their default values
|
|
560
|
-
critical: -
|
|
561
|
-
high: -
|
|
562
|
-
low:
|
|
657
|
+
critical: -80,
|
|
658
|
+
high: -10,
|
|
659
|
+
low: 20
|
|
563
660
|
};
|
|
564
661
|
function tagWeight(tag) {
|
|
565
|
-
let weight =
|
|
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 = -
|
|
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 =
|
|
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.
|
|
4
|
+
"version": "1.6.0",
|
|
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.
|
|
37
|
+
"@unhead/schema": "1.6.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"packrup": "^0.1.0"
|