browserslistTargetHandler.js 8.9 KB

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