@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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. 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.0",
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": "echo 'noop (js plugin)'",
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
- if (!isIdentifierNamed(node.typeName, "Promise")) return;
30
- const args = node.typeArguments?.params;
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 (!inner || inner.type !== "TSTypeReference") return;
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",