@@ -118,8 +118,13 @@ export default class DataModelValidator implements AstValidator<DataModel> {
118
118
}
119
119
120
120
if ( ! fields || ! references ) {
121
- if ( accept ) {
122
- accept ( 'error' , `Both "fields" and "references" must be provided` , { node : relAttr } ) ;
121
+ if ( this . isSelfRelation ( field , name ) ) {
122
+ // self relations are partial
123
+ // https://www.prisma.io/docs/concepts/components/prisma-schema/relations/self-relations
124
+ } else {
125
+ if ( accept ) {
126
+ accept ( 'error' , `Both "fields" and "references" must be provided` , { node : relAttr } ) ;
127
+ }
123
128
}
124
129
} else {
125
130
// validate "fields" and "references" typing consistency
@@ -157,6 +162,33 @@ export default class DataModelValidator implements AstValidator<DataModel> {
157
162
return { attr : relAttr , name, fields, references, valid } ;
158
163
}
159
164
165
+ private isSelfRelation ( field : DataModelField , relationName ?: string ) {
166
+ if ( field . type . reference ?. ref === field . $container ) {
167
+ // field directly references back to its type
168
+ return true ;
169
+ }
170
+
171
+ if ( relationName ) {
172
+ // field's relation points to another type, and that type's opposite relation field
173
+ // points back
174
+ const oppositeModelFields = field . type . reference ?. ref ?. fields as DataModelField [ ] ;
175
+ if ( oppositeModelFields ) {
176
+ for ( const oppositeField of oppositeModelFields ) {
177
+ const { name : oppositeRelationName } = this . parseRelation ( oppositeField ) ;
178
+ if (
179
+ oppositeRelationName === relationName &&
180
+ oppositeField . type . reference ?. ref === field . $container
181
+ ) {
182
+ // found an opposite relation field that points back to this field's type
183
+ return true ;
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ return false ;
190
+ }
191
+
160
192
private validateRelationField ( field : DataModelField , accept : ValidationAcceptor ) {
161
193
const thisRelation = this . parseRelation ( field , accept ) ;
162
194
if ( ! thisRelation . valid ) {
@@ -180,15 +212,20 @@ export default class DataModelValidator implements AstValidator<DataModel> {
180
212
) ;
181
213
return ;
182
214
} else if ( oppositeFields . length > 1 ) {
183
- oppositeFields . forEach ( ( f ) =>
184
- accept (
185
- 'error' ,
186
- `Fields ${ oppositeFields . map ( ( f ) => '"' + f . name + '"' ) . join ( ', ' ) } on model "${
187
- oppositeModel . name
188
- } " refer to the same relation to model "${ field . $container . name } "`,
189
- { node : f }
190
- )
191
- ) ;
215
+ oppositeFields . forEach ( ( f ) => {
216
+ if ( this . isSelfRelation ( f ) ) {
217
+ // self relations are partial
218
+ // https://www.prisma.io/docs/concepts/components/prisma-schema/relations/self-relations
219
+ } else {
220
+ accept (
221
+ 'error' ,
222
+ `Fields ${ oppositeFields . map ( ( f ) => '"' + f . name + '"' ) . join ( ', ' ) } on model "${
223
+ oppositeModel . name
224
+ } " refer to the same relation to model "${ field . $container . name } "`,
225
+ { node : f }
226
+ ) ;
227
+ }
228
+ } ) ;
192
229
return ;
193
230
}
194
231
0 commit comments