sort-vars.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * @fileoverview Rule to require sorting of variables within a single Variable Declaration block
  3. * @author Ilya Volodin
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. /** @type {import('../shared/types').Rule} */
  10. module.exports = {
  11. meta: {
  12. type: "suggestion",
  13. defaultOptions: [{
  14. ignoreCase: false
  15. }],
  16. docs: {
  17. description: "Require variables within the same declaration block to be sorted",
  18. recommended: false,
  19. url: "https://eslint.org/docs/latest/rules/sort-vars"
  20. },
  21. schema: [
  22. {
  23. type: "object",
  24. properties: {
  25. ignoreCase: {
  26. type: "boolean"
  27. }
  28. },
  29. additionalProperties: false
  30. }
  31. ],
  32. fixable: "code",
  33. messages: {
  34. sortVars: "Variables within the same declaration block should be sorted alphabetically."
  35. }
  36. },
  37. create(context) {
  38. const [{ ignoreCase }] = context.options;
  39. const sourceCode = context.sourceCode;
  40. return {
  41. VariableDeclaration(node) {
  42. const idDeclarations = node.declarations.filter(decl => decl.id.type === "Identifier");
  43. const getSortableName = ignoreCase ? decl => decl.id.name.toLowerCase() : decl => decl.id.name;
  44. const unfixable = idDeclarations.some(decl => decl.init !== null && decl.init.type !== "Literal");
  45. let fixed = false;
  46. idDeclarations.slice(1).reduce((memo, decl) => {
  47. const lastVariableName = getSortableName(memo),
  48. currentVariableName = getSortableName(decl);
  49. if (currentVariableName < lastVariableName) {
  50. context.report({
  51. node: decl,
  52. messageId: "sortVars",
  53. fix(fixer) {
  54. if (unfixable || fixed) {
  55. return null;
  56. }
  57. return fixer.replaceTextRange(
  58. [idDeclarations[0].range[0], idDeclarations.at(-1).range[1]],
  59. idDeclarations
  60. // Clone the idDeclarations array to avoid mutating it
  61. .slice()
  62. // Sort the array into the desired order
  63. .sort((declA, declB) => {
  64. const aName = getSortableName(declA);
  65. const bName = getSortableName(declB);
  66. return aName > bName ? 1 : -1;
  67. })
  68. // Build a string out of the sorted list of identifier declarations and the text between the originals
  69. .reduce((sourceText, identifier, index) => {
  70. const textAfterIdentifier = index === idDeclarations.length - 1
  71. ? ""
  72. : sourceCode.getText().slice(idDeclarations[index].range[1], idDeclarations[index + 1].range[0]);
  73. return sourceText + sourceCode.getText(identifier) + textAfterIdentifier;
  74. }, "")
  75. );
  76. }
  77. });
  78. fixed = true;
  79. return memo;
  80. }
  81. return decl;
  82. }, idDeclarations[0]);
  83. }
  84. };
  85. }
  86. };