diff --git a/index.d.ts b/index.d.ts index 076590c2..7f8e7605 100644 --- a/index.d.ts +++ b/index.d.ts @@ -124,7 +124,9 @@ declare namespace browserslist { let cache: { [feature: string]: { - [name: string]: 'y' | 'n' + [name: string]: { + [version: string]: string + } } } diff --git a/index.js b/index.js index c05cf6c3..62f42af2 100644 --- a/index.js +++ b/index.js @@ -302,6 +302,13 @@ function filterAndroid(list, versions, context) { return list.slice(nEvergreen - 1 - versions) } +function isSupported(flags) { + return ( + typeof flags === 'string' && + (flags.indexOf('y') >= 0 || flags.indexOf('a') >= 0) + ) +} + function resolve(queries, context) { return parse(QUERIES, queries).reduce(function (result, node, index) { if (node.not && index === 0) { @@ -893,13 +900,25 @@ var QUERIES = { select: function (context, node) { env.loadFeature(browserslist.cache, node.feature) var features = browserslist.cache[node.feature] - return Object.keys(features).reduce(function (result, version) { - var flags = features[version] - if (flags.indexOf('y') >= 0 || flags.indexOf('a') >= 0) { - result.push(version) - } - return result - }, []) + var result = [] + for (var name in features) { + var data = byName(name, context) + // Only check desktop when latest released mobile has support + var checkDesktop = + context.mobileToDesktop && + name in browserslist.desktopNames && + isSupported(features[name][data.released.slice(-1)[0]]) + data.versions.forEach(function (version) { + var flags = features[name][version] + if (flags === undefined && checkDesktop) { + flags = features[browserslist.desktopNames[name]][version] + } + if (isSupported(flags)) { + result.push(name + ' ' + version) + } + }) + } + return result } }, electron_range: { diff --git a/node.js b/node.js index 0e208d05..100ded66 100644 --- a/node.js +++ b/node.js @@ -272,8 +272,9 @@ module.exports = { var stats = feature(compressed).stats features[name] = {} for (var i in stats) { + features[name][i] = {} for (var j in stats[i]) { - features[name][i + ' ' + j] = stats[i][j] + features[name][i][j] = stats[i][j] } } }, diff --git a/test/feature.test.js b/test/feature.test.js index 43db21ff..47cf78f2 100644 --- a/test/feature.test.js +++ b/test/feature.test.js @@ -4,50 +4,105 @@ let { equal, is, throws } = require('uvu/assert') delete require.cache[require.resolve('..')] let browserslist = require('..') -let originCache = browserslist.cache +let originData = { ...browserslist.data } test.before.each(() => { - browserslist.cache = {} - browserslist.data.and_chr = { - name: 'and_chr', - versions: ['81'], - released: [], - releaseDate: {} + browserslist.data = { + and_chr: { + name: 'and_chr', + versions: ['81'], + released: ['81'], + releaseDate: {} + }, + chrome: { + name: 'chrome', + versions: ['80', '81', '82'], + released: ['80', '81'], + releaseDate: {} + }, + ie: { + name: 'ie', + versions: ['10', '11'], + released: ['10', '11'], + releaseDate: {} + } } }) test.after.each(() => { - browserslist.cache = originCache + browserslist.clearCaches() + browserslist.data = originData }) test('load features from Can I Use', () => { + browserslist.data = originData is(browserslist('supports objectrtc').length > 0, true) }) test('throw an error on wrong feature name from Can I Use', () => { - throws(() => browserslist('supports wrong-feature-name'), /Unknown feature name/) + throws( + () => browserslist('supports wrong-feature-name'), + /Unknown feature name/ + ) }) test('selects browsers by feature', () => { - browserslist.cache = { - rtcpeerconnection: { - 'and_chr 81': 'y', - 'firefox 2': 'n' - } + browserslist.cache.rtcpeerconnection = { + and_chr: { 81: 'y' }, + chrome: { 80: 'n', 81: 'y', 82: 'y' }, + ie: { 10: 'n', 11: 'n' } } - equal(browserslist('supports rtcpeerconnection'), ['and_chr 81']) + equal(browserslist('supports rtcpeerconnection'), [ + 'and_chr 81', + 'chrome 82', + 'chrome 81' + ]) }) test('selects browsers by feature with dashes in its name', () => { - browserslist.cache = { - 'arrow-functions': { - 'and_chr 81': 'y', - 'ie 11': 'n' - } + browserslist.cache['arrow-functions'] = { + and_chr: { 81: 'n' }, + chrome: { 80: 'n', 81: 'y', 82: 'y' }, + ie: { 10: 'n', 11: 'y' } + } + + equal(browserslist('supports arrow-functions'), [ + 'chrome 82', + 'chrome 81', + 'ie 11' + ]) +}) + +test('Selects extra versions with mobile to desktop option', () => { + browserslist.cache.filesystem = { + and_chr: { 81: 'y' }, + chrome: { 80: 'y', 81: 'y', 82: 'y' }, + ie: { 10: 'n', 11: 'n' } + } + + equal(browserslist('supports filesystem', { mobileToDesktop: true }), [ + 'and_chr 82', + 'and_chr 81', + 'and_chr 80', + 'chrome 82', + 'chrome 81', + 'chrome 80' + ]) +}) + +test('Ignores mobile to desktop if unsupported by latest', () => { + browserslist.cache['font-smooth'] = { + and_chr: { 81: 'n' }, + chrome: { 80: 'y', 81: 'y', 82: 'y' }, + ie: { 10: 'n', 11: 'n' } } - equal(browserslist('supports arrow-functions'), ['and_chr 81']) + equal(browserslist('supports font-smooth', { mobileToDesktop: true }), [ + 'chrome 82', + 'chrome 81', + 'chrome 80' + ]) }) test.run()