eslint-plugin-slonik 1.6.1 → 1.8.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 CHANGED
@@ -42,24 +42,23 @@ export default [
42
42
  overrides: {
43
43
  types: {
44
44
  // Map PostgreSQL types to Slonik token types
45
- date: "DateSqlToken",
46
- timestamp: "TimestampSqlToken",
47
- interval: "IntervalSqlToken",
48
- json: "JsonSqlToken",
49
- jsonb: "JsonBinarySqlToken",
50
- uuid: "UuidSqlToken",
51
- "int4[]": 'ArraySqlToken<"int4">',
52
- "text[]": 'ArraySqlToken<"text">',
53
- "uuid[]": 'ArraySqlToken<"uuid">',
54
- "numeric[]": 'ArraySqlToken<"numeric">',
55
- "real[]": "VectorSqlToken",
45
+ date: 'DateSqlToken',
46
+ timestamp: 'TimestampSqlToken',
47
+ interval: 'IntervalSqlToken',
48
+ json: 'JsonSqlToken',
49
+ jsonb: 'JsonBinarySqlToken',
50
+ uuid: 'UuidSqlToken',
51
+ 'int4[]': 'ArraySqlToken<"int4">',
52
+ 'text[]': 'ArraySqlToken<"text">',
53
+ 'uuid[]': 'ArraySqlToken<"uuid">',
54
+ 'numeric[]': 'ArraySqlToken<"numeric">',
55
+ 'real[]': 'VectorSqlToken',
56
56
  },
57
57
  },
58
58
  targets: [
59
59
  {
60
60
  // Match Slonik's typed query methods
61
- tag: "sql.+(type\\(*\\)|typeAlias\\(*\\)|unsafe)",
62
- skipTypeAnnotations: true,
61
+ tag: 'sql.+(type\\(*\\)|typeAlias\\(*\\)|unsafe)',
63
62
  },
64
63
  ],
65
64
  }),
@@ -131,14 +130,14 @@ When using Slonik, you'll want to map PostgreSQL types to Slonik's token types:
131
130
  overrides: {
132
131
  types: {
133
132
  // Date/Time types
134
- date: "DateSqlToken",
135
- timestamp: "TimestampSqlToken",
133
+ date: 'DateSqlToken',
134
+ timestamp: 'TimestampSqlToken',
136
135
  timestamptz: "TimestampSqlToken",
137
- interval: "IntervalSqlToken",
136
+ interval: 'IntervalSqlToken',
138
137
 
139
138
  // JSON types
140
- json: "JsonSqlToken",
141
- jsonb: "JsonBinarySqlToken",
139
+ json: 'JsonSqlToken',
140
+ jsonb: 'JsonBinarySqlToken',
142
141
 
143
142
  // UUID
144
143
  uuid: "UuidSqlToken",
@@ -167,7 +166,6 @@ targets: [
167
166
  {
168
167
  // Matches: sql.type(...)``, sql.typeAlias(...)``, sql.unsafe``
169
168
  tag: "sql.+(type\\(*\\)|typeAlias\\(*\\)|unsafe)",
170
- skipTypeAnnotations: true,
171
169
  },
172
170
  ]
173
171
  ```
@@ -209,19 +207,18 @@ export default tseslint.config(
209
207
  databaseUrl: process.env.DATABASE_URL,
210
208
  overrides: {
211
209
  types: {
212
- date: "DateSqlToken",
213
- timestamp: "TimestampSqlToken",
214
- json: "JsonSqlToken",
215
- jsonb: "JsonBinarySqlToken",
216
- uuid: "UuidSqlToken",
217
- "int4[]": 'ArraySqlToken<"int4">',
218
- "text[]": 'ArraySqlToken<"text">',
210
+ date: 'DateSqlToken',
211
+ timestamp: 'TimestampSqlToken',
212
+ json: 'JsonSqlToken',
213
+ jsonb: 'JsonBinarySqlToken',
214
+ uuid: 'UuidSqlToken',
215
+ 'int4[]': 'ArraySqlToken<"int4">',
216
+ 'text[]': 'ArraySqlToken<"text">',
219
217
  },
220
218
  },
221
219
  targets: [
222
220
  {
223
- tag: "sql.+(type\\(*\\)|typeAlias\\(*\\)|unsafe)",
224
- skipTypeAnnotations: true,
221
+ tag: 'sql.+(type\\(*\\)|typeAlias\\(*\\)|unsafe)',
225
222
  },
226
223
  ],
227
224
  })
@@ -273,6 +270,18 @@ This plugin is specifically designed for Slonik and includes:
273
270
  4. **Identifier support** — Converts `sql.identifier()` to quoted identifiers
274
271
  5. **Graceful degradation** — Skips validation for runtime-dependent constructs instead of erroring
275
272
 
273
+ ## How It Works
274
+
275
+ ESLint rules must be synchronous, but SQL validation requires async operations like database connections. This plugin solves this using [`synckit`](https://github.com/un-ts/synckit), which enables synchronous calls to async worker threads.
276
+
277
+ The architecture:
278
+
279
+ 1. **Worker Thread** — Runs all async operations (database connections, migrations, type generation) in a separate thread
280
+ 2. **Synchronous Bridge** — Uses `synckit` to block the main thread until the worker completes, making async operations appear synchronous to ESLint
281
+ 3. **Connection Pooling** — Reuses database connections across lint runs for performance
282
+
283
+ Under the hood, `synckit` uses Node.js Worker Threads with `Atomics.wait()` to block the main thread until the worker signals completion via `Atomics.notify()`.
284
+
276
285
  ## Development
277
286
 
278
287
  ### Prerequisites
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const checkSql_utils = require('./shared/eslint-plugin-slonik.rlOTrCdf.cjs');
3
+ const checkSql_utils = require('./shared/eslint-plugin-slonik.kniz1QWh.cjs');
4
4
  const utils = require('@typescript-eslint/utils');
5
5
  const tsPattern = require('ts-pattern');
6
6
  const E = require('fp-ts/lib/Either.js');
@@ -8,9 +8,9 @@ const function_js = require('fp-ts/lib/function.js');
8
8
  require('fp-ts/lib/Option.js');
9
9
  require('fp-ts/lib/TaskEither.js');
10
10
  const J = require('fp-ts/lib/Json.js');
11
- const ts = require('typescript');
12
11
  const path = require('path');
13
12
  const fs = require('fs');
13
+ const ts = require('typescript');
14
14
  const synckit = require('synckit');
15
15
  const node_url = require('node:url');
16
16
  const z = require('zod');
@@ -35,226 +35,16 @@ function _interopNamespaceCompat(e) {
35
35
 
36
36
  const E__namespace = /*#__PURE__*/_interopNamespaceCompat(E);
37
37
  const J__namespace = /*#__PURE__*/_interopNamespaceCompat(J);
38
- const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
39
38
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
40
39
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
40
+ const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
41
41
  const z__default = /*#__PURE__*/_interopDefaultCompat(z);
42
42
 
43
- const PRIMITIVES = {
44
- string: "string",
45
- number: "number",
46
- boolean: "boolean",
47
- false: "false",
48
- true: "true",
49
- null: "null",
50
- undefined: "undefined",
51
- any: "any"
52
- };
53
- function getResolvedTargetByTypeNode(params) {
54
- if (!params.typeNode) {
55
- console.error("[slonik/check-sql] DEBUG: typeNode is undefined in getResolvedTargetByTypeNode");
56
- throw new Error("typeNode is undefined");
57
- }
58
- const tsNode = params.parser.esTreeNodeToTSNodeMap.get(params.typeNode);
59
- if (!tsNode) {
60
- console.error("[slonik/check-sql] DEBUG: Could not map ESTree node to TS node. typeNode.type:", params.typeNode.type);
61
- throw new Error(`Could not map ESTree node (type: ${params.typeNode.type}) to TS node`);
62
- }
63
- const typeText = tsNode.getText();
64
- if (isReservedType(typeText, params.reservedTypes)) {
65
- return { kind: "type", value: typeText };
66
- }
67
- switch (params.typeNode.type) {
68
- case utils.TSESTree.AST_NODE_TYPES.TSLiteralType:
69
- return handleLiteralType(params.typeNode);
70
- case utils.TSESTree.AST_NODE_TYPES.TSUnionType:
71
- return {
72
- kind: "union",
73
- value: params.typeNode.types.map(
74
- (type) => getResolvedTargetByTypeNode({ ...params, typeNode: type })
75
- )
76
- };
77
- case utils.TSESTree.AST_NODE_TYPES.TSNullKeyword:
78
- return { kind: "type", value: "null" };
79
- case utils.TSESTree.AST_NODE_TYPES.TSUndefinedKeyword:
80
- return { kind: "type", value: "undefined" };
81
- case utils.TSESTree.AST_NODE_TYPES.TSTypeLiteral:
82
- return handleTypeLiteral(params.typeNode, params);
83
- case utils.TSESTree.AST_NODE_TYPES.TSTypeReference:
84
- return handleTypeReference(params.typeNode, params);
85
- case utils.TSESTree.AST_NODE_TYPES.TSIntersectionType:
86
- return handleIntersectionType(params.typeNode, params);
87
- case utils.TSESTree.AST_NODE_TYPES.TSArrayType:
88
- return {
89
- kind: "array",
90
- value: getResolvedTargetByTypeNode({
91
- ...params,
92
- typeNode: params.typeNode.elementType
93
- })
94
- };
95
- default:
96
- return { kind: "type", value: typeText };
97
- }
98
- }
99
- function isReservedType(typeText, reservedTypes) {
100
- return reservedTypes.has(typeText) || reservedTypes.has(`${typeText}[]`);
101
- }
102
- function handleLiteralType(typeNode) {
103
- return typeNode.literal.type === utils.TSESTree.AST_NODE_TYPES.Literal ? { kind: "type", value: `'${typeNode.literal.value}'` } : { kind: "type", value: "unknown" };
104
- }
105
- function handleTypeLiteral(typeNode, params) {
106
- const properties = typeNode.members.flatMap((member) => {
107
- if (member.type !== utils.TSESTree.AST_NODE_TYPES.TSPropertySignature || !member.typeAnnotation) {
108
- return [];
109
- }
110
- const key = extractPropertyKey(member.key);
111
- if (!key) return [];
112
- const propertyName = member.optional ? `${key}?` : key;
113
- const propertyType = getResolvedTargetByTypeNode({
114
- ...params,
115
- typeNode: member.typeAnnotation.typeAnnotation
116
- });
117
- return [[propertyName, propertyType]];
118
- });
119
- return { kind: "object", value: properties };
120
- }
121
- function extractPropertyKey(key) {
122
- switch (key.type) {
123
- case utils.TSESTree.AST_NODE_TYPES.Identifier:
124
- return key.name;
125
- case utils.TSESTree.AST_NODE_TYPES.Literal:
126
- return String(key.value);
127
- default:
128
- return void 0;
129
- }
130
- }
131
- function handleTypeReference(typeNode, params) {
132
- if (typeNode.typeName.type !== utils.TSESTree.AST_NODE_TYPES.Identifier && typeNode.typeName.type !== utils.TSESTree.AST_NODE_TYPES.TSQualifiedName) {
133
- return { kind: "type", value: "unknown" };
134
- }
135
- const typeNameText = params.parser.esTreeNodeToTSNodeMap.get(typeNode.typeName).getText();
136
- if (params.reservedTypes.has(typeNameText)) {
137
- return { kind: "type", value: typeNameText };
138
- }
139
- if (typeNameText === "Array" && typeNode.typeArguments?.params[0]) {
140
- return {
141
- kind: "array",
142
- syntax: "type-reference",
143
- value: getResolvedTargetByTypeNode({
144
- ...params,
145
- typeNode: typeNode.typeArguments.params[0]
146
- })
147
- };
148
- }
149
- const type = params.checker.getTypeFromTypeNode(
150
- params.parser.esTreeNodeToTSNodeMap.get(typeNode)
151
- );
152
- return resolveType(type, { ...params, typeNode });
153
- }
154
- function handleIntersectionType(typeNode, params) {
155
- const allProperties = typeNode.types.flatMap((type) => {
156
- const resolved = getResolvedTargetByTypeNode({ ...params, typeNode: type });
157
- return resolved.kind === "object" ? resolved.value : [];
158
- });
159
- return { kind: "object", value: Array.from(new Map(allProperties).entries()) };
160
- }
161
- function resolveType(type, params) {
162
- const typeAsString = params.checker.typeToString(type);
163
- if (params.reservedTypes.has(typeAsString)) {
164
- return { kind: "type", value: typeAsString };
165
- }
166
- if (params.reservedTypes.has(`${typeAsString}[]`)) {
167
- return { kind: "array", value: { kind: "type", value: typeAsString.replace("[]", "") } };
168
- }
169
- const primitive = getPrimitiveType(type, typeAsString);
170
- if (primitive) return primitive;
171
- if (type.isLiteral()) {
172
- return { kind: "type", value: `'${type.value}'` };
173
- }
174
- if (type.isUnion()) {
175
- return handleUnionTypeReference(type, params);
176
- }
177
- if (type.isIntersection()) {
178
- return handleIntersectionTypeReference(type, params);
179
- }
180
- if (params.checker.isArrayType(type)) {
181
- return handleArrayTypeReferenceFromType(type, params);
182
- }
183
- return handleObjectType(type, params);
184
- }
185
- function getPrimitiveType(type, typeAsString) {
186
- if (PRIMITIVES[typeAsString]) {
187
- return { kind: "type", value: PRIMITIVES[typeAsString] };
188
- }
189
- const flagMap = {
190
- [ts__default.TypeFlags.String]: "string",
191
- [ts__default.TypeFlags.Number]: "number",
192
- [ts__default.TypeFlags.Boolean]: "boolean",
193
- [ts__default.TypeFlags.Null]: "null",
194
- [ts__default.TypeFlags.Undefined]: "undefined",
195
- [ts__default.TypeFlags.Any]: "any"
196
- };
197
- return flagMap[type.flags] ? { kind: "type", value: flagMap[type.flags] } : null;
198
- }
199
- function handleUnionTypeReference(type, params) {
200
- const types = type.types.map((t) => resolveType(t, params));
201
- const isBooleanUnionWithNull = types.length === 3 && types.some((t) => t.value === "false") && types.some((t) => t.value === "true") && types.some((t) => t.value === "null");
202
- if (isBooleanUnionWithNull) {
203
- return {
204
- kind: "union",
205
- value: [
206
- { kind: "type", value: "boolean" },
207
- { kind: "type", value: "null" }
208
- ]
209
- };
210
- }
211
- return { kind: "union", value: types };
212
- }
213
- function handleIntersectionTypeReference(type, params) {
214
- const properties = type.types.flatMap((t) => {
215
- const resolved = resolveType(t, params);
216
- return resolved.kind === "object" ? resolved.value : [];
217
- });
218
- return { kind: "object", value: properties };
43
+ function isIdentifier(node) {
44
+ return node?.type === utils.TSESTree.AST_NODE_TYPES.Identifier;
219
45
  }
220
- function handleArrayTypeReferenceFromType(type, params) {
221
- const typeArguments = type.typeArguments;
222
- const firstArgument = typeArguments?.[0];
223
- if (firstArgument) {
224
- const elementType = resolveType(firstArgument, params);
225
- return { kind: "array", value: elementType };
226
- }
227
- return { kind: "array", value: { kind: "type", value: "unknown" } };
228
- }
229
- function handleObjectType(type, params) {
230
- if (!type.symbol) {
231
- return { kind: "type", value: type.aliasSymbol?.escapedName.toString() ?? "unknown" };
232
- }
233
- if (type.symbol.valueDeclaration) {
234
- const declaration = type.symbol.valueDeclaration;
235
- const sourceFile = declaration.getSourceFile();
236
- const filePath = sourceFile.fileName;
237
- if (!filePath.includes("node_modules")) {
238
- return extractObjectProperties(type, params);
239
- }
240
- return { kind: "type", value: type.symbol.name };
241
- }
242
- if (type.flags === ts__default.TypeFlags.Object) {
243
- return extractObjectProperties(type, params);
244
- }
245
- return { kind: "object", value: [] };
246
- }
247
- function extractObjectProperties(type, params) {
248
- const properties = type.getProperties().map((property) => {
249
- const key = property.escapedName.toString();
250
- const propType = params.checker.getTypeOfSymbolAtLocation(
251
- property,
252
- params.parser.esTreeNodeToTSNodeMap.get(params.typeNode)
253
- );
254
- const resolvedType = resolveType(propType, params);
255
- return [key, resolvedType];
256
- });
257
- return { kind: "object", value: properties };
46
+ function isMemberExpression(node) {
47
+ return node?.type === utils.TSESTree.AST_NODE_TYPES.MemberExpression;
258
48
  }
259
49
 
260
50
  function isInEditorEnv() {
@@ -1108,11 +898,7 @@ const zBaseTarget = z__default.object({
1108
898
  * - `"pascal"` - `user_id` → `UserId`
1109
899
  * - `"screaming snake"` - `user_id` → `USER_ID`
1110
900
  */
1111
- fieldTransform: z__default.enum(["snake", "pascal", "camel", "screaming snake"]).optional(),
1112
- /**
1113
- * Whether or not to skip type annotation.
1114
- */
1115
- skipTypeAnnotations: z__default.boolean().optional()
901
+ fieldTransform: z__default.enum(["snake", "pascal", "camel", "screaming snake"]).optional()
1116
902
  });
1117
903
  const zWrapperTarget = z__default.object({ wrapper: zStringOrRegex, maxDepth: z__default.number().optional() }).merge(zBaseTarget);
1118
904
  const zTagTarget = z__default.object({ tag: zStringOrRegex }).merge(zBaseTarget);
@@ -1193,7 +979,6 @@ const zConfig = z__default.object({
1193
979
  connections: z__default.union([z__default.array(zRuleOptionConnection), zRuleOptionConnection])
1194
980
  });
1195
981
  const RuleOptions = z__default.array(zConfig).min(1).max(1);
1196
- const defaultInferLiteralOptions = ["string"];
1197
982
 
1198
983
  function getConfigFromFileWithContext(params) {
1199
984
  return params.context.options[0];
@@ -1202,12 +987,7 @@ function getConfigFromFileWithContext(params) {
1202
987
  const messages = {
1203
988
  typeInferenceFailed: "Type inference failed {{error}}",
1204
989
  error: "{{error}}",
1205
- invalidQuery: "Invalid Query: {{error}}",
1206
- missingTypeAnnotations: "Query is missing type annotation\n Fix with: {{fix}}",
1207
- incorrectTypeAnnotations: `Query has incorrect type annotation.
1208
- Expected: {{expected}}
1209
- Actual: {{actual}}`,
1210
- invalidTypeAnnotations: `Query has invalid type annotation (SafeQL does not support it. If you think it should, please open an issue)`
990
+ invalidQuery: "Invalid Query: {{error}}"
1211
991
  };
1212
992
  function check(params) {
1213
993
  const connections = Array.isArray(params.config.connections) ? params.config.connections : [params.config.connections];
@@ -1218,10 +998,10 @@ function check(params) {
1218
998
  }
1219
999
  }
1220
1000
  function isTagMemberValid(expr) {
1221
- if (checkSql_utils.isIdentifier(expr.tag)) {
1001
+ if (isIdentifier(expr.tag)) {
1222
1002
  return true;
1223
1003
  }
1224
- if (checkSql_utils.isMemberExpression(expr.tag) && checkSql_utils.isIdentifier(expr.tag.property)) {
1004
+ if (isMemberExpression(expr.tag) && isIdentifier(expr.tag.property)) {
1225
1005
  return true;
1226
1006
  }
1227
1007
  return false;
@@ -1243,13 +1023,11 @@ const generateSyncE = function_js.flow(
1243
1023
  );
1244
1024
  let fatalError;
1245
1025
  function reportCheck(params) {
1246
- const { context, tag, connection, target, projectDir, typeParameter, baseNode } = params;
1026
+ const { context, tag, connection, target, projectDir } = params;
1247
1027
  if (fatalError !== void 0) {
1248
1028
  const hint = isInEditorEnv() ? "If you think this is a bug, please open an issue. If not, please try to fix the error and restart ESLint." : "If you think this is a bug, please open an issue.";
1249
1029
  return checkSql_utils.reportBaseError({ context, error: fatalError, tag, hint });
1250
1030
  }
1251
- const nullAsOptional = connection.nullAsOptional ?? false;
1252
- const nullAsUndefined = connection.nullAsUndefined ?? false;
1253
1031
  return function_js.pipe(
1254
1032
  E__namespace.Do,
1255
1033
  E__namespace.bind("parser", () => {
@@ -1309,89 +1087,11 @@ function reportCheck(params) {
1309
1087
  If you believe this query should be supported, please open an issue at https://github.com/gajus/eslint-plugin-slonik/issues`
1310
1088
  );
1311
1089
  return;
1090
+ }).with({ _tag: "ConnectionFailedError" }, () => {
1091
+ return;
1312
1092
  }).exhaustive();
1313
1093
  },
1314
- ({ result, checker, parser }) => {
1315
- if (result === null) {
1316
- return;
1317
- }
1318
- const shouldSkipTypeAnnotations = target.skipTypeAnnotations === true;
1319
- if (shouldSkipTypeAnnotations) {
1320
- return;
1321
- }
1322
- const isMissingTypeAnnotations = typeParameter === void 0;
1323
- if (isMissingTypeAnnotations) {
1324
- if (result.output === null) {
1325
- return;
1326
- }
1327
- return checkSql_utils.reportMissingTypeAnnotations({
1328
- tag,
1329
- context,
1330
- baseNode,
1331
- actual: checkSql_utils.getFinalResolvedTargetString({
1332
- target: result.output,
1333
- nullAsOptional: nullAsOptional ?? false,
1334
- nullAsUndefined: nullAsUndefined ?? false,
1335
- transform: target.transform,
1336
- inferLiterals: connection.inferLiterals ?? defaultInferLiteralOptions
1337
- })
1338
- });
1339
- }
1340
- const reservedTypes = memoize({
1341
- key: `reserved-types:${JSON.stringify(connection.overrides)}`,
1342
- value: () => {
1343
- const types = /* @__PURE__ */ new Set();
1344
- for (const value of Object.values(connection.overrides?.types ?? {})) {
1345
- types.add(typeof value === "string" ? value : value.return);
1346
- }
1347
- for (const columnType of Object.values(connection.overrides?.columns ?? {})) {
1348
- types.add(columnType);
1349
- }
1350
- return types;
1351
- }
1352
- });
1353
- const typeAnnotationState = getTypeAnnotationState({
1354
- generated: result.output,
1355
- typeParameter,
1356
- transform: target.transform,
1357
- checker,
1358
- parser,
1359
- reservedTypes,
1360
- nullAsOptional,
1361
- nullAsUndefined,
1362
- inferLiterals: connection.inferLiterals ?? defaultInferLiteralOptions
1363
- });
1364
- if (typeAnnotationState === "INVALID") {
1365
- return checkSql_utils.reportInvalidTypeAnnotations({
1366
- context,
1367
- typeParameter
1368
- });
1369
- }
1370
- if (!typeAnnotationState.isEqual) {
1371
- return checkSql_utils.reportIncorrectTypeAnnotations({
1372
- context,
1373
- typeParameter,
1374
- expected: checkSql_utils.fmap(
1375
- typeAnnotationState.expected,
1376
- (expected) => checkSql_utils.getResolvedTargetString({
1377
- target: expected,
1378
- nullAsOptional: false,
1379
- nullAsUndefined: false,
1380
- inferLiterals: params.connection.inferLiterals ?? defaultInferLiteralOptions
1381
- })
1382
- ),
1383
- actual: checkSql_utils.fmap(
1384
- result.output,
1385
- (output) => checkSql_utils.getFinalResolvedTargetString({
1386
- target: output,
1387
- nullAsOptional: connection.nullAsOptional ?? false,
1388
- nullAsUndefined: connection.nullAsUndefined ?? false,
1389
- transform: target.transform,
1390
- inferLiterals: connection.inferLiterals ?? defaultInferLiteralOptions
1391
- })
1392
- )
1393
- });
1394
- }
1094
+ () => {
1395
1095
  }
1396
1096
  )
1397
1097
  );
@@ -1445,98 +1145,13 @@ function checkConnectionByWrapperExpression(params) {
1445
1145
  });
1446
1146
  }
1447
1147
  }
1448
- function getTypeAnnotationState({
1449
- generated,
1450
- typeParameter,
1451
- transform,
1452
- parser,
1453
- checker,
1454
- reservedTypes,
1455
- nullAsOptional,
1456
- nullAsUndefined,
1457
- inferLiterals
1458
- }) {
1459
- if (typeParameter.params.length !== 1) {
1460
- return "INVALID";
1461
- }
1462
- const typeNode = typeParameter.params[0];
1463
- let expected;
1464
- try {
1465
- expected = getResolvedTargetByTypeNode({
1466
- checker,
1467
- parser,
1468
- typeNode,
1469
- reservedTypes
1470
- });
1471
- } catch (error) {
1472
- console.error("[slonik/check-sql] DEBUG: Error in getResolvedTargetByTypeNode:", error);
1473
- console.error("[slonik/check-sql] DEBUG: typeNode:", typeNode);
1474
- console.error("[slonik/check-sql] DEBUG: typeNode.type:", typeNode?.type);
1475
- throw error;
1476
- }
1477
- return getResolvedTargetsEquality({
1478
- expected,
1479
- generated,
1480
- nullAsOptional,
1481
- nullAsUndefined,
1482
- inferLiterals,
1483
- transform
1484
- });
1485
- }
1486
- function getResolvedTargetsEquality(params) {
1487
- if (params.expected === null && params.generated === null) {
1488
- return {
1489
- isEqual: true,
1490
- expected: params.expected,
1491
- generated: params.generated
1492
- };
1493
- }
1494
- if (params.expected === null || params.generated === null) {
1495
- return {
1496
- isEqual: false,
1497
- expected: params.expected,
1498
- generated: params.generated
1499
- };
1500
- }
1501
- let expectedString = checkSql_utils.getResolvedTargetComparableString({
1502
- target: params.expected,
1503
- nullAsOptional: false,
1504
- nullAsUndefined: false,
1505
- inferLiterals: params.inferLiterals
1506
- });
1507
- let generatedString = checkSql_utils.getResolvedTargetComparableString({
1508
- target: params.generated,
1509
- nullAsOptional: params.nullAsOptional,
1510
- nullAsUndefined: params.nullAsUndefined,
1511
- inferLiterals: params.inferLiterals
1512
- });
1513
- if (expectedString === null || generatedString === null) {
1514
- return {
1515
- isEqual: false,
1516
- expected: params.expected,
1517
- generated: params.generated
1518
- };
1519
- }
1520
- expectedString = expectedString.replace(/'/g, '"');
1521
- generatedString = generatedString.replace(/'/g, '"');
1522
- expectedString = expectedString.split(", ").sort().join(", ");
1523
- generatedString = generatedString.split(", ").sort().join(", ");
1524
- if (params.transform !== void 0) {
1525
- generatedString = checkSql_utils.transformTypes(generatedString, params.transform);
1526
- }
1527
- return {
1528
- isEqual: expectedString === generatedString,
1529
- expected: params.expected,
1530
- generated: params.generated
1531
- };
1532
- }
1533
1148
  const createRule = utils.ESLintUtils.RuleCreator(() => `https://github.com/gajus/eslint-plugin-slonik`);
1534
1149
  const checkSql = createRule({
1535
1150
  name: "check-sql",
1536
1151
  meta: {
1537
1152
  fixable: "code",
1538
1153
  docs: {
1539
- description: "Ensure that sql queries have type annotations"
1154
+ description: "Validate SQL queries against the database schema"
1540
1155
  },
1541
1156
  messages,
1542
1157
  type: "problem",