import * as Stream from 'stream' ; import { IRange , IMapItem } from './file.map' ; import * as StreamMarkers from '../../consts/stream.markers' ; import Logger from '../../tools/env.logger' ; import { CRegCarrets } from '../../consts/regs' ; export interface ITransformResult { output : string ; bytesSize : number ; map : IMapItem ; } export type TCustomCallback = ( results : ITransformResult ) => void ; export function getSourceMarker ( sourceId ) { return `${ StreamMarkers . PluginId }${ sourceId }${ StreamMarkers . PluginId }` ; } export default class Transform extends Stream . Transform { public static Events = { onMap : 'onMap' , } ; private _logger : Logger ; private _pluginId : number ; private _rest : string = '' ; private _streamId : string ; private _offsets : { bytes : number ; rows : number ; } = { bytes : 0 , rows : 0 } ; private _map : IMapItem [ ] = [ ] ; private _writtenBytes : number = 0 ; constructor ( options , streamId , pluginId , offsets ) { super ( options ) ; this . _streamId = streamId ; this . _pluginId = pluginId ; this . _offsets = offsets ; this . _logger = new Logger ( `ControllerStreamTransformer: ${ this . _streamId }` ) ; } public _transform ( chunk , encoding , callback , customCB ? ) { let output = '' ; if ( typeof chunk === 'string' ) { output = `${ this . _rest }${ chunk }` ; } else { output = `${ this . _rest }${ chunk . toString ( 'utf8' ) }` ; } output = output . replace ( CRegCarrets , '\n' ) . replace ( /\n{2,}/g , '\n' ) ; const rest = this . _getRest ( output ) ; this . _rest = rest . rest ; output = rest . cleared ; const rows = { from : this . _offsets . rows , to : this . _offsets . rows , } ; const bytes = { from : this . _offsets . bytes , to : this . _offsets . bytes , } ; output = output . replace ( CRegCarrets , ( ) => { return `${ getSourceMarker ( this . _pluginId ) }${ StreamMarkers . RowNumber }${ rows . to ++ }${ StreamMarkers . RowNumber }\n` ; } ) ; if ( rows . to === rows . from ) { output = `${ output }${ getSourceMarker ( this . _pluginId ) }${ StreamMarkers . RowNumber }${ rows . to ++ }${ StreamMarkers . RowNumber }\n` ; } const size = Buffer . byteLength ( output , 'utf8' ) ; rows . to -= 1 ; bytes . to += size - 1 ; const results = { output : output , bytesSize : size , map : { rows : rows , bytes : bytes } , } ; this . _writtenBytes += size ; this . _offsets . rows = results . map . rows . to + 1 ; this . _offsets . bytes = results . map . bytes . to + 1 ; this . _map . push ( results . map ) ; if ( callback !== undefined ) { callback ( undefined , output ) ; } if ( customCB !== undefined ) { customCB ( results ) ; } this . emit ( Transform . Events . onMap , results . map , size ) ; return results ; } public convert ( chunk , callback ? ) { return this . _transform ( chunk , 'utf8' , undefined , callback ) ; } public getMap ( ) { return this . _map ; } public getBytesWritten ( ) { return this . _writtenBytes ; } private _getRest ( str ) { const last = str . length - 1 ; for ( let i = last ; i >= 0 ; i -= 1 ) { if ( str [ i ] === '\n' && i > 0 ) { return { rest : str . substr ( i + 1 , last ) , cleared : str . substr ( 0 , i + 1 ) , } ; } } return { rest : '' , cleared : str } ; } }