instance-manager
TypeScript icon, indicating that this package has built-in type declarations

1.1.5 • Public • Published

instance-manager

Latest Version Documentation Contributions welcome License: MIT Package Size

Build Status Dependencies Known Vulnerabilities CodeCov Total alerts Language grade: JavaScript Maintainability


Manage references to dynamically created classes and their instances.

🏠 Homepage | 🗃 Repository | 📦 NPM | 📚 Documentation | 🐛 Issue Tracker

🪑 Table of Content

🧰 Features

  • Useful for managing references to dynamically-created classes and their instances
  • If you use class factory functions to create classes, you can store the class config along with the class in instance-manager
  • Access classes and their instances using primitives (numbers)
  • Classes and instances are weakly-references, so they will be garbage-collected

🔮 Background

This package is used to enable loader and plugin instances created from dynamically created classes to share information in Webpack, effectively using a single dynamically created instance to behave both as a loader and a plugin.

If you need that functionality, head over to ploadin, where the functionality has been abstracted.

This package can be used for more cases. The problem faced can be generalized as follows:

  • There are dynamically created classes with unknown number of instances.
  • You need to access a specific instance of a specific class.
  • You are unable to pass the instance reference directly.

👶 Install

npm install instance-manager

Then in your file

const InstanceManager = require('instance-manager').default;
const im = new InstanceManager();

Or if using TypeScript

import InstanceManager from 'instance-manager';
const im = new InstanceManager();

🚀 Usage

Simple usage

// -----
// Setup
// -----
 
// Import the package
const InstanceManager = require('instance-manager');
 
// Instantiate
const im = new InstanceManager();
 
// Class factory that creates a class based on passed options
const classFactory = (options) => {
  class MyClass {
    hello() {
      return options.message;
    }
  }
  return MyClass;
};
 
// Create a dynamic class
const myClassOptions = { param: 42 };
const MyDynamicClass = classFactory(myClassOptions);
 
// -----------------------------
// InstanceManager API - Classes
// -----------------------------
 
// Add classes
const arrayClassId = im.addClass(Array);
 
// Optionally pass an object. Useful when working with dynamically created
// classes where the object can be the parameters that were passed to the
// factory function
const myClassId = im.addClass(MyDynamicClass, myClassOptions);
 
// Get classes by ID
im.getClassById(arrayClassId) === Array; // true
 
// Or get class IDs
im.getClassId(Array) === arrayClassId; // true
 
// Get class options (the 2nd argument)
im.getClassOptions(MyDynamicClass) === myClassOptions; // true
 
// Or get class options by class ID
im.getClassOptionsById(myClassId) === myClassOptions; // true
 
// Remove class and all its instances along from InstanceManager
im.removeClass(Array);
 
// -------------------------------
// InstanceManager API - Instances
// -------------------------------
 
// Adds class instances.
const instanceId1 = im.addInstance(new MyDynamicClass());
 
// Get instance id
const anotherInstance = new MyDynamicClass();
const anotherId = im.addInstance(anotherInstance);
const sameId = im.getInstanceId(anotherInstance);
anotherId === sameId; // true
 
// If the class hasn't been registered before it will be added too.
const myNum = new Number('2');
const numInstanceId = im.addInstance(myNum);
im.getClassId(Number); // some number, so it is registered
 
// Get instance having class reference and knowing instance ID
im.getClassInstance(MyDynamicClass, anotherId) === anotherInstance; // true
 
// Get instance knowing only class ID and instance ID
const klass = im.getClassById(myClassId);
im.getClassInstance(klass, anotherId) === anotherInstance; // true
 
// Remove single instance
im.removeClassInstance(MyDynamicClass, instanceId1);
 
// Remove instance by reference
im.removeInstance(myNum);

Advanced usage

Advanced usage is shown with TypeScript.

Problem

Imagine we have the following 3 files:

  • class-factory.ts - defines a class factory.
  • create-message.ts - dynamically creates a class and instance. Sends a message that is read by listener.
  • listener.ts - reads message, needs to access an instance that created the message, but doesn't have a way to get the reference directly.
// class-factory.ts
export default classFactory = (data: any) => {
  class MyClass {
    makeMessage(senderContext: any) {
      // ...some behavior based on given data
    }
    doSomethingElseWithData(listenerContext: any) {
      // ...some other behavior based on given data
    }
  }
  return MyClass;
};
// create-message.ts
import classFactory from './class-factory';
import someData from './some-data';
import otherData from './other-data';
import messageQueue from './message-queue';
 
// Dynamically create your class and instance
const CustomClass = classFactory(someData);
const customInstance = new CustomClass(otherData);
 
const senderContext = this;
const message = customInstance.makeMessage(senderContext);
 
// Send signal that listener is waiting for. You can pass only strings.
// In Webpack, this is the quivalent of specifying the path to the loader
messageQueue.send(message);
// listener.ts
import messageQueue from './message-queue';
 
messageQueue.listen((listenerContext, message) => {
  // Here, we need to do something with the listenerContext, that is available
  // only in this function, but we also need to work with the data that was
  // passed to the class and instance that created this message.
  //
  // In other words, we need to call
  // customInstance.doSomethingElseWithData(listenerContext)
  //
  // But we don't have reference to the instance, so what can we do?
});

Solution

We will use the instance-manager to store references to each newly created class and instance and we will attach these IDs as properties of those classes and instance.

We will then send the IDs as strings through the message.

The listener will be able to read the IDs, and will be able to request the correct instances from our instance of instance-manager.

// provider.ts
import InstanceManager from 'instance-manager';
 
// InstanceManager instance that will manage our dynamically created
// instances and classes
const provider = new InstanceManager<any, any>();
export default provider;
// class-factory.ts
import provider from './provider';
 
// This time, we register each created class and instance to instance-manager
 
export default classFactory = (data: any) => {
  class MyClass {
    constructor() {
      // Register a new instance on creation
      this._instanceId = provider.addInstance(this);
    }
    // ... the rest is same as before
  }
 
  // Register the newly created class on creation and keep it's ID
  MiniExtractPlugin._classId = provider.addClass(MyClass);
 
  return MyClass;
};
// create-message.ts
import classFactory from './class-factory';
import someData from './some-data';
import otherData from './other-data';
import messageQueue from './message-queue';
 
// Dynamically create your class and instance
const CustomClass = classFactory(someData);
const customInstance = new CustomClass(otherData);
 
const senderContext = this;
const message = customInstance.makeMessage(senderContext);
 
// This time, we can access the IDs that were generated by registering the
// class / instance with the InstanceManager
const { _classId, _instanceId } = customInstance;
 
// So this time, we can pass the class and instance IDs to the listener
messageQueue.send(`${message}?classId=${_classId}&instanceId=${_instanceId}`);
// listener.ts
import messageQueue from './message-queue';
import provider from './provider';
import { parseMessage } from './some-utils';
 
messageQueue.listen((listenerContext, message) => {
  // This time, we can get the class and instance IDs from the message
  const { classId, instanceId } = parseMessage(message);
  // And so we can request the correct instance from our instance of
  // instance-manager
  const klass = provider.getClassById(classId);
  const instance = provider.getClassInstance(klass, instanceId);
  // And we can successfully pass the context to the desired method
  // 🎉🎉🎉
  instance.doSomethingElseWithData(listenerContext);
});

🤖 API

Full API documentation can be found here.

⏳ Changelog

This projects follows semantic versioning. The changelog can be found here.

🛠 Developing

If you want to contribute to the project or forked it, this guide will get you up and going.

🏗 Roadmap

This package is considered feature-complete. However, if you have ideas how it could be improved, please be sure to share it with us by opening an issue.

🤝 Contributing

Contributions, issues and feature requests are welcome! Thank you ❤️

Feel free to dive in! See current issues, open an issue, or submit PRs.

How to report bugs, feature requests, and how to contribute and what conventions we use is all described in the contributing guide.

When contributing we follow the Contributor Covenant. See our Code of Conduct.

🧙 Contributors

Contributions of any kind welcome. Thanks goes to these wonderful people ❤️

Recent and Top Contributors

Hall of Fame Contributor 1 Hall of Fame Contributor 2 Hall of Fame Contributor 3 Hall of Fame Contributor 4 Hall of Fame Contributor 5 Hall of Fame Contributor 6 Hall of Fame Contributor 7 Hall of Fame Contributor 8

Generated using Hall of Fame.

All Contributors

Contribution type emoji legend

No additional contributors. Be the first one!

This project follows the all-contributors specification.

⭐ Show your support

Give a ⭐️ if this project helped you!

🐙 Community

🔗 Related Projects

👨‍🔧 Maintainers

👤 Juro Oravec

📝 License

Copyright © 2020 Juro Oravec.

This project is MIT licensed.

Readme

Keywords

none

Package Sidebar

Install

npm i instance-manager

Weekly Downloads

10

Version

1.1.5

License

MIT

Unpacked Size

34.6 kB

Total Files

8

Last publish

Collaborators

  • juro-oravec