npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

blissful-hooks

v0.0.2

Published

Deep extensibility for all!

Downloads

14

Readme

Blissful Hooks

Deep extensibility for all!

A spinoff from ye olde BlissfulJS project. A precursor of this code powers the plugin system of PrismJS.

  • ✅ Tiny, dependency-free codebase. Literally < 50 loc.
  • ✅ Works everywhere, browsers, JS runtimes, you name it

Usage

For Library Authors

First, create a new Hooks instance, and expose it to third-party code. E.g. if your library is a class, it can be a static property:

// In a browser? Just import src/index.js instead
import Hooks from "blissful-hooks";

class MyAwesomeClass {
	static hooks = new Hooks();
}

If your library is a set of modules, it can just be an export:

import Hooks from "blissful-hooks";
export const hooks = new Hooks();

Or, if you don’t care about potential name clashes across other libraries using this module, you can just use/export the hooks singleton:

import { hooks } from "blissful-hooks";
export { hooks };

Now, at the places in your code where you want to allow extensions, call hooks.run(hookName, env).

hooks.run("hook-name", this);

Plugin code will now be executed with the same context as your code.

Instead of the current context, you can pass variables that plugins can read and manipulate! Ideally, it would be exposing local variables, and you'd be reading and writing directly from it. Then, you pass can still pass the current context to the plugin as a special this or context property (if both are present, this wins). For example, here’s how a syntax highlighter might use it:

let env = {
	callback,
	container,
	selector: 'code[class*="language-"], [class*="language-"] code',
	this: this,
};

hooks.run('before-highlightall', env);

env.elements = env.container.querySelectorAll(env.selector);

hooks.run('before-all-elements-highlight', env);

for (let element of env.elements) {
	highlightElement(element, env.callback);
}

hooks.run('after-all-elements-highlight', env);

For Plugin Authors

Plugin authors use hooks.add() to schedule code to run at a given hook. For example, suppose we wanted to change the selector above to also accept lang-* classes. It would be as simple as:

hooks.add("before-highlightall", function(env) {
	env.selector += ', code[class*="lang-"], [class*="lang-"] code';
});

You can also add multiple hooks at one:

hooks.add({
	"before-highlightall": function(env) {
		env.selector += ', code[class*="lang-"], [class*="lang-"] code';
	},
	"before-all-elements-highlight": function(env) {
		env.elements = Array.from(env.elements).filter(e => !e.classList.contains("no-highlight"));
	}
});

Or the same callback to multiple hooks:

hooks.add(["before-highlightall", "some-other-hook"], function(env) {
	env.selector += ', code[class*="lang-"], [class*="lang-"] code';
});

FAQ

Can a hook cause a function to early return?

Yes, but only with the consent of the library author. Simply returning a value from your hook callback will do nothing (duh). However, the library author can designate that a certain property holds a return value, and if that property is non-empty, they can choose to honor it:

let env = {this: this}
hooks.run("hook-name", env);

if (env.returnValue) {
	return env.returnValue;
}

What happens if I add code to a hook that doesn't exist?

Nothing happens. The code simply never gets executed. Libraries don't need to declare their hooks upfront (by design), so there is no way for Blissful Hooks to know the difference between a hook that hasn't run yet and a hook that never will.

Used By

It took 12 years of usage before I released this as a separate package, but precursors of it are used in several libraries: