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,57 @@
import { ReflectAdapter } from '../../server/web/spec-extension/adapters/reflect';
import { describeStringPropertyAccess, wellKnownProperties } from '../../shared/lib/utils/reflect-utils';
const CachedParams = new WeakMap();
function makeDynamicallyTrackedParamsWithDevWarnings(underlyingParams) {
const cachedParams = CachedParams.get(underlyingParams);
if (cachedParams) {
return cachedParams;
}
// We don't use makeResolvedReactPromise here because params
// supports copying with spread and we don't want to unnecessarily
// instrument the promise with spreadable properties of ReactPromise.
const promise = Promise.resolve(underlyingParams);
const proxiedProperties = new Set();
Object.keys(underlyingParams).forEach((prop)=>{
if (wellKnownProperties.has(prop)) {
// These properties cannot be shadowed because they need to be the
// true underlying value for Promises to work correctly at runtime
} else {
proxiedProperties.add(prop);
}
});
const proxiedPromise = new Proxy(promise, {
get (target, prop, receiver) {
if (typeof prop === 'string') {
if (// We are accessing a property that was proxied to the promise instance
proxiedProperties.has(prop)) {
const expression = describeStringPropertyAccess('params', prop);
warnForSyncAccess(expression);
}
}
return ReflectAdapter.get(target, prop, receiver);
},
set (target, prop, value, receiver) {
if (typeof prop === 'string') {
proxiedProperties.delete(prop);
}
return ReflectAdapter.set(target, prop, value, receiver);
},
ownKeys (target) {
warnForEnumeration();
return Reflect.ownKeys(target);
}
});
CachedParams.set(underlyingParams, proxiedPromise);
return proxiedPromise;
}
function warnForSyncAccess(expression) {
console.error(`A param property was accessed directly with ${expression}. ` + `\`params\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`);
}
function warnForEnumeration() {
console.error(`params are being enumerated. ` + `\`params\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`);
}
export function createRenderParamsFromClient(clientParams) {
return makeDynamicallyTrackedParamsWithDevWarnings(clientParams);
}
//# sourceMappingURL=params.browser.dev.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/client/request/params.browser.dev.ts"],"sourcesContent":["import type { Params } from '../../server/request/params'\n\nimport { ReflectAdapter } from '../../server/web/spec-extension/adapters/reflect'\nimport {\n describeStringPropertyAccess,\n wellKnownProperties,\n} from '../../shared/lib/utils/reflect-utils'\n\ninterface CacheLifetime {}\nconst CachedParams = new WeakMap<CacheLifetime, Promise<Params>>()\n\nfunction makeDynamicallyTrackedParamsWithDevWarnings(\n underlyingParams: Params\n): Promise<Params> {\n const cachedParams = CachedParams.get(underlyingParams)\n if (cachedParams) {\n return cachedParams\n }\n\n // We don't use makeResolvedReactPromise here because params\n // supports copying with spread and we don't want to unnecessarily\n // instrument the promise with spreadable properties of ReactPromise.\n const promise = Promise.resolve(underlyingParams)\n\n const proxiedProperties = new Set<string>()\n\n Object.keys(underlyingParams).forEach((prop) => {\n if (wellKnownProperties.has(prop)) {\n // These properties cannot be shadowed because they need to be the\n // true underlying value for Promises to work correctly at runtime\n } else {\n proxiedProperties.add(prop)\n }\n })\n\n const proxiedPromise = new Proxy(promise, {\n get(target, prop, receiver) {\n if (typeof prop === 'string') {\n if (\n // We are accessing a property that was proxied to the promise instance\n proxiedProperties.has(prop)\n ) {\n const expression = describeStringPropertyAccess('params', prop)\n warnForSyncAccess(expression)\n }\n }\n return ReflectAdapter.get(target, prop, receiver)\n },\n set(target, prop, value, receiver) {\n if (typeof prop === 'string') {\n proxiedProperties.delete(prop)\n }\n return ReflectAdapter.set(target, prop, value, receiver)\n },\n ownKeys(target) {\n warnForEnumeration()\n return Reflect.ownKeys(target)\n },\n })\n\n CachedParams.set(underlyingParams, proxiedPromise)\n return proxiedPromise\n}\n\nfunction warnForSyncAccess(expression: string) {\n console.error(\n `A param property was accessed directly with ${expression}. ` +\n `\\`params\\` is a Promise and must be unwrapped with \\`React.use()\\` before accessing its properties. ` +\n `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`\n )\n}\n\nfunction warnForEnumeration() {\n console.error(\n `params are being enumerated. ` +\n `\\`params\\` is a Promise and must be unwrapped with \\`React.use()\\` before accessing its properties. ` +\n `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`\n )\n}\n\nexport function createRenderParamsFromClient(\n clientParams: Params\n): Promise<Params> {\n return makeDynamicallyTrackedParamsWithDevWarnings(clientParams)\n}\n"],"names":["ReflectAdapter","describeStringPropertyAccess","wellKnownProperties","CachedParams","WeakMap","makeDynamicallyTrackedParamsWithDevWarnings","underlyingParams","cachedParams","get","promise","Promise","resolve","proxiedProperties","Set","Object","keys","forEach","prop","has","add","proxiedPromise","Proxy","target","receiver","expression","warnForSyncAccess","set","value","delete","ownKeys","warnForEnumeration","Reflect","console","error","createRenderParamsFromClient","clientParams"],"mappings":"AAEA,SAASA,cAAc,QAAQ,mDAAkD;AACjF,SACEC,4BAA4B,EAC5BC,mBAAmB,QACd,uCAAsC;AAG7C,MAAMC,eAAe,IAAIC;AAEzB,SAASC,4CACPC,gBAAwB;IAExB,MAAMC,eAAeJ,aAAaK,GAAG,CAACF;IACtC,IAAIC,cAAc;QAChB,OAAOA;IACT;IAEA,4DAA4D;IAC5D,kEAAkE;IAClE,qEAAqE;IACrE,MAAME,UAAUC,QAAQC,OAAO,CAACL;IAEhC,MAAMM,oBAAoB,IAAIC;IAE9BC,OAAOC,IAAI,CAACT,kBAAkBU,OAAO,CAAC,CAACC;QACrC,IAAIf,oBAAoBgB,GAAG,CAACD,OAAO;QACjC,kEAAkE;QAClE,kEAAkE;QACpE,OAAO;YACLL,kBAAkBO,GAAG,CAACF;QACxB;IACF;IAEA,MAAMG,iBAAiB,IAAIC,MAAMZ,SAAS;QACxCD,KAAIc,MAAM,EAAEL,IAAI,EAAEM,QAAQ;YACxB,IAAI,OAAON,SAAS,UAAU;gBAC5B,IACE,uEAAuE;gBACvEL,kBAAkBM,GAAG,CAACD,OACtB;oBACA,MAAMO,aAAavB,6BAA6B,UAAUgB;oBAC1DQ,kBAAkBD;gBACpB;YACF;YACA,OAAOxB,eAAeQ,GAAG,CAACc,QAAQL,MAAMM;QAC1C;QACAG,KAAIJ,MAAM,EAAEL,IAAI,EAAEU,KAAK,EAAEJ,QAAQ;YAC/B,IAAI,OAAON,SAAS,UAAU;gBAC5BL,kBAAkBgB,MAAM,CAACX;YAC3B;YACA,OAAOjB,eAAe0B,GAAG,CAACJ,QAAQL,MAAMU,OAAOJ;QACjD;QACAM,SAAQP,MAAM;YACZQ;YACA,OAAOC,QAAQF,OAAO,CAACP;QACzB;IACF;IAEAnB,aAAauB,GAAG,CAACpB,kBAAkBc;IACnC,OAAOA;AACT;AAEA,SAASK,kBAAkBD,UAAkB;IAC3CQ,QAAQC,KAAK,CACX,CAAC,4CAA4C,EAAET,WAAW,EAAE,CAAC,GAC3D,CAAC,oGAAoG,CAAC,GACtG,CAAC,8DAA8D,CAAC;AAEtE;AAEA,SAASM;IACPE,QAAQC,KAAK,CACX,CAAC,6BAA6B,CAAC,GAC7B,CAAC,oGAAoG,CAAC,GACtG,CAAC,8DAA8D,CAAC;AAEtE;AAEA,OAAO,SAASC,6BACdC,YAAoB;IAEpB,OAAO9B,4CAA4C8B;AACrD","ignoreList":[0]}

View File

@@ -0,0 +1,3 @@
export const createRenderParamsFromClient = process.env.NODE_ENV === 'development' ? require('./params.browser.dev').createRenderParamsFromClient : require('./params.browser.prod').createRenderParamsFromClient;
//# sourceMappingURL=params.browser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/client/request/params.browser.ts"],"sourcesContent":["export const createRenderParamsFromClient =\n process.env.NODE_ENV === 'development'\n ? (require('./params.browser.dev') as typeof import('./params.browser.dev'))\n .createRenderParamsFromClient\n : (\n require('./params.browser.prod') as typeof import('./params.browser.prod')\n ).createRenderParamsFromClient\n"],"names":["createRenderParamsFromClient","process","env","NODE_ENV","require"],"mappings":"AAAA,OAAO,MAAMA,+BACXC,QAAQC,GAAG,CAACC,QAAQ,KAAK,gBACrB,AAACC,QAAQ,wBACNJ,4BAA4B,GAC/B,AACEI,QAAQ,yBACRJ,4BAA4B,CAAA","ignoreList":[0]}

View File

@@ -0,0 +1,15 @@
const CachedParams = new WeakMap();
function makeUntrackedParams(underlyingParams) {
const cachedParams = CachedParams.get(underlyingParams);
if (cachedParams) {
return cachedParams;
}
const promise = Promise.resolve(underlyingParams);
CachedParams.set(underlyingParams, promise);
return promise;
}
export function createRenderParamsFromClient(clientParams) {
return makeUntrackedParams(clientParams);
}
//# sourceMappingURL=params.browser.prod.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/client/request/params.browser.prod.ts"],"sourcesContent":["import type { Params } from '../../server/request/params'\n\ninterface CacheLifetime {}\nconst CachedParams = new WeakMap<CacheLifetime, Promise<Params>>()\n\nfunction makeUntrackedParams(underlyingParams: Params): Promise<Params> {\n const cachedParams = CachedParams.get(underlyingParams)\n if (cachedParams) {\n return cachedParams\n }\n\n const promise = Promise.resolve(underlyingParams)\n CachedParams.set(underlyingParams, promise)\n\n return promise\n}\n\nexport function createRenderParamsFromClient(\n clientParams: Params\n): Promise<Params> {\n return makeUntrackedParams(clientParams)\n}\n"],"names":["CachedParams","WeakMap","makeUntrackedParams","underlyingParams","cachedParams","get","promise","Promise","resolve","set","createRenderParamsFromClient","clientParams"],"mappings":"AAGA,MAAMA,eAAe,IAAIC;AAEzB,SAASC,oBAAoBC,gBAAwB;IACnD,MAAMC,eAAeJ,aAAaK,GAAG,CAACF;IACtC,IAAIC,cAAc;QAChB,OAAOA;IACT;IAEA,MAAME,UAAUC,QAAQC,OAAO,CAACL;IAChCH,aAAaS,GAAG,CAACN,kBAAkBG;IAEnC,OAAOA;AACT;AAEA,OAAO,SAASI,6BACdC,YAAoB;IAEpB,OAAOT,oBAAoBS;AAC7B","ignoreList":[0]}

View File

@@ -0,0 +1,66 @@
import { ReflectAdapter } from '../../server/web/spec-extension/adapters/reflect';
import { describeStringPropertyAccess, describeHasCheckingStringProperty, wellKnownProperties } from '../../shared/lib/utils/reflect-utils';
const CachedSearchParams = new WeakMap();
function makeUntrackedSearchParamsWithDevWarnings(underlyingSearchParams) {
const cachedSearchParams = CachedSearchParams.get(underlyingSearchParams);
if (cachedSearchParams) {
return cachedSearchParams;
}
const proxiedProperties = new Set();
const promise = Promise.resolve(underlyingSearchParams);
Object.keys(underlyingSearchParams).forEach((prop)=>{
if (wellKnownProperties.has(prop)) {
// These properties cannot be shadowed because they need to be the
// true underlying value for Promises to work correctly at runtime
} else {
proxiedProperties.add(prop);
}
});
const proxiedPromise = new Proxy(promise, {
get (target, prop, receiver) {
if (typeof prop === 'string') {
if (!wellKnownProperties.has(prop) && (proxiedProperties.has(prop) || // We are accessing a property that doesn't exist on the promise nor
// the underlying searchParams.
Reflect.has(target, prop) === false)) {
const expression = describeStringPropertyAccess('searchParams', prop);
warnForSyncAccess(expression);
}
}
return ReflectAdapter.get(target, prop, receiver);
},
set (target, prop, value, receiver) {
if (typeof prop === 'string') {
proxiedProperties.delete(prop);
}
return Reflect.set(target, prop, value, receiver);
},
has (target, prop) {
if (typeof prop === 'string') {
if (!wellKnownProperties.has(prop) && (proxiedProperties.has(prop) || // We are accessing a property that doesn't exist on the promise nor
// the underlying searchParams.
Reflect.has(target, prop) === false)) {
const expression = describeHasCheckingStringProperty('searchParams', prop);
warnForSyncAccess(expression);
}
}
return Reflect.has(target, prop);
},
ownKeys (target) {
warnForSyncSpread();
return Reflect.ownKeys(target);
}
});
CachedSearchParams.set(underlyingSearchParams, proxiedPromise);
return proxiedPromise;
}
function warnForSyncAccess(expression) {
console.error(`A searchParam property was accessed directly with ${expression}. ` + `\`searchParams\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`);
}
function warnForSyncSpread() {
console.error(`The keys of \`searchParams\` were accessed directly. ` + `\`searchParams\` is a Promise and must be unwrapped with \`React.use()\` before accessing its properties. ` + `Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis`);
}
export function createRenderSearchParamsFromClient(underlyingSearchParams) {
return makeUntrackedSearchParamsWithDevWarnings(underlyingSearchParams);
}
//# sourceMappingURL=search-params.browser.dev.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export const createRenderSearchParamsFromClient = process.env.NODE_ENV === 'development' ? require('./search-params.browser.dev').createRenderSearchParamsFromClient : require('./search-params.browser.prod').createRenderSearchParamsFromClient;
//# sourceMappingURL=search-params.browser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/client/request/search-params.browser.ts"],"sourcesContent":["export const createRenderSearchParamsFromClient =\n process.env.NODE_ENV === 'development'\n ? (\n require('./search-params.browser.dev') as typeof import('./search-params.browser.dev')\n ).createRenderSearchParamsFromClient\n : (\n require('./search-params.browser.prod') as typeof import('./search-params.browser.prod')\n ).createRenderSearchParamsFromClient\n"],"names":["createRenderSearchParamsFromClient","process","env","NODE_ENV","require"],"mappings":"AAAA,OAAO,MAAMA,qCACXC,QAAQC,GAAG,CAACC,QAAQ,KAAK,gBACrB,AACEC,QAAQ,+BACRJ,kCAAkC,GACpC,AACEI,QAAQ,gCACRJ,kCAAkC,CAAA","ignoreList":[0]}

View File

@@ -0,0 +1,15 @@
const CachedSearchParams = new WeakMap();
function makeUntrackedSearchParams(underlyingSearchParams) {
const cachedSearchParams = CachedSearchParams.get(underlyingSearchParams);
if (cachedSearchParams) {
return cachedSearchParams;
}
const promise = Promise.resolve(underlyingSearchParams);
CachedSearchParams.set(underlyingSearchParams, promise);
return promise;
}
export function createRenderSearchParamsFromClient(underlyingSearchParams) {
return makeUntrackedSearchParams(underlyingSearchParams);
}
//# sourceMappingURL=search-params.browser.prod.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/client/request/search-params.browser.prod.ts"],"sourcesContent":["import type { SearchParams } from '../../server/request/search-params'\n\ninterface CacheLifetime {}\nconst CachedSearchParams = new WeakMap<CacheLifetime, Promise<SearchParams>>()\n\nfunction makeUntrackedSearchParams(\n underlyingSearchParams: SearchParams\n): Promise<SearchParams> {\n const cachedSearchParams = CachedSearchParams.get(underlyingSearchParams)\n if (cachedSearchParams) {\n return cachedSearchParams\n }\n\n const promise = Promise.resolve(underlyingSearchParams)\n CachedSearchParams.set(underlyingSearchParams, promise)\n\n return promise\n}\n\nexport function createRenderSearchParamsFromClient(\n underlyingSearchParams: SearchParams\n): Promise<SearchParams> {\n return makeUntrackedSearchParams(underlyingSearchParams)\n}\n"],"names":["CachedSearchParams","WeakMap","makeUntrackedSearchParams","underlyingSearchParams","cachedSearchParams","get","promise","Promise","resolve","set","createRenderSearchParamsFromClient"],"mappings":"AAGA,MAAMA,qBAAqB,IAAIC;AAE/B,SAASC,0BACPC,sBAAoC;IAEpC,MAAMC,qBAAqBJ,mBAAmBK,GAAG,CAACF;IAClD,IAAIC,oBAAoB;QACtB,OAAOA;IACT;IAEA,MAAME,UAAUC,QAAQC,OAAO,CAACL;IAChCH,mBAAmBS,GAAG,CAACN,wBAAwBG;IAE/C,OAAOA;AACT;AAEA,OAAO,SAASI,mCACdP,sBAAoC;IAEpC,OAAOD,0BAA0BC;AACnC","ignoreList":[0]}