Skip to content

Commit

Permalink
feat: add overrides.namedExports to func-style rule (#18444)
Browse files Browse the repository at this point in the history
* feat: add `overrides.namedExports` to `func-style` rule

* Apply suggestions from code review

Co-authored-by: Tanuj Kanti <86398394+Tanujkanti4441@users.noreply.github.com>

* docs: fix examples

* Update lib/rules/func-style.js

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* fix: false negative

---------

Co-authored-by: Tanuj Kanti <86398394+Tanujkanti4441@users.noreply.github.com>
Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
  • Loading branch information
3 people committed May 17, 2024
1 parent 06f1d1c commit b32153c
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 5 deletions.
97 changes: 96 additions & 1 deletion docs/src/rules/func-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,14 @@ This rule has a string option:
* `"expression"` (default) requires the use of function expressions instead of function declarations
* `"declaration"` requires the use of function declarations instead of function expressions

This rule has an object option for an exception:
This rule has an object option for two exceptions:

* `"allowArrowFunctions"`: `true` (default `false`) allows the use of arrow functions. This option applies only when the string option is set to `"declaration"` (arrow functions are always allowed when the string option is set to `"expression"`, regardless of this option)
* `"overrides"`:
* `"namedExports": "expression" | "declaration" | "ignore"`: used to override function styles in named exports
* `"expression"`: like string option
* `"declaration"`: like string option
* `"ignore"`: either style is acceptable

### expression

Expand Down Expand Up @@ -148,6 +153,96 @@ var foo = () => {};

:::

### overrides

#### namedExports

##### expression

Examples of **incorrect** code for this rule with the `"declaration"` and `{"overrides": { "namedExports": "expression" }}` option:

::: incorrect

```js
/*eslint func-style: ["error", "declaration", { "overrides": { "namedExports": "expression" } }]*/

export function foo() {
// ...
}
```

:::

Examples of **correct** code for this rule with the `"declaration"` and `{"overrides": { "namedExports": "expression" }}` option:

::: correct

```js
/*eslint func-style: ["error", "declaration", { "overrides": { "namedExports": "expression" } }]*/

export var foo = function() {
// ...
};

export var bar = () => {};
```

:::

##### declaration

Examples of **incorrect** code for this rule with the `"expression"` and `{"overrides": { "namedExports": "declaration" }}` option:

::: incorrect

```js
/*eslint func-style: ["error", "expression", { "overrides": { "namedExports": "declaration" } }]*/

export var foo = function() {
// ...
};

export var bar = () => {};
```

:::

Examples of **correct** code for this rule with the `"expression"` and `{"overrides": { "namedExports": "declaration" }}` option:

::: correct

```js
/*eslint func-style: ["error", "expression", { "overrides": { "namedExports": "declaration" } }]*/

export function foo() {
// ...
}
```

:::

##### ignore

Examples of **correct** code for this rule with the `{"overrides": { "namedExports": "ignore" }}` option:

::: correct

```js
/*eslint func-style: ["error", "expression", { "overrides": { "namedExports": "ignore" } }]*/

export var foo = function() {
// ...
};

export var bar = () => {};

export function baz() {
// ...
}
```

:::

## When Not To Use It

If you want to allow developers to each decide how they want to write functions on their own, then you can disable this rule.
46 changes: 42 additions & 4 deletions lib/rules/func-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ module.exports = {
allowArrowFunctions: {
type: "boolean",
default: false
},
overrides: {
type: "object",
properties: {
namedExports: {
enum: ["declaration", "expression", "ignore"]
}
},
additionalProperties: false
}
},
additionalProperties: false
Expand All @@ -46,13 +55,22 @@ module.exports = {
const style = context.options[0],
allowArrowFunctions = context.options[1] && context.options[1].allowArrowFunctions,
enforceDeclarations = (style === "declaration"),
exportFunctionStyle = context.options[1] && context.options[1].overrides && context.options[1].overrides.namedExports,
stack = [];

const nodesToCheck = {
FunctionDeclaration(node) {
stack.push(false);

if (!enforceDeclarations && node.parent.type !== "ExportDefaultDeclaration") {
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" });
}
},
Expand All @@ -63,7 +81,18 @@ module.exports = {
FunctionExpression(node) {
stack.push(false);

if (enforceDeclarations && node.parent.type === "VariableDeclarator") {
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" });
}
},
Expand All @@ -86,8 +115,17 @@ module.exports = {
nodesToCheck["ArrowFunctionExpression:exit"] = function(node) {
const hasThisExpr = stack.pop();

if (enforceDeclarations && !hasThisExpr && node.parent.type === "VariableDeclarator") {
context.report({ node: node.parent, messageId: "declaration" });
if (!hasThisExpr && 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" });
}
}
};
}
Expand Down

0 comments on commit b32153c

Please sign in to comment.