sort-vars.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. },
  32. create(context) {
  33. const configuration = context.options[0] || {},
  34. ignoreCase = configuration.ignoreCase || false,
  35. sourceCode = context.getSourceCode();
  36. return {
  37. VariableDeclaration(node) {
  38. const idDeclarations = node.declarations.filter(decl => decl.id.type === "Identifier");
  39. const getSortableName = ignoreCase ? decl => decl.id.name.toLowerCase() : decl => decl.id.name;
  40. const unfixable = idDeclarations.some(decl => decl.init !== null && decl.init.type !== "Literal");
  41. let fixed = false;
  42. idDeclarations.slice(1).reduce((memo, decl) => {
  43. const lastVariableName = getSortableName(memo),
  44. currentVariableName = getSortableName(decl);
  45. if (currentVariableName < lastVariableName) {
  46. context.report({
  47. node: decl,
  48. message: "Variables within the same declaration block should be sorted alphabetically.",
  49. fix(fixer) {
  50. if (unfixable || fixed) {
  51. return null;
  52. }
  53. return fixer.replaceTextRange(
  54. [idDeclarations[0].range[0], idDeclarations[idDeclarations.length - 1].range[1]],
  55. idDeclarations
  56. // Clone the idDeclarations array to avoid mutating it
  57. .slice()
  58. // Sort the array into the desired order
  59. .sort((declA, declB) => {
  60. const aName = getSortableName(declA);
  61. const bName = getSortableName(declB);
  62. return aName > bName ? 1 : -1;
  63. })
  64. // Build a string out of the sorted list of identifier declarations and the text between the originals
  65. .reduce((sourceText, identifier, index) => {
  66. const textAfterIdentifier = index === idDeclarations.length - 1
  67. ? ""
  68. : sourceCode.getText().slice(idDeclarations[index].range[1], idDeclarations[index + 1].range[0]);
  69. return sourceText + sourceCode.getText(identifier) + textAfterIdentifier;
  70. }, "")
  71. );
  72. }
  73. });
  74. fixed = true;
  75. return memo;
  76. }
  77. return decl;
  78. }, idDeclarations[0]);
  79. }
  80. };
  81. }
  82. };