UNPKG

@gravityforms/utils

Version:
251 lines (238 loc) 7.37 kB
/* eslint-disable */ const re = { not_string: /[^s]/, not_bool: /[^t]/, not_type: /[^T]/, not_primitive: /[^v]/, number: /[diefg]/, numeric_arg: /[bcdiefguxX]/, json: /[j]/, not_json: /[^j]/, text: /^[^\x25]+/, modulo: /^\x25{2}/, placeholder: /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/, key: /^([a-z_][a-z_\d]*)/i, key_access: /^\.([a-z_][a-z_\d]*)/i, index_access: /^\[(\d+)\]/, sign: /^[+-]/, }; /** * @module sprintf * @description Returns a string produced according to a formatting string. * * @since 1.0.0 * * @param {string} format The format to be used when creating the string. ( i.e. 'My name is %1$s %2$s' ). * @param {string} args Any number of parameters to be replaced in the format string. * * @return {string} Returns the resulting string. * * @example * import { sprintf } from "@gravityforms/utils"; * * function Example() { * const str = sprintf('My name is %1$s %2$s. I am %3$i years old.', 'Joe', 'Doe', 30); * } * */ function sprintf( format ) { // `arguments` is not an array, but should be fine for this call return sprintf_format( sprintf_parse( format ), arguments ); } /** * @module vsprintf * @description Returns a string produced according to a formatting string. * * @since 1.0.0 * * @param {string} format The format to be used when creating the string. ( i.e. 'My name is %1$s %2$s' ). * @param {Array} args Arguments to be replaced in the format string. * * @return {string} Returns the resulting string. * * @example * import { vsprintf } from "@gravityforms/utils"; * * function Example() { * const str = vsprintf( 'My name is %1$s %2$s. I am %3$i years old.', ['Joe', 'Doe', 30] ); * } * */ function vsprintf( format, args ) { return sprintf.apply( null, [ format ].concat( args || [] ) ); } /** * * @param parse_tree * @param argv */ function sprintf_format( parse_tree, argv ) { let cursor = 1, tree_length = parse_tree.length, arg, output = '', i, k, ph, pad, pad_character, pad_length, is_positive, sign; for ( i = 0; i < tree_length; i++ ) { if ( typeof parse_tree[ i ] === 'string' ) { output += parse_tree[ i ]; } else if ( typeof parse_tree[ i ] === 'object' ) { ph = parse_tree[ i ]; if ( ph.keys ) { // keyword argument arg = argv[ cursor ]; for ( k = 0; k < ph.keys.length; k++ ) { if ( arg == undefined ) { throw new Error( sprintf( '[sprintf] Cannot access property "%s" of undefined value "%s"', ph.keys[ k ], ph.keys[ k - 1 ] ) ); } arg = arg[ ph.keys[ k ] ]; } } else if ( ph.param_no ) { // positional argument (explicit) arg = argv[ ph.param_no ]; } else { // positional argument (implicit) arg = argv[ cursor++ ]; } if ( re.not_type.test( ph.type ) && re.not_primitive.test( ph.type ) && arg instanceof Function ) { arg = arg(); } if ( re.numeric_arg.test( ph.type ) && ( typeof arg !== 'number' && isNaN( arg ) ) ) { throw new TypeError( sprintf( '[sprintf] expecting number but found %T', arg ) ); } if ( re.number.test( ph.type ) ) { is_positive = arg >= 0; } switch ( ph.type ) { case 'b': arg = parseInt( arg, 10 ).toString( 2 ); break; case 'c': arg = String.fromCharCode( parseInt( arg, 10 ) ); break; case 'd': case 'i': arg = parseInt( arg, 10 ); break; case 'j': arg = JSON.stringify( arg, null, ph.width ? parseInt( ph.width ) : 0 ); break; case 'e': arg = ph.precision ? parseFloat( arg ).toExponential( ph.precision ) : parseFloat( arg ).toExponential(); break; case 'f': arg = ph.precision ? parseFloat( arg ).toFixed( ph.precision ) : parseFloat( arg ); break; case 'g': arg = ph.precision ? String( Number( arg.toPrecision( ph.precision ) ) ) : parseFloat( arg ); break; case 'o': arg = ( parseInt( arg, 10 ) >>> 0 ).toString( 8 ); break; case 's': arg = String( arg ); arg = ( ph.precision ? arg.substring( 0, ph.precision ) : arg ); break; case 't': arg = String( !! arg ); arg = ( ph.precision ? arg.substring( 0, ph.precision ) : arg ); break; case 'T': arg = Object.prototype.toString.call( arg ).slice( 8, -1 ).toLowerCase(); arg = ( ph.precision ? arg.substring( 0, ph.precision ) : arg ); break; case 'u': arg = parseInt( arg, 10 ) >>> 0; break; case 'v': arg = arg.valueOf(); arg = ( ph.precision ? arg.substring( 0, ph.precision ) : arg ); break; case 'x': arg = ( parseInt( arg, 10 ) >>> 0 ).toString( 16 ); break; case 'X': arg = ( parseInt( arg, 10 ) >>> 0 ).toString( 16 ).toUpperCase(); break; } if ( re.json.test( ph.type ) ) { output += arg; } else { if ( re.number.test( ph.type ) && ( ! is_positive || ph.sign ) ) { sign = is_positive ? '+' : '-'; arg = arg.toString().replace( re.sign, '' ); } else { sign = ''; } pad_character = ph.pad_char ? ph.pad_char === '0' ? '0' : ph.pad_char.charAt( 1 ) : ' '; pad_length = ph.width - ( sign + arg ).length; pad = ph.width ? ( pad_length > 0 ? pad_character.repeat( pad_length ) : '' ) : ''; output += ph.align ? sign + arg + pad : ( pad_character === '0' ? sign + pad + arg : pad + sign + arg ); } } } return output; } const sprintf_cache = Object.create( null ); /** * * @param fmt */ function sprintf_parse( fmt ) { if ( sprintf_cache[ fmt ] ) { return sprintf_cache[ fmt ]; } let _fmt = fmt, match, parse_tree = [], arg_names = 0; while ( _fmt ) { if ( ( match = re.text.exec( _fmt ) ) !== null ) { parse_tree.push( match[ 0 ] ); } else if ( ( match = re.modulo.exec( _fmt ) ) !== null ) { parse_tree.push( '%' ); } else if ( ( match = re.placeholder.exec( _fmt ) ) !== null ) { if ( match[ 2 ] ) { arg_names |= 1; let field_list = [], replacement_field = match[ 2 ], field_match = []; if ( ( field_match = re.key.exec( replacement_field ) ) !== null ) { field_list.push( field_match[ 1 ] ); while ( ( replacement_field = replacement_field.substring( field_match[ 0 ].length ) ) !== '' ) { if ( ( field_match = re.key_access.exec( replacement_field ) ) !== null ) { field_list.push( field_match[ 1 ] ); } else if ( ( field_match = re.index_access.exec( replacement_field ) ) !== null ) { field_list.push( field_match[ 1 ] ); } else { throw new SyntaxError( '[sprintf] failed to parse named argument key' ); } } } else { throw new SyntaxError( '[sprintf] failed to parse named argument key' ); } match[ 2 ] = field_list; } else { arg_names |= 2; } if ( arg_names === 3 ) { throw new Error( '[sprintf] mixing positional and named placeholders is not (yet) supported' ); } parse_tree.push( { placeholder: match[ 0 ], param_no: match[ 1 ], keys: match[ 2 ], sign: match[ 3 ], pad_char: match[ 4 ], align: match[ 5 ], width: match[ 6 ], precision: match[ 7 ], type: match[ 8 ], } ); } else { throw new SyntaxError( '[sprintf] unexpected placeholder' ); } _fmt = _fmt.substring( match[ 0 ].length ); } return sprintf_cache[ fmt ] = parse_tree; } export { sprintf, vsprintf };