browserslist 4.3.6 → 4.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Change Log
2
2
  This project adheres to [Semantic Versioning](http://semver.org/).
3
3
 
4
+ ## 4.4.2
5
+ * Allow to have string in `package.json` (by @dmarkhas).
6
+
7
+ ## 4.4.1
8
+ * Allow to use `.` in scope name of shareable config (by Gustav Nikolaj).
9
+
10
+ ## 4.4
11
+ * Added `and` and `or` keywords to combine queries (by Jon Ege Ronnenberg).
12
+
13
+ ## 4.3.7
14
+ * Fix fraction years support in `last 1.5 years` (by Clément P).
15
+ * Fix version-less browser support.
16
+
4
17
  ## 4.3.6
5
18
  * Fix version-less browser support in custom statistics (by Alex Walter).
6
19
 
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Browserslist [![Cult Of Martians][cult-img]][cult]
2
2
 
3
3
  <img width="120" height="120" alt="Browserslist logo by Anton Lovchikov"
4
- src="http://browserslist.github.io/browserslist/logo.svg" align="right">
4
+ src="https://browserslist.github.io/browserslist/logo.svg" align="right">
5
5
 
6
6
  The config to share target browsers and Node.js versions between different
7
7
  front-end tools. It is used in:
@@ -12,6 +12,7 @@ front-end tools. It is used in:
12
12
  * [eslint-plugin-compat]
13
13
  * [stylelint-no-unsupported-browser-features]
14
14
  * [postcss-normalize]
15
+ * [obsolete-webpack-plugin]
15
16
 
16
17
  [Browserslist Example] shows how every tool uses Browserslist.
17
18
  All tools will find target browsers automatically,
@@ -41,16 +42,16 @@ not dead
41
42
 
42
43
  Developers set versions list in queries like `last 2 version`
43
44
  to be free from updating versions manually.
44
- Browserslist will use [Can I Use] data for this queries.
45
+ Browserslist will use [Can I Use] data for this queries.
45
46
 
46
47
  Browserslist will take queries from tool option,
47
48
  `browserslist` config, `.browserslistrc` config,
48
- `browserslist` section in `package.json` or environment variables.
49
+ `browserslist` section in `package.json` or environment variables.
49
50
 
50
51
  You can test Browserslist queries in [online demo].
51
52
 
52
- [cult-img]: http://cultofmartians.com/assets/badges/badge.svg
53
- [cult]: http://cultofmartians.com/done.html
53
+ [cult-img]: https://cultofmartians.com/assets/badges/badge.svg
54
+ [cult]: https://cultofmartians.com/done.html
54
55
 
55
56
  <a href="https://evilmartians.com/?utm_source=browserslist">
56
57
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
@@ -59,13 +60,14 @@ You can test Browserslist queries in [online demo].
59
60
 
60
61
  [stylelint-no-unsupported-browser-features]: https://github.com/ismay/stylelint-no-unsupported-browser-features
61
62
  [eslint-plugin-compat]: https://github.com/amilajack/eslint-plugin-compat
62
- [Browserslist Example]: https://github.com/browserslist/browserslist-example
63
+ [Browserslist Example]: https://github.com/browserslist/browserslist-example
63
64
  [postcss-preset-env]: https://github.com/jonathantneal/postcss-preset-env
64
65
  [postcss-normalize]: https://github.com/jonathantneal/postcss-normalize
65
66
  [Autoprefixer]: https://github.com/postcss/autoprefixer
66
- [online demo]: http://browserl.ist/
67
- [Can I Use]: http://caniuse.com/
68
- [Babel]: https://github.com/babel/babel/tree/master/packages/babel-preset-env
67
+ [online demo]: https://browserl.ist/
68
+ [Can I Use]: https://caniuse.com/
69
+ [Babel]: https://github.com/babel/babel/tree/master/packages/babel-preset-env
70
+ [obsolete-webpack-plugin]: https://github.com/ElemeFE/obsolete-webpack-plugin
69
71
 
70
72
  ## Tools
71
73
 
@@ -102,14 +104,38 @@ from one of this sources:
102
104
  `> 0.5%, last 2 versions, Firefox ESR, not dead`.
103
105
 
104
106
 
107
+ ### Query Composition
108
+
109
+ An `or` combiner can use the keyword `or` as well as `,`.
110
+ `last 1 version or > 1%` is equal to `last 1 version, > 1%`.
111
+
112
+ `and` query combinations are also supported to perform an
113
+ intersection of the previous query: `last 1 version and > 1%`.
114
+
115
+ There is 3 different ways to combine queries as depicted below. First you start
116
+ with a single query and then we combine the queries to get our final list.
117
+
118
+ Obviously you can *not* start with a `not` combiner, since the is no left-hand
119
+ side query to combine it with.
120
+
121
+ | Query combiner type | Illustration | Example |
122
+ | ------------------- | :----------: | ------- |
123
+ |`or`/ `,` combiner <br> (union) | ![Union of queries](img/union.svg) | `'> .5% or last 2 versions'` <br> `'> .5%, last 2 versions'` |
124
+ | `and` combiner <br> (intersection) | ![intersection of queries](img/intersection.svg) | `'> .5% and last 2 versions'` |
125
+ | `not` combiner <br> (relative complement) | ![Relative complement of queries](img/complement.svg) | `'> .5% and not last 2 versions'` <br> `'> .5% or not last 2 versions'` <br> `'> .5%, not last 2 versions'` |
126
+
127
+ _A quick way to test your query is to do `npx browserslist '> 0.5%, not IE 11'`
128
+ in your terminal._
129
+
130
+
105
131
  ### Best Practices
106
132
 
107
133
  * Select browsers directly (`last 2 Chrome versions`) only if you are making
108
- a web app for a kiosk with one browser. There are a lot of browsers
134
+ a web app for a kiosk with one browser. There are a lot of browsers
109
135
  on the market. If you are making general web app you should respect
110
136
  browsers diversity.
111
137
  * If you want to change the default set of browsers we recommend to combine
112
- `last 1 version`, `not dead` with `> 0.2%` (or `> 1% in US`,
138
+ `last 1 version`, `not dead` with `> 0.2%` (or `> 1% in US`,
113
139
  `> 1% in my stats`). `last n versions` adds too many dead browsers
114
140
  and does not add popular old versions. Choosing a percentage above `0.2%`
115
141
  will in the long run make popular browsers even more popular. We might run
@@ -193,19 +219,17 @@ samsung 5
193
219
 
194
220
  Browserslist works with separated versions of browsers.
195
221
  You should avoid queries like `Firefox > 0`.
196
-
197
- Multiple criteria are combined as a boolean `OR`. A browser version must match
198
- at least one of the criteria to be selected.
222
+ The list of the specified browsers acts as a white list which means other browsers are ignored.
199
223
 
200
224
  All queries are based on the [Can I Use] support table,
201
225
  e.g. `last 3 iOS versions` might select `8.4, 9.2, 9.3` (mixed major and minor),
202
226
  whereas `last 3 Chrome versions` might select `50, 49, 48` (major only).
203
227
 
204
228
  [`caniuse-lite/data/regions`]: https://github.com/ben-eb/caniuse-lite/tree/master/data/regions
205
- [two-letter country code]: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
229
+ [two-letter country code]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
206
230
  [custom usage data]: #custom-usage-data
207
231
  [still maintained]: https://github.com/nodejs/Release
208
- [Can I Use]: http://caniuse.com/
232
+ [Can I Use]: https://caniuse.com/
209
233
 
210
234
 
211
235
  ### Browsers
@@ -425,7 +449,7 @@ while also querying against global or regional data.
425
449
  For example, the query `> 1% in my stats, > 5% in US, 10%` is permitted.
426
450
 
427
451
  [`browserslist-ga`]: https://github.com/browserslist/browserslist-ga
428
- [Can I Use]: http://caniuse.com/
452
+ [Can I Use]: https://caniuse.com/
429
453
 
430
454
 
431
455
  ## JS API
package/index.js CHANGED
@@ -8,6 +8,13 @@ var BrowserslistError = require('./error')
8
8
  var env = require('./node') // Will load browser.js in webpack
9
9
 
10
10
  var FLOAT_RANGE = /^\d+(\.\d+)?(-\d+(\.\d+)?)*$/
11
+ var YEAR = 365.259641 * 24 * 60 * 60 * 1000
12
+
13
+ // Enum values MUST be powers of 2, so combination are safe
14
+ /** @constant {number} */
15
+ var QUERY_OR = 1
16
+ /** @constant {number} */
17
+ var QUERY_AND = 2
11
18
 
12
19
  function isVersionsMatch (versionA, versionB) {
13
20
  return (versionA + '.').indexOf(versionB + '.') === 0
@@ -103,6 +110,7 @@ function normalizeVersion (data, version) {
103
110
  }
104
111
 
105
112
  function filterByYear (since) {
113
+ since = since / 1000
106
114
  return Object.keys(agents).reduce(function (selected, name) {
107
115
  var data = byName(name)
108
116
  if (!data) return selected
@@ -132,10 +140,23 @@ function unknownQuery (query) {
132
140
  )
133
141
  }
134
142
 
143
+ /**
144
+ * Resolves queries into a browser list.
145
+ * @param {string|string[]} queries Queries to combine.
146
+ * Either an array of queries or a long string of queries.
147
+ * @param {object} [context] Optional arguments to
148
+ * the select function in `queries`.
149
+ * @returns {string[]} A list of browsers
150
+ */
135
151
  function resolve (queries, context) {
136
- return queries.reduce(function (result, selection, index) {
137
- selection = selection.trim()
138
- if (selection === '') return result
152
+ if (Array.isArray(queries)) {
153
+ queries = flatten(queries.map(parse))
154
+ } else {
155
+ queries = parse(queries)
156
+ }
157
+
158
+ return queries.reduce(function (result, query, index) {
159
+ var selection = query.queryString
139
160
 
140
161
  var isExclude = selection.indexOf('not ') === 0
141
162
  if (isExclude) {
@@ -152,35 +173,44 @@ function resolve (queries, context) {
152
173
  var match = selection.match(type.regexp)
153
174
  if (match) {
154
175
  var args = [context].concat(match.slice(1))
155
- var array = type.select.apply(browserslist, args)
156
- if (isExclude) {
157
- var filter = { }
158
- var browsers = { }
159
- var versionLess = { }
160
- array.forEach(function (j) {
161
- filter[j] = true
162
- var browser = j.replace(/\s\S+$/, '')
163
- browsers[browser] = true
164
- if (/\s0$/.test(j)) {
165
- versionLess[browser] = true
166
- }
167
- })
168
- return result.filter(function (j) {
169
- if (filter[j]) {
170
- return false
176
+ var array = type.select.apply(browserslist, args).map(function (j) {
177
+ var parts = j.split(' ')
178
+ if (parts[1] === '0') {
179
+ return parts[0] + ' ' + byName(parts[0]).versions[0]
180
+ } else {
181
+ return j
182
+ }
183
+ })
184
+
185
+ switch (query.type) {
186
+ case QUERY_AND:
187
+ if (isExclude) {
188
+ return result.filter(function (j) {
189
+ // remove result items that are in array
190
+ // (the relative complement of array in result)
191
+ return array.indexOf(j) === -1
192
+ })
171
193
  } else {
172
- var browser = j.replace(/\s\S+$/, '')
173
- if (versionLess[browser]) {
174
- return false
175
- } else if (/\s0$/.test(j) && browsers[browser]) {
176
- return false
177
- } else {
178
- return true
179
- }
194
+ return result.filter(function (j) {
195
+ // remove result items not in array
196
+ // (intersect of result and array)
197
+ return array.indexOf(j) !== -1
198
+ })
180
199
  }
181
- })
200
+ case QUERY_OR:
201
+ default:
202
+ if (isExclude) {
203
+ var filter = { }
204
+ array.forEach(function (j) {
205
+ filter[j] = true
206
+ })
207
+ return result.filter(function (j) {
208
+ return !filter[j]
209
+ })
210
+ }
211
+ // union of result and array
212
+ return result.concat(array)
182
213
  }
183
- return result.concat(array)
184
214
  }
185
215
  }
186
216
 
@@ -205,7 +235,7 @@ function resolve (queries, context) {
205
235
  * version in direct query.
206
236
  * @param {boolean} [opts.dangerousExtend] Disable security checks
207
237
  * for extend query.
208
- * @return {string[]} Array with browser names in Can I Use.
238
+ * @returns {string[]} Array with browser names in Can I Use.
209
239
  *
210
240
  * @example
211
241
  * browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8']
@@ -226,13 +256,9 @@ function browserslist (queries, opts) {
226
256
  }
227
257
  }
228
258
 
229
- if (typeof queries === 'string') {
230
- queries = queries.split(/,\s*/)
231
- }
232
-
233
- if (!Array.isArray(queries)) {
259
+ if (!(typeof queries === 'string' || Array.isArray(queries))) {
234
260
  throw new BrowserslistError(
235
- 'Browser queries must be an array. Got ' + typeof queries + '.')
261
+ 'Browser queries must be an array or string. Got ' + typeof queries + '.')
236
262
  }
237
263
 
238
264
  var context = {
@@ -249,16 +275,7 @@ function browserslist (queries, opts) {
249
275
  }
250
276
  }
251
277
 
252
- var result = resolve(queries, context).map(function (i) {
253
- var parts = i.split(' ')
254
- var name = parts[0]
255
- var version = parts[1]
256
- if (version === '0') {
257
- return name + ' ' + byName(name).versions[0]
258
- } else {
259
- return i
260
- }
261
- }).sort(function (name1, name2) {
278
+ var result = resolve(queries, context).sort(function (name1, name2) {
262
279
  name1 = name1.split(' ')
263
280
  name2 = name2.split(' ')
264
281
  if (name1[0] === name2[0]) {
@@ -275,6 +292,74 @@ function browserslist (queries, opts) {
275
292
  return uniq(result)
276
293
  }
277
294
 
295
+ /**
296
+ * @typedef {object} BrowserslistQuery
297
+ * @property {number} type A type constant like QUERY_OR @see QUERY_OR.
298
+ * @property {string} queryString A query like "not ie < 11".
299
+ */
300
+
301
+ /**
302
+ * Parse a browserslist string query
303
+ * @param {string} queries One or more queries as a string
304
+ * @returns {BrowserslistQuery[]} An array of BrowserslistQuery
305
+ */
306
+ function parse (queries) {
307
+ var qs = []
308
+
309
+ do {
310
+ queries = doMatch(queries, qs)
311
+ } while (queries)
312
+
313
+ return qs
314
+ }
315
+
316
+ /**
317
+ * Find query matches in a string. This function is meant to be called
318
+ * repeatedly with the returned query string until there is no more matches.
319
+ * @param {string} string A string with one or more queries.
320
+ * @param {BrowserslistQuery[]} qs Out parameter,
321
+ * will be filled with `BrowserslistQuery`.
322
+ * @returns {string} The rest of the query string minus the matched part.
323
+ */
324
+ function doMatch (string, qs) {
325
+ var or = /^(?:,\s*|\s+OR\s+)(.*)/i
326
+ var and = /^\s+AND\s+(.*)/i
327
+
328
+ return find(
329
+ string,
330
+ function (parsed, n, max) {
331
+ if (and.test(parsed)) {
332
+ qs.unshift({ type: QUERY_AND, queryString: parsed.match(and)[1] })
333
+ return true
334
+ } else if (or.test(parsed)) {
335
+ qs.unshift({ type: QUERY_OR, queryString: parsed.match(or)[1] })
336
+ return true
337
+ } else if (n === max) {
338
+ qs.unshift({ type: QUERY_OR, queryString: parsed.trim() })
339
+ return true
340
+ }
341
+ return false
342
+ }
343
+ )
344
+ }
345
+
346
+ function find (string, predicate) {
347
+ for (var n = 1, max = string.length; n <= max; n++) {
348
+ var parsed = string.substr(-n, n)
349
+ if (predicate(parsed, n, max)) {
350
+ return string.replace(parsed, '')
351
+ }
352
+ }
353
+ return ''
354
+ }
355
+
356
+ function flatten (array) {
357
+ if (!Array.isArray(array)) return [array]
358
+ return array.reduce(function (a, b) {
359
+ return a.concat(flatten(b))
360
+ }, [])
361
+ }
362
+
278
363
  // Will be filled by Can I Use data below
279
364
  browserslist.data = { }
280
365
  browserslist.usage = {
@@ -463,12 +548,9 @@ var QUERIES = [
463
548
  }
464
549
  },
465
550
  {
466
- regexp: /^last\s+(\d+)\s+years?$/i,
551
+ regexp: /^last\s+(\d*.?\d+)\s+years?$/i,
467
552
  select: function (context, years) {
468
- var date = new Date()
469
- var since = date.setFullYear(date.getFullYear() - years) / 1000
470
-
471
- return filterByYear(since)
553
+ return filterByYear(Date.now() - YEAR * years)
472
554
  }
473
555
  },
474
556
  {
@@ -477,9 +559,7 @@ var QUERIES = [
477
559
  year = parseInt(year)
478
560
  month = parseInt(month || '01') - 1
479
561
  date = parseInt(date || '01')
480
- var since = Date.UTC(year, month, date, 0, 0, 0) / 1000
481
-
482
- return filterByYear(since)
562
+ return filterByYear(Date.UTC(year, month, date, 0, 0, 0))
483
563
  }
484
564
  },
485
565
  {
package/node.js CHANGED
@@ -6,7 +6,7 @@ var BrowserslistError = require('./error')
6
6
 
7
7
  var IS_SECTION = /^\s*\[(.+)\]\s*$/
8
8
  var CONFIG_PATTERN = /^browserslist-config-/
9
- var SCOPED_CONFIG__PATTERN = /@[^./]+\/browserslist-config(-|$|\/)/
9
+ var SCOPED_CONFIG__PATTERN = /@[^/]+\/browserslist-config(-|$|\/)/
10
10
  var TIME_TO_UPDATE_CANIUSE = 6 * 30 * 24 * 60 * 60 * 1000
11
11
  var FORMAT = 'Browserslist config should be a string or an array ' +
12
12
  'of strings with browser queries'
@@ -14,14 +14,13 @@ var FORMAT = 'Browserslist config should be a string or an array ' +
14
14
  var dataTimeChecked = false
15
15
  var filenessCache = { }
16
16
  var configCache = { }
17
-
18
17
  function checkExtend (name) {
19
18
  var use = ' Use `dangerousExtend` option to disable.'
20
19
  if (!CONFIG_PATTERN.test(name) && !SCOPED_CONFIG__PATTERN.test(name)) {
21
20
  throw new BrowserslistError(
22
21
  'Browserslist config needs `browserslist-config-` prefix. ' + use)
23
22
  }
24
- if (name.indexOf('.') !== -1) {
23
+ if (name.replace(/^@[^/]+\//, '').indexOf('.') !== -1) {
25
24
  throw new BrowserslistError(
26
25
  '`.` not allowed in Browserslist config name. ' + use)
27
26
  }
@@ -87,10 +86,9 @@ function parsePackage (file) {
87
86
  '`browserlist` key instead of `browserslist` in ' + file)
88
87
  }
89
88
  var list = config.browserslist
90
- if (Array.isArray(list)) {
89
+ if (Array.isArray(list) || typeof list === 'string') {
91
90
  list = { defaults: list }
92
91
  }
93
-
94
92
  for (var i in list) {
95
93
  check(list[i])
96
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browserslist",
3
- "version": "4.3.6",
3
+ "version": "4.4.2",
4
4
  "description": "Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-env-preset",
5
5
  "keywords": [
6
6
  "caniuse",
@@ -11,9 +11,9 @@
11
11
  "license": "MIT",
12
12
  "repository": "browserslist/browserslist",
13
13
  "dependencies": {
14
- "caniuse-lite": "^1.0.30000921",
15
- "electron-to-chromium": "^1.3.92",
16
- "node-releases": "^1.1.1"
14
+ "caniuse-lite": "^1.0.30000939",
15
+ "electron-to-chromium": "^1.3.113",
16
+ "node-releases": "^1.1.8"
17
17
  },
18
18
  "bin": "./cli.js",
19
19
  "browser": {