browserslistTargetHandler.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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. /**
  9. * @import {
  10. * ApiTargetProperties,
  11. * EcmaTargetProperties,
  12. * PlatformTargetProperties
  13. * } from "./target"
  14. */
  15. // [[C:]/path/to/config][:env]
  16. const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i;
  17. // One resolution per `[input, context]` — several callers ask for the same
  18. // target, and each miss re-reads the config and re-runs the queries. The list is
  19. // handed out as it stands, so no caller may write to it.
  20. /** @type {Map<string, string[] | undefined> | null} */
  21. const cache = process.env.BROWSERSLIST_DISABLE_CACHE ? null : new Map();
  22. /**
  23. * Returns selected browsers.
  24. * @param {string | null | undefined} input input string
  25. * @param {string} context the context directory
  26. * @returns {string[] | undefined} selected browsers
  27. */
  28. const load = (input, context) => {
  29. if (cache === null) return loadBrowsers(input, context);
  30. // A newline separates the two: an input carries a config path, an env or a
  31. // query, and a directory carries none.
  32. const key = `${input || ""}\n${context}`;
  33. let browsers = cache.get(key);
  34. if (browsers === undefined && !cache.has(key)) {
  35. browsers = loadBrowsers(input, context);
  36. cache.set(key, browsers);
  37. }
  38. return browsers;
  39. };
  40. /**
  41. * Read the selection out of the browserslist config or query.
  42. * @param {string | null | undefined} input input string
  43. * @param {string} context the context directory
  44. * @returns {string[] | undefined} selected browsers
  45. */
  46. const loadBrowsers = (input, context) => {
  47. // browserslist:path-to-config
  48. // browserslist:path-to-config:env
  49. if (input && path.isAbsolute(input)) {
  50. const [, configPath, env] = inputRx.exec(input) || [];
  51. const config = browserslist.loadConfig({
  52. config: configPath,
  53. env
  54. });
  55. return browserslist(config, { env });
  56. }
  57. const env = input || undefined;
  58. const config = browserslist.loadConfig({
  59. path: context,
  60. env
  61. });
  62. // browserslist
  63. // browserslist:env
  64. if (config) {
  65. try {
  66. return browserslist(config, { env, throwOnMissing: true });
  67. } catch (_err) {
  68. // Nothing, no `env` was found in browserslist, maybe input is `queries`
  69. }
  70. }
  71. // browserslist:query
  72. if (env) {
  73. return browserslist(env);
  74. }
  75. };
  76. /**
  77. * Returns target properties.
  78. * @param {string[]} browsers supported browsers list
  79. * @returns {EcmaTargetProperties & PlatformTargetProperties & ApiTargetProperties} target properties
  80. */
  81. const resolve = (browsers) => {
  82. /**
  83. * Checks all against a version number
  84. * @param {Record<string, number | [number, number]>} versions first supported version
  85. * @returns {boolean} true if supports
  86. */
  87. const rawChecker = (versions) => checkAll(browsers, versions);
  88. /**
  89. * Checks a subset of the list against a version number
  90. * @param {string[]} list the browsers to check
  91. * @param {Record<string, number | [number, number]>} versions first supported version
  92. * @returns {boolean} true if all support
  93. */
  94. const checkAll = (list, versions) =>
  95. list.every((v) => {
  96. const [name, parsedVersion] = v.split(" ");
  97. if (!name) return false;
  98. const requiredVersion = versions[name];
  99. if (!requiredVersion) return false;
  100. const [parsedMajor, parserMinor] =
  101. // safari TP supports all features for normal safari
  102. parsedVersion === "TP"
  103. ? [Infinity, Infinity]
  104. : parsedVersion.includes("-")
  105. ? parsedVersion.split("-")[0].split(".")
  106. : parsedVersion.split(".");
  107. if (typeof requiredVersion === "number") {
  108. return Number(parsedMajor) >= requiredVersion;
  109. }
  110. return requiredVersion[0] === Number(parsedMajor)
  111. ? Number(parserMinor) >= requiredVersion[1]
  112. : Number(parsedMajor) > requiredVersion[0];
  113. });
  114. const anyNode = browsers.some((b) => b.startsWith("node "));
  115. const anyBrowser = browsers.some((b) => /^(?!node)/.test(b));
  116. const browserProperty = !anyBrowser ? false : anyNode ? null : true;
  117. const nodeProperty = !anyNode ? false : anyBrowser ? null : true;
  118. return {
  119. /* eslint-disable camelcase */
  120. const: rawChecker({
  121. chrome: 49,
  122. and_chr: 49,
  123. edge: 12,
  124. // Prior to Firefox 13, <code>const</code> is implemented, but re-assignment is not failing.
  125. // Prior to Firefox 46, a <code>TypeError</code> was thrown on redeclaration instead of a <code>SyntaxError</code>.
  126. firefox: 36,
  127. and_ff: 36,
  128. // Not supported in for-in and for-of loops
  129. // ie: Not supported
  130. opera: 36,
  131. op_mob: 36,
  132. safari: [10, 0],
  133. ios_saf: [10, 0],
  134. // Before 5.0 supported correctly in strict mode, otherwise supported without block scope
  135. samsung: [5, 0],
  136. android: 37,
  137. and_qq: [10, 4],
  138. // Supported correctly in strict mode, otherwise supported without block scope
  139. baidu: [13, 18],
  140. and_uc: [12, 12],
  141. kaios: [2, 5],
  142. node: [6, 0]
  143. }),
  144. let: rawChecker({
  145. chrome: 49,
  146. and_chr: 49,
  147. edge: 12,
  148. firefox: 44,
  149. and_ff: 44,
  150. // ie: Not supported
  151. opera: 36,
  152. op_mob: 36,
  153. safari: [10, 0],
  154. ios_saf: [10, 0],
  155. samsung: [5, 0],
  156. android: 49,
  157. and_qq: [10, 4],
  158. baidu: [13, 18],
  159. and_uc: [12, 12],
  160. kaios: [2, 5],
  161. node: [6, 0]
  162. }),
  163. logicalAssignment: rawChecker({
  164. chrome: 85,
  165. and_chr: 85,
  166. edge: 85,
  167. firefox: 79,
  168. and_ff: 79,
  169. // ie: Not supported,
  170. opera: 71,
  171. op_mob: 60,
  172. safari: 14,
  173. ios_saf: 14,
  174. samsung: [14, 0],
  175. android: 85,
  176. and_qq: [14, 9],
  177. baidu: [13, 52],
  178. and_uc: [15, 5],
  179. kaios: [3, 0],
  180. node: [15, 0]
  181. }),
  182. methodShorthand: rawChecker({
  183. chrome: 47,
  184. and_chr: 47,
  185. edge: 12,
  186. firefox: 34,
  187. and_ff: 34,
  188. // ie: Not supported,
  189. opera: 34,
  190. op_mob: 34,
  191. safari: 9,
  192. ios_saf: 9,
  193. samsung: 5,
  194. android: 47,
  195. // baidu: Not tracked,
  196. and_qq: [14, 9],
  197. and_uc: [15, 5],
  198. kaios: [2, 5],
  199. node: [4, 9]
  200. }),
  201. arrowFunction: rawChecker({
  202. chrome: 45,
  203. and_chr: 45,
  204. edge: 12,
  205. // 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.
  206. // 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.
  207. firefox: 39,
  208. and_ff: 39,
  209. // ie: Not supported,
  210. opera: 32,
  211. op_mob: 32,
  212. safari: 10,
  213. ios_saf: 10,
  214. samsung: [5, 0],
  215. android: 45,
  216. and_qq: [10, 4],
  217. baidu: [7, 12],
  218. and_uc: [12, 12],
  219. kaios: [2, 5],
  220. node: [6, 0]
  221. }),
  222. forOf: rawChecker({
  223. chrome: 38,
  224. and_chr: 38,
  225. edge: 12,
  226. // Prior to Firefox 51, using the for...of loop construct with the const keyword threw a SyntaxError ("missing = in const declaration").
  227. firefox: 51,
  228. and_ff: 51,
  229. // ie: Not supported,
  230. opera: 25,
  231. op_mob: 25,
  232. safari: 7,
  233. ios_saf: 7,
  234. samsung: [3, 0],
  235. android: 38,
  236. and_qq: [10, 4],
  237. // baidu: Unknown support
  238. and_uc: [12, 12],
  239. kaios: [3, 0],
  240. node: [0, 12]
  241. }),
  242. destructuring: rawChecker({
  243. chrome: 49,
  244. and_chr: 49,
  245. edge: 14,
  246. firefox: 41,
  247. and_ff: 41,
  248. // ie: Not supported,
  249. opera: 36,
  250. op_mob: 36,
  251. safari: 8,
  252. ios_saf: 8,
  253. samsung: [5, 0],
  254. android: 49,
  255. and_qq: [10, 4],
  256. // baidu: Unknown support
  257. and_uc: [12, 12],
  258. kaios: [2, 5],
  259. node: [6, 0]
  260. }),
  261. bigIntLiteral: rawChecker({
  262. chrome: 67,
  263. and_chr: 67,
  264. edge: 79,
  265. firefox: 68,
  266. and_ff: 68,
  267. // ie: Not supported,
  268. opera: 54,
  269. op_mob: 48,
  270. safari: 14,
  271. ios_saf: 14,
  272. samsung: [9, 2],
  273. android: 67,
  274. and_qq: [13, 1],
  275. baidu: [13, 18],
  276. and_uc: [15, 5],
  277. kaios: [3, 0],
  278. node: [10, 4]
  279. }),
  280. // Support syntax `import` and `export` and no limitations and bugs on Node.js
  281. // Not include `export * as namespace`
  282. module: rawChecker({
  283. chrome: 61,
  284. and_chr: 61,
  285. edge: 16,
  286. firefox: 60,
  287. and_ff: 60,
  288. // ie: Not supported,
  289. opera: 48,
  290. op_mob: 45,
  291. safari: [10, 1],
  292. ios_saf: [10, 3],
  293. samsung: [8, 0],
  294. android: 61,
  295. and_qq: [10, 4],
  296. baidu: [13, 18],
  297. and_uc: [15, 5],
  298. kaios: [3, 0],
  299. node: [12, 17]
  300. }),
  301. dynamicImport: rawChecker({
  302. chrome: 63,
  303. and_chr: 63,
  304. edge: 79,
  305. firefox: 67,
  306. and_ff: 67,
  307. // ie: Not supported
  308. opera: 50,
  309. op_mob: 46,
  310. safari: [11, 1],
  311. ios_saf: [11, 3],
  312. samsung: [8, 2],
  313. android: 63,
  314. and_qq: [10, 4],
  315. baidu: [13, 18],
  316. and_uc: [15, 5],
  317. kaios: [3, 0],
  318. node: [12, 17]
  319. }),
  320. dynamicImportInWorker: rawChecker({
  321. chrome: 80,
  322. and_chr: 80,
  323. edge: 80,
  324. firefox: 114,
  325. and_ff: 114,
  326. // ie: Not supported
  327. opera: 67,
  328. op_mob: 57,
  329. safari: [15, 0],
  330. ios_saf: [15, 0],
  331. samsung: [13, 0],
  332. android: 80,
  333. and_qq: [10, 4],
  334. baidu: [13, 18],
  335. and_uc: [15, 5],
  336. kaios: [3, 0],
  337. node: [12, 17]
  338. }),
  339. // browserslist does not have info about globalThis
  340. // so this is based on mdn-browser-compat-data
  341. globalThis: rawChecker({
  342. chrome: 71,
  343. and_chr: 71,
  344. edge: 79,
  345. firefox: 65,
  346. and_ff: 65,
  347. // ie: Not supported,
  348. opera: 58,
  349. op_mob: 50,
  350. safari: [12, 1],
  351. ios_saf: [12, 2],
  352. samsung: [10, 1],
  353. android: 71,
  354. and_qq: [13, 1],
  355. // baidu: Unknown support
  356. and_uc: [15, 5],
  357. kaios: [3, 0],
  358. node: 12
  359. }),
  360. optionalChaining: rawChecker({
  361. chrome: 80,
  362. and_chr: 80,
  363. edge: 80,
  364. firefox: 74,
  365. and_ff: 79,
  366. // ie: Not supported,
  367. opera: 67,
  368. op_mob: 64,
  369. safari: [13, 1],
  370. ios_saf: [13, 4],
  371. samsung: 13,
  372. android: 80,
  373. and_qq: [13, 1],
  374. // baidu: Not supported
  375. and_uc: [15, 5],
  376. kaios: [3, 0],
  377. node: 14
  378. }),
  379. symbol: rawChecker({
  380. chrome: 38,
  381. and_chr: 38,
  382. edge: 12,
  383. firefox: 36,
  384. and_ff: 36,
  385. // ie: Not supported,
  386. opera: 25,
  387. op_mob: 25,
  388. safari: 9,
  389. ios_saf: 9,
  390. samsung: [3, 0],
  391. android: 38,
  392. and_qq: [10, 4],
  393. and_uc: [12, 12],
  394. kaios: [2, 5],
  395. node: [0, 12]
  396. }),
  397. hasOwn: rawChecker({
  398. chrome: 93,
  399. and_chr: 93,
  400. edge: 93,
  401. firefox: 92,
  402. and_ff: 92,
  403. // ie: Not supported,
  404. opera: 79,
  405. op_mob: 66,
  406. safari: [15, 4],
  407. ios_saf: [15, 4],
  408. samsung: 17,
  409. android: 93,
  410. // and_qq: Not supported
  411. // baidu: Not supported
  412. // and_uc: Not supported
  413. // kaios: Not supported
  414. node: [16, 9]
  415. }),
  416. spread: rawChecker({
  417. chrome: 60,
  418. and_chr: 60,
  419. edge: 79,
  420. firefox: 55,
  421. and_ff: 55,
  422. // ie: Not supported,
  423. opera: 47,
  424. op_mob: 44,
  425. safari: [11, 1],
  426. ios_saf: [11, 3],
  427. samsung: [8, 2],
  428. android: 60,
  429. and_qq: [13, 1],
  430. // baidu: Not supported
  431. and_uc: [15, 5],
  432. kaios: [3, 0],
  433. node: [8, 3]
  434. }),
  435. templateLiteral: rawChecker({
  436. chrome: 41,
  437. and_chr: 41,
  438. edge: 13,
  439. firefox: 34,
  440. and_ff: 34,
  441. // ie: Not supported,
  442. opera: 29,
  443. op_mob: 64,
  444. safari: [9, 1],
  445. ios_saf: 9,
  446. samsung: 4,
  447. android: 41,
  448. and_qq: [10, 4],
  449. baidu: [7, 12],
  450. and_uc: [12, 12],
  451. kaios: [2, 5],
  452. node: 4
  453. }),
  454. asyncFunction: rawChecker({
  455. chrome: 55,
  456. and_chr: 55,
  457. edge: 15,
  458. firefox: 52,
  459. and_ff: 52,
  460. // ie: Not supported,
  461. opera: 42,
  462. op_mob: 42,
  463. safari: 11,
  464. ios_saf: 11,
  465. samsung: [6, 2],
  466. android: 55,
  467. and_qq: [10, 4],
  468. baidu: [13, 18],
  469. and_uc: [12, 12],
  470. kaios: 3,
  471. node: [7, 6]
  472. }),
  473. generator: rawChecker({
  474. chrome: 39,
  475. and_chr: 39,
  476. edge: 13,
  477. firefox: 26,
  478. and_ff: 26,
  479. // ie: Not supported,
  480. opera: 26,
  481. op_mob: 26,
  482. safari: 10,
  483. ios_saf: 10,
  484. samsung: [3, 4],
  485. android: 39,
  486. and_qq: [10, 4],
  487. baidu: [7, 12],
  488. and_uc: [12, 12],
  489. kaios: [2, 5],
  490. node: 4
  491. }),
  492. /* eslint-enable camelcase */
  493. browser: browserProperty,
  494. electron: false,
  495. node: nodeProperty,
  496. nwjs: false,
  497. web: browserProperty,
  498. webworker: false,
  499. document: browserProperty,
  500. modulePreload:
  501. browserProperty &&
  502. /* eslint-disable camelcase */
  503. rawChecker({
  504. chrome: 66,
  505. and_chr: 66,
  506. edge: 79,
  507. firefox: 115,
  508. and_ff: 115,
  509. opera: 53,
  510. op_mob: 47,
  511. safari: 17,
  512. ios_saf: 17,
  513. samsung: [9, 0],
  514. android: 66,
  515. and_qq: [13, 1],
  516. baidu: [13, 18],
  517. and_uc: [15, 5],
  518. kaios: [3, 1]
  519. }),
  520. /* eslint-enable camelcase */
  521. fetchWasm: browserProperty,
  522. global: nodeProperty,
  523. importScripts: false,
  524. importScriptsInWorker: Boolean(browserProperty),
  525. nodeBuiltins: nodeProperty,
  526. nodePrefixForCoreModules:
  527. nodeProperty &&
  528. !browsers.some((b) => b.startsWith("node 15")) &&
  529. rawChecker({
  530. node: [14, 18]
  531. }),
  532. importMetaDirnameAndFilename:
  533. nodeProperty &&
  534. rawChecker({
  535. node: [22, 16]
  536. }),
  537. nodeBuiltinModuleGetter:
  538. nodeProperty &&
  539. rawChecker({
  540. node: [22, 3]
  541. }),
  542. require: nodeProperty
  543. };
  544. };
  545. module.exports = {
  546. load,
  547. resolve
  548. };