feat(blog): add file-based blog with dynamic slugs, MDX content and layout shell

- 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
This commit is contained in:
PascalSchattenburg
2026-01-22 14:14:15 +01:00
parent b717952234
commit d147843c76
10412 changed files with 2475583 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
import type { Viewport } from '../types/metadata-interface';
import type { FieldResolver, AsyncFieldResolverExtraArgs, MetadataContext } from '../types/resolvers';
import { type MetadataBaseURL } from './resolve-url';
export declare const resolveThemeColor: FieldResolver<'themeColor', Viewport>;
export declare const resolveAlternates: AsyncFieldResolverExtraArgs<'alternates', [
MetadataBaseURL,
Promise<string>,
MetadataContext
]>;
export declare const resolveRobots: FieldResolver<'robots'>;
export declare const resolveVerification: FieldResolver<'verification'>;
export declare const resolveAppleWebApp: FieldResolver<'appleWebApp'>;
export declare const resolveAppLinks: FieldResolver<'appLinks'>;
export declare const resolveItunes: AsyncFieldResolverExtraArgs<'itunes', [
MetadataBaseURL,
Promise<string>,
MetadataContext
]>;
export declare const resolveFacebook: FieldResolver<'facebook'>;
export declare const resolvePagination: AsyncFieldResolverExtraArgs<'pagination', [
MetadataBaseURL,
Promise<string>,
MetadataContext
]>;

View File

@@ -0,0 +1,232 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
resolveAlternates: null,
resolveAppLinks: null,
resolveAppleWebApp: null,
resolveFacebook: null,
resolveItunes: null,
resolvePagination: null,
resolveRobots: null,
resolveThemeColor: null,
resolveVerification: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
resolveAlternates: function() {
return resolveAlternates;
},
resolveAppLinks: function() {
return resolveAppLinks;
},
resolveAppleWebApp: function() {
return resolveAppleWebApp;
},
resolveFacebook: function() {
return resolveFacebook;
},
resolveItunes: function() {
return resolveItunes;
},
resolvePagination: function() {
return resolvePagination;
},
resolveRobots: function() {
return resolveRobots;
},
resolveThemeColor: function() {
return resolveThemeColor;
},
resolveVerification: function() {
return resolveVerification;
}
});
const _utils = require("../generate/utils");
const _resolveurl = require("./resolve-url");
function resolveAlternateUrl(url, metadataBase, pathname, 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(pathname, url);
url.searchParams.forEach((value, key)=>newUrl.searchParams.set(key, value));
url = newUrl;
}
return (0, _resolveurl.resolveAbsoluteUrlWithPathname)(url, metadataBase, pathname, metadataContext);
}
const resolveThemeColor = (themeColor)=>{
var _resolveAsArrayOrUndefined;
if (!themeColor) return null;
const themeColorDescriptors = [];
(_resolveAsArrayOrUndefined = (0, _utils.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;
};
async function resolveUrlValuesOfObject(obj, metadataBase, pathname, metadataContext) {
if (!obj) return null;
const result = {};
for (const [key, value] of Object.entries(obj)){
if (typeof value === 'string' || value instanceof URL) {
const pathnameForUrl = await pathname;
result[key] = [
{
url: resolveAlternateUrl(value, metadataBase, pathnameForUrl, metadataContext)
}
];
} else if (value && value.length) {
result[key] = [];
const pathnameForUrl = await pathname;
value.forEach((item, index)=>{
const url = resolveAlternateUrl(item.url, metadataBase, pathnameForUrl, metadataContext);
result[key][index] = {
url,
title: item.title
};
});
}
}
return result;
}
async function resolveCanonicalUrl(urlOrDescriptor, metadataBase, pathname, metadataContext) {
if (!urlOrDescriptor) return null;
const url = typeof urlOrDescriptor === 'string' || urlOrDescriptor instanceof URL ? urlOrDescriptor : urlOrDescriptor.url;
const pathnameForUrl = await pathname;
// Return string url because structureClone can't handle URL instance
return {
url: resolveAlternateUrl(url, metadataBase, pathnameForUrl, metadataContext)
};
}
const resolveAlternates = async (alternates, metadataBase, pathname, context)=>{
if (!alternates) return null;
const canonical = await resolveCanonicalUrl(alternates.canonical, metadataBase, pathname, context);
const languages = await resolveUrlValuesOfObject(alternates.languages, metadataBase, pathname, context);
const media = await resolveUrlValuesOfObject(alternates.media, metadataBase, pathname, context);
const types = await resolveUrlValuesOfObject(alternates.types, metadataBase, pathname, context);
return {
canonical,
languages,
media,
types
};
};
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(', ');
};
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'
];
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 = (0, _utils.resolveAsArrayOrUndefined)(verification.other[otherKey]);
if (otherValue) res.other[otherKey] = otherValue;
}
} else res[key] = (0, _utils.resolveAsArrayOrUndefined)(value);
}
}
return res;
};
const resolveAppleWebApp = (appWebApp)=>{
var _resolveAsArrayOrUndefined;
if (!appWebApp) return null;
if (appWebApp === true) {
return {
capable: true
};
}
const startupImages = appWebApp.startupImage ? (_resolveAsArrayOrUndefined = (0, _utils.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'
};
};
const resolveAppLinks = (appLinks)=>{
if (!appLinks) return null;
for(const key in appLinks){
// @ts-ignore // TODO: type infer
appLinks[key] = (0, _utils.resolveAsArrayOrUndefined)(appLinks[key]);
}
return appLinks;
};
const resolveItunes = async (itunes, metadataBase, pathname, context)=>{
if (!itunes) return null;
return {
appId: itunes.appId,
appArgument: itunes.appArgument ? resolveAlternateUrl(itunes.appArgument, metadataBase, await pathname, context) : undefined
};
};
const resolveFacebook = (facebook)=>{
if (!facebook) return null;
return {
appId: facebook.appId,
admins: (0, _utils.resolveAsArrayOrUndefined)(facebook.admins)
};
};
const resolvePagination = async (pagination, metadataBase, pathname, context)=>{
return {
previous: (pagination == null ? void 0 : pagination.previous) ? resolveAlternateUrl(pagination.previous, metadataBase, await pathname, context) : null,
next: (pagination == null ? void 0 : pagination.next) ? resolveAlternateUrl(pagination.next, metadataBase, await pathname, context) : null
};
};
//# sourceMappingURL=resolve-basics.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import type { Icon, IconDescriptor } from '../types/metadata-types';
import type { FieldResolver } from '../types/resolvers';
export declare function resolveIcon(icon: Icon): IconDescriptor;
export declare const resolveIcons: FieldResolver<'icons'>;

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
resolveIcon: null,
resolveIcons: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
resolveIcon: function() {
return resolveIcon;
},
resolveIcons: function() {
return resolveIcons;
}
});
const _utils = require("../generate/utils");
const _resolveurl = require("./resolve-url");
const _constants = require("../constants");
function resolveIcon(icon) {
if ((0, _resolveurl.isStringOrURL)(icon)) return {
url: icon
};
else if (Array.isArray(icon)) return icon;
return icon;
}
const resolveIcons = (icons)=>{
if (!icons) {
return null;
}
const resolved = {
icon: [],
apple: []
};
if (Array.isArray(icons)) {
resolved.icon = icons.map(resolveIcon).filter(Boolean);
} else if ((0, _resolveurl.isStringOrURL)(icons)) {
resolved.icon = [
resolveIcon(icons)
];
} else {
for (const key of _constants.IconKeys){
const values = (0, _utils.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 { ResolvedMetadataWithURLs } 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: ResolvedMetadataWithURLs['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":["resolveIcon","resolveIcons","icon","isStringOrURL","url","Array","isArray","icons","resolved","apple","map","filter","Boolean","key","IconKeys","values","resolveAsArrayOrUndefined"],"mappings":";;;;;;;;;;;;;;;IAOgBA,WAAW;eAAXA;;IAMHC,YAAY;eAAZA;;;uBAV6B;4BACZ;2BACL;AAElB,SAASD,YAAYE,IAAU;IACpC,IAAIC,IAAAA,yBAAa,EAACD,OAAO,OAAO;QAAEE,KAAKF;IAAK;SACvC,IAAIG,MAAMC,OAAO,CAACJ,OAAO,OAAOA;IACrC,OAAOA;AACT;AAEO,MAAMD,eAAuC,CAACM;IACnD,IAAI,CAACA,OAAO;QACV,OAAO;IACT;IAEA,MAAMC,WAA8C;QAClDN,MAAM,EAAE;QACRO,OAAO,EAAE;IACX;IACA,IAAIJ,MAAMC,OAAO,CAACC,QAAQ;QACxBC,SAASN,IAAI,GAAGK,MAAMG,GAAG,CAACV,aAAaW,MAAM,CAACC;IAChD,OAAO,IAAIT,IAAAA,yBAAa,EAACI,QAAQ;QAC/BC,SAASN,IAAI,GAAG;YAACF,YAAYO;SAAO;IACtC,OAAO;QACL,KAAK,MAAMM,OAAOC,mBAAQ,CAAE;YAC1B,MAAMC,SAASC,IAAAA,gCAAyB,EAACT,KAAK,CAACM,IAAI;YACnD,IAAIE,QAAQP,QAAQ,CAACK,IAAI,GAAGE,OAAOL,GAAG,CAACV;QACzC;IACF;IACA,OAAOQ;AACT","ignoreList":[0]}

View File

@@ -0,0 +1,18 @@
import type { ResolvedMetadataWithURLs } from '../types/metadata-interface';
import type { OpenGraph } from '../types/opengraph-types';
import type { FieldResolverExtraArgs, AsyncFieldResolverExtraArgs, MetadataContext } from '../types/resolvers';
import type { Twitter } from '../types/twitter-types';
import { type MetadataBaseURL } from './resolve-url';
export declare function resolveImages(images: Twitter['images'], metadataBase: MetadataBaseURL, isStaticMetadataRouteFile: boolean): NonNullable<ResolvedMetadataWithURLs['twitter']>['images'];
export declare function resolveImages(images: OpenGraph['images'], metadataBase: MetadataBaseURL, isStaticMetadataRouteFile: boolean): NonNullable<ResolvedMetadataWithURLs['openGraph']>['images'];
export declare const resolveOpenGraph: AsyncFieldResolverExtraArgs<'openGraph', [
MetadataBaseURL,
Promise<string>,
MetadataContext,
string | null
]>;
export declare const resolveTwitter: FieldResolverExtraArgs<'twitter', [
MetadataBaseURL,
MetadataContext,
string | null
]>;

View File

@@ -0,0 +1,199 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
resolveImages: null,
resolveOpenGraph: null,
resolveTwitter: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
resolveImages: function() {
return resolveImages;
},
resolveOpenGraph: function() {
return resolveOpenGraph;
},
resolveTwitter: function() {
return resolveTwitter;
}
});
const _utils = require("../generate/utils");
const _resolveurl = require("./resolve-url");
const _resolvetitle = require("./resolve-title");
const _url = require("../../url");
const _log = require("../../../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 = (0, _resolveurl.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' && !(0, _url.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 = (0, _resolveurl.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) {
(0, _log.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: (0, _resolveurl.resolveUrl)(inputUrl, metadataBase)
} : {
...item,
// Update image descriptor url
url: (0, _resolveurl.resolveUrl)(inputUrl, metadataBase)
};
}
function resolveImages(images, metadataBase, isStaticMetadataRouteFile) {
const resolvedImages = (0, _utils.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);
}
const resolveOpenGraph = async (openGraph, metadataBase, pathname, 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 ? (0, _utils.resolveArray)(value) : null;
}
}
target.images = resolveImages(og.images, metadataBase, metadataContext.isStaticMetadataRouteFile);
}
const resolved = {
...openGraph,
title: (0, _resolvetitle.resolveTitle)(openGraph.title, titleTemplate)
};
resolveProps(resolved, openGraph);
resolved.url = openGraph.url ? (0, _resolveurl.resolveAbsoluteUrlWithPathname)(openGraph.url, metadataBase, await pathname, metadataContext) : null;
return resolved;
};
const TwitterBasicInfoKeys = [
'site',
'siteId',
'creator',
'creatorId',
'description'
];
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: (0, _resolvetitle.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 = (0, _utils.resolveAsArrayOrUndefined)(resolved.players) || [];
break;
}
case 'app':
{
resolved.app = resolved.app || {};
break;
}
case 'summary':
case 'summary_large_image':
break;
default:
resolved;
}
}
return resolved;
};
//# sourceMappingURL=resolve-opengraph.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { Metadata } from '../types/metadata-interface';
import type { AbsoluteTemplateString } from '../types/metadata-types';
export declare function resolveTitle(title: Metadata['title'], stashedTemplate: string | null | undefined): AbsoluteTemplateString;

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "resolveTitle", {
enumerable: true,
get: function() {
return resolveTitle;
}
});
function resolveTitleTemplate(template, title) {
return template ? template.replace(/%s/g, title) : title;
}
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":["resolveTitle","resolveTitleTemplate","template","title","replace","stashedTemplate","resolved","default","absolute"],"mappings":";;;;+BAUgBA;;;eAAAA;;;AAPhB,SAASC,qBACPC,QAAmC,EACnCC,KAAa;IAEb,OAAOD,WAAWA,SAASE,OAAO,CAAC,OAAOD,SAASA;AACrD;AAEO,SAASH,aACdG,KAAwB,EACxBE,eAA0C;IAE1C,IAAIC;IACJ,MAAMJ,WACJ,OAAOC,UAAU,YAAYA,SAAS,cAAcA,QAChDA,MAAMD,QAAQ,GACd;IAEN,IAAI,OAAOC,UAAU,UAAU;QAC7BG,WAAWL,qBAAqBI,iBAAiBF;IACnD,OAAO,IAAIA,OAAO;QAChB,IAAI,aAAaA,OAAO;YACtBG,WAAWL,qBAAqBI,iBAAiBF,MAAMI,OAAO;QAChE;QACA,IAAI,cAAcJ,SAASA,MAAMK,QAAQ,EAAE;YACzCF,WAAWH,MAAMK,QAAQ;QAC3B;IACF;IAEA,IAAIL,SAAS,OAAOA,UAAU,UAAU;QACtC,OAAO;YACLD;YACAM,UAAUF,YAAY;QACxB;IACF,OAAO;QACL,OAAO;YAAEE,UAAUF,YAAYH,SAAS;YAAID;QAAS;IACvD;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,18 @@
import type { MetadataContext } from '../types/resolvers';
export type MetadataBaseURL = URL | null;
declare function isStringOrURL(icon: any): icon is string | URL;
/**
* 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 declare function getSocialImageMetadataBaseFallback(metadataBase: MetadataBaseURL): URL;
declare function resolveUrl(url: null | undefined, metadataBase: MetadataBaseURL): null;
declare function resolveUrl(url: string | URL, metadataBase: MetadataBaseURL): URL;
declare function resolveUrl(url: string | MetadataBaseURL | undefined, metadataBase: MetadataBaseURL): MetadataBaseURL;
declare function resolveRelativeUrl(url: string | URL, pathname: string): string | URL;
declare function resolveAbsoluteUrlWithPathname(url: string | URL, metadataBase: MetadataBaseURL, pathname: string, { trailingSlash }: MetadataContext): string;
export { isStringOrURL, resolveUrl, resolveRelativeUrl, resolveAbsoluteUrlWithPathname, };

View File

@@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
getSocialImageMetadataBaseFallback: null,
isStringOrURL: null,
resolveAbsoluteUrlWithPathname: null,
resolveRelativeUrl: null,
resolveUrl: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getSocialImageMetadataBaseFallback: function() {
return getSocialImageMetadataBaseFallback;
},
isStringOrURL: function() {
return isStringOrURL;
},
resolveAbsoluteUrlWithPathname: function() {
return resolveAbsoluteUrlWithPathname;
},
resolveRelativeUrl: function() {
return resolveRelativeUrl;
},
resolveUrl: function() {
return resolveUrl;
}
});
const _path = /*#__PURE__*/ _interop_require_default(require("../../../shared/lib/isomorphic/path"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function isStringOrURL(icon) {
return typeof icon === 'string' || icon instanceof URL;
}
function createLocalMetadataBase() {
// Check if experimental HTTPS is enabled
const isExperimentalHttps = Boolean(process.env.__NEXT_EXPERIMENTAL_HTTPS);
const protocol = isExperimentalHttps ? 'https' : 'http';
return new URL(`${protocol}://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;
}
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.default.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.default.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, pathname, { trailingSlash }) {
// 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.searchParams.size === 0 ? 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;
}
//# sourceMappingURL=resolve-url.js.map

File diff suppressed because one or more lines are too long