codemod-fix-backslash-escapes.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. /**
  2. * codemod for fixing backslash \escapes to quote 'escapes' in MessageFormat strings
  3. *
  4. * messageformat-parser v3 (used by messageformat v2) no longer allows for the
  5. * characters #{}\ to be escaped with a \ prefix, as well as dropping support
  6. * for \u0123 character escapes. This codemod can help fix your MessageFormat
  7. * JSON sources to use ICU MessageFormat 'escapes' instead.
  8. *
  9. * To enable jscodeshift to handle JSON input, you'll need to have an
  10. * appropriate parser available:
  11. *
  12. * npm install --no-save json-estree-ast
  13. *
  14. * Then apply the codemod:
  15. *
  16. * npx jscodeshift -t node_modules/messageformat-parser/codemod-fix-backslash-escapes.js [input]
  17. *
  18. * If your input includes doubled single quotes '', they will need to be
  19. * escaped as well; use the command-line option --doubleSingleQuotes=true to
  20. * enable that. Note that applying the codemod with that option multiple times
  21. * will double your doubled quotes each time.
  22. */
  23. let doubleSingleQuotes = false;
  24. const fixEscapes = node => {
  25. if (node.type !== 'Literal' || typeof node.value !== 'string') return;
  26. if (doubleSingleQuotes) node.value = node.value.replace(/''+/g, '$&$&');
  27. node.value = node.value.replace(
  28. /('*)\\([#{}\\]|u[0-9a-f]{4})('*)/g,
  29. (_, start, char, end) => {
  30. switch (char[0]) {
  31. case 'u': {
  32. const code = parseInt(char.slice(1), 16);
  33. return start + String.fromCharCode(code) + end;
  34. }
  35. case '\\':
  36. return `${start}\\${end}`;
  37. default:
  38. // Assume multiple ' are already escaped
  39. if (start === "'") start = "''";
  40. if (end === "'") end = "''";
  41. return `'${start}${char}${end}'`;
  42. }
  43. }
  44. );
  45. };
  46. module.exports = ({ source }, { jscodeshift: j }, options) => {
  47. if (options.doubleSingleQuotes) doubleSingleQuotes = true;
  48. const ast = j(source);
  49. ast.find(j.Property).forEach(({ value: { value } }) => fixEscapes(value));
  50. ast
  51. .find(j.ArrayExpression)
  52. .forEach(({ value: { elements } }) => elements.forEach(fixEscapes));
  53. return ast.toSource();
  54. };
  55. module.exports.parser = require('json-estree-ast');