sort-vars.js 3.9 KB

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