Last active
November 22, 2022 17:43
-
-
Save kibolho/19f1cce6767be7b635e21af225bb4c74 to your computer and use it in GitHub Desktop.
Stringify Objects and Arrays into Query Params
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import stringify from './stringfy'; | |
| const params = [{"teste": "1"},{"teste": "2"},{"teste": "3"}]; | |
| const stringParams = stringify(params, { | |
| encodeValuesOnly: true, | |
| encode: false, | |
| encoder: encodeURIComponent, | |
| addQueryPrefix: true, | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* eslint-disable no-bitwise */ | |
| interface StringifyOptions { | |
| encoder?: any; | |
| encode?: boolean; | |
| encodeValuesOnly?: boolean; | |
| addQueryPrefix?: boolean; | |
| allowDots?: boolean; | |
| charset?: string; | |
| charsetSentinel?: boolean; | |
| delimiter?: string; | |
| skipNulls?: boolean; | |
| strictNullHandling?: boolean; | |
| } | |
| const stringifyObject = (object: any, opts: any): string => { | |
| let obj = object; | |
| const options = normalizeStringifyOptions(opts); | |
| let objKeys; | |
| let filter; | |
| if (typeof options.filter === 'function') { | |
| filter = options.filter; | |
| obj = filter('', obj); | |
| } else if (isArray(options.filter)) { | |
| filter = options.filter; | |
| objKeys = filter; | |
| } | |
| const keys: any = []; | |
| if (typeof obj !== 'object' || obj === null) { | |
| return ''; | |
| } | |
| let arrayFormat; | |
| if (opts && opts.arrayFormat in arrayPrefixGenerators) { | |
| arrayFormat = opts.arrayFormat; | |
| } else if (opts && 'indices' in opts) { | |
| arrayFormat = opts.indices ? 'indices' : 'repeat'; | |
| } else { | |
| arrayFormat = 'indices'; | |
| } | |
| const generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; | |
| if (!objKeys) { | |
| objKeys = Object.keys(obj); | |
| } | |
| if (options.sort) { | |
| objKeys.sort(options.sort); | |
| } | |
| for (let i = 0; i < objKeys.length; ++i) { | |
| const key = objKeys[i]; | |
| if (options.skipNulls && obj[key] === null) { | |
| continue; | |
| } | |
| pushToArray( | |
| keys, | |
| stringify({ | |
| object: obj[key], | |
| prefix: key, | |
| generateArrayPrefix: generateArrayPrefix, | |
| strictNullHandling: options.strictNullHandling, | |
| skipNulls: options.skipNulls, | |
| encoder: options.encode ? options.encoder : null, | |
| filter: options.filter, | |
| sort: options.sort, | |
| allowDots: options.allowDots, | |
| serializeDate: options.serializeDate, | |
| format: options.format, | |
| formatter: options.formatter, | |
| encodeValuesOnly: options.encodeValuesOnly, | |
| charset: options.charset, | |
| }), | |
| ); | |
| } | |
| const joined = keys.join(options.delimiter); | |
| let prefix = options.addQueryPrefix === true ? '?' : ''; | |
| if (options.charsetSentinel) { | |
| if (options.charset === 'iso-8859-1') { | |
| // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark | |
| prefix += 'utf8=%26%2310003%3B&'; | |
| } else { | |
| // encodeURIComponent('✓') | |
| prefix += 'utf8=%E2%9C%93&'; | |
| } | |
| } | |
| return joined.length > 0 ? prefix + joined : ''; | |
| }; | |
| export default (params: any, opts: StringifyOptions) => | |
| stringifyObject(params, opts); | |
| const utils = { | |
| hexTable: (() => { | |
| const array = []; | |
| for (let i = 0; i < 256; ++i) { | |
| array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); | |
| } | |
| return array; | |
| })(), | |
| encode(str: any, defaultEncoder: any, charset: any, kind: any, format: any) { | |
| // This code was originally written by Brian White (mscdex) for the io.js core querystring library. | |
| // It has been adapted here for stricter adherence to RFC 3986 | |
| if (str.length === 0) { | |
| return str; | |
| } | |
| let string = str; | |
| if (typeof str === 'symbol') { | |
| string = Symbol.prototype.toString.call(str); | |
| } else if (typeof str !== 'string') { | |
| string = String(str); | |
| } | |
| if (charset === 'iso-8859-1') { | |
| return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { | |
| return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; | |
| }); | |
| } | |
| let out = ''; | |
| for (let i = 0; i < string.length; ++i) { | |
| let c = string.charCodeAt(i); | |
| if ( | |
| c === 0x2d || // - | |
| c === 0x2e || // . | |
| c === 0x5f || // _ | |
| c === 0x7e || // ~ | |
| (c >= 0x30 && c <= 0x39) || // 0-9 | |
| (c >= 0x41 && c <= 0x5a) || // a-z | |
| (c >= 0x61 && c <= 0x7a) || // A-Z | |
| (format === 'RFC1738' && (c === 0x28 || c === 0x29)) // ( ) | |
| ) { | |
| out += string.charAt(i); | |
| continue; | |
| } | |
| if (c < 0x80) { | |
| out = out + this.hexTable[c]; | |
| continue; | |
| } | |
| if (c < 0x800) { | |
| out = | |
| out + | |
| (this.hexTable[0xc0 | (c >> 6)] + this.hexTable[0x80 | (c & 0x3f)]); | |
| continue; | |
| } | |
| if (c < 0xd800 || c >= 0xe000) { | |
| out = | |
| out + | |
| (this.hexTable[0xe0 | (c >> 12)] + | |
| this.hexTable[0x80 | ((c >> 6) & 0x3f)] + | |
| this.hexTable[0x80 | (c & 0x3f)]); | |
| continue; | |
| } | |
| i += 1; | |
| c = 0x10000 + (((c & 0x3ff) << 10) | (string.charCodeAt(i) & 0x3ff)); | |
| out += | |
| this.hexTable[0xf0 | (c >> 18)] + | |
| this.hexTable[0x80 | ((c >> 12) & 0x3f)] + | |
| this.hexTable[0x80 | ((c >> 6) & 0x3f)] + | |
| this.hexTable[0x80 | (c & 0x3f)]; | |
| } | |
| return out; | |
| }, | |
| maybeMap: (val: any, fn: any) => { | |
| if (isArray(val)) { | |
| const mapped = []; | |
| for (let i = 0; i < val.length; i += 1) { | |
| mapped.push(fn(val[i])); | |
| } | |
| return mapped; | |
| } | |
| return fn(val); | |
| }, | |
| isBuffer: (obj: any) => { | |
| if (!obj || typeof obj !== 'object') { | |
| return false; | |
| } | |
| return !!( | |
| obj.constructor && | |
| obj.constructor.isBuffer && | |
| obj.constructor.isBuffer(obj) | |
| ); | |
| }, | |
| }; | |
| const formatterFunction = (value: any) => String(value); | |
| const arrayPrefixGenerators: any = { | |
| brackets: function brackets(prefix: any) { | |
| return prefix + '[]'; | |
| }, | |
| comma: 'comma', | |
| indices: function indices(prefix: any, key: any) { | |
| return prefix + '[' + key + ']'; | |
| }, | |
| repeat: function repeat(prefix: any) { | |
| return prefix; | |
| }, | |
| }; | |
| const isArray = Array.isArray; | |
| const push = Array.prototype.push; | |
| const pushToArray = function (arr: any[], valueOrArray: any[]) { | |
| push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]); | |
| }; | |
| const toISO = Date.prototype.toISOString; | |
| const defaultFormat = 'RFC3986'; | |
| const defaults = { | |
| addQueryPrefix: false, | |
| allowDots: false, | |
| charset: 'utf-8', | |
| charsetSentinel: false, | |
| delimiter: '&', | |
| encode: false, | |
| encoder: utils.encode, | |
| encodeValuesOnly: false, | |
| format: defaultFormat, | |
| formatter: formatterFunction, | |
| // deprecatedÎ | |
| indices: false, | |
| serializeDate: function serializeDate(date: any) { | |
| return toISO.call(date); | |
| }, | |
| skipNulls: false, | |
| strictNullHandling: false, | |
| }; | |
| const isNonNullishPrimitive = function isNonNullishPrimitive(v: any) { | |
| return ( | |
| typeof v === 'string' || | |
| typeof v === 'number' || | |
| typeof v === 'boolean' || | |
| typeof v === 'symbol' || | |
| typeof v === 'bigint' | |
| ); | |
| }; | |
| const stringify = ({ | |
| object, | |
| prefix, | |
| generateArrayPrefix, | |
| strictNullHandling, | |
| skipNulls, | |
| encoder, | |
| filter, | |
| sort, | |
| allowDots, | |
| serializeDate, | |
| format, | |
| formatter, | |
| encodeValuesOnly, | |
| charset, | |
| }: any) => { | |
| let obj = object; | |
| if (typeof filter === 'function') { | |
| obj = filter(prefix, obj); | |
| } else if (obj instanceof Date) { | |
| obj = serializeDate(obj); | |
| } else if (generateArrayPrefix === 'comma' && isArray(obj)) { | |
| obj = utils.maybeMap(obj, function (value: any) { | |
| if (value instanceof Date) { | |
| return serializeDate(value); | |
| } | |
| return value; | |
| }); | |
| } | |
| if (obj === null) { | |
| if (strictNullHandling) { | |
| return encoder && !encodeValuesOnly | |
| ? encoder(prefix, defaults.encoder, charset, 'key', format) | |
| : prefix; | |
| } | |
| obj = ''; | |
| } | |
| if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) { | |
| if (encoder) { | |
| const keyValue = encodeValuesOnly | |
| ? prefix | |
| : encoder(prefix, defaults.encoder, charset, 'key', format); | |
| return [ | |
| formatter(keyValue) + | |
| '=' + | |
| formatter(encoder(obj, defaults.encoder, charset, 'value', format)), | |
| ]; | |
| } | |
| return [formatter(prefix) + '=' + formatter(String(obj))]; | |
| } | |
| const values: any = []; | |
| if (typeof obj === 'undefined') { | |
| return values; | |
| } | |
| let objKeys; | |
| if (generateArrayPrefix === 'comma' && isArray(obj)) { | |
| // we need to join elements in | |
| objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : undefined }]; | |
| } else if (isArray(filter)) { | |
| objKeys = filter; | |
| } else { | |
| const keys = Object.keys(obj); | |
| objKeys = sort ? keys.sort(sort) : keys; | |
| } | |
| for (let i = 0; i < objKeys.length; ++i) { | |
| const key = objKeys[i]; | |
| const value = | |
| typeof key === 'object' && key.value !== undefined ? key.value : obj[key]; | |
| if (skipNulls && value === null) { | |
| continue; | |
| } | |
| const keyPrefix = isArray(obj) | |
| ? typeof generateArrayPrefix === 'function' | |
| ? generateArrayPrefix(prefix, key) | |
| : prefix | |
| : prefix + (allowDots ? '.' + key : '[' + key + ']'); | |
| pushToArray( | |
| values, | |
| stringify({ | |
| object: value, | |
| prefix: keyPrefix, | |
| generateArrayPrefix, | |
| strictNullHandling, | |
| skipNulls, | |
| encoder, | |
| filter, | |
| sort, | |
| allowDots, | |
| serializeDate, | |
| format, | |
| formatter, | |
| encodeValuesOnly, | |
| charset, | |
| }), | |
| ); | |
| } | |
| return values; | |
| }; | |
| const normalizeStringifyOptions = (opts: any): any => { | |
| if (!opts) { | |
| return defaults; | |
| } | |
| if ( | |
| opts.encoder !== null && | |
| opts.encoder !== undefined && | |
| typeof opts.encoder !== 'function' | |
| ) { | |
| throw new TypeError('Encoder has to be a function.'); | |
| } | |
| const charset = opts.charset || defaults.charset; | |
| if ( | |
| typeof opts.charset !== 'undefined' && | |
| opts.charset !== 'utf-8' && | |
| opts.charset !== 'iso-8859-1' | |
| ) { | |
| throw new TypeError( | |
| 'The charset option must be either utf-8, iso-8859-1, or undefined', | |
| ); | |
| } | |
| let format = 'RFC3986'; | |
| if (typeof opts.format !== 'undefined') { | |
| format = opts.format; | |
| } | |
| const formatter = formatterFunction; | |
| let filter; | |
| if (typeof opts.filter === 'function' || isArray(opts.filter)) { | |
| filter = opts.filter; | |
| } | |
| return { | |
| addQueryPrefix: | |
| typeof opts.addQueryPrefix === 'boolean' | |
| ? opts.addQueryPrefix | |
| : defaults.addQueryPrefix, | |
| allowDots: | |
| typeof opts.allowDots === 'undefined' | |
| ? defaults.allowDots | |
| : !!opts.allowDots, | |
| charset: charset, | |
| charsetSentinel: | |
| typeof opts.charsetSentinel === 'boolean' | |
| ? opts.charsetSentinel | |
| : defaults.charsetSentinel, | |
| delimiter: | |
| typeof opts.delimiter === 'undefined' | |
| ? defaults.delimiter | |
| : opts.delimiter, | |
| encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, | |
| encoder: | |
| typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, | |
| encodeValuesOnly: | |
| typeof opts.encodeValuesOnly === 'boolean' | |
| ? opts.encodeValuesOnly | |
| : defaults.encodeValuesOnly, | |
| filter: filter, | |
| format: format, | |
| formatter: formatter, | |
| serializeDate: | |
| typeof opts.serializeDate === 'function' | |
| ? opts.serializeDate | |
| : defaults.serializeDate, | |
| skipNulls: | |
| typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, | |
| sort: typeof opts.sort === 'function' ? opts.sort : null, | |
| strictNullHandling: | |
| typeof opts.strictNullHandling === 'boolean' | |
| ? opts.strictNullHandling | |
| : defaults.strictNullHandling, | |
| }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment