no-unicode-codepoint-escapes.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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. schema: [],
  29. messages: {
  30. forbidden:
  31. "ES2015 Unicode code point escape sequences are forbidden.",
  32. },
  33. },
  34. create(context) {
  35. const sourceCode = context.getSourceCode()
  36. /**
  37. * find code point escape, and report
  38. * @param {string} text text
  39. * @param {Node} node node
  40. * @returns {void}
  41. */
  42. function findAndReport(text, node) {
  43. for (const match of codePointEscapeSearchGenerator(text)) {
  44. const start = match.index
  45. const end = start + match[0].length
  46. const range = [start + node.start, end + node.start]
  47. context.report({
  48. node,
  49. loc: {
  50. start: sourceCode.getLocFromIndex(range[0]),
  51. end: sourceCode.getLocFromIndex(range[1]),
  52. },
  53. messageId: "forbidden",
  54. fix(fixer) {
  55. const codePointStr = text.slice(start + 3, end - 1)
  56. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
  57. let codePoint = Number(`0x${codePointStr}`)
  58. let replacement = null
  59. if (codePoint <= 0xffff) {
  60. // BMP code point
  61. replacement = toHex(codePoint)
  62. } else {
  63. // Astral code point; split in surrogate halves
  64. // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  65. codePoint -= 0x10000
  66. const highSurrogate = (codePoint >> 10) + 0xd800
  67. const lowSurrogate = (codePoint % 0x400) + 0xdc00
  68. replacement = `${toHex(highSurrogate)}\\u${toHex(
  69. lowSurrogate
  70. )}`
  71. }
  72. return fixer.replaceTextRange(
  73. [range[0] + 2, range[1]],
  74. replacement
  75. )
  76. },
  77. })
  78. }
  79. }
  80. return {
  81. Identifier(node) {
  82. findAndReport(sourceCode.getText(node), node)
  83. },
  84. Literal(node) {
  85. if (typeof node.value === "string") {
  86. findAndReport(node.raw, node)
  87. }
  88. },
  89. TemplateElement(elementNode) {
  90. findAndReport(elementNode.value.raw, elementNode)
  91. },
  92. }
  93. },
  94. }