From ab989f13f157da5b44a791043eaaa3de6dd18468 Mon Sep 17 00:00:00 2001 From: chylex <contact@chylex.com> Date: Mon, 24 Jul 2017 11:46:13 +0200 Subject: [PATCH] Add support for being included as an npm module --- README.md | 2 +- objtree.js | 371 ++++++++++++++++++++++++++++------------------------- 2 files changed, 197 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index 25288c2..e6dfc93 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -`objtree` is a script that generates a text tree representation of an object. The script can be executed in a browser console. +`objtree` is a script that generates a text tree representation of an object. The script can be executed in a browser console or imported as an `npm` module. # How to Use diff --git a/objtree.js b/objtree.js index 175a424..276f1d9 100644 --- a/objtree.js +++ b/objtree.js @@ -1,194 +1,215 @@ -const OBJTREE_OBJECT = 0; -const OBJTREE_UNKNOWN = 1; -const OBJTREE_COMPLEX_FUNCTION = 2; -const OBJTREE_SIMPLE_FUNCTION = 3; -const OBJTREE_PRIMARRAY = 4; -const OBJTREE_VARIABLE = 5; -const OBJTREE_TOODEEP = 6; - -const OBJTREE_NAMES = [ - "[obj]", "[???]", "[fun]", "[fun]", "[arr]", "[var]", "[!!!]" -]; - -var objtree = function(target, { - maxlevel = 10, - grandparent = "", - exclude = [] -} = {}){ - var excludeRules = exclude.map(rule => new RegExp(rule)); +(function(){ + const OBJTREE_OBJECT = 0; + const OBJTREE_UNKNOWN = 1; + const OBJTREE_COMPLEX_FUNCTION = 2; + const OBJTREE_SIMPLE_FUNCTION = 3; + const OBJTREE_PRIMARRAY = 4; + const OBJTREE_VARIABLE = 5; + const OBJTREE_TOODEEP = 6; - var getObjectDesc = function(obj){ - switch(typeof obj){ - case "object": - if (obj === null){ - return [ OBJTREE_VARIABLE, "(null)" ]; - } - else if (Array.isArray(obj)){ - if (obj.length === 0){ - return [ OBJTREE_PRIMARRAY, "[]" ]; - } - else if (obj.every(ele => { - let type = typeof ele; - return type === "boolean" || type === "number" || (type === "string" && ele.indexOf('\n') === -1); - })){ - return [ OBJTREE_PRIMARRAY, "[ "+obj.join(", ")+" ]" ]; - } - } - - return [ OBJTREE_OBJECT ]; // special handling - - case "function": - if (Object.keys(obj).length === 0 && (!obj.prototype || Object.keys(obj.prototype).length === 0)){ - return [ OBJTREE_SIMPLE_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ]; - } - else{ - return [ OBJTREE_COMPLEX_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ]; - } - - case "boolean": - case "number": - case "string": - return [ OBJTREE_VARIABLE, obj ]; - - case "undefined": - return [ OBJTREE_VARIABLE, "(undefined)" ]; - - default: - return [ OBJTREE_UNKNOWN, obj.toString() ]; - } - }; + const OBJTREE_NAMES = [ + "[obj]", "[???]", "[fun]", "[fun]", "[arr]", "[var]", "[!!!]" + ]; - var generateTree = function(node, parents, level){ - let res = {}; + var objtree = function(target, { + maxlevel = 10, + grandparent = "", + exclude = [] + } = {}){ + var excludeRules = exclude.map(rule => new RegExp(rule)); - for(let key in node){ - if (excludeRules.length){ - let fullKey = parents+key; - - if (excludeRules.some(rule => rule.test(fullKey))){ - res[key] = { - type: OBJTREE_UNKNOWN, - value: "(excluded)" - }; - - continue; - } - } - - let obj = node[key]; - let [ type, value ] = getObjectDesc(obj); - - if (type === OBJTREE_OBJECT){ - if (level > maxlevel){ - type = OBJTREE_TOODEEP; - value = "(too deep)"; - } - else{ - value = generateTree(obj, parents+key+"/", level+1); - } - } - else if (type === OBJTREE_COMPLEX_FUNCTION){ - let data = { __args: value }; - - for(let fkey in obj){ - data[fkey] = obj[fkey]; - } - - if (obj.prototype){ - data.prototype = {}; - - for(let pkey in obj.prototype){ - if (pkey !== "constructor"){ - data.prototype[pkey] = obj.prototype[pkey]; + var getObjectDesc = function(obj){ + switch(typeof obj){ + case "object": + if (obj === null){ + return [ OBJTREE_VARIABLE, "(null)" ]; + } + else if (Array.isArray(obj)){ + if (obj.length === 0){ + return [ OBJTREE_PRIMARRAY, "[]" ]; + } + else if (obj.every(ele => { + let type = typeof ele; + return type === "boolean" || type === "number" || (type === "string" && ele.indexOf('\n') === -1); + })){ + return [ OBJTREE_PRIMARRAY, "[ "+obj.join(", ")+" ]" ]; } } - } - - value = generateTree(data, parents+key+"/", level+1); - } - - res[key] = { type, value }; - } - - return res; - }; - - var tree = generateTree(target, "", 1); - - var obj = { - asObj: function(){ - return tree; - }, - - asText: function(){ - var lines = [ "OBJECT TREE", "===========" ]; - var grandpa = " "+(grandparent ? grandparent+"." : ""); - - var sorter = function(entry1, entry2){ - let v = entry1[1].type - entry2[1].type; - return v === 0 ? +(entry1[0] > entry2[0]) : v; - }; - - var keyRegex = /^[a-z_$][a-z0-9_$]+$/i; - - var getKeyAccess = function(key){ - return keyRegex.test(key) ? "."+key : "['"+key+"']"; - } - - var varTypes = [ - OBJTREE_VARIABLE, OBJTREE_PRIMARRAY, OBJTREE_UNKNOWN, OBJTREE_TOODEEP - ]; - - var addLines = function(node, parents, level){ - let entries = Object.entries(node); - let prefix = " ".repeat(level)+"|-- "; - - let longest = Math.max.apply(null, entries - .filter(entry => varTypes.includes(entry[1].type)) - .map(entry => (level === 0 ? entry[0] : getKeyAccess(entry[0])).length) - ); - - for(let [key, desc] of entries.sort(sorter)){ - let keyText = level === 0 ? key : getKeyAccess(key); - if (desc.type === OBJTREE_OBJECT){ - lines.push(prefix+grandpa+parents+keyText); - addLines(desc.value, parents+keyText, level+1); + return [ OBJTREE_OBJECT ]; // special handling + + case "function": + if (Object.keys(obj).length === 0 && (!obj.prototype || Object.keys(obj.prototype).length === 0)){ + return [ OBJTREE_SIMPLE_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ]; } else{ - let commonPre = prefix+OBJTREE_NAMES[desc.type]+grandpa+parents+keyText; + return [ OBJTREE_COMPLEX_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ]; + } + + case "boolean": + case "number": + case "string": + return [ OBJTREE_VARIABLE, obj ]; + + case "undefined": + return [ OBJTREE_VARIABLE, "(undefined)" ]; + + default: + return [ OBJTREE_UNKNOWN, obj.toString() ]; + } + }; + + var generateTree = function(node, parents, level){ + let res = {}; + + for(let key in node){ + if (excludeRules.length){ + let fullKey = parents+key; + + if (excludeRules.some(rule => rule.test(fullKey))){ + res[key] = { + type: OBJTREE_UNKNOWN, + value: "(excluded)" + }; - if (desc.type === OBJTREE_SIMPLE_FUNCTION){ - lines.push(commonPre+desc.value); + continue; + } + } + + let obj = node[key]; + let [ type, value ] = getObjectDesc(obj); + + if (type === OBJTREE_OBJECT){ + if (level > maxlevel){ + type = OBJTREE_TOODEEP; + value = "(too deep)"; + } + else{ + value = generateTree(obj, parents+key+"/", level+1); + } + } + else if (type === OBJTREE_COMPLEX_FUNCTION){ + let data = { __args: value }; + + for(let fkey in obj){ + data[fkey] = obj[fkey]; + } + + if (obj.prototype){ + data.prototype = {}; + + for(let pkey in obj.prototype){ + if (pkey !== "constructor"){ + data.prototype[pkey] = obj.prototype[pkey]; + } } - else if (desc.type === OBJTREE_COMPLEX_FUNCTION){ - lines.push(commonPre+desc.value.__args.value); - delete desc.value.__args; + } + + value = generateTree(data, parents+key+"/", level+1); + } + + res[key] = { type, value }; + } + + return res; + }; + + var tree = generateTree(target, "", 1); + + var obj = { + asObj: function(){ + return tree; + }, + + asText: function(){ + var lines = [ "OBJECT TREE", "===========" ]; + var grandpa = " "+(grandparent ? grandparent+"." : ""); + + var sorter = function(entry1, entry2){ + let v = entry1[1].type - entry2[1].type; + return v === 0 ? +(entry1[0] > entry2[0]) : v; + }; + + var keyRegex = /^[a-z_$][a-z0-9_$]+$/i; + + var getKeyAccess = function(key){ + return keyRegex.test(key) ? "."+key : "['"+key+"']"; + } + + var varTypes = [ + OBJTREE_VARIABLE, OBJTREE_PRIMARRAY, OBJTREE_UNKNOWN, OBJTREE_TOODEEP + ]; + + var addLines = function(node, parents, level){ + let entries = Object.entries(node); + let prefix = " ".repeat(level)+"|-- "; + + let longest = Math.max.apply(null, entries + .filter(entry => varTypes.includes(entry[1].type)) + .map(entry => (level === 0 ? entry[0] : getKeyAccess(entry[0])).length) + ); + + for(let [key, desc] of entries.sort(sorter)){ + let keyText = level === 0 ? key : getKeyAccess(key); + + if (desc.type === OBJTREE_OBJECT){ + lines.push(prefix+grandpa+parents+keyText); addLines(desc.value, parents+keyText, level+1); } else{ - lines.push(commonPre+(" ".repeat(longest-keyText.length))+" > "+desc.value); + let commonPre = prefix+OBJTREE_NAMES[desc.type]+grandpa+parents+keyText; + + if (desc.type === OBJTREE_SIMPLE_FUNCTION){ + lines.push(commonPre+desc.value); + } + else if (desc.type === OBJTREE_COMPLEX_FUNCTION){ + lines.push(commonPre+desc.value.__args.value); + delete desc.value.__args; + addLines(desc.value, parents+keyText, level+1); + } + else{ + lines.push(commonPre+(" ".repeat(longest-keyText.length))+" > "+desc.value); + } } } - } - }; + }; + + addLines(tree, "", 0); + return lines.join("\n"); + }, - addLines(tree, "", 0); - return lines.join("\n"); - }, + downloadText: function(filename){ + if (typeof window === "undefined"){ + throw "objtree.downloadText is only supported in a browser"; + } + + let url = window.URL.createObjectURL(new Blob([obj.asText()], { "type": "octet/stream" })); + let ele = document.createElement("a"); + document.body.appendChild(ele); + ele.href = url; + ele.download = filename; + ele.style.display = "none"; + ele.click(); + document.body.removeChild(ele); + window.URL.revokeObjectURL(url); + } + }; - downloadText: function(filename){ - let url = window.URL.createObjectURL(new Blob([obj.asText()], { "type": "octet/stream" })); - let ele = document.createElement("a"); - document.body.appendChild(ele); - ele.href = url; - ele.download = filename; - ele.style.display = "none"; - ele.click(); - document.body.removeChild(ele); - window.URL.revokeObjectURL(url); - } + return obj; }; - return obj; -}; + objtree.TYPE_OBJECT = OBJTREE_OBJECT; + objtree.TYPE_UNKNOWN = OBJTREE_UNKNOWN; + objtree.TYPE_COMPLEX_FUNCTION = OBJTREE_COMPLEX_FUNCTION; + objtree.TYPE_SIMPLE_FUNCTION = OBJTREE_SIMPLE_FUNCTION; + objtree.TYPE_PRIMARRAY = OBJTREE_PRIMARRAY; + objtree.TYPE_VARIABLE = OBJTREE_VARIABLE; + objtree.TYPE_TOODEEP = OBJTREE_TOODEEP; + + if (typeof module !== "undefined" && typeof module.exports !== "undefined"){ + module.exports = objtree; + } + else{ + window.objtree = objtree; + } +})();