From 9f0bb3f04d0580696df540b64c1e039e7c996697 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 12:47:08 +0200 Subject: [PATCH 1/8] Move pure functions to module scope --- lib/rules/no-mocha-arrows.js | 124 ++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index aa84eaf..c09aacf 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -7,11 +7,74 @@ const createAstUtils = require('../util/ast'); +function extractSourceTextByRange(sourceCode, start, end) { + return sourceCode.text.slice(start, end).trim(); +} + +// eslint-disable-next-line max-statements +function formatFunctionHead(sourceCode, fn) { + const arrow = sourceCode.getTokenBefore(fn.body); + const beforeArrowToken = sourceCode.getTokenBefore(arrow); + let firstToken = sourceCode.getFirstToken(fn); + + let functionKeyword = 'function'; + let params = extractSourceTextByRange( + sourceCode, + firstToken.range[0], + beforeArrowToken.range[1] + ); + if (fn.async) { + // When 'async' specified strip the token from the params text + // and prepend it to the function keyword + params = params.slice(firstToken.range[1] - firstToken.range[0]).trim(); + functionKeyword = 'async function'; + + // Advance firstToken pointer + firstToken = sourceCode.getTokenAfter(firstToken); + } + + const beforeArrowComment = extractSourceTextByRange( + sourceCode, + beforeArrowToken.range[1], + arrow.range[0] + ); + const afterArrowComment = extractSourceTextByRange( + sourceCode, + arrow.range[1], + fn.body.range[0] + ); + let paramsFullText; + if (firstToken.type !== 'Punctuator') { + paramsFullText = `(${params}${beforeArrowComment})${afterArrowComment}`; + } else { + paramsFullText = `${params}${beforeArrowComment}${afterArrowComment}`; + } + + return `${functionKeyword}${paramsFullText} `; +} + +function fixArrowFunction(fixer, sourceCode, fn) { + if (fn.body.type === 'BlockStatement') { + // When it((...) => { ... }), + // simply replace '(...) => ' with 'function () ' + return fixer.replaceTextRange( + [ fn.range[0], fn.body.range[0] ], + formatFunctionHead(sourceCode, fn) + ); + } + + const bodyText = sourceCode.text.slice(fn.body.range[0], fn.body.range[1]); + return fixer.replaceTextRange( + [ fn.range[0], fn.range[1] ], + `${formatFunctionHead(sourceCode, fn)}{ return ${bodyText}; }` + ); +} module.exports = { meta: { type: 'suggestion', docs: { - description: 'Disallow arrow functions as arguments to mocha functions' + description: + 'Disallow arrow functions as arguments to mocha functions' }, fixable: 'code' }, @@ -19,57 +82,6 @@ module.exports = { const astUtils = createAstUtils(context.settings); const sourceCode = context.getSourceCode(); - function extractSourceTextByRange(start, end) { - return sourceCode.text.slice(start, end).trim(); - } - - // eslint-disable-next-line max-statements - function formatFunctionHead(fn) { - const arrow = sourceCode.getTokenBefore(fn.body); - const beforeArrowToken = sourceCode.getTokenBefore(arrow); - let firstToken = sourceCode.getFirstToken(fn); - - let functionKeyword = 'function'; - let params = extractSourceTextByRange(firstToken.range[0], beforeArrowToken.range[1]); - if (fn.async) { - // When 'async' specified strip the token from the params text - // and prepend it to the function keyword - params = params.slice(firstToken.range[1] - firstToken.range[0]).trim(); - functionKeyword = 'async function'; - - // Advance firstToken pointer - firstToken = sourceCode.getTokenAfter(firstToken); - } - - const beforeArrowComment = extractSourceTextByRange(beforeArrowToken.range[1], arrow.range[0]); - const afterArrowComment = extractSourceTextByRange(arrow.range[1], fn.body.range[0]); - let paramsFullText; - if (firstToken.type !== 'Punctuator') { - paramsFullText = `(${params}${beforeArrowComment})${afterArrowComment}`; - } else { - paramsFullText = `${params}${beforeArrowComment}${afterArrowComment}`; - } - - return `${functionKeyword}${paramsFullText} `; - } - - function fixArrowFunction(fixer, fn) { - if (fn.body.type === 'BlockStatement') { - // When it((...) => { ... }), - // simply replace '(...) => ' with 'function () ' - return fixer.replaceTextRange( - [ fn.range[0], fn.body.range[0] ], - formatFunctionHead(fn) - ); - } - - const bodyText = sourceCode.text.slice(fn.body.range[0], fn.body.range[1]); - return fixer.replaceTextRange( - [ fn.range[0], fn.range[1] ], - `${formatFunctionHead(fn)}{ return ${ bodyText }; }` - ); - } - return { CallExpression(node) { const name = astUtils.getNodeName(node.callee); @@ -79,9 +91,13 @@ module.exports = { if (fnArg && fnArg.type === 'ArrowFunctionExpression') { context.report({ node, - message: `Do not pass arrow functions to ${ name }()`, + message: `Do not pass arrow functions to ${name}()`, fix(fixer) { - return fixArrowFunction(fixer, fnArg); + return fixArrowFunction( + fixer, + sourceCode, + fnArg + ); } }); } From 183e19d784f30339332e0c4561410ee51884d93c Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 12:58:35 +0200 Subject: [PATCH 2/8] Introduce buildIsMochaFunctionCallAnswerer --- lib/rules/no-mocha-arrows.js | 9 ++++++++- lib/util/ast.js | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index c09aacf..07d56a3 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -69,6 +69,7 @@ function fixArrowFunction(fixer, sourceCode, fn) { `${formatFunctionHead(sourceCode, fn)}{ return ${bodyText}; }` ); } + module.exports = { meta: { type: 'suggestion', @@ -81,12 +82,18 @@ module.exports = { create(context) { const astUtils = createAstUtils(context.settings); const sourceCode = context.getSourceCode(); + const isTestCase = astUtils.buildIsTestCaseAnswerer(); + const isDescribe = astUtils.buildIsDescribeAnswerer(); + const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( + isTestCase, + isDescribe + ); return { CallExpression(node) { const name = astUtils.getNodeName(node.callee); - if (astUtils.isMochaFunctionCall(node, context.getScope())) { + if (isMochaFunctionCall(node, context.getScope())) { const fnArg = node.arguments.slice(-1)[0]; if (fnArg && fnArg.type === 'ArrowFunctionExpression') { context.report({ diff --git a/lib/util/ast.js b/lib/util/ast.js index 95fe823..37ffa55 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -107,6 +107,16 @@ function createAstUtils(settings) { return isCallExpression(node) && isSuiteConfigExpression(node.callee); } + function buildIsMochaFunctionCallAnswerer(_isTestCase, _isDescribe) { + return (node, scope) => { + if (isCallToShadowedReference(node, scope)) { + return false; + } + + return _isTestCase(node) || _isDescribe(node) || isHookCall(node); + }; + } + function isMochaFunctionCall(node, scope) { if (isCallToShadowedReference(node, scope)) { return false; @@ -143,7 +153,8 @@ function createAstUtils(settings) { findReturnStatement, isReturnOfUndefined, buildIsDescribeAnswerer, - buildIsTestCaseAnswerer + buildIsTestCaseAnswerer, + buildIsMochaFunctionCallAnswerer }; } From 85494b883fa9967706fa69d57f4ffb568cb1afeb Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 12:59:27 +0200 Subject: [PATCH 3/8] Determine node name only when needed --- lib/rules/no-mocha-arrows.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index 07d56a3..9ead2bc 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -91,11 +91,10 @@ module.exports = { return { CallExpression(node) { - const name = astUtils.getNodeName(node.callee); - if (isMochaFunctionCall(node, context.getScope())) { const fnArg = node.arguments.slice(-1)[0]; if (fnArg && fnArg.type === 'ArrowFunctionExpression') { + const name = astUtils.getNodeName(node.callee); context.report({ node, message: `Do not pass arrow functions to ${name}()`, From f9c360c661ce901ca328a2f4610c604f2d743ffc Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 13:04:06 +0200 Subject: [PATCH 4/8] Properly check length of arguments --- lib/rules/no-mocha-arrows.js | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index 9ead2bc..5a14099 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -92,20 +92,26 @@ module.exports = { return { CallExpression(node) { if (isMochaFunctionCall(node, context.getScope())) { - const fnArg = node.arguments.slice(-1)[0]; - if (fnArg && fnArg.type === 'ArrowFunctionExpression') { - const name = astUtils.getNodeName(node.callee); - context.report({ - node, - message: `Do not pass arrow functions to ${name}()`, - fix(fixer) { - return fixArrowFunction( - fixer, - sourceCode, - fnArg - ); - } - }); + const amountOfArguments = node.arguments.length; + + if (amountOfArguments > 0) { + const lastArgument = + node.arguments[amountOfArguments - 1]; + + if (lastArgument.type === 'ArrowFunctionExpression') { + const name = astUtils.getNodeName(node.callee); + context.report({ + node, + message: `Do not pass arrow functions to ${name}()`, + fix(fixer) { + return fixArrowFunction( + fixer, + sourceCode, + lastArgument + ); + } + }); + } } } } From 2a95e3e3108164dc5768cb750a97d13050080654 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 13:17:40 +0200 Subject: [PATCH 5/8] Stop creating unnecessary arrays --- lib/rules/no-mocha-arrows.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index 5a14099..037528b 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -63,9 +63,9 @@ function fixArrowFunction(fixer, sourceCode, fn) { ); } - const bodyText = sourceCode.text.slice(fn.body.range[0], fn.body.range[1]); + const bodyText = sourceCode.text.slice(...fn.body.range); return fixer.replaceTextRange( - [ fn.range[0], fn.range[1] ], + fn.range, `${formatFunctionHead(sourceCode, fn)}{ return ${bodyText}; }` ); } From b5b97b3fa3f6d488d64df323f954905161de4ba1 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 13:21:39 +0200 Subject: [PATCH 6/8] Use getText() method instead of custom text slicing --- lib/rules/no-mocha-arrows.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index 037528b..2fee41a 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -63,7 +63,7 @@ function fixArrowFunction(fixer, sourceCode, fn) { ); } - const bodyText = sourceCode.text.slice(...fn.body.range); + const bodyText = sourceCode.getText(fn.body); return fixer.replaceTextRange( fn.range, `${formatFunctionHead(sourceCode, fn)}{ return ${bodyText}; }` From 41951020d1d625d7f77dcd99f7b9a3626575c831 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 14:22:22 +0200 Subject: [PATCH 7/8] Check scope references as late as possible --- lib/rules/no-mocha-arrows.js | 2 +- lib/rules/no-nested-tests.js | 18 +++++++++-- lib/util/ast.js | 62 ++++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js index 2fee41a..56e79aa 100644 --- a/lib/rules/no-mocha-arrows.js +++ b/lib/rules/no-mocha-arrows.js @@ -91,7 +91,7 @@ module.exports = { return { CallExpression(node) { - if (isMochaFunctionCall(node, context.getScope())) { + if (isMochaFunctionCall(node, context)) { const amountOfArguments = node.arguments.length; if (amountOfArguments > 0) { diff --git a/lib/rules/no-nested-tests.js b/lib/rules/no-nested-tests.js index 089bfdf..e623604 100644 --- a/lib/rules/no-nested-tests.js +++ b/lib/rules/no-nested-tests.js @@ -30,13 +30,20 @@ module.exports = { return isNested && isTest; } - function checkForAndReportErrors(node, isTestCase, isDescribe, isHookCall) { + function checkForAndReportErrors( + node, + isTestCase, + isDescribe, + isHookCall + ) { if (isNestedTest(isTestCase, isDescribe, testNestingLevel)) { const message = isDescribe ? 'Unexpected suite nested within a test.' : 'Unexpected test nested within another test.'; report(node, message); - } else if (isNestedTest(isTestCase, isHookCall, hookCallNestingLevel)) { + } else if ( + isNestedTest(isTestCase, isHookCall, hookCallNestingLevel) + ) { const message = isHookCall ? 'Unexpected test hook nested within a test hook.' : 'Unexpected test nested within a test hook.'; @@ -50,7 +57,12 @@ module.exports = { const isHookCall = astUtils.isHookCall(node); const isDescribe = astUtils.isDescribe(node); - checkForAndReportErrors(node, isTestCase, isDescribe, isHookCall); + checkForAndReportErrors( + node, + isTestCase, + isDescribe, + isHookCall + ); if (isTestCase) { testNestingLevel += 1; diff --git a/lib/util/ast.js b/lib/util/ast.js index 37ffa55..4a8c8d9 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -13,8 +13,16 @@ const isDefined = complement(isNil); const isCallExpression = both(isDefined, propEq('type', 'CallExpression')); const hooks = [ - 'before', 'after', 'beforeEach', 'afterEach', 'beforeAll', 'afterAll', - 'setup', 'teardown', 'suiteSetup', 'suiteTeardown' + 'before', + 'after', + 'beforeEach', + 'afterEach', + 'beforeAll', + 'afterAll', + 'setup', + 'teardown', + 'suiteSetup', + 'suiteTeardown' ]; const suiteConfig = [ 'timeout', 'slow', 'retries' ]; @@ -26,15 +34,13 @@ function getPropertyName(property) { function getNodeName(node) { if (node.type === 'MemberExpression') { - return `${getNodeName(node.object) }.${ getPropertyName(node.property)}`; + return `${getNodeName(node.object)}.${getPropertyName(node.property)}`; } return node.name; } function isHookIdentifier(node) { - return node && - node.type === 'Identifier' && - hooks.includes(node.name); + return node && node.type === 'Identifier' && hooks.includes(node.name); } function isHookCall(node) { @@ -50,11 +56,16 @@ function findReference(scope, node) { function isShadowed(scope, identifier) { const reference = findReference(scope, identifier); - return reference && reference.resolved && reference.resolved.defs.length > 0; + return ( + reference && reference.resolved && reference.resolved.defs.length > 0 + ); } function isCallToShadowedReference(node, scope) { - const identifier = node.callee.type === 'MemberExpression' ? node.callee.object : node.callee; + const identifier = + node.callee.type === 'MemberExpression' ? + node.callee.object : + node.callee; return isShadowed(scope, identifier); } @@ -67,9 +78,13 @@ function isFunctionCallWithName(node, names) { function createAstUtils(settings) { const additionalCustomNames = getAddtionalNames(settings); - function buildIsDescribeAnswerer(options) { + function buildIsDescribeAnswerer(options = {}) { const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options; - const describeAliases = getSuiteNames({ modifiersOnly, modifiers, additionalCustomNames }); + const describeAliases = getSuiteNames({ + modifiersOnly, + modifiers, + additionalCustomNames + }); return (node) => isFunctionCallWithName(node, describeAliases); } @@ -80,7 +95,11 @@ function createAstUtils(settings) { function buildIsTestCaseAnswerer(options = {}) { const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options; - const testCaseNames = getTestCaseNames({ modifiersOnly, modifiers, additionalCustomNames }); + const testCaseNames = getTestCaseNames({ + modifiersOnly, + modifiers, + additionalCustomNames + }); return (node) => isFunctionCallWithName(node, testCaseNames); } @@ -108,12 +127,20 @@ function createAstUtils(settings) { } function buildIsMochaFunctionCallAnswerer(_isTestCase, _isDescribe) { - return (node, scope) => { - if (isCallToShadowedReference(node, scope)) { - return false; + function _isMochaFunctionCall(node) { + return _isTestCase(node) || _isDescribe(node) || isHookCall(node); + } + + return (node, context) => { + if (_isMochaFunctionCall(node)) { + const scope = context.getScope(); + + if (!isCallToShadowedReference(node, scope)) { + return true; + } } - return _isTestCase(node) || _isDescribe(node) || isHookCall(node); + return false; }; } @@ -126,7 +153,10 @@ function createAstUtils(settings) { } function hasParentMochaFunctionCall(functionExpression, options) { - return isTestCase(functionExpression.parent, options) || isHookCall(functionExpression.parent); + return ( + isTestCase(functionExpression.parent, options) || + isHookCall(functionExpression.parent) + ); } function isExplicitUndefined(node) { From e499b273a0e7b0ae20473f71615edb903d00a286 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Wed, 26 May 2021 14:31:03 +0200 Subject: [PATCH 8/8] Always use buildIsMochaFunctionCallAnswerer instead of isMochaFunctionCall --- lib/rules/no-exports.js | 11 ++-- lib/rules/prefer-arrow-callback.js | 89 +++++++++++++++++++++--------- lib/util/ast.js | 13 +---- 3 files changed, 73 insertions(+), 40 deletions(-) diff --git a/lib/rules/no-exports.js b/lib/rules/no-exports.js index 41026c0..eeca6d8 100644 --- a/lib/rules/no-exports.js +++ b/lib/rules/no-exports.js @@ -16,6 +16,12 @@ module.exports = { const astUtils = createAstUtils(context.settings); const exportNodes = []; let hasTestCase = false; + const isTestCase = astUtils.buildIsTestCaseAnswerer(); + const isDescribe = astUtils.buildIsDescribeAnswerer(); + const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( + isTestCase, + isDescribe + ); function isCommonJsExport(node) { if (node.type === 'MemberExpression') { @@ -37,10 +43,7 @@ module.exports = { }, CallExpression(node) { - if ( - !hasTestCase && - astUtils.isMochaFunctionCall(node, context.getScope()) - ) { + if (!hasTestCase && isMochaFunctionCall(node, context)) { hasTestCase = true; } }, diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js index d60584e..16a9851 100644 --- a/lib/rules/prefer-arrow-callback.js +++ b/lib/rules/prefer-arrow-callback.js @@ -55,7 +55,10 @@ function getVariableOfArguments(scope) { * implicit "arguments" is not defined. * So does fast return with null. */ - if (variable.name === 'arguments' && variable.identifiers.length === 0) { + if ( + variable.name === 'arguments' && + variable.identifiers.length === 0 + ) { variableObject = variable; break; } @@ -69,9 +72,11 @@ function getVariableOfArguments(scope) { * @returns {boolean} */ function propertyIndicatesBind(property) { - return !property.computed && - property.type === 'Identifier' && - property.name === 'bind'; + return ( + !property.computed && + property.type === 'Identifier' && + property.name === 'bind' + ); } /** @@ -80,10 +85,12 @@ function propertyIndicatesBind(property) { * @returns {boolean} */ function isBindThis(node, currentNode) { - return node.object === currentNode && + return ( + node.object === currentNode && propertyIndicatesBind(node.property) && node.parent.type === 'CallExpression' && - node.parent.callee === node; + node.parent.callee === node + ); } /** @@ -94,8 +101,11 @@ function isBindThis(node, currentNode) { * @returns {boolean} `true` if the list of parameters contains any duplicates */ function hasDuplicateParams(paramsList) { - return paramsList.every((param) => param.type === 'Identifier') && - paramsList.length !== new Set(paramsList.map((param) => param.name)).size; + return ( + paramsList.every((param) => param.type === 'Identifier') && + paramsList.length !== + new Set(paramsList.map((param) => param.name)).size + ); } // ------------------------------------------------------------------------------ @@ -139,6 +149,12 @@ module.exports = { const allowUnboundThis = options.allowUnboundThis !== false; const allowNamedFunctions = options.allowNamedFunctions; const sourceCode = context.getSourceCode(); + const isTestCase = astUtils.buildIsTestCaseAnswerer(); + const isDescribe = astUtils.buildIsDescribeAnswerer(); + const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( + isTestCase, + isDescribe + ); /** * Checkes whether or not a given node is a callback. @@ -167,8 +183,9 @@ module.exports = { case 'MemberExpression': if (isBindThis(parent, currentNode)) { retv.isLexicalThis = - parent.parent.arguments.length === 1 && - parent.parent.arguments[0].type === 'ThisExpression'; + parent.parent.arguments.length === 1 && + parent.parent.arguments[0].type === + 'ThisExpression'; parent = parent.parent; } else { searchComplete = true; @@ -182,7 +199,10 @@ module.exports = { retv.isCallback = true; } // Checks whether the node is a mocha function callback. - if (retv.isCallback && astUtils.isMochaFunctionCall(parent, context.getScope())) { + if ( + retv.isCallback && + isMochaFunctionCall(parent, context) + ) { retv.isMochaCallback = true; } searchComplete = true; @@ -224,7 +244,6 @@ module.exports = { } return { - // Reset internal state. Program() { stack = []; @@ -283,8 +302,11 @@ module.exports = { // Reports if it's a callback which can replace with arrows. const callbackInfo = getCallbackInfo(node); - if (callbackInfo.isCallback && - (!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) && + if ( + callbackInfo.isCallback && + (!allowUnboundThis || + !scopeInfo.this || + callbackInfo.isLexicalThis) && !scopeInfo.meta && !callbackInfo.isMochaCallback ) { @@ -292,7 +314,11 @@ module.exports = { node, message: 'Unexpected function expression.', fix(fixer) { - if (!callbackInfo.isLexicalThis && scopeInfo.this || hasDuplicateParams(node.params)) { + if ( + !callbackInfo.isLexicalThis && + scopeInfo.this || + hasDuplicateParams(node.params) + ) { /* * If the callback function does not have .bind(this) and contains a reference to * `this`, there is no way to determine what `this` should be, so don't perform any @@ -306,30 +332,43 @@ module.exports = { const paramsLeftParen = node.params.length ? sourceCode.getTokenBefore(node.params[0]) : sourceCode.getTokenBefore(node.body, 1); - const paramsRightParen = sourceCode.getTokenBefore(node.body); + const paramsRightParen = sourceCode.getTokenBefore( + node.body + ); const asyncKeyword = node.async ? 'async ' : ''; const paramsFullText = sourceCode.text.slice( - paramsLeftParen.range[0], paramsRightParen.range[1] + paramsLeftParen.range[0], + paramsRightParen.range[1] ); - const arrowFunctionText = - `${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`; + const arrowFunctionText = `${asyncKeyword}${paramsFullText} => ${sourceCode.getText( + node.body + )}`; /* * If the callback function has `.bind(this)`, replace it with an arrow function and remove * the binding. Otherwise, just replace the arrow function itself. */ - const replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node; + const replacedNode = callbackInfo.isLexicalThis ? + node.parent.parent : + node; /* * If the replaced node is part of a BinaryExpression, LogicalExpression, or * MemberExpression, then the arrow function needs to be parenthesized, because * `foo || () => {}` is invalid syntax even though `foo || function() {}` is valid. */ - const needsParens = replacedNode.parent.type !== 'CallExpression' && - replacedNode.parent.type !== 'ConditionalExpression'; - const replacementText = needsParens ? `(${arrowFunctionText})` : arrowFunctionText; - - return fixer.replaceText(replacedNode, replacementText); + const needsParens = + replacedNode.parent.type !== 'CallExpression' && + replacedNode.parent.type !== + 'ConditionalExpression'; + const replacementText = needsParens ? + `(${arrowFunctionText})` : + arrowFunctionText; + + return fixer.replaceText( + replacedNode, + replacementText + ); } }); } diff --git a/lib/util/ast.js b/lib/util/ast.js index 4a8c8d9..3552d03 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -127,12 +127,12 @@ function createAstUtils(settings) { } function buildIsMochaFunctionCallAnswerer(_isTestCase, _isDescribe) { - function _isMochaFunctionCall(node) { + function isMochaFunctionCall(node) { return _isTestCase(node) || _isDescribe(node) || isHookCall(node); } return (node, context) => { - if (_isMochaFunctionCall(node)) { + if (isMochaFunctionCall(node)) { const scope = context.getScope(); if (!isCallToShadowedReference(node, scope)) { @@ -144,14 +144,6 @@ function createAstUtils(settings) { }; } - function isMochaFunctionCall(node, scope) { - if (isCallToShadowedReference(node, scope)) { - return false; - } - - return isTestCase(node) || isDescribe(node) || isHookCall(node); - } - function hasParentMochaFunctionCall(functionExpression, options) { return ( isTestCase(functionExpression.parent, options) || @@ -176,7 +168,6 @@ function createAstUtils(settings) { isTestCase, getPropertyName, getNodeName, - isMochaFunctionCall, isHookCall, isSuiteConfigCall, hasParentMochaFunctionCall,