no-unicode-codepoint-escapes.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * @author Toru Nagashima <https://github.com/mysticatea>
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const { definePatternSearchGenerator } = require("../utils")
  7. const codePointEscapeSearchGenerator = definePatternSearchGenerator(
  8. /\\u\{[0-9a-fA-F]+\}/gu
  9. )
  10. /**
  11. * Number to Hex
  12. * @param {number} num number
  13. * @returns {string} hex string
  14. */
  15. function toHex(num) {
  16. return `0000${num.toString(16).toUpperCase()}`.substr(-4)
  17. }
  18. module.exports = {
  19. meta: {
  20. docs: {
  21. description: "disallow Unicode code point escape sequences.",
  22. category: "ES2015",
  23. recommended: false,
  24. url:
  25. "http://mysticatea.github.io/eslint-plugin-es/rules/no-unicode-codepoint-escapes.html",
  26. },
  27. fixable: "code",
  28. messages: {
  29. forbidden:
  30. "ES2015 Unicode code point escape sequences are forbidden.",
  31. },
  32. schema: [],
  33. type: "problem",
  34. },
  35. create(context) {
  36. const sourceCode = context.getSourceCode()
  37. /**
  38. * find code point escape, and report
  39. * @param {Node} node node
  40. * @returns {void}
  41. */
  42. function findAndReport(node) {
  43. const text = sourceCode.getText(node)
  44. for (const match of codePointEscapeSearchGenerator(text)) {
  45. const start = match.index
  46. const end = start + match[0].length
  47. const range = [start + node.range[0], end + node.range[0]]
  48. context.report({
  49. node,
  50. loc: {
  51. start: sourceCode.getLocFromIndex(range[0]),
  52. end: sourceCode.getLocFromIndex(range[1]),
  53. },
  54. messageId: "forbidden",
  55. fix(fixer) {
  56. const codePointStr = text.slice(start + 3, end - 1)
  57. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
  58. let codePoint = Number(`0x${codePointStr}`)
  59. let replacement = null
  60. if (codePoint <= 0xffff) {
  61. // BMP code point
  62. replacement = toHex(codePoint)
  63. } else {
  64. // Astral code point; split in surrogate halves
  65. // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  66. codePoint -= 0x10000
  67. const highSurrogate = (codePoint >> 10) + 0xd800
  68. const lowSurrogate = (codePoint % 0x400) + 0xdc00
  69. replacement = `${toHex(highSurrogate)}\\u${toHex(
  70. lowSurrogate
  71. )}`
  72. }
  73. return fixer.replaceTextRange(
  74. [range[0] + 2, range[1]],
  75. replacement
  76. )
  77. },
  78. })
  79. }
  80. }
  81. return {
  82. Identifier(node) {
  83. findAndReport(node)
  84. },
  85. Literal(node) {
  86. if (typeof node.value === "string") {
  87. findAndReport(node)
  88. }
  89. },
  90. TemplateElement(node) {
  91. findAndReport(node)
  92. },
  93. }
  94. },
  95. }