UNPKG

7.69 kBJavaScriptView Raw
1"use strict";
2
3var samsam = require("@sinonjs/samsam");
4var functionName = require("@sinonjs/commons").functionName;
5var typeOf = require("@sinonjs/commons").typeOf;
6
7var formatio = {
8 excludeConstructors: ["Object", /^.$/],
9 quoteStrings: true,
10 limitChildrenCount: 0
11};
12
13var specialObjects = [];
14/* istanbul ignore else */
15if (typeof global !== "undefined") {
16 specialObjects.push({ object: global, value: "[object global]" });
17}
18if (typeof document !== "undefined") {
19 specialObjects.push({
20 object: document,
21 value: "[object HTMLDocument]"
22 });
23}
24if (typeof window !== "undefined") {
25 specialObjects.push({ object: window, value: "[object Window]" });
26}
27
28function constructorName(f, object) {
29 var name = functionName(object && object.constructor);
30 var excludes = f.excludeConstructors || formatio.excludeConstructors;
31
32 var i, l;
33 for (i = 0, l = excludes.length; i < l; ++i) {
34 if (typeof excludes[i] === "string" && excludes[i] === name) {
35 return "";
36 } else if (excludes[i].test && excludes[i].test(name)) {
37 return "";
38 }
39 }
40
41 return name;
42}
43
44function isCircular(object, objects) {
45 if (typeof object !== "object") {
46 return false;
47 }
48 var i, l;
49 for (i = 0, l = objects.length; i < l; ++i) {
50 if (objects[i] === object) {
51 return true;
52 }
53 }
54 return false;
55}
56
57// eslint-disable-next-line complexity
58function ascii(f, object, processed, indent) {
59 if (typeof object === "string") {
60 if (object.length === 0) {
61 return "(empty string)";
62 }
63 var qs = f.quoteStrings;
64 var quote = typeof qs !== "boolean" || qs;
65 // eslint-disable-next-line quotes
66 return processed || quote ? '"' + object + '"' : object;
67 }
68
69 if (typeof object === "symbol") {
70 return object.toString();
71 }
72
73 if (typeof object === "function" && !(object instanceof RegExp)) {
74 return ascii.func(object);
75 }
76
77 // eslint supports bigint as of version 6.0.0
78 // https://github.com/eslint/eslint/commit/e4ab0531c4e44c23494c6a802aa2329d15ac90e5
79 // eslint-disable-next-line
80 if (typeOf(object) === "bigint") {
81 return object.toString();
82 }
83
84 var internalProcessed = processed || [];
85
86 if (isCircular(object, internalProcessed)) {
87 return "[Circular]";
88 }
89
90 if (typeOf(object) === "array") {
91 return ascii.array.call(f, object, internalProcessed);
92 }
93
94 if (!object) {
95 return String(1 / object === -Infinity ? "-0" : object);
96 }
97 if (samsam.isElement(object)) {
98 return ascii.element(object);
99 }
100
101 if (
102 typeof object.toString === "function" &&
103 object.toString !== Object.prototype.toString
104 ) {
105 return object.toString();
106 }
107
108 var i, l;
109 for (i = 0, l = specialObjects.length; i < l; i++) {
110 if (object === specialObjects[i].object) {
111 return specialObjects[i].value;
112 }
113 }
114
115 if (samsam.isSet(object)) {
116 return ascii.set.call(f, object, internalProcessed);
117 }
118
119 if (object instanceof Map) {
120 return ascii.map.call(f, object, internalProcessed);
121 }
122
123 return ascii.object.call(f, object, internalProcessed, indent);
124}
125
126ascii.func = function(func) {
127 var funcName = functionName(func) || "";
128 return "function " + funcName + "() {}";
129};
130
131function delimit(str, delimiters) {
132 var delims = delimiters || ["[", "]"];
133 return delims[0] + str + delims[1];
134}
135
136ascii.array = function(array, processed, delimiters) {
137 processed.push(array);
138 var pieces = [];
139 var i, l;
140 l =
141 this.limitChildrenCount > 0
142 ? Math.min(this.limitChildrenCount, array.length)
143 : array.length;
144
145 for (i = 0; i < l; ++i) {
146 pieces.push(ascii(this, array[i], processed));
147 }
148
149 if (l < array.length) {
150 pieces.push("[... " + (array.length - l) + " more elements]");
151 }
152
153 return delimit(pieces.join(", "), delimiters);
154};
155
156ascii.set = function(set, processed) {
157 return ascii.array.call(this, Array.from(set), processed, ["Set {", "}"]);
158};
159
160ascii.map = function(map, processed) {
161 return ascii.array.call(this, Array.from(map), processed, ["Map [", "]"]);
162};
163
164function getSymbols(object) {
165 if (samsam.isArguments(object)) {
166 return [];
167 }
168
169 /* istanbul ignore else */
170 if (typeof Object.getOwnPropertySymbols === "function") {
171 return Object.getOwnPropertySymbols(object);
172 }
173
174 /* istanbul ignore next: This is only for IE, since getOwnPropertySymbols
175 * does not exist on Object there
176 */
177 return [];
178}
179
180ascii.object = function(object, processed, indent) {
181 processed.push(object);
182 var internalIndent = indent || 0;
183 var pieces = [];
184 var properties = Object.keys(object)
185 .sort()
186 .concat(getSymbols(object));
187 var length = 3;
188 var prop, str, obj, i, k, l;
189 l =
190 this.limitChildrenCount > 0
191 ? Math.min(this.limitChildrenCount, properties.length)
192 : properties.length;
193
194 for (i = 0; i < l; ++i) {
195 prop = properties[i];
196 obj = object[prop];
197
198 if (isCircular(obj, processed)) {
199 str = "[Circular]";
200 } else {
201 str = ascii(this, obj, processed, internalIndent + 2);
202 }
203
204 str =
205 (typeof prop === "string" && /\s/.test(prop)
206 ? // eslint-disable-next-line quotes
207 '"' + prop + '"'
208 : prop.toString()) +
209 ": " +
210 str;
211 length += str.length;
212 pieces.push(str);
213 }
214
215 var cons = constructorName(this, object);
216 var prefix = cons ? "[" + cons + "] " : "";
217 var is = "";
218 for (i = 0, k = internalIndent; i < k; ++i) {
219 is += " ";
220 }
221
222 if (l < properties.length) {
223 pieces.push("[... " + (properties.length - l) + " more elements]");
224 }
225
226 if (length + internalIndent > 80) {
227 return (
228 prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + is + "}"
229 );
230 }
231 return prefix + "{ " + pieces.join(", ") + " }";
232};
233
234ascii.element = function(element) {
235 var tagName = element.tagName.toLowerCase();
236 var attrs = element.attributes;
237 var pairs = [];
238 var attr, attrName, i, l, val;
239
240 for (i = 0, l = attrs.length; i < l; ++i) {
241 attr = attrs.item(i);
242 attrName = attr.nodeName.toLowerCase().replace("html:", "");
243 val = attr.nodeValue;
244 if (attrName !== "contenteditable" || val !== "inherit") {
245 if (val) {
246 // eslint-disable-next-line quotes
247 pairs.push(attrName + '="' + val + '"');
248 }
249 }
250 }
251
252 var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
253 // SVG elements have undefined innerHTML
254 var content = element.innerHTML || "";
255
256 if (content.length > 20) {
257 content = content.substr(0, 20) + "[...]";
258 }
259
260 var res =
261 formatted + pairs.join(" ") + ">" + content + "</" + tagName + ">";
262
263 return res.replace(/ contentEditable="inherit"/, "");
264};
265
266function Formatio(options) {
267 // eslint-disable-next-line guard-for-in
268 for (var opt in options) {
269 this[opt] = options[opt];
270 }
271}
272
273Formatio.prototype = {
274 functionName: functionName,
275
276 configure: function(options) {
277 return new Formatio(options);
278 },
279
280 constructorName: function(object) {
281 return constructorName(this, object);
282 },
283
284 ascii: function(object, processed, indent) {
285 return ascii(this, object, processed, indent);
286 }
287};
288
289module.exports = Formatio.prototype;