Skip to content

Commit

Permalink
Merge branch 'main' into prettier-all
Browse files Browse the repository at this point in the history
  • Loading branch information
brettz9 authored Jul 24, 2024
2 parents e54f523 + 8a981d2 commit e392e77
Show file tree
Hide file tree
Showing 17 changed files with 152 additions and 43 deletions.
14 changes: 5 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,23 @@ jobs:
strategy:
matrix:
eslint: [8]
node: [12.22.0, 12, 14.17.0, 14, 16.0.0, 16, 18, 20]
node: [18.18.0, 20.9.0, 21.1.0, 22]
os: [ubuntu-latest]
include:
# ESLint v9
- eslint: 9
node: 20
node: 22
os: ubuntu-latest
# On other platforms
- os: windows-latest
eslint: 8
node: 18
node: 22
- os: macos-latest
eslint: 8
node: 18
# On old ESLint versions
- eslint: 7
node: 18
os: ubuntu-latest
node: 22
# On the minimum supported ESLint/Node.js version
- eslint: 7.0.0
node: 12.22.0
node: 18.18.0
os: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:

steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: ⎔ Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18

Expand Down
40 changes: 21 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,23 @@ or start with the recommended rule set:
Set in the `recommended` configuration.\
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).

| Name                      | Description | 💼 | ⚠️ | 🚫 | 🔧 |
| :------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
| [always-return](docs/rules/always-return.md) | Require returning inside each `then()` to create readable and reusable Promise chains. || | | |
| [avoid-new](docs/rules/avoid-new.md) | Disallow creating `new` promises outside of utility libs (use [pify][] instead). | | || |
| [catch-or-return](docs/rules/catch-or-return.md) | Enforce the use of `catch()` on un-returned promises. || | | |
| [no-callback-in-promise](docs/rules/no-callback-in-promise.md) | Disallow calling `cb()` inside of a `then()` (use [nodeify][] instead). | || | |
| [no-multiple-resolved](docs/rules/no-multiple-resolved.md) | Disallow creating new promises with paths that resolve multiple times. | | | | |
| [no-native](docs/rules/no-native.md) | Require creating a `Promise` constructor before using it in an ES5 environment. | | | ✅ | |
| [no-nesting](docs/rules/no-nesting.md) | Disallow nested `then()` or `catch()` statements. | | ✅ | | |
| [no-new-statics](docs/rules/no-new-statics.md) | Disallow calling `new` on a Promise static method. | ✅ | | | 🔧 |
| [no-promise-in-callback](docs/rules/no-promise-in-callback.md) | Disallow using promises inside of callbacks. | | ✅ | | |
| [no-return-in-finally](docs/rules/no-return-in-finally.md) | Disallow return statements in `finally()`. | | ✅ | | |
| [no-return-wrap](docs/rules/no-return-wrap.md) | Disallow wrapping values in `Promise.resolve` or `Promise.reject` when not needed. | ✅ | | | |
| [param-names](docs/rules/param-names.md) | Enforce consistent param names and ordering when creating new promises. | ✅ | | | |
| [prefer-await-to-callbacks](docs/rules/prefer-await-to-callbacks.md) | Prefer async/await to the callback pattern. | | | | |
| [prefer-await-to-then](docs/rules/prefer-await-to-then.md) | Prefer `await` to `then()`/`catch()`/`finally()` for reading Promise values. | | | | |
| [valid-params](docs/rules/valid-params.md) | Enforces the proper number of arguments are passed to Promise functions. | | ✅ | | |
| Name                      | Description | 💼 | ⚠️ | 🚫 | 🔧 |
| :------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
| [always-return](docs/rules/always-return.md) | Require returning inside each `then()` to create readable and reusable Promise chains. || | | |
| [avoid-new](docs/rules/avoid-new.md) | Disallow creating `new` promises outside of utility libs (use [util.promisify][] instead). | | || |
| [catch-or-return](docs/rules/catch-or-return.md) | Enforce the use of `catch()` on un-returned promises. || | | |
| [no-callback-in-promise](docs/rules/no-callback-in-promise.md) | Disallow calling `cb()` inside of a `then()` (use [util.callbackify][] instead). | || | |
| [no-multiple-resolved](docs/rules/no-multiple-resolved.md) | Disallow creating new promises with paths that resolve multiple times. | | | | |
| [no-native](docs/rules/no-native.md) | Require creating a `Promise` constructor before using it in an ES5 environment. | | | ✅ | |
| [no-nesting](docs/rules/no-nesting.md) | Disallow nested `then()` or `catch()` statements. | | ✅ | | |
| [no-new-statics](docs/rules/no-new-statics.md) | Disallow calling `new` on a Promise static method. | ✅ | | | 🔧 |
| [no-promise-in-callback](docs/rules/no-promise-in-callback.md) | Disallow using promises inside of callbacks. | | ✅ | | |
| [no-return-in-finally](docs/rules/no-return-in-finally.md) | Disallow return statements in `finally()`. | | ✅ | | |
| [no-return-wrap](docs/rules/no-return-wrap.md) | Disallow wrapping values in `Promise.resolve` or `Promise.reject` when not needed. | ✅ | | | |
| [param-names](docs/rules/param-names.md) | Enforce consistent param names and ordering when creating new promises. | ✅ | | | |
| [prefer-await-to-callbacks](docs/rules/prefer-await-to-callbacks.md) | Prefer `async`/`await` to the callback pattern. | | | | |
| [prefer-await-to-then](docs/rules/prefer-await-to-then.md) | Prefer `await` to `then()`/`catch()`/`finally()` for reading Promise values. | | | | |
| [valid-params](docs/rules/valid-params.md) | Enforces the proper number of arguments are passed to Promise functions. | | ✅ | | |

<!-- end auto-generated rules list -->

Expand All @@ -129,8 +129,10 @@ or start with the recommended rule set:
- (c) MMXV jden <mailto:[email protected]> - ISC license.
- (c) 2016 Jamund Ferguson <mailto:[email protected]> - ISC license.

[nodeify]: https://www.npmjs.com/package/nodeify
[pify]: https://www.npmjs.com/package/pify
[util.callbackify]:
https://nodejs.org/docs/latest/api/util.html#utilcallbackifyoriginal
[util.promisify]:
https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
[@aaditmshah]: https://github.com/aaditmshah
[@macklinu]: https://github.com/macklinu
[@xjamundx]: https://github.com/xjamundx
8 changes: 8 additions & 0 deletions __tests__/always-return.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ ruleTester.run('always-return', rule, {
'hey.then(x => { return; })',
'hey.then(x => { return x ? x.id : null })',
'hey.then(x => { return x * 10 })',
'hey.then(x => { process.exit(0); })',
'hey.then(x => { process.abort(); })',
'hey.then(function() { return 42; })',
'hey.then(function() { return new Promise(); })',
'hey.then(function() { return "x"; }).then(doSomethingWicked)',
'hey.then(x => x).then(function() { return "3" })',
'hey.then(function() { throw new Error("msg"); })',
'hey.then(function(x) { if (!x) { throw new Error("no x"); } return x; })',
'hey.then(function(x) { if (x) { return x; } throw new Error("no x"); })',
'hey.then(function(x) { if (x) { process.exit(0); } throw new Error("no x"); })',
'hey.then(function(x) { if (x) { process.abort(); } throw new Error("no x"); })',
'hey.then(x => { throw new Error("msg"); })',
'hey.then(x => { if (!x) { throw new Error("no x"); } return x; })',
'hey.then(x => { if (x) { return x; } throw new Error("no x"); })',
Expand Down Expand Up @@ -140,6 +144,10 @@ ruleTester.run('always-return', rule, {
code: 'hey.then(function() { if (x) { } else { return x; }})',
errors: [{ message }],
},
{
code: 'hey.then(function() { if (x) { process.chdir(); } else { return x; }})',
errors: [{ message }],
},
{
code: 'hey.then(function() { if (x) { return you.then(function() { return x; }); } })',
errors: [{ message }],
Expand Down
38 changes: 38 additions & 0 deletions __tests__/prefer-await-to-then.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ ruleTester.run('prefer-await-to-then', rule, {
'async function hi() { await thing() }',
'async function hi() { await thing().then() }',
'async function hi() { await thing().catch() }',
'async function hi() { await thing().finally() }',
'function * hi() { yield thing().then() }',
'a = async () => (await something())',
`a = async () => {
try { await something() } catch (error) { somethingElse() }
Expand Down Expand Up @@ -54,5 +56,41 @@ ruleTester.run('prefer-await-to-then', rule, {
code: 'function foo() { hey.finally(x => {}) }',
errors: [{ message }],
},
{
code: 'async function hi() { await thing().then() }',
errors: [{ message }],
options: [
{
strict: true,
},
],
},
{
code: 'async function hi() { await thing().catch() }',
errors: [{ message }],
options: [
{
strict: true,
},
],
},
{
code: 'async function hi() { await thing().finally() }',
errors: [{ message }],
options: [
{
strict: true,
},
],
},
{
code: 'function * hi() { yield thing().then() }',
errors: [{ message }],
options: [
{
strict: true,
},
],
},
],
})
5 changes: 3 additions & 2 deletions docs/rules/avoid-new.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Disallow creating `new` promises outside of utility libs (use [pify][] instead) (`promise/avoid-new`)
# Disallow creating `new` promises outside of utility libs (use [util.promisify][] instead) (`promise/avoid-new`)

🚫 This rule is _disabled_ in the following configs: ✅ `flat/recommended`, ✅
`recommended`.

<!-- end auto-generated rule header -->

[pify]: https://www.npmjs.com/package/pify
[util.promisify]:
https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
5 changes: 3 additions & 2 deletions docs/rules/no-callback-in-promise.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Disallow calling `cb()` inside of a `then()` (use [nodeify][] instead) (`promise/no-callback-in-promise`)
# Disallow calling `cb()` inside of a `then()` (use [util.callbackify][] instead) (`promise/no-callback-in-promise`)

⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅
`recommended`.
Expand Down Expand Up @@ -80,4 +80,5 @@ callback code instead of combining the approaches.

String list of callback function names to exempt.

[nodeify]: https://www.npmjs.com/package/nodeify
[util.callbackify]:
https://nodejs.org/docs/latest/api/util.html#utilcallbackifyoriginal
36 changes: 35 additions & 1 deletion docs/rules/prefer-await-to-callbacks.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
# Prefer async/await to the callback pattern (`promise/prefer-await-to-callbacks`)
# Prefer `async`/`await` to the callback pattern (`promise/prefer-await-to-callbacks`)

<!-- end auto-generated rule header -->

`async`/`await` is a clearer pattern to follow than using callbacks.

## Rule details

ES2017's `async`/`await` makes it easier to deal with asynchronous code than the
callback pattern.

Examples of **incorrect** code for this rule:

```js
cb()
callback()
doSomething(arg, (err) => {})
function doSomethingElse(cb) {}
```

Examples of **correct** code for this rule:

```js
await doSomething(arg)
async function doSomethingElse() {}
yield yieldValue(err => {})
eventEmitter.on('error', err => {})
```

## When Not To Use It

If you are not targeting an ES2017 or higher environment and cannot transpile
`async`/`await`, you should disable this rule.

## Further Reading

- [Making asynchronous programming easier with async and await on MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises)
15 changes: 15 additions & 0 deletions docs/rules/prefer-await-to-then.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,18 @@ function exampleFour() {
return myPromise.finally(cleanup)
}
```

## Options

### `strict`

Normally, this rule allows `then` or `catch` following an `await` (or `yield`)
expression. Setting this option to `true` will err on such cases:

This will fail with the `strict` option:

```js
async function hi() {
await thing().then()
}
```
1 change: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const pluginPromise = {
}
pluginPromise.configs = {
recommended: {
name: 'promise/recommended',
plugins: ['promise'],
rules: recommendedRules,
},
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": "https://opencollective.com/eslint"
}
4 changes: 4 additions & 0 deletions rules/always-return.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ module.exports = {
return {
'ReturnStatement:exit': markCurrentBranchAsGood,
'ThrowStatement:exit': markCurrentBranchAsGood,
'ExpressionStatement > CallExpression > MemberExpression[object.name="process"][property.name="exit"]:exit':
markCurrentBranchAsGood,
'ExpressionStatement > CallExpression > MemberExpression[object.name="process"][property.name="abort"]:exit':
markCurrentBranchAsGood,

/**
* @param {CodePathSegment} segment
Expand Down
2 changes: 1 addition & 1 deletion rules/avoid-new.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
type: 'suggestion',
docs: {
description:
'Disallow creating `new` promises outside of utility libs (use [pify][] instead).',
'Disallow creating `new` promises outside of utility libs (use [util.promisify][] instead).',
url: getDocsUrl('avoid-new'),
},
schema: [],
Expand Down
2 changes: 1 addition & 1 deletion rules/no-callback-in-promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
type: 'suggestion',
docs: {
description:
'Disallow calling `cb()` inside of a `then()` (use [nodeify][] instead).',
'Disallow calling `cb()` inside of a `then()` (use [util.callbackify][] instead).',
url: getDocsUrl('no-callback-in-promise'),
},
messages: {
Expand Down
2 changes: 1 addition & 1 deletion rules/prefer-await-to-callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Prefer async/await to the callback pattern.',
description: 'Prefer `async`/`await` to the callback pattern.',
url: getDocsUrl('prefer-await-to-callbacks'),
},
messages: {
Expand Down
15 changes: 13 additions & 2 deletions rules/prefer-await-to-then.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ module.exports = {
'Prefer `await` to `then()`/`catch()`/`finally()` for reading Promise values.',
url: getDocsUrl('prefer-await-to-then'),
},
schema: [],
schema: [
{
type: 'object',
properties: {
strict: {
type: 'boolean',
},
},
},
],
messages: {
preferAwaitToCallback: 'Prefer await to then()/catch()/finally().',
},
Expand All @@ -40,9 +49,11 @@ module.exports = {
return getScope(context, node).block.type === 'Program'
}

const { strict } = context.options[0] || {}

return {
'CallExpression > MemberExpression.callee'(node) {
if (isTopLevelScoped(node) || isInsideYieldOrAwait(node)) {
if (isTopLevelScoped(node) || (!strict && isInsideYieldOrAwait(node))) {
return
}

Expand Down

0 comments on commit e392e77

Please sign in to comment.