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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@itrocks/compose

v0.1.0

Published

Class compositions via configuration file, enabling mixins addition and module exports replacement

Downloads

77

Readme

npm version npm downloads GitHub issues discord

compose

Class compositions via configuration file, enabling mixins addition and module exports replacement.

Installation

npm install @itrocks/compose

Usage

The compose() function must be called as early as possible, before any configured class is loaded.

In practice: call compose() before any import() or require() of application modules.

A minimal and typical setup can be seen in @itrocks/framework.

Example

Given the @itrocks/user package providing a User class, and an override of this class in a user-override.ts file stored at your project root:

// user-override.ts
import { User } from '@itrocks/user'
export class UserOverride extends User {}

Your main entry point may start with:

// main.ts
import { compose } from '@itrocks/compose'

compose(__dirname, {
	'@itrocks/user': '/user-override'
})

// ... in code using User through dynamic import/require,
// User will be replaced by UserOverride and include any new or overridden features.

Limitations

CommonJS execution

This package relies on dynamic module loading. In Node.js:

  • require() is dynamic and can be overridden,
  • native ES import is static and cannot.

You can write ES import syntax in TypeScript, then transpile it to CommonJS, where imports become require().

Recommended minimal TypeScript configuration:

{
	"compilerOptions": {
		"module": "nodenext",
		"moduleResolution": "nodenext",
		"target": "ES2022"
	}
}

Static import declarations

Static imports must be declared first in modules. As a result, compose() will only affect:

  • modules loaded dynamically after it is called,
  • static imports declared inside dynamically loaded modules.

API

compose(baseDir, config)

compose(baseDir: string, config: Record<string, string | string[]>): void

Parameters:

  • baseDir: a directory used to resolve paths starting with /
  • config: an object where:
    • the key is the module (and optional export) to replace
    • the value is the replacement module (and optional export), or an array of replacements

Configuration module export format

Both replaced and replacement module / export apply these rules:

  • A module path starting with / refers to the JavaScript file path, relative to baseDir's argument.
  • A module path not starting with / refers to the name of node_modules package or package module.
  • The name of the export can be explicitly defined after the module path, separated by ':'.
  • If no export is given: the default export is used if set, otherwise the first exported value encountered at runtime is used.
  • If the default export is given but the module has no default export, the first exported value encountered at runtime is used.

Note: omitting :exportName is strictly equivalent to using :default.

Single replacement

If one replacement is given for a module, it is applied:

  • if the replacement inherits the original type, it is selected as the replacement type,
  • otherwise, the original type is kept as the base type, and the replacement is applied as a mixin via @itrocks/use,

Multiple replacements

If multiple replacements are given for a module, they are applied in the order they are defined:

  • the first entry that inherits the original type is selected as the replacement base type,
  • if no entry inherits the original type: the original type is kept as the base type,
  • the remaining entries are applied as mixins via @itrocks/use.

Summary table

| Configuration entry | Effect | |--------------------------------------------|----------------------------------------------------------------------------------------------------------| | 'pkg' : 'override' | Replaces the module default export (or first export) with the override default export (or first export). | | 'pkg:default' : 'override' | Same as above, explicit default on both sides. | | 'pkg:User' : 'override:UserOverride' | Replaces the named export User with UserOverride. | | 'pkg' : ['override'] | Single replacement: if it inherits the original type → replacement, otherwise applied as a mixin. | | 'pkg' : ['override', 'mixinA', 'mixinB'] | First inheriting entry becomes the base type, others are applied as mixins (in order). | | 'pkg' : ['mixinA', 'mixinB'] | No replacement found: original type kept, all entries applied as mixins. | | '/local-module' : 'pkg' | Local module (resolved from baseDir) replaces a node_modules package. | | 'pkg' : '/local-module' | Package export replaced by a local implementation. | | 'pkg' : 'override:mixinOnly' | No inheritance detected → original kept, override applied as mixin. |

Common mistakes

Calling compose() too late

import { compose } from '@itrocks/compose'
import { User }    from '@itrocks/user'

compose(__dirname, { '@itrocks/user': '/user-override' }) // too late

If a module is already loaded, its exports are fixed.
compose() will not retroactively replace anything.

✔️ Always call compose() before loading any module you want to affect.

Using native ES modules at runtime

Running Node.js in native ESM mode means:

  • import is static
  • module loading cannot be intercepted

In that case, compose() cannot work.

✔️ Use TypeScript with CommonJS output (even if you write import syntax).

Expecting static imports to be replaced

import { User } from '@itrocks/user'

Static imports are resolved before any runtime code runs.

✔️ Only modules loaded:

  • dynamically (require, or import transpiled to require at runtime)
  • or statically inside dynamically loaded modules

can be affected.

Forgetting that :default is implicit

'@itrocks/user': '/user-override'

This is strictly equivalent to:

'@itrocks/user:default': '/user-override:default'

✔️ If a module has no default export, compose() will use the first exported value it finds.

Assuming an override always replaces the base type

If a replacement does not inherit the original type:

  • it is not used as the base
  • it is applied as a mixin instead

✔️ To fully replace a type, the replacement must extend or inherit from it.

Mixing up path resolution rules

'@itrocks/user': 'user-override' // node resolution '@itrocks/user': '/user-override' // resolved from baseDir

✔️ Paths starting with / are resolved from baseDir.
✔️ Others are resolved via Node’s module resolution (node_modules).