Spaces:
Sleeping
Sleeping
| export default { | |
| meta: { | |
| type: 'layout', | |
| docs: { | |
| description: 'enforce spacing inside parentheses for control structures only', | |
| category: 'Stylistic Issues', | |
| }, | |
| fixable: 'whitespace', | |
| schema: [], | |
| messages: { | |
| missingSpaceAfterOpen: 'Missing space after opening parenthesis in control structure.', | |
| missingSpaceBeforeClose: 'Missing space before closing parenthesis in control structure.', | |
| unexpectedSpaceAfterOpen: 'Unexpected space after opening parenthesis in function call.', | |
| unexpectedSpaceBeforeClose: 'Unexpected space before closing parenthesis in function call.', | |
| }, | |
| }, | |
| create (context) { | |
| const sourceCode = context.getSourceCode(); | |
| function checkControlStructureSpacing (node) { | |
| // For control structures, we need to find the parentheses around the condition/test | |
| let conditionNode; | |
| if ( node.type === 'IfStatement' || node.type === 'WhileStatement' || node.type === 'DoWhileStatement' ) { | |
| conditionNode = node.test; | |
| } else if ( node.type === 'ForStatement' || node.type === 'ForInStatement' || node.type === 'ForOfStatement' ) { | |
| // For loops, we want the parentheses around the entire for clause | |
| conditionNode = node; | |
| } else if ( node.type === 'SwitchStatement' ) { | |
| conditionNode = node.discriminant; | |
| } else if ( node.type === 'CatchClause' ) { | |
| conditionNode = node.param; | |
| } | |
| if ( ! conditionNode ) return; | |
| // Find the opening paren - it should be right before the condition starts | |
| const openParen = sourceCode.getTokenBefore(conditionNode, token => token.value === '('); | |
| if ( !openParen || openParen.value !== '(' ) return; | |
| // Find the closing paren - it should be right after the condition ends | |
| const closeParen = sourceCode.getTokenAfter(conditionNode, token => token.value === ')'); | |
| if ( !closeParen || closeParen.value !== ')' ) return; | |
| const afterOpen = sourceCode.getTokenAfter(openParen); | |
| const beforeClose = sourceCode.getTokenBefore(closeParen); | |
| { | |
| const contentBetweenParens = sourceCode.getText().slice(openParen.range[1], closeParen.range[0]); | |
| const isSingleCharVariable = /^\s*[a-zA-Z_$]\s*$/.test(contentBetweenParens); | |
| // Skip spacing requirements for single character variables | |
| if ( isSingleCharVariable ) { | |
| return; | |
| } | |
| } | |
| // Control structures should have spacing | |
| if ( afterOpen && openParen.range[1] === afterOpen.range[0] ) { | |
| context.report({ | |
| node, | |
| loc: openParen.loc, | |
| messageId: 'missingSpaceAfterOpen', | |
| fix (fixer) { | |
| return fixer.insertTextAfter(openParen, ' '); | |
| }, | |
| }); | |
| } | |
| if ( beforeClose && beforeClose.range[1] === closeParen.range[0] ) { | |
| context.report({ | |
| node, | |
| loc: closeParen.loc, | |
| messageId: 'missingSpaceBeforeClose', | |
| fix (fixer) { | |
| return fixer.insertTextBefore(closeParen, ' '); | |
| }, | |
| }); | |
| } | |
| } | |
| function checkForLoopSpacing (node) { | |
| // For loops are special - we need to find the opening paren after the 'for' keyword | |
| // and the closing paren before the body | |
| const forKeyword = sourceCode.getFirstToken(node); | |
| if ( !forKeyword || forKeyword.value !== 'for' ) return; | |
| const openParen = sourceCode.getTokenAfter(forKeyword, token => token.value === '('); | |
| if ( ! openParen ) return; | |
| // The closing paren should be right before the body | |
| const closeParen = sourceCode.getTokenBefore(node.body, token => token.value === ')'); | |
| if ( ! closeParen ) return; | |
| const afterOpen = sourceCode.getTokenAfter(openParen); | |
| const beforeClose = sourceCode.getTokenBefore(closeParen); | |
| if ( afterOpen && openParen.range[1] === afterOpen.range[0] ) { | |
| context.report({ | |
| node, | |
| loc: openParen.loc, | |
| messageId: 'missingSpaceAfterOpen', | |
| fix (fixer) { | |
| return fixer.insertTextAfter(openParen, ' '); | |
| }, | |
| }); | |
| } | |
| if ( beforeClose && beforeClose.range[1] === closeParen.range[0] ) { | |
| context.report({ | |
| node, | |
| loc: closeParen.loc, | |
| messageId: 'missingSpaceBeforeClose', | |
| fix (fixer) { | |
| return fixer.insertTextBefore(closeParen, ' '); | |
| }, | |
| }); | |
| } | |
| } | |
| function checkFunctionCallSpacing (node) { | |
| // Find the opening parenthesis for this function call | |
| const openParen = sourceCode.getFirstToken(node, token => token.value === '('); | |
| const closeParen = sourceCode.getLastToken(node, token => token.value === ')'); | |
| if ( !openParen || !closeParen ) return; | |
| const afterOpen = sourceCode.getTokenAfter(openParen); | |
| const beforeClose = sourceCode.getTokenBefore(closeParen); | |
| // Function calls should NOT have spacing | |
| if ( afterOpen && openParen.range[1] !== afterOpen.range[0] ) { | |
| const spaceAfter = sourceCode.getText().slice(openParen.range[1], afterOpen.range[0]); | |
| if ( /^\s+$/.test(spaceAfter) ) { | |
| context.report({ | |
| node, | |
| loc: openParen.loc, | |
| messageId: 'unexpectedSpaceAfterOpen', | |
| fix (fixer) { | |
| return fixer.removeRange([openParen.range[1], afterOpen.range[0]]); | |
| }, | |
| }); | |
| } | |
| } | |
| if ( beforeClose && beforeClose.range[1] !== closeParen.range[0] ) { | |
| const spaceBefore = sourceCode.getText().slice(beforeClose.range[1], closeParen.range[0]); | |
| if ( /^\s+$/.test(spaceBefore) ) { | |
| context.report({ | |
| node, | |
| loc: closeParen.loc, | |
| messageId: 'unexpectedSpaceBeforeClose', | |
| fix (fixer) { | |
| return fixer.removeRange([beforeClose.range[1], closeParen.range[0]]); | |
| }, | |
| }); | |
| } | |
| } | |
| } | |
| return { | |
| // Control structures that should have spacing | |
| IfStatement (node) { | |
| checkControlStructureSpacing(node); | |
| }, | |
| WhileStatement (node) { | |
| checkControlStructureSpacing(node); | |
| }, | |
| DoWhileStatement (node) { | |
| checkControlStructureSpacing(node); | |
| }, | |
| SwitchStatement (node) { | |
| checkControlStructureSpacing(node); | |
| }, | |
| CatchClause (node) { | |
| if ( node.param ) { | |
| checkControlStructureSpacing(node); | |
| } | |
| }, | |
| // For loops need special handling | |
| ForStatement (node) { | |
| checkForLoopSpacing(node); | |
| }, | |
| ForInStatement (node) { | |
| checkForLoopSpacing(node); | |
| }, | |
| ForOfStatement (node) { | |
| checkForLoopSpacing(node); | |
| }, | |
| // Function calls that should NOT have spacing | |
| CallExpression (node) { | |
| checkFunctionCallSpacing(node); | |
| }, | |
| NewExpression (node) { | |
| if ( node.arguments.length > 0 || sourceCode.getLastToken(node).value === ')' ) { | |
| checkFunctionCallSpacing(node); | |
| } | |
| }, | |
| }; | |
| }, | |
| }; |