Skip to content

Commit d22f550

Browse files
committed
feat: base plugin handling user created exceptions
0 parents  commit d22f550

15 files changed

+2553
-0
lines changed

.gitignore

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Created by https://www.toptal.com/developers/gitignore/api/node,yarn,windows,macos,linux
2+
# Edit at https://www.toptal.com/developers/gitignore?templates=node,yarn,windows,macos,linux
3+
4+
test
5+
6+
### Linux ###
7+
*~
8+
9+
# temporary files which can be created if a process still has a handle open of a deleted file
10+
.fuse_hidden*
11+
12+
# KDE directory preferences
13+
.directory
14+
15+
# Linux trash folder which might appear on any partition or disk
16+
.Trash-*
17+
18+
# .nfs files are created when an open file is removed but is still being accessed
19+
.nfs*
20+
21+
### macOS ###
22+
# General
23+
.DS_Store
24+
.AppleDouble
25+
.LSOverride
26+
27+
# Icon must end with two \r
28+
Icon
29+
30+
31+
# Thumbnails
32+
._*
33+
34+
# Files that might appear in the root of a volume
35+
.DocumentRevisions-V100
36+
.fseventsd
37+
.Spotlight-V100
38+
.TemporaryItems
39+
.Trashes
40+
.VolumeIcon.icns
41+
.com.apple.timemachine.donotpresent
42+
43+
# Directories potentially created on remote AFP share
44+
.AppleDB
45+
.AppleDesktop
46+
Network Trash Folder
47+
Temporary Items
48+
.apdisk
49+
50+
### macOS Patch ###
51+
# iCloud generated files
52+
*.icloud
53+
54+
### Node ###
55+
# Logs
56+
logs
57+
*.log
58+
npm-debug.log*
59+
yarn-debug.log*
60+
yarn-error.log*
61+
lerna-debug.log*
62+
.pnpm-debug.log*
63+
64+
# Diagnostic reports (https://nodejs.org/api/report.html)
65+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
66+
67+
# Runtime data
68+
pids
69+
*.pid
70+
*.seed
71+
*.pid.lock
72+
73+
# Directory for instrumented libs generated by jscoverage/JSCover
74+
lib-cov
75+
76+
# Coverage directory used by tools like istanbul
77+
coverage
78+
*.lcov
79+
80+
# nyc test coverage
81+
.nyc_output
82+
83+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
84+
.grunt
85+
86+
# Bower dependency directory (https://bower.io/)
87+
bower_components
88+
89+
# node-waf configuration
90+
.lock-wscript
91+
92+
# Compiled binary addons (https://nodejs.org/api/addons.html)
93+
build/Release
94+
95+
# Dependency directories
96+
node_modules/
97+
jspm_packages/
98+
99+
# Snowpack dependency directory (https://snowpack.dev/)
100+
web_modules/
101+
102+
# TypeScript cache
103+
*.tsbuildinfo
104+
105+
# Optional npm cache directory
106+
.npm
107+
108+
# Optional eslint cache
109+
.eslintcache
110+
111+
# Optional stylelint cache
112+
.stylelintcache
113+
114+
# Microbundle cache
115+
.rpt2_cache/
116+
.rts2_cache_cjs/
117+
.rts2_cache_es/
118+
.rts2_cache_umd/
119+
120+
# Optional REPL history
121+
.node_repl_history
122+
123+
# Output of 'npm pack'
124+
*.tgz
125+
126+
# Yarn Integrity file
127+
.yarn-integrity
128+
129+
# dotenv environment variable files
130+
.env
131+
.env.development.local
132+
.env.test.local
133+
.env.production.local
134+
.env.local
135+
136+
# parcel-bundler cache (https://parceljs.org/)
137+
.cache
138+
.parcel-cache
139+
140+
# Next.js build output
141+
.next
142+
out
143+
144+
# Nuxt.js build / generate output
145+
.nuxt
146+
dist
147+
148+
# Gatsby files
149+
.cache/
150+
# Comment in the public line in if your project uses Gatsby and not Next.js
151+
# https://nextjs.org/blog/next-9-1#public-directory-support
152+
# public
153+
154+
# vuepress build output
155+
.vuepress/dist
156+
157+
# vuepress v2.x temp and cache directory
158+
.temp
159+
160+
# Docusaurus cache and generated files
161+
.docusaurus
162+
163+
# Serverless directories
164+
.serverless/
165+
166+
# FuseBox cache
167+
.fusebox/
168+
169+
# DynamoDB Local files
170+
.dynamodb/
171+
172+
# TernJS port file
173+
.tern-port
174+
175+
# Stores VSCode versions used for testing VSCode extensions
176+
.vscode-test
177+
178+
# yarn v2
179+
.yarn/cache
180+
.yarn/unplugged
181+
.yarn/build-state.yml
182+
.yarn/install-state.gz
183+
.pnp.*
184+
185+
### Node Patch ###
186+
# Serverless Webpack directories
187+
.webpack/
188+
189+
# Optional stylelint cache
190+
191+
# SvelteKit build / generate output
192+
.svelte-kit
193+
194+
### Windows ###
195+
# Windows thumbnail cache files
196+
Thumbs.db
197+
Thumbs.db:encryptable
198+
ehthumbs.db
199+
ehthumbs_vista.db
200+
201+
# Dump file
202+
*.stackdump
203+
204+
# Folder config file
205+
[Dd]esktop.ini
206+
207+
# Recycle Bin used on file shares
208+
$RECYCLE.BIN/
209+
210+
# Windows Installer files
211+
*.cab
212+
*.msi
213+
*.msix
214+
*.msm
215+
*.msp
216+
217+
# Windows shortcuts
218+
*.lnk
219+
220+
### yarn ###
221+
# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
222+
223+
.yarn/*
224+
!.yarn/releases
225+
!.yarn/patches
226+
!.yarn/plugins
227+
!.yarn/sdks
228+
!.yarn/versions
229+
230+
# if you are NOT using Zero-installs, then:
231+
# comment the following lines
232+
!.yarn/cache
233+
234+
# and uncomment the following lines
235+
# .pnp.*
236+
237+
# End of https://www.toptal.com/developers/gitignore/api/node,yarn,windows,macos,linux

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v20.11.1

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!-- begin auto-generated rules list -->
2+
3+
| Name | Description |
4+
| :----------------------------------------- | :-------------------------------------------------------- |
5+
| [no-unhandled](docs/rules/no-unhandled.md) | Enforce handling of functions that might throw exceptions |
6+
7+
<!-- end auto-generated rules list -->

docs/rules/no-unhandled.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Enforce handling of functions that might throw exceptions (`exception-handling/no-unhandled`)
2+
3+
<!-- end auto-generated rule header -->

eslint.config.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @ts-check
2+
3+
import eslint from "@eslint/js";
4+
import tseslint from "typescript-eslint";
5+
import { plugin as ex } from "./dist/index.mjs";
6+
7+
export default tseslint.config(
8+
eslint.configs.recommended,
9+
...tseslint.configs.recommended,
10+
{ plugins: { ex }, rules: { "ex/my-rule": "error" } }
11+
);

package.json

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "eslint-plugin-exception-handling",
3+
"version": "1.0.0",
4+
"description": "An eslint plugin that enforces exception (Error) handling",
5+
"repository": "https://www.github.com/akronae/eslint-plugin-exception-handling",
6+
"author": "Alexandre Daubricourt",
7+
"license": "MIT",
8+
"type": "module",
9+
"engines": {
10+
"node": ">=20"
11+
},
12+
"main": "./dist/index.cjs",
13+
"module": "./dist/index.mjs",
14+
"types": "./dist/index.d.cts",
15+
"exports": {
16+
"require": {
17+
"types": "./dist/index.d.cts",
18+
"default": "./dist/index.cjs"
19+
},
20+
"import": {
21+
"types": "./dist/index.d.mts",
22+
"default": "./dist/index.mjs"
23+
}
24+
},
25+
"imports": {
26+
"@": "./"
27+
},
28+
"scripts": {
29+
"build": "pkgroll",
30+
"docs": "yarn build && eslint-doc-generator --init-rule-docs"
31+
},
32+
"dependencies": {
33+
"@types/eslint": "^8.56.10",
34+
"@typescript-eslint/types": "^7.9.0",
35+
"@typescript-eslint/utils": "^7.9.0",
36+
"eslint": "^9.3.0"
37+
},
38+
"devDependencies": {
39+
"@typescript-eslint/parser": "^7.9.0",
40+
"@typescript-eslint/rule-tester": "^7.9.0",
41+
"eslint-doc-generator": "^1.7.1",
42+
"pkgroll": "^2.1.0",
43+
"typescript": "^5.4.5",
44+
"typescript-eslint": "^7.9.0",
45+
"vitest": "^1.6.0"
46+
}
47+
}

src/index.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { RuleModule } from "@typescript-eslint/utils/ts-eslint";
2+
import { ESLint } from "eslint";
3+
import { rules } from "./rules";
4+
import noUnhandled from "./rules/no-unhandled";
5+
6+
type RuleKey = keyof typeof rules;
7+
8+
interface Plugin extends Omit<ESLint.Plugin, "rules"> {
9+
rules: Record<RuleKey, RuleModule<string>>;
10+
}
11+
12+
export const name = "eslint-plugin-exception-handling";
13+
14+
export const plugin: Plugin = {
15+
meta: {
16+
name,
17+
version: "1.0.0",
18+
},
19+
configs: {
20+
recommended: {
21+
plugins: [name],
22+
rules: {
23+
[`${name}/${noUnhandled.name}`]: "error",
24+
},
25+
},
26+
},
27+
rules,
28+
};
29+
30+
export { rules };

src/rules/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import myRule from "./no-unhandled";
2+
3+
export const rules = {
4+
[myRule.name]: myRule.rule,
5+
};

src/rules/no-unhandled.spec.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as vitest from "vitest";
2+
import { RuleTester } from "@typescript-eslint/rule-tester";
3+
4+
RuleTester.afterAll = vitest.afterAll;
5+
RuleTester.it = vitest.it;
6+
RuleTester.itOnly = vitest.it.only;
7+
RuleTester.describe = vitest.describe;
8+
9+
import myRule from "./no-unhandled";
10+
import * as plugin from "..";
11+
12+
const ruleTester = new RuleTester({
13+
parser: "@typescript-eslint/parser",
14+
plugins: [plugin.name],
15+
rules: {
16+
[myRule.name]: "error",
17+
},
18+
});
19+
20+
ruleTester.run(`${plugin.name}/${myRule.name}`, myRule.rule, {
21+
valid: [
22+
{
23+
// a code snippet that should pass the linter
24+
code: `const x = 5;`,
25+
},
26+
{
27+
code: `let y = 'abc123';`,
28+
},
29+
{
30+
code: `myFunction();`,
31+
},
32+
{
33+
code: `const a = new Error();`,
34+
},
35+
],
36+
invalid: [
37+
{
38+
code: `throw new Error("heyyyy!");`,
39+
errors: [
40+
{
41+
messageId: "noUnhandled",
42+
},
43+
],
44+
},
45+
],
46+
});

0 commit comments

Comments
 (0)