- Introduced blog routing using Next.js App Router - Implemented dynamic [slug] pages for blog posts - Added MDX-based content loading via lib/posts - Integrated shared TopBar layout with navigation - Established clear content, lib and component separation
859 lines
36 KiB
JavaScript
859 lines
36 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
accumulateMetadata: null,
|
|
accumulateViewport: null,
|
|
resolveMetadata: null,
|
|
resolveViewport: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
accumulateMetadata: function() {
|
|
return accumulateMetadata;
|
|
},
|
|
accumulateViewport: function() {
|
|
return accumulateViewport;
|
|
},
|
|
resolveMetadata: function() {
|
|
return resolveMetadata;
|
|
},
|
|
resolveViewport: function() {
|
|
return resolveViewport;
|
|
}
|
|
});
|
|
require("server-only");
|
|
const _react = require("react");
|
|
const _defaultmetadata = require("./default-metadata");
|
|
const _resolveopengraph = require("./resolvers/resolve-opengraph");
|
|
const _resolvetitle = require("./resolvers/resolve-title");
|
|
const _utils = require("./generate/utils");
|
|
const _appdirmodule = require("../../server/lib/app-dir-module");
|
|
const _interopdefault = require("../interop-default");
|
|
const _resolvebasics = require("./resolvers/resolve-basics");
|
|
const _resolveicons = require("./resolvers/resolve-icons");
|
|
const _tracer = require("../../server/lib/trace/tracer");
|
|
const _constants = require("../../server/lib/trace/constants");
|
|
const _segment = require("../../shared/lib/segment");
|
|
const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../build/output/log"));
|
|
const _params = require("../../server/request/params");
|
|
const _clientandserverreferences = require("../client-and-server-references");
|
|
const _lazyresult = require("../../server/lib/lazy-result");
|
|
function _getRequireWildcardCache(nodeInterop) {
|
|
if (typeof WeakMap !== "function") return null;
|
|
var cacheBabelInterop = new WeakMap();
|
|
var cacheNodeInterop = new WeakMap();
|
|
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
})(nodeInterop);
|
|
}
|
|
function _interop_require_wildcard(obj, nodeInterop) {
|
|
if (!nodeInterop && obj && obj.__esModule) {
|
|
return obj;
|
|
}
|
|
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
return {
|
|
default: obj
|
|
};
|
|
}
|
|
var cache = _getRequireWildcardCache(nodeInterop);
|
|
if (cache && cache.has(obj)) {
|
|
return cache.get(obj);
|
|
}
|
|
var newObj = {
|
|
__proto__: null
|
|
};
|
|
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
for(var key in obj){
|
|
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
if (desc && (desc.get || desc.set)) {
|
|
Object.defineProperty(newObj, key, desc);
|
|
} else {
|
|
newObj[key] = obj[key];
|
|
}
|
|
}
|
|
}
|
|
newObj.default = obj;
|
|
if (cache) {
|
|
cache.set(obj, newObj);
|
|
}
|
|
return newObj;
|
|
}
|
|
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 convertUrlsToStrings(input) {
|
|
if (input instanceof URL) {
|
|
return input.toString();
|
|
} else if (Array.isArray(input)) {
|
|
return input.map((item)=>convertUrlsToStrings(item));
|
|
} else if (input && typeof input === 'object') {
|
|
const result = {};
|
|
for (const [key, value] of Object.entries(input)){
|
|
result[key] = convertUrlsToStrings(value);
|
|
}
|
|
return result;
|
|
}
|
|
return input;
|
|
}
|
|
function normalizeMetadataBase(metadataBase) {
|
|
if (typeof metadataBase === 'string') {
|
|
try {
|
|
metadataBase = new URL(metadataBase);
|
|
} catch {
|
|
throw Object.defineProperty(new Error(`metadataBase is not a valid URL: ${metadataBase}`), "__NEXT_ERROR_CODE", {
|
|
value: "E850",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
return metadataBase;
|
|
}
|
|
async function mergeStaticMetadata(metadataBase, source, target, staticFilesMetadata, metadataContext, titleTemplates, leafSegmentStaticIcons, pathname) {
|
|
var _source_twitter, _source_openGraph;
|
|
if (!staticFilesMetadata) return target;
|
|
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 = (0, _resolveopengraph.resolveTwitter)({
|
|
...target.twitter,
|
|
images: twitter
|
|
}, metadataBase, {
|
|
...metadataContext,
|
|
isStaticMetadataRouteFile: true
|
|
}, titleTemplates.twitter);
|
|
target.twitter = convertUrlsToStrings(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 = await (0, _resolveopengraph.resolveOpenGraph)({
|
|
...target.openGraph,
|
|
images: openGraph
|
|
}, metadataBase, pathname, {
|
|
...metadataContext,
|
|
isStaticMetadataRouteFile: true
|
|
}, titleTemplates.openGraph);
|
|
target.openGraph = convertUrlsToStrings(resolvedOpenGraph);
|
|
}
|
|
if (manifest) {
|
|
target.manifest = manifest;
|
|
}
|
|
return target;
|
|
}
|
|
/**
|
|
* Merges the given metadata with the resolved metadata. Returns a new object.
|
|
*/ async function mergeMetadata(route, pathname, { metadata, resolvedMetadata, staticFilesMetadata, titleTemplates, metadataContext, buildState, leafSegmentStaticIcons }) {
|
|
const newResolvedMetadata = structuredClone(resolvedMetadata);
|
|
const metadataBase = normalizeMetadataBase((metadata == null ? void 0 : metadata.metadataBase) !== undefined ? metadata.metadataBase : resolvedMetadata.metadataBase);
|
|
for(const key_ in metadata){
|
|
const key = key_;
|
|
switch(key){
|
|
case 'title':
|
|
{
|
|
newResolvedMetadata.title = (0, _resolvetitle.resolveTitle)(metadata.title, titleTemplates.title);
|
|
break;
|
|
}
|
|
case 'alternates':
|
|
{
|
|
newResolvedMetadata.alternates = convertUrlsToStrings(await (0, _resolvebasics.resolveAlternates)(metadata.alternates, metadataBase, pathname, metadataContext));
|
|
break;
|
|
}
|
|
case 'openGraph':
|
|
{
|
|
newResolvedMetadata.openGraph = convertUrlsToStrings(await (0, _resolveopengraph.resolveOpenGraph)(metadata.openGraph, metadataBase, pathname, metadataContext, titleTemplates.openGraph));
|
|
break;
|
|
}
|
|
case 'twitter':
|
|
{
|
|
newResolvedMetadata.twitter = convertUrlsToStrings((0, _resolveopengraph.resolveTwitter)(metadata.twitter, metadataBase, metadataContext, titleTemplates.twitter));
|
|
break;
|
|
}
|
|
case 'facebook':
|
|
newResolvedMetadata.facebook = (0, _resolvebasics.resolveFacebook)(metadata.facebook);
|
|
break;
|
|
case 'verification':
|
|
newResolvedMetadata.verification = (0, _resolvebasics.resolveVerification)(metadata.verification);
|
|
break;
|
|
case 'icons':
|
|
{
|
|
newResolvedMetadata.icons = convertUrlsToStrings((0, _resolveicons.resolveIcons)(metadata.icons));
|
|
break;
|
|
}
|
|
case 'appleWebApp':
|
|
newResolvedMetadata.appleWebApp = (0, _resolvebasics.resolveAppleWebApp)(metadata.appleWebApp);
|
|
break;
|
|
case 'appLinks':
|
|
newResolvedMetadata.appLinks = convertUrlsToStrings((0, _resolvebasics.resolveAppLinks)(metadata.appLinks));
|
|
break;
|
|
case 'robots':
|
|
{
|
|
newResolvedMetadata.robots = (0, _resolvebasics.resolveRobots)(metadata.robots);
|
|
break;
|
|
}
|
|
case 'archives':
|
|
case 'assets':
|
|
case 'bookmarks':
|
|
case 'keywords':
|
|
{
|
|
newResolvedMetadata[key] = (0, _utils.resolveAsArrayOrUndefined)(metadata[key]);
|
|
break;
|
|
}
|
|
case 'authors':
|
|
{
|
|
newResolvedMetadata[key] = convertUrlsToStrings((0, _utils.resolveAsArrayOrUndefined)(metadata.authors));
|
|
break;
|
|
}
|
|
case 'itunes':
|
|
{
|
|
newResolvedMetadata[key] = await (0, _resolvebasics.resolveItunes)(metadata.itunes, metadataBase, pathname, metadataContext);
|
|
break;
|
|
}
|
|
case 'pagination':
|
|
{
|
|
newResolvedMetadata.pagination = await (0, _resolvebasics.resolvePagination)(metadata.pagination, metadataBase, pathname, metadataContext);
|
|
break;
|
|
}
|
|
// directly assign fields that fallback to null
|
|
case 'abstract':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'applicationName':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'description':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'generator':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'creator':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'publisher':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'category':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'classification':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'referrer':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'formatDetection':
|
|
newResolvedMetadata[key] = metadata[key] ?? null;
|
|
break;
|
|
case 'manifest':
|
|
newResolvedMetadata[key] = convertUrlsToStrings(metadata[key]) ?? null;
|
|
break;
|
|
case 'pinterest':
|
|
newResolvedMetadata[key] = convertUrlsToStrings(metadata[key]) ?? null;
|
|
break;
|
|
case 'other':
|
|
newResolvedMetadata.other = Object.assign({}, newResolvedMetadata.other, metadata.other);
|
|
break;
|
|
case 'metadataBase':
|
|
newResolvedMetadata.metadataBase = metadataBase ? metadataBase.toString() : null;
|
|
break;
|
|
case 'apple-touch-fullscreen':
|
|
{
|
|
buildState.warnings.add(`Use appleWebApp instead\nRead more: https://nextjs.org/docs/app/api-reference/functions/generate-metadata`);
|
|
break;
|
|
}
|
|
case 'apple-touch-icon-precomposed':
|
|
{
|
|
buildState.warnings.add(`Use icons.apple instead\nRead more: https://nextjs.org/docs/app/api-reference/functions/generate-metadata`);
|
|
break;
|
|
}
|
|
case 'themeColor':
|
|
case 'colorScheme':
|
|
case 'viewport':
|
|
if (metadata[key] != null) {
|
|
buildState.warnings.add(`Unsupported metadata ${key} is configured in metadata export in ${route}. Please move it to viewport export instead.\nRead more: https://nextjs.org/docs/app/api-reference/functions/generate-viewport`);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
key;
|
|
}
|
|
}
|
|
}
|
|
return mergeStaticMetadata(metadataBase, metadata, newResolvedMetadata, staticFilesMetadata, metadataContext, titleTemplates, leafSegmentStaticIcons, pathname);
|
|
}
|
|
/**
|
|
* Merges the given viewport with the resolved viewport. Returns a new object.
|
|
*/ function mergeViewport({ resolvedViewport, viewport }) {
|
|
const newResolvedViewport = structuredClone(resolvedViewport);
|
|
if (viewport) {
|
|
for(const key_ in viewport){
|
|
const key = key_;
|
|
switch(key){
|
|
case 'themeColor':
|
|
{
|
|
newResolvedViewport.themeColor = (0, _resolvebasics.resolveThemeColor)(viewport.themeColor);
|
|
break;
|
|
}
|
|
case 'colorScheme':
|
|
newResolvedViewport.colorScheme = viewport.colorScheme || null;
|
|
break;
|
|
case 'width':
|
|
case 'height':
|
|
case 'initialScale':
|
|
case 'minimumScale':
|
|
case 'maximumScale':
|
|
case 'userScalable':
|
|
case 'viewportFit':
|
|
case 'interactiveWidget':
|
|
// always override the target with the source
|
|
// @ts-ignore viewport properties
|
|
newResolvedViewport[key] = viewport[key];
|
|
break;
|
|
default:
|
|
key;
|
|
}
|
|
}
|
|
}
|
|
return newResolvedViewport;
|
|
}
|
|
function getDefinedViewport(mod, props, tracingProps) {
|
|
if (typeof mod.generateViewport === 'function') {
|
|
const { route } = tracingProps;
|
|
const segmentProps = createSegmentProps(mod.generateViewport, props);
|
|
return Object.assign((parent)=>(0, _tracer.getTracer)().trace(_constants.ResolveMetadataSpan.generateViewport, {
|
|
spanName: `generateViewport ${route}`,
|
|
attributes: {
|
|
'next.page': route
|
|
}
|
|
}, ()=>mod.generateViewport(segmentProps, parent)), {
|
|
$$original: mod.generateViewport
|
|
});
|
|
}
|
|
return mod.viewport || null;
|
|
}
|
|
function getDefinedMetadata(mod, props, tracingProps) {
|
|
if (typeof mod.generateMetadata === 'function') {
|
|
const { route } = tracingProps;
|
|
const segmentProps = createSegmentProps(mod.generateMetadata, props);
|
|
return Object.assign((parent)=>(0, _tracer.getTracer)().trace(_constants.ResolveMetadataSpan.generateMetadata, {
|
|
spanName: `generateMetadata ${route}`,
|
|
attributes: {
|
|
'next.page': route
|
|
}
|
|
}, ()=>mod.generateMetadata(segmentProps, parent)), {
|
|
$$original: mod.generateMetadata
|
|
});
|
|
}
|
|
return mod.metadata || null;
|
|
}
|
|
/**
|
|
* If `fn` is a `'use cache'` function, we add special markers to the props,
|
|
* that the cache wrapper reads and removes, before passing the props to the
|
|
* user function.
|
|
*/ function createSegmentProps(fn, props) {
|
|
return (0, _clientandserverreferences.isUseCacheFunction)(fn) ? 'searchParams' in props ? {
|
|
...props,
|
|
$$isPage: true
|
|
} : {
|
|
...props,
|
|
$$isLayout: true
|
|
} : props;
|
|
}
|
|
async function collectStaticImagesFiles(metadata, props, type) {
|
|
var _this;
|
|
if (!(metadata == null ? void 0 : metadata[type])) return undefined;
|
|
const iconPromises = metadata[type].map(async (imageModule)=>(0, _interopdefault.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 (0, _appdirmodule.getComponentTypeModule)(tree, 'layout');
|
|
modType = errorConvention;
|
|
} else {
|
|
const { mod: layoutOrPageMod, modType: layoutOrPageModType } = await (0, _appdirmodule.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;
|
|
metadataItems.push([
|
|
metadataExport,
|
|
staticFilesMetadata
|
|
]);
|
|
if (hasErrorConventionComponent && errorConvention) {
|
|
const errorMod = await (0, _appdirmodule.getComponentTypeModule)(tree, errorConvention);
|
|
const errorMetadataExport = errorMod ? getDefinedMetadata(errorMod, props, {
|
|
route
|
|
}) : null;
|
|
errorMetadataItem[0] = errorMetadataExport;
|
|
errorMetadataItem[1] = staticFilesMetadata;
|
|
}
|
|
}
|
|
// [layout.metadata, static files metadata] -> ... -> [page.metadata, static files metadata]
|
|
async function collectViewport({ tree, viewportItems, errorViewportItemRef, props, route, errorConvention }) {
|
|
let mod;
|
|
let modType;
|
|
const hasErrorConventionComponent = Boolean(errorConvention && tree[2][errorConvention]);
|
|
if (errorConvention) {
|
|
mod = await (0, _appdirmodule.getComponentTypeModule)(tree, 'layout');
|
|
modType = errorConvention;
|
|
} else {
|
|
const { mod: layoutOrPageMod, modType: layoutOrPageModType } = await (0, _appdirmodule.getLayoutOrPageModule)(tree);
|
|
mod = layoutOrPageMod;
|
|
modType = layoutOrPageModType;
|
|
}
|
|
if (modType) {
|
|
route += `/${modType}`;
|
|
}
|
|
const viewportExport = mod ? getDefinedViewport(mod, props, {
|
|
route
|
|
}) : null;
|
|
viewportItems.push(viewportExport);
|
|
if (hasErrorConventionComponent && errorConvention) {
|
|
const errorMod = await (0, _appdirmodule.getComponentTypeModule)(tree, errorConvention);
|
|
const errorViewportExport = errorMod ? getDefinedViewport(errorMod, props, {
|
|
route
|
|
}) : null;
|
|
errorViewportItemRef.current = errorViewportExport;
|
|
}
|
|
}
|
|
const resolveMetadataItems = (0, _react.cache)(async function(tree, searchParams, errorConvention, getDynamicParamFromSegment, workStore) {
|
|
const parentParams = {};
|
|
const metadataItems = [];
|
|
const errorMetadataItem = [
|
|
null,
|
|
null
|
|
];
|
|
const treePrefix = undefined;
|
|
return resolveMetadataItemsImpl(metadataItems, tree, treePrefix, parentParams, searchParams, errorConvention, errorMetadataItem, getDynamicParamFromSegment, 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, 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 = (0, _params.createServerParamsForMetadata)(currentParams, workStore);
|
|
const props = isPage ? {
|
|
params,
|
|
searchParams
|
|
} : {
|
|
params
|
|
};
|
|
await collectMetadata({
|
|
tree,
|
|
metadataItems,
|
|
errorMetadataItem,
|
|
errorConvention,
|
|
props,
|
|
route: currentTreePrefix// __PAGE__ shouldn't be shown in a route
|
|
.filter((s)=>s !== _segment.PAGE_SEGMENT_KEY).join('/')
|
|
});
|
|
for(const key in parallelRoutes){
|
|
const childTree = parallelRoutes[key];
|
|
await resolveMetadataItemsImpl(metadataItems, childTree, currentTreePrefix, currentParams, searchParams, errorConvention, errorMetadataItem, getDynamicParamFromSegment, 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 resolveViewportItems = (0, _react.cache)(async function(tree, searchParams, errorConvention, getDynamicParamFromSegment, workStore) {
|
|
const parentParams = {};
|
|
const viewportItems = [];
|
|
const errorViewportItemRef = {
|
|
current: null
|
|
};
|
|
const treePrefix = undefined;
|
|
return resolveViewportItemsImpl(viewportItems, tree, treePrefix, parentParams, searchParams, errorConvention, errorViewportItemRef, getDynamicParamFromSegment, workStore);
|
|
});
|
|
async function resolveViewportItemsImpl(viewportItems, tree, /** Provided tree can be nested subtree, this argument says what is the path of such subtree */ treePrefix, parentParams, searchParams, errorConvention, errorViewportItemRef, getDynamicParamFromSegment, 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 = (0, _params.createServerParamsForMetadata)(currentParams, workStore);
|
|
let layerProps;
|
|
if (isPage) {
|
|
layerProps = {
|
|
params,
|
|
searchParams
|
|
};
|
|
} else {
|
|
layerProps = {
|
|
params
|
|
};
|
|
}
|
|
await collectViewport({
|
|
tree,
|
|
viewportItems,
|
|
errorViewportItemRef,
|
|
errorConvention,
|
|
props: layerProps,
|
|
route: currentTreePrefix// __PAGE__ shouldn't be shown in a route
|
|
.filter((s)=>s !== _segment.PAGE_SEGMENT_KEY).join('/')
|
|
});
|
|
for(const key in parallelRoutes){
|
|
const childTree = parallelRoutes[key];
|
|
await resolveViewportItemsImpl(viewportItems, childTree, currentTreePrefix, currentParams, searchParams, errorConvention, errorViewportItemRef, getDynamicParamFromSegment, 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
|
|
viewportItems.push(errorViewportItemRef.current);
|
|
}
|
|
return viewportItems;
|
|
}
|
|
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 = (0, _resolveopengraph.resolveTwitter)(autoFillProps, normalizeMetadataBase(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 = convertUrlsToStrings(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 prerenderMetadata(metadataItems) {
|
|
// If the index is a function then it is a resolver and the next slot
|
|
// is the corresponding result. If the index is not a function it is the result
|
|
// itself.
|
|
const resolversAndResults = [];
|
|
for(let i = 0; i < metadataItems.length; i++){
|
|
const metadataExport = metadataItems[i][0];
|
|
getResult(resolversAndResults, metadataExport);
|
|
}
|
|
return resolversAndResults;
|
|
}
|
|
function prerenderViewport(viewportItems) {
|
|
// If the index is a function then it is a resolver and the next slot
|
|
// is the corresponding result. If the index is not a function it is the result
|
|
// itself.
|
|
const resolversAndResults = [];
|
|
for(let i = 0; i < viewportItems.length; i++){
|
|
const viewportExport = viewportItems[i];
|
|
getResult(resolversAndResults, viewportExport);
|
|
}
|
|
return resolversAndResults;
|
|
}
|
|
const noop = ()=>{};
|
|
function getResult(resolversAndResults, exportForResult) {
|
|
if (typeof exportForResult === 'function') {
|
|
// If the function is a 'use cache' function that uses the parent data as
|
|
// the second argument, we don't want to eagerly execute it during
|
|
// metadata/viewport pre-rendering, as the parent data might also be
|
|
// computed from another 'use cache' function. To ensure that the hanging
|
|
// input abort signal handling works in this case (i.e. the depending
|
|
// function waits for the cached input to resolve while encoding its args),
|
|
// they must be called sequentially. This can be accomplished by wrapping
|
|
// the call in a lazy promise, so that the original function is only called
|
|
// when the result is actually awaited.
|
|
const useCacheFunctionInfo = (0, _clientandserverreferences.getUseCacheFunctionInfo)(exportForResult.$$original);
|
|
if (useCacheFunctionInfo && useCacheFunctionInfo.usedArgs[1]) {
|
|
const promise = new Promise((resolve)=>resolversAndResults.push(resolve));
|
|
resolversAndResults.push((0, _lazyresult.createLazyResult)(async ()=>exportForResult(promise)));
|
|
} else {
|
|
let result;
|
|
if (useCacheFunctionInfo) {
|
|
resolversAndResults.push(noop);
|
|
// @ts-expect-error We intentionally omit the parent argument, because
|
|
// we know from the check above that the 'use cache' function does not
|
|
// use it.
|
|
result = exportForResult();
|
|
} else {
|
|
result = exportForResult(new Promise((resolve)=>resolversAndResults.push(resolve)));
|
|
}
|
|
resolversAndResults.push(result);
|
|
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
|
|
};
|
|
});
|
|
}
|
|
}
|
|
} else if (typeof exportForResult === 'object') {
|
|
resolversAndResults.push(exportForResult);
|
|
} else {
|
|
resolversAndResults.push(null);
|
|
}
|
|
}
|
|
function freezeInDev(obj) {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
return require('../../shared/lib/deep-freeze').deepFreeze(obj);
|
|
}
|
|
return obj;
|
|
}
|
|
async function accumulateMetadata(route, metadataItems, pathname, metadataContext) {
|
|
let resolvedMetadata = (0, _defaultmetadata.createDefaultMetadata)();
|
|
let titleTemplates = {
|
|
title: null,
|
|
twitter: null,
|
|
openGraph: null
|
|
};
|
|
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: []
|
|
};
|
|
const resolversAndResults = prerenderMetadata(metadataItems);
|
|
let resultIndex = 0;
|
|
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;
|
|
}
|
|
let pendingMetadata = resolversAndResults[resultIndex++];
|
|
if (typeof pendingMetadata === 'function') {
|
|
// This metadata item had a `generateMetadata` and
|
|
// we need to provide the currently resolved metadata
|
|
// to it before we continue;
|
|
const resolveParentMetadata = pendingMetadata;
|
|
// we know that the next item is a result if this item
|
|
// was a resolver
|
|
pendingMetadata = resolversAndResults[resultIndex++];
|
|
resolveParentMetadata(freezeInDev(resolvedMetadata));
|
|
}
|
|
// Otherwise the item was either null or a static export
|
|
let metadata;
|
|
if (isPromiseLike(pendingMetadata)) {
|
|
metadata = await pendingMetadata;
|
|
} else {
|
|
metadata = pendingMetadata;
|
|
}
|
|
resolvedMetadata = await mergeMetadata(route, pathname, {
|
|
resolvedMetadata,
|
|
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);
|
|
}
|
|
async function accumulateViewport(viewportItems) {
|
|
let resolvedViewport = (0, _defaultmetadata.createDefaultViewport)();
|
|
const resolversAndResults = prerenderViewport(viewportItems);
|
|
let i = 0;
|
|
while(i < resolversAndResults.length){
|
|
let pendingViewport = resolversAndResults[i++];
|
|
if (typeof pendingViewport === 'function') {
|
|
// this viewport item had a `generateViewport` and
|
|
// we need to provide the currently resolved viewport
|
|
// to it before we continue;
|
|
const resolveParentViewport = pendingViewport;
|
|
// we know that the next item is a result if this item
|
|
// was a resolver
|
|
pendingViewport = resolversAndResults[i++];
|
|
resolveParentViewport(freezeInDev(resolvedViewport));
|
|
}
|
|
// Otherwise the item was either null or a static export
|
|
let viewport;
|
|
if (isPromiseLike(pendingViewport)) {
|
|
viewport = await pendingViewport;
|
|
} else {
|
|
viewport = pendingViewport;
|
|
}
|
|
resolvedViewport = mergeViewport({
|
|
resolvedViewport,
|
|
viewport
|
|
});
|
|
}
|
|
return resolvedViewport;
|
|
}
|
|
async function resolveMetadata(tree, pathname, searchParams, errorConvention, getDynamicParamFromSegment, workStore, metadataContext) {
|
|
const metadataItems = await resolveMetadataItems(tree, searchParams, errorConvention, getDynamicParamFromSegment, workStore);
|
|
return accumulateMetadata(workStore.route, metadataItems, pathname, metadataContext);
|
|
}
|
|
async function resolveViewport(tree, searchParams, errorConvention, getDynamicParamFromSegment, workStore) {
|
|
const viewportItems = await resolveViewportItems(tree, searchParams, errorConvention, getDynamicParamFromSegment, workStore);
|
|
return accumulateViewport(viewportItems);
|
|
}
|
|
function isPromiseLike(value) {
|
|
return typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
}
|
|
|
|
//# sourceMappingURL=resolve-metadata.js.map
|