ninjs-lodash
Version:
lodash wrapper + utils
273 lines (229 loc) • 8.23 kB
JavaScript
/**
* JPretty
* pretty print js/json
*/
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()
}
}