@antithrow/eslint-plugin 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/create-rule.d.ts +10 -0
- package/dist/create-rule.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +24 -0
- package/dist/rules/index.d.ts +1 -0
- package/dist/rules/index.js +1 -0
- package/dist/rules/no-unused-result.d.ts +4 -0
- package/dist/rules/no-unused-result.js +85 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@antithrow/eslint-plugin</h1>
|
|
3
|
+
<p>
|
|
4
|
+
ESLint rules for <a href="https://github.com/jack-weilage/antithrow">antithrow</a> Result types
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun add -d @antithrow/eslint-plugin
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This plugin requires [typed linting](https://typescript-eslint.io/getting-started/typed-linting/) to be configured.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Add the recommended config to your `eslint.config.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import antithrow from "@antithrow/eslint-plugin";
|
|
25
|
+
|
|
26
|
+
export default [
|
|
27
|
+
// ... your other configs
|
|
28
|
+
antithrow.configs.recommended,
|
|
29
|
+
];
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or configure rules individually:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import antithrow from "@antithrow/eslint-plugin";
|
|
36
|
+
|
|
37
|
+
export default [
|
|
38
|
+
{
|
|
39
|
+
plugins: {
|
|
40
|
+
"@antithrow": antithrow,
|
|
41
|
+
},
|
|
42
|
+
rules: {
|
|
43
|
+
"@antithrow/no-unused-result": "error",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Rules
|
|
50
|
+
|
|
51
|
+
| Rule | Description | Recommended |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| [`no-unused-result`](./docs/rules/no-unused-result.md) | Require `Result` and `ResultAsync` values to be used | `error` |
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
/** @lintignore */
|
|
3
|
+
export interface AntithrowPluginDocs {
|
|
4
|
+
description: string;
|
|
5
|
+
recommended?: boolean;
|
|
6
|
+
requiresTypeChecking?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, AntithrowPluginDocs>>) => ESLintUtils.RuleModule<MessageIds, Options, AntithrowPluginDocs, ESLintUtils.RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
2
|
+
import { noUnusedResult } from "./rules/index.js";
|
|
3
|
+
const plugin = {
|
|
4
|
+
meta: {
|
|
5
|
+
name: packageJson.name,
|
|
6
|
+
version: packageJson.version,
|
|
7
|
+
},
|
|
8
|
+
rules: {
|
|
9
|
+
"no-unused-result": noUnusedResult,
|
|
10
|
+
},
|
|
11
|
+
configs: {},
|
|
12
|
+
};
|
|
13
|
+
plugin.configs = {
|
|
14
|
+
...plugin.configs,
|
|
15
|
+
recommended: {
|
|
16
|
+
plugins: {
|
|
17
|
+
"@antithrow": plugin,
|
|
18
|
+
},
|
|
19
|
+
rules: {
|
|
20
|
+
"@antithrow/no-unused-result": "error",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export default plugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { noUnusedResult } from "./no-unused-result.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { noUnusedResult } from "./no-unused-result.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { createRule } from "../create-rule.js";
|
|
4
|
+
const RESULT_TYPE_NAMES = new Set(["Ok", "Err", "ResultAsync"]);
|
|
5
|
+
function isResultType(type) {
|
|
6
|
+
if (type.isUnion()) {
|
|
7
|
+
return type.types.some((t) => isResultType(t));
|
|
8
|
+
}
|
|
9
|
+
const flags = type.getFlags();
|
|
10
|
+
if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const symbol = type.getSymbol();
|
|
14
|
+
if (!symbol || !RESULT_TYPE_NAMES.has(symbol.getName())) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
18
|
+
return declarations.some((decl) => {
|
|
19
|
+
const sourceFile = decl.getSourceFile();
|
|
20
|
+
return sourceFile.fileName.includes("antithrow");
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export const noUnusedResult = createRule({
|
|
24
|
+
name: "no-unused-result",
|
|
25
|
+
meta: {
|
|
26
|
+
type: "problem",
|
|
27
|
+
docs: {
|
|
28
|
+
description: "Require Result and ResultAsync values to be used, preventing silently ignored errors.",
|
|
29
|
+
recommended: true,
|
|
30
|
+
requiresTypeChecking: true,
|
|
31
|
+
},
|
|
32
|
+
messages: {
|
|
33
|
+
unusedResult: "This Result must be used. Handle the error case or explicitly discard it with `void`.",
|
|
34
|
+
},
|
|
35
|
+
schema: [],
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [],
|
|
38
|
+
create(context) {
|
|
39
|
+
const services = ESLintUtils.getParserServices(context);
|
|
40
|
+
const checker = services.program.getTypeChecker();
|
|
41
|
+
function checkExpression(node) {
|
|
42
|
+
switch (node.type) {
|
|
43
|
+
case "UnaryExpression":
|
|
44
|
+
if (node.operator === "void") {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
case "ConditionalExpression":
|
|
49
|
+
checkExpression(node.consequent);
|
|
50
|
+
checkExpression(node.alternate);
|
|
51
|
+
return;
|
|
52
|
+
case "LogicalExpression":
|
|
53
|
+
checkExpression(node.left);
|
|
54
|
+
checkExpression(node.right);
|
|
55
|
+
return;
|
|
56
|
+
case "SequenceExpression":
|
|
57
|
+
for (const expr of node.expressions) {
|
|
58
|
+
checkExpression(expr);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
case "TSAsExpression":
|
|
62
|
+
case "TSNonNullExpression":
|
|
63
|
+
checkExpression(node.expression);
|
|
64
|
+
return;
|
|
65
|
+
case "ChainExpression":
|
|
66
|
+
checkExpression(node.expression);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
|
|
70
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
71
|
+
if (!isResultType(type)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
context.report({
|
|
75
|
+
node,
|
|
76
|
+
messageId: "unusedResult",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
ExpressionStatement(node) {
|
|
81
|
+
checkExpression(node.expression);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@antithrow/eslint-plugin",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "ESLint plugin for antithrow Result types",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Jack Weilage",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/jack-weilage/antithrow.git",
|
|
10
|
+
"directory": "packages/eslint-plugin"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"eslint",
|
|
14
|
+
"eslintplugin",
|
|
15
|
+
"result",
|
|
16
|
+
"error-handling",
|
|
17
|
+
"typescript"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -p tsconfig.build.json",
|
|
33
|
+
"clean": "rm -rf dist",
|
|
34
|
+
"lint": "bun run lint:publint && bun run lint:types",
|
|
35
|
+
"lint:publint": "publint",
|
|
36
|
+
"lint:types": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@typescript-eslint/utils": "8.54.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@typescript-eslint/parser": "8.54.0",
|
|
43
|
+
"@typescript-eslint/rule-tester": "8.54.0",
|
|
44
|
+
"antithrow": "workspace:*",
|
|
45
|
+
"eslint": "9.39.2",
|
|
46
|
+
"typescript": "5.9.3"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"eslint": "^9.0.0",
|
|
50
|
+
"typescript": ">=5.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|