SSG Build Cache Incremental Invalidation

Speed up static site builds by hashing content inputs per page, reusing unchanged build cache entries, and invalidating only affected pages.

JavascriptNodeCli Tooling

Code

snippet
// SSG build cache: invalidate prerender output when page hashes change
import { createHash } from 'node:crypto';
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';

async function buildIncremental(pagesDir, cacheDir) {
  await mkdir(cacheDir, { recursive: true });

  const pages = (await readdir(pagesDir)).filter((f) => f.endsWith('.md'));

  let cacheMeta = {};
  try {
    cacheMeta = JSON.parse(await readFile(cacheDir + '/manifest.json', 'utf8'));
  } catch {
    cacheMeta = {};
  }

  const manifest = {};
  let rebuilt = 0;

  for (const file of pages) {
    const raw = await readFile(pagesDir + '/' + file, 'utf8');
    const hash = createHash('sha256').update(raw).digest('hex').slice(0, 16);

    if (cacheMeta[file] === hash) {
      manifest[file] = hash; // 静态内容未变,复用缓存产物
      continue;
    }

    const html = '<article>' + raw.replace(/^# (.*)$/m, '<h1>$1</h1>') + '</article>';
    await writeFile(cacheDir + '/' + file.replace(/\.md$/, '.html'), html);
    manifest[file] = hash;
    rebuilt += 1;
  }

  await writeFile(cacheDir + '/manifest.json', JSON.stringify(manifest, null, 2));
  return { total: pages.length, rebuilt, cached: pages.length - rebuilt };
}

const stats = await buildIncremental('content/pages', 'dist/cache');
console.log('build finished:', stats);