@gesslar/toolkit 3.42.0 → 4.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 (48) hide show
  1. package/README.md +1 -0
  2. package/package.json +4 -4
  3. package/src/browser/lib/Collection.js +1 -2
  4. package/src/browser/lib/Data.js +4 -4
  5. package/src/browser/lib/TypeSpec.js +115 -39
  6. package/src/node/index.js +2 -1
  7. package/src/node/lib/Cache.js +105 -35
  8. package/src/node/lib/Data.js +49 -0
  9. package/src/node/lib/DirectoryObject.js +4 -7
  10. package/src/node/lib/FileObject.js +89 -53
  11. package/src/node/lib/FileSystem.js +47 -2
  12. package/src/node/lib/Font.js +1 -1
  13. package/src/node/lib/Glog.js +47 -28
  14. package/src/node/lib/Notify.js +6 -6
  15. package/src/node/lib/Sass.js +3 -6
  16. package/src/node/lib/Term.js +11 -11
  17. package/src/node/lib/Util.js +3 -3
  18. package/src/node/lib/Valid.js +3 -6
  19. package/src/node/lib/Watcher.js +118 -0
  20. package/types/browser/lib/Collection.d.ts.map +1 -1
  21. package/types/browser/lib/Data.d.ts +2 -8
  22. package/types/browser/lib/Data.d.ts.map +1 -1
  23. package/types/browser/lib/TypeSpec.d.ts +21 -36
  24. package/types/browser/lib/TypeSpec.d.ts.map +1 -1
  25. package/types/node/index.d.ts +2 -1
  26. package/types/node/lib/Cache.d.ts +36 -5
  27. package/types/node/lib/Cache.d.ts.map +1 -1
  28. package/types/node/lib/Data.d.ts +19 -0
  29. package/types/node/lib/Data.d.ts.map +1 -0
  30. package/types/node/lib/DirectoryObject.d.ts +6 -5
  31. package/types/node/lib/DirectoryObject.d.ts.map +1 -1
  32. package/types/node/lib/FileObject.d.ts +54 -26
  33. package/types/node/lib/FileObject.d.ts.map +1 -1
  34. package/types/node/lib/FileSystem.d.ts +19 -0
  35. package/types/node/lib/FileSystem.d.ts.map +1 -1
  36. package/types/node/lib/Glog.d.ts +2 -2
  37. package/types/node/lib/Glog.d.ts.map +1 -1
  38. package/types/node/lib/Notify.d.ts +10 -10
  39. package/types/node/lib/Notify.d.ts.map +1 -1
  40. package/types/node/lib/Sass.d.ts +7 -0
  41. package/types/node/lib/Sass.d.ts.map +1 -1
  42. package/types/node/lib/Term.d.ts +2 -9
  43. package/types/node/lib/Term.d.ts.map +1 -1
  44. package/types/node/lib/Util.d.ts +6 -6
  45. package/types/node/lib/Util.d.ts.map +1 -1
  46. package/types/node/lib/Valid.d.ts.map +1 -1
  47. package/types/node/lib/Watcher.d.ts +38 -0
  48. package/types/node/lib/Watcher.d.ts.map +1 -0
@@ -4,14 +4,13 @@
4
4
  * resolution and existence checks.
5
5
  */
6
6
 
7
- import JSON5 from "json5"
7
+ import {Buffer} from "node:buffer"
8
8
  import fs from "node:fs/promises"
9
- import YAML from "yaml"
10
9
  import {URL} from "node:url"
11
- import {Buffer} from "node:buffer"
12
10
  import {inspect} from "node:util"
13
11
 
14
- import Data from "../../browser/lib/Data.js"
12
+ import Cache from "./Cache.js"
13
+ import Data from "./Data.js"
15
14
  import DirectoryObject from "./DirectoryObject.js"
16
15
  import FS from "./FileSystem.js"
17
16
  import Sass from "./Sass.js"
@@ -33,19 +32,6 @@ import Valid from "./Valid.js"
33
32
  */
34
33
 
35
34
  export default class FileObject extends FS {
36
- /**
37
- * Configuration mapping data types to their respective parser modules for loadData method.
38
- * Each parser module must have a .parse() method that accepts a string and returns parsed data.
39
- *
40
- * @type {{[key: string]: Array<typeof JSON5 | typeof YAML>}}
41
- */
42
- static dataLoaderConfig = Object.freeze({
43
- json5: [JSON5],
44
- json: [JSON5],
45
- yaml: [YAML],
46
- any: [JSON5, YAML]
47
- })
48
-
49
35
  /**
50
36
  * @type {object}
51
37
  * @property {string|null} supplied - User-supplied path
@@ -70,6 +56,8 @@ export default class FileObject extends FS {
70
56
  real: null,
71
57
  })
72
58
 
59
+ #cache = null
60
+
73
61
  /**
74
62
  * Constructs a FileObject instance.
75
63
  *
@@ -162,6 +150,7 @@ export default class FileObject extends FS {
162
150
  extension: this.extension,
163
151
  isFile: this.isFile,
164
152
  parentPath: this.parentPath,
153
+ cached: this.cached
165
154
  }
166
155
  }
167
156
 
@@ -377,17 +366,74 @@ export default class FileObject extends FS {
377
366
  }
378
367
  }
379
368
 
369
+ /**
370
+ * Whether this FileObject has an active cache attached.
371
+ *
372
+ * @returns {boolean} True if a Cache instance is attached
373
+ */
374
+ get cached() {
375
+ return Data.isType(this.#cache, "Cache")
376
+ }
377
+
378
+ /**
379
+ * Attaches a Cache instance to this FileObject for caching read and
380
+ * loadData results. If no cache is provided, a new Cache is created.
381
+ *
382
+ * @param {Cache} [cache] - The Cache instance to attach
383
+ * @returns {FileObject} This FileObject for chaining
384
+ * @throws {Sass} If a cache is already attached
385
+ */
386
+ withCache(cache) {
387
+ Valid.type(cache, "Undefined|Cache")
388
+ Valid.assert(!this.#cache, "Cache already set. Remove the cache before re-assigning.")
389
+
390
+ cache ??= new Cache()
391
+
392
+ this.#cache = cache
393
+
394
+ return this
395
+ }
396
+
397
+ /**
398
+ * Removes the attached cache, clearing any cached data for this file first.
399
+ *
400
+ * @returns {FileObject} This FileObject for chaining
401
+ */
402
+ removeCache() {
403
+ this.resetCache()
404
+ this.#cache = null
405
+
406
+ return this
407
+ }
408
+
409
+ /**
410
+ * Clears cached data for this file without removing the cache itself.
411
+ *
412
+ * @returns {FileObject} This FileObject for chaining
413
+ */
414
+ resetCache() {
415
+ this.#cache?.resetCache(this)
416
+
417
+ return this
418
+ }
419
+
380
420
  /**
381
421
  * Reads the content of a file asynchronously.
382
422
  *
383
- * @param {string} [encoding] - The encoding to read the file as.
423
+ * @param {object} [options] - Read options
424
+ * @param {string} [options.encoding="utf8"] - The encoding to read the file as
425
+ * @param {boolean} [options.skipCache=false] - If true, bypass the cache
384
426
  * @returns {Promise<string>} The file contents
385
427
  */
386
- async read(encoding="utf8") {
428
+ async read({encoding="utf8", skipCache=false} = {}) {
387
429
  const filePath = this.path
388
430
 
431
+ if(this.#cache && skipCache === false)
432
+ return await this.#cache.loadFromCache(
433
+ this, {encoding})
434
+
389
435
  if(!(await this.exists))
390
- throw Sass.new(`No such file '${filePath}'`)
436
+ throw Sass.new(`No such file '${this}'`)
391
437
 
392
438
  return await fs.readFile(filePath, encoding)
393
439
  }
@@ -419,7 +465,7 @@ export default class FileObject extends FS {
419
465
  *
420
466
  * @param {string} content - The content to write
421
467
  * @param {string} [encoding] - The encoding in which to write (default: "utf8")
422
- * @returns {Promise<void>}
468
+ * @returns {Promise<undefined>}
423
469
  * @throws {Sass} If the file URL is invalid or the parent directory doesn't exist
424
470
  * @example
425
471
  * const file = new FileObject('./output/data.json')
@@ -444,7 +490,7 @@ export default class FileObject extends FS {
444
490
  * Supports ArrayBuffer, TypedArrays (Uint8Array, etc.), Blob, and Node Buffer types.
445
491
  *
446
492
  * @param {ArrayBuffer|Blob|Buffer} data - The binary data to write
447
- * @returns {Promise<void>}
493
+ * @returns {Promise<undefined>}
448
494
  * @throws {Sass} If the file URL is invalid
449
495
  * @throws {Sass} If the parent directory doesn't exist
450
496
  * @throws {Sass} If the data is not a valid binary type
@@ -471,44 +517,34 @@ export default class FileObject extends FS {
471
517
  }
472
518
 
473
519
  /**
474
- * Loads an object from JSON or YAML file.
475
- * Attempts to parse content as JSON5 first, then falls back to YAML if specified.
520
+ * Loads an object from JSON or YAML file. Attempts to parse content as JSON5
521
+ * first, then falls back to YAML if specified.
476
522
  *
477
- * @param {string} [type] - The expected type of data to parse ("json", "json5", "yaml", or "any")
478
- * @param {string} [encoding] - The encoding to read the file as (default: "utf8")
523
+ * @param {object} [options] - Load options
524
+ * @param {string} [options.type="any"] - The expected type of data to parse ("json", "json5", "yaml", or "any")
525
+ * @param {string} [options.encoding="utf8"] - The encoding to read the file as
526
+ * @param {boolean} [options.skipCache=false] - If true, bypass the cache
479
527
  * @returns {Promise<unknown>} The parsed data object
480
528
  * @throws {Sass} If the content cannot be parsed or type is unsupported
481
529
  * @example
482
530
  * const configFile = new FileObject('./config.json5')
483
- * const config = await configFile.loadData('json5')
531
+ * const config = await configFile.loadData({type: 'json5'})
484
532
  *
485
533
  * // Auto-detect format
486
- * const data = await configFile.loadData('any')
487
- */
488
- async loadData(type="any", encoding="utf8") {
489
- const content = await this.read(encoding)
490
- const normalizedType = type.toLowerCase()
491
- const toTry = {
492
- json5: [JSON5],
493
- json: [JSON5],
494
- yaml: [YAML],
495
- any: [JSON5,YAML]
496
- }[normalizedType]
497
-
498
- if(!toTry)
499
- throw Sass.new(`Unsupported data type '${type}'. Supported types: json, json5, yaml.`)
500
-
501
- for(const format of toTry) {
502
- try {
503
- const result = format.parse(content)
504
-
505
- return result
506
- } catch {
507
- // nothing to see here
508
- }
509
- }
534
+ * const data = await configFile.loadData()
535
+ */
536
+ async loadData({type="any", encoding="utf8", skipCache=false} = {}) {
537
+ if(this.#cache && skipCache === false)
538
+ return await this.#cache.loadDataFromCache(
539
+ this, {encoding, type})
540
+
541
+ if(!(await this.exists))
542
+ throw Sass.new(`No such file '${this}'`)
543
+
544
+ const content = await this.read({encoding, skipCache})
545
+ const result = Data.textAsData(content, type)
510
546
 
511
- throw Sass.new(`Content is neither valid JSON5 nor valid YAML:\n'${this.path}'`)
547
+ return result
512
548
  }
513
549
 
514
550
  /**
@@ -610,7 +646,7 @@ export default class FileObject extends FS {
610
646
  /**
611
647
  * Deletes the file from the filesystem.
612
648
  *
613
- * @returns {Promise<void>} Resolves when file is deleted
649
+ * @returns {Promise<undefined>} Resolves when file is deleted
614
650
  * @throws {Sass} If the file URL is invalid
615
651
  * @throws {Sass} If the file does not exist
616
652
  * @example
@@ -11,12 +11,14 @@
11
11
  import path from "node:path"
12
12
  import url from "node:url"
13
13
 
14
+ import Collection from "../../browser/lib/Collection.js"
14
15
  import Data from "../../browser/lib/Data.js"
16
+ import Glog from "./Glog.js"
15
17
  import Valid from "./Valid.js"
16
- import Collection from "../../browser/lib/Collection.js"
18
+ import Watcher from "./Watcher.js"
17
19
 
18
20
  /**
19
- * @import {Sass} from "./Sass.js"
21
+ * @import Sass from "./Sass.js"
20
22
  */
21
23
 
22
24
  const fdTypes = Object.freeze(["file", "directory"])
@@ -33,6 +35,8 @@ export default class FileSystem {
33
35
  static upperFdTypes = upperFdTypes
34
36
  static fdType = fdType
35
37
 
38
+ #watcher = null
39
+
36
40
  /**
37
41
  * Compute the relative path from another file or directory to this instance.
38
42
  *
@@ -53,6 +57,47 @@ export default class FileSystem {
53
57
  return FileSystem.relativeOrAbsolute(fileOrDirectoryObject, this)
54
58
  }
55
59
 
60
+ /**
61
+ * Watch this file or directory for changes.
62
+ *
63
+ * @param {object} [options] - Watch options
64
+ * @param {Function} [options.onChange] - Callback invoked on change
65
+ * @param {number} [options.debounceMs] - Debounce interval in milliseconds
66
+ * @param {boolean} [options.persistent] - Keep the process alive while watching
67
+ * @returns {Promise<undefined>}
68
+ */
69
+ async watch(options={}) {
70
+ Valid.type(options, "Object")
71
+
72
+ const localOptions = Collection.cloneObject(options)
73
+
74
+ const {onChange} = localOptions ?? {}
75
+ Valid.type(onChange, "Undefined|Function")
76
+
77
+ delete localOptions.onChange
78
+
79
+ this.stopWatching()
80
+
81
+ this.#watcher = new Watcher()
82
+
83
+ await this.#watcher.watch(this, Object.assign({},
84
+ localOptions,
85
+ {
86
+ onChange: onChange ?? (() => {
87
+ Glog(`${this} changed somehow.`)
88
+ })
89
+ }
90
+ ))
91
+ }
92
+
93
+ /**
94
+ * Stop watching this file or directory for changes.
95
+ */
96
+ stopWatching() {
97
+ this.#watcher?.stopWatching()
98
+ this.#watcher = null
99
+ }
100
+
56
101
  /**
57
102
  * Fix slashes in a path
58
103
  *
@@ -11,7 +11,7 @@ import DirectoryObject from "./DirectoryObject.js"
11
11
  import FS from "./FileSystem.js"
12
12
 
13
13
  /**
14
- * @import {FileObject} from "./FileObject.js"
14
+ * @import FileObject from "./FileObject.js"
15
15
  */
16
16
 
17
17
  const execFile = promisify(child_process.execFile)
@@ -11,12 +11,24 @@
11
11
  * 5. Traditional logger: logger.debug("message", level)
12
12
  */
13
13
 
14
+ import {createRequire} from "node:module"
15
+
14
16
  import c from "@gesslar/colours"
15
17
 
16
18
  import Data from "../../browser/lib/Data.js"
17
19
  import Term from "./Term.js"
18
20
  import Util from "../../browser/lib/Util.js"
19
21
 
22
+ // Auto-detect VS Code extension environment
23
+ let vscodeApi = null
24
+ try {
25
+ const _require = createRequire(import.meta.url)
26
+
27
+ vscodeApi = _require("vscode")
28
+ } catch {
29
+ // Not in a VS Code extension environment
30
+ }
31
+
20
32
  /**
21
33
  * Default colour configuration for logger output using @gesslar/colours format
22
34
  *
@@ -61,16 +73,6 @@ export const logSymbols = {
61
73
  success: "✓"
62
74
  }
63
75
 
64
- // Set up convenient aliases for common log colours
65
- c.alias.set("debug", "{F033}")
66
- c.alias.set("info", "{F036}")
67
- c.alias.set("warn", "{F214}")
68
- c.alias.set("error", "{F196}")
69
- c.alias.set("success", "{F046}")
70
- c.alias.set("muted", "{F244}")
71
- c.alias.set("bold", "{<B}")
72
- c.alias.set("dim", "{<D}")
73
-
74
76
  class Glog {
75
77
  // Static properties (for global usage)
76
78
  static logLevel = 0
@@ -90,9 +92,9 @@ class Glog {
90
92
  #tagsAsStrings = false
91
93
  #displayName = true
92
94
  #symbols = null
93
- #vscodeError = null
94
- #vscodeWarn = null
95
- #vscodeInfo = null
95
+ #vscodeWindow = null
96
+ #vscodeApi = null
97
+ #outputChannel = null
96
98
 
97
99
  /**
98
100
  * Create a new Glog logger instance with optional configuration
@@ -107,23 +109,18 @@ class Glog {
107
109
  * @param {boolean} [options.stackTrace=false] - Enable stack trace extraction
108
110
  * @param {boolean} [options.tagsAsStrings=false] - Use string tags instead of symbols
109
111
  * @param {boolean} [options.displayName=true] - Display logger name in output
110
- * @param {string} [options.env] - Environment mode ("extension" for VSCode integration)
112
+ * @param {object} [options.vscode] - VS Code API object (auto-detected if not provided)
111
113
  */
112
114
  constructor(options = {}) {
113
115
  this.setOptions(options)
114
116
  this.constructor.name = "Glog"
115
117
 
116
- // VSCode integration if specified
117
- if(options.env === "extension") {
118
- try {
119
- const vscode = require("vscode")
118
+ // VSCode integration - prefer passed-in vscode, fall back to auto-detected
119
+ const vsapi = options.vscode ?? vscodeApi
120
120
 
121
- this.#vscodeError = vscode.window.showErrorMessage
122
- this.#vscodeWarn = vscode.window.showWarningMessage
123
- this.#vscodeInfo = vscode.window.showInformationMessage
124
- } catch {
125
- // VSCode not available, ignore
126
- }
121
+ if(vsapi) {
122
+ this.#vscodeApi = vsapi
123
+ this.#vscodeWindow = vsapi.window
127
124
  }
128
125
  }
129
126
 
@@ -514,6 +511,23 @@ class Glog {
514
511
  }
515
512
  }
516
513
 
514
+ get #channel() {
515
+ if(!this.#outputChannel && this.#vscodeApi) {
516
+ const name = this.#name || "Log"
517
+
518
+ this.#outputChannel = this.#vscodeApi.window.createOutputChannel(name)
519
+ }
520
+
521
+ return this.#outputChannel
522
+ }
523
+
524
+ #appendToChannel(level, message, ...arg) {
525
+ const timestamp = new Date().toISOString()
526
+ const parts = [timestamp, `[${level.toUpperCase()}]`, message, ...arg].join(" ")
527
+
528
+ this.#channel?.appendLine(parts)
529
+ }
530
+
517
531
  #compose(level, message, debugLevel = 0) {
518
532
  const colours = this.#colours || Glog.colours || loggerColours
519
533
  const name = this.#name || Glog.name || "Log"
@@ -603,8 +617,10 @@ class Glog {
603
617
 
604
618
  const currentLevel = this.#logLevel || Glog.logLevel
605
619
 
606
- if(currentLevel > 0 && level <= currentLevel)
620
+ if(currentLevel > 0 && level <= currentLevel) {
607
621
  Term.debug(this.#compose("debug", message, level), ...arg)
622
+ this.#appendToChannel("debug", message, ...arg)
623
+ }
608
624
  }
609
625
 
610
626
  /**
@@ -615,7 +631,8 @@ class Glog {
615
631
  */
616
632
  info(message, ...arg) {
617
633
  Term.info(this.#compose("info", message), ...arg)
618
- this.#vscodeInfo?.(JSON.stringify(message))
634
+ this.#appendToChannel("info", message, ...arg)
635
+ this.#vscodeWindow?.showInformationMessage(message)
619
636
  }
620
637
 
621
638
  /**
@@ -626,7 +643,8 @@ class Glog {
626
643
  */
627
644
  warn(message, ...arg) {
628
645
  Term.warn(this.#compose("warn", message), ...arg)
629
- this.#vscodeWarn?.(JSON.stringify(message))
646
+ this.#appendToChannel("warn", message, ...arg)
647
+ this.#vscodeWindow?.showWarningMessage(message)
630
648
  }
631
649
 
632
650
  /**
@@ -637,7 +655,8 @@ class Glog {
637
655
  */
638
656
  error(message, ...arg) {
639
657
  Term.error(this.#compose("error", message), ...arg)
640
- this.#vscodeError?.(JSON.stringify(message))
658
+ this.#appendToChannel("error", message, ...arg)
659
+ this.#vscodeWindow?.showErrorMessage(message)
641
660
  }
642
661
 
643
662
  /**
@@ -31,7 +31,7 @@ export class Notify {
31
31
  *
32
32
  * @param {string} type - Event name to dispatch.
33
33
  * @param {unknown} [payload] - Data to send with the event.
34
- * @returns {void}
34
+ * @returns {undefined}
35
35
  */
36
36
  emit(type, payload=undefined) {
37
37
  Valid.type(type, "String", {allowEmpty: false})
@@ -46,7 +46,7 @@ export class Notify {
46
46
  *
47
47
  * @param {string} type - Event name to dispatch.
48
48
  * @param {unknown} [payload] - Data to send with the event.
49
- * @returns {Promise<void>} Resolves when all listeners have completed.
49
+ * @returns {Promise<undefined>} Resolves when all listeners have completed.
50
50
  */
51
51
  async asyncEmit(type, payload) {
52
52
  Valid.type(type, "String", {allowEmpty: false})
@@ -74,10 +74,10 @@ export class Notify {
74
74
  * Registers a listener for the given event type.
75
75
  *
76
76
  * @param {string} type - Event name to listen for.
77
- * @param {(payload: unknown) => void} handler - Listener callback.
77
+ * @param {(payload: unknown) => undefined} handler - Listener callback.
78
78
  * @param {EventEmitter} [emitter] - The EventEmitter to attach to. Default is internal emitter.
79
79
  * @param {NotifyEventOptions} [options] - Options for the listener.
80
- * @returns {() => void} Dispose function to unregister the handler.
80
+ * @returns {() => undefined} Dispose function to unregister the handler.
81
81
  */
82
82
  on(type, handler, emitter=this.#emitter, options=undefined) {
83
83
  Valid.type(type, "String", {allowEmpty: false})
@@ -96,9 +96,9 @@ export class Notify {
96
96
  * Removes a previously registered listener for the given event type.
97
97
  *
98
98
  * @param {string} type - Event name to remove.
99
- * @param {(payload: unknown) => void} handler - Listener callback to detach.
99
+ * @param {(payload: unknown) => undefined} handler - Listener callback to detach.
100
100
  * @param {EventEmitter} [emitter] - The EventEmitter from which to remove. Default is internal emitter.
101
- * @returns {void}
101
+ * @returns {undefined}
102
102
  */
103
103
  off(type, handler, emitter=this.#emitter) {
104
104
  Valid.type(type, "String", {allowEmpty: false})
@@ -24,20 +24,17 @@ export default class Sass extends BrowserSass {
24
24
  * Optionally includes detailed stack trace information.
25
25
  *
26
26
  * @param {boolean} [nerdMode] - Whether to include detailed stack trace
27
- * @param {boolean} [isNested] - Whether this is a nested error report
28
27
  */
29
- report(nerdMode=false, isNested=false) {
30
- if(isNested)
31
- Term.error()
32
-
28
+ report(nerdMode=false) {
33
29
  Term.group(
30
+ "\r\n" +
34
31
  `${Term.terminalBracket(["error", "Something Went Wrong"])}\n` +
35
32
  this.trace.join("\n")
36
33
  )
37
34
 
38
35
  if(nerdMode) {
39
36
  Term.error(
40
- "\n" +
37
+ "\r\n" +
41
38
  `${Term.terminalBracket(["error", "Nerd Victuals"])}\n` +
42
39
  this.#fullBodyMassage(this.stack)
43
40
  )
@@ -12,22 +12,22 @@ import Sass from "./Sass.js"
12
12
 
13
13
  c.alias.set("success", "{F035}")
14
14
  c.alias.set("info", "{F033}")
15
- c.alias.set("warn", "{F208}")
16
- c.alias.set("error", "{F032}")
17
- c.alias.set("modified", "{F147}")
15
+ c.alias.set("warn", "{F214}")
16
+ c.alias.set("error", "{F124}")
17
+
18
+ c.alias.set("debug", "{F033}")
19
+
20
+ c.alias.set("modified", "{F099}")
21
+
22
+ c.alias.set("muted", "{F244}")
23
+ c.alias.set("bold", "{<B}")
24
+ c.alias.set("dim", "{<D}")
18
25
 
19
26
  /**
20
27
  * Terminal output utilities with ANSI colour support.
21
28
  *
22
29
  * Provides console logging wrappers, cursor control, and formatted message
23
30
  * output with colour styling via `@gesslar/colours`.
24
- *
25
- * Predefined colour aliases:
26
- * - `success` - green (F035)
27
- * - `info` - blue (F033)
28
- * - `warn` - orange (F208)
29
- * - `error` - red (F032)
30
- * - `modified` - purple (F147)
31
31
  */
32
32
  export default class Term {
33
33
  static #cache = new Map()
@@ -230,7 +230,7 @@ export default class Term {
230
230
  * @param {string | Array<string | [string, string]>} args - Message or segments.
231
231
  * @param {object} [options] - Behaviour flags.
232
232
  * @param {boolean} options.silent - When true, suppress output.
233
- * @returns {void}
233
+ * @returns {undefined}
234
234
  */
235
235
  static status(args, {silent=false} = {}) {
236
236
  if(silent)
@@ -65,7 +65,7 @@ export default class Util extends BrowserUtil {
65
65
  * @param {object} emitter - The emitter object (already validated)
66
66
  * @param {string} event - The event name to emit
67
67
  * @param {...unknown} args - Arguments to pass to event listeners
68
- * @returns {Promise<void>} Resolves when all listeners have completed
68
+ * @returns {Promise<undefined>} Resolves when all listeners have completed
69
69
  */
70
70
  static async #performAsyncEmit(emitter, event, ...args) {
71
71
  const listeners = emitter.listeners(event)
@@ -97,7 +97,7 @@ export default class Util extends BrowserUtil {
97
97
  * @param {EventEmitter} emitter - The EventEmitter instance to emit on
98
98
  * @param {string} event - The event name to emit
99
99
  * @param {...unknown} args - Arguments to pass to event listeners
100
- * @returns {Promise<void>} Resolves when all listeners have completed
100
+ * @returns {Promise<undefined>} Resolves when all listeners have completed
101
101
  */
102
102
  static async asyncEmit(emitter, event, ...args) {
103
103
  try {
@@ -124,7 +124,7 @@ export default class Util extends BrowserUtil {
124
124
  * @param {object} emitter - Any object with EventEmitter-like interface
125
125
  * @param {string} event - The event name to emit
126
126
  * @param {...unknown} args - Arguments to pass to event listeners
127
- * @returns {Promise<void>} Resolves when all listeners have completed, but no grapes.
127
+ * @returns {Promise<undefined>} Resolves when all listeners have completed, but no grapes.
128
128
  */
129
129
  static async asyncEmitQuack(emitter, event, ...args) {
130
130
  try {
@@ -51,17 +51,14 @@ export default class Valid {
51
51
  * met (optional)
52
52
  */
53
53
  static assert(condition, message, arg = null) {
54
- if(!Data.isType(condition, "boolean")) {
54
+ if(!Data.isType(condition, "boolean"))
55
55
  throw Sass.new(`Condition must be a boolean, got ${condition}`)
56
- }
57
56
 
58
- if(!Data.isType(message, "string")) {
57
+ if(!Data.isType(message, "string"))
59
58
  throw Sass.new(`Message must be a string, got ${message}`)
60
- }
61
59
 
62
- if(!(arg === null || arg === undefined || typeof arg === "number")) {
60
+ if(!(arg === null || arg === undefined || typeof arg === "number"))
63
61
  throw Sass.new(`Arg must be a number, got ${arg}`)
64
- }
65
62
 
66
63
  if(!condition)
67
64
  throw Sass.new(`${message}${arg ? `: ${arg}` : ""}`)