UNPKG

ninjs-lodash

Version:
273 lines (229 loc) 8.23 kB
/** * JPretty * pretty print js/json */ 'use strict' const _ = require('lodash') const util = require('util') const clc = require('cli-color') const fs = require('fs-extra') const REGX_SPACES = /^\s+/ /** * Config */ const SPACES = fs.spaces const INDENT = _.repeat(' ', SPACES) const OPTS = { "colors": true, "theme": 'json', "indent": INDENT, "wrap": 120, "sort": false, "align": true, "decimals": 3, "arrayPadding": 1, "objectPadding": 1, "afterComma": 1, "beforeComma": 0, "afterColon": 1, "beforeColon": 1 } const THEMES = { "json": { "cls": "val", "color": clc.whiteBright, "colon": clc.cyanBright, "brace": clc.cyanBright, "bracket": clc.cyanBright, "comma": clc.cyanBright, "key": clc.blackBright, "val": clc.xterm(128), "null": clc.xterm(13), "undefined": clc.xterm(13), "string": clc.greenBright, "boolean": clc.magentaBright, "number": clc.magentaBright, "regex": clc.magentaBright }, "js": { "cls": "val", "color": clc.white, "colon": clc.white, "brace": clc.white, "bracket": clc.white, "comma": clc.white, "key": clc.blackBright, "val": clc.xterm(128), "null": clc.redBright, "undefined": clc.redBright, "string": clc.greenBright, "boolean": clc.magentaBright, "number": clc.magentaBright, "regex": clc.yellowBright } } const CODES = _.get(THEMES, _.get(OPTS, 'theme')) || THEMES.json const DEF_CLS = _.get(CODES, 'cls') const DEF_COLOR = _.get(CODES, 'color') function _colorfy(enabled, str, cls) { if(!enabled) return str cls = cls || DEF_CLS let setting = _.get(CODES, cls) setting = setting || DEF_COLOR return setting ? setting(str) : str } const REGX_REMOVE_ANSI_COLOR = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g function _uncolorfy(str) { return str && _.isString(str) ? str.replace(REGX_REMOVE_ANSI_COLOR, '') : '' } // const REGX_COMMENTS = /(\/\*([\s\S]*?)\*\/)|(\/\/(.*)$)/gm const REGX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg // const REGX_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg const REGX_ARG_NAMES = /([^\s,]+)/g function _argNames(src) { if(!_.isFunction(src)) return [] let ret = _.replace(src.toString(), REGX_COMMENTS, '') let result = ret.slice(ret.indexOf('(') + 1, ret.indexOf(')')).match(REGX_ARG_NAMES) return _.isArray(result) ? result : [] } // returns function signature name function _sigName(src) { if (!_.isFunction(src)) return '' let ret = _.trim(_.replace(src.toString(), 'function', '')) ret = ret.substr(0, ret.indexOf('(')) if(ret && _.includes(ret, 'class')) ret = '' return ret || '' } exports = module.exports = jpretty _.mixin({ "jpretty": jpretty }) exports.uncolorfy = _uncolorfy function jpretty(val, options) { let CACHE_MAP = new WeakMap() let opts = _.merge({}, OPTS, options) let colors = _.get(opts, 'colors') let wrap = _.get(opts, 'wrap') let sort = _.get(opts, 'sort') let align = _.get(opts, 'align') let indent = _.get(opts, 'indent') let decimals = _.get(opts, 'decimals') let arrayPadding = _.get(opts, 'arrayPadding') let objectPadding = _.get(opts, 'objectPadding') let afterComma = _.get(opts, 'afterComma') let beforeComma = _.get(opts, 'beforeComma') let afterColon = _.get(opts, 'afterColon') let beforeColon = _.get(opts, 'beforeColon') if (wrap===true) wrap = -1 let oComma = _colorfy(colors, ',', 'comma') let oColon = _colorfy(colors, ':', 'colon') let oBrace = _colorfy(colors, '{', 'brace') let eBrace = _colorfy(colors, '}', 'brace') let oBracket = _colorfy(colors, '[', 'bracket') let eBracket = _colorfy(colors, ']', 'bracket') let oNull = _colorfy(colors, 'null', 'null') let oUndefined = _colorfy(colors, 'undefined', 'undefined') let apad = _.repeat(' ', arrayPadding) let opad = _.repeat(' ', objectPadding) let comma = _.repeat(' ', beforeComma) + oComma + _.repeat(' ', afterComma) let colon = _.repeat(' ', beforeColon) + oColon + _.repeat(' ', afterColon) let fin = build(val,'') CACHE_MAP = new WeakMap() return fin function safeObj(obj) { let res = {} _.forIn(obj, function(v, k) { if(_.isObject(v) || _.isFunction(v)) { if (CACHE_MAP.has(v)) return true CACHE_MAP.set(v, k) } res[k] = v }) return res } function build(o,dent) { function buildFunction() { let okeys = _.keys(o) if(okeys.length) { o = safeObj(_.pick(o, okeys)) //return buildObject() return build(o, '') } else { let signame = _sigName(o) var sigval = _.compact([signame || 'function', "(", _argNames(o).join(", "), ")"]).join("") return _colorfy(colors, `"${sigval}"`, 'string') } } function buildArray() { if(_.isEmpty(o)) return oBracket+eBracket let pieces = o.map((v) => { return build(v,'') }) let arrLine = `${dent}${oBracket}${apad}${pieces.join(comma)}${apad}${eBracket}` if (wrap === false || arrLine.length <= wrap) return arrLine return `${dent}${oBracket}\n${ o.map((v) => { return build(v, dent + indent) }).join(`${oComma}\n`) }\n${dent}${eBracket}` } function buildObject() { let keyvals=[] let i = 0 if(_.isEmpty(o)) return oBrace+eBrace _.forIn(o, (v, k) => { let kstr = JSON.stringify(k) kstr = _colorfy(colors, kstr, 'key') keyvals[i++] = [kstr, build(v, '')] }) if (sort) { keyvals = keyvals.sort((kv1,kv2) => { kv1 = kv1[0] kv2 = kv2[0] return kv1 < kv2 ? -1 : kv1 > kv2 ? 1 : 0 }) } keyvals = keyvals.map((kv) => { return kv.join(colon) }).join(comma) let objLine = `${oBrace}${opad}${keyvals}${opad}${eBrace}` if (wrap===false || objLine.length < wrap) return objLine keyvals = [] i = 0 _.forIn(o, (v, k) => { let kstr = JSON.stringify(k) kstr = _colorfy(colors, kstr, 'key') keyvals[i++] = [`${dent}${indent}${kstr}`, v] }) if (sort) { keyvals = keyvals.sort((kv1, kv2) => { kv1 = kv1[0] kv2=kv2[0] return kv1 < kv2 ? -1 : kv1 > kv2 ? 1 : 0 }) } if (align) { let longest = 0; for (let i=keyvals.length;i--;) if (keyvals[i][0].length > longest) longest = keyvals[i][0].length for (let i=keyvals.length;i--;) keyvals[i][0] = _.padEnd(keyvals[i][0], longest) } let indent2 = dent + indent; for (let i=keyvals.length; i--; ){ let k = keyvals[i][0] let v = keyvals[i][1] let oLine = k + colon + build(v,'') keyvals[i] = (wrap===false || oLine.length <= wrap || !v || typeof v!="object") ? oLine : ( k + colon + build(v,indent2).replace(REGX_SPACES,'')) } return `${dent}${oBrace}\n${keyvals.join(`${oComma}\n`)}\n${dent}${eBrace}` } function buildRegExp() { return dent + _colorfy(colors, `"${o}"`, 'regex') } function buildString() { return dent + _colorfy(colors, `"${_.trim(o)}"`, 'string') } function buildBoolean() { return dent + _colorfy(colors, o, 'boolean') } function buildDefault() { return dent + o } function buildNumber() { let isFloat = Number(o) === o && o % 1 !== 0 o = isFloat && decimals ? o.toFixed(decimals) : o + '' return dent + _colorfy(colors, o, 'number') } if (o===null) return `${dent}${oNull}` if (o===undefined) return `${dent}${oUndefined}` if(_.isFunction(o)) return buildFunction() if(_.isRegExp(o)) return buildRegExp() if(_.isNumber(o)) return buildNumber() if(_.isString(o)) return buildString() if(_.isBoolean(o)) return buildBoolean() if(_.isArray(o)) return buildArray() if(_.isObject(o)) return buildObject() return buildDefault() } }