target.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const memoize = require("../util/memoize");
  7. // browserslist pulls in caniuse-lite; only a browserslist target needs it
  8. const getBrowserslistTargetHandler = memoize(() =>
  9. require("./browserslistTargetHandler")
  10. );
  11. /**
  12. * Gets default target.
  13. * @param {string} context the context directory
  14. * @returns {string} default target
  15. */
  16. const getDefaultTarget = (context) => {
  17. const browsers = getBrowserslistTargetHandler().load(undefined, context);
  18. return browsers ? "browserslist" : "web";
  19. };
  20. /**
  21. * Defines the platform target properties type used by this module.
  22. * @typedef {object} PlatformTargetProperties
  23. * @property {boolean | null=} web web platform, importing of http(s) and std: is available
  24. * @property {boolean | null=} browser browser platform, running in a normal web browser
  25. * @property {boolean | null=} webworker (Web)Worker platform, running in a web/shared/service worker
  26. * @property {boolean | null=} node node platform, require of node built-in modules is available
  27. * @property {boolean | null=} deno deno platform, node built-in modules (via the `node:` specifier) and web APIs are available
  28. * @property {boolean | null=} bun bun platform, bun and node built-in modules are available
  29. * @property {boolean | null=} nwjs nwjs platform, require of legacy nw.gui is available
  30. * @property {boolean | null=} electron electron platform, require of some electron built-in modules is available
  31. * @property {boolean | null=} universal universal ESM target spanning both web and node (target `"universal"` or `["web", "node"]`)
  32. */
  33. /**
  34. * Defines the electron context target properties type used by this module.
  35. * @typedef {object} ElectronContextTargetProperties
  36. * @property {boolean | null} electronMain in main context
  37. * @property {boolean | null} electronPreload in preload context
  38. * @property {boolean | null} electronRenderer in renderer context with node integration
  39. */
  40. /**
  41. * Defines the api target properties type used by this module.
  42. * @typedef {object} ApiTargetProperties
  43. * @property {boolean | null} require has require function available
  44. * @property {boolean | null} nodeBuiltins has node.js built-in modules available
  45. * @property {boolean | null} nodePrefixForCoreModules node.js allows to use `node:` prefix for core modules
  46. * @property {boolean | null} nodeBuiltinModuleGetter node.js exposes `process.getBuiltinModule()` to synchronously load core modules
  47. * @property {boolean | null} importMetaDirnameAndFilename node.js allows to use `import.meta.dirname` and `import.meta.filename`
  48. * @property {boolean | null} document has document available (allows script tags)
  49. * @property {(boolean | null)=} modulePreload supports `<link rel="modulepreload">` (optional; absent means "assume supported")
  50. * @property {boolean | null} importScripts has importScripts available
  51. * @property {boolean | null} importScriptsInWorker has importScripts available when creating a worker
  52. * @property {boolean | null} fetchWasm has fetch function available for WebAssembly
  53. * @property {boolean | null} global has global variable available
  54. */
  55. /**
  56. * Defines the ecma target properties type used by this module.
  57. * @typedef {object} EcmaTargetProperties
  58. * @property {boolean | null} globalThis has globalThis variable available
  59. * @property {boolean | null} symbol `Symbol` is available
  60. * @property {boolean | null} hasOwn `Object.hasOwn` is available
  61. * @property {boolean | null} bigIntLiteral big int literal syntax is available
  62. * @property {boolean | null} const const variable declarations are available
  63. * @property {boolean | null} let let variable declarations are available
  64. * @property {boolean | null} logicalAssignment logical assignment operators (`||=`, `&&=`, `??=`) are available
  65. * @property {boolean | null} methodShorthand object method shorthand is available
  66. * @property {boolean | null} arrowFunction arrow functions are available
  67. * @property {boolean | null} forOf for of iteration is available
  68. * @property {boolean | null} destructuring destructuring is available
  69. * @property {boolean | null} dynamicImport async import() is available
  70. * @property {boolean | null} dynamicImportInWorker async import() is available when creating a worker
  71. * @property {boolean | null} module ESM syntax is available (when in module)
  72. * @property {boolean | null} optionalChaining optional chaining is available
  73. * @property {boolean | null} spread spread and rest in array/object literals and calls is available
  74. * @property {boolean | null} templateLiteral template literal is available
  75. * @property {boolean | null} asyncFunction async functions and await are available
  76. * @property {boolean | null} generator generator functions and yield are available
  77. * @property {(boolean | null)=} deferImport deferred evaluation (`import defer`, `import.defer()`) is available (optional; absent means "assume unavailable")
  78. * @property {(boolean | null)=} sourceImport source phase imports (`import source`, `import.source()`) are available (optional; absent means "assume unavailable")
  79. */
  80. /**
  81. * Defines the shared type used by this module.
  82. * @template T
  83. * @typedef {{ [P in keyof T]?: never }} Never<T>
  84. */
  85. /**
  86. * Defines the shared type used by this module.
  87. * @template A
  88. * @template B
  89. * @typedef {(A & Never<B>) | (Never<A> & B) | (A & B)} Mix<A, B>
  90. */
  91. /** @typedef {Mix<Mix<PlatformTargetProperties, ElectronContextTargetProperties>, Mix<ApiTargetProperties, EcmaTargetProperties>>} TargetProperties */
  92. /**
  93. * Returns check if version is greater or equal.
  94. * @param {string} major major version
  95. * @param {string | undefined} minor minor version
  96. * @returns {(vMajor: number, vMinor?: number) => boolean | undefined} check if version is greater or equal
  97. */
  98. const versionDependent = (major, minor) => {
  99. if (!major) {
  100. return () => /** @type {undefined} */ (undefined);
  101. }
  102. /** @type {number} */
  103. const nMajor = Number(major);
  104. /** @type {number} */
  105. const nMinor = minor ? Number(minor) : 0;
  106. return (vMajor, vMinor = 0) =>
  107. nMajor > vMajor || (nMajor === vMajor && nMinor >= vMinor);
  108. };
  109. // A `browserslist` target, with an optional `:env` / `:query` / config path
  110. // after the keyword that `browserslistTargetHandler.load` reads.
  111. const BROWSERSLIST_TARGET = /^browserslist(?::(.+))?$/;
  112. /** @type {[string, string, RegExp, (...args: string[]) => Partial<TargetProperties>][]} */
  113. const TARGETS = [
  114. [
  115. "browserslist / browserslist:env / browserslist:query / browserslist:path-to-config / browserslist:path-to-config:env",
  116. "Resolve features from browserslist. Will resolve browserslist config automatically. Only browser or node queries are supported (electron is not supported). Examples: 'browserslist:modern' to use 'modern' environment from browserslist config",
  117. BROWSERSLIST_TARGET,
  118. (rest, context) => {
  119. const browserslistTargetHandler = getBrowserslistTargetHandler();
  120. const browsers = browserslistTargetHandler.load(
  121. rest ? rest.trim() : null,
  122. context
  123. );
  124. if (!browsers) {
  125. throw new Error(`No browserslist config found to handle the 'browserslist' target.
  126. See https://github.com/browserslist/browserslist#queries for possible ways to provide a config.
  127. The recommended way is to add a 'browserslist' key to your package.json and list supported browsers (resp. node.js versions).
  128. You can also more options via the 'target' option: 'browserslist' / 'browserslist:env' / 'browserslist:query' / 'browserslist:path-to-config' / 'browserslist:path-to-config:env'`);
  129. }
  130. return browserslistTargetHandler.resolve(browsers);
  131. }
  132. ],
  133. [
  134. "web",
  135. "Web browser.",
  136. /^web$/,
  137. () => ({
  138. node: false,
  139. deno: false,
  140. bun: false,
  141. web: true,
  142. webworker: null,
  143. browser: true,
  144. electron: false,
  145. nwjs: false,
  146. document: true,
  147. importScriptsInWorker: true,
  148. fetchWasm: true,
  149. nodeBuiltins: false,
  150. importScripts: false,
  151. require: false,
  152. global: false
  153. })
  154. ],
  155. [
  156. "webworker",
  157. "Web Worker, SharedWorker or Service Worker.",
  158. /^webworker$/,
  159. () => ({
  160. node: false,
  161. deno: false,
  162. bun: false,
  163. web: true,
  164. webworker: true,
  165. browser: true,
  166. electron: false,
  167. nwjs: false,
  168. importScripts: true,
  169. importScriptsInWorker: true,
  170. fetchWasm: true,
  171. nodeBuiltins: false,
  172. require: false,
  173. document: false,
  174. global: false
  175. })
  176. ],
  177. [
  178. "universal",
  179. "Universal target running in browser, web worker, Node.js, Electron and NW.js. Output is always ECMAScript modules.",
  180. /^universal$/,
  181. // merged web + webworker + node + electron + nwjs properties; output is
  182. // always ESM, so `require` (sync CommonJS) is never available
  183. () => ({
  184. node: null,
  185. deno: null,
  186. bun: null,
  187. web: null,
  188. webworker: null,
  189. browser: null,
  190. electron: null,
  191. nwjs: null,
  192. electronMain: null,
  193. electronPreload: null,
  194. electronRenderer: null,
  195. document: null,
  196. importScriptsInWorker: null,
  197. fetchWasm: null,
  198. nodeBuiltins: null,
  199. importScripts: null,
  200. require: false,
  201. global: null,
  202. module: true,
  203. dynamicImport: true,
  204. dynamicImportInWorker: true
  205. })
  206. ],
  207. [
  208. "[async-]node[X[.Y]]",
  209. "Node.js in version X.Y. The 'async-' prefix will load chunks asynchronously via 'fs' and 'vm' instead of 'require()'. Examples: node14.5, async-node10.",
  210. /^(async-)?node((\d+)(?:\.(\d+))?)?$/,
  211. (asyncFlag, _, major, minor) => {
  212. const v = versionDependent(major, minor);
  213. // see https://node.green/
  214. return {
  215. node: true,
  216. deno: false,
  217. bun: false,
  218. web: false,
  219. webworker: false,
  220. browser: false,
  221. electron: false,
  222. nwjs: false,
  223. require: !asyncFlag,
  224. nodeBuiltins: true,
  225. // v16.0.0, v14.18.0
  226. nodePrefixForCoreModules: Number(major) < 15 ? v(14, 18) : v(16),
  227. // v22.3.0, backported to v20.16.0; use the conservative single threshold
  228. nodeBuiltinModuleGetter: v(22, 3),
  229. // Added in: v21.2.0, v20.11.0, but Node.js will output experimental warning, we don't want it
  230. // v24.0.0, v22.16.0 - This property is no longer experimental.
  231. importMetaDirnameAndFilename: v(22, 16),
  232. global: true,
  233. document: false,
  234. fetchWasm: false,
  235. importScripts: false,
  236. importScriptsInWorker: false,
  237. globalThis: v(12),
  238. symbol: v(0, 12),
  239. hasOwn: v(16, 9),
  240. const: v(6),
  241. let: v(6),
  242. logicalAssignment: v(15),
  243. templateLiteral: v(4),
  244. optionalChaining: v(14),
  245. spread: v(8, 3),
  246. methodShorthand: v(4),
  247. arrowFunction: v(6),
  248. asyncFunction: v(7, 6),
  249. generator: v(4),
  250. forOf: v(5),
  251. destructuring: v(6),
  252. bigIntLiteral: v(10, 4),
  253. dynamicImport: v(12, 17),
  254. dynamicImportInWorker: v(12, 17),
  255. module: v(12, 17),
  256. // v24.5.0, for WebAssembly only; `import defer` is not implemented
  257. sourceImport: v(24, 5)
  258. };
  259. }
  260. ],
  261. [
  262. "deno[X[.Y]]",
  263. "Deno in version X.Y. Emits ESM output; node.js built-ins are available via the required 'node:' specifier and web APIs (fetch, WebAssembly, ...) are available too. Examples: deno2, deno1.40.",
  264. /^deno((\d+)(?:\.(\d+))?)?$/,
  265. (_, major, minor) => {
  266. const v = versionDependent(major, minor);
  267. // Deno ships a modern V8, so syntax features are available since 1.0.
  268. return {
  269. node: true,
  270. deno: true,
  271. web: true,
  272. webworker: false,
  273. browser: false,
  274. electron: false,
  275. nwjs: false,
  276. require: false,
  277. nodeBuiltins: true,
  278. // Deno only resolves node.js core modules through the `node:` specifier
  279. nodePrefixForCoreModules: true,
  280. // process.getBuiltinModule() - v2.1.0
  281. nodeBuiltinModuleGetter: v(2, 1),
  282. // import.meta.dirname / import.meta.filename - v1.40.0
  283. importMetaDirnameAndFilename: v(1, 40),
  284. global: false,
  285. document: false,
  286. fetchWasm: true,
  287. importScripts: false,
  288. importScriptsInWorker: false,
  289. globalThis: true,
  290. symbol: true,
  291. hasOwn: true,
  292. bigIntLiteral: true,
  293. const: true,
  294. let: true,
  295. logicalAssignment: true,
  296. methodShorthand: true,
  297. arrowFunction: true,
  298. forOf: true,
  299. destructuring: true,
  300. dynamicImport: true,
  301. dynamicImportInWorker: true,
  302. module: true,
  303. optionalChaining: true,
  304. spread: true,
  305. templateLiteral: true,
  306. asyncFunction: true,
  307. generator: true,
  308. // import defer - v2.8.0
  309. deferImport: v(2, 8),
  310. // import source - v2.6.0
  311. sourceImport: v(2, 6)
  312. };
  313. }
  314. ],
  315. [
  316. "bun[X[.Y]]",
  317. "Bun in version X.Y. Output is always ECMAScript modules. Examples: bun, bun1.1.",
  318. /^bun((\d+)(?:\.(\d+))?)?$/,
  319. () => ({
  320. // Bun is a modern, Node.js-compatible runtime; every released version
  321. // supports the full modern ECMAScript feature set, so the version is
  322. // accepted but does not gate any feature. Output is always ESM, so sync
  323. // CommonJS `require` is never available.
  324. node: true,
  325. bun: true,
  326. web: false,
  327. webworker: false,
  328. browser: false,
  329. electron: false,
  330. nwjs: false,
  331. require: false,
  332. nodeBuiltins: true,
  333. nodePrefixForCoreModules: true,
  334. nodeBuiltinModuleGetter: true,
  335. importMetaDirnameAndFilename: true,
  336. // Bun implements web-standard globals; use `globalThis` and load wasm
  337. // via `fetch()` (Bun's fetch resolves `file:` URLs) like the deno target,
  338. // instead of proprietary `Bun.*` runtime APIs.
  339. global: false,
  340. document: false,
  341. fetchWasm: true,
  342. importScripts: false,
  343. importScriptsInWorker: false,
  344. globalThis: true,
  345. symbol: true,
  346. hasOwn: true,
  347. const: true,
  348. let: true,
  349. logicalAssignment: true,
  350. templateLiteral: true,
  351. optionalChaining: true,
  352. spread: true,
  353. methodShorthand: true,
  354. arrowFunction: true,
  355. asyncFunction: true,
  356. generator: true,
  357. forOf: true,
  358. destructuring: true,
  359. bigIntLiteral: true,
  360. dynamicImport: true,
  361. dynamicImportInWorker: true,
  362. module: true
  363. })
  364. ],
  365. [
  366. "electron[X[.Y]]-main/preload/renderer",
  367. "Electron in version X.Y. Script is running in main, preload resp. renderer context.",
  368. /^electron((\d+)(?:\.(\d+))?)?-(main|preload|renderer)$/,
  369. (_, major, minor, context) => {
  370. const v = versionDependent(major, minor);
  371. // see https://node.green/ + https://github.com/electron/releases
  372. return {
  373. node: true,
  374. deno: false,
  375. bun: false,
  376. web: context !== "main",
  377. webworker: false,
  378. browser: false,
  379. electron: true,
  380. nwjs: false,
  381. electronMain: context === "main",
  382. electronPreload: context === "preload",
  383. electronRenderer: context === "renderer",
  384. global: true,
  385. nodeBuiltins: true,
  386. // 15.0.0 - Node.js v16.5
  387. // 14.0.0 - Mode.js v14.17, but prefixes only since v14.18
  388. nodePrefixForCoreModules: v(15),
  389. // 37.0.0 - Node.js v22.16
  390. importMetaDirnameAndFilename: v(37),
  391. require: true,
  392. document: context === "renderer",
  393. fetchWasm: context === "renderer",
  394. importScripts: false,
  395. importScriptsInWorker: true,
  396. globalThis: v(5),
  397. symbol: v(0, 24),
  398. hasOwn: v(16),
  399. const: v(1, 1),
  400. let: v(1, 1),
  401. // 10.0.0 - Chromium 85
  402. logicalAssignment: v(10),
  403. templateLiteral: v(1, 1),
  404. optionalChaining: v(8),
  405. spread: v(2),
  406. methodShorthand: v(1, 1),
  407. arrowFunction: v(1, 1),
  408. asyncFunction: v(1, 7),
  409. generator: v(1, 1),
  410. forOf: v(0, 36),
  411. destructuring: v(1, 1),
  412. bigIntLiteral: v(4),
  413. dynamicImport: v(11),
  414. dynamicImportInWorker: v(11),
  415. // 28.0.0 - ESM support was added
  416. module: v(28)
  417. };
  418. }
  419. ],
  420. [
  421. "nwjs[X[.Y]] / node-webkit[X[.Y]]",
  422. "NW.js in version X.Y.",
  423. /^(?:nwjs|node-webkit)((\d+)(?:\.(\d+))?)?$/,
  424. (_, major, minor) => {
  425. const v = versionDependent(major, minor);
  426. // see https://node.green/ + https://github.com/nwjs/nw.js/blob/nw48/CHANGELOG.md
  427. return {
  428. node: true,
  429. deno: false,
  430. bun: false,
  431. web: true,
  432. webworker: null,
  433. browser: false,
  434. electron: false,
  435. nwjs: true,
  436. global: true,
  437. nodeBuiltins: true,
  438. document: false,
  439. importScriptsInWorker: false,
  440. fetchWasm: false,
  441. importScripts: false,
  442. require: false,
  443. globalThis: v(0, 43),
  444. symbol: v(0, 12),
  445. hasOwn: v(0, 58),
  446. const: v(0, 15),
  447. let: v(0, 15),
  448. // 0.48.0 - Chromium 85
  449. logicalAssignment: v(0, 48),
  450. templateLiteral: v(0, 13),
  451. optionalChaining: v(0, 44),
  452. spread: v(0, 23),
  453. methodShorthand: v(0, 15),
  454. arrowFunction: v(0, 15),
  455. asyncFunction: v(0, 21),
  456. generator: v(0, 15),
  457. forOf: v(0, 13),
  458. destructuring: v(0, 15),
  459. bigIntLiteral: v(0, 32),
  460. dynamicImport: v(0, 43),
  461. dynamicImportInWorker: v(0, 44),
  462. module: v(0, 43)
  463. };
  464. }
  465. ],
  466. [
  467. "esX",
  468. "EcmaScript in this version. Examples: es2020, es5.",
  469. /^es(\d+)$/,
  470. (version) => {
  471. let v = Number(version);
  472. if (v < 1000) v += 2009;
  473. return {
  474. const: v >= 2015,
  475. let: v >= 2015,
  476. logicalAssignment: v >= 2021,
  477. templateLiteral: v >= 2015,
  478. optionalChaining: v >= 2020,
  479. spread: v >= 2018,
  480. methodShorthand: v >= 2015,
  481. arrowFunction: v >= 2015,
  482. forOf: v >= 2015,
  483. destructuring: v >= 2015,
  484. module: v >= 2015,
  485. asyncFunction: v >= 2017,
  486. generator: v >= 2015,
  487. globalThis: v >= 2020,
  488. symbol: v >= 2015,
  489. hasOwn: v >= 2022,
  490. bigIntLiteral: v >= 2020,
  491. dynamicImport: v >= 2020,
  492. dynamicImportInWorker: v >= 2020
  493. };
  494. }
  495. ]
  496. ];
  497. /**
  498. * Gets target properties.
  499. * @param {string} target the target
  500. * @param {string} context the context directory
  501. * @returns {TargetProperties} target properties
  502. */
  503. const getTargetProperties = (target, context) => {
  504. for (const [, , regExp, handler] of TARGETS) {
  505. const match = regExp.exec(target);
  506. if (match) {
  507. const [, ...args] = match;
  508. const result = handler(...args, context);
  509. if (result) return /** @type {TargetProperties} */ (result);
  510. }
  511. }
  512. throw new Error(
  513. `Unknown target '${target}'. The following targets are supported:\n${TARGETS.map(
  514. ([name, description]) => `* ${name}: ${description}`
  515. ).join("\n")}`
  516. );
  517. };
  518. // Absence means "cannot read it" here, so a target omitting one votes against rather
  519. // than letting another's `true` stand — `["web", "deno2.8"]` reads neither phase.
  520. const UNSUPPORTED_WHEN_ABSENT = new Set(["deferImport", "sourceImport"]);
  521. /**
  522. * Merges target properties.
  523. * @param {TargetProperties[]} targetProperties array of target properties
  524. * @returns {TargetProperties} merged target properties
  525. */
  526. const mergeTargetProperties = (targetProperties) => {
  527. /** @type {Set<keyof TargetProperties>} */
  528. const keys = new Set();
  529. for (const tp of targetProperties) {
  530. for (const key of Object.keys(tp)) {
  531. keys.add(/** @type {keyof TargetProperties} */ (key));
  532. }
  533. }
  534. /** @type {TargetProperties} */
  535. const result = {};
  536. for (const key of keys) {
  537. let hasTrue = false;
  538. let hasFalse = false;
  539. for (const tp of targetProperties) {
  540. const value = tp[key];
  541. switch (value) {
  542. case true:
  543. hasTrue = true;
  544. break;
  545. case false:
  546. hasFalse = true;
  547. break;
  548. default:
  549. if (value === undefined && UNSUPPORTED_WHEN_ABSENT.has(key)) {
  550. hasFalse = true;
  551. }
  552. }
  553. }
  554. if (hasTrue || hasFalse) {
  555. /** @type {TargetProperties} */
  556. (result)[key] = hasFalse && hasTrue ? null : Boolean(hasTrue);
  557. }
  558. }
  559. return result;
  560. };
  561. /**
  562. * Gets targets properties.
  563. * @param {string[]} targets the targets
  564. * @param {string} context the context directory
  565. * @returns {TargetProperties} target properties
  566. */
  567. const getTargetsProperties = (targets, context) =>
  568. mergeTargetProperties(targets.map((t) => getTargetProperties(t, context)));
  569. /**
  570. * The raw browserslist selection behind the targets (`["chrome 100", …]`), merged
  571. * across every `browserslist` target and deduplicated — undefined when none is
  572. * one, and prefixing stays off with no browser list to prefix for.
  573. * @param {string[]} targets the targets
  574. * @param {string} context the context directory
  575. * @returns {string[] | undefined} the selected browsers, or undefined
  576. */
  577. const getTargetsBrowsers = (targets, context) => {
  578. const { load } = getBrowserslistTargetHandler();
  579. /** @type {Set<string>} */
  580. const browsers = new Set();
  581. for (const target of targets) {
  582. const match = BROWSERSLIST_TARGET.exec(target);
  583. if (!match) continue;
  584. const selected = load(match[1] ? match[1].trim() : null, context);
  585. if (selected) for (const browser of selected) browsers.add(browser);
  586. }
  587. return browsers.size === 0 ? undefined : [...browsers];
  588. };
  589. module.exports.getDefaultTarget = getDefaultTarget;
  590. module.exports.getTargetProperties = getTargetProperties;
  591. module.exports.getTargetsBrowsers = getTargetsBrowsers;
  592. module.exports.getTargetsProperties = getTargetsProperties;