browserslistTargetHandler.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Sergey Melyukov @smelukov
  4. */
  5. "use strict";
  6. const browserslist = require("browserslist");
  7. const path = require("path");
  8. /** @typedef {import("./target").ApiTargetProperties} ApiTargetProperties */
  9. /** @typedef {import("./target").EcmaTargetProperties} EcmaTargetProperties */
  10. /** @typedef {import("./target").PlatformTargetProperties} PlatformTargetProperties */
  11. // [[C:]/path/to/config][:env]
  12. const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i;
  13. /**
  14. * @typedef {object} BrowserslistHandlerConfig
  15. * @property {string=} configPath
  16. * @property {string=} env
  17. * @property {string=} query
  18. */
  19. /**
  20. * @param {string | null | undefined} input input string
  21. * @param {string} context the context directory
  22. * @returns {BrowserslistHandlerConfig} config
  23. */
  24. const parse = (input, context) => {
  25. if (!input) {
  26. return {};
  27. }
  28. if (path.isAbsolute(input)) {
  29. const [, configPath, env] = inputRx.exec(input) || [];
  30. return { configPath, env };
  31. }
  32. const config = browserslist.findConfig(context);
  33. if (config && Object.keys(config).includes(input)) {
  34. return { env: input };
  35. }
  36. return { query: input };
  37. };
  38. /**
  39. * @param {string | null | undefined} input input string
  40. * @param {string} context the context directory
  41. * @returns {string[] | undefined} selected browsers
  42. */
  43. const load = (input, context) => {
  44. const { configPath, env, query } = parse(input, context);
  45. // if a query is specified, then use it, else
  46. // if a path to a config is specified then load it, else
  47. // find a nearest config
  48. const config = query
  49. ? query
  50. : configPath
  51. ? browserslist.loadConfig({
  52. config: configPath,
  53. env
  54. })
  55. : browserslist.loadConfig({ path: context, env });
  56. if (!config) return;
  57. return browserslist(config);
  58. };
  59. /**
  60. * @param {string[]} browsers supported browsers list
  61. * @returns {EcmaTargetProperties & PlatformTargetProperties & ApiTargetProperties} target properties
  62. */
  63. const resolve = browsers => {
  64. /**
  65. * Checks all against a version number
  66. * @param {Record<string, number | [number, number]>} versions first supported version
  67. * @returns {boolean} true if supports
  68. */
  69. const rawChecker = versions => {
  70. return browsers.every(v => {
  71. const [name, parsedVersion] = v.split(" ");
  72. if (!name) return false;
  73. const requiredVersion = versions[name];
  74. if (!requiredVersion) return false;
  75. const [parsedMajor, parserMinor] =
  76. // safari TP supports all features for normal safari
  77. parsedVersion === "TP"
  78. ? [Infinity, Infinity]
  79. : parsedVersion.includes("-")
  80. ? parsedVersion.split("-")[0].split(".")
  81. : parsedVersion.split(".");
  82. if (typeof requiredVersion === "number") {
  83. return +parsedMajor >= requiredVersion;
  84. }
  85. return requiredVersion[0] === +parsedMajor
  86. ? +parserMinor >= requiredVersion[1]
  87. : +parsedMajor > requiredVersion[0];
  88. });
  89. };
  90. const anyNode = browsers.some(b => /^node /.test(b));
  91. const anyBrowser = browsers.some(b => /^(?!node)/.test(b));
  92. const browserProperty = !anyBrowser ? false : anyNode ? null : true;
  93. const nodeProperty = !anyNode ? false : anyBrowser ? null : true;
  94. // Internet Explorer Mobile, Blackberry browser and Opera Mini are very old browsers, they do not support new features
  95. const es6DynamicImport = rawChecker({
  96. chrome: 63,
  97. and_chr: 63,
  98. edge: 79,
  99. firefox: 67,
  100. and_ff: 67,
  101. // ie: Not supported
  102. opera: 50,
  103. op_mob: 46,
  104. safari: [11, 1],
  105. ios_saf: [11, 3],
  106. samsung: [8, 2],
  107. android: 63,
  108. and_qq: [10, 4],
  109. baidu: [13, 18],
  110. and_uc: [15, 5],
  111. kaios: [3, 0],
  112. node: [12, 17]
  113. });
  114. return {
  115. const: rawChecker({
  116. chrome: 49,
  117. and_chr: 49,
  118. edge: 12,
  119. // Prior to Firefox 13, <code>const</code> is implemented, but re-assignment is not failing.
  120. // Prior to Firefox 46, a <code>TypeError</code> was thrown on redeclaration instead of a <code>SyntaxError</code>.
  121. firefox: 36,
  122. and_ff: 36,
  123. // Not supported in for-in and for-of loops
  124. // ie: Not supported
  125. opera: 36,
  126. op_mob: 36,
  127. safari: [10, 0],
  128. ios_saf: [10, 0],
  129. // Before 5.0 supported correctly in strict mode, otherwise supported without block scope
  130. samsung: [5, 0],
  131. android: 37,
  132. and_qq: [10, 4],
  133. // Supported correctly in strict mode, otherwise supported without block scope
  134. baidu: [13, 18],
  135. and_uc: [12, 12],
  136. kaios: [2, 5],
  137. node: [6, 0]
  138. }),
  139. arrowFunction: rawChecker({
  140. chrome: 45,
  141. and_chr: 45,
  142. edge: 12,
  143. // The initial implementation of arrow functions in Firefox made them automatically strict. This has been changed as of Firefox 24. The use of <code>'use strict';</code> is now required.
  144. // Prior to Firefox 39, a line terminator (<code>\\n</code>) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES2015 specification and code like <code>() \\n => {}</code> will now throw a <code>SyntaxError</code> in this and later versions.
  145. firefox: 39,
  146. and_ff: 39,
  147. // ie: Not supported,
  148. opera: 32,
  149. op_mob: 32,
  150. safari: 10,
  151. ios_saf: 10,
  152. samsung: [5, 0],
  153. android: 45,
  154. and_qq: [10, 4],
  155. baidu: [7, 12],
  156. and_uc: [12, 12],
  157. kaios: [2, 5],
  158. node: [6, 0]
  159. }),
  160. forOf: rawChecker({
  161. chrome: 38,
  162. and_chr: 38,
  163. edge: 12,
  164. // Prior to Firefox 51, using the for...of loop construct with the const keyword threw a SyntaxError ("missing = in const declaration").
  165. firefox: 51,
  166. and_ff: 51,
  167. // ie: Not supported,
  168. opera: 25,
  169. op_mob: 25,
  170. safari: 7,
  171. ios_saf: 7,
  172. samsung: [3, 0],
  173. android: 38,
  174. // and_qq: Unknown support
  175. // baidu: Unknown support
  176. // and_uc: Unknown support
  177. kaios: [3, 0],
  178. node: [0, 12]
  179. }),
  180. destructuring: rawChecker({
  181. chrome: 49,
  182. and_chr: 49,
  183. edge: 14,
  184. firefox: 41,
  185. and_ff: 41,
  186. // ie: Not supported,
  187. opera: 36,
  188. op_mob: 36,
  189. safari: 8,
  190. ios_saf: 8,
  191. samsung: [5, 0],
  192. android: 49,
  193. // and_qq: Unknown support
  194. // baidu: Unknown support
  195. // and_uc: Unknown support
  196. kaios: [2, 5],
  197. node: [6, 0]
  198. }),
  199. bigIntLiteral: rawChecker({
  200. chrome: 67,
  201. and_chr: 67,
  202. edge: 79,
  203. firefox: 68,
  204. and_ff: 68,
  205. // ie: Not supported,
  206. opera: 54,
  207. op_mob: 48,
  208. safari: 14,
  209. ios_saf: 14,
  210. samsung: [9, 2],
  211. android: 67,
  212. and_qq: [13, 1],
  213. baidu: [13, 18],
  214. and_uc: [15, 5],
  215. kaios: [3, 0],
  216. node: [10, 4]
  217. }),
  218. // Support syntax `import` and `export` and no limitations and bugs on Node.js
  219. // Not include `export * as namespace`
  220. module: rawChecker({
  221. chrome: 61,
  222. and_chr: 61,
  223. edge: 16,
  224. firefox: 60,
  225. and_ff: 60,
  226. // ie: Not supported,
  227. opera: 48,
  228. op_mob: 45,
  229. safari: [10, 1],
  230. ios_saf: [10, 3],
  231. samsung: [8, 0],
  232. android: 61,
  233. and_qq: [10, 4],
  234. baidu: [13, 18],
  235. and_uc: [15, 5],
  236. kaios: [3, 0],
  237. node: [12, 17]
  238. }),
  239. dynamicImport: es6DynamicImport,
  240. dynamicImportInWorker: es6DynamicImport && !anyNode,
  241. // browserslist does not have info about globalThis
  242. // so this is based on mdn-browser-compat-data
  243. globalThis: rawChecker({
  244. chrome: 71,
  245. and_chr: 71,
  246. edge: 79,
  247. firefox: 65,
  248. and_ff: 65,
  249. // ie: Not supported,
  250. opera: 58,
  251. op_mob: 50,
  252. safari: [12, 1],
  253. ios_saf: [12, 2],
  254. samsung: [10, 1],
  255. android: 71,
  256. // and_qq: Unknown support
  257. // baidu: Unknown support
  258. // and_uc: Unknown support
  259. kaios: [3, 0],
  260. node: 12
  261. }),
  262. optionalChaining: rawChecker({
  263. chrome: 80,
  264. and_chr: 80,
  265. edge: 80,
  266. firefox: 74,
  267. and_ff: 79,
  268. // ie: Not supported,
  269. opera: 67,
  270. op_mob: 64,
  271. safari: [13, 1],
  272. ios_saf: [13, 4],
  273. samsung: 13,
  274. android: 80,
  275. // and_qq: Not supported
  276. // baidu: Not supported
  277. // and_uc: Not supported
  278. kaios: [3, 0],
  279. node: 14
  280. }),
  281. templateLiteral: rawChecker({
  282. chrome: 41,
  283. and_chr: 41,
  284. edge: 13,
  285. firefox: 34,
  286. and_ff: 34,
  287. // ie: Not supported,
  288. opera: 29,
  289. op_mob: 64,
  290. safari: [9, 1],
  291. ios_saf: 9,
  292. samsung: 4,
  293. android: 41,
  294. and_qq: [10, 4],
  295. baidu: [7, 12],
  296. and_uc: [12, 12],
  297. kaios: [2, 5],
  298. node: 4
  299. }),
  300. asyncFunction: rawChecker({
  301. chrome: 55,
  302. and_chr: 55,
  303. edge: 15,
  304. firefox: 52,
  305. and_ff: 52,
  306. // ie: Not supported,
  307. opera: 42,
  308. op_mob: 42,
  309. safari: 11,
  310. ios_saf: 11,
  311. samsung: [6, 2],
  312. android: 55,
  313. and_qq: [13, 1],
  314. baidu: [13, 18],
  315. and_uc: [15, 5],
  316. kaios: 3,
  317. node: [7, 6]
  318. }),
  319. browser: browserProperty,
  320. electron: false,
  321. node: nodeProperty,
  322. nwjs: false,
  323. web: browserProperty,
  324. webworker: false,
  325. document: browserProperty,
  326. fetchWasm: browserProperty,
  327. global: nodeProperty,
  328. importScripts: false,
  329. importScriptsInWorker: true,
  330. nodeBuiltins: nodeProperty,
  331. nodePrefixForCoreModules:
  332. nodeProperty &&
  333. !browsers.some(b => /^node 15/.test(b)) &&
  334. rawChecker({
  335. node: [14, 18]
  336. }),
  337. require: nodeProperty
  338. };
  339. };
  340. module.exports = {
  341. resolve,
  342. load
  343. };