modular-css v28.2.0

modular-css API

You can use modular-css a variety of ways. The easiest is with one of the bundler integrations, which exist for most popular bundlers in use today.

#Bundlers

#rollup

Rollup support for modular-css is provided by the @modular-css/rollup package.

#Install

> npm i @modular-css/rollup --save-dev

#Usage

#API
const bundle = await rollup({ input : "./index.js", plugins : [ require("@modular-css/rollup")() ] });
#rollup.config.js
import css from "@modular-css/rollup"; export default { input : "./index.js", output : { dest : "./gen/bundle.js", format : "umd" }, plugins : [ css() ] };

#Options

#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 incorrect 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.

#vite

Vite support for modular-css is provided by @modular-css/vite.

NOTE: To work around vite's built-in CSS handling you will need to use a file extension other than .css for modular-css files. The default for this plugin is .mcss, but you can use include to change that.

#Install

> npm i @modular-css/vite --save-dev

#Usage

// vite.config.js import mcss from "@modular-css/vite"; export default { plugins : [ mcss() ] };

#Options

#include/exclude

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

#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.

#Shared Options

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

#sveltekit

SvelteKit support for modular-css is provided by @modular-css/vite (since SvelteKit is powered by vite).

NOTE: To work around vite's built-in CSS handling you will need to use a file extension other than .css for modular-css files. The default for this plugin is .mcss, but you can use include to change that.

#Install

> npm i @modular-css/vite --save-dev

#Usage

// svelte.config.js import adapter from '@sveltejs/adapter-auto'; import mcss from "@modular-css/vite"; /** @type {import('@sveltejs/kit').Config} */ const config = { kit : { adapter : adapter(), // Add mcss vite plugin to the underlying vite instance vite : { plugins : [ mcss(), ] }, }, }; export default config;

If you'd like to use the modular-css svelte preprocessor with SvelteKit (and you definitely should because it'll make your site faster) you can use the instructions for @modular-css/svelte with vite.

#Options

See the vite options.

#webpack

Webpack 2/3/4 support for modular-css is provided by @modular-css/webpack.

NOTE: webpack@5 is untested but may be functional. If you try it out report back in Discord!

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.

#Install

> npm i @modular-css/webpack --save-dev

#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" }) ] });

#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..

#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 is provided by @modular-css/browserify.

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 --save-dev

#Usage

#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" ] });

#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.

#Direct Usage

#JS API

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

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

#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.file(<path>)

Pass a file path to the processor instance for processing. Returns a promise that resolves to details about the file that was added.

#processor.string(<name>, <css>)

Pass a name and css string to the processor instance for processing. Returns a promise that resolves to details about the file that was added.

#processor.root(<name>, Root)

Add a file to the Processor instance using an already-parsed Postcss Root object to avoid unnecessary reparsing. Returns a promise that resolves to details about the file that was added.

#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 evaluated, 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:

  • src, the file that included file
  • file, the file path being included by src
  • resolve, the default resolver function

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

Default: See resolve.js for the default implementation.

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

Specify a function that will be responsible for taking a file path and returning the text contents of the file. The function can be synchronous, return a promise, or use async/await.

The default implementation uses fs to read files off the local filesystem.

new Processor({ async loadFile(id) { const text = await goGetItFromSomewhere(id); return text; }, });
#exportGlobals

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

new Processor({ exportGlobals : false });
Open in the REPL ▶️
/* exportGlobals: true */ .a {} :global(.b) {} /* Outputs { "a" : "mc12345_a", "b" : "b" } */ /* exportGlobals: false */ .a {} :global(.b) {} /* Outputs { "a" : "mc12345_a" } */
#dupewarn

Boolean value that determines whether or not the Processor instance will issue warnings for duplicate seeming files (identical path with only case variations). If you're using a case-sensitive filesystem feel free to disable by setting it to false.

Default: true, so warnings are emitted.

new Processor({ dupewarn : true });

#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.

#.fileDependencies([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.

#.invalidate(file)

Marks a file as stale, if that file is re-added either directly or as a dependency it will be reloaded from disk instead of the Processor cache.

#.has(file)

Checks if the Processor instance knows about a file.

#.normalize(file)

Uses the built-in path normalization settings to normalize the case of a file path.

#.resolve(src, file)

Resolves file from src, using any of the specified resolvers.

#globbing API

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

#Install

$ npm i @modular-css/glob

#Usage

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

#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.

#CLI

CLI interface to modular-css.

#Install

$ npm i @modular-css/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

#Other Tools

#svelte preprocessor

Svelte preprocessor support for modular-css.

Process inline <style type="text/m-css"> or <link> or import styles from "./foo.css"; references inside your Svelte components using the full power of modular-css. Dynamic references will be replaced where possible with static ones, allowing for greater compile-time optimizations, smaller bundles, and even faster runtime performance.

#Example

#<style> processing

Turns this

<div class="{css.main}"> <h1 class="{css.title}">Title</h1> </div> <!-- type attribute is **required** --> <style type="text/m-css"> .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 can 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.

<link href="./style.css" /> <div class="{css.main}"> <h1 class="{css.title}">Title</h1> </div>
#import processing

It'll even check your ES Module import statements and use those.

<div class="{styles.main}"> <h1 class="{styles.title}">Title</h1> </div> <script> // Only default exports are supported, but it will use the name you // give it instead of the hardcoded css like the other approaches import styles from "./style.css"; </script>

#Install

> npm i @modular-css/svelte -D

#Usage

You can use the svelte preprocessor in almost every environment where you're using svelte. Here are a few examples of common usage to help get you started.

#via svelte.preprocess()
const filename = "./Component.svelte"; const { processor, preprocess } = require("@modular-css/svelte")({ // Processor options // ... }); const processed = await svelte.preprocess( fs.readFileSync(filename, "utf8"), { ...preprocess, filename }, ); const result = await processor.output(); fs.writeFileSync("./dist/bundle.css", result.css);
#via rollup
// rollup.config.js const { preprocess, processor } = require("@modular-css/svelte")({ // Processor options // ... }); module.exports = { input : "./Component.svelte", output : { format : "es", file : "./dist/bundle.js" }, plugins : [ require("rollup-plugin-svelte")({ preprocess, }), require("@modular-css/rollup")({ processor, }), ] };
#via vite
// vite.config.js import { defineConfig } from "vite"; import preprocessor from "@modular-css/svelte"; import mcssVite from "@modular-css/vite"; // Set up the svelte preprocessor and get a reference to the // mcss processor so we can pass it into the vite plugin const { preprocess, processor } = preprocessor({ // Default is .css but we need .mcss because of vite include : /.mcss$/i, // Other processor options // ... }); export default defineConfig({ plugins : [ mcssVite({ processor, }), ], });

#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.

#Install

> npm i @modular-css/postcss

#Usage

#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
#config
> postcss --config postcss.json input.css
{ "output" : "out.css", "@modular-css/postcss": { "json" : "./path/to/output.json" } }
#CLI
> postcss --use @modular-css/postcss input.css

#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.

#Other Utilities

#Path Aliasing

A resolver for modular-css that will let you resolve file references against named aliases. Useful to avoid code like

@value foo from "../../../../../../../../some/other/directory/file.css";

which is annoying to write, annoying to read, and also super-brittle.

#Install

$ npm i @modular-css/path-aliases --save-dev

#Usage

Pass as part of the resolvers array in the modular-css options (via JS API/Rollup/Browserify/WebPack/etc). When modular-css is trying to resolve @value or composes file references it'll replace the alias keys with their path value for file lookups.

import Processor from "@modular-css/processor"; import aliases from "@modular-css/path-aliases"; const processor = new Processor({ resolvers : [ aliases({ aliases : { one : "./path/one", path : "../../some/other/path" } }) ] });

which allows you to write CSS like this.

Open in the REPL ▶️
@value one from "one/one.css"; .a { composes: path from "path/path.css"; }

#Options

#aliases

A object consisting of key/value pairs of alias names to file paths. Paths can be relative to the cwd of the Processor instance or absolute paths.

#Namer

Tiny classnames for modular-css production builds!

#Install

> npm install @modular-css/shortnames

#Usage

#JS API
const Processor = require("@modular-css/processor"); const processor = new Processor({ namer : require("@modular-css/shortnames")() }); // ...
#Browserify
build.plugin("@modular-css/browserify", { css : "./style.css", namer : require("@modular-css/shortnames")() }); // ...
#Rollup
rollup({ entry : "./index.js", plugins : [ require("@modular-css/rollup")({ css : "./gen/index.css", namer : require("@modular-css/shortnames")() }) ] });

#Example output

Open in the REPL ▶️
/* one.css */ .alert {} .notification {} /* two.css */ .title {} .heading .subheading {}

becomes

Open in the REPL ▶️
/* output.css */ .AA {} .AB {} .BA {} .BB .BC {}

#Path resolver

A resolver for modular-css that will let you resolve file references against arbitrary paths. Useful to avoid code like

@value foo from "../../../../../../../../some/other/directory/file.css";

which is annoying to write, annoying to read, and also super-brittle.

#Install

$ npm i @modular-css/path-resolver

#Usage

Pass as part of the resolvers array in the modular-css options (via JS API/Rollup/Browserify/WebPack/etc). When modular-css is trying to resolve @value or composes file references it'll use the default node resolution algorithm against whichever paths you specified.

const Processor = require("@modular-css/processor"); const paths = require("@modular-css/path-resolver"); const processor = new Processor({ resolvers : [ paths({ paths : [ "./path/one", "../../some/other/path" ] }) ] });

#Options

#paths

An array of string file paths, they can be relative to the cwd of the Processor instance or absolute paths.

#Lifecycle Hooks

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

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.

#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

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.

#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

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.

Open in the REPL ▶️
@import "bootstrap.css"; /* Will export as "btn .abc123_button" */ .button { composes: global(btn); }

#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: []

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

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.

#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")()] });