unplugin icon indicating copy to clipboard operation
unplugin copied to clipboard

Feature: Custom Hooks

Open jwr1 opened this issue 3 years ago • 2 comments

Custom hooks would be useful for plugin creators looking to extend an already existing plugin's functionality. For example, let's say I wanted to create a plugin that adds to (or modifies) the presets in unplugin-auto-import; I could use the "presets" hook to gain access to the presets map and modify it from there. Since webpack already allows plugins to create their own hooks, we can see some already existing use cases, like how the html-webpack-plugin lets other plugins modify the HTML output in various locations.

Proof of Concept

Here's a rough design of what the custom hooks API could look like.

Creating hooks:

import { createUnplugin, SyncHook } from 'unplugin';

import { SyncHook } from 'unplugin/hooks'; // Maybe even separate hook types into another import

export const unplugin = createUnplugin((options) => {
  return {
    name: 'unplugin-auto-import',

    hooks: {
      presets: SyncHook, // Define the hook
    },

    buildStart() {
      this.hooks.presets(currentPresets); // Run the hook
    },
  };
});

export const hooks = unplugin.hooks

Using hooks:

import { createUnplugin } from 'unplugin';

import UnpluginAutoImportHooks from 'unplugin-auto-import/hooks';

export const unplugin = createUnplugin((options) => {
  return {
    name: 'unplugin-auto-import-presets',

    // Use hook from another unplugin
    [ UnpluginAutoImportHooks.presets /* this is a symbol to ensure uniqueness */ ](presets) {
      presets['my-preset'] = '...';
      delete presets.react;
    },

  };
});

jwr1 avatar Sep 21 '22 16:09 jwr1

I personally would consider this a bit out-of-scope.

/cc @pi0 do you think this could have a valid use case using bookable?

antfu avatar Sep 22 '22 02:09 antfu

I think this feature can be easily implemented by creating a hookable instance and register user provided hooks but also seems a nice idea to consider being built-in (can't think of any generic use case). On one hand, simple callback option can be also used for simpler usecases.

pi0 avatar Sep 22 '22 14:09 pi0

One way I think could be more generic is that we implement the Rollups' api props that basically allowing arbitrary objects to pass in, and let plugins define the interface (via bookable or plain values). https://rollupjs.org/guide/en/#inter-plugin-communication

antfu avatar Sep 23 '22 05:09 antfu

That does seem like a better option, as it more closely follows Rollup's API.

jwr1 avatar Sep 24 '22 02:09 jwr1

@antfu, I have created a new proposal that includes your suggestions and follows closer to Rollup's API. I also included a method to create new plugin instances (if they do not already exist) (suggested in https://github.com/unjs/unplugin/issues/173).

Closing in favor of https://github.com/unjs/unplugin/issues/174.

jwr1 avatar Oct 02 '22 19:10 jwr1