ploadin


Ploadin - Webpack PLugIN and LOADer in one. Use data from loaders in plugins and vice-versa.
🪑 Table of Content
🧰 Features
Are you developing a complex plugin that needs to be used as both a plugin and a loader? ploadin
got you covered:
-
Easily develop Webpack plugins that can access files passed through loaders.
-
Use instances of Ploadin
subclasses both as plugins and loaders.
-
Plugin and loader methods share instance's state, so plugin behaviour can be modified based on data passed to loaders and vice versa.
👶 Install
npm install ploadin
yarn add ploadin
🚀 Usage
Subclassing
Subclass Ploadin
to create a class that can access both plugin and loader contexts.
Following example shows how to communicate between loader and plugin. Following happens in the example:
-
Ploadin
is subclassed to PloadinSubclass
so the loader and plugin methods can communicate.
-
Before compilation starts (and so before loaders are run),pitch data and loader behaviour are decided based on some conditions in apply
.
-
When pitch
is called, the method passes data from plugin down to other pitch
methods
(Webpack docs on pitch).
-
When loader
is called, the loader will skip its job if ignoreLoader
was set to truthy.
-
After all is done, the state is reset in apply
on compiler's afterCompile
hook.
Notice that in loader
and pitch
methods, this
refers to the Ploadin
instance and not loaderContext
as it is in
Webpack loaders.
The loaderContext
is, instead, passed as the first argument, so all other arguments are shifted by one.
const { Ploadin, registerSubclass } = require('ploadin');
export class PloadinSubclass extends Ploadin {
constructor() {
this.pitchData = null;
this.ignoreLoader = false;
super();
}
apply(compiler) {
compiler.hooks.beforeCompile.tapAsync(
'PloadinSubclass',
(stats, callback) => {
this.pitchData = { someData: compiler.xxx };
this.ignoreLoader = compiler.yyy;
callback();
},
);
compiler.hooks.afterCompile.tapAsync(
'SubclassPloadin',
(compilation, callback) => {
this.pitchData = null
this.ignoreLoader = false;
callback();
},
);
}
loader(loaderContext, source, ...args) {
if (this.ignoreLoader) {
return source
}
...
}
pitch(
loaderContext,
remainingRequest,
precedingRequest,
data
) {
Object.assign(data, this.pitchData);
}
}
The equivalent of the above in TypeScript
import { Ploadin, registerSubclass } from 'ploadin';
export class PloadinSubclass extends Ploadin {
constructor() {
this.pitchData = null;
this.ignoreLoader = false;
super();
}
apply(compiler: Compiler) {
compiler.hooks.beforeCompile.tapAsync(
'PloadinSubclass',
(stats, callback) => {
this.pitchData = { someData: compiler.xxx };
this.ignoreLoader = compiler.yyy;
callback();
},
);
compiler.hooks.afterCompile.tapAsync(
'SubclassPloadin',
(compilation, callback) => {
this.pitchData = null
this.ignoreLoader = false;
callback();
},
);
}
loader(loaderContext: any, source?: string, ...args: any[]) {
if (this.ignoreLoader) {
return source
}
...
}
pitch(
loaderContext,
remainingRequest: string,
precedingRequest: string,
data: any,
) {
Object.assign(data, this.pitchData);
}
}
Using in Webpack
To use as plugin, pass the instance itself as a plugin.
To use as loader, pass the asLoader
property.
const { PloadinSubclass } = require('./subclass');
const myPloadinSubclass = new PloadinSubclass();
module.exports = {
plugins: [
myPloadinSubclass,
],
module: {
rules: [
{
test: /\.js$/i,
use: [
myPloadinSubclass.asLoader,
...
],
},
],
},
};
Using multiple subclasses and instances
You can use multiple Ploadin
subclasses, and even multiple instances of the same class, within the same config, they will not interfere.
const { PloadinSubclass } = require('./subclass1');
const { AnotherPloadinSubclass } = require('./subclass2');
const myPloadinSubclass1 = new PloadinSubclass();
const myPloadinSubclass2 = new PloadinSubclass();
const anotherPloadin1 = new AnotherPloadinSubclass();
const anotherPloadin2 = new AnotherPloadinSubclass();
module.exports = {
plugins: [
myPloadinSubclass1,
myPloadinSubclass2,
anotherPloadin1,
anotherPloadin2
],
module: {
rules: [
{
test: /\.js$/i,
use: [
myPloadinSubclass1.asLoader,
myPloadinSubclass2.asLoader,
anotherPloadin1.asLoader,
anotherPloadin2.asLoader,
...
],
},
],
},
};
🤖 API
TypeDoc documentation can be found here.
Ploadin
Ploadin class has following properties:
Ploadin.asLoader: object
Loader object to be used in webpack config.
Following methods will be called if defined:
Ploadin.classOptions: any
Data associated with the Ploadin class. The data returned by classOptions
is
the same data (copy actually) of what is passed to registerSubclass
.
Ploadin.apply(compiler: Compiler): void
Webpack plugin's apply
method. See
Writing a Webpack plugin
for details.
Ploadin.loader(loaderContext: LoaderContext, content?: string, map?: string, data: any): void
Webpack loader's loader
method. See Webpack loaders
for details.
Note that argument signature is shifted as loaderContext
is passed as first argument. this
, instead, refers to Ploadin
instance.
Ploadin.pitch(loaderContext: LoaderContext, remainingRequest: string, precedingRequest: string, data: any): void
Webpack loader's pitch
method. See Webpack loader's pitch
for details.
Note that argument signature is shifted as loaderContext
is passed as first argument. this
, instead, refers to Ploadin
instance.
Helpers
registerSubclass(subclass: Subclass, options: any): boolean
- subclass - class extending
Ploadin
- options - any data associated with this subclass
Returns true
if successfully registered, false
if the class has been
registered before.
Normally, any class subclassing Ploadin
is automatically registered with
instance-manager when a first instance is created. This is necessary so the
class and its instances can be looked up by indices.
You can register the class yourself. This enables you to optionally pass along
options associated with the given class.
One use of this is to store options passed to class factory so we can associate
the options with the dynamically-created class.
🔮 Background
This package was prompted by the challenge of how to use and manage
dynamically created Webpack plugins that need to access both loader and plugin
contexts (similarly to how
mini-css-extract-plugin
needs access to both).
Webpack passes only JSON-serializable data to loaders, so loaders don't have direct access to plugins. And if you're dealing with dynamically-created classes, correctly matching loaders with their respective plugins gets more complicated.
⏳ 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!
🔗 Related Projects
👨🔧 Maintainers
👤 Juro Oravec
📝 License
Copyright © 2020 Juro Oravec.
This project is MIT licensed.