You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
2.7 KiB

4 years ago
  1. 'use strict';
  2. const { coerce, lt, lte } = require('semver');
  3. const browserslist = require('browserslist');
  4. const data = require('./data');
  5. const getModulesListForTargetVersion = require('./get-modules-list-for-target-version');
  6. const has = Function.call.bind({}.hasOwnProperty);
  7. const mapping = new Map([
  8. ['ios_saf', 'ios'],
  9. ['and_chr', 'chrome'],
  10. ['and_ff', 'firefox'],
  11. ]);
  12. const validTargets = new Set([
  13. 'android',
  14. 'chrome',
  15. 'edge',
  16. 'electron',
  17. 'firefox',
  18. 'ie',
  19. 'ios',
  20. 'node',
  21. 'opera',
  22. 'phantom',
  23. 'safari',
  24. 'samsung',
  25. ]);
  26. function coercedLte(a, b) {
  27. return lte(coerce(a), coerce(b));
  28. }
  29. function coercedLt(a, b) {
  30. return lt(coerce(a), coerce(b));
  31. }
  32. function normalizeBrowsersList(list) {
  33. return list.map(it => {
  34. let [engine, version] = it.split(' ');
  35. if (mapping.has(engine)) engine = mapping.get(engine);
  36. else if (engine === 'android' && !coercedLte(version, '4.4.4')) engine = 'chrome';
  37. return [engine, version];
  38. }).filter(([engine]) => validTargets.has(engine));
  39. }
  40. function reduceByMinVersion(list) {
  41. const targets = new Map();
  42. for (const [engine, version] of list) {
  43. if (!targets.has(engine) || coercedLte(version, targets.get(engine))) {
  44. targets.set(engine, version);
  45. }
  46. }
  47. return targets;
  48. }
  49. function checkModule(name, targets) {
  50. if (!has(data, name)) throw new TypeError(`Incorrect module: ${ name }`);
  51. const requirements = data[name];
  52. const result = {
  53. required: false,
  54. targets: {},
  55. };
  56. for (const [engine, version] of targets) {
  57. if (!has(requirements, engine) || coercedLt(version, requirements[engine])) {
  58. result.required = true;
  59. result.targets[engine] = version;
  60. }
  61. }
  62. return result;
  63. }
  64. function compat({ targets, filter, version }) {
  65. const list = browserslist(targets);
  66. const engines = normalizeBrowsersList(list);
  67. const reducedTargets = reduceByMinVersion(engines);
  68. const result = {
  69. list: [],
  70. targets: {},
  71. };
  72. let modules = Array.isArray(filter) ? filter : Object.keys(data);
  73. if (filter instanceof RegExp) modules = modules.filter(it => filter.test(it));
  74. else if (typeof filter == 'string') modules = modules.filter(it => it.startsWith(filter));
  75. if (version) {
  76. const availableModules = new Set(getModulesListForTargetVersion(version));
  77. modules = modules.filter(name => availableModules.has(name));
  78. }
  79. modules.forEach(key => {
  80. const check = checkModule(key, reducedTargets);
  81. if (check.required) {
  82. result.list.push(key);
  83. result.targets[key] = check.targets;
  84. }
  85. });
  86. return result;
  87. }
  88. module.exports = compat;
  89. module.exports.compat = compat;
  90. module.exports.getModulesListForTargetVersion = getModulesListForTargetVersion;