instance-manager v1.1.5
instance-manager
Manage references to dynamically created classes and their instances.
๐ Homepage | ๐ Repository | ๐ฆ NPM | ๐ Documentation | ๐ Issue Tracker
๐ช Table of Content
- ๐งฐ Features
- ๐ฎ Background
- ๐ถ Install
- ๐ Usage
- ๐ค API
- โณ Changelog
- ๐ Developing
- ๐ Roadmap
- ๐ค Contributing
- ๐ง Contributors
- โญ Show your support
- ๐ Community
- ๐ Related Projects
- ๐จโ๐ง Maintainers
- ๐ License
๐งฐ 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
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
- ploadin - Webpack plugin and loader in one
- mini-extract-plugin - Generalized extensible variation of mini-css-extract-plugin that can be used to create plugins to extract text from custom file types
๐จโ๐ง Maintainers
๐ค Juro Oravec
- Twitter: @JuroOravec
- GitHub: @JuroOravec
- LinkedIn: @jurooravec
- Sourcerer: @JuroOravec
๐ License
Copyright ยฉ 2020 Juro Oravec.
This project is MIT licensed.