/** * @fileoverview Rule to enforce a particular function style * @author Nicholas C. Zakas */ 'use strict'; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ /** @type {import('../types').Rule.RuleModule} */ module.exports = { meta: { type: 'suggestion', defaultOptions: [ 'expression', { allowArrowFunctions: false, overrides: {}, }, ], docs: { description: 'Enforce the consistent use of either `function` declarations or expressions assigned to variables', recommended: false, frozen: true, url: 'https://eslint.org/docs/latest/rules/func-style', }, schema: [ { enum: ['declaration', 'expression'], }, { type: 'object', properties: { allowArrowFunctions: { type: 'boolean', }, overrides: { type: 'object', properties: { namedExports: { enum: ['declaration', 'expression', 'ignore'], }, }, additionalProperties: false, }, }, additionalProperties: false, }, ], messages: { expression: 'Expected a function expression.', declaration: 'Expected a function declaration.', }, }, create(context) { const [style, { allowArrowFunctions, overrides }] = context.options; const enforceDeclarations = style === 'declaration'; const { namedExports: exportFunctionStyle } = overrides; const stack = []; const nodesToCheck = { FunctionDeclaration(node) { stack.push(false); if ( !enforceDeclarations && node.parent.type !== 'ExportDefaultDeclaration' && (typeof exportFunctionStyle === 'undefined' || node.parent.type !== 'ExportNamedDeclaration') ) { context.report({ node, messageId: 'expression' }); } if ( node.parent.type === 'ExportNamedDeclaration' && exportFunctionStyle === 'expression' ) { context.report({ node, messageId: 'expression' }); } }, 'FunctionDeclaration:exit'() { stack.pop(); }, FunctionExpression(node) { stack.push(false); if ( enforceDeclarations && node.parent.type === 'VariableDeclarator' && (typeof exportFunctionStyle === 'undefined' || node.parent.parent.parent.type !== 'ExportNamedDeclaration') ) { context.report({ node: node.parent, messageId: 'declaration', }); } if ( node.parent.type === 'VariableDeclarator' && node.parent.parent.parent.type === 'ExportNamedDeclaration' && exportFunctionStyle === 'declaration' ) { context.report({ node: node.parent, messageId: 'declaration', }); } }, 'FunctionExpression:exit'() { stack.pop(); }, 'ThisExpression, Super'() { if (stack.length > 0) { stack[stack.length - 1] = true; } }, }; if (!allowArrowFunctions) { nodesToCheck.ArrowFunctionExpression = function () { stack.push(false); }; nodesToCheck['ArrowFunctionExpression:exit'] = function (node) { const hasThisOrSuperExpr = stack.pop(); if (!hasThisOrSuperExpr && node.parent.type === 'VariableDeclarator') { if ( enforceDeclarations && (typeof exportFunctionStyle === 'undefined' || node.parent.parent.parent.type !== 'ExportNamedDeclaration') ) { context.report({ node: node.parent, messageId: 'declaration', }); } if ( node.parent.parent.parent.type === 'ExportNamedDeclaration' && exportFunctionStyle === 'declaration' ) { context.report({ node: node.parent, messageId: 'declaration', }); } } }; } return nodesToCheck; }, };