-
Implement
composes
from CSS modules (#20)This release implements the
composes
annotation from the CSS modules specification. It provides a way for class selectors to reference other class selectors (assuming you are using thelocal-css
loader). And with thefrom
syntax, this can even work with local names across CSS files. For example:// app.js import { submit } from './style.css' const div = document.createElement('div') div.className = submit document.body.appendChild(div)
/* style.css */ .button { composes: pulse from "anim.css"; display: inline-block; } .submit { composes: button; font-weight: bold; }
/* anim.css */ @keyframes pulse { from, to { opacity: 1 } 50% { opacity: 0.5 } } .pulse { animation: 2s ease-in-out infinite pulse; }
Bundling this with esbuild using
--bundle --outdir=dist --loader:.css=local-css
now gives the following:(() => { // style.css var submit = "anim_pulse style_button style_submit"; // app.js var div = document.createElement("div"); div.className = submit; document.body.appendChild(div); })();
/* anim.css */ @keyframes anim_pulse { from, to { opacity: 1; } 50% { opacity: 0.5; } } .anim_pulse { animation: 2s ease-in-out infinite anim_pulse; } /* style.css */ .style_button { display: inline-block; } .style_submit { font-weight: bold; }
Import paths in the
composes: ... from
syntax are resolved using the newcomposes-from
import kind, which can be intercepted by plugins during import path resolution when bundling is enabled.Note that the order in which composed CSS classes from separate files appear in the bundled output file is deliberately undefined by design (see the specification for details). You are not supposed to declare the same CSS property in two separate class selectors and then compose them together. You are only supposed to compose CSS class selectors that declare non-overlapping CSS properties.
Issue #20 (the issue tracking CSS modules) is esbuild's most-upvoted issue! With this change, I now consider esbuild's implementation of CSS modules to be complete. There are still improvements to make and there may also be bugs with the current implementation, but these can be tracked in separate issues.
-
Fix non-determinism with
tsconfig.json
and symlinks (#3284)This release fixes an issue that could cause esbuild to sometimes emit incorrect build output in cases where a file under the effect of
tsconfig.json
is inconsistently referenced through a symlink. It can happen when usingnpm link
to create a symlink withinnode_modules
to an unpublished package. The build result was non-deterministic because esbuild runs module resolution in parallel and the result of thetsconfig.json
lookup depended on whether the import through the symlink or not through the symlink was resolved first. This problem was fixed by moving therealpath
operation before thetsconfig.json
lookup. -
Add a
hash
property to output files (#3084, #3293)As a convenience, every output file in esbuild's API now includes a
hash
property that is a hash of thecontents
field. This is the hash that's used internally by esbuild to detect changes between builds for esbuild's live-reload feature. You may also use it to detect changes between your own builds if its properties are sufficient for your use case.This feature has been added directly to output file objects since it's just a hash of the
contents
field, so it makes conceptual sense to store it in the same location. Another benefit of putting it there instead of including it as a part of the watch mode API is that it can be used without watch mode enabled. You can use it to compare the output of two independent builds that were done at different times.The hash algorithm (currently XXH64) is implementation-dependent and may be changed at any time in between esbuild versions. If you don't like esbuild's choice of hash algorithm then you are welcome to hash the contents yourself instead. As with any hash algorithm, note that while two different hashes mean that the contents are different, two equal hashes do not necessarily mean that the contents are equal. You may still want to compare the contents in addition to the hashes to detect with certainty when output files have been changed.
-
Avoid generating duplicate prefixed declarations in CSS (#3292)
There was a request for esbuild's CSS prefixer to avoid generating a prefixed declaration if a declaration by that name is already present in the same rule block. So with this release, esbuild will now avoid doing this:
/* Original code */ body { backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(45px); } /* Old output (with --target=safari12) */ body { -webkit-backdrop-filter: blur(30px); backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(45px); } /* New output (with --target=safari12) */ body { backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(45px); }
This can result in a visual difference in certain cases (for example if the browser understands
blur(30px)
but notblur(45px)
, it will be able to fall back toblur(30px)
). But this change means esbuild now matches the behavior of Autoprefixer which is probably a good representation of how people expect this feature to work.