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,9 @@
import type { EnvVars, RestoreOriginalFunction } from './types';
/**
* Proxy the environment to track environment variables keys that
* are accessed during the build.
*
* @param envVars A set to track environment variable keys that are accessed.
* @returns A function that restores the original environment.
*/
export declare function envProxy(envVars: EnvVars): RestoreOriginalFunction;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "envProxy", {
enumerable: true,
get: function() {
return envProxy;
}
});
function envProxy(envVars) {
const newEnv = new Proxy(process.env, {
get: (target, key, receiver)=>{
envVars.add(key);
return Reflect.get(target, key, receiver);
},
set: (target, key, value)=>{
return Reflect.set(target, key, value);
}
});
const oldEnv = process.env;
process.env = newEnv;
// Return a function that restores the original environment.
return ()=>{
process.env = oldEnv;
};
}
//# sourceMappingURL=env.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/build/turborepo-access-trace/env.ts"],"sourcesContent":["import type { EnvVars, RestoreOriginalFunction } from './types'\n\n/**\n * Proxy the environment to track environment variables keys that\n * are accessed during the build.\n *\n * @param envVars A set to track environment variable keys that are accessed.\n * @returns A function that restores the original environment.\n */\nexport function envProxy(envVars: EnvVars): RestoreOriginalFunction {\n const newEnv = new Proxy(process.env, {\n get: (target, key, receiver) => {\n envVars.add(key)\n return Reflect.get(target, key, receiver)\n },\n set: (target, key, value) => {\n return Reflect.set(target, key, value)\n },\n })\n\n const oldEnv = process.env\n process.env = newEnv\n\n // Return a function that restores the original environment.\n return () => {\n process.env = oldEnv\n }\n}\n"],"names":["envProxy","envVars","newEnv","Proxy","process","env","get","target","key","receiver","add","Reflect","set","value","oldEnv"],"mappings":";;;;+BASgBA;;;eAAAA;;;AAAT,SAASA,SAASC,OAAgB;IACvC,MAAMC,SAAS,IAAIC,MAAMC,QAAQC,GAAG,EAAE;QACpCC,KAAK,CAACC,QAAQC,KAAKC;YACjBR,QAAQS,GAAG,CAACF;YACZ,OAAOG,QAAQL,GAAG,CAACC,QAAQC,KAAKC;QAClC;QACAG,KAAK,CAACL,QAAQC,KAAKK;YACjB,OAAOF,QAAQC,GAAG,CAACL,QAAQC,KAAKK;QAClC;IACF;IAEA,MAAMC,SAASV,QAAQC,GAAG;IAC1BD,QAAQC,GAAG,GAAGH;IAEd,4DAA4D;IAC5D,OAAO;QACLE,QAAQC,GAAG,GAAGS;IAChB;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,20 @@
import { TurborepoAccessTraceResult } from './result';
/**
* Trace access to the filesystem (TODO), environment variables, and TCP addresses and
* merge the results into the parent `TurborepoAccessTraceResult`.
*
* @param f the function to trace
* @param parent the `TurborepoAccessTraceResult` to merge the results into
* @returns the result of the function
*/
export declare function turborepoTraceAccess<T>(f: () => T | Promise<T>, parent: TurborepoAccessTraceResult): Promise<T> | T;
/**
* Write the access trace to the trace file.
*
* @param distDir the directory to write the trace file to
* @param traces an array of traces to merge and write to the trace file
*/
export declare function writeTurborepoAccessTraceResult({ distDir, traces, }: {
distDir: string;
traces: Array<TurborepoAccessTraceResult>;
}): Promise<void>;

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
turborepoTraceAccess: null,
writeTurborepoAccessTraceResult: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
turborepoTraceAccess: function() {
return turborepoTraceAccess;
},
writeTurborepoAccessTraceResult: function() {
return writeTurborepoAccessTraceResult;
}
});
const _promises = /*#__PURE__*/ _interop_require_default(require("fs/promises"));
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _env = require("./env");
const _tcp = require("./tcp");
const _result = require("./result");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function turborepoTraceAccess(f, parent) {
// If the trace file is not set, don't trace and instead just call the
// function.
if (!process.env.TURBOREPO_TRACE_FILE) return f();
// Otherwise, trace the function and merge the results into the parent. Using
// `then` instead of `await` here to avoid creating a new async context when
// tracing is disabled.
return withTurborepoTraceAccess(f).then(([result, proxy])=>{
parent.merge(proxy);
// Return the result of the function.
return result;
});
}
async function writeTurborepoAccessTraceResult({ distDir, traces }) {
const configTraceFile = process.env.TURBOREPO_TRACE_FILE;
if (!configTraceFile || traces.length === 0) return;
// merge traces
const [accessTrace, ...otherTraces] = traces;
for (const trace of otherTraces){
accessTrace.merge(trace);
}
try {
// make sure the directory exists
await _promises.default.mkdir(_path.default.dirname(configTraceFile), {
recursive: true
});
await _promises.default.writeFile(configTraceFile, JSON.stringify({
outputs: [
`${distDir}/**`,
`!${distDir}/cache/**`
],
accessed: accessTrace.toPublicTrace()
}));
} catch (err) {
// if we can't write this file, we should bail out here to avoid
// the possibility of incorrect turborepo cache hits.
throw Object.defineProperty(new Error(`Failed to write turborepo access trace file`, {
cause: err
}), "__NEXT_ERROR_CODE", {
value: "E342",
enumerable: false,
configurable: true
});
}
}
async function withTurborepoTraceAccess(f) {
const envVars = new Set([]);
// addresses is an array of objects, so a set is useless
const addresses = [];
// TODO: watch fsPaths (removed from this implementation for now)
const fsPaths = new Set();
// setup proxies
const restoreTCP = (0, _tcp.tcpProxy)(addresses);
const restoreEnv = (0, _env.envProxy)(envVars);
let functionResult;
// NOTE: we intentionally don't catch errors here so the calling function can handle them
try {
// call the wrapped function
functionResult = await f();
} finally{
// remove proxies
restoreTCP();
restoreEnv();
}
const traceResult = new _result.TurborepoAccessTraceResult(envVars, addresses, fsPaths);
return [
functionResult,
traceResult
];
}
//# sourceMappingURL=helpers.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export type { SerializableTurborepoAccessTraceResult } from './types';
export { turborepoTraceAccess, writeTurborepoAccessTraceResult, } from './helpers';
export { TurborepoAccessTraceResult } from './result';

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
TurborepoAccessTraceResult: null,
turborepoTraceAccess: null,
writeTurborepoAccessTraceResult: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
TurborepoAccessTraceResult: function() {
return _result.TurborepoAccessTraceResult;
},
turborepoTraceAccess: function() {
return _helpers.turborepoTraceAccess;
},
writeTurborepoAccessTraceResult: function() {
return _helpers.writeTurborepoAccessTraceResult;
}
});
const _helpers = require("./helpers");
const _result = require("./result");
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/build/turborepo-access-trace/index.ts"],"sourcesContent":["export type { SerializableTurborepoAccessTraceResult } from './types'\nexport {\n turborepoTraceAccess,\n writeTurborepoAccessTraceResult,\n} from './helpers'\nexport { TurborepoAccessTraceResult } from './result'\n"],"names":["TurborepoAccessTraceResult","turborepoTraceAccess","writeTurborepoAccessTraceResult"],"mappings":";;;;;;;;;;;;;;;;IAKSA,0BAA0B;eAA1BA,kCAA0B;;IAHjCC,oBAAoB;eAApBA,6BAAoB;;IACpBC,+BAA+B;eAA/BA,wCAA+B;;;yBAC1B;wBACoC","ignoreList":[0]}

View File

@@ -0,0 +1,24 @@
import type { Addresses, EnvVars, FS, SerializableTurborepoAccessTraceResult, PublicTurborepoAccessTraceResult } from './types';
export declare class TurborepoAccessTraceResult {
private envVars;
private addresses;
private fsPaths;
constructor(envVars?: EnvVars, addresses?: Addresses, fsPaths?: FS);
/**
* Merge another `TurborepoAccessTraceResult` into this one, mutating this `TurborepoAccessTraceResult`.
*/
merge(other: TurborepoAccessTraceResult): this;
/**
* Serialize this `TurborepoAccessTraceResult` into a serializable object. Used for passing
* the `TurborepoAccessTraceResult` between workers where Sets are not serializable.
*/
serialize(): SerializableTurborepoAccessTraceResult;
/**
* Squash this `TurborepoAccessTraceResult` into a public trace object that can be written to a file
*/
toPublicTrace(): PublicTurborepoAccessTraceResult;
/**
* Create an `TurborepoAccessTraceResult` from a serialized `SerializableTurborepoAccessTraceResult`
*/
static fromSerialized(serialized: SerializableTurborepoAccessTraceResult): TurborepoAccessTraceResult;
}

View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "TurborepoAccessTraceResult", {
enumerable: true,
get: function() {
return TurborepoAccessTraceResult;
}
});
class TurborepoAccessTraceResult {
constructor(envVars = new Set([]), addresses = [], fsPaths = new Set([])){
this.envVars = envVars;
this.addresses = addresses;
this.fsPaths = fsPaths;
}
/**
* Merge another `TurborepoAccessTraceResult` into this one, mutating this `TurborepoAccessTraceResult`.
*/ merge(other) {
other.envVars.forEach((envVar)=>this.envVars.add(envVar));
other.fsPaths.forEach((path)=>this.fsPaths.add(path));
this.addresses.push(...other.addresses);
return this;
}
/**
* Serialize this `TurborepoAccessTraceResult` into a serializable object. Used for passing
* the `TurborepoAccessTraceResult` between workers where Sets are not serializable.
*/ serialize() {
return {
fs: Array.from(this.fsPaths).map(String),
addresses: this.addresses,
envVars: Array.from(this.envVars).map(String)
};
}
/**
* Squash this `TurborepoAccessTraceResult` into a public trace object that can be written to a file
*/ toPublicTrace() {
return {
network: this.addresses.length > 0,
envVarKeys: Array.from(this.envVars).map(String),
filePaths: Array.from(this.fsPaths).map(String)
};
}
/**
* Create an `TurborepoAccessTraceResult` from a serialized `SerializableTurborepoAccessTraceResult`
*/ static fromSerialized(serialized) {
return new TurborepoAccessTraceResult(new Set(serialized.envVars), serialized.addresses, new Set(serialized.fs));
}
}
//# sourceMappingURL=result.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/build/turborepo-access-trace/result.ts"],"sourcesContent":["import type {\n Addresses,\n EnvVars,\n FS,\n SerializableTurborepoAccessTraceResult,\n PublicTurborepoAccessTraceResult,\n} from './types'\n\nexport class TurborepoAccessTraceResult {\n constructor(\n private envVars: EnvVars = new Set([]),\n private addresses: Addresses = [],\n private fsPaths: FS = new Set([])\n ) {}\n\n /**\n * Merge another `TurborepoAccessTraceResult` into this one, mutating this `TurborepoAccessTraceResult`.\n */\n public merge(other: TurborepoAccessTraceResult) {\n other.envVars.forEach((envVar) => this.envVars.add(envVar))\n other.fsPaths.forEach((path) => this.fsPaths.add(path))\n this.addresses.push(...other.addresses)\n\n return this\n }\n\n /**\n * Serialize this `TurborepoAccessTraceResult` into a serializable object. Used for passing\n * the `TurborepoAccessTraceResult` between workers where Sets are not serializable.\n */\n public serialize(): SerializableTurborepoAccessTraceResult {\n return {\n fs: Array.from(this.fsPaths).map(String),\n addresses: this.addresses,\n envVars: Array.from(this.envVars).map(String),\n }\n }\n\n /**\n * Squash this `TurborepoAccessTraceResult` into a public trace object that can be written to a file\n */\n public toPublicTrace(): PublicTurborepoAccessTraceResult {\n return {\n network: this.addresses.length > 0,\n envVarKeys: Array.from(this.envVars).map(String),\n filePaths: Array.from(this.fsPaths).map(String),\n }\n }\n\n /**\n * Create an `TurborepoAccessTraceResult` from a serialized `SerializableTurborepoAccessTraceResult`\n */\n public static fromSerialized(\n serialized: SerializableTurborepoAccessTraceResult\n ) {\n return new TurborepoAccessTraceResult(\n new Set(serialized.envVars),\n serialized.addresses,\n new Set(serialized.fs)\n )\n }\n}\n"],"names":["TurborepoAccessTraceResult","constructor","envVars","Set","addresses","fsPaths","merge","other","forEach","envVar","add","path","push","serialize","fs","Array","from","map","String","toPublicTrace","network","length","envVarKeys","filePaths","fromSerialized","serialized"],"mappings":";;;;+BAQaA;;;eAAAA;;;AAAN,MAAMA;IACXC,YACE,AAAQC,UAAmB,IAAIC,IAAI,EAAE,CAAC,EACtC,AAAQC,YAAuB,EAAE,EACjC,AAAQC,UAAc,IAAIF,IAAI,EAAE,CAAC,CACjC;aAHQD,UAAAA;aACAE,YAAAA;aACAC,UAAAA;IACP;IAEH;;GAEC,GACD,AAAOC,MAAMC,KAAiC,EAAE;QAC9CA,MAAML,OAAO,CAACM,OAAO,CAAC,CAACC,SAAW,IAAI,CAACP,OAAO,CAACQ,GAAG,CAACD;QACnDF,MAAMF,OAAO,CAACG,OAAO,CAAC,CAACG,OAAS,IAAI,CAACN,OAAO,CAACK,GAAG,CAACC;QACjD,IAAI,CAACP,SAAS,CAACQ,IAAI,IAAIL,MAAMH,SAAS;QAEtC,OAAO,IAAI;IACb;IAEA;;;GAGC,GACD,AAAOS,YAAoD;QACzD,OAAO;YACLC,IAAIC,MAAMC,IAAI,CAAC,IAAI,CAACX,OAAO,EAAEY,GAAG,CAACC;YACjCd,WAAW,IAAI,CAACA,SAAS;YACzBF,SAASa,MAAMC,IAAI,CAAC,IAAI,CAACd,OAAO,EAAEe,GAAG,CAACC;QACxC;IACF;IAEA;;GAEC,GACD,AAAOC,gBAAkD;QACvD,OAAO;YACLC,SAAS,IAAI,CAAChB,SAAS,CAACiB,MAAM,GAAG;YACjCC,YAAYP,MAAMC,IAAI,CAAC,IAAI,CAACd,OAAO,EAAEe,GAAG,CAACC;YACzCK,WAAWR,MAAMC,IAAI,CAAC,IAAI,CAACX,OAAO,EAAEY,GAAG,CAACC;QAC1C;IACF;IAEA;;GAEC,GACD,OAAcM,eACZC,UAAkD,EAClD;QACA,OAAO,IAAIzB,2BACT,IAAIG,IAAIsB,WAAWvB,OAAO,GAC1BuB,WAAWrB,SAAS,EACpB,IAAID,IAAIsB,WAAWX,EAAE;IAEzB;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,8 @@
import type { Addresses, RestoreOriginalFunction } from './types';
/**
* Proxy the TCP connect method to determine if any network access is made during the build
*
* @param addresses An array to track the addresses that are accessed.
* @returns A function that restores the original connect method.
*/
export declare function tcpProxy(addresses: Addresses): RestoreOriginalFunction;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "tcpProxy", {
enumerable: true,
get: function() {
return tcpProxy;
}
});
const _net = /*#__PURE__*/ _interop_require_default(require("net"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function tcpProxy(addresses) {
// net.Socket docs https://nodejs.org/api/net.html#class-netsocket
const originalConnect = _net.default.Socket.prototype.connect;
// Override the connect method
_net.default.Socket.prototype.connect = function(...args) {
// First, check if the first argument is an object and not null
if (typeof args[0] === 'object' && args[0] !== null) {
const options = args[0];
// check if the options has what we need
if ('port' in options && options.port !== undefined && 'host' in options && options.host !== undefined) {
addresses.push({
addr: options.host,
port: options.port.toString()
});
}
}
return originalConnect.apply(this, args);
};
return ()=>{
// Restore the original connect method
_net.default.Socket.prototype.connect = originalConnect;
};
}
//# sourceMappingURL=tcp.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/build/turborepo-access-trace/tcp.ts"],"sourcesContent":["import net from 'net'\nimport type { Addresses, RestoreOriginalFunction } from './types'\n\n/**\n * Proxy the TCP connect method to determine if any network access is made during the build\n *\n * @param addresses An array to track the addresses that are accessed.\n * @returns A function that restores the original connect method.\n */\nexport function tcpProxy(addresses: Addresses): RestoreOriginalFunction {\n // net.Socket docs https://nodejs.org/api/net.html#class-netsocket\n const originalConnect = net.Socket.prototype.connect\n\n // Override the connect method\n net.Socket.prototype.connect = function (...args: any) {\n // First, check if the first argument is an object and not null\n if (typeof args[0] === 'object' && args[0] !== null) {\n const options = args[0] as net.SocketConnectOpts\n\n // check if the options has what we need\n if (\n 'port' in options &&\n options.port !== undefined &&\n 'host' in options &&\n options.host !== undefined\n ) {\n addresses.push({ addr: options.host, port: options.port.toString() })\n }\n }\n\n return originalConnect.apply(this, args)\n }\n\n return () => {\n // Restore the original connect method\n net.Socket.prototype.connect = originalConnect\n }\n}\n"],"names":["tcpProxy","addresses","originalConnect","net","Socket","prototype","connect","args","options","port","undefined","host","push","addr","toString","apply"],"mappings":";;;;+BASgBA;;;eAAAA;;;4DATA;;;;;;AAST,SAASA,SAASC,SAAoB;IAC3C,kEAAkE;IAClE,MAAMC,kBAAkBC,YAAG,CAACC,MAAM,CAACC,SAAS,CAACC,OAAO;IAEpD,8BAA8B;IAC9BH,YAAG,CAACC,MAAM,CAACC,SAAS,CAACC,OAAO,GAAG,SAAU,GAAGC,IAAS;QACnD,+DAA+D;QAC/D,IAAI,OAAOA,IAAI,CAAC,EAAE,KAAK,YAAYA,IAAI,CAAC,EAAE,KAAK,MAAM;YACnD,MAAMC,UAAUD,IAAI,CAAC,EAAE;YAEvB,wCAAwC;YACxC,IACE,UAAUC,WACVA,QAAQC,IAAI,KAAKC,aACjB,UAAUF,WACVA,QAAQG,IAAI,KAAKD,WACjB;gBACAT,UAAUW,IAAI,CAAC;oBAAEC,MAAML,QAAQG,IAAI;oBAAEF,MAAMD,QAAQC,IAAI,CAACK,QAAQ;gBAAG;YACrE;QACF;QAEA,OAAOZ,gBAAgBa,KAAK,CAAC,IAAI,EAAER;IACrC;IAEA,OAAO;QACL,sCAAsC;QACtCJ,YAAG,CAACC,MAAM,CAACC,SAAS,CAACC,OAAO,GAAGJ;IACjC;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,40 @@
/**
* A single Addr / Port pair that was accessed during the duration of the trace
*/
export interface Address {
addr: string;
port: string;
}
/**
* Tracked environment variable keys that were accessed during the duration of the trace
*/
export type EnvVars = Set<string | Symbol>;
/**
* Tracks the file system paths that were accessed during the duration of the trace
*/
export type FS = Set<string>;
/**
* Tracked Addr / Port pairs that were accessed during the duration of the trace
*/
export type Addresses = Array<Address>;
/**
* The serializable version of `TurborepoAccessTraceResult` - this is required to pass the `TurborepoAccessTraceResult`
* between workers where Sets are not serializable.
*/
export type SerializableTurborepoAccessTraceResult = Readonly<{
fs: Array<string>;
addresses: Addresses;
envVars: Array<string>;
}>;
/**
* The public version of `TurborepoAccessTraceResult` - this is what is written to the trace file
*/
export type PublicTurborepoAccessTraceResult = Readonly<{
filePaths: Array<string>;
network: boolean;
envVarKeys: Array<string>;
}>;
/**
* A function that restores the original state of a proxy
*/
export type RestoreOriginalFunction = () => void;

View File

@@ -0,0 +1,8 @@
/**
* A single Addr / Port pair that was accessed during the duration of the trace
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/build/turborepo-access-trace/types.ts"],"sourcesContent":["/**\n * A single Addr / Port pair that was accessed during the duration of the trace\n */\nexport interface Address {\n addr: string\n port: string\n}\n\n/**\n * Tracked environment variable keys that were accessed during the duration of the trace\n */\nexport type EnvVars = Set<string | Symbol>\n\n/**\n * Tracks the file system paths that were accessed during the duration of the trace\n */\nexport type FS = Set<string>\n\n/**\n * Tracked Addr / Port pairs that were accessed during the duration of the trace\n */\nexport type Addresses = Array<Address>\n\n/**\n * The serializable version of `TurborepoAccessTraceResult` - this is required to pass the `TurborepoAccessTraceResult`\n * between workers where Sets are not serializable.\n */\nexport type SerializableTurborepoAccessTraceResult = Readonly<{\n fs: Array<string>\n addresses: Addresses\n envVars: Array<string>\n}>\n\n/**\n * The public version of `TurborepoAccessTraceResult` - this is what is written to the trace file\n */\nexport type PublicTurborepoAccessTraceResult = Readonly<{\n filePaths: Array<string>\n network: boolean\n envVarKeys: Array<string>\n}>\n\n/**\n * A function that restores the original state of a proxy\n */\nexport type RestoreOriginalFunction = () => void\n"],"names":[],"mappings":"AAAA;;CAEC","ignoreList":[0]}