import { createHash } from 'crypto' ; import * as fs from 'fs' ; import { join } from 'path' ; import * as yargs from 'yargs' ; import * as zlib from 'zlib' ; import { checksumIndices , CodeBuilder } from '@phensley/cldr-core' ; import { getMain } from '../../cldr' ; import { Encoder , EncoderMachine } from '../../resource/machine' ; import { ResourcePack } from '../../resource/pack' ; import { loadPatch , applyPatch , PatchFile } from './patch' ; import { buildLocaleMap , checkLanguages , getProjectInfo , ProjectInfo } from './util' ; import DEFAULT_CONFIG from './config.json' ; import { Downloader } from '../downloader/downloader' ; import { validateConfig } from './validate' ; import { RBNFCollector } from '../../rbnf' ; /**
 * Encodes fields into a resource pack and returns the offset
 * to the field. Undefined fields get encoded as ''.
 */ export class PackEncoder implements Encoder { private _distinct : { [ x ] : number ; } = { } ; private _count : number = 0 ; private _size : number = 0 ; constructor ( private pack ) { } encode ( field ) { this . _count ++ ; if ( field !== undefined ) { const c = this . _distinct [ field ] || 0 ; this . _distinct [ field ] = c + 1 ; this . _size += field . length ; } return this . pack . add ( field === undefined ? '' : field ) ; } count ( ) { return this . _count ; } size ( ) { return this . _size ; } distinct ( ) { return Object . keys ( this . _distinct ) . length ; } } export const sha256 = ( data ) : string => createHash ( 'sha256' ) . update ( data ) . digest ( 'hex' ) ; export interface PackArgs { out : string ; lang ? : string ; patch ? : string ; config ? : string ; regions ? : string ; verbose : boolean ; } /**
 * Generates static data that will be impored into the runtime.
 */ export const runPack = ( argv ) => { const pkg = getProjectInfo ( ) ; const downloader = new Downloader ( pkg . cldrVersion ) ; downloader . run ( ) . then ( ( ) => runPackImpl ( argv , pkg ) ) . catch ( ( e ) => { console . log ( e ) ; process . exit ( 1 ) ; } ) ; } ; const runPackImpl = ( argv , pkg ) => { const localeMap = buildLocaleMap ( ) ; let langs = Object . keys ( localeMap ) . sort ( ) ; if ( argv . lang ) { langs = checkLanguages ( argv . lang . split ( ',' ) , localeMap ) ; } const configpath = argv . config ; let config ; if ( configpath ) { const configraw = fs . readFileSync ( configpath , { encoding : 'utf-8' } ) ; config = JSON . parse ( configraw ) ; validateConfig ( config ) ; } else { config = DEFAULT_CONFIG ; } const regions = new Set ( argv . regions ? argv . regions . split ( ',' ) : [ ] ) ; const dest = argv . out ; if ( ! fs . existsSync ( dest ) ) { fs . mkdirSync ( dest ) ; } const patchfiles = [ ] ; if ( argv . patch ) { let fail = false ; for ( const path of argv . patch . split ( ',' ) ) { try { const patchfile = loadPatch ( path ) ; patchfiles . push ( patchfile ) ; } catch ( e ) { console . warn ( `failed to load patch: ${ e }` ) ; fail = true ; } } if ( fail ) { process . exit ( 1 ) ; } } const builder = new CodeBuilder ( config ) ; const origin = builder . origin ( ) ; const checksum = checksumIndices ( pkg . version , origin . indices ) ; const rbnf = new RBNFCollector ( ) ; rbnf . load ( ) ; let path ; const hashes = { } ; const pkghash = createHash ( 'sha256' ) ; langs . forEach ( ( lang ) => { console . warn ( `processing:  ${ lang }` ) ; let locales = localeMap [ lang ] ; if ( regions . size > 0 ) { locales = locales . filter ( ( l ) => l . id === lang || regions . has ( l . tag . region ( ) ) ) ; } const m = /^(\d+\.\d+\.\d+).*$/ . exec ( pkg . cldrVersion ) ; if ( ! m ) { console . error ( `Invalid CLDR version string: ${ pkg . cldrVersion }` ) ; process . exit ( 1 ) ; } const cldrversion = m [ 1 ] ; const pack = new ResourcePack ( lang , pkg . version , cldrversion , rbnf ) ; const encoder = new PackEncoder ( pack ) ; const machine = new EncoderMachine ( encoder , argv . verbose ) ; locales . forEach ( ( locale ) => { if ( argv . verbose ) { console . warn ( `   locale: ${ locale . id }` ) ; } pack . push ( locale ) ; const main = getMain ( locale . id ) ; if ( patchfiles . length ) { for ( const patch of patchfiles ) { if ( ! applyPatch ( locale . id , main , patch ) ) { console . warn ( `failed to apply patch ${ patch . path } to ${ locale . id }, aborting` ) ; process . exit ( 1 ) ; } } } machine . encode ( main , origin ) ; if ( argv . verbose ) { console . warn ( '' ) ; } } ) ; const raw = pack . render ( checksum ) ; let name = `${ lang }.json` ; path = join ( dest , name ) ; console . warn ( `writing:     ${ path }` ) ; fs . writeFileSync ( path , raw , { encoding : 'utf-8' } ) ; hashes [ name ] = sha256 ( raw ) ; pkghash . update ( raw ) ; name = `${ lang }.json.gz` ; path = join ( dest , name ) ; console . warn ( `writing:     ${ path }` ) ; const data = zlib . gzipSync ( raw , { level : zlib . constants . Z_BEST_COMPRESSION } ) ; fs . writeFileSync ( path , data , { encoding : 'binary' } ) ; } ) ; path = join ( dest , 'sha256sums.txt' ) ; console . warn ( `writing:     ${ path }` ) ; fs . writeFileSync ( path , Object . keys ( hashes ) . sort ( ) . map ( ( k ) => `${ hashes [ k ] }  ${ k }` ) . join ( '\n' ) + '\n' ) ; path = join ( dest , 'resource.json' ) ; console . warn ( `writing:     ${ path }` ) ; fs . writeFileSync ( path , JSON . stringify ( { sha256 : pkghash . digest ( 'hex' ) } ) ) ; } ;