browserslist/lib.rs
1//! **oxc-browserslist** is a Rust-based implementation of [Browserslist](https://github.com/browserslist/browserslist).
2//!
3//! ## Introduction
4//!
5//! This library bundles Can I Use data, Electron versions list and Node.js releases list,
6//! so it won't and doesn't need to access any data files.
7//!
8//! Except several non-widely/non-frequently used features,
9//! this library works as same as the JavaScript-based
10//! implementation [Browserslist](https://github.com/browserslist/browserslist).
11//!
12//! ## Usage
13//!
14//! It provides a simple API for querying which accepts a sequence of strings and options [`Opts`],
15//! then returns the result.
16//!
17//! ```
18//! use browserslist::{Distrib, Opts, resolve, Error};
19//!
20//! let distribs = resolve(&["ie <= 6"], &Opts::default()).unwrap();
21//! assert_eq!(distribs[0].name(), "ie");
22//! assert_eq!(distribs[0].version(), "6");
23//! assert_eq!(distribs[1].name(), "ie");
24//! assert_eq!(distribs[1].version(), "5.5");
25//!
26//! assert_eq!(
27//! resolve(&["yuru 1.0"], &Opts::default()),
28//! Err(Error::BrowserNotFound(String::from("yuru")))
29//! );
30//! ```
31//!
32//! The result isn't a list of strings, instead, it's a tuple struct called [`Distrib`].
33//! If you need to retrieve something like JavaScript-based implementation of
34//! [Browserslist](https://github.com/browserslist/browserslist),
35//! you can convert them to strings:
36//!
37//! ```
38//! use browserslist::{Distrib, Opts, resolve, Error};
39//!
40//! let distribs = resolve(&["ie <= 6"], &Opts::default()).unwrap();
41//! assert_eq!(
42//! distribs.into_iter().map(|d| d.to_string()).collect::<Vec<_>>(),
43//! vec![String::from("ie 6"), String::from("ie 5.5")]
44//! );
45//! ```
46//!
47//! ## WebAssembly
48//!
49//! This crate can be compiled as WebAssembly, without configuring any features manually.
50//!
51//! Please note that browser and Deno can run WebAssembly,
52//! but those environments aren't Node.js,
53//! so you will receive an error when querying `current node` in those environments.
54
55pub use error::Error;
56pub use opts::Opts;
57use parser::parse_browserslist_query;
58pub use queries::Distrib;
59pub use semver::Version;
60#[cfg(all(feature = "wasm_bindgen", target_arch = "wasm32"))]
61pub use wasm::browserslist;
62
63#[cfg(not(target_arch = "wasm32"))]
64mod config;
65mod data;
66mod error;
67mod generated;
68mod opts;
69mod parser;
70mod queries;
71mod semver;
72#[cfg(test)]
73mod test;
74#[cfg(all(feature = "wasm_bindgen", target_arch = "wasm32"))]
75mod wasm;
76
77/// Resolve browserslist queries.
78///
79/// This is a low-level API.
80/// If you want to load queries from configuration file and
81/// resolve them automatically,
82/// use the higher-level API [`execute`] instead.
83///
84/// ```
85/// use browserslist::{Distrib, Opts, resolve};
86///
87/// let distribs = resolve(&["ie <= 6"], &Opts::default()).unwrap();
88/// assert_eq!(distribs[0].name(), "ie");
89/// assert_eq!(distribs[0].version(), "6");
90/// assert_eq!(distribs[1].name(), "ie");
91/// assert_eq!(distribs[1].version(), "5.5");
92/// ```
93pub fn resolve<S>(queries: &[S], opts: &Opts) -> Result<Vec<Distrib>, Error>
94where
95 S: AsRef<str>,
96{
97 if queries.len() == 1 {
98 _resolve(queries[0].as_ref(), opts)
99 } else {
100 let s = &queries.iter().map(|q| q.as_ref()).collect::<Vec<_>>().join(", ");
101 _resolve(s, opts)
102 }
103}
104
105// reduce generic monomorphization
106fn _resolve(query: &str, opts: &Opts) -> Result<Vec<Distrib>, Error> {
107 let queries = parse_browserslist_query(query)?;
108 let mut distribs = vec![];
109 for (i, current) in queries.1.into_iter().enumerate() {
110 if i == 0 && current.negated {
111 return Err(Error::NotAtFirst(current.raw.to_string()));
112 }
113
114 let mut dist = queries::query(current.atom, opts)?;
115 if current.negated {
116 distribs.retain(|distrib| !dist.contains(distrib));
117 } else if current.is_and {
118 distribs.retain(|distrib| dist.contains(distrib));
119 } else {
120 distribs.append(&mut dist);
121 }
122 }
123
124 distribs.sort_by_cached_key(|d| {
125 let version = d.version().parse::<semver::Version>().unwrap_or_default();
126 (d.name(), std::cmp::Reverse(version))
127 });
128 distribs.dedup();
129
130 Ok(distribs)
131}
132
133#[cfg(not(target_arch = "wasm32"))]
134/// Load queries from configuration with environment information,
135/// then resolve those queries.
136///
137/// If you want to resolve custom queries (not from configuration file),
138/// use the lower-level API [`resolve`] instead.
139///
140/// ```
141/// use browserslist::{Opts, execute};
142///
143/// // when no config found, it use `defaults` query
144/// assert!(!execute(&Opts::default()).unwrap().is_empty());
145/// ```
146pub fn execute(opts: &Opts) -> Result<Vec<Distrib>, Error> {
147 resolve(&config::load(opts)?, opts)
148}