ISG Pre-render Engine (generating-isg.js)
Explore how Dinou dynamically generates and caches dynamic routes on their first request at runtime.
Key File Location: ./dinou/core/generating-isg.js💡 Overview
Incremental Static Generation (ISG) resolves a classic scaling dilemma: if your application contains thousands or millions of dynamic paths (such as product detail pages), rendering all of them upfront would lead to significant delay and resource consumption.
To optimize this, during production server startup, Dinou evaluates which routes should be static. For routes with dynamic segments (e.g., /posts/[id]), it pre-compiles only the paths explicitly returned by the route's getStaticPaths() function. For all other un-precompiled paths, the server executes generating-isg.js on their first request. This dynamic compiler evaluates the route, creates the static cache files (both RSC and HTML) on disk, and promotes the route to static so that subsequent visits bypass rendering entirely.
📊 Compilation Lifecycle
Below is the lifecycle of an incoming request on a non-compiled path:
🚀 Lazy Static Promotion
When the Express routing middleware intercepts a request and detects that the route exists in your dynamic files (e.g. src/posts/[id]/page.tsx) but has no pre-compiled index in the dist2/ cache, it triggers the ISG thread:
- Bailout Checking: The engine renders the component tree. If the page is marked as dynamic (e.g., calls
cookies()), it setsisDynamicFromServer.value = trueand exits. The server continues to render this path dynamically. - Cache Promotion: If the run completes without bailouts, the engine compiles the RSC stream and HTML document. Once successfully saved to disk, subsequent page views bypass the React compiler entirely and are served as static files by Express.
🔒 Mutex Sharing & Lock Pools
To save resources and avoid race conditions, the ISG engine does not maintain its own lock pool.
Instead, it shares the exact same Set lock registry imported from revalidating.js:
const { regenerating } = require("./revalidating");This ensures that if a background ISR task is already updating a page, the ISG thread cannot attempt to create a parallel compilation task for the same path, and vice versa.
🎯 Invocation & Conditionals (Where is it called?)
The generatingISG function is imported and called by the main web server (core/server.js) during the handling of wildcard page requests (/*).
To avoid slowing down the active user's request, the server executes the compilation as a "fire-and-forget" background task. It waits until the response has successfully finished streaming to the client (listening to Express's res.on("finish")) and then checks these conditions:
- Production Only (
!isDevelopment): ISG cache files are only compiled and served in production mode. - Success Status (
res.statusCode === 200): Only promotes the page if it rendered successfully without crashes. - HTTP GET Method (
req.method === "GET"): Only triggers compilation on standard GET navigations. - Server Ready (
isReady): Verifies that the initial startup SSG generation of crawled routes has completed.
// Inside core/server.js wildcard route handler:
res.on("finish", () => {
if (
!isDevelopment &&
res.statusCode === 200 &&
req.method === "GET" &&
isReady
) {
generatingISG(reqPath, dynamicState); // Triggers background compile
}
});⚙️ Complete Code Walkthrough
Below is the full, complete code of generating-isg.js:
const fs = require("fs").promises;
const path = require("path");
const { existsSync, copyFileSync } = require("fs");
const generateStaticPage = require("./generate-static-page");
const generateStaticRSC = require("./generate-static-rsc");
const { buildStaticPage } = require("./build-static-pages");
const { regenerating } = require("./revalidating"); // Shares the Mutex Set
const { safeRename } = require("./safe-rename");
const { updateStatus } = require("./status-manifest");
function generatingISG(reqPath, isDynamicFromServer) {
const dist2Folder = path.resolve(process.cwd(), "dist2");
// 1. Concurrency Protection
if (regenerating.has(reqPath)) return;
try {
if (existsSync(path.join(dist2Folder, reqPath, "index.html")))
copyFileSync(
path.join(dist2Folder, reqPath, "index.html"),
path.join(dist2Folder, reqPath, "index._old.html")
);
if (existsSync(path.join(dist2Folder, reqPath, "rsc.rsc")))
copyFileSync(
path.join(dist2Folder, reqPath, "rsc.rsc"),
path.join(dist2Folder, reqPath, "rsc._old.rsc")
);
} catch (e) {
/* Ignore copy errors */
}
// 2. Set Lock in the shared registry
regenerating.add(reqPath);
(async () => {
try {
console.log(`[ISG] Promoting new page to static: ${reqPath}...`);
const isDynamic = {};
// A. Build Data (Runs compiler with Proxy spies)
await buildStaticPage(reqPath, isDynamic);
// B. Dynamic check bailout
if (isDynamic.value) {
isDynamicFromServer.value = true;
console.log(`[ISG] Skipped ${reqPath}: is dynamic`);
return; // Exit compiler - route compiles as dynamic SSR going forward
}
// C. Generate and commit RSC Flight payload
const rscResult = await generateStaticRSC(reqPath);
if (!rscResult.success) {
console.warn(`⚠️ [ISG] RSC generation failed for ${reqPath}. Aborting.`);
await fs.unlink(rscResult.tempPath).catch(() => {});
return;
}
await safeRename(rscResult.tempPath, rscResult.finalPath);
// D. Generate and commit static HTML page
const pageResult = await generateStaticPage(reqPath);
if (pageResult.success) {
await safeRename(pageResult.tempPath, pageResult.finalPath);
updateStatus(reqPath, pageResult.status);
isDynamicFromServer.value = false;
console.log(`✅ [ISG] Successfully promoted ${reqPath} to static.`);
} else {
await fs.unlink(pageResult.tempPath).catch(() => {});
console.warn(`⚠️ [ISG] HTML generation failed for ${reqPath}. Aborting commit.`);
}
} catch (e) {
console.error(`[ISG] Critical error promoting ${reqPath}:`, e);
} finally {
// 3. Clear Lock state
regenerating.delete(reqPath);
}
})();
}
module.exports = { generatingISG };