2 * @fileoverview Rule to warn about using dot notation instead of square bracket notation when possible.
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const astUtils = require("./utils/ast-utils");
12 const keywords = require("./utils/keywords");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
20 // `null` literal must be handled separately.
21 const literalTypesToCheck = new Set(["string", "boolean"]);
28 description: "enforce dot notation whenever possible",
29 category: "Best Practices",
31 url: "https://eslint.org/docs/rules/dot-notation"
47 additionalProperties: false
54 useDot: "[{{key}}] is better written in dot notation.",
55 useBrackets: ".{{key}} is a syntax error."
60 const options = context.options[0] || {};
61 const allowKeywords = options.allowKeywords === void 0 || options.allowKeywords;
62 const sourceCode = context.getSourceCode();
66 if (options.allowPattern) {
67 allowPattern = new RegExp(options.allowPattern, "u");
71 * Check if the property is valid dot notation
72 * @param {ASTNode} node The dot notation node
73 * @param {string} value Value which is to be checked
76 function checkComputedProperty(node, value) {
78 validIdentifier.test(value) &&
79 (allowKeywords || keywords.indexOf(String(value)) === -1) &&
80 !(allowPattern && allowPattern.test(value))
82 const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
91 const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
92 const rightBracket = sourceCode.getLastToken(node);
94 if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
96 // Don't perform any fixes if there are comments inside the brackets.
100 const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
101 const needsSpaceAfterProperty = tokenAfterProperty &&
102 rightBracket.range[1] === tokenAfterProperty.range[0] &&
103 !astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
105 const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
106 const textAfterProperty = needsSpaceAfterProperty ? " " : "";
108 return fixer.replaceTextRange(
109 [leftBracket.range[0], rightBracket.range[1]],
110 `${textBeforeDot}.${value}${textAfterProperty}`
118 MemberExpression(node) {
121 node.property.type === "Literal" &&
122 (literalTypesToCheck.has(typeof node.property.value) || astUtils.isNullLiteral(node.property))
124 checkComputedProperty(node, node.property.value);
128 node.property.type === "TemplateLiteral" &&
129 node.property.expressions.length === 0
131 checkComputedProperty(node, node.property.quasis[0].value.cooked);
136 keywords.indexOf(String(node.property.name)) !== -1
140 messageId: "useBrackets",
142 key: node.property.name
145 const dot = sourceCode.getTokenBefore(node.property);
146 const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
148 if (textAfterDot.trim()) {
150 // Don't perform any fixes if there are comments between the dot and the property name.
154 if (node.object.type === "Identifier" && node.object.name === "let") {
157 * A statement that starts with `let[` is parsed as a destructuring variable declaration, not
158 * a MemberExpression.
163 return fixer.replaceTextRange(
164 [dot.range[0], node.property.range[1]],
165 `[${textAfterDot}"${node.property.name}"]`