Repository: fastify/fastify (JavaScript)

Issue:
Report actual plugin requiring decorator
https://github.com/fastify/fastify/blob/c2c5cfca689234864d5a208301dde10cefab9804/lib/pluginUtils.js#L82-L85

The above assertion isn't too helpful. If a plugin asks for the "foo" decorator on the "fastify" object, then it gives a message like the following when that decorator is not found:

> The decorator 'foo' is not present in Fastify

The stack trace ends up looking something like:

```
"error": {
        "generatedMessage": false,
        "code": "ERR_ASSERTION",
        "actual": false,
        "expected": true,
        "operator": "==",
        "name": "AssertionError",
        "message": "The decorator 'mysql' is not present in Fastify",
        "errorClass": "AssertionError",
        "stacktrace": [
          {
            "file": "node_modules/fastify/lib/pluginUtils.js",
            "method": "<anonymous>",
            "lineNumber": 79,
            "columnNumber": 5
          },
          {
            "file": "node_modules/fastify/lib/pluginUtils.js",
            "method": "Object._checkDecorators",
            "lineNumber": 78,
            "columnNumber": 14
          },
          {
            "file": "node_modules/fastify/lib/pluginUtils.js",
            "method": "Object.checkDecorators",
            "lineNumber": 70,
            "columnNumber": 44
          },
          {
            "file": "node_modules/fastify/lib/pluginUtils.js",
            "method": "Object.registerPlugin",
            "lineNumber": 97,
            "columnNumber": 19
          },
          {
            "file": "node_modules/fastify/fastify.js",
            "method": "Boot.override",
            "lineNumber": 566,
            "columnNumber": 57
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/plugin.js",
            "method": "Plugin.exec",
            "lineNumber": 64,
            "columnNumber": 31
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/plugin.js",
            "method": "Boot.loadPlugin",
            "lineNumber": 187,
            "columnNumber": 10
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/node_modules/fastq/queue.js",
            "method": "release",
            "lineNumber": 140,
            "columnNumber": 16
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/node_modules/fastq/queue.js",
            "method": "Object.resume",
            "lineNumber": 74,
            "columnNumber": 7
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/plugin.js",
            "method": "Plugin.finish",
            "lineNumber": 166,
            "columnNumber": 10
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/plugin.js",
            "method": "<anonymous>",
            "lineNumber": 188,
            "columnNumber": 12
          },
          {
            "file": "node_modules/fastify/node_modules/avvio/plugin.js",
            "method": "done",
            "lineNumber": 115,
            "columnNumber": 5
          }
        ]
      }
    }
```

Notice that none of this information gives me any hint as to which plugin is causing this error.


Discussion:
I can take a look into it, will basically just enhance the report hiding the stack trace of the error object, or keep everything as it is just appending which plugin is having mismatches?
I'd try to keep everything as it is but add a report of the actual plugin causing the error. But I suspect that is going to be quite difficult. So whatever works is fine by me.
To be clear, it's a plugin defined like so that causes the error:

```js
const fp = require('fastify-plugin')

module.exports = fp(thePlugin, { decorators: { fastify: ['foo'] } })

async function thePlugin(server) {
  // something in here requires `foo` on `server`, but
  // we forgot to register the plugin that provides that decoration
}
```
Thank you for solving this @MetCoder95 
Glad to be helpful 🙏 



Patch:
diff --git a/lib/pluginUtils.js b/lib/pluginUtils.js
index cca5b70e528..384b52cec09 100644
--- a/lib/pluginUtils.js
+++ b/lib/pluginUtils.js
@@ -67,21 +67,22 @@ function checkDecorators (fn) {
   const meta = getMeta(fn)
   if (!meta) return
 
-  const decorators = meta.decorators
+  const { decorators, name } = meta
   if (!decorators) return
 
-  if (decorators.fastify) _checkDecorators.call(this, 'Fastify', decorators.fastify)
-  if (decorators.reply) _checkDecorators.call(this[kReply], 'Reply', decorators.reply)
-  if (decorators.request) _checkDecorators.call(this[kRequest], 'Request', decorators.request)
+  if (decorators.fastify) _checkDecorators.call(this, 'Fastify', decorators.fastify, name)
+  if (decorators.reply) _checkDecorators.call(this[kReply], 'Reply', decorators.reply, name)
+  if (decorators.request) _checkDecorators.call(this[kRequest], 'Request', decorators.request, name)
 }
 
-function _checkDecorators (instance, decorators) {
+function _checkDecorators (instance, decorators, name) {
   assert(Array.isArray(decorators), 'The decorators should be an array of strings')
 
   decorators.forEach(decorator => {
+    const withPluginName = typeof name === 'string' ? ` required by '${name}'` : ''
     assert(
       instance === 'Fastify' ? decorator in this : decorator in this.prototype,
-      `The decorator '${decorator}' is not present in ${instance}`
+      `The decorator '${decorator}'${withPluginName} is not present in ${instance}`
     )
   })
 }


Test patch:
diff --git a/test/plugin.test.js b/test/plugin.test.js
index cdb1ea64145..d927b5e5b16 100644
--- a/test/plugin.test.js
+++ b/test/plugin.test.js
@@ -725,6 +725,61 @@ test('plugin metadata - decorators', t => {
   }
 })
 
+test('plugin metadata - decorators - should throw', t => {
+  t.plan(1)
+  const fastify = Fastify()
+
+  fastify.decorate('plugin1', true)
+  fastify.decorateReply('plugin1', true)
+
+  plugin[Symbol.for('skip-override')] = true
+  plugin[Symbol.for('plugin-meta')] = {
+    decorators: {
+      fastify: ['plugin1'],
+      reply: ['plugin1'],
+      request: ['plugin1']
+    }
+  }
+
+  fastify.register(plugin)
+  fastify.ready((err) => {
+    t.equals(err.message, "The decorator 'plugin1' is not present in Request")
+  })
+
+  function plugin (instance, opts, next) {
+    instance.decorate('plugin', true)
+    next()
+  }
+})
+
+test('plugin metadata - decorators - should throw with plugin name', t => {
+  t.plan(1)
+  const fastify = Fastify()
+
+  fastify.decorate('plugin1', true)
+  fastify.decorateReply('plugin1', true)
+
+  plugin[Symbol.for('skip-override')] = true
+  plugin[Symbol.for('plugin-meta')] = {
+    name: 'the-plugin',
+    decorators: {
+      fastify: ['plugin1'],
+      reply: ['plugin1'],
+      request: ['plugin1']
+    }
+  }
+
+  fastify.register(plugin)
+  fastify.ready((err) => {
+    t.equals(err.message, "The decorator 'plugin1' required by 'the-plugin' is not present in Request")
+  })
+
+  function plugin (instance, opts, next) {
+    instance.decorate('plugin', true)
+    next()
+  }
+})
+
 test('plugin metadata - dependencies', t => {
   t.plan(1)
   const fastify = Fastify()