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

@shrub/core

v0.5.86

Published

A framework for modular server-side applications and front-end components.

Downloads

215

Readme

Description

Shrub is a simple framework for building modular server-side applications and front-end components. The Core package provides a basic API for defining and loading modules.

Creating a Module

A module can be defined as a class that implements the IModule interface or as a JSON object that satisfies the IModule interface.

export class FooModule implements IModule {
    readonly name = "foo";
}
const fooModule: IModule = {
    name: "foo"
};

There are a few methods a module can define that allow it to configure functionality provided by the module and each method is invoked in the following order.

1. initialize(init: IModuleInitializer): void

Allows a module the ability to perform pre-initialization that needs to happen before any modules or services are configured. Such tasks include binding settings or registering a configuration interface that is exposed to other modules.

2. configureServices(registration: IServiceRegistration): void

Register services with a service collection.

3. configure(configurator: IModuleConfigurator): void | Promise<void>

Allows a module to perform additional configuration, such as configure a dependency module. This method supports async operations which can be useful if data needs to be fetched from a remote service during configuration.

Dependencies

Module depenedencies are specified by defining a dependencies property on a module and lifecycle methods get invoked on the dependency before they are invoked on the dependent.

export class FooModule implements IModule {
    readonly name = "foo";
    readonly dependencies = [BarModule];
}

Configuration

export const IBarModuleConfiguration = createConfig<IBarModuleConfiguration>();
export interface IBarModuleConfiguration {
    registerWidget(widget: IWidget): void;
}

export class BarModule implements IModule {
    readonly name = "bar";

    initialize({ config }: IModuleInitializer): void {
        config(IBarModuleConfiguration).register(() => ({
            registerWidget: widget => {}
        }));
    }
}

export class FooModule implements IModule {
    readonly name = "foo";
    readonly dependencies = [BarModule];

    configure({ config }: IModuleConfigurator): void {
        config.get(IBarModuleConfiguration).registerWidget({});
    }
}

Settings and Options

While Dependency Configuration is a way for one module to configure another at load time. Settings provide a way to provide settings/configuration externally, such as from a config file.

Module Settings are provided as a simple object keyed by a module's name; for example, the below is an example settings object that defines settings for the 'foo' module:

const settings = {
    foo: {
        key: value
    }
};

A module can access these settings directly from the configure method:

export class FooModule implements IModule {
    readonly name = "foo";

    configure({ settings }: IModuleConfigurator): void {
        const keyValue = settings.key;
    }
}

Sometimes it's useful to pass settings to service instances and this can be done via Service Options.

export const IFooOptions = createOptions<IFooOptions>("foo-options");
export interface IFooOptions {
    readonly value: string;
}

export class FooModule implements IModule {
    readonly name = "foo";

    initialize({ settings }: IModuleInitializer): void {
        settings.bindToOptions(IFooOptions);
    }

    configureServices(registration: IServiceRegistration): void {
        registration.registerTransient(IFooService, FooService);
    }
}

export class FooService implements IFooService {
    constructor(@IFooOptions private readonly options: IFooOptions) {
    }
}

Loading Modules

Modules are loaded using the ModuleLoader class by simply invoking ModuleLoader.load.

await ModuleLoader.load([
    FooModule,
    BarModule
]);

Note: If a module has any dependencies not specified when calling ModuleLoader.load those dependencies will automatically get loaded.

If you want a little more control the module loader provides additional methods for configuring the service collection or settings.

await ModuleLoader()
    .useModules([
        FooModule,
        BarModule
    ])
    .useSettings({
        foo: { value: "Hello!" }
    })
    .load();