

Generalized and hookable mini-css-extract-plugin. Extract any format, process it your way.
🪑 Table of Content
🧰 Features
-
Exposes 16 hooks that enable you to:
- Override how the modules are parsed when passed to the loader
- Override how the modules are merged into a common file
- Define if and how the modules should should be split
- Hook into the compiler / compilation.
-
Based on mini-css-extract-plugin v0.9.0, this package recycles the same logic, but allows you override the format-specific logic.
-
See how the original mini-css-extract-plugin has been reimplemented using this package.
👶 Install
npm install mini-extract-plugin
🚀 Usage
For a minimal setup, just import the plugin and give it a unique type name by which the plugin will be known. The instances of the created class can be used in Webpack.
import mep from 'mini-extract-plugin';
const MyMiniExtractPlugin = mep({
type: 'my-custom-type'
});
const myMEP = new MyMiniExtractPlugin({
...
});
const config = {
...
module: {
rules: [
{
resourceQuery: /\.some\.json$/,
use: [
myMEP.asLoader,
],
...
}
],
},
plugins: [
myMEP,
...
]
}
However, this example above is not terribly useful since we haven't specified any hooks. That means, for example, that found JSONs will be concatenated into as single file as strings (e.g. "{}" + "{}"), but that won't yield a valid JSON. So to make the new plugin class to work properly, we need to speficy more than just the type.
import mep from 'mini-extract-plugin';
const MyMiniExtractPlugin = mep({
type: 'my-custom-type',
displayName: `My Super Awesome Extract Plugin`,
hooks: [
{
name: 'compilation',
type: 'tap',
hooks: [compilationHookFn],
},
{ name: 'merge', type: 'tap', hooks: [mergeHookFn] },
],
});
const myMEP = new MyMiniExtractPlugin();
...
This is better! We have specified how the modules should be merged and so when
we use this plugin in the Webpack now, the end result will be a proper JSON.
This way, you can override only the parts of the process that needs to be
modified. You can also tap multiple functions into any single hook. And where
available, you can also tap asynchronously. Some hooks are "waterfall", meaning
that result from one function is passed to another. Other are regular hooks
where the tapped functions don't interact. See
tapable
for details on how hooks work.
For details on what hooks are available, their signatures, whether they're sync
or async, etc, see Hooks.
You may have noticed that we've also specified a displayName property.
Additional options allow you to:
- override what classes should be used for modules, dependencies, and other
Webpack entities (useful if you want to pass custom data along with the
modules).
- override what options can be passed to the class constructor when creating a
plugin instance (by default,
mini-css-extract-plugin
options are used)
- override the name of the created class, and how it is displayed.
See full description of the class options here.
Examples
Subclassing
Classes that can be passed to the factory functions can be found at the root of
the export.
See how classes are extended in the re-implementation of mini-css-extract-plugin.
Helpers
The package also exposes a util export, which contains utility modules used for working with the hooks or objects passed to hook functions:
util.module includes helper functions for working with modules.
util.subclass includes helper functions for subclassing the classes that can be passed to the class factory. Use these functions if you need a subclass but don't care about implementation.
Typing
This project is written in TypeScript and the typings are included under the
types import.
types, the root, includes interfaces related to subclassing (MiniExtractPlugin, class factory options, etc.)
types.context includes interfaces for the contexts passed to
tapped functions
types.hook includes types related to Hooks (Hook overrides, types of recognized hooks, etc.)
types.webpack is a shim for Webpack, it includes types that either are not
exposed in Webpack v4, or which have modified interfaces in the extraction
process, so the provided types reflect the actual interface.
types.util includes helper types which may or may not be useful when working with the hooks.
What you will most likely want is to have the hook functions types inferred.
You can use the types.hook.Taps interface, which is an object of
{ [hookName]: hookFunction }.
If you want to define only some hooks, use types.hook.PartialTaps.
import { types } from 'mini-extract-plugin';
const hooks: types.hook.PartialTaps = {
dependency: (context, {exports: exported}) => {
const { childCompilation, classOptions } = context;
...
};
Debugging
This project uses debug. To show debug logs, activate debug for mini-extract-plugin.
CLI example:
DEBUG=mini-extract-plugin node path/to/my/mini-extract-plugin-project
🔮 Background
mini-css-extract-plugin
is great because it allows you to have modularized definitions, which are then
merged or split as necessary for you during the build.
This is important for bundle optimization, but also for maintainability, as you
can keep the information where it makes sense, without making a tradeoff in
logistics.
When working with Vue, I was hoping to manage other auxiliary file types (i18n
and documentation, to be specific) in a similar manner - modularized definition
but processed and emitted as separate files during build.
There's (understandably) not as much support for other file formats as there is for CSS. But since other formats could benefit from same action,generalizing the process was in order (and thus encouraging modularized approach for more formats).
🤖 API
TypeDoc documentation can be found here.
Options
interface ClassOptions {
type: string;
pluginName?: string;
displayName?: string;
className?: string;
moduleType?: string;
pluginOptionsSchema?: any;
loaderOptionsSchema?: any;
dependencyTemplateClass?: DependencyTemplateClass;
dependencyClass?: DependencyClass;
moduleFactoryClass?: ModuleFactoryClass;
moduleClass?: ModuleClass;
hooks?: Overrides;
}
The class factory accepts an object with following options:
-
type - (Required) Namespace used by this class to distinguish it, its
instances, and webpack resources (e.g. modules and dependencies) from other
MiniExtractPlugin classes.
- E.g. MiniCssExtractPlugin uses
css, MiniI18nExtractPlugin uses i18n
-
pluginName - Name of plugin used in identifiers. Prefer package-like
(kebab-case) format.
- Default:
mini-${type}-extract-plugin.
-
displayName - String to be used when printing the class in logging messages
or similar.
- Default:
Mini ${type} Extract Plugin where type is capitalized.
-
className - Name of the plugin class that is shown when calling either class.toString() or class.name.
- Default:
Mini${type}ExtractPlugin where type is capitalized.
-
moduleType - Identifier used to find modules processed by the class.
- Default:
${type}/mini-extract.
-
pluginOptionsSchema - JSON schema used to validate options passed to constructor and used in loader methods.
-
loaderOptionsSchema - JSON schema used to validate options passed to constructor and used in plugin methods.
-
dependencyTemplateClass - Class that implements Webpack's
DependencyTemplate
interface.
- The instance of
dependencyTemplateClass must have a method apply.
- Default: Default is empty implementation.
(see source file).
-
dependencyClass - Class that implements Webpack's
Dependency
interface.
- Dependencies are used among other to pass data to Modules. Data passed to
Dependency constructor can be overriden in
dependency hook.
- If providing ypur own Dependency class, be sure to subclass the default
implementation to ensure the plugin works as expected. See the
re-implementation of MiniCssExtractPlugin
for an example of how the Dependency class can be extended.
- Default: Default implementation ensures the MiniExtractPlugin works
correctly. It stores information on MiniExtractPlugin type, identifier,
context, and content
(see source file).
-
moduleFactoryClass - Class that implements Webpack's
Module Factory
interface.
- The instance of
moduleFactoryClass must have a method create that is
called with data and callback and must call the callback with either an
error as first argument or
Module
instance as second argument. The data contains an array-wrapped dependency of
class dependencyClass.
- Default: Default implementation passed the dependency to Module constructor
(see source file).
If
moduleClass is specified, the created Module will be of class
moduleClass. Otherwise Webpack's Module class
is used.
-
moduleClass - Class that implements Webpack's
Module
interface.
- This class used only with the default
moduleFactoryClass
implementation. If you specify moduleFactoryClass, this option is
ignored.
- Module constructor accepts a dependency of class
dependencyClass.
- If providing your own Module class, be sure to subclass the default
implementation to ensure the plugin works as expected. See the
re-implementation of MiniCssExtractPlugin
for an example of how the Module class can be extended.
- Default: Default implementation ensures the MiniExtractPlugin works
correctly, and it updates identifiers and hashes
(see source file).
-
hooks - Array of objects specifying which Tapable hooks should be tapped
with what functions, and how the hook should behave (sync/async). See
Hooks for the list of available hooks.
Hooks order
Hooks are called in following order:
- Plugin initialization:
Plugin.apply is called
- Modules passed to loader:
Loader.pitch is called and modules are processed to dependencies
- If non-entrypoint chunks are emitted: Render and merge chunks' modules
- Render and merge entrypoint's modules
Hooks
The available hooks in alphabetical order are
(see source file):
afterMerge
-
Signature: (RenderContext,
Source) => void
-
Hook: SyncHook
-
Hook called after Modules were merged into a single Source that will be emitted into a file.
Use this hook if you want to modify the resulting Source without overriding
the merging process itself, or if you want to trigger some behaviour after the the merge step has finished.
-
Default: No behaviour.
afterRenderChunk
afterRenderMain
beforeMerge
-
Signature: (RenderContext,
Module[]) =>
Module[]
-
Hook: SyncWaterfallHook
-
Hook called when merging multiple Modules into a single Source that will be emitted into a file.
Use this hook if you want to modify the list of modules without overriding
the merging process itself.
-
Default: No modifications done.
beforeRenderChunk
-
Signature: (RenderContext,
Module[]) =>
Module[] | Module[][]
-
Hook: SyncWaterfallHook
-
Hook called when Webpack is generating a chunk (non-entrypoint file) from
the given set of modules.
Use this hook if you want to modify the list of modules or if you want to
split the list of modules into multiple chunks without overriding the
rendering process itself.
This hook is called only if the extracted Dependencies from
dependency hook were split into chunks by Webpack.
-
Default: No modifications done.
ModulesGroups | List or list of lists of Options.moduleClass modules. If a list of lists is returned, these are interpreted as module groups. Each module group will emit separate chunk file. |
beforeRenderMain
-
Signature: (RenderContext,
Module[]) =>
Module[] | Module[][]
-
Hook: SyncWaterfallHook
-
Hook called when Webpack is generating a main (entry) file from
the given set of modules.
Use this hook if you want to modify the list of modules or if you want to
split the list of modules into multiple groups without overriding the
rendering process itself.
-
Default: No modifications done.
ModulesGroups | List or list of lists of Options.moduleClass modules. If a list of lists is returned, these are interpreted as module groups. Each module group will emit separate file. |
childCompilation
-
Signature: (PitchCompilationContext) => void
-
Hook: SyncHook
-
Hook called when the compilation of child Compiler is run.
Use this hook if you need to modify the child Compiler's Compilation or if you want to access child Compiler's Compilation's hooks yourself.
-
Default: No behaviour.
childCompiler
-
Signature: (PitchCompilerContext) => void
-
Hook: AsyncParallelHook
-
Hook called after a child Compiler was set up in loader's pitch method. Child Compiler is used to evaluate the modules passed to the loader, and the resulting content will be extracted.
Use this hook if you need to modify the child Compiler before other hooks are tapped, or if you want to access child Compiler's hooks yourself.
-
Default: No behaviour.
compiler
compilation
dependency
merge
-
Signature: (RenderContext,
Module[]) =>
Source
-
Hook: SyncWaterfallHook
-
Hook called when merging multiple Modules into a single Source that will be emitted into a file.
Use this hook if you want to override how Modules are merged.
-
Default: Join contents with newline (\n).
Source | Instance of Webpack's Source with content from the modules. |
pitch
renderChunk
-
Signature: (RenderContext,
Module[][]) =>
RenderManifestEntry
-
Hook: SyncWaterfallHook
-
Hook called when Webpack is generating a chunk (non-entrypoint file) from
the given set of module groups. Each module group should yield a separate
file. The hook should return a list of
RenderManifestEntry
objects which specify the metadata of the to-be-generated file(s).
Use this hook if you want to override how the module groups are rendered and
processed for the chunk generation.
This hook is called only if the extracted Dependencies from
dependency hook were split into chunks by Webpack.
renderMain
-
Signature: (RenderContext,
Module[][]) =>
RenderManifestEntry
-
Hook: SyncWaterfallHook
-
Hook called when Webpack is generating a main (entrypoint) file from
the given set of module groups. Each module group should yield a separate
file. The hook should return a list of
RenderManifestEntry
objects which specify the metadata of the to-be-generated file(s).
Use this hook if you want to override how the module groups are rendered and
processed for the entry file generation.
source
-
Signature: (PitchCompilationContext) => string
-
Hook: AsyncSeriesWaterfallHook
-
Get source code from a Module that was passed to loader's pitch method. Use this hook if you need to modify the source code before it is evaluated.
-
Default: No modifications done.
ModuleSourceCode | String representation of the Module's content that was intercepted by the loader. |
Contexts
Hooks can be tapped to modify the extraction process at different stages. Therefore, also the information available is different. That's why different hooks expose different "contexts", or objects with contextual information available at the point in time of the call.
Here is the list of used contexts (see source file):
CompilerContext
plugin | Instance of MiniExtractPlugin where the process occurs | MiniExtractPlugin |
classOptions | Class options used to create the MiniExtractPlugin class that was used to create this instance. Shorthand for plugin.classOptions. See Options. | Options |
options | Options passed to the MiniExtractPlugin instance. Shorthand for plugin.options. Options object is defined by the pluginOptionsSchema and loaderOptionsSchema class option. | object |
compiler | Webpack's Compiler instance exposed to the plugin's apply method. | Compiler |
CompilationContext
Same as CompilerContext plus following:
RenderContext
Same as CompilationContext plus following:
Example of RenderManifestOptions object
{
chunk: Chunk {
id: 'entry2',
ids: [Array],
debugId: 1047,
name: 'entry2',
preventIntegration: false,
entryModule: [NormalModule],
_modules: [SortableSet [Set]],
filenameTemplate: undefined,
_groups: [SortableSet [Set]],
files: [],
rendered: false,
hash: '61a4b63e502b1ca58699200b35ff1691',
contentHash: [Object: null prototype],
renderedHash: '61a4b63e502b1ca58699',
chunkReason: undefined,
extraAsync: false,
removedModules: undefined
},
hash: '9d88bf494be3f1082cdb',
fullHash: '9d88bf494be3f1082cdbb4388c176e06',
outputOptions: {
path: '/Users/path/to/project/dist',
filename: '[name].js',
chunkFilename: '[name].js',
webassemblyModuleFilename: '[modulehash].module.wasm',
library: '',
hotUpdateFunction: 'webpackHotUpdate',
jsonpFunction: 'webpackJsonp',
chunkCallbackName: 'webpackChunk',
globalObject: 'window',
devtoolNamespace: '',
libraryTarget: 'var',
pathinfo: true,
sourceMapFilename: '[file].map[query]',
hotUpdateChunkFilename: '[id].[hash].hot-update.js',
hotUpdateMainFilename: '[hash].hot-update.json',
crossOriginLoading: false,
jsonpScriptType: false,
chunkLoadTimeout: 120000,
hashFunction: 'md4',
hashDigest: 'hex',
hashDigestLength: 20,
devtoolLineToLine: false,
strictModuleExceptionHandling: false
},
moduleTemplates: { javascript: [ModuleTemplate], webassembly: [ModuleTemplate] },
dependencyTemplates: Map(34) {
'hash' => '',
[Function: CssDependency] => CssDependencyTemplate {},
[Function] => ConstDependencyTemplate {},
...
}
}
PitchContext
plugin | Instance of MiniExtractPlugin where the process occurs | MiniExtractPlugin |
classOptions | Class options used to create the MiniExtractPlugin class that was used to create this instance. Shorthand for plugin.classOptions. See Options. | Options |
options | Options passed to the MiniExtractPlugin instance. Shorthand for plugin.options. Options object is defined by the pluginOptionsSchema and loaderOptionsSchema class option. | object |
loaderContext | Webpack's Loader Context. | LoaderContext |
remainingRequest | Request part ahead of the pitch. A string passed to pitch as the first argument. See Webpack's Loader API. | string |
precedingRequest | Request part past the pitch. A string passed to pitch as the second argument. See Webpack's Loader API. | string |
data | Data object to passed to pitch as the third argument. See Webpack's Loader API. | object |
PitchCompilerContext
Same as PitchContext plus the following:
childCompiler | Child instance of Webpack's Compiler used to render the extracted modules. | Compiler |
PitchCompilationContext
Same as PitchCompilerContext plus the following:
LoaderModuleContext
Data generated by evaluating the source code of the
Module
that triggered the MiniExtractPlugin's loader. The data is used to create
Dependencies
that will be used to emit files with extracted content.
source | String representation of the source code (value returned by source hook). | string |
locals | Local variables from the evaluated source code. | any |
exports | Exported variables from the evaluated source code. | any |
⏳ 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
There is no explicit roadmap for this project. 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
This project is based on mini-css-extract-plugin and the amazing work of Tobias Koppers (@sokra) and other Webpack maintainers.
👨🔧 Maintainers
👤 Juro Oravec
📝 License
Copyright © 2020 Juro Oravec.
This project is MIT licensed.