bare-console 2.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/index.js +45 -338
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -8,8 +8,9 @@ npm i bare-console
8
8
 
9
9
  ## Usage
10
10
 
11
- ```javascript
11
+ ```js
12
12
  const Console = require('bare-console')
13
+
13
14
  const console = new Console({ stdout: process.stdout, stderr: process.stderr })
14
15
 
15
16
  console.log('Hello')
package/index.js CHANGED
@@ -1,28 +1,61 @@
1
- const { Crayon } = require('tiny-crayon')
1
+ const inspect = require('bare-inspect')
2
2
 
3
3
  module.exports = class Console {
4
4
  constructor (opts = {}) {
5
- const { isTTY } = adaptStream(opts.stdout || opts.stderr)
6
- this.colors = typeof opts.colors === 'boolean' ? opts.colors : isTTY
7
- this.crayon = new Crayon({ isTTY: this.colors })
5
+ this._stdout = adaptStream(opts.stdout)
6
+ this._stderr = adaptStream(opts.stderr)
7
+
8
+ this._colors = opts.colors === true
9
+ this._timers = new Map()
10
+
11
+ if (opts.bind) {
12
+ this.log = this.log.bind(this)
13
+ this.error = this.error.bind(this)
14
+ this.time = this.time.bind(this)
15
+ this.timeEnd = this.timeEnd.bind(this)
16
+ this.trace = this.trace.bind(this)
17
+ }
18
+ }
19
+
20
+ log (...args) {
21
+ let out = ''
22
+ let first = true
23
+
24
+ for (const arg of args) {
25
+ if (first) first = false
26
+ else out += ' '
27
+
28
+ out += typeof arg === 'string' ? arg : inspect(arg, { colors: this._colors })
29
+ }
30
+
31
+ this._stdout.write(out + '\n')
32
+ }
33
+
34
+ error (...args) {
35
+ let out = ''
36
+ let first = true
8
37
 
9
- this.log = this._print.bind(this, adaptStream(opts.stdout))
10
- this.error = this._print.bind(this, adaptStream(opts.stderr))
38
+ for (const arg of args) {
39
+ if (first) first = false
40
+ else out += ' '
41
+
42
+ out += typeof arg === 'string' ? arg : inspect(arg, { colors: this._colors })
43
+ }
11
44
 
12
- this.times = new Map()
45
+ this._stderr.write(out + '\n')
13
46
  }
14
47
 
15
48
  time (label = 'default') {
16
- if (this.times.has(label)) {
49
+ if (this._timers.has(label)) {
17
50
  this.error('Warning: Label \'' + label + '\' already exists for console.time()')
18
51
  return
19
52
  }
20
53
 
21
- this.times.set(label, process.hrtime())
54
+ this._timers.set(label, process.hrtime())
22
55
  }
23
56
 
24
57
  timeEnd (label = 'default') {
25
- const started = this.times.get(label)
58
+ const started = this._timers.get(label)
26
59
 
27
60
  if (!started) {
28
61
  this.error('Warning: No such label \'' + label + '\' for console.timeEnd()')
@@ -31,7 +64,7 @@ module.exports = class Console {
31
64
 
32
65
  const d = process.hrtime(started)
33
66
  const ms = d[0] * 1e3 + d[1] / 1e6
34
- this.times.delete(label)
67
+ this._timers.delete(label)
35
68
 
36
69
  if (ms > 1000) this.log(label + ': ' + (ms / 1000).toFixed(3) + 's')
37
70
  else this.log(label + ': ' + ms.toFixed(3) + 'ms')
@@ -46,334 +79,8 @@ module.exports = class Console {
46
79
 
47
80
  this.error('Trace: ' + messages.join(' ') + stack.slice(start))
48
81
  }
49
-
50
- // + clear () {}
51
-
52
- _print (stream, ...args) {
53
- const paint = new Paint(this.crayon)
54
- let identifier = 0
55
-
56
- for (let i = 0; i < args.length; i++) {
57
- const arg = args[i]
58
-
59
- const single = generateSingleValue(arg, { escape: false })
60
- if (single !== null) {
61
- paint.push('value', single)
62
- } else if (typeof arg === 'object') {
63
- let levels = 0
64
-
65
- iterateObject(arg)
66
-
67
- function iterateObject (arg, backward = new WeakSet(), forward = new WeakSet(), add = true) {
68
- if (add) backward.add(arg)
69
- else forward.add(arg)
70
-
71
- const id = identifier++
72
- const isArray = Array.isArray(arg)
73
- const isBuffer = Buffer.isBuffer(arg) && arg.constructor.name === 'Buffer'
74
- const isInts = !isBuffer && isIntArray(arg)
75
- const isObject = !(isArray || isInts || isBuffer)
76
- const brackets = isBuffer ? '<>' : (isInts || isArray ? '[]' : '{}')
77
-
78
- levels++
79
-
80
- if (levels >= 4 && !isObjectEmpty(arg)) {
81
- let type = isArray ? 'Array' : (typeof arg)
82
- type = type[0].toUpperCase() + type.slice(1)
83
- paint.push('value', '[' + type + ']', { id, crayon: 'cyan' })
84
- levels--
85
- return paint.width[id]
86
- }
87
-
88
- if (isInts) paint.push('open', arg.constructor.name + '(' + arg.length + ') ', { id })
89
- paint.push('open', brackets[0], { id })
90
- if (isBuffer) paint.push('open', 'Buffer', { id })
91
-
92
- const MAX = isObject ? Infinity : (isBuffer ? 50 : 100)
93
- let count = 0
94
-
95
- for (const key in arg) {
96
- const k = isObject ? key : parseInt(key, 10)
97
- const isNumeric = !isObject && isFinite(k)
98
- const v = arg[isNumeric ? k : key]
99
-
100
- if (isBuffer && !isNumeric && Object.hasOwn(Object.getPrototypeOf(arg), key)) continue
101
-
102
- if (count++ >= MAX) break
103
-
104
- if (count === 1) {
105
- paint.push('spacing-start', null, { id, levels, isArray, isInts, isBuffer, arg, k })
106
- } else {
107
- paint.push('separator', isBuffer && isNumeric ? '' : ',', { id })
108
- paint.push('spacing-sep', null, { id, levels, isArray, isInts, isBuffer, arg, k })
109
- }
110
-
111
- if (!isNumeric) {
112
- const singleKey = generateSingleKey(key)
113
- paint.push('key', [singleKey, ': '], { id })
114
- }
115
-
116
- const single = generateSingleValue(v, { levels, stringColor: true, intToHex: isBuffer })
117
- if (single !== null) {
118
- paint.push('value', single, { id })
119
- } else if (typeof v === 'object') {
120
- if (backward.has(v) || (!add && forward.has(v))) {
121
- paint.push('value', '[Circular]', { id, crayon: 'cyan' })
122
- continue
123
- }
124
-
125
- const subWidth = iterateObject(v, backward, forward, false)
126
- paint.width[id].child += subWidth.self + subWidth.child // + double check after colors fix
127
- } else {
128
- throw new Error('Argument not supported (' + (typeof v) + '): ' + v)
129
- }
130
- }
131
-
132
- const symbols = Object.getOwnPropertySymbols(arg)
133
-
134
- for (const symbol of symbols) {
135
- count++
136
-
137
- if (count === 1) {
138
- paint.push('spacing-start', null, { id, levels })
139
- } else {
140
- paint.push('separator', ',', { id })
141
- paint.push('spacing-sep', null, { id, levels })
142
- }
143
-
144
- if (!isArray) {
145
- paint.push('key', ['[', { out: symbol.toString(), crayon: 'green' }, ']', ': '], { id })
146
- }
147
-
148
- const single = generateSingleValue(arg[symbol], { levels })
149
- if (single === null) throw new Error('Symbol value not supported: (' + (typeof arg[symbol]) + '): ' + arg[symbol])
150
- paint.push('value', single, { id })
151
- }
152
-
153
- if (!isObject && arg.length > MAX) paint.push('more', null, { id, levels, isArray, isInts, isBuffer, arg, left: (arg.length - MAX) })
154
-
155
- if (count > 0) paint.push('spacing-end', null, { id, levels, isArray, isInts, isBuffer, arg })
156
-
157
- if (count === 0 && isBuffer) paint.push('spacing-sep', null, { id, levels, isArray, isInts, isBuffer, arg })
158
-
159
- paint.push('close', brackets[1], { id })
160
-
161
- levels--
162
-
163
- return paint.width[id]
164
- }
165
- } else {
166
- throw new Error('Argument not supported (' + (typeof arg) + '): ' + arg)
167
- }
168
-
169
- if (i + 1 !== args.length) paint.push('space', ' ')
170
- }
171
-
172
- paint.push('break-line', '\n')
173
-
174
- stream.write(paint.done())
175
-
176
- function generateSingleKey (key) {
177
- if (key === '') return { out: "''", crayon: 'green' }
178
-
179
- const names = ['undefined', 'null', 'true', 'false', 'NaN', 'Infinity']
180
- if (names.indexOf(key) > -1) return { out: key }
181
-
182
- if (isKindOfAlphaNumeric(key) && !isFinite(key)) return { out: key }
183
-
184
- return { out: "'" + key + "'", crayon: 'green' }
185
- }
186
-
187
- function generateSingleValue (value, { levels = 0, stringColor = false, escape = true, intToHex = false } = {}) {
188
- if (typeof value === 'undefined') return { out: 'undefined', crayon: 'blackBright' }
189
- if (value === null) return { out: 'null', crayon: 'bold' }
190
-
191
- if (typeof value === 'string') return stringColor ? { out: dynamicQuotes(value, { escape }), crayon: 'green' } : { out: dynamicQuotes(value, { escape }) }
192
- if (typeof value === 'number') return intToHex ? { out: numberToHex(value) } : { out: value.toString(), crayon: 'yellow' }
193
- if (typeof value === 'boolean') return { out: value.toString(), crayon: 'yellow' }
194
- if (typeof value === 'function') return { out: (value.name ? '[Function: ' + value.name + ']' : '[Function (anonymous)]'), crayon: 'cyan' }
195
- if (typeof value === 'symbol') return { out: value.toString(), crayon: 'green' }
196
- if (typeof value === 'bigint') return { out: value.toString() + 'n', crayon: 'yellow' } // + edge case: typeof Object(1n) === 'object'
197
-
198
- if (value instanceof Promise) return { out: 'Promise' }
199
- if (value instanceof RegExp) return { out: value.toString(), crayon: 'red' }
200
-
201
- // + AggregateError?
202
- if (value instanceof Error) return { out: value.stack } // This includes EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError
203
- if (value instanceof String) return { out: '[String: ' + dynamicQuotes(value.toString()) + ']', crayon: 'green' }
204
- if (value instanceof Number) return { out: '[Number: ' + value.toString() + ']', crayon: 'yellow' }
205
- if (value instanceof Boolean) return { out: '[Boolean: ' + value.toString() + ']', crayon: 'yellow' }
206
- if (value instanceof Date) return { out: value.toISOString(), crayon: 'magenta' }
207
-
208
- if (value instanceof Map) return { out: 'Map(' + value.size + ') {' + (value.size ? ' ... ' : '') + '}' }
209
- if (value instanceof Set) return { out: 'Set(' + value.size + ') {' + (value.size ? ' ... ' : '') + '}' }
210
-
211
- if (value instanceof WeakMap) return [{ out: 'WeakMap { ' }, { out: '<items unknown>', crayon: 'cyan' }, { out: ' }' }]
212
- if (value instanceof WeakSet) return [{ out: 'WeakSet { ' }, { out: '<items unknown>', crayon: 'cyan' }, { out: ' }' }]
213
-
214
- return null
215
- }
216
- }
217
- }
218
-
219
- function numberToHex (value) {
220
- return value.toString(16).padStart(2, '0')
221
- }
222
-
223
- function isIntArray (value) {
224
- if (value instanceof Uint8Array || value instanceof Uint16Array || value instanceof Uint32Array) return true
225
- if (value instanceof Int8Array || value instanceof Int16Array || value instanceof Int32Array) return true
226
- return false
227
- }
228
-
229
- function dynamicQuotes (str, opts = {}) {
230
- if (opts.escape === false) return str
231
-
232
- if (str.indexOf("'") === -1) return "'" + escapeString(str) + "'"
233
- if (str.indexOf('"') === -1) return '"' + escapeString(str) + '"'
234
- if (str.indexOf('`') === -1) return '`' + escapeString(str) + '`'
235
-
236
- return "'" + escapeString(str, true) + "'"
237
- }
238
-
239
- function escapeString (str, singled = false) {
240
- str = str
241
- .replace(/[\\]/g, '\\\\')
242
- .replace(/[/]/g, '\\/')
243
- .replace(/[\b]/g, '\\b')
244
- .replace(/[\f]/g, '\\f')
245
- .replace(/[\n]/g, '\\n')
246
- .replace(/[\r]/g, '\\r')
247
- .replace(/[\t]/g, '\\t')
248
-
249
- if (singled) str = str.replace(/[']/g, '\\\'')
250
-
251
- return str
252
82
  }
253
83
 
254
84
  function adaptStream (stream) {
255
- if (typeof stream === 'function') {
256
- return { isTTY: true, write: stream }
257
- }
258
- return stream
259
- }
260
-
261
- class Paint {
262
- constructor (crayon) {
263
- this.prints = []
264
- this.width = { all: 0 }
265
- this.crayon = crayon
266
- }
267
-
268
- push (type, chunk = null, opts = {}) {
269
- if (Array.isArray(chunk)) return chunk.forEach(value => this.push(type, value, opts))
270
-
271
- let color = opts.crayon
272
-
273
- if (chunk && typeof chunk === 'object') {
274
- color = chunk.crayon
275
- chunk = chunk.out
276
- }
277
-
278
- if (chunk !== null) {
279
- this.width.all += chunk.length // + it's not including spaces as it's mostly dynamic
280
-
281
- if (opts && opts.id !== undefined) {
282
- if (!this.width[opts.id]) this.width[opts.id] = { self: 0, child: 0 }
283
- this.width[opts.id].self += chunk.length
284
- }
285
- }
286
-
287
- if (color) chunk = this.crayon[color](chunk)
288
-
289
- this.prints.push({ type, chunk, ...opts })
290
- }
291
-
292
- done () {
293
- let output = ''
294
-
295
- for (const print of this.prints) {
296
- // raw
297
- if (['open', 'close', 'key', 'value', 'separator', 'space', 'break-line'].indexOf(print.type) > -1) {
298
- output += print.chunk
299
- continue
300
- }
301
-
302
- // dynamic
303
- if (print.type === 'spacing-start' || print.type === 'spacing-sep' || print.type === 'spacing-end') {
304
- const type = print.type.replace('spacing-', '')
305
- const totalWidth = this.width[print.id] ? (this.width[print.id].self + this.width[print.id].child) : this.width.all
306
- const expand = print.isInts || print.isArray || totalWidth > 60 // + 64? double check after colors fix
307
- let arrayBreakpoint = 0
308
-
309
- if (print.isInts || print.isArray) {
310
- const lengths = [7, 9, 13, 17, 23, 29, 37, 45, 53]
311
-
312
- for (let i = lengths.length - 1; i >= 0; i--) {
313
- if (print.arg.length >= lengths[i]) {
314
- arrayBreakpoint = 4 + i // Range: 4-12
315
- break
316
- }
317
- }
318
- }
319
-
320
- if (print.isBuffer) {
321
- if (type === 'start' || type === 'sep') output += ' '
322
- continue
323
- }
324
-
325
- if (print.isInts || print.isArray) {
326
- const addSpacing = arrayBreakpoint !== 0
327
- const skipSpacing = type === 'sep' && !(print.k % arrayBreakpoint === 0)
328
-
329
- if (!addSpacing || skipSpacing) {
330
- output += ' '
331
- continue
332
- }
333
- }
334
-
335
- if (!expand) output += ' '
336
- else output += '\n' + ' '.repeat(print.levels - (type === 'end' ? 1 : 0))
337
-
338
- continue
339
- }
340
-
341
- if (print.type === 'more') {
342
- const addSpacing = !print.isBuffer
343
- const sep = print.isBuffer ? '' : ','
344
-
345
- output += addSpacing ? (',\n' + ' '.repeat(print.levels)) : (sep + ' ')
346
- output += '... ' + print.left + ' more ' + (print.isBuffer ? 'byte' : 'item') + (print.left >= 2 ? 's' : '')
347
-
348
- continue
349
- }
350
-
351
- throw new Error('Invalid print: ' + JSON.stringify(print))
352
- }
353
-
354
- return output
355
- }
356
- }
357
-
358
- function isObjectEmpty (obj) {
359
- for (const k in obj) return false // eslint-disable-line no-unreachable-loop
360
- return true
361
- }
362
-
363
- function isKindOfAlphaNumeric (str) {
364
- for (let i = 0; i < str.length; i++) {
365
- const code = str.charCodeAt(i)
366
-
367
- // first char, and numeric (0-9)
368
- if (i === 0 && (code > 47 && code < 58)) return false
369
-
370
- if (!(code > 47 && code < 58) && // numeric (0-9)
371
- !(code > 64 && code < 91) && // upper alpha (A-Z)
372
- !(code > 96 && code < 123) && // lower alpha (a-z)
373
- !(code === 95)) { // underscore (_)
374
- return false
375
- }
376
- }
377
-
378
- return true
85
+ return typeof stream === 'function' ? { write: stream } : stream
379
86
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "bare-console",
3
- "version": "2.0.2",
3
+ "version": "3.1.0",
4
4
  "description": "Simple debugging console for JavaScript",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "index.js"
8
8
  ],
9
9
  "scripts": {
10
- "test": "standard && bare test.js"
10
+ "test": "standard && brittle test.js"
11
11
  },
12
12
  "repository": {
13
13
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "homepage": "https://github.com/holepunchto/bare-console#readme",
22
22
  "dependencies": {
23
- "tiny-crayon": "^1.0.4"
23
+ "bare-inspect": "^1.0.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "brittle": "^3.1.1",