Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const setupHotreloading = async (

changes = [];

}, 150);
}, 500);

const absolutePathToDocumentsDirectory = path.resolve(
process.cwd(),
Expand Down
49 changes: 47 additions & 2 deletions packages/htmldocs/src/utils/get-document-component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as es from "esbuild";
import fs from "node:fs";
import crypto from "node:crypto";
import { BuildFailure, type OutputFile } from "esbuild";

import {
Expand All @@ -16,6 +17,15 @@ import postCssPlugin from "esbuild-style-plugin";
import { RawSourceMap } from "source-map-js";
import logger from "~/lib/logger";

// Build cache to avoid rebuilding unchanged files
const MAX_CACHE_SIZE = 50;
const buildCache = new Map<string, {
documentComponent: any;
documentCss: string | undefined;
renderAsync: typeof renderAsync;
sourceMapToOriginalFile: RawSourceMap;
}>();

export const getDocumentComponent = async (
documentPath: string
): Promise<
Expand All @@ -30,6 +40,25 @@ export const getDocumentComponent = async (
logger.debug(`[getDocumentComponent] Starting build for: ${documentPath}`);
const startTime = performance.now();

// Check cache based on file content hash
let fileContent: string | undefined;
let hash: string | undefined;
try {
fileContent = await fs.promises.readFile(documentPath, 'utf-8');
hash = crypto.createHash('md5').update(fileContent).digest('hex');

if (buildCache.has(hash)) {
logger.debug(`[getDocumentComponent] Using cached build for ${documentPath}`);
const cachedResult = buildCache.get(hash)!;
const totalTime = performance.now() - startTime;
logger.debug(`[getDocumentComponent] Cache hit in ${totalTime.toFixed(2)}ms`);
return cachedResult;
}
} catch (cacheError) {
// If cache check fails, continue with normal build
logger.debug(`[getDocumentComponent] Cache check failed, proceeding with build`);
}

let outputFiles: OutputFile[];
try {
logger.debug('Starting esbuild');
Expand All @@ -38,7 +67,7 @@ export const getDocumentComponent = async (
entryPoints: [documentPath],
platform: "node",
bundle: true,
minify: true,
minify: false,
write: false,
format: "cjs",
jsx: "automatic",
Expand Down Expand Up @@ -127,12 +156,28 @@ export const getDocumentComponent = async (
const totalTime = performance.now() - startTime;
logger.debug(`[getDocumentComponent] Total processing completed in ${totalTime.toFixed(2)}ms`);

return {
const result = {
documentComponent: executionResult.DocumentComponent,
documentCss,
renderAsync: executionResult.renderAsync,
sourceMapToOriginalFile: sourceMapToDocument,
};

// Cache the successful result using the hash computed earlier
if (hash) {
buildCache.set(hash, result);

// Limit cache size to prevent memory leaks
if (buildCache.size > MAX_CACHE_SIZE) {
const firstKey = buildCache.keys().next().value;
if (firstKey) {
buildCache.delete(firstKey);
}
}
logger.debug(`[getDocumentComponent] Result cached with hash ${hash}`);
}

return result;
} catch (error) {
logger.error('[getDocumentComponent] Processing error:', error);
return {
Expand Down