browserslistTargetHandler.js 8.9 KB

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