check-unsupported-builtins.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /**
  2. * @author Toru Nagashima
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const { Range, lt, major } = require("semver") //eslint-disable-line no-unused-vars
  7. const { ReferenceTracker } = require("eslint-utils")
  8. const getConfiguredNodeVersion = require("./get-configured-node-version")
  9. const getSemverRange = require("./get-semver-range")
  10. /**
  11. * @typedef {Object} SupportInfo
  12. * @property {string | null} supported The stably supported version. If `null` is present, it hasn't been supported yet.
  13. * @property {string[]} [backported] The backported versions.
  14. * @property {string} [experimental] The added version as experimental.
  15. */
  16. /**
  17. * Parses the options.
  18. * @param {RuleContext} context The rule context.
  19. * @returns {{version:Range,ignores:Set<string>}} Parsed value.
  20. */
  21. function parseOptions(context) {
  22. const raw = context.options[0] || {}
  23. const filePath = context.getFilename()
  24. const version = getConfiguredNodeVersion(raw.version, filePath)
  25. const ignores = new Set(raw.ignores || [])
  26. return Object.freeze({ version, ignores })
  27. }
  28. /**
  29. * Check if it has been supported.
  30. * @param {SupportInfo} info The support info.
  31. * @param {Range} configured The configured version range.
  32. */
  33. function isSupported({ backported, supported }, configured) {
  34. if (
  35. backported &&
  36. backported.length >= 2 &&
  37. !backported.every((v, i) => i === 0 || lt(backported[i - 1], v))
  38. ) {
  39. throw new Error("Invalid BackportConfiguration")
  40. }
  41. if (supported == null) {
  42. return false
  43. }
  44. if (backported == null || backported.length === 0) {
  45. return !configured.intersects(getSemverRange(`<${supported}`))
  46. }
  47. return !configured.intersects(
  48. getSemverRange(
  49. [...backported, supported]
  50. .map((v, i) => (i === 0 ? `<${v}` : `>=${major(v)}.0.0 <${v}`))
  51. .join(" || ")
  52. )
  53. )
  54. }
  55. /**
  56. * Get the formatted text of a given supported version.
  57. * @param {SupportInfo} info The support info.
  58. */
  59. function supportedVersionToString({ backported, supported }) {
  60. if (supported == null) {
  61. return "(none yet)"
  62. }
  63. if (backported == null || backported.length === 0) {
  64. return supported
  65. }
  66. return `${supported} (backported: ^${backported.join(", ^")})`
  67. }
  68. /**
  69. * Verify the code to report unsupported APIs.
  70. * @param {RuleContext} context The rule context.
  71. * @param {{modules:object,globals:object}} trackMap The map for APIs to report.
  72. * @returns {void}
  73. */
  74. module.exports = function checkUnsupportedBuiltins(context, trackMap) {
  75. const options = parseOptions(context)
  76. const tracker = new ReferenceTracker(context.getScope(), { mode: "legacy" })
  77. const references = [
  78. ...tracker.iterateCjsReferences(trackMap.modules || {}),
  79. ...tracker.iterateEsmReferences(trackMap.modules || {}),
  80. ...tracker.iterateGlobalReferences(trackMap.globals || {}),
  81. ]
  82. for (const { node, path, info } of references) {
  83. const name = path.join(".")
  84. const supported = isSupported(info, options.version)
  85. if (!supported && !options.ignores.has(name)) {
  86. context.report({
  87. node,
  88. messageId: "unsupported",
  89. data: {
  90. name,
  91. supported: supportedVersionToString(info),
  92. version: options.version.raw,
  93. },
  94. })
  95. }
  96. }
  97. }