2020-09-24 19:33:09 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Sergey Melyukov @smelukov
|
|
|
|
*/
|
|
|
|
|
2020-09-24 19:42:27 +08:00
|
|
|
"use strict";
|
2020-09-24 19:33:09 +08:00
|
|
|
|
|
|
|
const browserslist = require("browserslist");
|
2020-09-24 20:46:28 +08:00
|
|
|
const path = require("path");
|
2020-09-24 19:33:09 +08:00
|
|
|
|
2020-09-28 16:04:58 +08:00
|
|
|
/** @typedef {import("./target").ApiTargetProperties} ApiTargetProperties */
|
2020-09-28 15:57:29 +08:00
|
|
|
/** @typedef {import("./target").EcmaTargetProperties} EcmaTargetProperties */
|
2020-09-28 16:04:58 +08:00
|
|
|
/** @typedef {import("./target").PlatformTargetProperties} PlatformTargetProperties */
|
2020-09-28 15:57:29 +08:00
|
|
|
|
2020-09-25 04:10:33 +08:00
|
|
|
// [[C:]/path/to/config][:env]
|
2020-09-25 04:44:41 +08:00
|
|
|
const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i;
|
2020-09-24 19:33:09 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} BrowserslistHandlerConfig
|
|
|
|
* @property {string} [configPath]
|
|
|
|
* @property {string} [env]
|
|
|
|
* @property {string} [query]
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2020-09-28 16:04:58 +08:00
|
|
|
* @param {BrowserslistHandlerConfig | null} handlerConfig config
|
|
|
|
* @returns {EcmaTargetProperties & PlatformTargetProperties & ApiTargetProperties} target properties
|
2020-09-24 19:33:09 +08:00
|
|
|
*/
|
|
|
|
const resolve = handlerConfig => {
|
2020-09-24 20:08:09 +08:00
|
|
|
const { configPath, env, query } = handlerConfig || {};
|
2020-09-24 19:33:09 +08:00
|
|
|
|
|
|
|
// if a query is specified, then use it, else
|
|
|
|
// if a path to a config is specified then load it, else
|
|
|
|
// find a nearest config
|
|
|
|
const config = query
|
|
|
|
? query
|
|
|
|
: configPath
|
2020-09-24 20:08:09 +08:00
|
|
|
? browserslist.loadConfig({
|
2020-09-24 19:33:09 +08:00
|
|
|
config: configPath,
|
|
|
|
env
|
|
|
|
})
|
2020-09-24 20:08:09 +08:00
|
|
|
: browserslist.loadConfig({ path: process.cwd(), env });
|
2020-09-24 19:33:09 +08:00
|
|
|
|
2020-09-24 20:08:09 +08:00
|
|
|
const browsers = browserslist(config);
|
2020-09-24 19:33:09 +08:00
|
|
|
|
2020-09-28 15:57:29 +08:00
|
|
|
return resolveESFeatures(browsers);
|
2020-09-24 19:33:09 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} input input string
|
|
|
|
* @returns {BrowserslistHandlerConfig|null} config
|
|
|
|
*/
|
|
|
|
const parse = input => {
|
|
|
|
if (!input) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-09-24 20:41:54 +08:00
|
|
|
if (path.isAbsolute(input)) {
|
|
|
|
const [, configPath, env] = inputRx.exec(input) || [];
|
|
|
|
return { configPath, env };
|
|
|
|
}
|
|
|
|
|
|
|
|
const config = browserslist.findConfig(process.cwd());
|
|
|
|
|
2020-09-25 04:10:33 +08:00
|
|
|
if (config && Object.keys(config).includes(input)) {
|
|
|
|
return { env: input };
|
2020-09-24 20:41:54 +08:00
|
|
|
}
|
2020-09-24 19:33:09 +08:00
|
|
|
|
2020-09-25 04:10:33 +08:00
|
|
|
return { query: input };
|
2020-09-24 19:33:09 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string[]} browsers supported browsers list
|
2020-09-28 16:04:58 +08:00
|
|
|
* @returns {EcmaTargetProperties & PlatformTargetProperties & ApiTargetProperties} target properties
|
2020-09-24 19:33:09 +08:00
|
|
|
*/
|
2020-09-28 15:57:29 +08:00
|
|
|
const resolveESFeatures = browsers => {
|
2020-09-24 19:33:09 +08:00
|
|
|
/**
|
2020-09-28 15:57:29 +08:00
|
|
|
* Checks only browser against the browserslist feature query
|
2020-09-24 19:33:09 +08:00
|
|
|
* @param {string} feature an ES feature to test
|
|
|
|
* @returns {boolean} true if supports
|
|
|
|
*/
|
2020-09-28 15:57:29 +08:00
|
|
|
const browserslistChecker = feature => {
|
2020-09-24 19:33:09 +08:00
|
|
|
const supportsFeature = browserslist(`supports ${feature}`);
|
2020-09-28 15:57:29 +08:00
|
|
|
return browsers.every(v => /^node /.test(v) || supportsFeature.includes(v));
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Checks only node.js version against a version
|
|
|
|
* @param {number} major major version
|
|
|
|
* @param {number} minor minor version
|
|
|
|
* @returns {boolean} true if supports
|
|
|
|
*/
|
|
|
|
const nodeChecker = (major, minor = 0) => {
|
|
|
|
return browsers.every(v => {
|
|
|
|
const match = /^node (\d+)(?:\.\d+)?/.exec(v);
|
|
|
|
if (!match) return true;
|
|
|
|
const [, v1, v2] = match;
|
|
|
|
return major === +v1 ? +v2 >= minor : +v1 > major;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Checks all against a version number
|
|
|
|
* @param {Record<string, number | [number, number]} versions first supported version
|
|
|
|
* @returns {boolean} true if supports
|
|
|
|
*/
|
|
|
|
const rawChecker = versions => {
|
|
|
|
return browsers.every(v => {
|
|
|
|
const match = /^([^ ]+) (\d+)(?:\.\d+)?/.exec(v);
|
|
|
|
if (!match) return false;
|
|
|
|
const [, name, major, minor] = match;
|
|
|
|
const version = versions[name];
|
|
|
|
if (!version) return false;
|
|
|
|
if (typeof version === "number") return +major >= version;
|
|
|
|
return version[0] === +major ? +minor >= version[1] : +major > version[0];
|
|
|
|
});
|
|
|
|
};
|
2020-09-28 16:04:58 +08:00
|
|
|
const anyNode = browsers.some(b => /^node /.test(b));
|
|
|
|
const anyBrowser = browsers.some(b => /^(?!node)/.test(b));
|
|
|
|
const browserProperty = !anyBrowser ? false : anyNode ? null : true;
|
|
|
|
const nodeProperty = !anyNode ? false : anyBrowser ? null : true;
|
2020-09-28 15:57:29 +08:00
|
|
|
const es6 = browserslistChecker("es6");
|
|
|
|
const es6DynamicImport = browserslistChecker("es6-module-dynamic-import");
|
|
|
|
const node6 = nodeChecker(6);
|
|
|
|
return {
|
|
|
|
const: es6 && node6,
|
|
|
|
arrowFunction: es6 && node6,
|
|
|
|
forOf: es6 && nodeChecker(5),
|
|
|
|
destructuring: es6 && node6,
|
|
|
|
bigIntLiteral: browserslistChecker("bigint") && nodeChecker(10, 4),
|
|
|
|
module: browserslistChecker("es6-module") && nodeChecker(12, 17),
|
|
|
|
dynamicImport: es6DynamicImport && nodeChecker(10, 17),
|
|
|
|
dynamicImportInWorker: es6DynamicImport && nodeChecker(Infinity),
|
|
|
|
// browserslist does not have info about globalThis
|
|
|
|
// so this is based on mdn-browser-compat-data
|
|
|
|
globalThis: rawChecker({
|
|
|
|
chrome: 71,
|
|
|
|
chrome_android: 71,
|
|
|
|
edge: 79,
|
|
|
|
firefox: 65,
|
|
|
|
firefox_android: 65,
|
|
|
|
// ie: Not supported,
|
|
|
|
nodejs: 12,
|
|
|
|
opera: 58,
|
|
|
|
opera_android: 50,
|
|
|
|
safari: [12, 1],
|
|
|
|
safari_ios: [12, 2],
|
|
|
|
// cspell:word samsunginternet
|
|
|
|
samsunginternet_android: [10, 0],
|
|
|
|
webview_android: 71
|
2020-09-28 16:04:58 +08:00
|
|
|
}),
|
|
|
|
|
|
|
|
browser: browserProperty,
|
|
|
|
electron: false,
|
|
|
|
node: nodeProperty,
|
|
|
|
nwjs: false,
|
|
|
|
web: browserProperty,
|
|
|
|
webworker: false,
|
|
|
|
|
|
|
|
document: browserProperty,
|
|
|
|
fetchWasm: browserProperty,
|
|
|
|
global: nodeProperty,
|
|
|
|
importScripts: false,
|
|
|
|
importScriptsInWorker: true,
|
|
|
|
nodeBuiltins: nodeProperty,
|
|
|
|
require: nodeProperty
|
2020-09-24 19:33:09 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
resolve,
|
|
|
|
parse
|
|
|
|
};
|