@onrails/eslint-plugin 0.1.0 → 0.1.1
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/package.json +3 -3
- package/src/index.js +48 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onrails/eslint-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "ESLint rules for @onrails/result boundaries — flags Promise<Result<…>> in public APIs and _unsafeUnwrap*. Replaces unmaintained eslint-plugin-neverthrow.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alan R. Soares",
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
-
"typecheck": "
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
53
|
"test": "bun test",
|
|
54
|
-
"check": "bun test"
|
|
54
|
+
"check": "bun run typecheck && bun test"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@typescript-eslint/parser": "^8.60.0",
|
package/src/index.js
CHANGED
|
@@ -6,9 +6,48 @@
|
|
|
6
6
|
* for TypeScript files; without it the rules never fire.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Loose AST-node shape — typescript-eslint nodes (TSTypeReference, …) are not
|
|
11
|
+
* part of ESLint's bundled estree types, so rules narrow on `type` manually.
|
|
12
|
+
*
|
|
13
|
+
* @param {{ type: string; name?: string } | null | undefined} node
|
|
14
|
+
* @param {string} name
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
9
17
|
const isIdentifierNamed = (node, name) =>
|
|
10
|
-
node && node.type === "Identifier" && node.name === name;
|
|
18
|
+
Boolean(node && node.type === "Identifier" && node.name === name);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Minimal structural view of `@typescript-eslint/parser`'s TSTypeReference —
|
|
22
|
+
* only the fields these rules read. ESLint's bundled types know estree alone,
|
|
23
|
+
* so TS-AST visitors type their own slice and bridge at report boundaries.
|
|
24
|
+
*
|
|
25
|
+
* @typedef {object} TsTypeReference
|
|
26
|
+
* @property {string} type
|
|
27
|
+
* @property {{ type: string; name?: string }} typeName
|
|
28
|
+
* @property {{ params: TsTypeReference[] } | undefined} [typeArguments]
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Safe: TS-AST nodes carry the same range/loc contract ESLint needs; the
|
|
33
|
+
* bundled estree types just cannot name them.
|
|
34
|
+
*
|
|
35
|
+
* @param {TsTypeReference} node
|
|
36
|
+
* @returns {import('eslint').Rule.Node}
|
|
37
|
+
*/
|
|
38
|
+
const asReportable = (node) =>
|
|
39
|
+
/** @type {import('eslint').Rule.Node} */ (/** @type {unknown} */ (node));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Safe: the `TSTypeReference` visitor key guarantees the parser produced a
|
|
43
|
+
* typescript-eslint TSTypeReference node.
|
|
44
|
+
*
|
|
45
|
+
* @param {import('eslint').Rule.Node} node
|
|
46
|
+
* @returns {TsTypeReference}
|
|
47
|
+
*/
|
|
48
|
+
const asTsTypeReference = (node) => /** @type {TsTypeReference} */ (/** @type {unknown} */ (node));
|
|
11
49
|
|
|
50
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
51
|
const noPromiseResult = {
|
|
13
52
|
meta: {
|
|
14
53
|
type: "problem",
|
|
@@ -25,18 +64,19 @@ const noPromiseResult = {
|
|
|
25
64
|
create(context) {
|
|
26
65
|
const sourceCode = context.sourceCode;
|
|
27
66
|
return {
|
|
28
|
-
TSTypeReference(node) {
|
|
29
|
-
|
|
30
|
-
|
|
67
|
+
TSTypeReference(/** @type {import('eslint').Rule.Node} */ node) {
|
|
68
|
+
const typeRef = asTsTypeReference(node);
|
|
69
|
+
if (!isIdentifierNamed(typeRef.typeName, "Promise")) return;
|
|
70
|
+
const args = typeRef.typeArguments?.params;
|
|
31
71
|
const inner = args?.[0];
|
|
32
|
-
if (
|
|
72
|
+
if (inner?.type !== "TSTypeReference") return;
|
|
33
73
|
if (!isIdentifierNamed(inner.typeName, "Result")) return;
|
|
34
74
|
|
|
35
75
|
const innerArgs = inner.typeArguments?.params;
|
|
36
76
|
const okT = innerArgs?.[0];
|
|
37
77
|
const errT = innerArgs?.[1];
|
|
38
|
-
const okText = okT ? sourceCode.getText(okT) : "T";
|
|
39
|
-
const errText = errT ? sourceCode.getText(errT) : "E";
|
|
78
|
+
const okText = okT ? sourceCode.getText(asReportable(okT)) : "T";
|
|
79
|
+
const errText = errT ? sourceCode.getText(asReportable(errT)) : "E";
|
|
40
80
|
|
|
41
81
|
context.report({
|
|
42
82
|
node,
|
|
@@ -49,6 +89,7 @@ const noPromiseResult = {
|
|
|
49
89
|
},
|
|
50
90
|
};
|
|
51
91
|
|
|
92
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
52
93
|
const noUnsafeUnwrap = {
|
|
53
94
|
meta: {
|
|
54
95
|
type: "suggestion",
|