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

@plone/registry

v1.8.0

Published

Add-on and configuration registry for Plone and for JavaScript and TypeScript-based apps.

Downloads

2,218

Readme

Add-on and configuration registry

This package provides support for building an add-on registry and infrastructure for JavaScript and TypeScript-based apps.

Extensibility and pluggability

As a developer when you build an app, regardless of the framework and technologies used, it's a one-off app. That means you have to build something that has very specific requirements, behavior, and look and feel.

Sometimes you need to build something generic that is pluggable and extensible. In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this.

Add-on registry

An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable.

The add-on registry is a store where you can register a number of add-ons that your app consumes.

Add-on packages are just CommonJS packages. The only requirement is that they point the main key of their package.json to a module that exports as a default function, which acts as a configuration loader.

An add-on can be published in an npm registry, just as any other package. However, add-ons are meant to not be transpiled. They should be released as source packages.

Register an add-on

You should declare your add-on in your project. This is done in your app's package.json's addons key:

{
  "name": "my-app-project",
  "addons": [
    "acme-volto-foo-addon",
    "@plone/some-addon",
    "collective-another-volto-addon"
  ]
}

The addons key ensures the add-on's main default export function is executed, being passed the configuration registry. In that function, the add-on can customize the registry. The function needs to return the config object (the configuration registry), so that it's passed further along to the other add-ons.

The add-ons are registered in the order they are found in the addons key. The last add-on takes precedence over the others. This means that if you configure something in acme-volto-foo-addon, then the same thing later in collective-another-volto-addon, the latter configured thing will win and its configuration will be applied.

The default export of any add-on main index.js file should be a function with the signature config => config. That is, it should take the configuration registry object and return it, possibly mutated or changed.

Configuration registry

The configuration registry supplements the add-on registry.

It is a facility that stores app configuration to be shared in the app.

Let's say that your app is the user interface of a content management system (CMS). This CMS uses blocks as its main fundamental unit of content. The pages that the CMS builds are made up of these blocks. The CMS has some basic available blocks, but it's a requirement that integrators are able to register more blocks in a pluggable way.

This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to the basic CMS ones.

Let's assume we've defined a key in the registry config.blocks.blocksConfig, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry:

  config.blocks.blocksConfig.faq_viewer = {
    id: 'faq_viewer',
    title: 'FAQ Viewer',
    edit: FAQBlockEdit,
    view: FAQBlockView,
    icon: chartIcon,
    group: 'common',
    restricted: false,
    mostUsed: true,
    sidebarTab: 1,
  };

The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS. Then the CMS will properly populate the available blocks in the user interface.

The add-on is meant to extend the initial configuration. From the default export function of our add-on, we should provide the configuration of the new block:

export default function applyConfig(config: ConfigData) {
  config.blocks.blocksConfig.faq_viewer = {
    id: 'faq_viewer',
    title: 'FAQ Viewer',
    edit: FAQBlockEdit,
    view: FAQBlockView,
    icon: chartIcon,
    group: 'common',
    restricted: false,
    mostUsed: true,
    sidebarTab: 1,
  };

  return config;
}

Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block. The add-on will then become available to the CMS when it asks the configuration registry for it.

Accessing the configuration registry

The configuration registry can be accessed by:

import config from '@plone/registry'

const blocksConfig = config.blocks.blocksConfig

Component registry

The configuration registry also stores a components registry in itself. The components registry is a mapping of name to component. You can look up a name, and receive a component that you can reference in your code. This provides an alternative, and more convenient, way to customize components. You can override programmatically such registrations from your add-on or projects because it's stored in the configuration registry. You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather then import it directly. You can even have modifiers to the component registrations through dependencies. Thus you can adapt the call, given an array of such dependencies.

Register components by name using config.registerComponent

You can register components by name, typically from an add-on or project configuration:

import MyToolbarComponent from './MyToolbarComponent'

config.registerComponent({
  name: 'Toolbar',
  component: MyToolbarComponent,
});

Retrieve a component from the component registry

You can programmatically retrieve a component from the registry using config.getComponent:

const Toolbar = config.getComponent('Toolbar').component

Or you can retrieve a component by using the convenience component Component, if you want to use it in JSX code directly.

import Component from '@plone/volto/components/theme/Component/Component';

<Component componentName="Toolbar" {...props} />

Note that you can pass props down to the retrieved component.

Adapt the component using the dependencies array

You can register components, then retrieve them, given a list of modifiers using dependencies.

import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'

config.registerComponent({
    name: 'Teaser',
    component: MyTeaserNewsItemComponent,
    dependencies: 'News Item',
  });

And then retrieve the component:

config.getComponent({
    name: 'Teaser',
    dependencies: ['News Item'],
  }).component

You can have both, either with or without dependencies:

import MyTeaserDefaultComponent from './MyTeaserDefaultComponent'
import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'

config.registerComponent({
    name: 'Teaser',
    component: MyTeaserDefaultComponent,
  });

config.registerComponent({
    name: 'Teaser',
    component: MyTeaserNewsItemComponent,
    dependencies: 'News Item',
  });

Then retrieve them both, depending on the use case. In the example, given a content type value coming from the content prop, you would retrieve them as shown:

<Component componentName="Toolbar" dependencies={[props.content['@type']]} {...props} />