@@ -35,6 +35,7 @@ import { checkStacks } from "./checks.mjs";
3535import { stackPageHtml } from "./lib/stack-page.mjs" ;
3636import { freshnessLineHtml , freshnessPageHtml , freshnessStats , newEntriesLineHtml } from "./lib/freshness.mjs" ;
3737import { licenceTerms , listStackFiles , owes , parseStack , pickPath } from "./lib/stacks.mjs" ;
38+ import { createDistWriter } from "./lib/dist-writer.mjs" ;
3839
3940const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
4041const ROOT = path . resolve ( __dirname , ".." ) ;
@@ -281,36 +282,8 @@ function notFoundHtml(site) {
281282` ;
282283}
283284
284- /*
285- * Output goes through one writer that creates a directory once, writes a
286- * file only when its bytes changed and remembers every path it produced, so
287- * the build can prune what an earlier build left behind instead of wiping
288- * dist/ and recreating 420 directories (94% of the build time on a slow
289- * disk). `--clean` still wipes first; the result is byte-identical either way.
290- */
291- const produced = new Set ( ) ;
292- const madeDirs = new Set ( ) ;
293- let writes = 0 ;
294-
295- function ensureDir ( dir ) {
296- if ( madeDirs . has ( dir ) ) return ;
297- if ( ! fs . existsSync ( dir ) ) fs . mkdirSync ( dir , { recursive : true } ) ;
298- madeDirs . add ( dir ) ;
299- }
300-
301- /** Writes `content` (string or Buffer) to dist-relative `rel` if it differs. */
302- function writeOut ( rel , content ) {
303- const full = path . join ( DIST , rel ) ;
304- produced . add ( rel . split ( path . sep ) . join ( "/" ) ) ;
305- ensureDir ( path . dirname ( full ) ) ;
306- const next = Buffer . isBuffer ( content ) ? content : Buffer . from ( content , "utf8" ) ;
307- if ( fs . existsSync ( full ) ) {
308- const stat = fs . statSync ( full ) ;
309- if ( stat . isFile ( ) && stat . size === next . length && fs . readFileSync ( full ) . equals ( next ) ) return ;
310- }
311- fs . writeFileSync ( full , next ) ;
312- writes += 1 ;
313- }
285+ // Output writer: writes only changed bytes and prunes what it did not produce (lib/dist-writer.mjs).
286+ const { ensureDir, writeOut, pruneDist, produced, writes } = createDistWriter ( DIST ) ;
314287
315288/** Copies a file into dist (through writeOut, so unchanged files are left alone). */
316289function copyOut ( src , rel ) {
@@ -333,20 +306,6 @@ function copyPublic(src, skip, prefix = "") {
333306 }
334307}
335308
336- /** Removes every file and directory in dist that this build did not produce. */
337- function pruneDist ( dir = DIST , prefix = "" ) {
338- let kept = 0 ;
339- for ( const name of fs . readdirSync ( dir ) ) {
340- const full = path . join ( dir , name ) ;
341- const rel = prefix ? `${ prefix } /${ name } ` : name ;
342- if ( fs . statSync ( full ) . isDirectory ( ) ) {
343- if ( pruneDist ( full , rel ) === 0 ) fs . rmSync ( full , { recursive : true , force : true } ) ;
344- else kept += 1 ;
345- } else if ( produced . has ( rel ) ) kept += 1 ;
346- else fs . rmSync ( full , { force : true } ) ;
347- }
348- return kept ;
349- }
350309
351310/** Returns file => link resolver for links in that file's body (`file.path` is repo-relative). */
352311function linkResolvers ( entries , config , entryBase = "../" ) {
@@ -769,7 +728,7 @@ function main() {
769728 console . log (
770729 `Prerendered ${ visible . length } entry rows into index.html; wrote ${ pages . written . length } entry pages, ${ stackPages . written . length } stack pages, ${ categoryPages . written . length } category pages, ${ guidePages . written . length } guide pages, sitemap.xml, robots.txt, 404.html, freshness/index.html`
771730 ) ;
772- console . log ( `${ writes } of ${ produced . size } output files changed${ clean ? " (clean build)" : "" } ; ${ ( ( Date . now ( ) - started ) / 1000 ) . toFixed ( 1 ) } s` ) ;
731+ console . log ( `${ writes ( ) } of ${ produced . size } output files changed${ clean ? " (clean build)" : "" } ; ${ ( ( Date . now ( ) - started ) / 1000 ) . toFixed ( 1 ) } s` ) ;
773732}
774733
775734main ( ) ;
0 commit comments