@@ -31,13 +31,18 @@ const { Address6, Address4 } = require('ip-address');
3131// ============================================================================
3232
3333// Logging utility for consistent and readable output
34+ const numberFormatter = new Intl . NumberFormat ( 'en-US' ) ;
35+
3436const log = {
35- info : ( msg , ...logArgs ) => console . log ( '[INFO]' , msg , ...logArgs ) ,
36- success : ( msg , ...logArgs ) => console . log ( '[SUCCESS]' , msg , ...logArgs ) ,
37- warn : ( msg , ...logArgs ) => console . warn ( '[WARN]' , msg , ...logArgs ) ,
38- error : ( msg , ...logArgs ) => console . error ( '[ERROR]' , msg , ...logArgs ) ,
39- progress : ( msg ) => process . stdout . write ( `[INFO] ${ msg } ... ` ) ,
40- done : ( ) => console . log ( 'Done' ) ,
37+ info : ( msg , ...logArgs ) => console . log ( '[INFO]' , msg , ...logArgs ) ,
38+ success : ( msg , ...logArgs ) => console . log ( '[SUCCESS]' , msg , ...logArgs ) ,
39+ warn : ( msg , ...logArgs ) => console . warn ( '[WARN]' , msg , ...logArgs ) ,
40+ error : ( msg , ...logArgs ) => console . error ( '[ERROR]' , msg , ...logArgs ) ,
41+ progress : ( msg ) => process . stdout . write ( `[INFO] ${ msg } ... ` ) ,
42+ done : ( ) => console . log ( 'Done' ) ,
43+ progressCount : ( msg , count , unit = 'entries processed' ) => {
44+ console . log ( '[INFO]' , `${ msg } (${ numberFormatter . format ( count ) } ${ unit } )` ) ;
45+ } ,
4146} ;
4247
4348// ============================================================================
@@ -333,7 +338,7 @@ function processLookupCountry(src, cb) {
333338// Data Processing Functions
334339// ============================================================================
335340
336- async function processCountryData ( src , dest ) {
341+ async function processCountryData ( src , dest , label = 'Country data' ) {
337342 let lines = 0 ;
338343 const dataFile = path . join ( dataPath , dest ) ;
339344 const tmpDataFile = path . join ( tmpPath , src ) ;
@@ -342,13 +347,13 @@ async function processCountryData(src, dest) {
342347 mkdir ( dataFile ) ;
343348
344349 process . stdout . write ( '\n' ) ;
345- log . progress ( ' Processing country data (this may take a moment)' ) ;
350+ log . progress ( ` Processing ${ label } (this may take a moment)` ) ;
346351 let tstart = Date . now ( ) ;
347352 const datFile = fs . createWriteStream ( dataFile ) ;
348353
349- function processLine ( line ) {
350- const fields = CSVtoArray ( line ) ;
351- if ( ! fields || fields . length < 6 ) return log . warn ( 'Malformed line detected:' , line ) ;
354+ function processLine ( line ) {
355+ const fields = CSVtoArray ( line ) ;
356+ if ( ! fields || fields . length < 6 ) return log . warn ( 'Malformed line detected:' , line ) ;
352357
353358 lines ++ ;
354359
@@ -389,11 +394,12 @@ async function processCountryData(src, dest) {
389394 b . writeUInt32BE ( eip , 4 ) ;
390395 }
391396
392- b . write ( cc , bsz - 2 ) ;
393- if ( Date . now ( ) - tstart > 5000 ) {
394- tstart = Date . now ( ) ;
395- process . stdout . write ( `\nStill working (${ lines } )...` ) ;
396- }
397+ b . write ( cc , bsz - 2 ) ;
398+ if ( Date . now ( ) - tstart > 5000 ) {
399+ tstart = Date . now ( ) ;
400+ process . stdout . write ( '\n' ) ;
401+ log . progressCount ( `Processing ${ label } ` , lines ) ;
402+ }
397403
398404 if ( datFile . _writableState . needDrain ) {
399405 return new Promise ( resolve => {
@@ -405,26 +411,65 @@ async function processCountryData(src, dest) {
405411 }
406412 }
407413
408- const rl = readline . createInterface ( { input : fs . createReadStream ( tmpDataFile ) , crlfDelay : Infinity } ) ;
409- let i = 0 ;
410- for await ( const line of rl ) {
411- i ++ ;
412- if ( i === 1 ) continue ;
413- await processLine ( line ) ;
414- }
415- datFile . close ( ) ;
416- log . done ( ) ;
414+ await new Promise ( ( resolve , reject ) => {
415+ const rl = readline . createInterface ( { input : fs . createReadStream ( tmpDataFile ) , crlfDelay : Infinity } ) ;
416+ let settled = false ;
417+ let i = 0 ;
418+
419+ function finish ( err ) {
420+ if ( settled ) return ;
421+ settled = true ;
422+ if ( ! rl . closed ) rl . close ( ) ;
423+ if ( err ) reject ( err ) ;
424+ else resolve ( ) ;
425+ }
426+
427+ function resume ( ) {
428+ if ( ! settled && ! rl . closed ) rl . resume ( ) ;
429+ }
430+
431+ rl . on ( 'line' , line => {
432+ rl . pause ( ) ;
433+ i ++ ;
434+ if ( i === 1 ) {
435+ resume ( ) ;
436+ return ;
437+ }
438+
439+ let result ;
440+ try {
441+ result = processLine ( line ) ;
442+ } catch ( err ) {
443+ finish ( err ) ;
444+ return ;
445+ }
446+
447+ if ( result && typeof result . then === 'function' ) {
448+ result . then ( ( ) => {
449+ resume ( ) ;
450+ } ) . catch ( finish ) ;
451+ } else {
452+ resume ( ) ;
453+ }
454+ } ) ;
455+
456+ rl . on ( 'close' , ( ) => finish ( ) ) ;
457+ rl . on ( 'error' , finish ) ;
458+ } ) ;
459+ datFile . close ( ) ;
460+ log . done ( ) ;
461+ log . info ( `${ label } processed` ) ;
417462}
418463
419- async function processCityData ( src , dest ) {
464+ async function processCityData ( src , dest , label = 'City data' ) {
420465 let lines = 0 ;
421466 const dataFile = path . join ( dataPath , dest ) ;
422467 const tmpDataFile = path . join ( tmpPath , src ) ;
423468
424469 rimraf ( dataFile ) ;
425470
426471 process . stdout . write ( '\n' ) ;
427- log . progress ( ' Processing city data (this may take a moment)' ) ;
472+ log . progress ( ` Processing ${ label } (this may take a moment)` ) ;
428473 let tstart = Date . now ( ) ;
429474 const datFile = fs . createWriteStream ( dataFile ) ;
430475
@@ -500,10 +545,11 @@ async function processCityData(src, dest) {
500545 b . writeInt32BE ( area , 20 ) ;
501546 }
502547
503- if ( Date . now ( ) - tstart > 5000 ) {
504- tstart = Date . now ( ) ;
505- process . stdout . write ( '\n[INFO] Processing... (' + lines + ' entries) ' ) ;
506- }
548+ if ( Date . now ( ) - tstart > 5000 ) {
549+ tstart = Date . now ( ) ;
550+ process . stdout . write ( '\n' ) ;
551+ log . progressCount ( `Processing ${ label } ` , lines ) ;
552+ }
507553
508554 if ( datFile . _writableState . needDrain ) {
509555 return new Promise ( ( resolve ) => {
@@ -514,14 +560,54 @@ async function processCityData(src, dest) {
514560 }
515561 }
516562
517- const rl = readline . createInterface ( { input : fs . createReadStream ( tmpDataFile ) , crlfDelay : Infinity } ) ;
518- let i = 0 ;
519- for await ( const line of rl ) {
520- i ++ ;
521- if ( i === 1 ) continue ;
522- await processLine ( line ) ;
523- }
524- datFile . close ( ) ;
563+ await new Promise ( ( resolve , reject ) => {
564+ const rl = readline . createInterface ( { input : fs . createReadStream ( tmpDataFile ) , crlfDelay : Infinity } ) ;
565+ let settled = false ;
566+ let i = 0 ;
567+
568+ function finish ( err ) {
569+ if ( settled ) return ;
570+ settled = true ;
571+ if ( ! rl . closed ) rl . close ( ) ;
572+ if ( err ) reject ( err ) ;
573+ else resolve ( ) ;
574+ }
575+
576+ function resume ( ) {
577+ if ( ! settled && ! rl . closed ) rl . resume ( ) ;
578+ }
579+
580+ rl . on ( 'line' , line => {
581+ rl . pause ( ) ;
582+ i ++ ;
583+ if ( i === 1 ) {
584+ resume ( ) ;
585+ return ;
586+ }
587+
588+ let result ;
589+ try {
590+ result = processLine ( line ) ;
591+ } catch ( err ) {
592+ finish ( err ) ;
593+ return ;
594+ }
595+
596+ if ( result && typeof result . then === 'function' ) {
597+ result . then ( ( ) => {
598+ resume ( ) ;
599+ } ) . catch ( finish ) ;
600+ } else {
601+ resume ( ) ;
602+ }
603+ } ) ;
604+
605+ rl . on ( 'close' , ( ) => finish ( ) ) ;
606+ rl . on ( 'error' , finish ) ;
607+ } ) ;
608+ datFile . close ( ) ;
609+ log . done ( ) ;
610+ log . info ( `${ label } processed` ) ;
525611}
526612
527613function processCityDataNames ( src , dest , cb ) {
@@ -534,6 +620,9 @@ function processCityDataNames(src, dest, cb) {
534620
535621 const datFile = fs . openSync ( dataFile , 'w' ) ;
536622
623+ process . stdout . write ( '\n' ) ;
624+ log . progress ( 'Processing city names (this may take a moment)' ) ;
625+
537626 function processLine ( line ) {
538627 if ( line . match ( / ^ C o p y r i g h t / ) || ! line . match ( / \d / ) ) return ;
539628
@@ -578,7 +667,11 @@ function processCityDataNames(src, dest, cb) {
578667 lineCount ++ ;
579668 } ) ;
580669
581- rl . on ( 'close' , cb ) ;
670+ rl . on ( 'close' , ( ) => {
671+ log . done ( ) ;
672+ log . info ( 'City names processed' ) ;
673+ cb ( ) ;
674+ } ) ;
582675}
583676
584677// ============================================================================
@@ -595,26 +688,22 @@ function processData(database, cb) {
595688 if ( type === 'country' ) {
596689 if ( Array . isArray ( src ) ) {
597690 processLookupCountry ( src [ 0 ] , ( ) => {
598- processCountryData ( src [ 1 ] , dest [ 1 ] ) . then ( ( ) => {
599- return processCountryData ( src [ 2 ] , dest [ 2 ] ) ;
691+ processCountryData ( src [ 1 ] , dest [ 1 ] , 'Country IPv4 data' ) . then ( ( ) => {
692+ return processCountryData ( src [ 2 ] , dest [ 2 ] , 'Country IPv6 data' ) ;
600693 } ) . then ( ( ) => {
601694 cb ( null , database ) ;
602695 } ) ;
603696 } ) ;
604- }
605- else {
606- processCountryData ( src , dest , ( ) => {
697+ } else {
698+ processCountryData ( src , dest , 'Country data' ) . then ( ( ) => {
607699 cb ( null , database ) ;
608700 } ) ;
609701 }
610702 } else if ( type === 'city' ) {
611703 processCityDataNames ( src [ 0 ] , dest [ 0 ] , ( ) => {
612- processCityData ( src [ 1 ] , dest [ 1 ] ) . then ( ( ) => {
613- process . stdout . write ( '\n' ) ;
614- log . info ( 'City IPv4 data processed' ) ;
615- return processCityData ( src [ 2 ] , dest [ 2 ] ) ;
704+ processCityData ( src [ 1 ] , dest [ 1 ] , 'City IPv4 data' ) . then ( ( ) => {
705+ return processCityData ( src [ 2 ] , dest [ 2 ] , 'City IPv6 data' ) ;
616706 } ) . then ( ( ) => {
617- log . info ( 'City IPv6 data processed' ) ;
618707 cb ( null , database ) ;
619708 } ) ;
620709 } ) ;
0 commit comments