2 * @fileoverview Rule to disallow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
3 * @author Jonathan Kingston
4 * @author Christophe Porteneuve
9 //------------------------------------------------------------------------------
11 //------------------------------------------------------------------------------
13 const astUtils
= require("./utils/ast-utils");
15 //------------------------------------------------------------------------------
17 //------------------------------------------------------------------------------
19 const ALL_IRREGULARS
= /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/u;
20 const IRREGULAR_WHITESPACE
= /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mgu;
21 const IRREGULAR_LINE_TERMINATORS
= /[\u2028\u2029]/mgu;
22 const LINE_BREAK
= astUtils
.createGlobalLinebreakMatcher();
24 //------------------------------------------------------------------------------
26 //------------------------------------------------------------------------------
33 description
: "disallow irregular whitespace",
35 url
: "https://eslint.org/docs/rules/no-irregular-whitespace"
59 additionalProperties
: false
64 noIrregularWhitespace
: "Irregular whitespace not allowed."
70 // Module store of errors that we have found
73 // Lookup the `skipComments` option, which defaults to `false`.
74 const options
= context
.options
[0] || {};
75 const skipComments
= !!options
.skipComments
;
76 const skipStrings
= options
.skipStrings
!== false;
77 const skipRegExps
= !!options
.skipRegExps
;
78 const skipTemplates
= !!options
.skipTemplates
;
80 const sourceCode
= context
.getSourceCode();
81 const commentNodes
= sourceCode
.getAllComments();
84 * Removes errors that occur inside the given node
85 * @param {ASTNode} node to check for matching errors.
89 function removeWhitespaceError(node
) {
90 const locStart
= node
.loc
.start
;
91 const locEnd
= node
.loc
.end
;
93 errors
= errors
.filter(({ loc
: { start
: errorLocStart
} }) => (
94 errorLocStart
.line
< locStart
.line
||
95 errorLocStart
.line
=== locStart
.line
&& errorLocStart
.column
< locStart
.column
||
96 errorLocStart
.line
=== locEnd
.line
&& errorLocStart
.column
>= locEnd
.column
||
97 errorLocStart
.line
> locEnd
.line
102 * Checks identifier or literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
103 * @param {ASTNode} node to check for matching errors.
107 function removeInvalidNodeErrorsInIdentifierOrLiteral(node
) {
108 const shouldCheckStrings
= skipStrings
&& (typeof node
.value
=== "string");
109 const shouldCheckRegExps
= skipRegExps
&& Boolean(node
.regex
);
111 if (shouldCheckStrings
|| shouldCheckRegExps
) {
113 // If we have irregular characters remove them from the errors list
114 if (ALL_IRREGULARS
.test(node
.raw
)) {
115 removeWhitespaceError(node
);
121 * Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
122 * @param {ASTNode} node to check for matching errors.
126 function removeInvalidNodeErrorsInTemplateLiteral(node
) {
127 if (typeof node
.value
.raw
=== "string") {
128 if (ALL_IRREGULARS
.test(node
.value
.raw
)) {
129 removeWhitespaceError(node
);
135 * Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
136 * @param {ASTNode} node to check for matching errors.
140 function removeInvalidNodeErrorsInComment(node
) {
141 if (ALL_IRREGULARS
.test(node
.value
)) {
142 removeWhitespaceError(node
);
147 * Checks the program source for irregular whitespace
148 * @param {ASTNode} node The program node
152 function checkForIrregularWhitespace(node
) {
153 const sourceLines
= sourceCode
.lines
;
155 sourceLines
.forEach((sourceLine
, lineIndex
) => {
156 const lineNumber
= lineIndex
+ 1;
159 while ((match
= IRREGULAR_WHITESPACE
.exec(sourceLine
)) !== null) {
162 messageId
: "noIrregularWhitespace",
170 column
: match
.index
+ match
[0].length
179 * Checks the program source for irregular line terminators
180 * @param {ASTNode} node The program node
184 function checkForIrregularLineTerminators(node
) {
185 const source
= sourceCode
.getText(),
186 sourceLines
= sourceCode
.lines
,
187 linebreaks
= source
.match(LINE_BREAK
);
188 let lastLineIndex
= -1,
191 while ((match
= IRREGULAR_LINE_TERMINATORS
.exec(source
)) !== null) {
192 const lineIndex
= linebreaks
.indexOf(match
[0], lastLineIndex
+ 1) || 0;
196 messageId
: "noIrregularWhitespace",
200 column
: sourceLines
[lineIndex
].length
209 lastLineIndex
= lineIndex
;
214 * A no-op function to act as placeholder for comment accumulation when the `skipComments` option is `false`.
222 if (ALL_IRREGULARS
.test(sourceCode
.getText())) {
223 nodes
.Program = function(node
) {
226 * As we can easily fire warnings for all white space issues with
227 * all the source its simpler to fire them here.
228 * This means we can check all the application code without having
229 * to worry about issues caused in the parser tokens.
230 * When writing this code also evaluating per node was missing out
231 * connecting tokens in some cases.
232 * We can later filter the errors when they are found to be not an
233 * issue in nodes we don't care about.
235 checkForIrregularWhitespace(node
);
236 checkForIrregularLineTerminators(node
);
239 nodes
.Identifier
= removeInvalidNodeErrorsInIdentifierOrLiteral
;
240 nodes
.Literal
= removeInvalidNodeErrorsInIdentifierOrLiteral
;
241 nodes
.TemplateElement
= skipTemplates
? removeInvalidNodeErrorsInTemplateLiteral
: noop
;
242 nodes
["Program:exit"] = function() {
245 // First strip errors occurring in comment nodes.
246 commentNodes
.forEach(removeInvalidNodeErrorsInComment
);
249 // If we have any errors remaining report on them
250 errors
.forEach(error
=> context
.report(error
));
253 nodes
.Program
= noop
;