modular-css v22.3.0

# modular-css Guide

# Introduction

# Overview

modular-css implements the best features of the CSS Modules spec and then adds on several extra features to make for a smoother developer experience. It has support for almost all the different ways you might want to work, easy extensibility, and allows for using the entire ecosystem of postcss plugins to customize your CSS however you see fit.

# Why

CSS Modules has been abandoned for anyone who doesn’t use webpack, and the webpack version doesn’t support the features we need. Attempts to improve that situation have been unsuccessful for a variety of reasons. Thus, a perfect storm of compelling reasons to learn PostCSS was found.

Also because this:

Green pills look gross

# How

There are a lot of different ways to use modular-css, pick your favorite!

# Features

modular-css implements the best features of the CSS Modules spec and then adds on several extra features to make for a smoother developer experience.

# Scoped Selectors

By default all CSS selectors live in the global scope of the page and are chosen based on specificity rules. This has proven to be a model that makes it difficult to succeed and incredibly easy to blow off your own foot. modular-css scopes all selectors to the local file by default, ensuring that your CSS is always exactly as specific as it should be.

.wooga { color: red; }

/* Becomes */

.f5507abd_wooga { color: red; }

By default the selector scoping is based off hashing the contents of the file but you can also provide your own custom function.

Using these now-mangled selectors would be problematic, if modular-css didn’t give you the tools required to use them easily. When using the browserify transform any require() calls for CSS files will instead return an object where the keys match the classes/ids defined in the requested CSS file.

var css = require("./styles.css");

// css is:
/*
{
    wooga : "f5507abd3_wooga",
    ...
}
*/

// so mithril code (or any templating code!) can do the following
m("div", { class : css.wooga });
// which would output
// <div class="f5507abd_wooga"></div>

These arrays of selectors can then be applied to elements using the much more nicely-named object keys and you’re off to the races.

You can opt out of selector scoping by wrapping your classes/ids in the :global() pseudo-class, this will prevent them from being renamed but they will still be available in the module’s exported object.

/* == styles.css == */
:global(.global) { color: red; }
var css = require("./styles.css");

// css is:
/*
{
    global : "global"
}
*/

Selector scoping is only done on simple classes/ids, any selectors containing tags or pseudo-selectors won’t be exported.

:global() is treated the same as a CSS pseudo-class and therefore cannot wrap multiple comma seperated rules. For example if you’re using a CSS reset the following is required:

/* Local Scoped */
ol, ul {
    list-style: none;
}

/* Global Scoped (Wrong!) */
:global(ol, ul) {
    list-style: none;
}

/* Global Scoped (Correct!) */
:global(ol), :global(ul) {
    list-style: none;
}

Adding :global() to every comma seperated rule would be tedious when using something like Eric Meyer’s CSS Reset. Therefore it is recommended that you seperate the reset in to its own file, and make use of the postcss-import module with the after or done hooks to include the file when modular-css has finished processing. You would then need to include @import "reset.css"; somewhere in one of your CSS files.

# Composition

Selector limitations mean that it’s difficult to use complicated selectors, so to enable building anything of complexity you can compose selectors. These compositions can be within a file or even pull in classes defined in other files.

/* == styles.css == */
.single {
    composes: other from "./other.css";
    color: red;
}

.multiple {
    composes: more, than, one from "./multiple.css";
    /*
    Since .multiple doesn't declare any rules other than composes
    aggregate styles from other rules it will be stripped from the output
    */
}

.local {
    composes: single;
}

When this file is required the JS object will contain the expected keys, but the arrays will now contain more values.

var css = require("./styles.css");

// css is:
/*
{
    single   : "dafdfcc_other aeacf0c_single",
    // Since .multiple is only a singular composes: declaration there's no need
    // for it to be rewritten, it's left out of the output
    multiple : "dafdfcc_more f5507abd_than aeacf0c_one",
    local    : "dafdfcc_other aeacf0c_single"
}
*/

If you’re going to be doing a lot of composition with another file you can store the filename into a value for ease of referencing.

@value guide: "../style-guide.css";

.head {
    composes: heading from guide;
    font-size: 120%;
}

.body {
    composes: body from guide;
    padding: 10px;
}

You can also get access to variables defined in other files for simple sharing of useful information.

@value first, second from "./somewhere-else.css";

# Overriding Styles

Sometimes a component will need some customization for use in a specific location/design, but you don’t want to bake that customization into the component.:external(...) helps you solve that problem.

In this case we’ve got an input component that is normally 100% of the width of its container, but when it’s within the fieldset component it should only be half as wide.

/* == input.css == */
.input {
    width: 100%;
}

/* == fieldset.css == */
.fieldset :external(input from "./input.css") {
    width: 50%;
}

# Values

Values are re-usable pieces of content that can be used instead of hardcoding colors, sizes, media queries, or most other forms of CSS values. They’re automatically replaced during the build with their defined value, and can also be composed between files for further re-use or overriding.

/* == values.css == */
@value alert: #F00;
@value small: (max-width: 600px);

@media small {
    .alert { color: alert; }
}

/* will be output as */

@media (max-width: 600px) {
    .alert { color: #F00; }
}

# Importing Values

@values can be imported from another file by using a slightly different syntax.

/* == colors.css == */
@value main: red;
@value bg: white;

/* == site.css == */
@value main from "./colors.css";

body {
    color: main;
}

It’s also possible to import multiple values at once.

/* == colors.css == */
@value main: red;
@value bg: white;

/* == site.css == */
@value main, bg from "./colors.css";

body {
    color: main;
    background: bg;
}

# Namespaced values

@values can be imported as a namespace which provides a convenient shorthand way to access a bunch of shared values from a file.

/* == colors.css == */
@value main: red;
@value bg: white;

/* == site.css == */
@value * as colors from "./colors.css";

body {
    color: colors.main;
    background: colors.bg;
}

# wildcard values

It’s possible to import all the @value definitions from another file into the current one. Any local @value declarations will override the imported values.

/* == colors.css == */
@value main: red;
@value bg: white;

/* == site.css == */
@value * from "./colors.css";
@value bg: black;

body {
    background: bg; /* black */
    color: main; /* red */
}

Since all files in modular-css with @value declaration make that value available to other files it’s possible to use the wildcard imports feature to build complex theming systems. When using wildcard imports all the @values from the source file are re-exported by the file doing the importing.

/* == colors.css == */
@value main: red;
@value bg: white;

/* == mobile-colors.css == */
@value * from "./colors.css";
@value bg: gray;

/* == site.css == */
@value * as colors from "./mobile-colors.css";

body {
    background: colors.bg; /* gray */
    color: colors.main; /* red */
}

# Usage

# rollup

Rollup support for modular-css.

# Install

> npm i @modular-css/rollup

# Usage

# Usage via API

const bundle = await rollup({
    input   : "./index.js",
    plugins : [
        require("@modular-css/rollup")()
    ]
});

# Usage via rollup.config.js

import css from "@modular-css/rollup";

export default {
    input   : "./index.js",
    output  : {
        dest    : "./gen/bundle.js",
        format  : "umd"
    },
    plugins : [
        css()
    ]
};

# rollup Options

# common

File name to use in case there are any CSS dependencies that appear in multiple bundles. Defaults to “common.css”.

# dev

Enable dev mode. In dev mode the default export of a CSS file will be a Proxy instead of a bare object. Attempts to access non-existant properties on the proxy will throw a ReferenceError to assist in catching invalid usage.

# include/exclude

A minimatch pattern, or an array of minimatch patterns, relative to process.cwd(). include defaults to **/*.css.

# json

Boolean/String to determine if JSON files containing all exported classes & values should be output. If set to true will write out to a file named exports.json. If a String will write out to that file name. Defaults to false.

# map

Boolean to determine if inline source maps should be included. Defaults to true.

To force the creation of external source maps set the value to { inline : false }.

# meta

Boolean/String to determine if chunk metadata should be output. If set to true will write out a file named metadata.json. If a String will write out to that file name. Defaults to false.

Currently the only metadata being written is CSS dependencies, but that may change in the future.

# namedExports

By default this plugin will create both a default export and named exports for each class in a CSS file. You can disable this by setting namedExports to false.

# styleExport

By default this plugin will extract and bundle CSS in a separate file. If you would like the styles from each imported CSS file to be exported as a string for use in JS, you can enable this by setting styleExport to true. If you are using this option the after & done hooks will not run against the exported styles, you should perform any additional CSS transformations in the processing hook instead.

import { styles } from "./styles.css";

Enable styleExport will also disable the plugin from emitting any assets as well as sourcemaps (unless you explicitly opt-in to sourcemaps via the map option)

# processor

Pass an already-instantiated Processor instance to the rollup plugin. It will then add any files found when traversing the modules to it and both the rollup-discovered and any already-existing files will be output in the final CSS.

# empties

Set to a falsey value to disable outputting of empty CSS files. This is most common when a file contains only @values and then a minifier is used which strips out the file heading comments. Defaults to true.

# Shared Options

All other options are passed to the underlying Processor instance, see the Processor Options.

# webpack

Webpack 2/3/4 support for modular-css.

This package contains two entry points, you will need to use both in tandem for things to work!

  1. @modular-css/webpack/plugin provides a webpack plugin you can use to transform imported .css files into lookup objects while outputting CSS to disk.

  2. @modular-css/webpack/loader provides the file loader that does the actual transformation on files.

# webpack Install

> npm i @modular-css/webpack

# Usage

// webpack.config.js
const path = require("path");
    
const CSSPlugin = require("@modular-css/webpack/plugin");

module.exports = {
    entry   : "./input.js",
    output  : {
        path : path.resolve(__dirname, "dist"),
        filename : "./output.js"
    },
    module : {
        rules : [{
            test : /\.css$/,
            use  : "@modular-css/webpack/loader"
        }]
    },
    plugins : [
        new CSSPlugin({
            css  : "./output.css",
            json : "./output.json"
        })
    ]
});

# webpack Plugin Options

# css

Location to write the generated CSS file to, relative to output.path just like output.filename

# json

Location to write out the JSON mapping file to, relative to output.path just like output.filename

# processor

Pass an already-instantiated Processor instance to the Webpack plugin. It will then add any files found when traversing the modules to it and both the Webpack-discovered and any already-existing files will be output in the final CSS.

# Shared Options

All other options are passed to the underlying Processor instance, see the Processor Options

# webpack Loader Options

# namedExports

By default this plugin will create both a default export and named exports for each class in a CSS file. You can disable this by setting namedExports to false.

...
    module : {
        rules : [{
            test : /\.css$/,
            use  : {
                loader  : "@modular-css/webpack/loader",
                options : {
                    namedExports : false
                }
            }
        }]
    },
...

# browserify

Browserify support for modular-css.

This plugin can be combined with the factor-bundle plugin to output a common CSS file as well as bundle-specific CSS files.

@modular-css/browserify will use the basedir passed to browserify as it’s cwd parameter.

# Install

$ npm i @modular-css/browserify

# Options

# css

Location to write the generated CSS file to.

# Shared Options

All other options are passed to the underlying Processor instance, see the Processor Options.

# CLI

$ browserify -p [ @modular-css/browserify --css "./style.css" ] entry.js

# API

var browserify = require("browserify"),
    build;

build = browserify("./entry.js");

build.plugin("@modular-css/browserify", {
    css : "./style.css",
});

# factor-bundle

@modular-css/browserify is fully factor-bundle aware and will output correctly-partitioned CSS bundles to match the JS bundles created by factor-bundle.

WARNING: Due to how factor-bundle works the @modular-css/browserify must be applied to the Browserify object before factor-bundle.

# CLI
$ browserify home.js account.js \
    -p [ @modular-css/browserify --css gen/common.css ] \
    -p [ factor-bundle -o gen/home.js -o gen/account.js ] \
    -o bundle/common.js
# API
var build = browserify([
        "./home.js",
        "./account.js"
    ]);

// NOTE modular-css applied before factor-bundle, it won't work otherwise!
build.plugin("@modular-css/browserify", {
    css : "./gen/common.css"
});

build.plugin("factor-bundle", {
    outputs : [
        "./gen/home.js",
        "./get/account.js"
    ]
});

# svelte

Svelte preprocessor support for modular-css. Process inline <style> or <link> references inside your Svelte components using the full power of modular-css while also providing compile-time optimizations for smaller bundles and even faster runtime performance!

# Example

Turns this

<div class="{css.main}">
    <h1 class="{css.title}">Title</h1>
</div>

<style>
    .main {
        /* ... */
    }
    
    .title {
        /* ... */
    }
</style>

into this by running it through modular-css and then statically replacing everything possible for zero-cost run-time styling.

<div class="abc123_main">
    <h1 class="abc123_title">Title</h1>
</div>

You could also use <link href="./file.css" /> tags to reference CSS external to the component, but the component must have only one <link> (links to URLs are fine and ignored) or <style>. Combining them is not supported.

# Install

> npm i @modular-css/svelte -D

# Usage

# svelte.preprocess()
const filename = "./Component.html";

const { processor, preprocess } = require("@modular-css/svelte")({
    // Processor options
});

const processed = await svelte.preprocess(
    fs.readFileSync(filename, "utf8"),
    Object.assign({}, preprocess, { filename })
);

const result = await processor.output();

fs.writeFileSync("./dist/bundle.css", result.css);
# @modular-css/rollup
# API
const rollup = require("rollup").rollup;

const { preprocess, processor } = require("@modular-css/svelte")({
    // Processor options
});

const bundle = await rollup({
    input   : "./Component.html",
    
    plugins : [
        require("rollup-plugin-svelte")({
            preprocess,
        }),

        require("@modular-css/rollup")({
            processor,

            common : "common.css",
        }),
    ]
});

// bundle.write will also write out the CSS to the path specified in the `css` arg
bundle.write({
    format : "es",
    file   : "./dist/bundle.js"
});
# rollup.config.js
const { preprocess, processor } = require("@modular-css/svelte")({
    // Processor options
});

module.exports = {
    input   : "./Component.html",
    
    output  : {
        format : "es",
        file   : "./dist/bundle.js"
    },

    plugins : [
        require("rollup-plugin-svelte")({
            preprocess,
        }),
        
        require("@modular-css/rollup")({
            processor,

            common : "common.css",
        }),
    ]
};

# Options

# strict

If true whenever a missing replacement is found like {css.doesnotexist} an error will be thrown aborting the file processing. Defaults to false.

# procesor

Pass a previously-created @modular-css/processor instance into the preprocessor. Will not pass through any other options to the processor if this is set, but strict will still be honored by the preprocessor.

# Shared Options

All options are passed to the underlying Processor instance, see the Processor Options.

# PostCSS

@modular-css/postcss provides a PostCSS plugin that can be used like any other. It will output a message with a type of modular-css-exports containing all the exported class compositions.

# PostCSS Install

> npm i @modular-css/postcss

# PostCSS Usage (via API)

const postcss = require("postcss");
const processor = postcss([
    require("@modular-css/postcss")({
        json : "./path/to/output.json"
    })
]);

const result = await processor.process("<css>")

// result.css
// result.map
// result.messages.find((msg) => msg.type === "modular-css-exports")
// etc

# PostCSS Usage (via config)

> postcss --config postcss.json input.css
{
    "output" : "out.css",
    
    "@modular-css/postcss": {
        "json" : "./path/to/output.json"
    }
}

# PostCSS Usage (via CLI)

> postcss --use modular-css/postcss input.css

# PostCSS Options

# json

Write the class composition data to this location on disk.

# Shared Options

All other options are passed to the underlying Processor instance, see Processor Options.

# CLI

CLI interface to modular-css.

# CLI Install

$ npm i @modular-css/cli

# CLI Usage

$ modular-css [options] <glob>...

Options
--dir,     -d <dir>    Directory to search from [process cwd]
--out,     -o <file>   File to write output CSS to [stdout]
--json,    -j <file>   File to write output compositions JSON to
--map,     -m          Include inline source map in output
--rewrite, -r          Control rewriting of url() references in CSS
--help                 Show this help

# JS API

The heart of modular-css, the JS API is a Processor that will be fed files, transform them, then spit out their bones…

Or, you know, their CSS. One of those for sure.

# Processor Usage

Instantiate a new Processor instance, call .file(<path>) or .string(<name>, <contents>) methods, and then use the returned Promise to get access to the results/output.

const Processor = require("modular-css");
const processor = new Processor({
        // See "API Options" for valid options to pass to the Processor constructor
    });

// Add entries, either from disk using .file() or as strings with .string()
Promise.all([
    processor.file("./entry.css").then((result) => {
        // result contains
        //  id      : Absolute path of the file that was added
        //  file    : Absolute path of hte file that was added
        //  files   : metadata about the file hierarchy,
        //  details : metadata aboutthe file that was added,
        //  exports : Scoped selector mappings for the file that was added
    }),
    processor.string("./fake-file.css", ".class { color: red; }")
])
.then(() => {
    // Once all files are added, use .output() to get at the rewritten CSS
    return processor.output();
})
.then((result) => {
    // result.css : Combined CSS output
    // result.map : Source map data if enabled
    // result.compositions - All files and their composed dependencies
});

# Processor Options

# rewrite

Enable or disable the usage of postcss-url to correct any URL references within the CSS. The value of rewrite will be passed to postcss-url to allow for configuration of the plugin.

Default: true

// On by default, so this will have rewritten url() references
new Processor();

// A falsey value will disable the usage of postcss-url,
// url() references will not be changed
new Processor({ rewrite : false });

// Configure postcss-url
new Processor({
    rewrite : {
        url : "inline"
    }
});
# map

Enable source map generation. Can also be passed to .output().

Default: false

// Inline source map
new Processor({
    map : true
});

// External source map
new Processor({
    map : {
        inline : false
    }
});
# cwd

Specify the current working directory for this Processor instance, used when resolving composes/@value rules that reference other files.

Default: process.cwd()

new Processor({
    cwd : path.join(process.cwd(), "/sub/dir")
})
# namer

Specify a function (that takes filename & selector as arguments) to produce scoped selectors.

Can also pass a string that will be require()'d and executed, it should return the namer function.

Default: Function that returns "mc" + unique-slug(<file>) + "_" + selector

new Processor({
    namer : function(file, selector) {
        return file.replace(/[:\/\\ .]/g, "") + "_" + selector;
    }
});

// or

new Processor({
    namer : "@modular-css/shortnames"
});
# resolvers

If you want to provide your own file resolution logic you can pass an array of resolver functions. Each resolver function receives three arguments:

Resolver functions should either return an absolute path or a falsey value. They must also be synchronous.

Default: See /src/lib/resolve.js for the default implementation.

new Processor({
    resolvers : [
        (src, file, resolve) => ...,
        require("@modular-css/path-resolver")(
            "./some/other/path"
        )
    ]
})
# exportGlobals

By default identifiers wrapped in :global(...) are exported for ease of referencing via JS. By setting exportGlobals to false that behavior can be disabled. Mostly useful to avoid warnings when global CSS properties are not valid JS identifiers.

new Processor({
    exportGlobals : false
});
/* exportGlobals: true */
.a {}
:global(.b) {}

/* Outputs
{
    "a" : "mc12345_a",
    "b" : "b"
}
*/

/* exportGlobals: false */
.a {}
:global(.b) {}

/* Outputs
{
    "a" : "mc12345_a"
}
*/

# Processor Properties

# .files

Returns an object keyed by absolute file paths of all known files in the Processor instance.

# .options

Returns the options object passed to the Processor augmented with the defaults.

# Processor API

# .string(file, css)

Returns a promise. Add file to the Processor instance with css contents.

# .file(file)

Returns a promise. Add file to the Processor instance, reads contents from disk using fs.

# .output([files])

Returns a promise. Finalize processing of all added CSS and create combined CSS output file. Optionally allows for combining a subset of the loaded files by passing a single file or array of files.

WARNING: Calling .output() before any preceeding .file(...)/.string(...) calls have resolved their returned promises will return a rejected promise. See usage for an example of correct usage.

# .remove([files])

Remove files from the Processor instance. Accepts a single file or array of files.

# .dependencies([file])

Returns an array of file paths. Accepts a single file argument to get the dependencies for, will return entire dependency graph in order if argument is omitted.

# glob API

A JS API for using glob patterns with modular-css.

# glob install

$ npm i @modular-css/glob

# glob usage

const glob = require("@modular-css/glob");

    // returns a filled-out Processor instance you can use
const processor = await glob({
    search : [
        "**/*.css"
    ]
})

# glob options

Array of glob patterns to pass to globule for searching.

# Shared Options

All other options are passed to the underlying Processor instance, see the Processor Options.

# Extending

There are 4 built-in ways to extend the functionality of modular-css, the lifecycle hooks. They all can be used to add any number of PostCSS Plugins to modular-css at specific points in the processing cycle.

# before hook

The before hook is run before a CSS file is ever processed by modular-css, so it provides access to rewrite files if they aren’t actually CSS or contain non-standard syntax. Plugins like postcss-nested go well here.

# before usage

Specify an array of PostCSS plugins to be run against each file before it is processed. Plugin will be passed a from option.

new Processor({
    before : [ require("postcss-import") ]
});

# processing hook

The processing hook is run after modular-css has parsed the file, but before any response to processor.string or processor.file is returned. Plugins in this hook have a special power: they can change the exports of the file.

This works by having the plugin push an object onto the result.messages array. Here’s a very simple example:

new Processor({
    processing : [
        (css, result) => {
            result.messages.push({
                plugin  : "modular-css-exporter",
                exports : {
                    a : true,
                    b : false
                }
            });
        }
    ]
})

The plugin field must begin with “modular-css-export”, and the exports field should be the object to be mixed into the exports of the CSS file. It will be added last, so it can be used to override the default exports if desired.

# processing usage

Specify an array of PostCSS plugins to be run against each file during processing. Plugin will be passed a from option.

new Processor({
    processing : [ require("postcss-import") ]
});

# after hook

The after hook is run once the output location for the CSS is known, but before all the files are combined. By default it will run postcss-url to rebase file references based on the final output location, but this can be disabled using the rewrite option.

Since all manipulations on the file are complete at this point it is a good place to run plugins like postcss-import to inline @import rules. The rules inlined in this way won’t be scoped so it’s a convenient way to pull in 3rd party code which can be included in the selector heirarchy via composes.

@import "bootstrap.css";

/* Will export as "btn .abc123_button" */
.button {
    composes: global(btn);
}

# after usage

Specify an array of PostCSS plugins to be run after files are processed, but before they are combined. Plugin will be passed a to and from option.

Default: []

:warning: postcss-url automatically runs after any plugins defined in the after hook. To disable it use the rewrite option.

new Processor({
    after : [ require("postcss-someplugin") ]
});

# done hook

The done hook is run after all of the constituent files are combined into a single stylesheet. This makes it a good place to add tools like cssnano that need access to the entire stylesheet to be able to accurately optimize the CSS.

# done usage

Specify an array of PostCSS plugins to be run against the complete combined CSS. Plugin will be passed a to option.

new Processor({
    done : [ require("cssnano")()]
});

# vs CSS Modules

While modular-css was originallly built directly off the CSS Modules spec, during the course of development some decisions have been made that have broken 100% compatibility. In general these changes have been made in an attempt to try and add consistency or pave cowpaths. There have also been a few feature additions that enable solving new classes of problems or fix pain points in the spec.

# :global

In CSS Modules the :global pseudo-class allows usage with or without parentheses around its arguments.

/* CSS Modules */
:global .one { ... }
:global(.one) { ... }

In modular-css only the parenthesized form is allowed to reduce ambiguity around which selectors/names are being made global.

/* modular-css */
:global(.one) { ... }

# Scope

In CSS Modules it is possible to switch back and forth from local and global scope using :global and :local.

/* CSS Modules */
.localA :global .global-b .global-c :local(.localD.localE) .global-d { ... }

In modular-css all CSS is local by default, and since :global() is required to use parentheses there is no need for the :local pseudo.

/* modular-css */
.localA :global(.global-b .global-c) .localD.localE :global(.global-d) { ... }

# Style overriding

In CSS Modules there is no support for modifying styles in rules that aren’t direct ancestors via composes.

Discussion around potential solutions was happening in css-modules/css-modules#147 but eventually died out.

modular-css has implemented the :external() proposal from that issue. More context is available in the Overriding Styles section.

/* input.css */
.input {
    width: 100%;
}

/* fieldset.css */
.fieldset :external(input from "./input.css") {
    width: 50%;
}

# Namespaces

CSS Modules allows for importing multiple values from an external file.

/* CSS Modules */
@value one, two, three, red, blue, green, small, medium, large from "./constants.css";

.a {
  color: red;
  width: small;
}

modular-css implements a suggestion made in css-modules/css-modules#186 to allow importing all of a file’s exported values and aliasing it for easy use. More documentation can be found in the Namespaced values section.

/* modular-css */
@value * as constants from "./constants.css";

.a {
  color: constants.red;
  width: constants.small;
}

# Support

CSS Modules is really only fully-supported as part of webpack these days. The repo is effectively mothballed, and there appears to be almost no one supporting the current codebase. Since webpack is the only live version & we prefer to use rollup for it’s cleaner output and smaller bundles we needed an approach that could be flexibly be used with a variety of tools.