Skip to content

Commit 3bbbccc

Browse files
committed
refactor: extract rule listener
1 parent 4728b48 commit 3bbbccc

File tree

5 files changed

+66
-60
lines changed

5 files changed

+66
-60
lines changed

src/rules/parameter-prefix.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
1-
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
1+
import { ESLintUtils } from "@typescript-eslint/utils";
22

33
import { does_all_named_parameters_start_with_prefix } from "../parser/parser.js";
4-
import { getQueryValue } from "../utils.js";
4+
import { getQueryValue, makeRuleListener } from "../utils.js";
55

66
export const parameterPrefixRule = ESLintUtils.RuleCreator.withoutDocs({
77
create(context, options) {
88
const expectedPrefix = options[0];
99

10-
return {
11-
'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]'(
12-
node: Omit<TSESTree.CallExpression, "arguments" | "callee"> & {
13-
arguments: [TSESTree.CallExpression["arguments"][0]];
14-
callee: TSESTree.MemberExpression;
15-
},
16-
) {
17-
const arg = node.arguments[0];
18-
19-
const val = getQueryValue(arg, context.sourceCode.getScope(arg));
10+
return makeRuleListener({
11+
handleQuery({ queryNode }) {
12+
const val = getQueryValue(
13+
queryNode,
14+
context.sourceCode.getScope(queryNode),
15+
);
2016

2117
if (typeof val?.value !== "string") {
2218
return;
@@ -29,15 +25,15 @@ export const parameterPrefixRule = ESLintUtils.RuleCreator.withoutDocs({
2925

3026
if (result === false) {
3127
context.report({
32-
node: arg,
28+
node: queryNode,
3329
messageId: "incorrectPrefixUsed",
3430
data: {
3531
prefix: expectedPrefix,
3632
},
3733
});
3834
}
3935
},
40-
};
36+
});
4137
},
4238
meta: {
4339
messages: {

src/rules/typed-input.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import { ASTUtils, ESLintUtils, TSESTree } from "@typescript-eslint/utils";
22

33
import { inferQueryInput, type QueryInput } from "../inferQueryInput.js";
4-
import { getQueryValue } from "../utils.js";
4+
import { getQueryValue, makeRuleListener } from "../utils.js";
55

66
export const typedInputRule = ESLintUtils.RuleCreator.withoutDocs({
77
create(context) {
8-
return {
9-
'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]'(
10-
node: Omit<TSESTree.CallExpression, "arguments" | "callee"> & {
11-
arguments: [TSESTree.CallExpression["arguments"][0]];
12-
callee: TSESTree.MemberExpression;
13-
},
14-
) {
8+
return makeRuleListener({
9+
handleQuery({ typeArguments, rootNode, queryNode, callee }) {
1510
const val = getQueryValue(
16-
node.arguments[0],
17-
context.sourceCode.getScope(node.arguments[0]),
11+
queryNode,
12+
context.sourceCode.getScope(queryNode),
1813
);
1914
if (typeof val?.value !== "string") {
2015
return;
@@ -25,11 +20,10 @@ export const typedInputRule = ESLintUtils.RuleCreator.withoutDocs({
2520
return;
2621
}
2722

28-
const typeArguments = node.typeArguments;
2923
const inputParam = typeArguments?.params[0];
3024
if (!typeArguments || !inputParam) {
3125
context.report({
32-
node: node,
26+
node: rootNode,
3327
messageId: "missingInputType",
3428
*fix(fixer) {
3529
if (typeArguments && !inputParam) {
@@ -39,7 +33,7 @@ export const typedInputRule = ESLintUtils.RuleCreator.withoutDocs({
3933
);
4034
} else {
4135
yield fixer.insertTextAfter(
42-
node.callee,
36+
callee,
4337
`<${queryInputToText(queryInput)}>`,
4438
);
4539
}
@@ -99,7 +93,7 @@ export const typedInputRule = ESLintUtils.RuleCreator.withoutDocs({
9993
},
10094
});
10195
},
102-
};
96+
});
10397
},
10498
meta: {
10599
messages: {

src/rules/typed-result.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,24 @@ import {
66
inferQueryResult,
77
} from "../inferQueryResult.js";
88
import type { RuleOptions } from "../ruleOptions.js";
9-
import { getQueryValue, stringifyNode } from "../utils.js";
9+
import { getQueryValue, makeRuleListener, stringifyNode } from "../utils.js";
1010

1111
type ColumnInfoWithUserType = ColumnInfo & { userTSTypeAnnotation?: string };
1212

1313
export function createTypedResultRule(options: RuleOptions) {
1414
return ESLintUtils.RuleCreator.withoutDocs({
1515
create(context) {
16-
return {
17-
'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]'(
18-
node: Omit<TSESTree.CallExpression, "arguments" | "callee"> & {
19-
arguments: [TSESTree.CallExpression["arguments"][0]];
20-
callee: TSESTree.MemberExpression;
21-
},
22-
) {
16+
return makeRuleListener({
17+
handleQuery({ queryNode, callee, typeArguments, rootNode }) {
2318
const val = getQueryValue(
24-
node.arguments[0],
25-
context.sourceCode.getScope(node.arguments[0]),
19+
queryNode,
20+
context.sourceCode.getScope(queryNode),
2621
);
2722
if (typeof val?.value !== "string") {
2823
return;
2924
}
3025

31-
const databaseName = stringifyNode(node.callee.object);
26+
const databaseName = stringifyNode(callee.object);
3227
if (!databaseName) {
3328
return;
3429
}
@@ -46,7 +41,6 @@ export function createTypedResultRule(options: RuleOptions) {
4641
return;
4742
}
4843

49-
const typeArguments = node.typeArguments;
5044
if (columns.length === 0) {
5145
const inputNode = typeArguments?.params[0];
5246
const resultNode = typeArguments?.params[1];
@@ -69,10 +63,10 @@ export function createTypedResultRule(options: RuleOptions) {
6963
if (!typeArguments) {
7064
context.report({
7165
messageId: "missingResultType",
72-
node,
66+
node: rootNode,
7367
*fix(fixer) {
7468
yield fixer.insertTextAfter(
75-
node.callee,
69+
callee,
7670
`<[], ${columnsToObjectLiteralText(columns)}>`,
7771
);
7872
},
@@ -172,7 +166,7 @@ export function createTypedResultRule(options: RuleOptions) {
172166
return;
173167
}
174168
},
175-
};
169+
});
176170
},
177171
meta: {
178172
messages: {

src/rules/valid-query.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,38 @@
1-
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
1+
import { ESLintUtils } from "@typescript-eslint/utils";
22

33
import type { RuleOptions } from "../ruleOptions.js";
4-
import { getQueryValue, stringifyNode } from "../utils.js";
4+
import { getQueryValue, makeRuleListener, stringifyNode } from "../utils.js";
55

66
export function createValidQueryRule(options: RuleOptions) {
77
return ESLintUtils.RuleCreator.withoutDocs({
88
create(context) {
9-
return {
10-
'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]'(
11-
node: Omit<TSESTree.CallExpression, "arguments" | "callee"> & {
12-
arguments: [TSESTree.CallExpression["arguments"][0]];
13-
callee: TSESTree.MemberExpression;
14-
},
15-
) {
16-
const arg = node.arguments[0];
17-
18-
const val = getQueryValue(arg, context.sourceCode.getScope(arg));
9+
return makeRuleListener({
10+
handleQuery({ queryNode, callee }) {
11+
const val = getQueryValue(
12+
queryNode,
13+
context.sourceCode.getScope(queryNode),
14+
);
1915

2016
if (!val) {
2117
context.report({
2218
messageId: "nonStaticQuery",
23-
node: arg,
19+
node: queryNode,
2420
});
2521
return;
2622
}
2723

2824
if (typeof val.value !== "string") {
2925
context.report({
3026
messageId: "invalidQuery",
31-
node: arg,
27+
node: queryNode,
3228
data: {
3329
message: `typeof ${typeof val.value} is not a valid query`,
3430
},
3531
});
3632
return;
3733
}
3834

39-
const databaseName = stringifyNode(node.callee.object);
35+
const databaseName = stringifyNode(callee.object);
4036
if (!databaseName) {
4137
return;
4238
}
@@ -51,7 +47,7 @@ export function createValidQueryRule(options: RuleOptions) {
5147
} catch (error) {
5248
context.report({
5349
messageId: "invalidQuery",
54-
node: arg,
50+
node: queryNode,
5551
data: {
5652
message:
5753
error && typeof error === "object" && "message" in error
@@ -61,7 +57,7 @@ export function createValidQueryRule(options: RuleOptions) {
6157
});
6258
}
6359
},
64-
};
60+
});
6561
},
6662
meta: {
6763
messages: {

src/utils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ASTUtils, TSESLint, TSESTree } from "@typescript-eslint/utils";
2+
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
23

34
export function stringifyNode(
45
node: TSESTree.Expression | TSESTree.PrivateIdentifier,
@@ -140,3 +141,28 @@ function getParameterExpressionValue(expr: TSESTree.Expression) {
140141

141142
return mapValue.value;
142143
}
144+
145+
type HandleQueryFN = (data: {
146+
queryNode: TSESTree.CallExpression["arguments"][0];
147+
callee: TSESTree.MemberExpression;
148+
typeArguments: TSESTree.TSTypeParameterInstantiation | undefined;
149+
rootNode: TSESTree.Node;
150+
}) => void;
151+
152+
export function makeRuleListener(opts: { handleQuery: HandleQueryFN }) {
153+
return {
154+
'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]'(
155+
node: Omit<TSESTree.CallExpression, "arguments" | "callee"> & {
156+
arguments: [TSESTree.CallExpression["arguments"][0]];
157+
callee: TSESTree.MemberExpression;
158+
},
159+
) {
160+
opts.handleQuery({
161+
queryNode: node.arguments[0],
162+
callee: node.callee,
163+
typeArguments: node.typeArguments,
164+
rootNode: node,
165+
});
166+
},
167+
} satisfies RuleListener;
168+
}

0 commit comments

Comments
 (0)