2 * @fileoverview Disallow use of multiple spaces.
3 * @author Nicholas C. Zakas
8 const astUtils = require("./utils/ast-utils");
10 //------------------------------------------------------------------------------
12 //------------------------------------------------------------------------------
19 description: "disallow multiple spaces",
20 category: "Best Practices",
22 url: "https://eslint.org/docs/rules/no-multi-spaces"
25 fixable: "whitespace",
38 additionalProperties: false
45 additionalProperties: false
50 multipleSpaces: "Multiple spaces found before '{{displayValue}}'."
55 const sourceCode = context.getSourceCode();
56 const options = context.options[0] || {};
57 const ignoreEOLComments = options.ignoreEOLComments;
58 const exceptions = Object.assign({ Property: true }, options.exceptions);
59 const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
62 * Formats value of given comment token for error message by truncating its length.
63 * @param {Token} token comment token
64 * @returns {string} formatted value
67 function formatReportedCommentValue(token) {
68 const valueLines = token.value.split("\n");
69 const value = valueLines[0];
70 const formattedValue = `${value.slice(0, 12)}...`;
72 return valueLines.length === 1 && value.length <= 12 ? value : formattedValue;
75 //--------------------------------------------------------------------------
77 //--------------------------------------------------------------------------
81 sourceCode.tokensAndComments.forEach((leftToken, leftIndex, tokensAndComments) => {
82 if (leftIndex === tokensAndComments.length - 1) {
85 const rightToken = tokensAndComments[leftIndex + 1];
87 // Ignore tokens that don't have 2 spaces between them or are on different lines
89 !sourceCode.text.slice(leftToken.range[1], rightToken.range[0]).includes(" ") ||
90 leftToken.loc.end.line < rightToken.loc.start.line
95 // Ignore comments that are the last token on their line if `ignoreEOLComments` is active.
98 astUtils.isCommentToken(rightToken) &&
100 leftIndex === tokensAndComments.length - 2 ||
101 rightToken.loc.end.line < tokensAndComments[leftIndex + 2].loc.start.line
107 // Ignore tokens that are in a node in the "exceptions" object
109 const parentNode = sourceCode.getNodeByRangeIndex(rightToken.range[0] - 1);
111 if (parentNode && exceptions[parentNode.type]) {
118 if (rightToken.type === "Block") {
119 displayValue = `/*${formatReportedCommentValue(rightToken)}*/`;
120 } else if (rightToken.type === "Line") {
121 displayValue = `//${formatReportedCommentValue(rightToken)}`;
123 displayValue = rightToken.value;
128 loc: { start: leftToken.loc.end, end: rightToken.loc.start },
129 messageId: "multipleSpaces",
130 data: { displayValue },
131 fix: fixer => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ")