index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. 'use strict'
  2. const { format } = require('node:util')
  3. /**
  4. * An object that provides methods for creating process warning, emitting them,
  5. * and inspecting/managing the emission state of warning.
  6. *
  7. * @typedef {object} ProcessWarningManager
  8. */
  9. /**
  10. * @typedef {object} CreateOptions
  11. * @property {boolean} [unlimited=false] Indicates if the warning should be
  12. * emitted every time (`true`) or only the first time (`false`).
  13. */
  14. /**
  15. * An error instance representing a process warning. This is what will be
  16. * emitted to listeners when the warning is emitted.
  17. *
  18. * @typedef {Error} ProcessWarning
  19. * @property {string} code
  20. * @property {string} name
  21. * @property {string} message
  22. */
  23. /**
  24. * A function used to create new {@link ProcessWarning} instances.
  25. *
  26. * @callback ProcessWarningBuilder
  27. * @param {*} [param1] First possible string interpolation value.
  28. * @param {*} [param2] Second possible string interpolation value.
  29. * @param {*} [param3] Third possible string interpolation value.
  30. * @returns ProcessWarning
  31. */
  32. /**
  33. * Factory that builds a new {@link ProcessWarningManager} and returns it.
  34. *
  35. * @returns ProcessWarningManager
  36. */
  37. function processWarning () {
  38. const codes = {}
  39. const emitted = new Map()
  40. const opts = Object.create(null)
  41. /**
  42. * Builds a new {@link ProcessWarning} and adds it to the
  43. * {@link ProcessWarningManager} such that it can be emitted through the
  44. * {@link ProcessWarningManager#emit} method.
  45. *
  46. * @memberof! ProcessWarningManager
  47. * @instance
  48. *
  49. * @param {string} name Warning name, e.g. `'MyCustomWarning'`.
  50. * @param {string} code A unique code for the warning, e.g. `'WARN_001'`.
  51. * @param {string} message The body of the warning.
  52. * @param {CreateOptions} [options]
  53. * @returns {ProcessWarningBuilder}
  54. */
  55. function create (name, code, message, { unlimited = false } = {}) {
  56. if (!name) throw new Error('Warning name must not be empty')
  57. if (!code) throw new Error('Warning code must not be empty')
  58. if (!message) throw new Error('Warning message must not be empty')
  59. if (typeof unlimited !== 'boolean') throw new Error('Warning opts.unlimited must be a boolean')
  60. code = code.toUpperCase()
  61. if (codes[code] !== undefined) {
  62. throw new Error(`The code '${code}' already exist`)
  63. }
  64. function buildWarnOpts (a, b, c) {
  65. // more performant than spread (...) operator
  66. let formatted
  67. if (a && b && c) {
  68. formatted = format(message, a, b, c)
  69. } else if (a && b) {
  70. formatted = format(message, a, b)
  71. } else if (a) {
  72. formatted = format(message, a)
  73. } else {
  74. formatted = message
  75. }
  76. return {
  77. code,
  78. name,
  79. message: formatted
  80. }
  81. }
  82. Object.assign(opts, { unlimited })
  83. emitted.set(code, unlimited)
  84. codes[code] = buildWarnOpts
  85. return codes[code]
  86. }
  87. /**
  88. * A wrapper for {@link ProcessWarningManager#create} that builds a new
  89. * deprecation warning. This method is equivalent to passing
  90. * `name = 'DeprecationWarning'` to the create method.
  91. *
  92. * Deprecation warnings have extended support for the Node.js CLI options:
  93. * `--throw-deprecation`, `--no-deprecation`, and `--trace-deprecation`.
  94. *
  95. * @memberof! ProcessWarningManager
  96. * @instance
  97. *
  98. * @param {string} code
  99. * @param {string} message
  100. * @param {CreateOptions} opts
  101. * @returns {ProcessWarningBuilder}
  102. */
  103. function createDeprecation (code, message, opts = {}) {
  104. return create('DeprecationWarning', code, message, opts)
  105. }
  106. /**
  107. * Emits a registered warning associated with the given `code`. If the
  108. * warning message has interpolation strings present, up to the first three
  109. * of them can be supplied values with the optional interpolation value
  110. * parameters.
  111. *
  112. * If a warning is set to `unlimited: false`, and has already been emitted
  113. * once, invoking this method is a no-operation.
  114. *
  115. * @memberof! ProcessWarningManager
  116. * @instance
  117. *
  118. * @param {string} code The code for the error to emit, e.g. `'WARN_001'`.
  119. * This is the same code that was provided to {@link ProcessWarningManager#create}.
  120. * @param {*} [a] Possible message interpolation value.
  121. * @param {*} [b] Possible message interpolation value.
  122. * @param {*} [c] Possible message interpolation value.
  123. */
  124. function emit (code, a, b, c) {
  125. if (emitted.get(code) === true && opts.unlimited === false) return
  126. if (codes[code] === undefined) throw new Error(`The code '${code}' does not exist`)
  127. emitted.set(code, true)
  128. const warning = codes[code](a, b, c)
  129. process.emitWarning(warning.message, warning.name, warning.code)
  130. }
  131. return {
  132. create,
  133. createDeprecation,
  134. emit,
  135. emitted
  136. }
  137. }
  138. module.exports = processWarning
  139. module.exports.default = processWarning
  140. module.exports.processWarning = processWarning