@nejs/basic-extensions 2.8.0 → 2.9.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.
package/package.json CHANGED
@@ -60,10 +60,10 @@
60
60
  "repl": "npm run build && node --no-warnings repl.bootstrap.js"
61
61
  },
62
62
  "type": "module",
63
- "version": "2.8.0",
63
+ "version": "2.9.0",
64
64
  "dependencies": {
65
- "@nejs/extension": "^2.15.0",
65
+ "@nejs/extension": "^2.20.0",
66
66
  "zod": "^3.22.5"
67
67
  },
68
- "browser": "dist/@nejs/basic-extensions.bundle.2.7.0.js"
69
- }
68
+ "browser": "dist/@nejs/basic-extensions.bundle.2.9.0.js"
69
+ }
@@ -65,8 +65,9 @@ export const JSONExtensions = new Patch(JSON, {
65
65
  * // Using `mightContain`
66
66
  * console.log(JSON.mightContain(str)) // Output: true
67
67
  */
68
- mightContain(string) {
69
- return !!this.JSONStartPattern.exec(string)
68
+ mightContain(string, detail = false) {
69
+ const results = this.JSONStartPattern.exec(string)
70
+ return detail ? [!!results, results?.index ?? -1, results] : !!results
70
71
  },
71
72
 
72
73
  /**
@@ -181,18 +181,27 @@ export const ObjectExtensions = new Patch(Object, {
181
181
  return Object.defineProperty(object, key, { ...properties, get, set })
182
182
  },
183
183
 
184
+ addAccessor(to, key, getter, setter, storage) {
185
+ const store = storage ?? (!getter && !setter) ? true : undefined;
186
+ return this.add({ to, key, get: getter, set: setter, storage: store })
187
+ },
188
+
189
+ addData(to, key, value) {
190
+ return this.add({ to, key, value })
191
+ },
192
+
184
193
  add(...args) {
185
194
  const { isDescriptor } = Descriptor
186
195
  const { isObject: isObj } = this
187
196
  const { kDescriptorStore } = this
188
197
 
189
- let toObject, key, value, _get, _set, storage, storageKey
198
+ let obj, key, value, _get, _set, storage, storageKey
190
199
  let _type, _flag, _desc
191
200
 
192
201
  // Check to see if we received multiple arguments or an object
193
202
  if (args.length && isObj(args[0])) {
194
203
  ({
195
- toObject: obj,
204
+ to: obj,
196
205
  key,
197
206
  value,
198
207
  get: _get,
@@ -206,7 +215,7 @@ export const ObjectExtensions = new Patch(Object, {
206
215
  }
207
216
  else if (args.length > 1) {
208
217
  ([
209
- toObject,
218
+ to,
210
219
  _type,
211
220
  key,
212
221
  getOrValue,
@@ -217,6 +226,7 @@ export const ObjectExtensions = new Patch(Object, {
217
226
  _desc,
218
227
  ] = args)
219
228
 
229
+ obj = to
220
230
  _type = (
221
231
  ['accessor', 'data'].includes(_type.toLowerCase())
222
232
  ? _type.toLowerCase() : 'data'
@@ -225,6 +235,11 @@ export const ObjectExtensions = new Patch(Object, {
225
235
  _value = _type === 'data' ? getOrValue : undefined
226
236
  }
227
237
 
238
+ if (!this.isObject(obj)) {
239
+ console.warn('Object.add() must receive an object for `toObject`')
240
+ return obj;
241
+ }
242
+
228
243
  const more = isDescriptor(_desc) ? _desc : {}
229
244
  const flag = _flag || Object.definitionType.mutablyVisible
230
245
  const props = { ...Patch.getDescriptorOverridesFromSymbol(flag), ...more }
@@ -250,7 +265,7 @@ export const ObjectExtensions = new Patch(Object, {
250
265
  }
251
266
  // store should be defined by here: object or undefined
252
267
 
253
- if (!get && !set && makeStore) {
268
+ if ((!get && !set) && makeStore) {
254
269
  // being lazy here, someone has defined we make an accessor but
255
270
  // wants the default accessor behaviors with an associated store
256
271
  // made by us.
@@ -261,11 +276,11 @@ export const ObjectExtensions = new Patch(Object, {
261
276
  writable: true,
262
277
  })
263
278
 
264
- get = () => this[kDescriptorStore].data[storeKey]
279
+ get = () => this[kDescriptorStore]?.data?.[storeKey]
265
280
  set = (value) => { this[kDescriptorStore].data[storeKey] = value }
266
281
  }
267
282
 
268
- else if (get.length && set.length > 1 && store) {
283
+ else if (get?.length && set?.length > 1 && store) {
269
284
  // if we received a get or set that takes more arguments than
270
285
  // expected, assume the last argument should be the store variable
271
286
  // so we execute the supplied function with the storage and its
@@ -273,68 +273,287 @@ export const StringExtensions = new Patch(String, {
273
273
  return `rgba(${red}, ${green}, ${blue}, ${alpha.toFixed(2)})`
274
274
  },
275
275
 
276
+ /**
277
+ * Applies Select Graphic Rendition (SGR) parameters to a given message for
278
+ * styling in terminal environments. This function allows for the dynamic
279
+ * styling of text output using ANSI escape codes. It supports a variety of
280
+ * modes such as color, brightness, and text decorations like bold or underline.
281
+ *
282
+ * @param {string} message The message to be styled.
283
+ * @param {...string} useModes A series of strings representing the desired
284
+ * styling modes. Modes can include colors (e.g., 'red', 'blue'), brightness
285
+ * ('bright'), foreground/background ('fg', 'bg'), and text decorations
286
+ * ('bold', 'underline'). Modes can be combined in a single string using
287
+ * commas or passed as separate arguments.
288
+ *
289
+ * Colors:
290
+ * ```
291
+ * 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
292
+ * ```
293
+ * Color Specifiers:
294
+ * ```
295
+ * 'fg' -> foreground | 'bg' -> background | 'bright' -> bright colors
296
+ * ```
297
+ *
298
+ * Modes:
299
+ * ```
300
+ * 'blink' or 'k' | 'conceal' or 'c' | 'italics' or 'i' | 'strike' or 's'
301
+ * 'bold' or 'b' | 'dim' or 'd' | 'negative' or 'n' | 'underline' or 'u'
302
+ * ```
303
+ *
304
+ * Examples:
305
+ * - `sgr('Hello', 'red')` applies red color to 'Hello'.
306
+ * - `sgr('World', 'green,bold')` applies green color and bold styling
307
+ * to 'World'.
308
+ * - `sgr('Example', 'bluebgbright')` applies bright blue
309
+ * background color.
310
+ *
311
+ * Short hand syntax is also allowed:
312
+ * - `sgr('hello', 'biu')` applies bold, italics and underline
313
+ * - `sgr('hello', 'bi,redfg')` applies bold, italics and red foreground
314
+ *
315
+ * As a bonus, there is a secret getter applied to the return string that
316
+ * allows you to invoke `sgr(...).show` to automatically log the output to
317
+ * `console.log`. This is done by wrapping the output string in `Object()`
318
+ * to make it a `String` instance and then adding the property descriptor.
319
+ * A custom `Symbol` is applied to make it evaluate in nodejs as though it
320
+ * were a normal string. To strip the extras, wrap the output in `String()`
321
+ *
322
+ * @returns {string} The message wrapped in ANSI escape codes corresponding
323
+ * to the specified modes. The returned string, when printed to a terminal,
324
+ * displays the styled message. Additional properties are attached to the
325
+ * result for utility purposes, such as 'show' for immediate console output.
326
+ */
327
+ sgr(message, ...useModes) {
328
+ const colors = Object.assign(
329
+ ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'],
330
+ {
331
+ isBG: a => !!/bg/i.exec(a),
332
+ isBright: a => !!/bright/i.exec(a),
333
+ isColor: a => {
334
+ let color = colors.find(c => new RegExp(c, 'i').exec(a));
335
+ return [!!color, colors.indexOf(color)];
336
+ },
337
+ }
338
+ );
339
+
340
+ const arrayifyString = s => {
341
+ if (Array.isArray(s)) {
342
+ let results = [];
343
+
344
+ for (const i of s) {
345
+ results = [ ...results, ...arrayifyString(i) ];
346
+ }
347
+
348
+ return results.flat().filter(i => i.length);
349
+ }
350
+
351
+ if (!s || typeof s !== 'string') {
352
+ return [''];
353
+ }
354
+ else if (s.includes(',')) {
355
+ return arrayifyString(s.split(','));
356
+ }
357
+ else {
358
+ if (!colors.isColor(s)[0] && s.length > 1) {
359
+ return [...s];
360
+ }
361
+ else return [s];
362
+ }
363
+ }
364
+
365
+ let modes = arrayifyString(useModes)
366
+
367
+ const sgrModes = {
368
+ blink: ['\x1b[5m', '\x1b[25m', 'k'],
369
+ bold: ['\x1b[1m', '\x1b[22m', 'b'],
370
+ conceal: ['\x1b[8m', '\x1b[28m', 'c'],
371
+ dim: ['\x1b[2m', '\x1b[22m', 'd'],
372
+ italics: ['\x1b[3m', '\x1b[23m', 'i'],
373
+ negative: ['\x1b[7m', '\x1b[27m', 'n'],
374
+ strike: ['\x1b[9m', '\x1b[29m', 's'],
375
+ underline: ['\x1b[4m', '\x1b[24m', 'u'],
376
+ };
377
+
378
+ Object.values(sgrModes).forEach(mode => sgrModes[mode[2]] = mode);
379
+
380
+ const codes = a => {
381
+ let open = '', close = '', mode = String(a).toLowerCase();
382
+ let [_isColor, colorIndex] = colors.isColor(mode);
383
+
384
+ if (_isColor) {
385
+ open = colors.isBG(mode)
386
+ ? `\x1b[${colors.isBright(mode) ? 10 : 4}${colorIndex}m`
387
+ : `\x1b[${colors.isBright(mode) ? 9 : 3}${colorIndex}m`;
388
+ close = colors.isBG(mode) ? '\x1b[49m' : `\x1b[39m`;
389
+ }
390
+ else if (sgrModes[mode]) {
391
+ open = sgrModes[mode][0];
392
+ close = sgrModes[mode][1];
393
+ }
394
+
395
+ return [open, close];
396
+ };
397
+
398
+ const onOrder = modes.map(key => codes(key)[0]).join('');
399
+ const offOrder = modes.map(key => codes(key)[1]).reverse().join('');
400
+
401
+ let result = Object(`${onOrder}${message}${offOrder}`)
402
+
403
+ Object.defineProperties(result, {
404
+ show: {
405
+ get() { console.log(String(this)); return this },
406
+ enumerable: false,
407
+ },
408
+ [Symbol.for('nodejs.util.inspect.custom')]: {
409
+ value(depth, options, inspect) {
410
+ return inspect(String(this), options)
411
+ },
412
+ enumerable: false,
413
+ },
414
+ })
415
+
416
+ return result
417
+ },
418
+
419
+ /**
420
+ * Wraps an object's properties into a formatted string.
421
+ *
422
+ * This method takes an object and a set of options to format the
423
+ * object's properties into a string. It allows customization of
424
+ * indentation, line endings, maximum line length, and more.
425
+ *
426
+ * @param {Object} [object=globalThis] - The object to wrap.
427
+ * @param {Object} [options={}] - The formatting options.
428
+ * @param {number} [options.indent=2] - The number of indentation
429
+ * characters to use.
430
+ * @param {string} [options.indentCharacter=' '] - The character to use
431
+ * for indentation.
432
+ * @param {Array} [options.inspector=[Object, 'getOwnPropertyNames']] -
433
+ * The inspector to use for retrieving object properties.
434
+ * @param {string} [options.lineEnding='\n'] - The line ending character.
435
+ * @param {number} [options.maxLen=78] - The maximum line length.
436
+ * @param {Function} [options.perLine=undefined] - A function to apply
437
+ * per line of output.
438
+ * @param {Function} [options.perLinePerProperty=undefined] - A function
439
+ * to apply per property per line of output.
440
+ * @param {Function} [options.preProcess=undefined] - A function to
441
+ * preprocess the object's properties.
442
+ * @param {Function} [options.preReturn=undefined] - A function to apply
443
+ * to the final output before returning.
444
+ * @param {string} [options.separator=', '] - The separator to use
445
+ * between properties.
446
+ *
447
+ * @returns {string} The formatted string representation of the object.
448
+ *
449
+ * @example
450
+ * const obj = { a: 1, b: 2, c: 3 }
451
+ * const wrapped = StringExtensions.wrap(obj, { maxLen: 20 })
452
+ * console.log(wrapped)
453
+ * // Output:
454
+ * // {
455
+ * // a: 1,
456
+ * // b: 2,
457
+ * // c: 3
458
+ * // }
459
+ */
276
460
  wrap(
277
- object = globalThis,
461
+ objectOrLines,
278
462
  options = {
463
+ colorProperties: undefined,
279
464
  indent: 2,
280
- separator: ', ',
281
465
  indentCharacter: ' ',
282
- lineEnding: '\n',
283
466
  inspector: [Object, 'getOwnPropertyNames'],
284
- mapValues: undefined,
285
- mapLine: undefined,
467
+ lineEnding: '\n',
468
+ maxLen: 78,
469
+ perLine: undefined,
470
+ perLinePerProperty: undefined,
471
+ preProcess: undefined,
472
+ preReturn: undefined,
473
+ separator: ', ',
286
474
  }
287
475
  ) {
288
- const {
289
- indent = 2,
290
- separator = ', ',
291
- indentCharacter = ' ',
292
- lineEnding = '\n',
293
- inspector = [Object, 'getOwnPropertyNames'],
294
- mapValues,
295
- mapLine,
476
+ let {
477
+ colorProperties = undefined,
478
+ indent = options?.indent ?? 2,
479
+ indentCharacter = options?.indentCharacter ?? ' ',
480
+ inspector = options?.inspector ?? [Object, 'getOwnPropertyNames'],
481
+ lineEnding = options?.lineEnding ?? '\n',
482
+ maxLen = options?.maxLen ?? 78,
483
+ perLine = options?.perLine ?? undefined,
484
+ perLinePerProperty = options?.perLinePerProperty ?? undefined,
485
+ preProcess = options?.preProcess ?? undefined,
486
+ preReturn = options?.preReturn ?? undefined,
487
+ separator = options?.separator ?? ', ',
296
488
  } = options ?? {}
297
489
 
298
- let tab = indent === 0 ? '' : indentCharacter.repeat(Number(indent) || 2);
299
- let maxLen = 76 - tab.length;
300
- let line = [];
490
+ let tab = indent === 0 ? ''
491
+ : indentCharacter.repeat(Number(indent) || 2)
492
+ maxLen = 78 - tab.length
493
+
494
+ const sgr = this.sgr;
495
+ const validMapper = f => typeof f === 'function'
496
+
497
+ let line = []
301
498
  let getElements = inspector[0][inspector[1]]
302
- let values = Array.isArray(object) ? object : getElements(Object(object));
499
+ let values = Array.isArray(objectOrLines)
500
+ ? objectOrLines : getElements(Object(objectOrLines))
303
501
 
304
- if (typeof mapValues === 'function') {
305
- values = values.map(mapValues)
502
+ if (validMapper(preProcess)) {
503
+ values = preProcess(values)
306
504
  }
307
505
 
308
- return values.reduce((acc,key,index,{length}) => {
309
- const endOfLine = index < (length - 1) ? separator : '';
310
- let ifCombined = [
311
- tab,...line.join(separator), key, endOfLine
312
- ].join('').trim();
506
+ const context = { indent, indentCharacter, lineEnding, maxLen, tab, sgr }
507
+
508
+ let finalLines = values.reduce((acc, nextProp) => {
509
+ let ifCombined = [...line, nextProp].join(separator)
313
510
 
314
- if (ifCombined.length < maxLen) {
315
- line.push(key)
511
+ if ((tab.length + ifCombined.length) <= maxLen) {
512
+ line.push(nextProp)
316
513
  }
514
+
317
515
  else {
516
+ let lineProps = [...line]
517
+
518
+ if (validMapper(perLinePerProperty)) {
519
+ lineProps = lineProps.map((value, index, array) => {
520
+ return perLinePerProperty(value, index, array, context)
521
+ })
522
+ }
318
523
 
319
- let lineElements = [...line, key ]
320
- let debug = `<-- (len: ${[tab,...line.join(separator),key,endOfLine].join('').trim().length})`
321
- if (typeof mapLine === 'function') {
322
- lineElements = lineElements.map(mapLine)
524
+ if (colorProperties) {
525
+ const sgrArgs = (Array.isArray(colorProperties)
526
+ ? colorProperties
527
+ : [colorProperties]
528
+ )
529
+ lineProps = lineProps.map(v => sgr(v, ...sgrArgs))
323
530
  }
324
531
 
325
- const completeLine = (
326
- tab +
327
- lineElements.slice(1).join(separator) +
328
- endOfLine
329
- ).trim()
532
+ lineProps = [tab, lineProps.join(separator)].join('')
533
+ if (validMapper(perLine)) {
534
+ lineProps = perLine(lineProps[0], 0, lineProps)?.[0] ?? lineProps[0]
535
+ }
536
+
537
+ acc.push(lineProps)
538
+ line = []
539
+ }
330
540
 
331
- acc.push(completeLine + debug);
541
+ return acc
542
+ }, [])
332
543
 
333
- line = [];
334
- };
544
+ if (validMapper(preReturn)) {
545
+ finalLines = finalLines.map((value, index, array) => {
546
+ return preReturn(value, index, array, context)
547
+ })
548
+ }
549
+
550
+ Symbol.for(`@nejs.string.wrap ${JSON.stringify({lines: finalLines})}`)
551
+
552
+ if (lineEnding) {
553
+ finalLines = finalLines.join(lineEnding)
554
+ }
335
555
 
336
- return acc;
337
- }, []).join(lineEnding)
556
+ return finalLines
338
557
  },
339
558
  });
340
559
 
@@ -1,9 +1,9 @@
1
- import { Patch } from '@nejs/extension';
1
+ import { Patch, PatchToggle } from '@nejs/extension';
2
2
 
3
3
  import { Symkeys } from './classes/symkeys.js'
4
4
  import { JSONExtensions } from './json.extensions.js'
5
5
 
6
- const { extractFrom, mightContain } = JSONExtensions.patches
6
+ const JSONToggle = new PatchToggle(JSONExtensions)
7
7
 
8
8
  /**
9
9
  * `SymbolExtensions` is a patch for the JavaScript built-in `Symbol` class. It
@@ -14,31 +14,16 @@ const { extractFrom, mightContain } = JSONExtensions.patches
14
14
  * utility functions.
15
15
  */
16
16
  export const SymbolExtensions = new Patch(Symbol, {
17
- /**
18
- * Creates a new Symbol with the given name and optional data. If data
19
- * is provided, it will be stringified and appended to the symbol's
20
- * name. This method is useful for creating unique symbols that carry
21
- * additional metadata.
22
- *
23
- * @param {string} name The name of the symbol.
24
- * @param {*} [data] Optional data to be associated with the symbol.
25
- * @returns {symbol} A new symbol created with Symbol.for(), using the
26
- * provided name and stringified data (if provided).
27
- *
28
- * @example
29
- * const symbolWithData = Symbol.withData('mySymbol', { foo: 'bar' })
30
- * console.log(symbolWithData.toString())
31
- * // Output: "Symbol(mySymbol {"foo":"bar"})"
32
- *
33
- * @example
34
- * const symbolWithoutData = Symbol.withData('mySymbol')
35
- * console.log(symbolWithoutData.toString())
36
- * // Output: "Symbol(mySymbol)"
37
- */
38
- withData(name, data) {
39
- return data !== undefined
40
- ? Symbol.for(`${name} ${JSON.stringify(data)}`)
41
- : Symbol.for(name)
17
+ add(named, associatedData = {}) {
18
+ return this.keys.add(named, associatedData)
19
+ },
20
+
21
+ deleteData(forSymbol, replaceWith = undefined) {
22
+ return this.keys.deleteData(forSymbol, replaceWith)
23
+ },
24
+
25
+ hasData(forSymbol) {
26
+ return this.keys.hasData(forSymbol)
42
27
  },
43
28
 
44
29
  /**
@@ -125,6 +110,37 @@ export const SymbolExtensions = new Patch(Symbol, {
125
110
  * kOriginal.data = [Object.prototype, Array.prototype] // ...both work
126
111
  */
127
112
  keys: new Symkeys('nejs'),
113
+
114
+ setData(forSymbol, value) {
115
+ this.keys.setData(forSymbol, value)
116
+ },
117
+
118
+ /**
119
+ * Creates a new Symbol with the given name and optional data. If data
120
+ * is provided, it will be stringified and appended to the symbol's
121
+ * name. This method is useful for creating unique symbols that carry
122
+ * additional metadata.
123
+ *
124
+ * @param {string} name The name of the symbol.
125
+ * @param {*} [data] Optional data to be associated with the symbol.
126
+ * @returns {symbol} A new symbol created with Symbol.for(), using the
127
+ * provided name and stringified data (if provided).
128
+ *
129
+ * @example
130
+ * const symbolWithData = Symbol.withData('mySymbol', { foo: 'bar' })
131
+ * console.log(symbolWithData.toString())
132
+ * // Output: "Symbol(mySymbol {"foo":"bar"})"
133
+ *
134
+ * @example
135
+ * const symbolWithoutData = Symbol.withData('mySymbol')
136
+ * console.log(symbolWithoutData.toString())
137
+ * // Output: "Symbol(mySymbol)"
138
+ */
139
+ withData(name, data) {
140
+ return data !== undefined
141
+ ? Symbol.for(`${name} ${JSON.stringify(data)}`)
142
+ : Symbol.for(name)
143
+ },
128
144
  });
129
145
 
130
146
  export const SymbolPrototypeExtensions = new Patch(Symbol.prototype, {
@@ -195,7 +211,23 @@ export const SymbolPrototypeExtensions = new Patch(Symbol.prototype, {
195
211
  }
196
212
  }
197
213
 
198
- return extractFrom(string)
214
+ let result = undefined;
215
+ let revertToggle = false
216
+ if (!JSONExtensions.applied) {
217
+ JSONToggle.start()
218
+ revertToggle = true
219
+ }
220
+
221
+ if (JSON.mightContain(this.description)) {
222
+ try { result = JSON.extractFrom(this.description) }
223
+ catch (ignore) { }
224
+ }
225
+
226
+ if (revertToggle) {
227
+ JSONToggle.stop()
228
+ }
229
+
230
+ return result
199
231
  },
200
232
 
201
233
  /**
@@ -262,7 +294,96 @@ export const SymbolPrototypeExtensions = new Patch(Symbol.prototype, {
262
294
  * console.log(sym.mightHaveEmbeddedJSON) // Output: false
263
295
  */
264
296
  get mightHaveEmbeddedJSON() {
265
- return mightContain(this.description) && typeof this.data === 'function'
297
+ return mightContain(this.description)
298
+ },
299
+
300
+ get sgrString() {
301
+ let revert = false
302
+ let detail = undefined
303
+
304
+ let { sgr } = String
305
+ if (!sgr) { sgr = (string, ...args) => string }
306
+
307
+ if (!JSONExtensions.applied) { JSONToggle.start(); revert = true }
308
+ if ((detail = JSON.mightContain(this.description, true))) {
309
+ let jsonText = detail[2][0]
310
+ let index = detail[1]
311
+
312
+ if (~index && jsonText && jsonText.length > 30) {
313
+ let desc = this.description
314
+ let newDescription = [
315
+ sgr(`Symbol.for(${desc.slice(0, index)}`, 'green'),
316
+ sgr(jsonText.slice(0, 10), 'di'),
317
+ '...',
318
+ sgr(jsonText.slice(-5), 'di'),
319
+ sgr(`${desc.slice(index + jsonText.length + 1)})`, 'green'),
320
+ ].join('')
321
+
322
+ if (revert) { JSONToggle.stop() }
323
+ return `${newDescription}`
324
+ }
325
+ }
326
+
327
+ if (revert) { JSONToggle.stop() }
328
+
329
+ return newDescription
266
330
  },
267
- }
331
+
332
+ /**
333
+ * Custom inspect method for Node.js util.inspect.
334
+ *
335
+ * NOTE: this will only apply if looking at an instance of Symbol,
336
+ * sadly; {@see SymbolPrototypeExtensions.instance}
337
+ *
338
+ * This method is used by Node.js util.inspect to provide a custom
339
+ * representation of the symbol when inspected. It checks if the
340
+ * symbol's description might contain JSON data and if so, it
341
+ * truncates the JSON data in the description to a maximum of 30
342
+ * characters and formats the output with colors using the `sgr`
343
+ * function from the `String` object.
344
+ *
345
+ * @param {number} depth - The current depth of the recursive
346
+ * inspection.
347
+ * @param {Object} options - The options object passed to
348
+ * util.inspect.
349
+ * @param {Function} inspect - The original util.inspect function.
350
+ *
351
+ * @returns {string} - The formatted representation of the symbol.
352
+ *
353
+ * @example
354
+ * const sym = Symbol.for('fun {"name":"John Doe"}')
355
+ * console.log(util.inspect(sym))
356
+ * // Output: Symbol.for(fun {"name":"John ...hn Doe"})
357
+ */
358
+ [Symbol.for('nodejs.util.inspect.custom')](depth, options, inspect) {
359
+ let revert = false
360
+ let detail = undefined
361
+
362
+ let { sgr } = String
363
+ if (!sgr) { sgr = (string, ...args) => string }
364
+
365
+ if (!JSONExtensions.applied) { JSONToggle.start(); revert = true }
366
+ if ((detail = JSON.mightContain(this.description, true))) {
367
+ let jsonText = detail[2][0]
368
+ let index = detail[1]
369
+
370
+ if (~index && jsonText && jsonText.length > 30) {
371
+ let desc = this.description
372
+ let newDescription = [
373
+ sgr(`Symbol.for(${desc.slice(0, index)}`, 'green'),
374
+ sgr(jsonText.slice(0, 10), 'di'),
375
+ '...',
376
+ sgr(jsonText.slice(-5), 'di'),
377
+ sgr(`${desc.slice(index + jsonText.length + 1)})`, 'green'),
378
+ ].join('')
379
+
380
+ if (revert) { JSONToggle.stop() }
381
+ return `${newDescription}`
382
+ }
383
+ }
384
+
385
+ if (revert) { JSONToggle.stop() }
386
+ return inspect(this, { colors: true })
387
+ },
388
+ },
268
389
  })