@@ -11,15 +11,32 @@ import fs from "node:fs";
1111import path from "node:path" ;
1212import os from "node:os" ;
1313import zlib from "node:zlib" ;
14+ import { createHash } from "node:crypto" ;
1415import { precompressAssets } from "../packages/vinext/src/build/precompress.js" ;
16+ import { isPrecompressedVariantBeneficial } from "../packages/vinext/src/utils/precompressed-variant.js" ;
1517
1618/** Write a file with repeated content to ensure it exceeds compression threshold. */
17- async function writeAsset ( clientDir : string , relativePath : string , content : string ) : Promise < void > {
19+ async function writeAsset (
20+ clientDir : string ,
21+ relativePath : string ,
22+ content : string | Uint8Array ,
23+ ) : Promise < void > {
1824 const fullPath = path . join ( clientDir , relativePath ) ;
1925 await fsp . mkdir ( path . dirname ( fullPath ) , { recursive : true } ) ;
2026 await fsp . writeFile ( fullPath , content ) ;
2127}
2228
29+ function deterministicHighEntropyBytes ( size : number ) : Buffer {
30+ const chunks : Buffer [ ] = [ ] ;
31+ let bytes = 0 ;
32+ for ( let index = 0 ; bytes < size ; index ++ ) {
33+ const chunk = createHash ( "sha256" ) . update ( `vinext-precompress-${ index } ` ) . digest ( ) ;
34+ chunks . push ( chunk ) ;
35+ bytes += chunk . length ;
36+ }
37+ return Buffer . concat ( chunks ) . subarray ( 0 , size ) ;
38+ }
39+
2340describe ( "precompressAssets" , ( ) => {
2441 let clientDir : string ;
2542
@@ -82,6 +99,64 @@ describe("precompressAssets", () => {
8299 expect ( result . filesCompressed ) . toBe ( 0 ) ;
83100 } ) ;
84101
102+ it ( "does not emit compressed representations that are not smaller" , async ( ) => {
103+ const content = deterministicHighEntropyBytes ( 4096 ) ;
104+ await writeAsset ( clientDir , "_next/static/random-aaa111.wasm" , content ) ;
105+
106+ const result = await precompressAssets ( clientDir ) ;
107+
108+ expect ( result . filesCompressed ) . toBe ( 0 ) ;
109+ expect ( fs . existsSync ( path . join ( clientDir , "_next/static/random-aaa111.wasm.br" ) ) ) . toBe ( false ) ;
110+ expect ( fs . existsSync ( path . join ( clientDir , "_next/static/random-aaa111.wasm.gz" ) ) ) . toBe ( false ) ;
111+ expect ( fs . existsSync ( path . join ( clientDir , "_next/static/random-aaa111.wasm.zst" ) ) ) . toBe ( false ) ;
112+ } ) ;
113+
114+ it ( "does not emit a marginally smaller representation with larger wire overhead" , async ( ) => {
115+ const content = deterministicHighEntropyBytes ( 4096 ) ;
116+ content . copy ( content , content . length - 48 , 0 , 48 ) ;
117+ const brotli = zlib . brotliCompressSync ( content , {
118+ params : { [ zlib . constants . BROTLI_PARAM_QUALITY ] : 5 } ,
119+ } ) ;
120+ expect ( brotli . length ) . toBeLessThan ( content . length ) ;
121+ expect ( isPrecompressedVariantBeneficial ( brotli . length , content . length , "br" ) ) . toBe ( false ) ;
122+
123+ const relativePath = "_next/static/marginal-aaa111.wasm" ;
124+ await writeAsset ( clientDir , relativePath , content ) ;
125+ await precompressAssets ( clientDir ) ;
126+
127+ expect ( fs . existsSync ( path . join ( clientDir , relativePath + ".br" ) ) ) . toBe ( false ) ;
128+ } ) ;
129+
130+ it ( "removes obsolete compressed representations on a later run" , async ( ) => {
131+ const relativePath = "_next/static/changing-aaa111.wasm" ;
132+ const fullPath = path . join ( clientDir , relativePath ) ;
133+ await writeAsset ( clientDir , relativePath , "compressible\n" . repeat ( 500 ) ) ;
134+ await precompressAssets ( clientDir ) ;
135+ expect ( fs . existsSync ( fullPath + ".br" ) ) . toBe ( true ) ;
136+
137+ await writeAsset ( clientDir , relativePath , deterministicHighEntropyBytes ( 4096 ) ) ;
138+ await precompressAssets ( clientDir ) ;
139+
140+ expect ( fs . existsSync ( fullPath + ".br" ) ) . toBe ( false ) ;
141+ expect ( fs . existsSync ( fullPath + ".gz" ) ) . toBe ( false ) ;
142+ expect ( fs . existsSync ( fullPath + ".zst" ) ) . toBe ( false ) ;
143+ } ) ;
144+
145+ it ( "removes obsolete variants when an asset falls below the size threshold" , async ( ) => {
146+ const relativePath = "_next/static/shrinking-aaa111.js" ;
147+ const fullPath = path . join ( clientDir , relativePath ) ;
148+ await writeAsset ( clientDir , relativePath , "const value = 1;\n" . repeat ( 500 ) ) ;
149+ await precompressAssets ( clientDir ) ;
150+ expect ( fs . existsSync ( fullPath + ".br" ) ) . toBe ( true ) ;
151+
152+ await writeAsset ( clientDir , relativePath , "const value = 1;" ) ;
153+ await precompressAssets ( clientDir ) ;
154+
155+ expect ( fs . existsSync ( fullPath + ".br" ) ) . toBe ( false ) ;
156+ expect ( fs . existsSync ( fullPath + ".gz" ) ) . toBe ( false ) ;
157+ expect ( fs . existsSync ( fullPath + ".zst" ) ) . toBe ( false ) ;
158+ } ) ;
159+
85160 it ( "skips non-compressible file types (images, fonts)" , async ( ) => {
86161 // PNG file (binary, already compressed)
87162 const pngHeader = Buffer . alloc ( 2048 , 0x89 ) ; // fake PNG data, above threshold
@@ -229,3 +304,17 @@ describe("precompressAssets", () => {
229304 expect ( result . totalBrotliBytes ) . toBeLessThan ( result . totalOriginalBytes ) ;
230305 } ) ;
231306} ) ;
307+
308+ describe ( "isPrecompressedVariantBeneficial" , ( ) => {
309+ it . each ( [
310+ [ "br" , 45 ] ,
311+ [ "gzip" , 47 ] ,
312+ [ "zstd" , 47 ] ,
313+ ] as const ) (
314+ "requires %s to save more than its response-header overhead" ,
315+ ( encoding , overhead ) => {
316+ expect ( isPrecompressedVariantBeneficial ( 1000 - overhead , 1000 , encoding ) ) . toBe ( false ) ;
317+ expect ( isPrecompressedVariantBeneficial ( 999 - overhead , 1000 , encoding ) ) . toBe ( true ) ;
318+ } ,
319+ ) ;
320+ } ) ;
0 commit comments