diff --git a/packages/htmldocs/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts b/packages/htmldocs/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts index 586aca5..134b5bd 100644 --- a/packages/htmldocs/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts +++ b/packages/htmldocs/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts @@ -49,7 +49,7 @@ export const setupHotreloading = async ( changes = []; - }, 150); + }, 500); const absolutePathToDocumentsDirectory = path.resolve( process.cwd(), diff --git a/packages/htmldocs/src/utils/get-document-component.tsx b/packages/htmldocs/src/utils/get-document-component.tsx index 2ceb2f7..de72a42 100644 --- a/packages/htmldocs/src/utils/get-document-component.tsx +++ b/packages/htmldocs/src/utils/get-document-component.tsx @@ -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 { @@ -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(); + export const getDocumentComponent = async ( documentPath: string ): Promise< @@ -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'); @@ -38,7 +67,7 @@ export const getDocumentComponent = async ( entryPoints: [documentPath], platform: "node", bundle: true, - minify: true, + minify: false, write: false, format: "cjs", jsx: "automatic", @@ -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 {