Skip to content

Commit abd5a89

Browse files
committed
Initial
0 parents  commit abd5a89

File tree

9 files changed

+4177
-0
lines changed

9 files changed

+4177
-0
lines changed

.eslintrc.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use strict";
2+
3+
module.exports = {
4+
root: true,
5+
extends: [
6+
"eslint:recommended",
7+
"plugin:eslint-plugin/recommended",
8+
"plugin:node/recommended",
9+
],
10+
env: {
11+
node: true,
12+
},
13+
overrides: [
14+
{
15+
files: ["tests/**/*.js"],
16+
env: { mocha: true },
17+
},
18+
],
19+
};

.gitignore

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
.pnpm-debug.log*
9+
10+
# Diagnostic reports (https://nodejs.org/api/report.html)
11+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12+
13+
# Runtime data
14+
pids
15+
*.pid
16+
*.seed
17+
*.pid.lock
18+
19+
# Directory for instrumented libs generated by jscoverage/JSCover
20+
lib-cov
21+
22+
# Coverage directory used by tools like istanbul
23+
coverage
24+
*.lcov
25+
26+
# nyc test coverage
27+
.nyc_output
28+
29+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30+
.grunt
31+
32+
# Bower dependency directory (https://bower.io/)
33+
bower_components
34+
35+
# node-waf configuration
36+
.lock-wscript
37+
38+
# Compiled binary addons (https://nodejs.org/api/addons.html)
39+
build/Release
40+
41+
# Dependency directories
42+
node_modules/
43+
jspm_packages/
44+
45+
# Snowpack dependency directory (https://snowpack.dev/)
46+
web_modules/
47+
48+
# TypeScript cache
49+
*.tsbuildinfo
50+
51+
# Optional npm cache directory
52+
.npm
53+
54+
# Optional eslint cache
55+
.eslintcache
56+
57+
# Optional stylelint cache
58+
.stylelintcache
59+
60+
# Microbundle cache
61+
.rpt2_cache/
62+
.rts2_cache_cjs/
63+
.rts2_cache_es/
64+
.rts2_cache_umd/
65+
66+
# Optional REPL history
67+
.node_repl_history
68+
69+
# Output of 'npm pack'
70+
*.tgz
71+
72+
# Yarn Integrity file
73+
.yarn-integrity
74+
75+
# dotenv environment variable files
76+
.env
77+
.env.development.local
78+
.env.test.local
79+
.env.production.local
80+
.env.local
81+
82+
# parcel-bundler cache (https://parceljs.org/)
83+
.cache
84+
.parcel-cache
85+
86+
# Next.js build output
87+
.next
88+
out
89+
90+
# Nuxt.js build / generate output
91+
.nuxt
92+
dist
93+
94+
# Gatsby files
95+
.cache/
96+
# Comment in the public line in if your project uses Gatsby and not Next.js
97+
# https://nextjs.org/blog/next-9-1#public-directory-support
98+
# public
99+
100+
# vuepress build output
101+
.vuepress/dist
102+
103+
# vuepress v2.x temp and cache directory
104+
.temp
105+
.cache
106+
107+
# Docusaurus cache and generated files
108+
.docusaurus
109+
110+
# Serverless directories
111+
.serverless/
112+
113+
# FuseBox cache
114+
.fusebox/
115+
116+
# DynamoDB Local files
117+
.dynamodb/
118+
119+
# TernJS port file
120+
.tern-port
121+
122+
# Stores VSCode versions used for testing VSCode extensions
123+
.vscode-test
124+
125+
# yarn v2
126+
.yarn/cache
127+
.yarn/unplugged
128+
.yarn/build-state.yml
129+
.yarn/install-state.gz
130+
.pnp.*
131+
.DS_Store

README.md

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# eslint-plugin-no-throw-await
2+
3+
Forces explicit error handling for promises (Similar to golang) through disallowing unpredictable awaits
4+
5+
## Why?
6+
7+
`await` can't be trusted. and try-catching everything is 💩. Explicit error handling is the way. It doesn't clutter your code it makes it better. Take a look at the following code:
8+
```typescript
9+
async function foo() {
10+
await bar()
11+
}
12+
```
13+
Is `bar` safe to await? does it throw an exception maybe? Just in case let's wrap it in a try-catch
14+
```typescript
15+
async function foo() {
16+
try {
17+
await bar()
18+
} catch (e) {
19+
/* whatever */
20+
}
21+
}
22+
```
23+
Now assume you don't know what `foo` does. Or you don't want to read every async function line by line to check if it may throw an exception before using it. So what do you do? Also wrap `foo` in a try-catch just in case
24+
```typescript
25+
try { await foo() } catch (e) { }
26+
```
27+
When/how do you propgate an error to the caller? or do you silence everything throuh a try catch? What if you have a series of async functions. But you don't want one throw to stop everything. Do you just wrap every single one in a try-catch. Or worse, use `.catch` nesting hell. There are many other examples of how bad this trycatching can get, amongst other issues with throwing in an async func.
28+
29+
The goal of this plugin is to treat every promise as unsafe, which they are, and only allow awaiting a safe promise. A safe promise in this case means one that will not crash the application if left outside of a try-catch (will never throw). To to that, a linter rule will prevent you from awaiting a promise unless it's wrapped by a `awaitable` function.
30+
31+
## awaitable
32+
A function that turns unsafe promises into safe promises. One implementation (golang like error handling):
33+
```typescript
34+
/**
35+
* Guarantees that a promise throw will be handled and returned gracefully as an error if any
36+
* The returned promise will never throw an exception
37+
* Result and error type can be specified through awaitable<ResultType, ErrorType>
38+
* @param fn Promise
39+
* @returns Promise<[result, error]>
40+
* - `result`: Returned on success, null on throw (Infered type of `fn`)
41+
* - `error`: Null on success, returned on throw (Default to Error)
42+
*/
43+
/* Modified version from karanpratapsingh */
44+
async function awaitable<R, E = Error> (
45+
fn: Promise<R>
46+
): Promise<[R | null, E | null]> {
47+
// eslint-disable-next-line no-try-catch/no-try-catch
48+
try {
49+
// eslint-disable-next-line no-try-catch/no-direct-await
50+
const data: R = await fn
51+
return [data, null]
52+
} catch (error: any) {
53+
return [null, error]
54+
}
55+
}
56+
```
57+
58+
## Example
59+
```typescript
60+
async function foo (): Promise<boolean> {
61+
throw new Error('Some error')
62+
}
63+
64+
async function testing (): Promise<void> {
65+
const [result, err] = await awaitable(foo())
66+
if (err != null) {
67+
console.log(err.message)
68+
return
69+
}
70+
71+
// Do stuff with result
72+
console.log(result)
73+
}
74+
```
75+
76+
77+
## Installation
78+
79+
You'll first need to install [ESLint](https://eslint.org/):
80+
81+
```sh
82+
npm i eslint --save-dev
83+
```
84+
85+
Next, install `eslint-plugin-no-throw-await`:
86+
87+
```sh
88+
npm install eslint-plugin-no-throw-await --save-dev
89+
```
90+
91+
## Usage
92+
93+
Add `no-throw-await` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
94+
95+
```json
96+
{
97+
"plugins": [
98+
"no-throw-await"
99+
]
100+
}
101+
```
102+
103+
104+
Then configure the rule under the rules section.
105+
106+
```json
107+
{
108+
"rules": {
109+
"no-throw-await/no-direct-await": "error"
110+
}
111+
}
112+
```
113+
114+
## Rules
115+
116+
<!-- begin auto-generated rules list -->
117+
118+
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
119+
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
120+
121+
| Name | Description | 🔧 | 💡 |
122+
| :----------------------------------------------- | :------------------------------------------------- | :- | :- |
123+
| [no-direct-await](docs/rules/no-direct-await.md) | Enforces using an await handler before every await | 🔧 | 💡 |
124+
125+
<!-- end auto-generated rules list -->
126+
127+

docs/rules/no-direct-await.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Enforces using an await handler before every await (`no-throw-await/no-direct-await`)
2+
3+
🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
4+
5+
<!-- end auto-generated rule header -->
6+
7+
The goal of this rule is to provide a way of error handling that is similar to golang. [Why?](../../README.md#Why)
8+
9+
## Rule Details
10+
11+
This rule aims to enforces using of an await handler before every await
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```js
16+
// 1
17+
await someAsyncFunc()
18+
// 2
19+
const aPromise = new Promise()
20+
await aPromise
21+
// 3
22+
await new Promise()
23+
```
24+
25+
Examples of **correct** code for this rule:
26+
27+
```js
28+
// 1
29+
await awaitable(someAsyncFunc())
30+
// 2
31+
const aPromise = new Promise()
32+
await awaitable(aPromise)
33+
// 3
34+
await awaitable(new Promise())
35+
```

lib/index.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @fileoverview Forces explicit error handling through disallowing try-catch, and regular awaits.
3+
* @author wes4m
4+
*/
5+
"use strict";
6+
7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
const requireIndex = require("requireindex");
12+
13+
//------------------------------------------------------------------------------
14+
// Plugin Definition
15+
//------------------------------------------------------------------------------
16+
17+
18+
// import all rules in lib/rules
19+
module.exports.rules = requireIndex(__dirname + "/rules");

0 commit comments

Comments
 (0)