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:
187
apps/public-web/node_modules/next/dist/esm/server/response-cache/index.js
generated
vendored
Normal file
187
apps/public-web/node_modules/next/dist/esm/server/response-cache/index.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
import { Batcher } from '../../lib/batcher';
|
||||
import { scheduleOnNextTick } from '../../lib/scheduler';
|
||||
import { fromResponseCacheEntry, routeKindToIncrementalCacheKind, toResponseCacheEntry } from './utils';
|
||||
export * from './types';
|
||||
export default class ResponseCache {
|
||||
constructor(minimal_mode){
|
||||
this.getBatcher = Batcher.create({
|
||||
// Ensure on-demand revalidate doesn't block normal requests, it should be
|
||||
// safe to run an on-demand revalidate for the same key as a normal request.
|
||||
cacheKeyFn: ({ key, isOnDemandRevalidate })=>`${key}-${isOnDemandRevalidate ? '1' : '0'}`,
|
||||
// We wait to do any async work until after we've added our promise to
|
||||
// `pendingResponses` to ensure that any any other calls will reuse the
|
||||
// same promise until we've fully finished our work.
|
||||
schedulerFn: scheduleOnNextTick
|
||||
});
|
||||
this.revalidateBatcher = Batcher.create({
|
||||
// We wait to do any async work until after we've added our promise to
|
||||
// `pendingResponses` to ensure that any any other calls will reuse the
|
||||
// same promise until we've fully finished our work.
|
||||
schedulerFn: scheduleOnNextTick
|
||||
});
|
||||
this.minimal_mode = minimal_mode;
|
||||
}
|
||||
/**
|
||||
* Gets the response cache entry for the given key.
|
||||
*
|
||||
* @param key - The key to get the response cache entry for.
|
||||
* @param responseGenerator - The response generator to use to generate the response cache entry.
|
||||
* @param context - The context for the get request.
|
||||
* @returns The response cache entry.
|
||||
*/ async get(key, responseGenerator, context) {
|
||||
var _this_previousCacheItem;
|
||||
// If there is no key for the cache, we can't possibly look this up in the
|
||||
// cache so just return the result of the response generator.
|
||||
if (!key) {
|
||||
return responseGenerator({
|
||||
hasResolved: false,
|
||||
previousCacheEntry: null
|
||||
});
|
||||
}
|
||||
// Check minimal mode cache before doing any other work
|
||||
if (this.minimal_mode && ((_this_previousCacheItem = this.previousCacheItem) == null ? void 0 : _this_previousCacheItem.key) === key && this.previousCacheItem.expiresAt > Date.now()) {
|
||||
return toResponseCacheEntry(this.previousCacheItem.entry);
|
||||
}
|
||||
const { incrementalCache, isOnDemandRevalidate = false, isFallback = false, isRoutePPREnabled = false, isPrefetch = false, waitUntil, routeKind } = context;
|
||||
const response = await this.getBatcher.batch({
|
||||
key,
|
||||
isOnDemandRevalidate
|
||||
}, ({ resolve })=>{
|
||||
const promise = this.handleGet(key, responseGenerator, {
|
||||
incrementalCache,
|
||||
isOnDemandRevalidate,
|
||||
isFallback,
|
||||
isRoutePPREnabled,
|
||||
isPrefetch,
|
||||
routeKind
|
||||
}, resolve);
|
||||
// We need to ensure background revalidates are passed to waitUntil.
|
||||
if (waitUntil) waitUntil(promise);
|
||||
return promise;
|
||||
});
|
||||
return toResponseCacheEntry(response);
|
||||
}
|
||||
/**
|
||||
* Handles the get request for the response cache.
|
||||
*
|
||||
* @param key - The key to get the response cache entry for.
|
||||
* @param responseGenerator - The response generator to use to generate the response cache entry.
|
||||
* @param context - The context for the get request.
|
||||
* @param resolve - The resolve function to use to resolve the response cache entry.
|
||||
* @returns The response cache entry.
|
||||
*/ async handleGet(key, responseGenerator, context, resolve) {
|
||||
let previousIncrementalCacheEntry = null;
|
||||
let resolved = false;
|
||||
try {
|
||||
// Get the previous cache entry if not in minimal mode
|
||||
previousIncrementalCacheEntry = !this.minimal_mode ? await context.incrementalCache.get(key, {
|
||||
kind: routeKindToIncrementalCacheKind(context.routeKind),
|
||||
isRoutePPREnabled: context.isRoutePPREnabled,
|
||||
isFallback: context.isFallback
|
||||
}) : null;
|
||||
if (previousIncrementalCacheEntry && !context.isOnDemandRevalidate) {
|
||||
resolve(previousIncrementalCacheEntry);
|
||||
resolved = true;
|
||||
if (!previousIncrementalCacheEntry.isStale || context.isPrefetch) {
|
||||
// The cached value is still valid, so we don't need to update it yet.
|
||||
return previousIncrementalCacheEntry;
|
||||
}
|
||||
}
|
||||
// Revalidate the cache entry
|
||||
const incrementalResponseCacheEntry = await this.revalidate(key, context.incrementalCache, context.isRoutePPREnabled, context.isFallback, responseGenerator, previousIncrementalCacheEntry, previousIncrementalCacheEntry !== null && !context.isOnDemandRevalidate);
|
||||
// Handle null response
|
||||
if (!incrementalResponseCacheEntry) {
|
||||
// Unset the previous cache item if it was set so we don't use it again.
|
||||
if (this.minimal_mode) this.previousCacheItem = undefined;
|
||||
return null;
|
||||
}
|
||||
// Resolve for on-demand revalidation or if not already resolved
|
||||
if (context.isOnDemandRevalidate && !resolved) {
|
||||
return incrementalResponseCacheEntry;
|
||||
}
|
||||
return incrementalResponseCacheEntry;
|
||||
} catch (err) {
|
||||
// If we've already resolved the cache entry, we can't reject as we
|
||||
// already resolved the cache entry so log the error here.
|
||||
if (resolved) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Revalidates the cache entry for the given key.
|
||||
*
|
||||
* @param key - The key to revalidate the cache entry for.
|
||||
* @param incrementalCache - The incremental cache to use to revalidate the cache entry.
|
||||
* @param isRoutePPREnabled - Whether the route is PPR enabled.
|
||||
* @param isFallback - Whether the route is a fallback.
|
||||
* @param responseGenerator - The response generator to use to generate the response cache entry.
|
||||
* @param previousIncrementalCacheEntry - The previous cache entry to use to revalidate the cache entry.
|
||||
* @param hasResolved - Whether the response has been resolved.
|
||||
* @returns The revalidated cache entry.
|
||||
*/ async revalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved, waitUntil) {
|
||||
return this.revalidateBatcher.batch(key, ()=>{
|
||||
const promise = this.handleRevalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved);
|
||||
// We need to ensure background revalidates are passed to waitUntil.
|
||||
if (waitUntil) waitUntil(promise);
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
async handleRevalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved) {
|
||||
try {
|
||||
// Generate the response cache entry using the response generator.
|
||||
const responseCacheEntry = await responseGenerator({
|
||||
hasResolved,
|
||||
previousCacheEntry: previousIncrementalCacheEntry,
|
||||
isRevalidating: true
|
||||
});
|
||||
if (!responseCacheEntry) {
|
||||
return null;
|
||||
}
|
||||
// Convert the response cache entry to an incremental response cache entry.
|
||||
const incrementalResponseCacheEntry = await fromResponseCacheEntry({
|
||||
...responseCacheEntry,
|
||||
isMiss: !previousIncrementalCacheEntry
|
||||
});
|
||||
// We want to persist the result only if it has a cache control value
|
||||
// defined.
|
||||
if (incrementalResponseCacheEntry.cacheControl) {
|
||||
if (this.minimal_mode) {
|
||||
this.previousCacheItem = {
|
||||
key,
|
||||
entry: incrementalResponseCacheEntry,
|
||||
expiresAt: Date.now() + 1000
|
||||
};
|
||||
} else {
|
||||
await incrementalCache.set(key, incrementalResponseCacheEntry.value, {
|
||||
cacheControl: incrementalResponseCacheEntry.cacheControl,
|
||||
isRoutePPREnabled,
|
||||
isFallback
|
||||
});
|
||||
}
|
||||
}
|
||||
return incrementalResponseCacheEntry;
|
||||
} catch (err) {
|
||||
// When a path is erroring we automatically re-set the existing cache
|
||||
// with new revalidate and expire times to prevent non-stop retrying.
|
||||
if (previousIncrementalCacheEntry == null ? void 0 : previousIncrementalCacheEntry.cacheControl) {
|
||||
const revalidate = Math.min(Math.max(previousIncrementalCacheEntry.cacheControl.revalidate || 3, 3), 30);
|
||||
const expire = previousIncrementalCacheEntry.cacheControl.expire === undefined ? undefined : Math.max(revalidate + 3, previousIncrementalCacheEntry.cacheControl.expire);
|
||||
await incrementalCache.set(key, previousIncrementalCacheEntry.value, {
|
||||
cacheControl: {
|
||||
revalidate: revalidate,
|
||||
expire: expire
|
||||
},
|
||||
isRoutePPREnabled,
|
||||
isFallback
|
||||
});
|
||||
}
|
||||
// We haven't resolved yet, so let's throw to indicate an error.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/index.js.map
generated
vendored
Normal file
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
19
apps/public-web/node_modules/next/dist/esm/server/response-cache/types.js
generated
vendored
Normal file
19
apps/public-web/node_modules/next/dist/esm/server/response-cache/types.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
export var CachedRouteKind = /*#__PURE__*/ function(CachedRouteKind) {
|
||||
CachedRouteKind["APP_PAGE"] = "APP_PAGE";
|
||||
CachedRouteKind["APP_ROUTE"] = "APP_ROUTE";
|
||||
CachedRouteKind["PAGES"] = "PAGES";
|
||||
CachedRouteKind["FETCH"] = "FETCH";
|
||||
CachedRouteKind["REDIRECT"] = "REDIRECT";
|
||||
CachedRouteKind["IMAGE"] = "IMAGE";
|
||||
return CachedRouteKind;
|
||||
}({});
|
||||
export var IncrementalCacheKind = /*#__PURE__*/ function(IncrementalCacheKind) {
|
||||
IncrementalCacheKind["APP_PAGE"] = "APP_PAGE";
|
||||
IncrementalCacheKind["APP_ROUTE"] = "APP_ROUTE";
|
||||
IncrementalCacheKind["PAGES"] = "PAGES";
|
||||
IncrementalCacheKind["FETCH"] = "FETCH";
|
||||
IncrementalCacheKind["IMAGE"] = "IMAGE";
|
||||
return IncrementalCacheKind;
|
||||
}({});
|
||||
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/types.js.map
generated
vendored
Normal file
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/types.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
72
apps/public-web/node_modules/next/dist/esm/server/response-cache/utils.js
generated
vendored
Normal file
72
apps/public-web/node_modules/next/dist/esm/server/response-cache/utils.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import { CachedRouteKind, IncrementalCacheKind } from './types';
|
||||
import RenderResult from '../render-result';
|
||||
import { RouteKind } from '../route-kind';
|
||||
import { HTML_CONTENT_TYPE_HEADER } from '../../lib/constants';
|
||||
export async function fromResponseCacheEntry(cacheEntry) {
|
||||
var _cacheEntry_value, _cacheEntry_value1;
|
||||
return {
|
||||
...cacheEntry,
|
||||
value: ((_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.kind) === CachedRouteKind.PAGES ? {
|
||||
kind: CachedRouteKind.PAGES,
|
||||
html: await cacheEntry.value.html.toUnchunkedString(true),
|
||||
pageData: cacheEntry.value.pageData,
|
||||
headers: cacheEntry.value.headers,
|
||||
status: cacheEntry.value.status
|
||||
} : ((_cacheEntry_value1 = cacheEntry.value) == null ? void 0 : _cacheEntry_value1.kind) === CachedRouteKind.APP_PAGE ? {
|
||||
kind: CachedRouteKind.APP_PAGE,
|
||||
html: await cacheEntry.value.html.toUnchunkedString(true),
|
||||
postponed: cacheEntry.value.postponed,
|
||||
rscData: cacheEntry.value.rscData,
|
||||
headers: cacheEntry.value.headers,
|
||||
status: cacheEntry.value.status,
|
||||
segmentData: cacheEntry.value.segmentData
|
||||
} : cacheEntry.value
|
||||
};
|
||||
}
|
||||
export async function toResponseCacheEntry(response) {
|
||||
var _response_value, _response_value1;
|
||||
if (!response) return null;
|
||||
return {
|
||||
isMiss: response.isMiss,
|
||||
isStale: response.isStale,
|
||||
cacheControl: response.cacheControl,
|
||||
value: ((_response_value = response.value) == null ? void 0 : _response_value.kind) === CachedRouteKind.PAGES ? {
|
||||
kind: CachedRouteKind.PAGES,
|
||||
html: RenderResult.fromStatic(response.value.html, HTML_CONTENT_TYPE_HEADER),
|
||||
pageData: response.value.pageData,
|
||||
headers: response.value.headers,
|
||||
status: response.value.status
|
||||
} : ((_response_value1 = response.value) == null ? void 0 : _response_value1.kind) === CachedRouteKind.APP_PAGE ? {
|
||||
kind: CachedRouteKind.APP_PAGE,
|
||||
html: RenderResult.fromStatic(response.value.html, HTML_CONTENT_TYPE_HEADER),
|
||||
rscData: response.value.rscData,
|
||||
headers: response.value.headers,
|
||||
status: response.value.status,
|
||||
postponed: response.value.postponed,
|
||||
segmentData: response.value.segmentData
|
||||
} : response.value
|
||||
};
|
||||
}
|
||||
export function routeKindToIncrementalCacheKind(routeKind) {
|
||||
switch(routeKind){
|
||||
case RouteKind.PAGES:
|
||||
return IncrementalCacheKind.PAGES;
|
||||
case RouteKind.APP_PAGE:
|
||||
return IncrementalCacheKind.APP_PAGE;
|
||||
case RouteKind.IMAGE:
|
||||
return IncrementalCacheKind.IMAGE;
|
||||
case RouteKind.APP_ROUTE:
|
||||
return IncrementalCacheKind.APP_ROUTE;
|
||||
case RouteKind.PAGES_API:
|
||||
// Pages Router API routes are not cached in the incremental cache.
|
||||
throw Object.defineProperty(new Error(`Unexpected route kind ${routeKind}`), "__NEXT_ERROR_CODE", {
|
||||
value: "E64",
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
default:
|
||||
return routeKind;
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=utils.js.map
|
||||
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/utils.js.map
generated
vendored
Normal file
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/utils.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
91
apps/public-web/node_modules/next/dist/esm/server/response-cache/web.js
generated
vendored
Normal file
91
apps/public-web/node_modules/next/dist/esm/server/response-cache/web.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
import { DetachedPromise } from '../../lib/detached-promise';
|
||||
/**
|
||||
* In the web server, there is currently no incremental cache provided and we
|
||||
* always SSR the page.
|
||||
*/ export default class WebResponseCache {
|
||||
constructor(minimalMode){
|
||||
this.pendingResponses = new Map();
|
||||
// this is a hack to avoid Webpack knowing this is equal to this.minimalMode
|
||||
// because we replace this.minimalMode to true in production bundles.
|
||||
Object.assign(this, {
|
||||
minimalMode
|
||||
});
|
||||
}
|
||||
get(key, responseGenerator, context) {
|
||||
var _this_previousCacheItem;
|
||||
// ensure on-demand revalidate doesn't block normal requests
|
||||
const pendingResponseKey = key ? `${key}-${context.isOnDemandRevalidate ? '1' : '0'}` : null;
|
||||
const pendingResponse = pendingResponseKey ? this.pendingResponses.get(pendingResponseKey) : null;
|
||||
if (pendingResponse) {
|
||||
return pendingResponse;
|
||||
}
|
||||
const { promise, resolve: resolver, reject: rejecter } = new DetachedPromise();
|
||||
if (pendingResponseKey) {
|
||||
this.pendingResponses.set(pendingResponseKey, promise);
|
||||
}
|
||||
let hasResolved = false;
|
||||
const resolve = (cacheEntry)=>{
|
||||
if (pendingResponseKey) {
|
||||
// Ensure all reads from the cache get the latest value.
|
||||
this.pendingResponses.set(pendingResponseKey, Promise.resolve(cacheEntry));
|
||||
}
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
resolver(cacheEntry);
|
||||
}
|
||||
};
|
||||
// we keep the previous cache entry around to leverage
|
||||
// when the incremental cache is disabled in minimal mode
|
||||
if (pendingResponseKey && this.minimalMode && ((_this_previousCacheItem = this.previousCacheItem) == null ? void 0 : _this_previousCacheItem.key) === pendingResponseKey && this.previousCacheItem.expiresAt > Date.now()) {
|
||||
resolve(this.previousCacheItem.entry);
|
||||
this.pendingResponses.delete(pendingResponseKey);
|
||||
return promise;
|
||||
}
|
||||
// We wait to do any async work until after we've added our promise to
|
||||
// `pendingResponses` to ensure that any any other calls will reuse the
|
||||
// same promise until we've fully finished our work.
|
||||
;
|
||||
(async ()=>{
|
||||
try {
|
||||
const cacheEntry = await responseGenerator({
|
||||
hasResolved
|
||||
});
|
||||
const resolveValue = cacheEntry === null ? null : {
|
||||
...cacheEntry,
|
||||
isMiss: true
|
||||
};
|
||||
// for on-demand revalidate wait to resolve until cache is set
|
||||
if (!context.isOnDemandRevalidate) {
|
||||
resolve(resolveValue);
|
||||
}
|
||||
if (key && cacheEntry && cacheEntry.cacheControl) {
|
||||
this.previousCacheItem = {
|
||||
key: pendingResponseKey || key,
|
||||
entry: cacheEntry,
|
||||
expiresAt: Date.now() + 1000
|
||||
};
|
||||
} else {
|
||||
this.previousCacheItem = undefined;
|
||||
}
|
||||
if (context.isOnDemandRevalidate) {
|
||||
resolve(resolveValue);
|
||||
}
|
||||
} catch (err) {
|
||||
// while revalidating in the background we can't reject as
|
||||
// we already resolved the cache entry so log the error here
|
||||
if (hasResolved) {
|
||||
console.error(err);
|
||||
} else {
|
||||
rejecter(err);
|
||||
}
|
||||
} finally{
|
||||
if (pendingResponseKey) {
|
||||
this.pendingResponses.delete(pendingResponseKey);
|
||||
}
|
||||
}
|
||||
})();
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=web.js.map
|
||||
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/web.js.map
generated
vendored
Normal file
1
apps/public-web/node_modules/next/dist/esm/server/response-cache/web.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user