Skip to content

Commit 7b9d5ff

Browse files
authored
require-field-index: add support for functions in indexes (#774)
I wanted to enable the `require-field-index` in my project when I got an error message: ``` Error: value is not a string array {"type":"function","name":"endDate","params":[{"type":"keyValue","key":"sort","value":"Desc","location":{"startLine":125,"startColumn":19,"startOffset":4111,"endLine":125,"endColumn":22,"endOffset":4114}}],"location":{"startLine":125,"startColumn":11,"startOffset":4103,"endLine":125,"endColumn":17,"endOffset":4109}} at assertValueIsStringArray (file:///Users/<...>/node_modules/prisma-lint/dist/common/prisma.js:83:11) ``` The line that failed the parsing here is an index with a `sort` option set ([relevant Prisma docs](https://www.prisma.io/docs/orm/prisma-schema/data-model/indexes#configuring-the-index-sort-order-with-sort)): ```prisma @@index([endDate(sort: Desc)]) ``` I gave a stab at fixing this myself, let me know what you think!
1 parent ed2a12c commit 7b9d5ff

4 files changed

Lines changed: 52 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Add support for index functions (e.g. `@@index(createdAt(sort: Desc))`) in `require-field-index` rule.
6+
57
## 0.10.2 (2025-05-24)
68

79
- [#683](https://github.com/loop-payments/prisma-lint/issues/683) Stop requiring defaults for `@ignored` relation fields.

src/common/prisma.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
BlockAttribute,
55
Enum as PrismaEnum,
66
Field,
7+
Func,
78
KeyValue,
89
Model,
910
Schema,
@@ -108,9 +109,15 @@ export function isKeyValue(value: Value | KeyValue): value is KeyValue {
108109
return false;
109110
}
110111

111-
export function assertValueIsStringArray(value: Value): Array<string> {
112+
export function isFunc(value: Value): value is Func {
113+
return (
114+
typeof value === 'object' && 'type' in value && value.type === 'function'
115+
);
116+
}
117+
118+
export function assertValueIsArray(value: Value): Array<Value> {
112119
if (Array.isArray(value)) {
113-
return value as Array<string>;
120+
return value as Array<Value>;
114121
}
115122

116123
if (typeof value === 'object') {
@@ -119,7 +126,7 @@ export function assertValueIsStringArray(value: Value): Array<string> {
119126
}
120127
}
121128

122-
throw new Error(`value is not a string array ${JSON.stringify(value)}`);
129+
throw new Error(`value is not an array ${JSON.stringify(value)}`);
123130
}
124131

125132
export function looksLikeAssociationFieldType(fieldType: any) {

src/rules/require-field-index.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,32 @@ describe('require-field-index', () => {
151151
});
152152
});
153153

154+
describe('with function in @@index', () => {
155+
it('returns violation', async () => {
156+
const violations = await run(`
157+
model Users {
158+
tenantQid String
159+
createdAt DateTime
160+
@@index(tenantQid(sort: Desc))
161+
}
162+
`);
163+
expect(violations.length).toEqual(0);
164+
});
165+
});
166+
167+
describe('with function in compound @@index', () => {
168+
it('returns violation', async () => {
169+
const violations = await run(`
170+
model Users {
171+
tenantQid String
172+
createdAt DateTime
173+
@@index([tenantQid(sort: Desc), createdAt])
174+
}
175+
`);
176+
expect(violations.length).toEqual(0);
177+
});
178+
});
179+
154180
describe('with no index', () => {
155181
it('returns violation', async () => {
156182
const violations = await run(`

src/rules/require-field-index.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { z } from 'zod';
99

1010
import { getRuleIgnoreParams as listRuleIgnoreParams } from '#src/common/ignore.js';
1111
import {
12-
assertValueIsStringArray,
12+
assertValueIsArray,
13+
isFunc,
1314
isKeyValue,
1415
isValue,
1516
listAttributes,
@@ -240,13 +241,22 @@ function extractPrimaryFieldNameFromRelationListAttribute(
240241
return value;
241242
}
242243

244+
// @@index(value(sort: Desc))
245+
if (isFunc(value)) {
246+
return value.name;
247+
}
248+
243249
// @@index([value]) or @@unique([value])
244-
const [firstFieldValue] = assertValueIsStringArray(value);
250+
const [firstFieldValue] = assertValueIsArray(value);
245251
if (typeof firstFieldValue === 'string') {
246-
// it should always be a string
247252
return firstFieldValue;
248253
}
249254

255+
// @@index([value(sort: Desc)])
256+
if (isFunc(firstFieldValue)) {
257+
return firstFieldValue.name;
258+
}
259+
250260
throw new Error('Failed to parse attribute, first value is not a string');
251261
}
252262

@@ -267,5 +277,5 @@ function extractRelationFieldNames(field: Field): Array<string> {
267277
}
268278

269279
const fieldsArgValue = (fieldsArg.value as KeyValue).value;
270-
return assertValueIsStringArray(fieldsArgValue);
280+
return assertValueIsArray(fieldsArgValue) as string[];
271281
}

0 commit comments

Comments
 (0)