dot-location.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /**
  2. * @fileoverview Validates newlines before and after dots
  3. * @author Greg Cochard
  4. */
  5. "use strict";
  6. const astUtils = require("./utils/ast-utils");
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. module.exports = {
  11. meta: {
  12. type: "layout",
  13. docs: {
  14. description: "enforce consistent newlines before and after dots",
  15. category: "Best Practices",
  16. recommended: false,
  17. url: "https://eslint.org/docs/rules/dot-location"
  18. },
  19. schema: [
  20. {
  21. enum: ["object", "property"]
  22. }
  23. ],
  24. fixable: "code",
  25. messages: {
  26. expectedDotAfterObject: "Expected dot to be on same line as object.",
  27. expectedDotBeforeProperty: "Expected dot to be on same line as property."
  28. }
  29. },
  30. create(context) {
  31. const config = context.options[0];
  32. // default to onObject if no preference is passed
  33. const onObject = config === "object" || !config;
  34. const sourceCode = context.getSourceCode();
  35. /**
  36. * Reports if the dot between object and property is on the correct loccation.
  37. * @param {ASTNode} node The `MemberExpression` node.
  38. * @returns {void}
  39. */
  40. function checkDotLocation(node) {
  41. const property = node.property;
  42. const dot = sourceCode.getTokenBefore(property);
  43. // `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
  44. const tokenBeforeDot = sourceCode.getTokenBefore(dot);
  45. const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
  46. const textAfterDot = sourceCode.getText().slice(dot.range[1], property.range[0]);
  47. if (onObject) {
  48. if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
  49. const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
  50. context.report({
  51. node,
  52. loc: dot.loc,
  53. messageId: "expectedDotAfterObject",
  54. fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
  55. });
  56. }
  57. } else if (!astUtils.isTokenOnSameLine(dot, property)) {
  58. context.report({
  59. node,
  60. loc: dot.loc,
  61. messageId: "expectedDotBeforeProperty",
  62. fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${textBeforeDot}${textAfterDot}.`)
  63. });
  64. }
  65. }
  66. /**
  67. * Checks the spacing of the dot within a member expression.
  68. * @param {ASTNode} node The node to check.
  69. * @returns {void}
  70. */
  71. function checkNode(node) {
  72. if (!node.computed) {
  73. checkDotLocation(node);
  74. }
  75. }
  76. return {
  77. MemberExpression: checkNode
  78. };
  79. }
  80. };