sort-vars.js 4.0 KB

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