Skip to content

Commit e59558b

Browse files
feat(cli): Load browserslist configuration if no --targets supplied. (#324)
1 parent 6dc3ca0 commit e59558b

File tree

4 files changed

+385
-8
lines changed

4 files changed

+385
-8
lines changed

README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ An extremely fast CSS parser, transformer, and minifier written in Rust. Use it
2323
- Removing default property sub-values which will be inferred by browsers.
2424
- Many micro-optimizations, e.g. converting to shorter units, removing unnecessary quotation marks, etc.
2525
- **Vendor prefixing** – Lightning CSS accepts a list of browser targets, and automatically adds (and removes) vendor prefixes.
26+
- **Browserslist configuration** – Lightning CSS supports opt-in browserslist configuration discovery to resolve browser targets and integrate with your existing tools and config setup.
2627
- **Syntax lowering** – Lightning CSS parses modern CSS syntax, and generates more compatible output where needed, based on browser targets.
2728
- CSS Nesting (draft spec)
2829
- Custom media queries (draft spec)
@@ -197,6 +198,46 @@ To see all of the available options, use the `--help` argument:
197198
npx lightningcss --help
198199
```
199200

201+
#### Browserslist configuration
202+
203+
If the `--browserslist` option is provided, then `lightningcss` finds browserslist configuration,
204+
selects queries by environment and loads the resulting queries as targets.
205+
206+
Configuration discovery and targets resolution is modeled after the original `browserslist` nodeJS package.
207+
The configuration is resolved in the following order:
208+
209+
- If a `BROWSERSLIST` environment variable is present, then load targets from its value. This is analog to the `--targets` CLI option.
210+
_Example:_ `BROWSERSLIST="firefox ESR" lightningcss [OPTIONS] <INPUT_FILE>`
211+
- If a `BROWSERSLIST_CONFIG` environment variable is present, then resolve the file at the provided path.
212+
Then parse and use targets from `package.json` or any browserslist configuration file pointed to by the environment variable.
213+
_Example:_ `BROWSERSLIST_CONFIG="../config/browserslist" lightningcss [OPTIONS] <INPUT_FILE>`
214+
- If none of the above apply, then find, parse and use targets from the first `browserslist`, `.browserslistrc`
215+
or `package.json` configuration file in any parent directory.
216+
217+
Browserslist configuration files may contain sections denoted by angular brackets `[]`.
218+
Use these to specify different targets for different environments.
219+
Targets which are not placed in a section are added to `defaults` and used if no section applies matches the environment.
220+
221+
_Example:_
222+
223+
```
224+
# Defaults, applied when no other section matches the provided environment.
225+
firefox ESR
226+
227+
[staging]
228+
# Targets applied only to the staging environment.
229+
samsung >= 4
230+
```
231+
232+
When using parsed configuration from `browserslist`, `.browserslistrc` or `package.json` configuration files,
233+
the environment determined by
234+
235+
- the `BROWSERSLIST_ENV` environment variable if present,
236+
- otherwise the `NODE_ENV` environment variable if present,
237+
- otherwise `production` is used.
238+
239+
If no targets are found for the resulting environment, then the `defaults` configuration section is used.
240+
200241
### Error recovery
201242

202243
By default, Lightning CSS is strict, and will error when parsing an invalid rule or declaration. However, sometimes you may encounter a third party library that you can't easily modify, which unintentionally contains invalid syntax, or IE-specific hacks. In these cases, you can enable the `errorRecovery` option (or `--error-recovery` CLI flag). This will skip over invalid rules and declarations, omitting them in the output, and producing a warning instead of an error. You should also open an issue or PR to fix the issue in the library if possible.
@@ -207,7 +248,7 @@ By default, Lightning CSS is strict, and will error when parsing an invalid rule
207248
<img width="680" alt="performance and build size charts" src="https://user-images.githubusercontent.com/19409/189022693-6956b044-422b-4f56-9628-d59c6f791095.png#gh-dark-mode-only">
208249

209250
```
210-
$ node bench.js bootstrap-4.css
251+
$ node bench.js bootstrap-4.css
211252
cssnano: 544.809ms
212253
159636 bytes
213254
@@ -229,7 +270,7 @@ lightningcss: 1.973ms
229270
23666 bytes
230271
231272
232-
$ node bench.js tailwind.css
273+
$ node bench.js tailwind.css
233274
cssnano: 2.198s
234275
1925626 bytes
235276

src/main.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clap::Parser;
1+
use clap::{ArgGroup, Parser};
22
use lightningcss::bundler::{Bundler, FileProvider};
33
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet};
44
use lightningcss::targets::Browsers;
@@ -13,6 +13,10 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
1313

1414
#[derive(Parser, Debug)]
1515
#[clap(author, version, about, long_about = None)]
16+
#[clap(group(
17+
ArgGroup::new("targets-resolution")
18+
.args(&["targets", "browserslist"]),
19+
))]
1620
struct CliArgs {
1721
/// Target CSS file
1822
#[clap(value_parser)]
@@ -46,6 +50,8 @@ struct CliArgs {
4650
#[clap(short, long, value_parser)]
4751
targets: Vec<String>,
4852
#[clap(long, value_parser)]
53+
browserslist: bool,
54+
#[clap(long, value_parser)]
4955
error_recovery: bool,
5056
}
5157

@@ -124,11 +130,14 @@ pub fn main() -> Result<(), std::io::Error> {
124130
StyleSheet::parse(&source, options).unwrap()
125131
};
126132

127-
let targets = if cli_args.targets.is_empty() {
128-
None
129-
} else {
133+
let targets = if !cli_args.targets.is_empty() {
130134
Browsers::from_browserslist(cli_args.targets).unwrap()
135+
} else if cli_args.browserslist {
136+
Browsers::load_browserslist().unwrap()
137+
} else {
138+
None
131139
};
140+
132141
stylesheet
133142
.minify(MinifyOptions {
134143
targets,

src/targets.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,40 @@ impl Browsers {
4242
) -> Result<Option<Browsers>, browserslist::Error> {
4343
use browserslist::{resolve, Opts};
4444

45-
let res = resolve(query, &Opts::new())?;
45+
Self::from_distribs(resolve(query, &Opts::new())?)
46+
}
47+
48+
/// Finds browserslist configuration, selects queries by environment and loads the resulting queries into LightningCSS targets.
49+
///
50+
/// Configuration resolution is modeled after the original `browserslist` nodeJS package.
51+
/// The configuration is resolved in the following order:
52+
///
53+
/// - If a `BROWSERSLIST` environment variable is present, then load targets from its value. This is analog to the `--targets` CLI option.
54+
/// Example: `BROWSERSLIST="firefox ESR" lightningcss [OPTIONS] <INPUT_FILE>`
55+
/// - If a `BROWSERSLIST_CONFIG` environment variable is present, then resolve the file at the provided path.
56+
/// Then parse and use targets from `package.json` or any browserslist configuration file pointed to by the environment variable.
57+
/// Example: `BROWSERSLIST_CONFIG="../config/browserslist" lightningcss [OPTIONS] <INPUT_FILE>`
58+
/// - If none of the above apply, then find, parse and use targets from the first `browserslist`, `.browserslistrc`
59+
/// or `package.json` configuration file in any parent directory.
60+
///
61+
/// When using parsed configuration from `browserslist`, `.browserslistrc` or `package.json` configuration files,
62+
/// the environment determined by:
63+
///
64+
/// - the `BROWSERSLIST_ENV` environment variable if present,
65+
/// - otherwise the `NODE_ENV` environment varialbe if present,
66+
/// - otherwise `production` is used.
67+
///
68+
/// If no targets are found for the resulting environment, then the `defaults` configuration section is used.
69+
pub fn load_browserslist() -> Result<Option<Browsers>, browserslist::Error> {
70+
use browserslist::{execute, Opts};
71+
72+
Self::from_distribs(execute(&Opts::new())?)
73+
}
4674

75+
fn from_distribs(distribs: Vec<browserslist::Distrib>) -> Result<Option<Browsers>, browserslist::Error> {
4776
let mut browsers = Browsers::default();
4877
let mut has_any = false;
49-
for distrib in res {
78+
for distrib in distribs {
5079
macro_rules! browser {
5180
($browser: ident) => {{
5281
if let Some(v) = parse_version(distrib.version()) {

0 commit comments

Comments
 (0)