@@ -419,4 +419,151 @@ describe.each([
419419 expect ( getHash ( clone ( ) ) ) . toBe ( getHash ( original ) ) ;
420420 expect ( calls ) . toBe ( 3 ) ;
421421 } ) ;
422+
423+ it ( "should expose originalLazy (function form) and original()" , ( ) => {
424+ const original = new RawSource ( "Hello World" ) ;
425+ const lazy = ( ) => original ;
426+ const source = new CachedSource ( lazy ) ;
427+ expect ( source . originalLazy ( ) ) . toBe ( lazy ) ;
428+ expect ( source . original ( ) ) . toBe ( original ) ;
429+ // After original() resolves the function, originalLazy returns the resolved source
430+ expect ( source . originalLazy ( ) ) . toBe ( original ) ;
431+ } ) ;
432+
433+ it ( "should compute size from cached buffer when _cachedSize is undefined" , ( ) => {
434+ const buffer = Buffer . from ( "Hello World" ) ;
435+ // Provide cachedData with buffer but no size
436+ const cachedSource = new CachedSource ( new RawSource ( "Hello World" ) , {
437+ buffer,
438+ maps : new Map ( ) ,
439+ } ) ;
440+ expect ( cachedSource . size ( ) ) . toBe ( buffer . length ) ;
441+ expect ( cachedSource . size ( ) ) . toBe ( buffer . length ) ;
442+ } ) ;
443+
444+ it ( "should return null for missing map when cached entry is empty" , ( ) => {
445+ const cachedSource = new CachedSource ( new RawSource ( "Hello World" ) , {
446+ buffer : Buffer . from ( "Hello World" ) ,
447+ size : 11 ,
448+ maps : new Map ( [ [ "{}" , { } ] ] ) ,
449+ } ) ;
450+ expect ( cachedSource . map ( ) ) . toBeNull ( ) ;
451+ } ) ;
452+
453+ it ( "should flush accumulated hash strings when they exceed the threshold" , ( ) => {
454+ class StringyHashSource extends Source {
455+ source ( ) {
456+ return "ignored" ;
457+ }
458+
459+ buffer ( ) {
460+ return Buffer . from ( "ignored" ) ;
461+ }
462+
463+ size ( ) {
464+ return 7 ;
465+ }
466+
467+ map ( ) {
468+ return null ;
469+ }
470+
471+ updateHash ( hash ) {
472+ for ( let i = 0 ; i < 15000 ; i ++ ) {
473+ hash . update ( `chunk-${ i } -` ) ;
474+ }
475+ }
476+ }
477+
478+ const cachedSource = new CachedSource ( new StringyHashSource ( ) ) ;
479+
480+ const hashA = crypto . createHash ( "md5" ) ;
481+ cachedSource . updateHash ( hashA ) ;
482+ const digestA = hashA . digest ( "hex" ) ;
483+
484+ // When hashing again, the cached hash update is replayed directly
485+ const hashB = crypto . createHash ( "md5" ) ;
486+ cachedSource . updateHash ( hashB ) ;
487+ const digestB = hashB . digest ( "hex" ) ;
488+
489+ expect ( digestA ) . toBe ( digestB ) ;
490+ } ) ;
491+
492+ it ( "should handle hash updates starting with a Buffer (no prior string to flush)" , ( ) => {
493+ class BufferFirstHashSource extends Source {
494+ source ( ) {
495+ return "text" ;
496+ }
497+
498+ buffer ( ) {
499+ return Buffer . from ( "text" ) ;
500+ }
501+
502+ size ( ) {
503+ return 4 ;
504+ }
505+
506+ map ( ) {
507+ return null ;
508+ }
509+
510+ updateHash ( hash ) {
511+ // Start with a Buffer so the tracker "else" branch runs
512+ // with currentString === undefined, and also pass a long string
513+ // so the length-gate in the "string" branch is exercised.
514+ hash . update ( Buffer . from ( "leading-buffer-" ) ) ;
515+ hash . update ( "a" . repeat ( 11000 ) ) ;
516+ hash . update ( "short-string" ) ;
517+ }
518+ }
519+
520+ const cachedSource = new CachedSource ( new BufferFirstHashSource ( ) ) ;
521+ const hashA = crypto . createHash ( "md5" ) ;
522+ cachedSource . updateHash ( hashA ) ;
523+ const digestA = hashA . digest ( "hex" ) ;
524+
525+ const hashB = crypto . createHash ( "md5" ) ;
526+ cachedSource . updateHash ( hashB ) ;
527+ const digestB = hashB . digest ( "hex" ) ;
528+
529+ expect ( digestA ) . toBe ( digestB ) ;
530+ } ) ;
531+
532+ it ( "should allow streamChunks when cached map exists but source is not cached" , ( ) => {
533+ const original = new OriginalSource ( "Hello World" , "file.js" ) ;
534+ const cachedSource = new CachedSource ( original ) ;
535+
536+ // Populate map cache only (no source/buffer cached yet)
537+ cachedSource . map ( { } ) ;
538+
539+ const chunks = [ ] ;
540+ cachedSource . streamChunks (
541+ { } ,
542+ ( ...args ) => {
543+ chunks . push ( args ) ;
544+ } ,
545+ ( ) => { } ,
546+ ( ) => { } ,
547+ ) ;
548+ expect ( chunks . length ) . toBeGreaterThan ( 0 ) ;
549+ } ) ;
550+
551+ it ( "should round-trip CachedSource with a Buffer-backed source" , ( ) => {
552+ const buffer = Buffer . from ( Array . from ( { length : 64 } , ( _ , i ) => i ) ) ;
553+ const original = new RawSource ( buffer ) ;
554+ const source = new CachedSource ( original ) ;
555+
556+ // Populate _cachedSource with the Buffer
557+ source . source ( ) ;
558+ source . size ( ) ;
559+
560+ const cachedData = source . getCachedData ( ) ;
561+ expect ( cachedData . source ) . toBe ( false ) ;
562+
563+ // @ts -expect-error for tests
564+ const clone = new CachedSource ( null , cachedData ) ;
565+ expect ( clone . source ( ) ) . toEqual ( source . source ( ) ) ;
566+ expect ( clone . buffer ( ) ) . toEqual ( source . buffer ( ) ) ;
567+ expect ( clone . size ( ) ) . toEqual ( source . size ( ) ) ;
568+ } ) ;
422569} ) ;
0 commit comments