inital commit

This commit is contained in:
2026-01-01 15:25:19 +05:30
commit f0ae49465a
36361 changed files with 4894111 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
const TYPE_URL = '__METADATA_URL';
function replacer(_key, val) {
// clone URL as string but recover it as URL
if (val instanceof URL) {
return {
_type: TYPE_URL,
value: val.href
};
}
return val;
}
function reviver(_key, val) {
if (typeof val === 'object' && val !== null && val._type === TYPE_URL) {
return new URL(val.value);
}
return val;
}
export function cloneMetadata(metadata) {
const jsonString = JSON.stringify(metadata, replacer);
return JSON.parse(jsonString, reviver);
}
//# sourceMappingURL=clone-metadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/metadata/clone-metadata.ts"],"sourcesContent":["import type { ResolvedMetadata } from './types/metadata-interface'\n\nconst TYPE_URL = '__METADATA_URL'\n\nfunction replacer(_key: string, val: any) {\n // clone URL as string but recover it as URL\n if (val instanceof URL) {\n return { _type: TYPE_URL, value: val.href }\n }\n return val\n}\n\nfunction reviver(_key: string, val: any) {\n if (typeof val === 'object' && val !== null && val._type === TYPE_URL) {\n return new URL(val.value)\n }\n return val\n}\n\nexport function cloneMetadata(metadata: ResolvedMetadata): ResolvedMetadata {\n const jsonString = JSON.stringify(metadata, replacer)\n return JSON.parse(jsonString, reviver)\n}\n"],"names":["TYPE_URL","replacer","_key","val","URL","_type","value","href","reviver","cloneMetadata","metadata","jsonString","JSON","stringify","parse"],"mappings":"AAEA,MAAMA,WAAW;AAEjB,SAASC,SAASC,IAAY,EAAEC,GAAQ;IACtC,4CAA4C;IAC5C,IAAIA,eAAeC,KAAK;QACtB,OAAO;YAAEC,OAAOL;YAAUM,OAAOH,IAAII,IAAI;QAAC;IAC5C;IACA,OAAOJ;AACT;AAEA,SAASK,QAAQN,IAAY,EAAEC,GAAQ;IACrC,IAAI,OAAOA,QAAQ,YAAYA,QAAQ,QAAQA,IAAIE,KAAK,KAAKL,UAAU;QACrE,OAAO,IAAII,IAAID,IAAIG,KAAK;IAC1B;IACA,OAAOH;AACT;AAEA,OAAO,SAASM,cAAcC,QAA0B;IACtD,MAAMC,aAAaC,KAAKC,SAAS,CAACH,UAAUT;IAC5C,OAAOW,KAAKE,KAAK,CAACH,YAAYH;AAChC"}

18
node_modules/next/dist/esm/lib/metadata/constants.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
export const ViewportMetaKeys = {
width: 'width',
height: 'height',
initialScale: 'initial-scale',
minimumScale: 'minimum-scale',
maximumScale: 'maximum-scale',
viewportFit: 'viewport-fit',
userScalable: 'user-scalable',
interactiveWidget: 'interactive-widget'
};
export const IconKeys = [
'icon',
'shortcut',
'apple',
'other'
];
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/metadata/constants.ts"],"sourcesContent":["import type { ViewportLayout } from './types/extra-types'\nimport type { Icons } from './types/metadata-types'\n\nexport const ViewportMetaKeys: { [k in keyof ViewportLayout]: string } = {\n width: 'width',\n height: 'height',\n initialScale: 'initial-scale',\n minimumScale: 'minimum-scale',\n maximumScale: 'maximum-scale',\n viewportFit: 'viewport-fit',\n userScalable: 'user-scalable',\n interactiveWidget: 'interactive-widget',\n} as const\n\nexport const IconKeys: (keyof Icons)[] = ['icon', 'shortcut', 'apple', 'other']\n"],"names":["ViewportMetaKeys","width","height","initialScale","minimumScale","maximumScale","viewportFit","userScalable","interactiveWidget","IconKeys"],"mappings":"AAGA,OAAO,MAAMA,mBAA4D;IACvEC,OAAO;IACPC,QAAQ;IACRC,cAAc;IACdC,cAAc;IACdC,cAAc;IACdC,aAAa;IACbC,cAAc;IACdC,mBAAmB;AACrB,EAAU;AAEV,OAAO,MAAMC,WAA4B;IAAC;IAAQ;IAAY;IAAS;CAAQ,CAAA"}

View File

@@ -0,0 +1,59 @@
export function createDefaultViewport() {
return {
// name=viewport
width: 'device-width',
initialScale: 1,
// visual metadata
themeColor: null,
colorScheme: null
};
}
export function createDefaultMetadata() {
return {
// Deprecated ones
viewport: null,
themeColor: null,
colorScheme: null,
metadataBase: null,
// Other values are all null
title: null,
description: null,
applicationName: null,
authors: null,
generator: null,
keywords: null,
referrer: null,
creator: null,
publisher: null,
robots: null,
manifest: null,
alternates: {
canonical: null,
languages: null,
media: null,
types: null
},
icons: null,
openGraph: null,
twitter: null,
verification: {},
appleWebApp: null,
formatDetection: null,
itunes: null,
facebook: null,
abstract: null,
appLinks: null,
archives: null,
assets: null,
bookmarks: null,
category: null,
classification: null,
pagination: {
previous: null,
next: null
},
other: {}
};
}
//# sourceMappingURL=default-metadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/metadata/default-metadata.tsx"],"sourcesContent":["import type {\n ResolvedMetadata,\n ResolvedViewport,\n} from './types/metadata-interface'\n\nexport function createDefaultViewport(): ResolvedViewport {\n return {\n // name=viewport\n width: 'device-width',\n initialScale: 1,\n // visual metadata\n themeColor: null,\n colorScheme: null,\n }\n}\n\nexport function createDefaultMetadata(): ResolvedMetadata {\n return {\n // Deprecated ones\n viewport: null,\n themeColor: null,\n colorScheme: null,\n\n metadataBase: null,\n // Other values are all null\n title: null,\n description: null,\n applicationName: null,\n authors: null,\n generator: null,\n keywords: null,\n referrer: null,\n creator: null,\n publisher: null,\n robots: null,\n manifest: null,\n alternates: {\n canonical: null,\n languages: null,\n media: null,\n types: null,\n },\n icons: null,\n openGraph: null,\n twitter: null,\n verification: {},\n appleWebApp: null,\n formatDetection: null,\n itunes: null,\n facebook: null,\n abstract: null,\n appLinks: null,\n archives: null,\n assets: null,\n bookmarks: null,\n category: null,\n classification: null,\n pagination: {\n previous: null,\n next: null,\n },\n other: {},\n }\n}\n"],"names":["createDefaultViewport","width","initialScale","themeColor","colorScheme","createDefaultMetadata","viewport","metadataBase","title","description","applicationName","authors","generator","keywords","referrer","creator","publisher","robots","manifest","alternates","canonical","languages","media","types","icons","openGraph","twitter","verification","appleWebApp","formatDetection","itunes","facebook","abstract","appLinks","archives","assets","bookmarks","category","classification","pagination","previous","next","other"],"mappings":"AAKA,OAAO,SAASA;IACd,OAAO;QACL,gBAAgB;QAChBC,OAAO;QACPC,cAAc;QACd,kBAAkB;QAClBC,YAAY;QACZC,aAAa;IACf;AACF;AAEA,OAAO,SAASC;IACd,OAAO;QACL,kBAAkB;QAClBC,UAAU;QACVH,YAAY;QACZC,aAAa;QAEbG,cAAc;QACd,4BAA4B;QAC5BC,OAAO;QACPC,aAAa;QACbC,iBAAiB;QACjBC,SAAS;QACTC,WAAW;QACXC,UAAU;QACVC,UAAU;QACVC,SAAS;QACTC,WAAW;QACXC,QAAQ;QACRC,UAAU;QACVC,YAAY;YACVC,WAAW;YACXC,WAAW;YACXC,OAAO;YACPC,OAAO;QACT;QACAC,OAAO;QACPC,WAAW;QACXC,SAAS;QACTC,cAAc,CAAC;QACfC,aAAa;QACbC,iBAAiB;QACjBC,QAAQ;QACRC,UAAU;QACVC,UAAU;QACVC,UAAU;QACVC,UAAU;QACVC,QAAQ;QACRC,WAAW;QACXC,UAAU;QACVC,gBAAgB;QAChBC,YAAY;YACVC,UAAU;YACVC,MAAM;QACR;QACAC,OAAO,CAAC;IACV;AACF"}

View File

@@ -0,0 +1,40 @@
import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
import { MetaFilter } from './meta';
function AlternateLink({ descriptor, ...props }) {
if (!descriptor.url) return null;
return /*#__PURE__*/ _jsx("link", {
...props,
...descriptor.title && {
title: descriptor.title
},
href: descriptor.url.toString()
});
}
export function AlternatesMetadata({ alternates }) {
if (!alternates) return null;
const { canonical, languages, media, types } = alternates;
return MetaFilter([
canonical ? AlternateLink({
rel: 'canonical',
descriptor: canonical
}) : null,
languages ? Object.entries(languages).flatMap(([locale, descriptors])=>descriptors == null ? void 0 : descriptors.map((descriptor)=>AlternateLink({
rel: 'alternate',
hrefLang: locale,
descriptor
}))) : null,
media ? Object.entries(media).flatMap(([mediaName, descriptors])=>descriptors == null ? void 0 : descriptors.map((descriptor)=>AlternateLink({
rel: 'alternate',
media: mediaName,
descriptor
}))) : null,
types ? Object.entries(types).flatMap(([type, descriptors])=>descriptors == null ? void 0 : descriptors.map((descriptor)=>AlternateLink({
rel: 'alternate',
type,
descriptor
}))) : null
]);
}
//# sourceMappingURL=alternate.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/generate/alternate.tsx"],"sourcesContent":["import type { ResolvedMetadata } from '../types/metadata-interface'\nimport type { AlternateLinkDescriptor } from '../types/alternative-urls-types'\n\nimport React from 'react'\nimport { MetaFilter } from './meta'\n\nfunction AlternateLink({\n descriptor,\n ...props\n}: {\n descriptor: AlternateLinkDescriptor\n} & React.LinkHTMLAttributes<HTMLLinkElement>) {\n if (!descriptor.url) return null\n return (\n <link\n {...props}\n {...(descriptor.title && { title: descriptor.title })}\n href={descriptor.url.toString()}\n />\n )\n}\n\nexport function AlternatesMetadata({\n alternates,\n}: {\n alternates: ResolvedMetadata['alternates']\n}) {\n if (!alternates) return null\n\n const { canonical, languages, media, types } = alternates\n\n return MetaFilter([\n canonical\n ? AlternateLink({ rel: 'canonical', descriptor: canonical })\n : null,\n languages\n ? Object.entries(languages).flatMap(([locale, descriptors]) =>\n descriptors?.map((descriptor) =>\n AlternateLink({ rel: 'alternate', hrefLang: locale, descriptor })\n )\n )\n : null,\n media\n ? Object.entries(media).flatMap(([mediaName, descriptors]) =>\n descriptors?.map((descriptor) =>\n AlternateLink({ rel: 'alternate', media: mediaName, descriptor })\n )\n )\n : null,\n types\n ? Object.entries(types).flatMap(([type, descriptors]) =>\n descriptors?.map((descriptor) =>\n AlternateLink({ rel: 'alternate', type, descriptor })\n )\n )\n : null,\n ])\n}\n"],"names":["React","MetaFilter","AlternateLink","descriptor","props","url","link","title","href","toString","AlternatesMetadata","alternates","canonical","languages","media","types","rel","Object","entries","flatMap","locale","descriptors","map","hrefLang","mediaName","type"],"mappings":";AAGA,OAAOA,WAAW,QAAO;AACzB,SAASC,UAAU,QAAQ,SAAQ;AAEnC,SAASC,cAAc,EACrBC,UAAU,EACV,GAAGC,OAGwC;IAC3C,IAAI,CAACD,WAAWE,GAAG,EAAE,OAAO;IAC5B,qBACE,KAACC;QACE,GAAGF,KAAK;QACR,GAAID,WAAWI,KAAK,IAAI;YAAEA,OAAOJ,WAAWI,KAAK;QAAC,CAAC;QACpDC,MAAML,WAAWE,GAAG,CAACI,QAAQ;;AAGnC;AAEA,OAAO,SAASC,mBAAmB,EACjCC,UAAU,EAGX;IACC,IAAI,CAACA,YAAY,OAAO;IAExB,MAAM,EAAEC,SAAS,EAAEC,SAAS,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGJ;IAE/C,OAAOV,WAAW;QAChBW,YACIV,cAAc;YAAEc,KAAK;YAAab,YAAYS;QAAU,KACxD;QACJC,YACII,OAAOC,OAAO,CAACL,WAAWM,OAAO,CAAC,CAAC,CAACC,QAAQC,YAAY,GACtDA,+BAAAA,YAAaC,GAAG,CAAC,CAACnB,aAChBD,cAAc;oBAAEc,KAAK;oBAAaO,UAAUH;oBAAQjB;gBAAW,OAGnE;QACJW,QACIG,OAAOC,OAAO,CAACJ,OAAOK,OAAO,CAAC,CAAC,CAACK,WAAWH,YAAY,GACrDA,+BAAAA,YAAaC,GAAG,CAAC,CAACnB,aAChBD,cAAc;oBAAEc,KAAK;oBAAaF,OAAOU;oBAAWrB;gBAAW,OAGnE;QACJY,QACIE,OAAOC,OAAO,CAACH,OAAOI,OAAO,CAAC,CAAC,CAACM,MAAMJ,YAAY,GAChDA,+BAAAA,YAAaC,GAAG,CAAC,CAACnB,aAChBD,cAAc;oBAAEc,KAAK;oBAAaS;oBAAMtB;gBAAW,OAGvD;KACL;AACH"}

View File

@@ -0,0 +1,253 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { Meta, MetaFilter, MultiMeta } from './meta';
import { ViewportMetaKeys } from '../constants';
import { getOrigin } from './utils';
// convert viewport object to string for viewport meta tag
function resolveViewportLayout(viewport) {
let resolved = null;
if (viewport && typeof viewport === 'object') {
resolved = '';
for(const viewportKey_ in ViewportMetaKeys){
const viewportKey = viewportKey_;
if (viewportKey in viewport) {
let value = viewport[viewportKey];
if (typeof value === 'boolean') {
value = value ? 'yes' : 'no';
} else if (!value && viewportKey === 'initialScale') {
value = undefined;
}
if (value) {
if (resolved) resolved += ', ';
resolved += `${ViewportMetaKeys[viewportKey]}=${value}`;
}
}
}
}
return resolved;
}
export function ViewportMeta({ viewport }) {
return MetaFilter([
/*#__PURE__*/ _jsx("meta", {
charSet: "utf-8"
}),
Meta({
name: 'viewport',
content: resolveViewportLayout(viewport)
}),
...viewport.themeColor ? viewport.themeColor.map((themeColor)=>Meta({
name: 'theme-color',
content: themeColor.color,
media: themeColor.media
})) : [],
Meta({
name: 'color-scheme',
content: viewport.colorScheme
})
]);
}
export function BasicMeta({ metadata }) {
var _metadata_keywords, _metadata_robots, _metadata_robots1;
const manifestOrigin = metadata.manifest ? getOrigin(metadata.manifest) : undefined;
return MetaFilter([
metadata.title !== null && metadata.title.absolute ? /*#__PURE__*/ _jsx("title", {
children: metadata.title.absolute
}) : null,
Meta({
name: 'description',
content: metadata.description
}),
Meta({
name: 'application-name',
content: metadata.applicationName
}),
...metadata.authors ? metadata.authors.map((author)=>[
author.url ? /*#__PURE__*/ _jsx("link", {
rel: "author",
href: author.url.toString()
}) : null,
Meta({
name: 'author',
content: author.name
})
]) : [],
metadata.manifest ? /*#__PURE__*/ _jsx("link", {
rel: "manifest",
href: metadata.manifest.toString(),
// If it's same origin, and it's a preview deployment,
// including credentials for manifest request.
crossOrigin: !manifestOrigin && process.env.VERCEL_ENV === 'preview' ? 'use-credentials' : undefined
}) : null,
Meta({
name: 'generator',
content: metadata.generator
}),
Meta({
name: 'keywords',
content: (_metadata_keywords = metadata.keywords) == null ? void 0 : _metadata_keywords.join(',')
}),
Meta({
name: 'referrer',
content: metadata.referrer
}),
Meta({
name: 'creator',
content: metadata.creator
}),
Meta({
name: 'publisher',
content: metadata.publisher
}),
Meta({
name: 'robots',
content: (_metadata_robots = metadata.robots) == null ? void 0 : _metadata_robots.basic
}),
Meta({
name: 'googlebot',
content: (_metadata_robots1 = metadata.robots) == null ? void 0 : _metadata_robots1.googleBot
}),
Meta({
name: 'abstract',
content: metadata.abstract
}),
...metadata.archives ? metadata.archives.map((archive)=>/*#__PURE__*/ _jsx("link", {
rel: "archives",
href: archive
})) : [],
...metadata.assets ? metadata.assets.map((asset)=>/*#__PURE__*/ _jsx("link", {
rel: "assets",
href: asset
})) : [],
...metadata.bookmarks ? metadata.bookmarks.map((bookmark)=>/*#__PURE__*/ _jsx("link", {
rel: "bookmarks",
href: bookmark
})) : [],
...metadata.pagination ? [
metadata.pagination.previous ? /*#__PURE__*/ _jsx("link", {
rel: "prev",
href: metadata.pagination.previous
}) : null,
metadata.pagination.next ? /*#__PURE__*/ _jsx("link", {
rel: "next",
href: metadata.pagination.next
}) : null
] : [],
Meta({
name: 'category',
content: metadata.category
}),
Meta({
name: 'classification',
content: metadata.classification
}),
...metadata.other ? Object.entries(metadata.other).map(([name, content])=>{
if (Array.isArray(content)) {
return content.map((contentItem)=>Meta({
name,
content: contentItem
}));
} else {
return Meta({
name,
content
});
}
}) : []
]);
}
export function ItunesMeta({ itunes }) {
if (!itunes) return null;
const { appId, appArgument } = itunes;
let content = `app-id=${appId}`;
if (appArgument) {
content += `, app-argument=${appArgument}`;
}
return /*#__PURE__*/ _jsx("meta", {
name: "apple-itunes-app",
content: content
});
}
export function FacebookMeta({ facebook }) {
if (!facebook) return null;
const { appId, admins } = facebook;
return MetaFilter([
appId ? /*#__PURE__*/ _jsx("meta", {
property: "fb:app_id",
content: appId
}) : null,
...admins ? admins.map((admin)=>/*#__PURE__*/ _jsx("meta", {
property: "fb:admins",
content: admin
})) : []
]);
}
const formatDetectionKeys = [
'telephone',
'date',
'address',
'email',
'url'
];
export function FormatDetectionMeta({ formatDetection }) {
if (!formatDetection) return null;
let content = '';
for (const key of formatDetectionKeys){
if (key in formatDetection) {
if (content) content += ', ';
content += `${key}=no`;
}
}
return /*#__PURE__*/ _jsx("meta", {
name: "format-detection",
content: content
});
}
export function AppleWebAppMeta({ appleWebApp }) {
if (!appleWebApp) return null;
const { capable, title, startupImage, statusBarStyle } = appleWebApp;
return MetaFilter([
capable ? Meta({
name: 'mobile-web-app-capable',
content: 'yes'
}) : null,
Meta({
name: 'apple-mobile-web-app-title',
content: title
}),
startupImage ? startupImage.map((image)=>/*#__PURE__*/ _jsx("link", {
href: image.url,
media: image.media,
rel: "apple-touch-startup-image"
})) : null,
statusBarStyle ? Meta({
name: 'apple-mobile-web-app-status-bar-style',
content: statusBarStyle
}) : null
]);
}
export function VerificationMeta({ verification }) {
if (!verification) return null;
return MetaFilter([
MultiMeta({
namePrefix: 'google-site-verification',
contents: verification.google
}),
MultiMeta({
namePrefix: 'y_key',
contents: verification.yahoo
}),
MultiMeta({
namePrefix: 'yandex-verification',
contents: verification.yandex
}),
MultiMeta({
namePrefix: 'me',
contents: verification.me
}),
...verification.other ? Object.entries(verification.other).map(([key, value])=>MultiMeta({
namePrefix: key,
contents: value
})) : []
]);
}
//# sourceMappingURL=basic.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { MetaFilter } from './meta';
function IconDescriptorLink({ icon }) {
const { url, rel = 'icon', ...props } = icon;
return /*#__PURE__*/ _jsx("link", {
rel: rel,
href: url.toString(),
...props
});
}
function IconLink({ rel, icon }) {
if (typeof icon === 'object' && !(icon instanceof URL)) {
if (!icon.rel && rel) icon.rel = rel;
return IconDescriptorLink({
icon
});
} else {
const href = icon.toString();
return /*#__PURE__*/ _jsx("link", {
rel: rel,
href: href
});
}
}
export function IconsMetadata({ icons }) {
if (!icons) return null;
const shortcutList = icons.shortcut;
const iconList = icons.icon;
const appleList = icons.apple;
const otherList = icons.other;
return MetaFilter([
shortcutList ? shortcutList.map((icon)=>IconLink({
rel: 'shortcut icon',
icon
})) : null,
iconList ? iconList.map((icon)=>IconLink({
rel: 'icon',
icon
})) : null,
appleList ? appleList.map((icon)=>IconLink({
rel: 'apple-touch-icon',
icon
})) : null,
otherList ? otherList.map((icon)=>IconDescriptorLink({
icon
})) : null
]);
}
//# sourceMappingURL=icons.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/generate/icons.tsx"],"sourcesContent":["import type { ResolvedMetadata } from '../types/metadata-interface'\nimport type { Icon, IconDescriptor } from '../types/metadata-types'\n\nimport { MetaFilter } from './meta'\n\nfunction IconDescriptorLink({ icon }: { icon: IconDescriptor }) {\n const { url, rel = 'icon', ...props } = icon\n\n return <link rel={rel} href={url.toString()} {...props} />\n}\n\nfunction IconLink({ rel, icon }: { rel?: string; icon: Icon }) {\n if (typeof icon === 'object' && !(icon instanceof URL)) {\n if (!icon.rel && rel) icon.rel = rel\n return IconDescriptorLink({ icon })\n } else {\n const href = icon.toString()\n return <link rel={rel} href={href} />\n }\n}\n\nexport function IconsMetadata({ icons }: { icons: ResolvedMetadata['icons'] }) {\n if (!icons) return null\n\n const shortcutList = icons.shortcut\n const iconList = icons.icon\n const appleList = icons.apple\n const otherList = icons.other\n\n return MetaFilter([\n shortcutList\n ? shortcutList.map((icon) => IconLink({ rel: 'shortcut icon', icon }))\n : null,\n iconList ? iconList.map((icon) => IconLink({ rel: 'icon', icon })) : null,\n appleList\n ? appleList.map((icon) => IconLink({ rel: 'apple-touch-icon', icon }))\n : null,\n otherList ? otherList.map((icon) => IconDescriptorLink({ icon })) : null,\n ])\n}\n"],"names":["MetaFilter","IconDescriptorLink","icon","url","rel","props","link","href","toString","IconLink","URL","IconsMetadata","icons","shortcutList","shortcut","iconList","appleList","apple","otherList","other","map"],"mappings":";AAGA,SAASA,UAAU,QAAQ,SAAQ;AAEnC,SAASC,mBAAmB,EAAEC,IAAI,EAA4B;IAC5D,MAAM,EAAEC,GAAG,EAAEC,MAAM,MAAM,EAAE,GAAGC,OAAO,GAAGH;IAExC,qBAAO,KAACI;QAAKF,KAAKA;QAAKG,MAAMJ,IAAIK,QAAQ;QAAK,GAAGH,KAAK;;AACxD;AAEA,SAASI,SAAS,EAAEL,GAAG,EAAEF,IAAI,EAAgC;IAC3D,IAAI,OAAOA,SAAS,YAAY,CAAEA,CAAAA,gBAAgBQ,GAAE,GAAI;QACtD,IAAI,CAACR,KAAKE,GAAG,IAAIA,KAAKF,KAAKE,GAAG,GAAGA;QACjC,OAAOH,mBAAmB;YAAEC;QAAK;IACnC,OAAO;QACL,MAAMK,OAAOL,KAAKM,QAAQ;QAC1B,qBAAO,KAACF;YAAKF,KAAKA;YAAKG,MAAMA;;IAC/B;AACF;AAEA,OAAO,SAASI,cAAc,EAAEC,KAAK,EAAwC;IAC3E,IAAI,CAACA,OAAO,OAAO;IAEnB,MAAMC,eAAeD,MAAME,QAAQ;IACnC,MAAMC,WAAWH,MAAMV,IAAI;IAC3B,MAAMc,YAAYJ,MAAMK,KAAK;IAC7B,MAAMC,YAAYN,MAAMO,KAAK;IAE7B,OAAOnB,WAAW;QAChBa,eACIA,aAAaO,GAAG,CAAC,CAAClB,OAASO,SAAS;gBAAEL,KAAK;gBAAiBF;YAAK,MACjE;QACJa,WAAWA,SAASK,GAAG,CAAC,CAAClB,OAASO,SAAS;gBAAEL,KAAK;gBAAQF;YAAK,MAAM;QACrEc,YACIA,UAAUI,GAAG,CAAC,CAAClB,OAASO,SAAS;gBAAEL,KAAK;gBAAoBF;YAAK,MACjE;QACJgB,YAAYA,UAAUE,GAAG,CAAC,CAAClB,OAASD,mBAAmB;gBAAEC;YAAK,MAAM;KACrE;AACH"}

View File

@@ -0,0 +1,92 @@
import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
import { nonNullable } from '../../non-nullable';
export function Meta({ name, property, content, media }) {
if (typeof content !== 'undefined' && content !== null && content !== '') {
return /*#__PURE__*/ _jsx("meta", {
...name ? {
name
} : {
property
},
...media ? {
media
} : undefined,
content: typeof content === 'string' ? content : content.toString()
});
}
return null;
}
export function MetaFilter(items) {
const acc = [];
for (const item of items){
if (Array.isArray(item)) {
acc.push(...item.filter(nonNullable));
} else if (nonNullable(item)) {
acc.push(item);
}
}
return acc;
}
function camelToSnake(camelCaseStr) {
return camelCaseStr.replace(/([A-Z])/g, function(match) {
return '_' + match.toLowerCase();
});
}
const aliasPropPrefixes = new Set([
'og:image',
'twitter:image',
'og:video',
'og:audio'
]);
function getMetaKey(prefix, key) {
// Use `twitter:image` and `og:image` instead of `twitter:image:url` and `og:image:url`
// to be more compatible as it's a more common format.
// `og:video` & `og:audio` do not have a `:url` suffix alias
if (aliasPropPrefixes.has(prefix) && key === 'url') {
return prefix;
}
if (prefix.startsWith('og:') || prefix.startsWith('twitter:')) {
key = camelToSnake(key);
}
return prefix + ':' + key;
}
function ExtendMeta({ content, namePrefix, propertyPrefix }) {
if (!content) return null;
return MetaFilter(Object.entries(content).map(([k, v])=>{
return typeof v === 'undefined' ? null : Meta({
...propertyPrefix && {
property: getMetaKey(propertyPrefix, k)
},
...namePrefix && {
name: getMetaKey(namePrefix, k)
},
content: typeof v === 'string' ? v : v == null ? void 0 : v.toString()
});
}));
}
export function MultiMeta({ propertyPrefix, namePrefix, contents }) {
if (typeof contents === 'undefined' || contents === null) {
return null;
}
return MetaFilter(contents.map((content)=>{
if (typeof content === 'string' || typeof content === 'number' || content instanceof URL) {
return Meta({
...propertyPrefix ? {
property: propertyPrefix
} : {
name: namePrefix
},
content
});
} else {
return ExtendMeta({
namePrefix,
propertyPrefix,
content
});
}
}));
}
//# sourceMappingURL=meta.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,452 @@
import { Meta, MetaFilter, MultiMeta } from './meta';
export function OpenGraphMetadata({ openGraph }) {
var _openGraph_title, _openGraph_url, _openGraph_ttl;
if (!openGraph) {
return null;
}
let typedOpenGraph;
if ('type' in openGraph) {
const openGraphType = openGraph.type;
switch(openGraphType){
case 'website':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'website'
})
];
break;
case 'article':
var _openGraph_publishedTime, _openGraph_modifiedTime, _openGraph_expirationTime;
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'article'
}),
Meta({
property: 'article:published_time',
content: (_openGraph_publishedTime = openGraph.publishedTime) == null ? void 0 : _openGraph_publishedTime.toString()
}),
Meta({
property: 'article:modified_time',
content: (_openGraph_modifiedTime = openGraph.modifiedTime) == null ? void 0 : _openGraph_modifiedTime.toString()
}),
Meta({
property: 'article:expiration_time',
content: (_openGraph_expirationTime = openGraph.expirationTime) == null ? void 0 : _openGraph_expirationTime.toString()
}),
MultiMeta({
propertyPrefix: 'article:author',
contents: openGraph.authors
}),
Meta({
property: 'article:section',
content: openGraph.section
}),
MultiMeta({
propertyPrefix: 'article:tag',
contents: openGraph.tags
})
];
break;
case 'book':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'book'
}),
Meta({
property: 'book:isbn',
content: openGraph.isbn
}),
Meta({
property: 'book:release_date',
content: openGraph.releaseDate
}),
MultiMeta({
propertyPrefix: 'book:author',
contents: openGraph.authors
}),
MultiMeta({
propertyPrefix: 'book:tag',
contents: openGraph.tags
})
];
break;
case 'profile':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'profile'
}),
Meta({
property: 'profile:first_name',
content: openGraph.firstName
}),
Meta({
property: 'profile:last_name',
content: openGraph.lastName
}),
Meta({
property: 'profile:username',
content: openGraph.username
}),
Meta({
property: 'profile:gender',
content: openGraph.gender
})
];
break;
case 'music.song':
var _openGraph_duration;
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'music.song'
}),
Meta({
property: 'music:duration',
content: (_openGraph_duration = openGraph.duration) == null ? void 0 : _openGraph_duration.toString()
}),
MultiMeta({
propertyPrefix: 'music:album',
contents: openGraph.albums
}),
MultiMeta({
propertyPrefix: 'music:musician',
contents: openGraph.musicians
})
];
break;
case 'music.album':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'music.album'
}),
MultiMeta({
propertyPrefix: 'music:song',
contents: openGraph.songs
}),
MultiMeta({
propertyPrefix: 'music:musician',
contents: openGraph.musicians
}),
Meta({
property: 'music:release_date',
content: openGraph.releaseDate
})
];
break;
case 'music.playlist':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'music.playlist'
}),
MultiMeta({
propertyPrefix: 'music:song',
contents: openGraph.songs
}),
MultiMeta({
propertyPrefix: 'music:creator',
contents: openGraph.creators
})
];
break;
case 'music.radio_station':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'music.radio_station'
}),
MultiMeta({
propertyPrefix: 'music:creator',
contents: openGraph.creators
})
];
break;
case 'video.movie':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'video.movie'
}),
MultiMeta({
propertyPrefix: 'video:actor',
contents: openGraph.actors
}),
MultiMeta({
propertyPrefix: 'video:director',
contents: openGraph.directors
}),
MultiMeta({
propertyPrefix: 'video:writer',
contents: openGraph.writers
}),
Meta({
property: 'video:duration',
content: openGraph.duration
}),
Meta({
property: 'video:release_date',
content: openGraph.releaseDate
}),
MultiMeta({
propertyPrefix: 'video:tag',
contents: openGraph.tags
})
];
break;
case 'video.episode':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'video.episode'
}),
MultiMeta({
propertyPrefix: 'video:actor',
contents: openGraph.actors
}),
MultiMeta({
propertyPrefix: 'video:director',
contents: openGraph.directors
}),
MultiMeta({
propertyPrefix: 'video:writer',
contents: openGraph.writers
}),
Meta({
property: 'video:duration',
content: openGraph.duration
}),
Meta({
property: 'video:release_date',
content: openGraph.releaseDate
}),
MultiMeta({
propertyPrefix: 'video:tag',
contents: openGraph.tags
}),
Meta({
property: 'video:series',
content: openGraph.series
})
];
break;
case 'video.tv_show':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'video.tv_show'
})
];
break;
case 'video.other':
typedOpenGraph = [
Meta({
property: 'og:type',
content: 'video.other'
})
];
break;
default:
const _exhaustiveCheck = openGraphType;
throw Object.defineProperty(new Error(`Invalid OpenGraph type: ${_exhaustiveCheck}`), "__NEXT_ERROR_CODE", {
value: "E237",
enumerable: false,
configurable: true
});
}
}
return MetaFilter([
Meta({
property: 'og:determiner',
content: openGraph.determiner
}),
Meta({
property: 'og:title',
content: (_openGraph_title = openGraph.title) == null ? void 0 : _openGraph_title.absolute
}),
Meta({
property: 'og:description',
content: openGraph.description
}),
Meta({
property: 'og:url',
content: (_openGraph_url = openGraph.url) == null ? void 0 : _openGraph_url.toString()
}),
Meta({
property: 'og:site_name',
content: openGraph.siteName
}),
Meta({
property: 'og:locale',
content: openGraph.locale
}),
Meta({
property: 'og:country_name',
content: openGraph.countryName
}),
Meta({
property: 'og:ttl',
content: (_openGraph_ttl = openGraph.ttl) == null ? void 0 : _openGraph_ttl.toString()
}),
MultiMeta({
propertyPrefix: 'og:image',
contents: openGraph.images
}),
MultiMeta({
propertyPrefix: 'og:video',
contents: openGraph.videos
}),
MultiMeta({
propertyPrefix: 'og:audio',
contents: openGraph.audio
}),
MultiMeta({
propertyPrefix: 'og:email',
contents: openGraph.emails
}),
MultiMeta({
propertyPrefix: 'og:phone_number',
contents: openGraph.phoneNumbers
}),
MultiMeta({
propertyPrefix: 'og:fax_number',
contents: openGraph.faxNumbers
}),
MultiMeta({
propertyPrefix: 'og:locale:alternate',
contents: openGraph.alternateLocale
}),
...typedOpenGraph ? typedOpenGraph : []
]);
}
function TwitterAppItem({ app, type }) {
var _app_url_type, _app_url;
return [
Meta({
name: `twitter:app:name:${type}`,
content: app.name
}),
Meta({
name: `twitter:app:id:${type}`,
content: app.id[type]
}),
Meta({
name: `twitter:app:url:${type}`,
content: (_app_url = app.url) == null ? void 0 : (_app_url_type = _app_url[type]) == null ? void 0 : _app_url_type.toString()
})
];
}
export function TwitterMetadata({ twitter }) {
var _twitter_title;
if (!twitter) return null;
const { card } = twitter;
return MetaFilter([
Meta({
name: 'twitter:card',
content: card
}),
Meta({
name: 'twitter:site',
content: twitter.site
}),
Meta({
name: 'twitter:site:id',
content: twitter.siteId
}),
Meta({
name: 'twitter:creator',
content: twitter.creator
}),
Meta({
name: 'twitter:creator:id',
content: twitter.creatorId
}),
Meta({
name: 'twitter:title',
content: (_twitter_title = twitter.title) == null ? void 0 : _twitter_title.absolute
}),
Meta({
name: 'twitter:description',
content: twitter.description
}),
MultiMeta({
namePrefix: 'twitter:image',
contents: twitter.images
}),
...card === 'player' ? twitter.players.flatMap((player)=>[
Meta({
name: 'twitter:player',
content: player.playerUrl.toString()
}),
Meta({
name: 'twitter:player:stream',
content: player.streamUrl.toString()
}),
Meta({
name: 'twitter:player:width',
content: player.width
}),
Meta({
name: 'twitter:player:height',
content: player.height
})
]) : [],
...card === 'app' ? [
TwitterAppItem({
app: twitter.app,
type: 'iphone'
}),
TwitterAppItem({
app: twitter.app,
type: 'ipad'
}),
TwitterAppItem({
app: twitter.app,
type: 'googleplay'
})
] : []
]);
}
export function AppLinksMeta({ appLinks }) {
if (!appLinks) return null;
return MetaFilter([
MultiMeta({
propertyPrefix: 'al:ios',
contents: appLinks.ios
}),
MultiMeta({
propertyPrefix: 'al:iphone',
contents: appLinks.iphone
}),
MultiMeta({
propertyPrefix: 'al:ipad',
contents: appLinks.ipad
}),
MultiMeta({
propertyPrefix: 'al:android',
contents: appLinks.android
}),
MultiMeta({
propertyPrefix: 'al:windows_phone',
contents: appLinks.windows_phone
}),
MultiMeta({
propertyPrefix: 'al:windows',
contents: appLinks.windows
}),
MultiMeta({
propertyPrefix: 'al:windows_universal',
contents: appLinks.windows_universal
}),
MultiMeta({
propertyPrefix: 'al:web',
contents: appLinks.web
})
]);
}
//# sourceMappingURL=opengraph.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
function resolveArray(value) {
if (Array.isArray(value)) {
return value;
}
return [
value
];
}
function resolveAsArrayOrUndefined(value) {
if (typeof value === 'undefined' || value === null) {
return undefined;
}
return resolveArray(value);
}
function getOrigin(url) {
let origin = undefined;
if (typeof url === 'string') {
try {
url = new URL(url);
origin = url.origin;
} catch {}
}
return origin;
}
export { resolveAsArrayOrUndefined, resolveArray, getOrigin };
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/generate/utils.ts"],"sourcesContent":["function resolveArray<T>(value: T | T[]): T[] {\n if (Array.isArray(value)) {\n return value as any\n }\n return [value] as any\n}\n\nfunction resolveAsArrayOrUndefined<T>(\n value: T | T[] | undefined | null\n): T extends undefined | null ? undefined : T[] {\n if (typeof value === 'undefined' || value === null) {\n return undefined as any\n }\n return resolveArray(value) as any\n}\n\nfunction getOrigin(url: string | URL): string | undefined {\n let origin = undefined\n if (typeof url === 'string') {\n try {\n url = new URL(url)\n origin = url.origin\n } catch {}\n }\n return origin\n}\n\nexport { resolveAsArrayOrUndefined, resolveArray, getOrigin }\n"],"names":["resolveArray","value","Array","isArray","resolveAsArrayOrUndefined","undefined","getOrigin","url","origin","URL"],"mappings":"AAAA,SAASA,aAAgBC,KAAc;IACrC,IAAIC,MAAMC,OAAO,CAACF,QAAQ;QACxB,OAAOA;IACT;IACA,OAAO;QAACA;KAAM;AAChB;AAEA,SAASG,0BACPH,KAAiC;IAEjC,IAAI,OAAOA,UAAU,eAAeA,UAAU,MAAM;QAClD,OAAOI;IACT;IACA,OAAOL,aAAaC;AACtB;AAEA,SAASK,UAAUC,GAAiB;IAClC,IAAIC,SAASH;IACb,IAAI,OAAOE,QAAQ,UAAU;QAC3B,IAAI;YACFA,MAAM,IAAIE,IAAIF;YACdC,SAASD,IAAIC,MAAM;QACrB,EAAE,OAAM,CAAC;IACX;IACA,OAAOA;AACT;AAEA,SAASJ,yBAAyB,EAAEJ,YAAY,EAAEM,SAAS,GAAE"}

View File

@@ -0,0 +1,102 @@
import { isMetadataRoute } from './is-metadata-route';
import path from '../../shared/lib/isomorphic/path';
import { interpolateDynamicPath } from '../../server/server-utils';
import { getNamedRouteRegex } from '../../shared/lib/router/utils/route-regex';
import { djb2Hash } from '../../shared/lib/hash';
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths';
import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep';
import { isGroupSegment, isParallelRouteSegment } from '../../shared/lib/segment';
/*
* If there's special convention like (...) or @ in the page path,
* Give it a unique hash suffix to avoid conflicts
*
* e.g.
* /opengraph-image -> /opengraph-image
* /(post)/opengraph-image.tsx -> /opengraph-image-[0-9a-z]{6}
*
* Sitemap is an exception, it should not have a suffix.
* Each sitemap contains all the urls of sub routes, we don't have the case of duplicates `/(group)/sitemap.[ext]` and `/sitemap.[ext]` since they should be the same.
* Hence we always normalize the urls for sitemap and do not append hash suffix, and ensure user-land only contains one sitemap per pathname.
*
* /sitemap -> /sitemap
* /(post)/sitemap -> /sitemap
*/ function getMetadataRouteSuffix(page) {
// Remove the last segment and get the parent pathname
// e.g. /parent/a/b/c -> /parent/a/b
// e.g. /parent/opengraph-image -> /parent
const parentPathname = path.dirname(page);
// Only apply suffix to metadata routes except for sitemaps
if (page.endsWith('/sitemap')) {
return '';
}
// Calculate the hash suffix based on the parent path
let suffix = '';
// Check if there's any special characters in the parent pathname.
const segments = parentPathname.split('/');
if (segments.some((seg)=>isGroupSegment(seg) || isParallelRouteSegment(seg))) {
// Hash the parent path to get a unique suffix
suffix = djb2Hash(parentPathname).toString(36).slice(0, 6);
}
return suffix;
}
/**
* Fill the dynamic segment in the metadata route
*
* Example:
* fillMetadataSegment('/a/[slug]', { params: { slug: 'b' } }, 'open-graph') -> '/a/b/open-graph'
*
*/ export function fillMetadataSegment(segment, params, lastSegment) {
const pathname = normalizeAppPath(segment);
const routeRegex = getNamedRouteRegex(pathname, {
prefixRouteKeys: false
});
const route = interpolateDynamicPath(pathname, params, routeRegex);
const { name, ext } = path.parse(lastSegment);
const pagePath = path.posix.join(segment, name);
const suffix = getMetadataRouteSuffix(pagePath);
const routeSuffix = suffix ? `-${suffix}` : '';
return normalizePathSep(path.join(route, `${name}${routeSuffix}${ext}`));
}
/**
* Map metadata page key to the corresponding route
*
* static file page key: /app/robots.txt -> /robots.xml -> /robots.txt/route
* dynamic route page key: /app/robots.tsx -> /robots -> /robots.txt/route
*
* @param page
* @returns
*/ export function normalizeMetadataRoute(page) {
if (!isMetadataRoute(page)) {
return page;
}
let route = page;
let suffix = '';
if (page === '/robots') {
route += '.txt';
} else if (page === '/manifest') {
route += '.webmanifest';
} else {
suffix = getMetadataRouteSuffix(page);
}
// Support both /<metadata-route.ext> and custom routes /<metadata-route>/route.ts.
// If it's a metadata file route, we need to append /[id]/route to the page.
if (!route.endsWith('/route')) {
const { dir, name: baseName, ext } = path.parse(route);
route = path.posix.join(dir, `${baseName}${suffix ? `-${suffix}` : ''}${ext}`, 'route');
}
return route;
}
// Normalize metadata route page to either a single route or a dynamic route.
// e.g. Input: /sitemap/route
// when isDynamic is false, single route -> /sitemap.xml/route
// when isDynamic is false, dynamic route -> /sitemap/[__metadata_id__]/route
// also works for pathname such as /sitemap -> /sitemap.xml, but will not append /route suffix
export function normalizeMetadataPageToRoute(page, isDynamic) {
const isRoute = page.endsWith('/route');
const routePagePath = isRoute ? page.slice(0, -'/route'.length) : page;
const metadataRouteExtension = routePagePath.endsWith('/sitemap') ? '.xml' : '';
const mapped = isDynamic ? `${routePagePath}/[__metadata_id__]` : `${routePagePath}${metadataRouteExtension}`;
return mapped + (isRoute ? '/route' : '');
}
//# sourceMappingURL=get-metadata-route.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep';
export const STATIC_METADATA_IMAGES = {
icon: {
filename: 'icon',
extensions: [
'ico',
'jpg',
'jpeg',
'png',
'svg'
]
},
apple: {
filename: 'apple-icon',
extensions: [
'jpg',
'jpeg',
'png'
]
},
favicon: {
filename: 'favicon',
extensions: [
'ico'
]
},
openGraph: {
filename: 'opengraph-image',
extensions: [
'jpg',
'jpeg',
'png',
'gif'
]
},
twitter: {
filename: 'twitter-image',
extensions: [
'jpg',
'jpeg',
'png',
'gif'
]
}
};
// Match routes that are metadata routes, e.g. /sitemap.xml, /favicon.<ext>, /<icon>.<ext>, etc.
// TODO-METADATA: support more metadata routes with more extensions
const defaultExtensions = [
'js',
'jsx',
'ts',
'tsx'
];
// Match the file extension with the dynamic multi-routes extensions
// e.g. ([xml, js], null) -> can match `/sitemap.xml/route`, `sitemap.js/route`
// e.g. ([png], [ts]) -> can match `/opengrapg-image.png/route`, `/opengraph-image.ts[]/route`
export const getExtensionRegexString = (staticExtensions, dynamicExtensions)=>{
// If there's no possible multi dynamic routes, will not match any <name>[].<ext> files
if (!dynamicExtensions) {
return `\\.(?:${staticExtensions.join('|')})`;
}
return `(?:\\.(${staticExtensions.join('|')})|((\\[\\])?\\.(${dynamicExtensions.join('|')})))`;
};
// When you only pass the file extension as `[]`, it will only match the static convention files
// e.g. /robots.txt, /sitemap.xml, /favicon.ico, /manifest.json
// When you pass the file extension as `['js', 'jsx', 'ts', 'tsx']`, it will also match the dynamic convention files
// e.g. /robots.js, /sitemap.tsx, /favicon.jsx, /manifest.ts
// When `withExtension` is false, it will match the static convention files without the extension, by default it's true
// e.g. /robots, /sitemap, /favicon, /manifest, use to match dynamic API routes like app/robots.ts
export function isMetadataRouteFile(appDirRelativePath, pageExtensions, withExtension) {
const metadataRouteFilesRegex = [
new RegExp(`^[\\\\/]robots${withExtension ? `${getExtensionRegexString(pageExtensions.concat('txt'), null)}$` : ''}`),
new RegExp(`^[\\\\/]manifest${withExtension ? `${getExtensionRegexString(pageExtensions.concat('webmanifest', 'json'), null)}$` : ''}`),
new RegExp(`^[\\\\/]favicon\\.ico$`),
new RegExp(`[\\\\/]sitemap${withExtension ? `${getExtensionRegexString([
'xml'
], pageExtensions)}$` : ''}`),
new RegExp(`[\\\\/]${STATIC_METADATA_IMAGES.icon.filename}\\d?${withExtension ? `${getExtensionRegexString(STATIC_METADATA_IMAGES.icon.extensions, pageExtensions)}$` : ''}`),
new RegExp(`[\\\\/]${STATIC_METADATA_IMAGES.apple.filename}\\d?${withExtension ? `${getExtensionRegexString(STATIC_METADATA_IMAGES.apple.extensions, pageExtensions)}$` : ''}`),
new RegExp(`[\\\\/]${STATIC_METADATA_IMAGES.openGraph.filename}\\d?${withExtension ? `${getExtensionRegexString(STATIC_METADATA_IMAGES.openGraph.extensions, pageExtensions)}$` : ''}`),
new RegExp(`[\\\\/]${STATIC_METADATA_IMAGES.twitter.filename}\\d?${withExtension ? `${getExtensionRegexString(STATIC_METADATA_IMAGES.twitter.extensions, pageExtensions)}$` : ''}`)
];
const normalizedAppDirRelativePath = normalizePathSep(appDirRelativePath);
return metadataRouteFilesRegex.some((r)=>r.test(normalizedAppDirRelativePath));
}
export function isStaticMetadataRouteFile(appDirRelativePath) {
return isMetadataRouteFile(appDirRelativePath, [], true);
}
export function isStaticMetadataRoute(page) {
return page === '/robots' || page === '/manifest' || isStaticMetadataRouteFile(page);
}
/*
* Remove the 'app' prefix or '/route' suffix, only check the route name since they're only allowed in root app directory
* e.g.
* /app/robots -> /robots
* app/robots -> /robots
* /robots -> /robots
*/ export function isMetadataRoute(route) {
let page = route.replace(/^\/?app\//, '').replace(/\/route$/, '');
if (page[0] !== '/') page = '/' + page;
return !page.endsWith('/page') && isMetadataRouteFile(page, defaultExtensions, false);
}
//# sourceMappingURL=is-metadata-route.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
export const METADATA_BOUNDARY_NAME = '__next_metadata_boundary__';
export const VIEWPORT_BOUNDARY_NAME = '__next_viewport_boundary__';
export const OUTLET_BOUNDARY_NAME = '__next_outlet_boundary__';
//# sourceMappingURL=metadata-constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/metadata/metadata-constants.tsx"],"sourcesContent":["export const METADATA_BOUNDARY_NAME = '__next_metadata_boundary__'\nexport const VIEWPORT_BOUNDARY_NAME = '__next_viewport_boundary__'\nexport const OUTLET_BOUNDARY_NAME = '__next_outlet_boundary__'\n"],"names":["METADATA_BOUNDARY_NAME","VIEWPORT_BOUNDARY_NAME","OUTLET_BOUNDARY_NAME"],"mappings":"AAAA,OAAO,MAAMA,yBAAyB,6BAA4B;AAClE,OAAO,MAAMC,yBAAyB,6BAA4B;AAClE,OAAO,MAAMC,uBAAuB,2BAA0B"}

View File

@@ -0,0 +1,26 @@
import { trackFallbackParamAccessed } from '../../server/app-render/dynamic-rendering';
export function createMetadataContext(pathname, renderOpts) {
return {
pathname,
trailingSlash: renderOpts.trailingSlash,
isStaticMetadataRouteFile: false
};
}
export function createTrackedMetadataContext(pathname, renderOpts, workStore) {
return {
// Use the regular metadata context, but we trap the pathname access.
...createMetadataContext(pathname, renderOpts),
// Setup the trap around the pathname access so we can track when the
// pathname is accessed while resolving metadata which would indicate it's
// being used to resolve a relative URL. If that's the case, we don't want
// to provide it, and instead we should error.
get pathname () {
if (workStore && workStore.isStaticGeneration && workStore.fallbackRouteParams && workStore.fallbackRouteParams.size > 0) {
trackFallbackParamAccessed(workStore, 'metadata relative url resolving');
}
return pathname;
}
};
}
//# sourceMappingURL=metadata-context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/metadata/metadata-context.tsx"],"sourcesContent":["import type { AppRenderContext } from '../../server/app-render/app-render'\nimport type { MetadataContext } from './types/resolvers'\nimport type { WorkStore } from '../../server/app-render/work-async-storage.external'\nimport { trackFallbackParamAccessed } from '../../server/app-render/dynamic-rendering'\n\nexport function createMetadataContext(\n pathname: string,\n renderOpts: AppRenderContext['renderOpts']\n): MetadataContext {\n return {\n pathname,\n trailingSlash: renderOpts.trailingSlash,\n isStaticMetadataRouteFile: false,\n }\n}\n\nexport function createTrackedMetadataContext(\n pathname: string,\n renderOpts: AppRenderContext['renderOpts'],\n workStore: WorkStore | null\n): MetadataContext {\n return {\n // Use the regular metadata context, but we trap the pathname access.\n ...createMetadataContext(pathname, renderOpts),\n\n // Setup the trap around the pathname access so we can track when the\n // pathname is accessed while resolving metadata which would indicate it's\n // being used to resolve a relative URL. If that's the case, we don't want\n // to provide it, and instead we should error.\n get pathname() {\n if (\n workStore &&\n workStore.isStaticGeneration &&\n workStore.fallbackRouteParams &&\n workStore.fallbackRouteParams.size > 0\n ) {\n trackFallbackParamAccessed(workStore, 'metadata relative url resolving')\n }\n\n return pathname\n },\n }\n}\n"],"names":["trackFallbackParamAccessed","createMetadataContext","pathname","renderOpts","trailingSlash","isStaticMetadataRouteFile","createTrackedMetadataContext","workStore","isStaticGeneration","fallbackRouteParams","size"],"mappings":"AAGA,SAASA,0BAA0B,QAAQ,4CAA2C;AAEtF,OAAO,SAASC,sBACdC,QAAgB,EAChBC,UAA0C;IAE1C,OAAO;QACLD;QACAE,eAAeD,WAAWC,aAAa;QACvCC,2BAA2B;IAC7B;AACF;AAEA,OAAO,SAASC,6BACdJ,QAAgB,EAChBC,UAA0C,EAC1CI,SAA2B;IAE3B,OAAO;QACL,qEAAqE;QACrE,GAAGN,sBAAsBC,UAAUC,WAAW;QAE9C,qEAAqE;QACrE,0EAA0E;QAC1E,0EAA0E;QAC1E,8CAA8C;QAC9C,IAAID,YAAW;YACb,IACEK,aACAA,UAAUC,kBAAkB,IAC5BD,UAAUE,mBAAmB,IAC7BF,UAAUE,mBAAmB,CAACC,IAAI,GAAG,GACrC;gBACAV,2BAA2BO,WAAW;YACxC;YAEA,OAAOL;QACT;IACF;AACF"}

235
node_modules/next/dist/esm/lib/metadata/metadata.js generated vendored Normal file
View File

@@ -0,0 +1,235 @@
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React, { Suspense, cache, cloneElement } from 'react';
import { AppleWebAppMeta, FormatDetectionMeta, ItunesMeta, BasicMeta, ViewportMeta, VerificationMeta, FacebookMeta } from './generate/basic';
import { AlternatesMetadata } from './generate/alternate';
import { OpenGraphMetadata, TwitterMetadata, AppLinksMeta } from './generate/opengraph';
import { IconsMetadata } from './generate/icons';
import { resolveMetadata, resolveViewport } from './resolve-metadata';
import { MetaFilter } from './generate/meta';
import { isHTTPAccessFallbackError } from '../../client/components/http-access-fallback/http-access-fallback';
import { METADATA_BOUNDARY_NAME, VIEWPORT_BOUNDARY_NAME } from './metadata-constants';
import { AsyncMetadata, AsyncMetadataOutlet } from '../../client/components/metadata/async-metadata';
import { isPostpone } from '../../server/lib/router-utils/is-postpone';
// Use a promise to share the status of the metadata resolving,
// returning two components `MetadataTree` and `MetadataOutlet`
// `MetadataTree` is the one that will be rendered at first in the content sequence for metadata tags.
// `MetadataOutlet` is the one that will be rendered under error boundaries for metadata resolving errors.
// In this way we can let the metadata tags always render successfully,
// and the error will be caught by the error boundary and trigger fallbacks.
export function createMetadataComponents({ tree, searchParams, metadataContext, getDynamicParamFromSegment, appUsingSizeAdjustment, errorType, createServerParamsForMetadata, workStore, MetadataBoundary, ViewportBoundary, serveStreamingMetadata }) {
function ViewportTree() {
return /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsx(ViewportBoundary, {
children: /*#__PURE__*/ _jsx(Viewport, {})
}),
appUsingSizeAdjustment ? /*#__PURE__*/ _jsx("meta", {
name: "next-size-adjust",
content: ""
}) : null
]
});
}
function MetadataTree() {
return /*#__PURE__*/ _jsx(MetadataBoundary, {
children: /*#__PURE__*/ _jsx(Metadata, {})
});
}
function viewport() {
return getResolvedViewport(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, errorType);
}
async function Viewport() {
try {
return await viewport();
} catch (error) {
if (!errorType && isHTTPAccessFallbackError(error)) {
try {
return await getNotFoundViewport(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
} catch {}
}
// We don't actually want to error in this component. We will
// also error in the MetadataOutlet which causes the error to
// bubble from the right position in the page to be caught by the
// appropriate boundaries
return null;
}
}
Viewport.displayName = VIEWPORT_BOUNDARY_NAME;
function metadata() {
return getResolvedMetadata(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore, errorType);
}
async function resolveFinalMetadata() {
let result;
let error = null;
try {
result = await metadata();
return {
metadata: result,
error: null,
digest: undefined
};
} catch (metadataErr) {
error = metadataErr;
if (!errorType && isHTTPAccessFallbackError(metadataErr)) {
try {
result = await getNotFoundMetadata(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore);
return {
metadata: result,
error,
digest: error == null ? void 0 : error.digest
};
} catch (notFoundMetadataErr) {
error = notFoundMetadataErr;
// In PPR rendering we still need to throw the postpone error.
// If metadata is postponed, React needs to be aware of the location of error.
if (serveStreamingMetadata && isPostpone(notFoundMetadataErr)) {
throw notFoundMetadataErr;
}
}
}
// In PPR rendering we still need to throw the postpone error.
// If metadata is postponed, React needs to be aware of the location of error.
if (serveStreamingMetadata && isPostpone(metadataErr)) {
throw metadataErr;
}
// We don't actually want to error in this component. We will
// also error in the MetadataOutlet which causes the error to
// bubble from the right position in the page to be caught by the
// appropriate boundaries
return {
metadata: result,
error,
digest: error == null ? void 0 : error.digest
};
}
}
async function Metadata() {
const promise = resolveFinalMetadata();
if (serveStreamingMetadata) {
return /*#__PURE__*/ _jsx(Suspense, {
fallback: null,
children: /*#__PURE__*/ _jsx(AsyncMetadata, {
promise: promise
})
});
}
const metadataState = await promise;
return metadataState.metadata;
}
Metadata.displayName = METADATA_BOUNDARY_NAME;
async function getMetadataReady() {
// Only warm up metadata() call when it's blocking metadata,
// otherwise it will be fully managed by AsyncMetadata component.
if (!serveStreamingMetadata) {
await metadata();
}
return undefined;
}
async function getViewportReady() {
await viewport();
return undefined;
}
function StreamingMetadataOutlet() {
if (serveStreamingMetadata) {
return /*#__PURE__*/ _jsx(AsyncMetadataOutlet, {
promise: resolveFinalMetadata()
});
}
return null;
}
return {
ViewportTree,
MetadataTree,
getViewportReady,
getMetadataReady,
StreamingMetadataOutlet
};
}
const getResolvedMetadata = cache(getResolvedMetadataImpl);
async function getResolvedMetadataImpl(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore, errorType) {
const errorConvention = errorType === 'redirect' ? undefined : errorType;
return renderMetadata(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore, errorConvention);
}
const getNotFoundMetadata = cache(getNotFoundMetadataImpl);
async function getNotFoundMetadataImpl(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore) {
const notFoundErrorConvention = 'not-found';
return renderMetadata(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore, notFoundErrorConvention);
}
const getResolvedViewport = cache(getResolvedViewportImpl);
async function getResolvedViewportImpl(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, errorType) {
const errorConvention = errorType === 'redirect' ? undefined : errorType;
return renderViewport(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, errorConvention);
}
const getNotFoundViewport = cache(getNotFoundViewportImpl);
async function getNotFoundViewportImpl(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore) {
const notFoundErrorConvention = 'not-found';
return renderViewport(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, notFoundErrorConvention);
}
async function renderMetadata(tree, searchParams, getDynamicParamFromSegment, metadataContext, createServerParamsForMetadata, workStore, errorConvention) {
const resolvedMetadata = await resolveMetadata(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, metadataContext);
const elements = createMetadataElements(resolvedMetadata);
return /*#__PURE__*/ _jsx(_Fragment, {
children: elements.map((el, index)=>{
return /*#__PURE__*/ cloneElement(el, {
key: index
});
})
});
}
async function renderViewport(tree, searchParams, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, errorConvention) {
const notFoundResolvedViewport = await resolveViewport(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
const elements = createViewportElements(notFoundResolvedViewport);
return /*#__PURE__*/ _jsx(_Fragment, {
children: elements.map((el, index)=>{
return /*#__PURE__*/ cloneElement(el, {
key: index
});
})
});
}
function createMetadataElements(metadata) {
return MetaFilter([
BasicMeta({
metadata
}),
AlternatesMetadata({
alternates: metadata.alternates
}),
ItunesMeta({
itunes: metadata.itunes
}),
FacebookMeta({
facebook: metadata.facebook
}),
FormatDetectionMeta({
formatDetection: metadata.formatDetection
}),
VerificationMeta({
verification: metadata.verification
}),
AppleWebAppMeta({
appleWebApp: metadata.appleWebApp
}),
OpenGraphMetadata({
openGraph: metadata.openGraph
}),
TwitterMetadata({
twitter: metadata.twitter
}),
AppLinksMeta({
appLinks: metadata.appLinks
}),
IconsMetadata({
icons: metadata.icons
})
]);
}
function createViewportElements(viewport) {
return MetaFilter([
ViewportMeta({
viewport: viewport
})
]);
}
//# sourceMappingURL=metadata.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,563 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import 'server-only';
import { cache } from 'react';
import { createDefaultMetadata, createDefaultViewport } from './default-metadata';
import { resolveOpenGraph, resolveTwitter } from './resolvers/resolve-opengraph';
import { resolveTitle } from './resolvers/resolve-title';
import { resolveAsArrayOrUndefined } from './generate/utils';
import { getComponentTypeModule, getLayoutOrPageModule } from '../../server/lib/app-dir-module';
import { interopDefault } from '../interop-default';
import { resolveAlternates, resolveAppleWebApp, resolveAppLinks, resolveRobots, resolveThemeColor, resolveVerification, resolveItunes, resolveFacebook, resolvePagination } from './resolvers/resolve-basics';
import { resolveIcons } from './resolvers/resolve-icons';
import { getTracer } from '../../server/lib/trace/tracer';
import { ResolveMetadataSpan } from '../../server/lib/trace/constants';
import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment';
import * as Log from '../../build/output/log';
function isFavicon(icon) {
if (!icon) {
return false;
}
// turbopack appends a hash to all images
return (icon.url === '/favicon.ico' || icon.url.toString().startsWith('/favicon.ico?')) && icon.type === 'image/x-icon';
}
function mergeStaticMetadata(source, target, staticFilesMetadata, metadataContext, titleTemplates, leafSegmentStaticIcons) {
var _source_twitter, _source_openGraph;
if (!staticFilesMetadata) return;
const { icon, apple, openGraph, twitter, manifest } = staticFilesMetadata;
// Keep updating the static icons in the most leaf node
if (icon) {
leafSegmentStaticIcons.icon = icon;
}
if (apple) {
leafSegmentStaticIcons.apple = apple;
}
// file based metadata is specified and current level metadata twitter.images is not specified
if (twitter && !(source == null ? void 0 : (_source_twitter = source.twitter) == null ? void 0 : _source_twitter.hasOwnProperty('images'))) {
const resolvedTwitter = resolveTwitter({
...target.twitter,
images: twitter
}, target.metadataBase, {
...metadataContext,
isStaticMetadataRouteFile: true
}, titleTemplates.twitter);
target.twitter = resolvedTwitter;
}
// file based metadata is specified and current level metadata openGraph.images is not specified
if (openGraph && !(source == null ? void 0 : (_source_openGraph = source.openGraph) == null ? void 0 : _source_openGraph.hasOwnProperty('images'))) {
const resolvedOpenGraph = resolveOpenGraph({
...target.openGraph,
images: openGraph
}, target.metadataBase, {
...metadataContext,
isStaticMetadataRouteFile: true
}, titleTemplates.openGraph);
target.openGraph = resolvedOpenGraph;
}
if (manifest) {
target.manifest = manifest;
}
return target;
}
// Merge the source metadata into the resolved target metadata.
function mergeMetadata({ source, target, staticFilesMetadata, titleTemplates, metadataContext, buildState, leafSegmentStaticIcons }) {
// If there's override metadata, prefer it otherwise fallback to the default metadata.
const metadataBase = typeof (source == null ? void 0 : source.metadataBase) !== 'undefined' ? source.metadataBase : target.metadataBase;
for(const key_ in source){
const key = key_;
switch(key){
case 'title':
{
target.title = resolveTitle(source.title, titleTemplates.title);
break;
}
case 'alternates':
{
target.alternates = resolveAlternates(source.alternates, metadataBase, metadataContext);
break;
}
case 'openGraph':
{
target.openGraph = resolveOpenGraph(source.openGraph, metadataBase, metadataContext, titleTemplates.openGraph);
break;
}
case 'twitter':
{
target.twitter = resolveTwitter(source.twitter, metadataBase, metadataContext, titleTemplates.twitter);
break;
}
case 'facebook':
target.facebook = resolveFacebook(source.facebook);
break;
case 'verification':
target.verification = resolveVerification(source.verification);
break;
case 'icons':
{
target.icons = resolveIcons(source.icons);
break;
}
case 'appleWebApp':
target.appleWebApp = resolveAppleWebApp(source.appleWebApp);
break;
case 'appLinks':
target.appLinks = resolveAppLinks(source.appLinks);
break;
case 'robots':
{
target.robots = resolveRobots(source.robots);
break;
}
case 'archives':
case 'assets':
case 'bookmarks':
case 'keywords':
{
target[key] = resolveAsArrayOrUndefined(source[key]);
break;
}
case 'authors':
{
target[key] = resolveAsArrayOrUndefined(source.authors);
break;
}
case 'itunes':
{
target[key] = resolveItunes(source.itunes, metadataBase, metadataContext);
break;
}
case 'pagination':
{
target.pagination = resolvePagination(source.pagination, metadataBase, metadataContext);
break;
}
// directly assign fields that fallback to null
case 'applicationName':
case 'description':
case 'generator':
case 'creator':
case 'publisher':
case 'category':
case 'classification':
case 'referrer':
case 'formatDetection':
case 'manifest':
// @ts-ignore TODO: support inferring
target[key] = source[key] || null;
break;
case 'other':
target.other = Object.assign({}, target.other, source.other);
break;
case 'metadataBase':
target.metadataBase = metadataBase;
break;
default:
{
if ((key === 'viewport' || key === 'themeColor' || key === 'colorScheme') && source[key] != null) {
buildState.warnings.add(`Unsupported metadata ${key} is configured in metadata export in ${metadataContext.pathname}. Please move it to viewport export instead.\nRead more: https://nextjs.org/docs/app/api-reference/functions/generate-viewport`);
}
break;
}
}
}
mergeStaticMetadata(source, target, staticFilesMetadata, metadataContext, titleTemplates, leafSegmentStaticIcons);
}
function mergeViewport({ target, source }) {
if (!source) return;
for(const key_ in source){
const key = key_;
switch(key){
case 'themeColor':
{
target.themeColor = resolveThemeColor(source.themeColor);
break;
}
case 'colorScheme':
target.colorScheme = source.colorScheme || null;
break;
default:
// always override the target with the source
// @ts-ignore viewport properties
target[key] = source[key];
break;
}
}
}
function getDefinedViewport(mod, props, tracingProps) {
if (typeof mod.generateViewport === 'function') {
const { route } = tracingProps;
return (parent)=>getTracer().trace(ResolveMetadataSpan.generateViewport, {
spanName: `generateViewport ${route}`,
attributes: {
'next.page': route
}
}, ()=>mod.generateViewport(props, parent));
}
return mod.viewport || null;
}
function getDefinedMetadata(mod, props, tracingProps) {
if (typeof mod.generateMetadata === 'function') {
const { route } = tracingProps;
return (parent)=>getTracer().trace(ResolveMetadataSpan.generateMetadata, {
spanName: `generateMetadata ${route}`,
attributes: {
'next.page': route
}
}, ()=>mod.generateMetadata(props, parent));
}
return mod.metadata || null;
}
async function collectStaticImagesFiles(metadata, props, type) {
var _this;
if (!(metadata == null ? void 0 : metadata[type])) return undefined;
const iconPromises = metadata[type].map(async (imageModule)=>interopDefault(await imageModule(props)));
return (iconPromises == null ? void 0 : iconPromises.length) > 0 ? (_this = await Promise.all(iconPromises)) == null ? void 0 : _this.flat() : undefined;
}
async function resolveStaticMetadata(modules, props) {
const { metadata } = modules;
if (!metadata) return null;
const [icon, apple, openGraph, twitter] = await Promise.all([
collectStaticImagesFiles(metadata, props, 'icon'),
collectStaticImagesFiles(metadata, props, 'apple'),
collectStaticImagesFiles(metadata, props, 'openGraph'),
collectStaticImagesFiles(metadata, props, 'twitter')
]);
const staticMetadata = {
icon,
apple,
openGraph,
twitter,
manifest: metadata.manifest
};
return staticMetadata;
}
// [layout.metadata, static files metadata] -> ... -> [page.metadata, static files metadata]
async function collectMetadata({ tree, metadataItems, errorMetadataItem, props, route, errorConvention }) {
let mod;
let modType;
const hasErrorConventionComponent = Boolean(errorConvention && tree[2][errorConvention]);
if (errorConvention) {
mod = await getComponentTypeModule(tree, 'layout');
modType = errorConvention;
} else {
const { mod: layoutOrPageMod, modType: layoutOrPageModType } = await getLayoutOrPageModule(tree);
mod = layoutOrPageMod;
modType = layoutOrPageModType;
}
if (modType) {
route += `/${modType}`;
}
const staticFilesMetadata = await resolveStaticMetadata(tree[2], props);
const metadataExport = mod ? getDefinedMetadata(mod, props, {
route
}) : null;
const viewportExport = mod ? getDefinedViewport(mod, props, {
route
}) : null;
metadataItems.push([
metadataExport,
staticFilesMetadata,
viewportExport
]);
if (hasErrorConventionComponent && errorConvention) {
const errorMod = await getComponentTypeModule(tree, errorConvention);
const errorViewportExport = errorMod ? getDefinedViewport(errorMod, props, {
route
}) : null;
const errorMetadataExport = errorMod ? getDefinedMetadata(errorMod, props, {
route
}) : null;
errorMetadataItem[0] = errorMetadataExport;
errorMetadataItem[1] = staticFilesMetadata;
errorMetadataItem[2] = errorViewportExport;
}
}
const resolveMetadataItems = cache(async function(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore) {
const parentParams = {};
const metadataItems = [];
const errorMetadataItem = [
null,
null,
null
];
const treePrefix = undefined;
return resolveMetadataItemsImpl(metadataItems, tree, treePrefix, parentParams, searchParams, errorConvention, errorMetadataItem, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
});
async function resolveMetadataItemsImpl(metadataItems, tree, /** Provided tree can be nested subtree, this argument says what is the path of such subtree */ treePrefix, parentParams, searchParams, errorConvention, errorMetadataItem, getDynamicParamFromSegment, createServerParamsForMetadata, workStore) {
const [segment, parallelRoutes, { page }] = tree;
const currentTreePrefix = treePrefix && treePrefix.length ? [
...treePrefix,
segment
] : [
segment
];
const isPage = typeof page !== 'undefined';
// Handle dynamic segment params.
const segmentParam = getDynamicParamFromSegment(segment);
/**
* Create object holding the parent params and current params
*/ let currentParams = parentParams;
if (segmentParam && segmentParam.value !== null) {
currentParams = {
...parentParams,
[segmentParam.param]: segmentParam.value
};
}
const params = createServerParamsForMetadata(currentParams, workStore);
let layerProps;
if (isPage) {
layerProps = {
params,
searchParams
};
} else {
layerProps = {
params
};
}
await collectMetadata({
tree,
metadataItems,
errorMetadataItem,
errorConvention,
props: layerProps,
route: currentTreePrefix// __PAGE__ shouldn't be shown in a route
.filter((s)=>s !== PAGE_SEGMENT_KEY).join('/')
});
for(const key in parallelRoutes){
const childTree = parallelRoutes[key];
await resolveMetadataItemsImpl(metadataItems, childTree, currentTreePrefix, currentParams, searchParams, errorConvention, errorMetadataItem, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
}
if (Object.keys(parallelRoutes).length === 0 && errorConvention) {
// If there are no parallel routes, place error metadata as the last item.
// e.g. layout -> layout -> not-found
metadataItems.push(errorMetadataItem);
}
return metadataItems;
}
const isTitleTruthy = (title)=>!!(title == null ? void 0 : title.absolute);
const hasTitle = (metadata)=>isTitleTruthy(metadata == null ? void 0 : metadata.title);
function inheritFromMetadata(target, metadata) {
if (target) {
if (!hasTitle(target) && hasTitle(metadata)) {
target.title = metadata.title;
}
if (!target.description && metadata.description) {
target.description = metadata.description;
}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const commonOgKeys = [
'title',
'description',
'images'
];
function postProcessMetadata(metadata, favicon, titleTemplates, metadataContext) {
const { openGraph, twitter } = metadata;
if (openGraph) {
// If there's openGraph information but not configured in twitter,
// inherit them from openGraph metadata.
let autoFillProps = {};
const hasTwTitle = hasTitle(twitter);
const hasTwDescription = twitter == null ? void 0 : twitter.description;
const hasTwImages = Boolean((twitter == null ? void 0 : twitter.hasOwnProperty('images')) && twitter.images);
if (!hasTwTitle) {
if (isTitleTruthy(openGraph.title)) {
autoFillProps.title = openGraph.title;
} else if (metadata.title && isTitleTruthy(metadata.title)) {
autoFillProps.title = metadata.title;
}
}
if (!hasTwDescription) autoFillProps.description = openGraph.description || metadata.description || undefined;
if (!hasTwImages) autoFillProps.images = openGraph.images;
if (Object.keys(autoFillProps).length > 0) {
const partialTwitter = resolveTwitter(autoFillProps, metadata.metadataBase, metadataContext, titleTemplates.twitter);
if (metadata.twitter) {
metadata.twitter = Object.assign({}, metadata.twitter, {
...!hasTwTitle && {
title: partialTwitter == null ? void 0 : partialTwitter.title
},
...!hasTwDescription && {
description: partialTwitter == null ? void 0 : partialTwitter.description
},
...!hasTwImages && {
images: partialTwitter == null ? void 0 : partialTwitter.images
}
});
} else {
metadata.twitter = partialTwitter;
}
}
}
// If there's no title and description configured in openGraph or twitter,
// use the title and description from metadata.
inheritFromMetadata(openGraph, metadata);
inheritFromMetadata(twitter, metadata);
if (favicon) {
if (!metadata.icons) {
metadata.icons = {
icon: [],
apple: []
};
}
metadata.icons.icon.unshift(favicon);
}
return metadata;
}
function collectMetadataExportPreloading(results, dynamicMetadataExportFn, resolvers) {
const result = dynamicMetadataExportFn(new Promise((resolve)=>{
resolvers.push(resolve);
}));
if (result instanceof Promise) {
// since we eager execute generateMetadata and
// they can reject at anytime we need to ensure
// we attach the catch handler right away to
// prevent unhandled rejections crashing the process
result.catch((err)=>{
return {
__nextError: err
};
});
}
results.push(result);
}
async function getMetadataFromExport(getPreloadMetadataExport, dynamicMetadataResolveState, metadataItems, currentIndex, resolvedMetadata, metadataResults) {
const metadataExport = getPreloadMetadataExport(metadataItems[currentIndex]);
const dynamicMetadataResolvers = dynamicMetadataResolveState.resolvers;
let metadata = null;
if (typeof metadataExport === 'function') {
// Only preload at the beginning when resolves are empty
if (!dynamicMetadataResolvers.length) {
for(let j = currentIndex; j < metadataItems.length; j++){
const preloadMetadataExport = getPreloadMetadataExport(metadataItems[j]);
// call each `generateMetadata function concurrently and stash their resolver
if (typeof preloadMetadataExport === 'function') {
collectMetadataExportPreloading(metadataResults, preloadMetadataExport, dynamicMetadataResolvers);
}
}
}
const resolveParent = dynamicMetadataResolvers[dynamicMetadataResolveState.resolvingIndex];
const metadataResult = metadataResults[dynamicMetadataResolveState.resolvingIndex++];
// In dev we clone and freeze to prevent relying on mutating resolvedMetadata directly.
// In prod we just pass resolvedMetadata through without any copying.
const currentResolvedMetadata = process.env.NODE_ENV === 'development' ? Object.freeze(require('./clone-metadata').cloneMetadata(resolvedMetadata)) : resolvedMetadata;
// This resolve should unblock the generateMetadata function if it awaited the parent
// argument. If it didn't await the parent argument it might already have a value since it was
// called concurrently. Regardless we await the return value before continuing on to the next layer
resolveParent(currentResolvedMetadata);
metadata = metadataResult instanceof Promise ? await metadataResult : metadataResult;
if (metadata && typeof metadata === 'object' && '__nextError' in metadata) {
// re-throw caught metadata error from preloading
throw metadata['__nextError'];
}
} else if (metadataExport !== null && typeof metadataExport === 'object') {
// This metadataExport is the object form
metadata = metadataExport;
}
return metadata;
}
export async function accumulateMetadata(metadataItems, metadataContext) {
const resolvedMetadata = createDefaultMetadata();
const metadataResults = [];
let titleTemplates = {
title: null,
twitter: null,
openGraph: null
};
// Loop over all metadata items again, merging synchronously any static object exports,
// awaiting any static promise exports, and resolving parent metadata and awaiting any generated metadata
const dynamicMetadataResolvers = {
resolvers: [],
resolvingIndex: 0
};
const buildState = {
warnings: new Set()
};
let favicon;
// Collect the static icons in the most leaf node,
// since we don't collect all the static metadata icons in the parent segments.
const leafSegmentStaticIcons = {
icon: [],
apple: []
};
for(let i = 0; i < metadataItems.length; i++){
var _staticFilesMetadata_icon;
const staticFilesMetadata = metadataItems[i][1];
// Treat favicon as special case, it should be the first icon in the list
// i <= 1 represents root layout, and if current page is also at root
if (i <= 1 && isFavicon(staticFilesMetadata == null ? void 0 : (_staticFilesMetadata_icon = staticFilesMetadata.icon) == null ? void 0 : _staticFilesMetadata_icon[0])) {
var _staticFilesMetadata_icon1;
const iconMod = staticFilesMetadata == null ? void 0 : (_staticFilesMetadata_icon1 = staticFilesMetadata.icon) == null ? void 0 : _staticFilesMetadata_icon1.shift();
if (i === 0) favicon = iconMod;
}
const metadata = await getMetadataFromExport((metadataItem)=>metadataItem[0], dynamicMetadataResolvers, metadataItems, i, resolvedMetadata, metadataResults);
mergeMetadata({
target: resolvedMetadata,
source: metadata,
metadataContext,
staticFilesMetadata,
titleTemplates,
buildState,
leafSegmentStaticIcons
});
// If the layout is the same layer with page, skip the leaf layout and leaf page
// The leaf layout and page are the last two items
if (i < metadataItems.length - 2) {
var _resolvedMetadata_title, _resolvedMetadata_openGraph, _resolvedMetadata_twitter;
titleTemplates = {
title: ((_resolvedMetadata_title = resolvedMetadata.title) == null ? void 0 : _resolvedMetadata_title.template) || null,
openGraph: ((_resolvedMetadata_openGraph = resolvedMetadata.openGraph) == null ? void 0 : _resolvedMetadata_openGraph.title.template) || null,
twitter: ((_resolvedMetadata_twitter = resolvedMetadata.twitter) == null ? void 0 : _resolvedMetadata_twitter.title.template) || null
};
}
}
if (leafSegmentStaticIcons.icon.length > 0 || leafSegmentStaticIcons.apple.length > 0) {
if (!resolvedMetadata.icons) {
resolvedMetadata.icons = {
icon: [],
apple: []
};
if (leafSegmentStaticIcons.icon.length > 0) {
resolvedMetadata.icons.icon.unshift(...leafSegmentStaticIcons.icon);
}
if (leafSegmentStaticIcons.apple.length > 0) {
resolvedMetadata.icons.apple.unshift(...leafSegmentStaticIcons.apple);
}
}
}
// Only log warnings if there are any, and only once after the metadata resolving process is finished
if (buildState.warnings.size > 0) {
for (const warning of buildState.warnings){
Log.warn(warning);
}
}
return postProcessMetadata(resolvedMetadata, favicon, titleTemplates, metadataContext);
}
export async function accumulateViewport(metadataItems) {
const resolvedViewport = createDefaultViewport();
const viewportResults = [];
const dynamicMetadataResolvers = {
resolvers: [],
resolvingIndex: 0
};
for(let i = 0; i < metadataItems.length; i++){
const viewport = await getMetadataFromExport((metadataItem)=>metadataItem[2], dynamicMetadataResolvers, metadataItems, i, resolvedViewport, viewportResults);
mergeViewport({
target: resolvedViewport,
source: viewport
});
}
return resolvedViewport;
}
// Exposed API for metadata component, that directly resolve the loader tree and related context as resolved metadata.
export async function resolveMetadata(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore, metadataContext) {
const metadataItems = await resolveMetadataItems(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
return accumulateMetadata(metadataItems, metadataContext);
}
// Exposed API for viewport component, that directly resolve the loader tree and related context as resolved viewport.
export async function resolveViewport(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore) {
const metadataItems = await resolveMetadataItems(tree, searchParams, errorConvention, getDynamicParamFromSegment, createServerParamsForMetadata, workStore);
return accumulateViewport(metadataItems);
}
//# sourceMappingURL=resolve-metadata.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,180 @@
import { resolveAsArrayOrUndefined } from '../generate/utils';
import { resolveAbsoluteUrlWithPathname } from './resolve-url';
function resolveAlternateUrl(url, metadataBase, metadataContext) {
// If alter native url is an URL instance,
// we treat it as a URL base and resolve with current pathname
if (url instanceof URL) {
const newUrl = new URL(metadataContext.pathname, url);
url.searchParams.forEach((value, key)=>newUrl.searchParams.set(key, value));
url = newUrl;
}
return resolveAbsoluteUrlWithPathname(url, metadataBase, metadataContext);
}
export const resolveThemeColor = (themeColor)=>{
var _resolveAsArrayOrUndefined;
if (!themeColor) return null;
const themeColorDescriptors = [];
(_resolveAsArrayOrUndefined = resolveAsArrayOrUndefined(themeColor)) == null ? void 0 : _resolveAsArrayOrUndefined.forEach((descriptor)=>{
if (typeof descriptor === 'string') themeColorDescriptors.push({
color: descriptor
});
else if (typeof descriptor === 'object') themeColorDescriptors.push({
color: descriptor.color,
media: descriptor.media
});
});
return themeColorDescriptors;
};
function resolveUrlValuesOfObject(obj, metadataBase, metadataContext) {
if (!obj) return null;
const result = {};
for (const [key, value] of Object.entries(obj)){
if (typeof value === 'string' || value instanceof URL) {
result[key] = [
{
url: resolveAlternateUrl(value, metadataBase, metadataContext)
}
];
} else {
result[key] = [];
value == null ? void 0 : value.forEach((item, index)=>{
const url = resolveAlternateUrl(item.url, metadataBase, metadataContext);
result[key][index] = {
url,
title: item.title
};
});
}
}
return result;
}
function resolveCanonicalUrl(urlOrDescriptor, metadataBase, metadataContext) {
if (!urlOrDescriptor) return null;
const url = typeof urlOrDescriptor === 'string' || urlOrDescriptor instanceof URL ? urlOrDescriptor : urlOrDescriptor.url;
// Return string url because structureClone can't handle URL instance
return {
url: resolveAlternateUrl(url, metadataBase, metadataContext)
};
}
export const resolveAlternates = (alternates, metadataBase, context)=>{
if (!alternates) return null;
const canonical = resolveCanonicalUrl(alternates.canonical, metadataBase, context);
const languages = resolveUrlValuesOfObject(alternates.languages, metadataBase, context);
const media = resolveUrlValuesOfObject(alternates.media, metadataBase, context);
const types = resolveUrlValuesOfObject(alternates.types, metadataBase, context);
const result = {
canonical,
languages,
media,
types
};
return result;
};
const robotsKeys = [
'noarchive',
'nosnippet',
'noimageindex',
'nocache',
'notranslate',
'indexifembedded',
'nositelinkssearchbox',
'unavailable_after',
'max-video-preview',
'max-image-preview',
'max-snippet'
];
const resolveRobotsValue = (robots)=>{
if (!robots) return null;
if (typeof robots === 'string') return robots;
const values = [];
if (robots.index) values.push('index');
else if (typeof robots.index === 'boolean') values.push('noindex');
if (robots.follow) values.push('follow');
else if (typeof robots.follow === 'boolean') values.push('nofollow');
for (const key of robotsKeys){
const value = robots[key];
if (typeof value !== 'undefined' && value !== false) {
values.push(typeof value === 'boolean' ? key : `${key}:${value}`);
}
}
return values.join(', ');
};
export const resolveRobots = (robots)=>{
if (!robots) return null;
return {
basic: resolveRobotsValue(robots),
googleBot: typeof robots !== 'string' ? resolveRobotsValue(robots.googleBot) : null
};
};
const VerificationKeys = [
'google',
'yahoo',
'yandex',
'me',
'other'
];
export const resolveVerification = (verification)=>{
if (!verification) return null;
const res = {};
for (const key of VerificationKeys){
const value = verification[key];
if (value) {
if (key === 'other') {
res.other = {};
for(const otherKey in verification.other){
const otherValue = resolveAsArrayOrUndefined(verification.other[otherKey]);
if (otherValue) res.other[otherKey] = otherValue;
}
} else res[key] = resolveAsArrayOrUndefined(value);
}
}
return res;
};
export const resolveAppleWebApp = (appWebApp)=>{
var _resolveAsArrayOrUndefined;
if (!appWebApp) return null;
if (appWebApp === true) {
return {
capable: true
};
}
const startupImages = appWebApp.startupImage ? (_resolveAsArrayOrUndefined = resolveAsArrayOrUndefined(appWebApp.startupImage)) == null ? void 0 : _resolveAsArrayOrUndefined.map((item)=>typeof item === 'string' ? {
url: item
} : item) : null;
return {
capable: 'capable' in appWebApp ? !!appWebApp.capable : true,
title: appWebApp.title || null,
startupImage: startupImages,
statusBarStyle: appWebApp.statusBarStyle || 'default'
};
};
export const resolveAppLinks = (appLinks)=>{
if (!appLinks) return null;
for(const key in appLinks){
// @ts-ignore // TODO: type infer
appLinks[key] = resolveAsArrayOrUndefined(appLinks[key]);
}
return appLinks;
};
export const resolveItunes = (itunes, metadataBase, context)=>{
if (!itunes) return null;
return {
appId: itunes.appId,
appArgument: itunes.appArgument ? resolveAlternateUrl(itunes.appArgument, metadataBase, context) : undefined
};
};
export const resolveFacebook = (facebook)=>{
if (!facebook) return null;
return {
appId: facebook.appId,
admins: resolveAsArrayOrUndefined(facebook.admins)
};
};
export const resolvePagination = (pagination, metadataBase, context)=>{
return {
previous: (pagination == null ? void 0 : pagination.previous) ? resolveAlternateUrl(pagination.previous, metadataBase, context) : null,
next: (pagination == null ? void 0 : pagination.next) ? resolveAlternateUrl(pagination.next, metadataBase, context) : null
};
};
//# sourceMappingURL=resolve-basics.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
import { resolveAsArrayOrUndefined } from '../generate/utils';
import { isStringOrURL } from './resolve-url';
import { IconKeys } from '../constants';
export function resolveIcon(icon) {
if (isStringOrURL(icon)) return {
url: icon
};
else if (Array.isArray(icon)) return icon;
return icon;
}
export const resolveIcons = (icons)=>{
if (!icons) {
return null;
}
const resolved = {
icon: [],
apple: []
};
if (Array.isArray(icons)) {
resolved.icon = icons.map(resolveIcon).filter(Boolean);
} else if (isStringOrURL(icons)) {
resolved.icon = [
resolveIcon(icons)
];
} else {
for (const key of IconKeys){
const values = resolveAsArrayOrUndefined(icons[key]);
if (values) resolved[key] = values.map(resolveIcon);
}
}
return resolved;
};
//# sourceMappingURL=resolve-icons.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/resolvers/resolve-icons.ts"],"sourcesContent":["import type { ResolvedMetadata } from '../types/metadata-interface'\nimport type { Icon, IconDescriptor } from '../types/metadata-types'\nimport type { FieldResolver } from '../types/resolvers'\nimport { resolveAsArrayOrUndefined } from '../generate/utils'\nimport { isStringOrURL } from './resolve-url'\nimport { IconKeys } from '../constants'\n\nexport function resolveIcon(icon: Icon): IconDescriptor {\n if (isStringOrURL(icon)) return { url: icon }\n else if (Array.isArray(icon)) return icon\n return icon\n}\n\nexport const resolveIcons: FieldResolver<'icons'> = (icons) => {\n if (!icons) {\n return null\n }\n\n const resolved: ResolvedMetadata['icons'] = {\n icon: [],\n apple: [],\n }\n if (Array.isArray(icons)) {\n resolved.icon = icons.map(resolveIcon).filter(Boolean)\n } else if (isStringOrURL(icons)) {\n resolved.icon = [resolveIcon(icons)]\n } else {\n for (const key of IconKeys) {\n const values = resolveAsArrayOrUndefined(icons[key])\n if (values) resolved[key] = values.map(resolveIcon)\n }\n }\n return resolved\n}\n"],"names":["resolveAsArrayOrUndefined","isStringOrURL","IconKeys","resolveIcon","icon","url","Array","isArray","resolveIcons","icons","resolved","apple","map","filter","Boolean","key","values"],"mappings":"AAGA,SAASA,yBAAyB,QAAQ,oBAAmB;AAC7D,SAASC,aAAa,QAAQ,gBAAe;AAC7C,SAASC,QAAQ,QAAQ,eAAc;AAEvC,OAAO,SAASC,YAAYC,IAAU;IACpC,IAAIH,cAAcG,OAAO,OAAO;QAAEC,KAAKD;IAAK;SACvC,IAAIE,MAAMC,OAAO,CAACH,OAAO,OAAOA;IACrC,OAAOA;AACT;AAEA,OAAO,MAAMI,eAAuC,CAACC;IACnD,IAAI,CAACA,OAAO;QACV,OAAO;IACT;IAEA,MAAMC,WAAsC;QAC1CN,MAAM,EAAE;QACRO,OAAO,EAAE;IACX;IACA,IAAIL,MAAMC,OAAO,CAACE,QAAQ;QACxBC,SAASN,IAAI,GAAGK,MAAMG,GAAG,CAACT,aAAaU,MAAM,CAACC;IAChD,OAAO,IAAIb,cAAcQ,QAAQ;QAC/BC,SAASN,IAAI,GAAG;YAACD,YAAYM;SAAO;IACtC,OAAO;QACL,KAAK,MAAMM,OAAOb,SAAU;YAC1B,MAAMc,SAAShB,0BAA0BS,KAAK,CAACM,IAAI;YACnD,IAAIC,QAAQN,QAAQ,CAACK,IAAI,GAAGC,OAAOJ,GAAG,CAACT;QACzC;IACF;IACA,OAAOO;AACT,EAAC"}

View File

@@ -0,0 +1,170 @@
import { resolveArray, resolveAsArrayOrUndefined } from '../generate/utils';
import { getSocialImageMetadataBaseFallback, isStringOrURL, resolveUrl, resolveAbsoluteUrlWithPathname } from './resolve-url';
import { resolveTitle } from './resolve-title';
import { isFullStringUrl } from '../../url';
import { warnOnce } from '../../../build/output/log';
const OgTypeFields = {
article: [
'authors',
'tags'
],
song: [
'albums',
'musicians'
],
playlist: [
'albums',
'musicians'
],
radio: [
'creators'
],
video: [
'actors',
'directors',
'writers',
'tags'
],
basic: [
'emails',
'phoneNumbers',
'faxNumbers',
'alternateLocale',
'audio',
'videos'
]
};
function resolveAndValidateImage(item, metadataBase, isStaticMetadataRouteFile) {
if (!item) return undefined;
const isItemUrl = isStringOrURL(item);
const inputUrl = isItemUrl ? item : item.url;
if (!inputUrl) return undefined;
// process.env.VERCEL is set to "1" when System Environment Variables are
// exposed. When exposed, validation is not necessary since we are falling back to
// process.env.VERCEL_PROJECT_PRODUCTION_URL, process.env.VERCEL_BRANCH_URL, or
// process.env.VERCEL_URL for the `metadataBase`. process.env.VERCEL is undefined
// when System Environment Variables are not exposed. When not exposed, we cannot
// detect in the build environment if the deployment is a Vercel deployment or not.
//
// x-ref: https://vercel.com/docs/projects/environment-variables/system-environment-variables#system-environment-variables
const isUsingVercelSystemEnvironmentVariables = Boolean(process.env.VERCEL);
const isRelativeUrl = typeof inputUrl === 'string' && !isFullStringUrl(inputUrl);
// When no explicit metadataBase is specified by the user, we'll override it with the fallback metadata
// under the following conditions:
// - The provided URL is relative (ie ./og-image).
// - The image is statically generated by Next.js (such as the special `opengraph-image` route)
// In both cases, we want to ensure that across all environments, the ogImage is a fully qualified URL.
// In the `opengraph-image` case, since the user isn't explicitly passing a relative path, this ensures
// the ogImage will be properly discovered across different environments without the user needing to
// have a bunch of `process.env` checks when defining their `metadataBase`.
if (isRelativeUrl && (!metadataBase || isStaticMetadataRouteFile)) {
const fallbackMetadataBase = getSocialImageMetadataBaseFallback(metadataBase);
// When not using Vercel environment variables for URL injection, we aren't able to determine
// a fallback value for `metadataBase`. For self-hosted setups, we want to warn
// about this since the only fallback we'll be able to generate is `localhost`.
// In development, we'll only warn for relative metadata that isn't part of the static
// metadata conventions (eg `opengraph-image`), as otherwise it's currently very noisy
// for common cases. Eventually we should remove this warning all together in favor of
// devtools.
const shouldWarn = !isUsingVercelSystemEnvironmentVariables && !metadataBase && (process.env.NODE_ENV === 'production' || !isStaticMetadataRouteFile);
if (shouldWarn) {
warnOnce(`metadataBase property in metadata export is not set for resolving social open graph or twitter images, using "${fallbackMetadataBase.origin}". See https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadatabase`);
}
metadataBase = fallbackMetadataBase;
}
return isItemUrl ? {
url: resolveUrl(inputUrl, metadataBase)
} : {
...item,
// Update image descriptor url
url: resolveUrl(inputUrl, metadataBase)
};
}
export function resolveImages(images, metadataBase, isStaticMetadataRouteFile) {
const resolvedImages = resolveAsArrayOrUndefined(images);
if (!resolvedImages) return resolvedImages;
const nonNullableImages = [];
for (const item of resolvedImages){
const resolvedItem = resolveAndValidateImage(item, metadataBase, isStaticMetadataRouteFile);
if (!resolvedItem) continue;
nonNullableImages.push(resolvedItem);
}
return nonNullableImages;
}
const ogTypeToFields = {
article: OgTypeFields.article,
book: OgTypeFields.article,
'music.song': OgTypeFields.song,
'music.album': OgTypeFields.song,
'music.playlist': OgTypeFields.playlist,
'music.radio_station': OgTypeFields.radio,
'video.movie': OgTypeFields.video,
'video.episode': OgTypeFields.video
};
function getFieldsByOgType(ogType) {
if (!ogType || !(ogType in ogTypeToFields)) return OgTypeFields.basic;
return ogTypeToFields[ogType].concat(OgTypeFields.basic);
}
export const resolveOpenGraph = (openGraph, metadataBase, metadataContext, titleTemplate)=>{
if (!openGraph) return null;
function resolveProps(target, og) {
const ogType = og && 'type' in og ? og.type : undefined;
const keys = getFieldsByOgType(ogType);
for (const k of keys){
const key = k;
if (key in og && key !== 'url') {
const value = og[key];
target[key] = value ? resolveArray(value) : null;
}
}
target.images = resolveImages(og.images, metadataBase, metadataContext.isStaticMetadataRouteFile);
}
const resolved = {
...openGraph,
title: resolveTitle(openGraph.title, titleTemplate)
};
resolveProps(resolved, openGraph);
resolved.url = openGraph.url ? resolveAbsoluteUrlWithPathname(openGraph.url, metadataBase, metadataContext) : null;
return resolved;
};
const TwitterBasicInfoKeys = [
'site',
'siteId',
'creator',
'creatorId',
'description'
];
export const resolveTwitter = (twitter, metadataBase, metadataContext, titleTemplate)=>{
var _resolved_images;
if (!twitter) return null;
let card = 'card' in twitter ? twitter.card : undefined;
const resolved = {
...twitter,
title: resolveTitle(twitter.title, titleTemplate)
};
for (const infoKey of TwitterBasicInfoKeys){
resolved[infoKey] = twitter[infoKey] || null;
}
resolved.images = resolveImages(twitter.images, metadataBase, metadataContext.isStaticMetadataRouteFile);
card = card || (((_resolved_images = resolved.images) == null ? void 0 : _resolved_images.length) ? 'summary_large_image' : 'summary');
resolved.card = card;
if ('card' in resolved) {
switch(resolved.card){
case 'player':
{
resolved.players = resolveAsArrayOrUndefined(resolved.players) || [];
break;
}
case 'app':
{
resolved.app = resolved.app || {};
break;
}
default:
break;
}
}
return resolved;
};
//# sourceMappingURL=resolve-opengraph.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
function resolveTitleTemplate(template, title) {
return template ? template.replace(/%s/g, title) : title;
}
export function resolveTitle(title, stashedTemplate) {
let resolved;
const template = typeof title !== 'string' && title && 'template' in title ? title.template : null;
if (typeof title === 'string') {
resolved = resolveTitleTemplate(stashedTemplate, title);
} else if (title) {
if ('default' in title) {
resolved = resolveTitleTemplate(stashedTemplate, title.default);
}
if ('absolute' in title && title.absolute) {
resolved = title.absolute;
}
}
if (title && typeof title !== 'string') {
return {
template,
absolute: resolved || ''
};
} else {
return {
absolute: resolved || title || '',
template
};
}
}
//# sourceMappingURL=resolve-title.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/resolvers/resolve-title.ts"],"sourcesContent":["import type { Metadata } from '../types/metadata-interface'\nimport type { AbsoluteTemplateString } from '../types/metadata-types'\n\nfunction resolveTitleTemplate(\n template: string | null | undefined,\n title: string\n) {\n return template ? template.replace(/%s/g, title) : title\n}\n\nexport function resolveTitle(\n title: Metadata['title'],\n stashedTemplate: string | null | undefined\n): AbsoluteTemplateString {\n let resolved\n const template =\n typeof title !== 'string' && title && 'template' in title\n ? title.template\n : null\n\n if (typeof title === 'string') {\n resolved = resolveTitleTemplate(stashedTemplate, title)\n } else if (title) {\n if ('default' in title) {\n resolved = resolveTitleTemplate(stashedTemplate, title.default)\n }\n if ('absolute' in title && title.absolute) {\n resolved = title.absolute\n }\n }\n\n if (title && typeof title !== 'string') {\n return {\n template,\n absolute: resolved || '',\n }\n } else {\n return { absolute: resolved || title || '', template }\n }\n}\n"],"names":["resolveTitleTemplate","template","title","replace","resolveTitle","stashedTemplate","resolved","default","absolute"],"mappings":"AAGA,SAASA,qBACPC,QAAmC,EACnCC,KAAa;IAEb,OAAOD,WAAWA,SAASE,OAAO,CAAC,OAAOD,SAASA;AACrD;AAEA,OAAO,SAASE,aACdF,KAAwB,EACxBG,eAA0C;IAE1C,IAAIC;IACJ,MAAML,WACJ,OAAOC,UAAU,YAAYA,SAAS,cAAcA,QAChDA,MAAMD,QAAQ,GACd;IAEN,IAAI,OAAOC,UAAU,UAAU;QAC7BI,WAAWN,qBAAqBK,iBAAiBH;IACnD,OAAO,IAAIA,OAAO;QAChB,IAAI,aAAaA,OAAO;YACtBI,WAAWN,qBAAqBK,iBAAiBH,MAAMK,OAAO;QAChE;QACA,IAAI,cAAcL,SAASA,MAAMM,QAAQ,EAAE;YACzCF,WAAWJ,MAAMM,QAAQ;QAC3B;IACF;IAEA,IAAIN,SAAS,OAAOA,UAAU,UAAU;QACtC,OAAO;YACLD;YACAO,UAAUF,YAAY;QACxB;IACF,OAAO;QACL,OAAO;YAAEE,UAAUF,YAAYJ,SAAS;YAAID;QAAS;IACvD;AACF"}

View File

@@ -0,0 +1,101 @@
import path from '../../../shared/lib/isomorphic/path';
function isStringOrURL(icon) {
return typeof icon === 'string' || icon instanceof URL;
}
function createLocalMetadataBase() {
return new URL(`http://localhost:${process.env.PORT || 3000}`);
}
function getPreviewDeploymentUrl() {
const origin = process.env.VERCEL_BRANCH_URL || process.env.VERCEL_URL;
return origin ? new URL(`https://${origin}`) : undefined;
}
function getProductionDeploymentUrl() {
const origin = process.env.VERCEL_PROJECT_PRODUCTION_URL;
return origin ? new URL(`https://${origin}`) : undefined;
}
/**
* Given an optional user-provided metadataBase, this determines what the metadataBase should
* fallback to. Specifically:
* - In dev, it should always be localhost
* - In Vercel preview builds, it should be the preview build ID
* - In start, it should be the user-provided metadataBase value. Otherwise,
* it'll fall back to the Vercel production deployment, and localhost as a last resort.
*/ export function getSocialImageMetadataBaseFallback(metadataBase) {
const defaultMetadataBase = createLocalMetadataBase();
const previewDeploymentUrl = getPreviewDeploymentUrl();
const productionDeploymentUrl = getProductionDeploymentUrl();
let fallbackMetadataBase;
if (process.env.NODE_ENV === 'development') {
fallbackMetadataBase = defaultMetadataBase;
} else {
fallbackMetadataBase = process.env.NODE_ENV === 'production' && previewDeploymentUrl && process.env.VERCEL_ENV === 'preview' ? previewDeploymentUrl : metadataBase || productionDeploymentUrl || defaultMetadataBase;
}
return fallbackMetadataBase;
}
function resolveUrl(url, metadataBase) {
if (url instanceof URL) return url;
if (!url) return null;
try {
// If we can construct a URL instance from url, ignore metadataBase
const parsedUrl = new URL(url);
return parsedUrl;
} catch {}
if (!metadataBase) {
metadataBase = createLocalMetadataBase();
}
// Handle relative or absolute paths
const pathname = metadataBase.pathname || '';
const joinedPath = path.posix.join(pathname, url);
return new URL(joinedPath, metadataBase);
}
// Resolve with `pathname` if `url` is a relative path.
function resolveRelativeUrl(url, pathname) {
if (typeof url === 'string' && url.startsWith('./')) {
return path.posix.resolve(pathname, url);
}
return url;
}
// The regex is matching logic from packages/next/src/lib/load-custom-routes.ts
const FILE_REGEX = /^(?:\/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+))(\/?|$)/i;
function isFilePattern(pathname) {
return FILE_REGEX.test(pathname);
}
// Resolve `pathname` if `url` is a relative path the compose with `metadataBase`.
function resolveAbsoluteUrlWithPathname(url, metadataBase, { trailingSlash, pathname }) {
// Resolve url with pathname that always starts with `/`
url = resolveRelativeUrl(url, pathname);
// Convert string url or URL instance to absolute url string,
// if there's case needs to be resolved with metadataBase
let resolvedUrl = '';
const result = metadataBase ? resolveUrl(url, metadataBase) : url;
if (typeof result === 'string') {
resolvedUrl = result;
} else {
resolvedUrl = result.pathname === '/' ? result.origin : result.href;
}
// Add trailing slash if it's enabled for urls matches the condition
// - Not external, same origin with metadataBase
// - Doesn't have query
if (trailingSlash && !resolvedUrl.endsWith('/')) {
let isRelative = resolvedUrl.startsWith('/');
let hasQuery = resolvedUrl.includes('?');
let isExternal = false;
let isFileUrl = false;
if (!isRelative) {
try {
const parsedUrl = new URL(resolvedUrl);
isExternal = metadataBase != null && parsedUrl.origin !== metadataBase.origin;
isFileUrl = isFilePattern(parsedUrl.pathname);
} catch {
// If it's not a valid URL, treat it as external
isExternal = true;
}
if (// Do not apply trailing slash for file like urls, aligning with the behavior with `trailingSlash`
!isFileUrl && !isExternal && !hasQuery) return `${resolvedUrl}/`;
}
}
return resolvedUrl;
}
export { isStringOrURL, resolveUrl, resolveRelativeUrl, resolveAbsoluteUrlWithPathname, };
//# sourceMappingURL=resolve-url.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
// Reference: https://hreflang.org/what-is-a-valid-hreflang
export { };
//# sourceMappingURL=alternative-urls-types.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
// When rendering applink meta tags add a namespace tag before each array instance
// if more than one member exists.
// ref: https://developers.facebook.com/docs/applinks/metadata-reference
// Format Detection
// This is a poorly specified metadata export type that is supposed to
// control whether the device attempts to conver text that matches
// certain formats into links for action. The most supported example
// is how mobile devices detect phone numbers and make them into links
// that can initiate a phone call
// https://www.goodemailcode.com/email-code/template.html
export { };
//# sourceMappingURL=extra-types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/types/extra-types.ts"],"sourcesContent":["// When rendering applink meta tags add a namespace tag before each array instance\n// if more than one member exists.\n// ref: https://developers.facebook.com/docs/applinks/metadata-reference\n\nexport type AppLinks = {\n ios?: AppLinksApple | Array<AppLinksApple> | undefined\n iphone?: AppLinksApple | Array<AppLinksApple> | undefined\n ipad?: AppLinksApple | Array<AppLinksApple> | undefined\n android?: AppLinksAndroid | Array<AppLinksAndroid> | undefined\n windows_phone?: AppLinksWindows | Array<AppLinksWindows> | undefined\n windows?: AppLinksWindows | Array<AppLinksWindows> | undefined\n windows_universal?: AppLinksWindows | Array<AppLinksWindows> | undefined\n web?: AppLinksWeb | Array<AppLinksWeb> | undefined\n}\nexport type ResolvedAppLinks = {\n ios?: Array<AppLinksApple> | undefined\n iphone?: Array<AppLinksApple> | undefined\n ipad?: Array<AppLinksApple> | undefined\n android?: Array<AppLinksAndroid> | undefined\n windows_phone?: Array<AppLinksWindows> | undefined\n windows?: Array<AppLinksWindows> | undefined\n windows_universal?: Array<AppLinksWindows> | undefined\n web?: Array<AppLinksWeb> | undefined\n}\nexport type AppLinksApple = {\n url: string | URL\n app_store_id?: string | number | undefined\n app_name?: string | undefined\n}\nexport type AppLinksAndroid = {\n package: string\n url?: string | URL | undefined\n class?: string | undefined\n app_name?: string | undefined\n}\nexport type AppLinksWindows = {\n url: string | URL\n app_id?: string | undefined\n app_name?: string | undefined\n}\nexport type AppLinksWeb = {\n url: string | URL\n should_fallback?: boolean | undefined\n}\n\n// Apple Itunes APp\n// https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners\nexport type ItunesApp = {\n appId: string\n appArgument?: string | undefined\n}\n\n// Viewport meta structure\n// https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag\n// intentionally leaving out user-scalable, use a string if you want that behavior\nexport type ViewportLayout = {\n width?: string | number | undefined\n height?: string | number | undefined\n initialScale?: number | undefined\n minimumScale?: number | undefined\n maximumScale?: number | undefined\n userScalable?: boolean | undefined\n viewportFit?: 'auto' | 'cover' | 'contain' | undefined\n interactiveWidget?:\n | 'resizes-visual'\n | 'resizes-content'\n | 'overlays-content'\n | undefined\n}\n\n// Apple Web App\n// https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html\n// https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html\nexport type AppleWebApp = {\n // default true\n capable?: boolean | undefined\n title?: string | undefined\n startupImage?: AppleImage | Array<AppleImage> | undefined\n // default \"default\"\n statusBarStyle?: 'default' | 'black' | 'black-translucent' | undefined\n}\nexport type AppleImage = string | AppleImageDescriptor\nexport type AppleImageDescriptor = {\n url: string\n media?: string | undefined\n}\n\nexport type ResolvedAppleWebApp = {\n capable: boolean\n title?: string | null | undefined\n startupImage?: AppleImageDescriptor[] | null | undefined\n statusBarStyle?: 'default' | 'black' | 'black-translucent' | undefined\n}\n\nexport type Facebook = FacebookAppId | FacebookAdmins\nexport type FacebookAppId = {\n appId: string\n admins?: never | undefined\n}\nexport type FacebookAdmins = {\n appId?: never | undefined\n admins: string | string[]\n}\nexport type ResolvedFacebook = {\n appId?: string | undefined\n admins?: string[] | undefined\n}\n\n// Format Detection\n// This is a poorly specified metadata export type that is supposed to\n// control whether the device attempts to conver text that matches\n// certain formats into links for action. The most supported example\n// is how mobile devices detect phone numbers and make them into links\n// that can initiate a phone call\n// https://www.goodemailcode.com/email-code/template.html\nexport type FormatDetection = {\n telephone?: boolean | undefined\n date?: boolean | undefined\n address?: boolean | undefined\n email?: boolean | undefined\n url?: boolean | undefined\n}\n"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,kCAAkC;AAClC,wEAAwE;AA0GxE,mBAAmB;AACnB,sEAAsE;AACtE,kEAAkE;AAClE,oEAAoE;AACpE,sEAAsE;AACtE,iCAAiC;AACjC,yDAAyD;AACzD,WAMC"}

View File

@@ -0,0 +1,3 @@
export { };
//# sourceMappingURL=icons.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/types/icons.ts"],"sourcesContent":["export type StaticMetadata = {\n icon: any[] | undefined\n apple: any[] | undefined\n openGraph: any[] | undefined\n twitter: any[] | undefined\n manifest: string | undefined\n} | null\n"],"names":[],"mappings":"AAAA,WAMQ"}

View File

@@ -0,0 +1,3 @@
export { };
//# sourceMappingURL=manifest-types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/types/manifest-types.ts"],"sourcesContent":["type ClientModeEnum =\n | 'auto'\n | 'focus-existing'\n | 'navigate-existing'\n | 'navigate-new'\n\ntype File = {\n name: string\n accept: string | string[]\n}\n\ntype Icon = {\n src: string\n type?: string | undefined\n sizes?: string | undefined\n purpose?: 'any' | 'maskable' | 'monochrome' | undefined\n}\n\nexport type Manifest = {\n background_color?: string | undefined\n categories?: string[] | undefined\n description?: string | undefined\n dir?: 'ltr' | 'rtl' | 'auto' | undefined\n display?: 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser' | undefined\n display_override?:\n | (\n | 'fullscreen'\n | 'standalone'\n | 'minimal-ui'\n | 'browser'\n | 'window-controls-overlay'\n )[]\n | undefined\n file_handlers?:\n | {\n action: string\n accept: {\n [mimeType: string]: string[]\n }\n }[]\n | undefined\n icons?: Icon[] | undefined\n id?: string | undefined\n lang?: string | undefined\n launch_handler?:\n | {\n client_mode: ClientModeEnum | ClientModeEnum[]\n }\n | undefined\n name?: string | undefined\n orientation?:\n | 'any'\n | 'natural'\n | 'landscape'\n | 'portrait'\n | 'portrait-primary'\n | 'portrait-secondary'\n | 'landscape-primary'\n | 'landscape-secondary'\n | undefined\n prefer_related_applications?: boolean | undefined\n protocol_handlers?:\n | {\n protocol: string\n url: string\n }[]\n | undefined\n related_applications?:\n | {\n platform: string\n url: string\n id?: string | undefined\n }[]\n | undefined\n scope?: string | undefined\n screenshots?:\n | {\n form_factor?: 'narrow' | 'wide' | undefined\n label?: string | undefined\n platform?:\n | 'android'\n | 'chromeos'\n | 'ipados'\n | 'ios'\n | 'kaios'\n | 'macos'\n | 'windows'\n | 'xbox'\n | 'chrome_web_store'\n | 'itunes'\n | 'microsoft-inbox'\n | 'microsoft-store'\n | 'play'\n | undefined\n src: string\n type?: string | undefined\n sizes?: string | undefined\n }[]\n | undefined\n share_target?:\n | {\n action: string\n method?: 'get' | 'post' | 'GET' | 'POST' | undefined\n enctype?:\n | 'application/x-www-form-urlencoded'\n | 'multipart/form-data'\n | undefined\n params: {\n title?: string | undefined\n text?: string | undefined\n url?: string | undefined\n files?: File | File[] | undefined\n }\n }\n | undefined\n short_name?: string | undefined\n shortcuts?:\n | {\n name: string\n short_name?: string | undefined\n description?: string | undefined\n url: string\n icons?: Icon[] | undefined\n }[]\n | undefined\n start_url?: string | undefined\n theme_color?: string | undefined\n}\n"],"names":[],"mappings":"AAkBA,WA6GC"}

View File

@@ -0,0 +1,15 @@
/**
* Next.js Metadata API
*
* This file defines the types used by Next.js to configure metadata
* through static exports or dynamic `generateMetadata` functions in Server Components.
*
* @remarks
* - The static `metadata` object and `generateMetadata` function are only supported in Server Components.
* - Do not export both a `metadata` object and a `generateMetadata` function from the same route segment.
* - You can still render metadata in client components directly as part of the component's JSX.
*
* @see https://nextjs.org/docs/app/api-reference/metadata
*/ export { };
//# sourceMappingURL=metadata-interface.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
/**
*
* Metadata types
*
*/ export { };
//# sourceMappingURL=metadata-types.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export { };
//# sourceMappingURL=opengraph-types.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export { };
//# sourceMappingURL=resolvers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/types/resolvers.ts"],"sourcesContent":["import type { Metadata, ResolvedMetadata } from './metadata-interface'\n\nexport type FieldResolver<\n Key extends keyof Data & keyof ResolvedData,\n Data = Metadata,\n ResolvedData = ResolvedMetadata,\n> = (T: Data[Key]) => ResolvedData[Key]\n\nexport type FieldResolverExtraArgs<\n Key extends keyof Data & keyof ResolvedData,\n ExtraArgs extends unknown[] = any[],\n Data = Metadata,\n ResolvedData = ResolvedMetadata,\n> = (T: Data[Key], ...args: ExtraArgs) => ResolvedData[Key]\n\nexport type MetadataContext = {\n pathname: string\n trailingSlash: boolean\n isStaticMetadataRouteFile: boolean\n}\n"],"names":[],"mappings":"AAeA,WAIC"}

View File

@@ -0,0 +1,4 @@
// Reference: https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
export { };
//# sourceMappingURL=twitter-types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/lib/metadata/types/twitter-types.ts"],"sourcesContent":["// Reference: https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup\n\nimport type { AbsoluteTemplateString, TemplateString } from './metadata-types'\n\nexport type Twitter =\n | TwitterSummary\n | TwitterSummaryLargeImage\n | TwitterPlayer\n | TwitterApp\n | TwitterMetadata\n\ntype TwitterMetadata = {\n // defaults to card=\"summary\"\n site?: string | undefined // username for account associated to the site itself\n siteId?: string | undefined // id for account associated to the site itself\n creator?: string | undefined // username for the account associated to the creator of the content on the site\n creatorId?: string | undefined // id for the account associated to the creator of the content on the site\n description?: string | undefined\n title?: string | TemplateString | undefined\n images?: TwitterImage | Array<TwitterImage> | undefined\n}\ntype TwitterSummary = TwitterMetadata & {\n card: 'summary'\n}\ntype TwitterSummaryLargeImage = TwitterMetadata & {\n card: 'summary_large_image'\n}\ntype TwitterPlayer = TwitterMetadata & {\n card: 'player'\n players: TwitterPlayerDescriptor | Array<TwitterPlayerDescriptor>\n}\ntype TwitterApp = TwitterMetadata & {\n card: 'app'\n app: TwitterAppDescriptor\n}\nexport type TwitterAppDescriptor = {\n id: {\n iphone?: string | number | undefined\n ipad?: string | number | undefined\n googleplay?: string | undefined\n }\n url?:\n | {\n iphone?: string | URL | undefined\n ipad?: string | URL | undefined\n googleplay?: string | URL | undefined\n }\n | undefined\n name?: string | undefined\n}\n\ntype TwitterImage = string | TwitterImageDescriptor | URL\ntype TwitterImageDescriptor = {\n url: string | URL\n alt?: string | undefined\n secureUrl?: string | URL | undefined\n type?: string | undefined\n width?: string | number | undefined\n height?: string | number | undefined\n}\ntype TwitterPlayerDescriptor = {\n playerUrl: string | URL\n streamUrl: string | URL\n width: number\n height: number\n}\n\ntype ResolvedTwitterImage = {\n url: string | URL\n alt?: string | undefined\n secureUrl?: string | URL | undefined\n type?: string | undefined\n width?: string | number | undefined\n height?: string | number | undefined\n}\ntype ResolvedTwitterSummary = {\n site: string | null\n siteId: string | null\n creator: string | null\n creatorId: string | null\n description: string | null\n title: AbsoluteTemplateString\n images?: Array<ResolvedTwitterImage> | undefined\n}\ntype ResolvedTwitterPlayer = ResolvedTwitterSummary & {\n players: Array<TwitterPlayerDescriptor>\n}\ntype ResolvedTwitterApp = ResolvedTwitterSummary & { app: TwitterAppDescriptor }\n\nexport type ResolvedTwitterMetadata =\n | ({ card: 'summary' } & ResolvedTwitterSummary)\n | ({ card: 'summary_large_image' } & ResolvedTwitterSummary)\n | ({ card: 'player' } & ResolvedTwitterPlayer)\n | ({ card: 'app' } & ResolvedTwitterApp)\n"],"names":[],"mappings":"AAAA,8FAA8F;AAyF9F,WAI0C"}