Bundling
Lightning CSS supports bundling dependencies referenced by CSS @import
rules into a single output file. When calling the Lightning CSS API, use the bundle
or bundleAsync
function instead of transform
. When using the CLI, enable the --bundle
flag.
This API requires filesystem access, so it does not accept code
directly via the API. Instead, the filename
option is used to read the entry file directly.
import { bundle } from 'lightningcss';
let { code, map } = bundle({
filename: 'style.css',
minify: true
});
Dependencies
CSS files can contain dependencies referenced by @import
syntax, as well as references to classes in other files via CSS modules.
@import
The @import
at-rule can be used to inline another CSS file into the same CSS bundle as the containing file. This means that at runtime a separate network request will not be needed to load the dependency. Referenced files should be relative to the containing CSS file.
@import 'other.css';
@import
rules must appear before all other rules in a stylesheet except @charset
and @layer
statement rules. Later import rules will cause an error to be emitted.
CSS modules
Dependencies are also bundled when referencing another file via CSS modules composition or external variables. See the linked CSS modules documentation for more details.
Conditional imports
The @import
rule can be conditional by appending a media query or supports()
query. Lightning CSS will preserve this behavior by wrapping the inlined rules in @media
and @supports
rules as needed.
/* a.css */
@import "b.css" print;
@import "c.css" supports(display: grid);
.a { color: red }
/* b.css */
.b { color: green }
/* c.css */
.c { display: grid }
compiles to:
@media print {
.b { color: green }
}
@supports (display: grid) {
.c { display: grid }
}
.a { color: red }
Note: There are currently two cases where combining conditional rules is unsupported:
- Importing the same CSS file with only a media query, and again with only a supports query. This would require duplicating all rules in the file.
- Importing a file with a negated media type (e.g.
not print
) within another file with a negated media type.
Cascade layers
Imported CSS rules can also be placed into a CSS cascade layer, allowing you to control the order they apply. Nested imports will be placed into nested layers.
/* a.css */
@import "b.css" layer(foo);
.a { color: red }
/* b.css */
@import "c.css" layer(bar);
.b { color: green }
/* c.css */
.c { color: green }
compiles to:
@layer foo.bar {
.c { color: green }
}
@layer foo {
.b { color: green }
}
.a { color: red }
Note: There are two unsupported layer combinations that will currently emit a compiler error:
- Importing the same CSS file with different layer names. This would require duplicating all imported rules multiple times.
- Nested anonymous layers.
Bundling order
When @import
rules are processed in browsers, if the same file appears more than once, the last instance applies. This is the opposite from behavior in other languages like JavaScript. Lightning CSS follows this behavior when bundling so that the output behaves the same as if it were not bundled.
/* index.css */
@import "a.css";
@import "b.css";
@import "a.css";
/* a.css */
body { background: green }
/* b.css */
body { background: red }
compiles to:
body { background: green }
Custom resolvers
The bundleAsync
API is an asynchronous version of bundle
, which also accepts a custom resolver
object. This allows you to provide custom JavaScript functions for resolving @import
specifiers to file paths, and reading files from the file system (or another source). The read
and resolve
functions are both optional, and may either return a string synchronously, or a Promise for asynchronous resolution.
import { bundleAsync } from 'lightningcss';
let { code, map } = await bundleAsync({
filename: 'style.css',
minify: true,
resolver: {
read(filePath) {
return fs.readFileSync(filePath, 'utf8');
},
resolve(specifier, from) {
return path.resolve(path.dirname(from), specifier);
}
}
});
Note that using a custom resolver can slow down bundling significantly, especially when reading files asynchronously. Use readFileSync
rather than readFile
if possible for better performance, or omit either of the methods if you don't need to override the default behavior.