prisma-arktype 2.2.0 → 2.4.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 +155 -14
- package/dist/index.js +229 -43
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -86,16 +86,18 @@ generator prisma-arktype {
|
|
|
86
86
|
|
|
87
87
|
For each model, the generator creates multiple schema types:
|
|
88
88
|
|
|
89
|
-
- **`ModelPlain`** -
|
|
90
|
-
- **`ModelRelations`** - Relationship
|
|
91
|
-
- **`Model`** - Complete composite schema
|
|
92
|
-
- **`ModelWhere`** - Where clause schema
|
|
93
|
-
- **`ModelWhereUnique`** - Unique where clause schema
|
|
94
|
-
- **`ModelCreate`** -
|
|
95
|
-
- **`ModelUpdate`** -
|
|
96
|
-
- **`ModelSelect`** -
|
|
97
|
-
- **`ModelInclude`** -
|
|
98
|
-
- **`ModelOrderBy`** -
|
|
89
|
+
- **`ModelPlain`** - Scalar fields only (strings, numbers, dates, enums) - no relations
|
|
90
|
+
- **`ModelRelations`** - Relationship fields only, referencing related model Plain types
|
|
91
|
+
- **`Model`** - Complete composite schema combining Plain & Relations
|
|
92
|
+
- **`ModelWhere`** - Where clause schema for filtering
|
|
93
|
+
- **`ModelWhereUnique`** - Unique where clause schema for finding specific records
|
|
94
|
+
- **`ModelCreate`** - Input schema for creating records
|
|
95
|
+
- **`ModelUpdate`** - Input schema for updating records
|
|
96
|
+
- **`ModelSelect`** - Schema for selecting specific fields
|
|
97
|
+
- **`ModelInclude`** - Schema for including relations
|
|
98
|
+
- **`ModelOrderBy`** - Schema for ordering results
|
|
99
|
+
|
|
100
|
+
**Enums** are generated as separate reusable types that are imported and referenced by models that use them.
|
|
99
101
|
|
|
100
102
|
### Using Generated Schemas
|
|
101
103
|
|
|
@@ -130,6 +132,101 @@ const whereResult = UserWhere(whereClause);
|
|
|
130
132
|
// ...
|
|
131
133
|
```
|
|
132
134
|
|
|
135
|
+
### Generated Code Examples
|
|
136
|
+
|
|
137
|
+
#### Enum Generation
|
|
138
|
+
|
|
139
|
+
For a Prisma enum like:
|
|
140
|
+
```prisma
|
|
141
|
+
enum Currency {
|
|
142
|
+
USD
|
|
143
|
+
EUR
|
|
144
|
+
GBP
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The generator creates a separate reusable type:
|
|
149
|
+
```typescript
|
|
150
|
+
// Currency.ts
|
|
151
|
+
import { type } from "arktype";
|
|
152
|
+
|
|
153
|
+
export const Currency = type("'USD' | 'EUR' | 'GBP'");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Which is then imported and used in models:
|
|
157
|
+
```typescript
|
|
158
|
+
// PaymentPlain.ts
|
|
159
|
+
import { type } from "arktype";
|
|
160
|
+
import { Currency } from "./Currency";
|
|
161
|
+
|
|
162
|
+
export const PaymentPlain = type({
|
|
163
|
+
"id": "string",
|
|
164
|
+
"amount": "number",
|
|
165
|
+
"currency": Currency, // Required enum
|
|
166
|
+
"status?": Currency.or("null") // Optional enum
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Relation Generation
|
|
171
|
+
|
|
172
|
+
For Prisma models with relations like:
|
|
173
|
+
```prisma
|
|
174
|
+
model User {
|
|
175
|
+
id String @id
|
|
176
|
+
email String
|
|
177
|
+
posts Post[]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
model Post {
|
|
181
|
+
id String @id
|
|
182
|
+
title String
|
|
183
|
+
author User @relation(fields: [authorId], references: [id])
|
|
184
|
+
authorId String
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The generator creates Plain types (without relations):
|
|
189
|
+
```typescript
|
|
190
|
+
// UserPlain.ts
|
|
191
|
+
export const UserPlain = type({
|
|
192
|
+
"id": "string",
|
|
193
|
+
"email": "string"
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// PostPlain.ts
|
|
197
|
+
export const PostPlain = type({
|
|
198
|
+
"id": "string",
|
|
199
|
+
"title": "string",
|
|
200
|
+
"authorId": "string"
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
And Relations types that reference the Plain types:
|
|
205
|
+
```typescript
|
|
206
|
+
// UserRelations.ts
|
|
207
|
+
import { PostPlain } from "./PostPlain";
|
|
208
|
+
|
|
209
|
+
export const UserRelations = type({
|
|
210
|
+
"posts": PostPlain.array() // Array of Post objects
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// PostRelations.ts
|
|
214
|
+
import { UserPlain } from "./UserPlain";
|
|
215
|
+
|
|
216
|
+
export const PostRelations = type({
|
|
217
|
+
"author": UserPlain // Single User object
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The combined model merges both:
|
|
222
|
+
```typescript
|
|
223
|
+
// User.ts
|
|
224
|
+
import { UserPlain } from "./UserPlain";
|
|
225
|
+
import { UserRelations } from "./UserRelations";
|
|
226
|
+
|
|
227
|
+
export const User = type(() => UserPlain.and(UserRelations));
|
|
228
|
+
```
|
|
229
|
+
|
|
133
230
|
## Annotations
|
|
134
231
|
|
|
135
232
|
Control schema generation using annotations in your Prisma schema. All annotations are added as documentation comments (`///`).
|
|
@@ -142,6 +239,7 @@ Control schema generation using annotations in your Prisma schema. All annotatio
|
|
|
142
239
|
| `@prisma-arktype.input.hide` | Field | Hide from Create and Update input schemas |
|
|
143
240
|
| `@prisma-arktype.create.input.hide` | Field | Hide from Create input schema only |
|
|
144
241
|
| `@prisma-arktype.update.input.hide` | Field | Hide from Update input schema only |
|
|
242
|
+
| `@prisma-arktype.schema="<schema>"` | Field | Custom ArkType schema (inline or external) |
|
|
145
243
|
| `@prisma-arktype.typeOverwrite="<type>"` | Field | Override the generated ArkType type |
|
|
146
244
|
|
|
147
245
|
### Hide Fields/Models
|
|
@@ -204,6 +302,48 @@ model User {
|
|
|
204
302
|
|
|
205
303
|
This allows you to use any ArkType type definition, including built-in refinements like `string.email`, `string.url`, `number.integer`, etc.
|
|
206
304
|
|
|
305
|
+
### Custom Schemas
|
|
306
|
+
|
|
307
|
+
Bring your own ArkType schemas for any field using `@prisma-arktype.schema`:
|
|
308
|
+
|
|
309
|
+
```prisma
|
|
310
|
+
model User {
|
|
311
|
+
id String @id
|
|
312
|
+
|
|
313
|
+
/// Inline schema for structured JSON
|
|
314
|
+
/// @prisma-arktype.schema="{ name: 'string', age: 'number' }"
|
|
315
|
+
profile Json
|
|
316
|
+
|
|
317
|
+
/// External schema from a file (named export)
|
|
318
|
+
/// @prisma-arktype.schema="../schemas/address:AddressSchema"
|
|
319
|
+
address Json
|
|
320
|
+
|
|
321
|
+
/// External schema (default export)
|
|
322
|
+
/// @prisma-arktype.schema="../schemas/config"
|
|
323
|
+
settings Json
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Import Path Rules:**
|
|
328
|
+
- Paths are relative to the generated validators directory
|
|
329
|
+
- Named exports use colon syntax: `"path:ExportName"`
|
|
330
|
+
- Default exports omit the colon: `"path"`
|
|
331
|
+
- Works with ANY field type (not just Json)
|
|
332
|
+
|
|
333
|
+
**Priority:** `schema` > `typeOverwrite` > default type mapping
|
|
334
|
+
|
|
335
|
+
**Example external schema file** (`schemas/address.ts`):
|
|
336
|
+
```typescript
|
|
337
|
+
import { type } from "arktype";
|
|
338
|
+
|
|
339
|
+
export const AddressSchema = type({
|
|
340
|
+
street: "string",
|
|
341
|
+
city: "string",
|
|
342
|
+
zipCode: "string",
|
|
343
|
+
country: "string",
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
207
347
|
## Type Mapping
|
|
208
348
|
|
|
209
349
|
Prisma types are mapped to ArkType as follows:
|
|
@@ -219,15 +359,16 @@ Prisma types are mapped to ArkType as follows:
|
|
|
219
359
|
| `DateTime` | `"Date"` | `"Date"` |
|
|
220
360
|
| `Json` | `"unknown"` | `"unknown"` |
|
|
221
361
|
| `Bytes` | `"instanceof Buffer"` | `"instanceof Buffer"` |
|
|
222
|
-
| Enums |
|
|
223
|
-
| Relations | `
|
|
362
|
+
| Enums | Reference to enum type | `Currency` (imported from `./Currency`) |
|
|
363
|
+
| Relations | Reference to related Plain type | `PostPlain` or `PostPlain.array()` |
|
|
224
364
|
|
|
225
365
|
### Special Handling
|
|
226
366
|
|
|
227
367
|
- **Optional fields**: Use `?` on the key name (`"name?": "string"`)
|
|
228
368
|
- **Nullable fields**: Add `| null` to the type (`"string | null"`)
|
|
229
|
-
- **Arrays**: Use `.array()` syntax for lists (`type("string").array()`)
|
|
230
|
-
- **Enums**: Generated as
|
|
369
|
+
- **Arrays**: Use `.array()` syntax for lists (`type("string").array()` or `Currency.array()`)
|
|
370
|
+
- **Enums**: Generated as separate reusable type definitions and imported where used
|
|
371
|
+
- **Relations**: Reference the Plain type of the related model, imported automatically
|
|
231
372
|
|
|
232
373
|
## Differences from prismabox
|
|
233
374
|
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,9 @@ function isHiddenInputUpdate(annotation) {
|
|
|
43
43
|
function isTypeOverwrite(annotation) {
|
|
44
44
|
return annotation.type === "TYPE_OVERWRITE";
|
|
45
45
|
}
|
|
46
|
+
function isSchema(annotation) {
|
|
47
|
+
return annotation.type === "SCHEMA";
|
|
48
|
+
}
|
|
46
49
|
const annotationKeys = [
|
|
47
50
|
{
|
|
48
51
|
keys: ["@prisma-arktype.hide", "@prisma-arktype.hidden"],
|
|
@@ -66,9 +69,11 @@ const annotationKeys = [
|
|
|
66
69
|
],
|
|
67
70
|
type: "HIDDEN_INPUT_UPDATE"
|
|
68
71
|
},
|
|
69
|
-
{ keys: ["@prisma-arktype.typeOverwrite"], type: "TYPE_OVERWRITE" }
|
|
72
|
+
{ keys: ["@prisma-arktype.typeOverwrite"], type: "TYPE_OVERWRITE" },
|
|
73
|
+
{ keys: ["@prisma-arktype.schema"], type: "SCHEMA" }
|
|
70
74
|
];
|
|
71
75
|
const prismaArktypeTypeOverwriteRegex = /@prisma-arktype\.typeOverwrite=(.+)/;
|
|
76
|
+
const prismaArktypeSchemaRegex = /@prisma-arktype\.schema="([^"]+)"/;
|
|
72
77
|
function extractAnnotations(documentation) {
|
|
73
78
|
const annotations = [];
|
|
74
79
|
const descriptionLines = [];
|
|
@@ -78,18 +83,52 @@ function extractAnnotations(documentation) {
|
|
|
78
83
|
for (const { keys, type } of annotationKeys) {
|
|
79
84
|
for (const key of keys) {
|
|
80
85
|
if (line.includes(key)) {
|
|
81
|
-
isAnnotation = true;
|
|
82
86
|
if (type === "TYPE_OVERWRITE") {
|
|
83
87
|
const match = line.match(prismaArktypeTypeOverwriteRegex);
|
|
84
88
|
if (match && match[1]) {
|
|
89
|
+
isAnnotation = true;
|
|
85
90
|
annotations.push({
|
|
86
91
|
type: "TYPE_OVERWRITE",
|
|
87
92
|
value: match[1].trim()
|
|
88
93
|
});
|
|
89
|
-
}
|
|
90
|
-
|
|
94
|
+
}
|
|
95
|
+
} else if (type === "SCHEMA") {
|
|
96
|
+
const match = line.match(prismaArktypeSchemaRegex);
|
|
97
|
+
if (match && match[1]) {
|
|
98
|
+
isAnnotation = true;
|
|
99
|
+
const schemaValue = match[1].trim();
|
|
100
|
+
const isExternal = schemaValue.includes("/") || !(schemaValue.startsWith("{") || schemaValue.startsWith('"') || schemaValue.startsWith("'")) && schemaValue.includes(":") && // Ensure it's a module:export pattern, not object syntax like { key: value }
|
|
101
|
+
!schemaValue.includes(" ") && !schemaValue.includes(",");
|
|
102
|
+
if (isExternal) {
|
|
103
|
+
const colonIndex = schemaValue.indexOf(":");
|
|
104
|
+
if (colonIndex > 0) {
|
|
105
|
+
const path = schemaValue.substring(0, colonIndex).trim();
|
|
106
|
+
const exportName = schemaValue.substring(colonIndex + 1).trim();
|
|
107
|
+
annotations.push({
|
|
108
|
+
type: "SCHEMA",
|
|
109
|
+
value: schemaValue,
|
|
110
|
+
isExternal: true,
|
|
111
|
+
importPath: path,
|
|
112
|
+
exportName
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
annotations.push({
|
|
116
|
+
type: "SCHEMA",
|
|
117
|
+
value: schemaValue,
|
|
118
|
+
isExternal: true,
|
|
119
|
+
importPath: schemaValue
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
annotations.push({
|
|
124
|
+
type: "SCHEMA",
|
|
125
|
+
value: schemaValue,
|
|
126
|
+
isExternal: false
|
|
127
|
+
});
|
|
128
|
+
}
|
|
91
129
|
}
|
|
92
130
|
} else {
|
|
131
|
+
isAnnotation = true;
|
|
93
132
|
annotations.push({ type });
|
|
94
133
|
}
|
|
95
134
|
break;
|
|
@@ -186,7 +225,8 @@ function processPlain(models) {
|
|
|
186
225
|
processedPlain.push({
|
|
187
226
|
name: model.name,
|
|
188
227
|
stringified: result.stringified,
|
|
189
|
-
enumDependencies: result.enumDependencies
|
|
228
|
+
enumDependencies: result.enumDependencies,
|
|
229
|
+
externalSchemaDependencies: result.externalSchemaDependencies
|
|
190
230
|
});
|
|
191
231
|
}
|
|
192
232
|
}
|
|
@@ -202,6 +242,12 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
|
|
|
202
242
|
}
|
|
203
243
|
const fields = [];
|
|
204
244
|
const enumDependencies = [];
|
|
245
|
+
const externalSchemaDependencies = [];
|
|
246
|
+
function generateUniqueAlias(path, exportName, fieldName) {
|
|
247
|
+
const baseName = exportName || path.split("/").pop()?.replace(/\.(ts|js)$/, "") || "Schema";
|
|
248
|
+
const suffix = fieldName ? `_${fieldName}` : "";
|
|
249
|
+
return `${baseName}${suffix}`;
|
|
250
|
+
}
|
|
205
251
|
for (const field of model.fields) {
|
|
206
252
|
const {
|
|
207
253
|
annotations: fieldAnnotations,
|
|
@@ -218,10 +264,36 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
|
|
|
218
264
|
if (config.ignoredKeysOnInputModels.includes(field.name)) continue;
|
|
219
265
|
if (field.name.endsWith("Id") && field.relationName) continue;
|
|
220
266
|
}
|
|
267
|
+
const schemaAnnotation = fieldAnnotations.find(isSchema);
|
|
221
268
|
const typeOverwrite = fieldAnnotations.find(isTypeOverwrite);
|
|
222
269
|
let fieldType;
|
|
223
270
|
let fieldName = field.name;
|
|
224
|
-
if (
|
|
271
|
+
if (schemaAnnotation) {
|
|
272
|
+
if (schemaAnnotation.isExternal) {
|
|
273
|
+
const alias = generateUniqueAlias(
|
|
274
|
+
schemaAnnotation.importPath,
|
|
275
|
+
schemaAnnotation.exportName,
|
|
276
|
+
field.name
|
|
277
|
+
);
|
|
278
|
+
if (!externalSchemaDependencies.some((d) => d.localAlias === alias)) {
|
|
279
|
+
const dependency = {
|
|
280
|
+
importPath: schemaAnnotation.importPath,
|
|
281
|
+
localAlias: alias
|
|
282
|
+
};
|
|
283
|
+
if (schemaAnnotation.exportName) {
|
|
284
|
+
dependency.exportName = schemaAnnotation.exportName;
|
|
285
|
+
}
|
|
286
|
+
externalSchemaDependencies.push(dependency);
|
|
287
|
+
}
|
|
288
|
+
fieldType = alias;
|
|
289
|
+
} else {
|
|
290
|
+
if (schemaAnnotation.value.trim().startsWith("{")) {
|
|
291
|
+
fieldType = schemaAnnotation.value;
|
|
292
|
+
} else {
|
|
293
|
+
fieldType = `"${schemaAnnotation.value}"`;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} else if (typeOverwrite) {
|
|
225
297
|
fieldType = typeOverwrite.value;
|
|
226
298
|
} else if (isPrimitivePrismaFieldType(field.type)) {
|
|
227
299
|
fieldType = stringifyPrimitiveType(field.type);
|
|
@@ -235,16 +307,17 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
|
|
|
235
307
|
} else {
|
|
236
308
|
continue;
|
|
237
309
|
}
|
|
238
|
-
const isEnumType = field.kind === "enum" && !typeOverwrite;
|
|
310
|
+
const isEnumType = field.kind === "enum" && !typeOverwrite && !schemaAnnotation;
|
|
311
|
+
const isExternalSchema = schemaAnnotation?.isExternal === true;
|
|
239
312
|
if (field.isList) {
|
|
240
|
-
if (isEnumType) {
|
|
313
|
+
if (isExternalSchema || isEnumType) {
|
|
241
314
|
fieldType = `${fieldType}.array()`;
|
|
242
315
|
} else {
|
|
243
316
|
fieldType = `"${wrapPrimitiveWithArray(fieldType.slice(1, -1))}"`;
|
|
244
317
|
}
|
|
245
318
|
}
|
|
246
319
|
if (!field.isRequired) {
|
|
247
|
-
if (isEnumType) {
|
|
320
|
+
if (isExternalSchema || isEnumType) {
|
|
248
321
|
fieldType = `${fieldType}.or("null")`;
|
|
249
322
|
} else {
|
|
250
323
|
const inner = fieldType.slice(1, -1);
|
|
@@ -263,7 +336,8 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
|
|
|
263
336
|
stringified: `{
|
|
264
337
|
${fields.join(",\n ")}
|
|
265
338
|
}`,
|
|
266
|
-
enumDependencies
|
|
339
|
+
enumDependencies,
|
|
340
|
+
externalSchemaDependencies
|
|
267
341
|
};
|
|
268
342
|
}
|
|
269
343
|
function stringifyPlainInputCreate(model) {
|
|
@@ -283,7 +357,8 @@ function processCreate(models) {
|
|
|
283
357
|
processedCreate.push({
|
|
284
358
|
name: model.name,
|
|
285
359
|
stringified: result.stringified,
|
|
286
|
-
enumDependencies: result.enumDependencies
|
|
360
|
+
enumDependencies: result.enumDependencies,
|
|
361
|
+
externalSchemaDependencies: result.externalSchemaDependencies
|
|
287
362
|
});
|
|
288
363
|
}
|
|
289
364
|
}
|
|
@@ -364,11 +439,12 @@ const processedRelationsCreate = [];
|
|
|
364
439
|
const processedRelationsUpdate = [];
|
|
365
440
|
function processRelations(models) {
|
|
366
441
|
for (const model of models) {
|
|
367
|
-
const
|
|
368
|
-
if (
|
|
442
|
+
const result = stringifyRelations(model);
|
|
443
|
+
if (result) {
|
|
369
444
|
processedRelations.push({
|
|
370
445
|
name: model.name,
|
|
371
|
-
stringified
|
|
446
|
+
stringified: result.stringified,
|
|
447
|
+
modelDependencies: result.modelDependencies
|
|
372
448
|
});
|
|
373
449
|
}
|
|
374
450
|
}
|
|
@@ -382,26 +458,34 @@ function stringifyRelations(model) {
|
|
|
382
458
|
return;
|
|
383
459
|
}
|
|
384
460
|
const fields = [];
|
|
461
|
+
const modelDependencies = [];
|
|
385
462
|
for (const field of model.fields) {
|
|
386
463
|
const { hidden: fieldHidden } = extractAnnotations(field.documentation);
|
|
387
464
|
if (fieldHidden) continue;
|
|
388
465
|
if (field.kind !== "object") continue;
|
|
466
|
+
const relatedModelPlain = `${field.type}Plain`;
|
|
467
|
+
if (!modelDependencies.includes(field.type)) {
|
|
468
|
+
modelDependencies.push(field.type);
|
|
469
|
+
}
|
|
389
470
|
let fieldType;
|
|
390
471
|
if (field.isList) {
|
|
391
|
-
fieldType =
|
|
472
|
+
fieldType = `${relatedModelPlain}.array()`;
|
|
392
473
|
} else if (!field.isRequired) {
|
|
393
|
-
fieldType =
|
|
474
|
+
fieldType = `${relatedModelPlain}.or("null")`;
|
|
394
475
|
} else {
|
|
395
|
-
fieldType =
|
|
476
|
+
fieldType = relatedModelPlain;
|
|
396
477
|
}
|
|
397
478
|
fields.push(`"${field.name}": ${fieldType}`);
|
|
398
479
|
}
|
|
399
480
|
if (fields.length === 0) {
|
|
400
481
|
return;
|
|
401
482
|
}
|
|
402
|
-
return
|
|
483
|
+
return {
|
|
484
|
+
stringified: `{
|
|
403
485
|
${fields.join(",\n ")}
|
|
404
|
-
}
|
|
486
|
+
}`,
|
|
487
|
+
modelDependencies
|
|
488
|
+
};
|
|
405
489
|
}
|
|
406
490
|
function processRelationsCreate(models) {
|
|
407
491
|
for (const model of models) {
|
|
@@ -523,9 +607,7 @@ function processSelect(models) {
|
|
|
523
607
|
Object.freeze(processedSelect);
|
|
524
608
|
}
|
|
525
609
|
function stringifySelect(model) {
|
|
526
|
-
const { hidden } = extractAnnotations(
|
|
527
|
-
model.documentation
|
|
528
|
-
);
|
|
610
|
+
const { hidden } = extractAnnotations(model.documentation);
|
|
529
611
|
if (hidden) {
|
|
530
612
|
return;
|
|
531
613
|
}
|
|
@@ -551,7 +633,8 @@ function processUpdate(models) {
|
|
|
551
633
|
processedUpdate.push({
|
|
552
634
|
name: model.name,
|
|
553
635
|
stringified: result.stringified,
|
|
554
|
-
enumDependencies: result.enumDependencies
|
|
636
|
+
enumDependencies: result.enumDependencies,
|
|
637
|
+
externalSchemaDependencies: result.externalSchemaDependencies
|
|
555
638
|
});
|
|
556
639
|
}
|
|
557
640
|
}
|
|
@@ -560,6 +643,7 @@ function processUpdate(models) {
|
|
|
560
643
|
|
|
561
644
|
const processedWhere = [];
|
|
562
645
|
const processedWhereUnique = [];
|
|
646
|
+
const extRegex = /\.(ts|js)$/;
|
|
563
647
|
function processWhere(models) {
|
|
564
648
|
for (const model of models) {
|
|
565
649
|
const result = stringifyWhere(model);
|
|
@@ -567,7 +651,8 @@ function processWhere(models) {
|
|
|
567
651
|
processedWhere.push({
|
|
568
652
|
name: model.name,
|
|
569
653
|
stringified: result.stringified,
|
|
570
|
-
enumDependencies: result.enumDependencies
|
|
654
|
+
enumDependencies: result.enumDependencies,
|
|
655
|
+
externalSchemaDependencies: result.externalSchemaDependencies
|
|
571
656
|
});
|
|
572
657
|
}
|
|
573
658
|
const uniqueResult = stringifyWhereUnique(model);
|
|
@@ -575,7 +660,8 @@ function processWhere(models) {
|
|
|
575
660
|
processedWhereUnique.push({
|
|
576
661
|
name: model.name,
|
|
577
662
|
stringified: uniqueResult.stringified,
|
|
578
|
-
enumDependencies: uniqueResult.enumDependencies
|
|
663
|
+
enumDependencies: uniqueResult.enumDependencies,
|
|
664
|
+
externalSchemaDependencies: uniqueResult.externalSchemaDependencies
|
|
579
665
|
});
|
|
580
666
|
}
|
|
581
667
|
}
|
|
@@ -583,21 +669,51 @@ function processWhere(models) {
|
|
|
583
669
|
Object.freeze(processedWhereUnique);
|
|
584
670
|
}
|
|
585
671
|
function stringifyWhere(model) {
|
|
586
|
-
const { hidden } = extractAnnotations(
|
|
587
|
-
model.documentation
|
|
588
|
-
);
|
|
672
|
+
const { hidden } = extractAnnotations(model.documentation);
|
|
589
673
|
if (hidden) {
|
|
590
674
|
return;
|
|
591
675
|
}
|
|
592
676
|
const fields = [];
|
|
593
677
|
const enumDependencies = [];
|
|
678
|
+
const externalSchemaDependencies = [];
|
|
679
|
+
function generateUniqueAlias(path, exportName, fieldName) {
|
|
680
|
+
const baseName = exportName || path.split("/").pop()?.replace(extRegex, "") || "Schema";
|
|
681
|
+
const suffix = fieldName ? `_${fieldName}` : "";
|
|
682
|
+
return `${baseName}${suffix}`;
|
|
683
|
+
}
|
|
594
684
|
for (const field of model.fields) {
|
|
595
685
|
const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
|
|
596
686
|
if (fieldHidden) continue;
|
|
597
687
|
if (field.kind === "object") continue;
|
|
688
|
+
const schemaAnnotation = fieldAnnotations.find(isSchema);
|
|
598
689
|
const typeOverwrite = fieldAnnotations.find(isTypeOverwrite);
|
|
599
690
|
let fieldType;
|
|
600
|
-
if (
|
|
691
|
+
if (schemaAnnotation) {
|
|
692
|
+
if (schemaAnnotation.isExternal) {
|
|
693
|
+
const alias = generateUniqueAlias(
|
|
694
|
+
schemaAnnotation.importPath,
|
|
695
|
+
schemaAnnotation.exportName,
|
|
696
|
+
field.name
|
|
697
|
+
);
|
|
698
|
+
if (!externalSchemaDependencies.some((d) => d.localAlias === alias)) {
|
|
699
|
+
const dependency = {
|
|
700
|
+
importPath: schemaAnnotation.importPath,
|
|
701
|
+
localAlias: alias
|
|
702
|
+
};
|
|
703
|
+
if (schemaAnnotation.exportName) {
|
|
704
|
+
dependency.exportName = schemaAnnotation.exportName;
|
|
705
|
+
}
|
|
706
|
+
externalSchemaDependencies.push(dependency);
|
|
707
|
+
}
|
|
708
|
+
fieldType = alias;
|
|
709
|
+
} else {
|
|
710
|
+
if (schemaAnnotation.value.trim().startsWith("{")) {
|
|
711
|
+
fieldType = schemaAnnotation.value;
|
|
712
|
+
} else {
|
|
713
|
+
fieldType = `"${schemaAnnotation.value}"`;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
} else if (typeOverwrite) {
|
|
601
717
|
fieldType = typeOverwrite.value;
|
|
602
718
|
} else if (isPrimitivePrismaFieldType(field.type)) {
|
|
603
719
|
fieldType = stringifyPrimitiveType(field.type);
|
|
@@ -611,9 +727,10 @@ function stringifyWhere(model) {
|
|
|
611
727
|
} else {
|
|
612
728
|
continue;
|
|
613
729
|
}
|
|
614
|
-
const isEnumType = field.kind === "enum" && !typeOverwrite;
|
|
730
|
+
const isEnumType = field.kind === "enum" && !typeOverwrite && !schemaAnnotation;
|
|
731
|
+
const isExternalSchema = schemaAnnotation?.isExternal === true;
|
|
615
732
|
if (field.isList) {
|
|
616
|
-
if (isEnumType) {
|
|
733
|
+
if (isExternalSchema || isEnumType) {
|
|
617
734
|
fieldType = `${fieldType}.array()`;
|
|
618
735
|
} else {
|
|
619
736
|
const inner = fieldType.slice(1, -1);
|
|
@@ -626,26 +743,57 @@ function stringifyWhere(model) {
|
|
|
626
743
|
stringified: `{
|
|
627
744
|
${fields.join(",\n ")}
|
|
628
745
|
}`,
|
|
629
|
-
enumDependencies
|
|
746
|
+
enumDependencies,
|
|
747
|
+
externalSchemaDependencies
|
|
630
748
|
};
|
|
631
749
|
}
|
|
632
750
|
function stringifyWhereUnique(model) {
|
|
633
|
-
const { hidden } = extractAnnotations(
|
|
634
|
-
model.documentation
|
|
635
|
-
);
|
|
751
|
+
const { hidden } = extractAnnotations(model.documentation);
|
|
636
752
|
if (hidden) {
|
|
637
753
|
return;
|
|
638
754
|
}
|
|
639
755
|
const fields = [];
|
|
640
756
|
const enumDependencies = [];
|
|
757
|
+
const externalSchemaDependencies = [];
|
|
758
|
+
function generateUniqueAlias(path, exportName, fieldName) {
|
|
759
|
+
const baseName = exportName || path.split("/").pop()?.replace(extRegex, "") || "Schema";
|
|
760
|
+
const suffix = fieldName ? `_${fieldName}` : "";
|
|
761
|
+
return `${baseName}${suffix}`;
|
|
762
|
+
}
|
|
641
763
|
for (const field of model.fields) {
|
|
642
764
|
const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
|
|
643
765
|
if (fieldHidden) continue;
|
|
644
766
|
if (field.kind === "object") continue;
|
|
645
767
|
if (!(field.isId || field.isUnique)) continue;
|
|
768
|
+
const schemaAnnotation = fieldAnnotations.find(isSchema);
|
|
646
769
|
const typeOverwrite = fieldAnnotations.find(isTypeOverwrite);
|
|
647
770
|
let fieldType;
|
|
648
|
-
if (
|
|
771
|
+
if (schemaAnnotation) {
|
|
772
|
+
if (schemaAnnotation.isExternal) {
|
|
773
|
+
const alias = generateUniqueAlias(
|
|
774
|
+
schemaAnnotation.importPath,
|
|
775
|
+
schemaAnnotation.exportName,
|
|
776
|
+
field.name
|
|
777
|
+
);
|
|
778
|
+
if (!externalSchemaDependencies.some((d) => d.localAlias === alias)) {
|
|
779
|
+
const dependency = {
|
|
780
|
+
importPath: schemaAnnotation.importPath,
|
|
781
|
+
localAlias: alias
|
|
782
|
+
};
|
|
783
|
+
if (schemaAnnotation.exportName) {
|
|
784
|
+
dependency.exportName = schemaAnnotation.exportName;
|
|
785
|
+
}
|
|
786
|
+
externalSchemaDependencies.push(dependency);
|
|
787
|
+
}
|
|
788
|
+
fieldType = alias;
|
|
789
|
+
} else {
|
|
790
|
+
if (schemaAnnotation.value.trim().startsWith("{")) {
|
|
791
|
+
fieldType = schemaAnnotation.value;
|
|
792
|
+
} else {
|
|
793
|
+
fieldType = `"${schemaAnnotation.value}"`;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
} else if (typeOverwrite) {
|
|
649
797
|
fieldType = typeOverwrite.value;
|
|
650
798
|
} else if (isPrimitivePrismaFieldType(field.type)) {
|
|
651
799
|
fieldType = stringifyPrimitiveType(field.type);
|
|
@@ -668,7 +816,8 @@ function stringifyWhereUnique(model) {
|
|
|
668
816
|
stringified: `{
|
|
669
817
|
${fields.join(",\n ")}
|
|
670
818
|
}`,
|
|
671
|
-
enumDependencies
|
|
819
|
+
enumDependencies,
|
|
820
|
+
externalSchemaDependencies
|
|
672
821
|
};
|
|
673
822
|
}
|
|
674
823
|
|
|
@@ -682,6 +831,27 @@ function generateEnumImports(enumDependencies) {
|
|
|
682
831
|
return `${enumDependencies.map((enumName) => `import { ${enumName} } from "./${enumName}";`).join("\n")}
|
|
683
832
|
`;
|
|
684
833
|
}
|
|
834
|
+
function generateModelImports(modelDependencies) {
|
|
835
|
+
if (!modelDependencies || modelDependencies.length === 0) {
|
|
836
|
+
return "";
|
|
837
|
+
}
|
|
838
|
+
return `${modelDependencies.map(
|
|
839
|
+
(modelName) => `import { ${modelName}Plain } from "./${modelName}Plain";`
|
|
840
|
+
).join("\n")}
|
|
841
|
+
`;
|
|
842
|
+
}
|
|
843
|
+
function generateExternalSchemaImports(externalSchemaDependencies) {
|
|
844
|
+
if (!externalSchemaDependencies || externalSchemaDependencies.length === 0) {
|
|
845
|
+
return "";
|
|
846
|
+
}
|
|
847
|
+
return `${externalSchemaDependencies.map((dep) => {
|
|
848
|
+
if (dep.exportName) {
|
|
849
|
+
return `import { ${dep.exportName} as ${dep.localAlias} } from "${dep.importPath}";`;
|
|
850
|
+
}
|
|
851
|
+
return `import ${dep.localAlias} from "${dep.importPath}";`;
|
|
852
|
+
}).join("\n")}
|
|
853
|
+
`;
|
|
854
|
+
}
|
|
685
855
|
function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations, processedWhere, processedWhereUnique, processedCreate, processedUpdate, processedRelationsCreate, processedRelationsUpdate, processedSelect, processedInclude, processedOrderBy) {
|
|
686
856
|
const config = getConfig();
|
|
687
857
|
const modelMap = /* @__PURE__ */ new Map();
|
|
@@ -695,12 +865,16 @@ function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations
|
|
|
695
865
|
}
|
|
696
866
|
for (const model of processedPlain) {
|
|
697
867
|
const enumImports = generateEnumImports(model.enumDependencies);
|
|
698
|
-
const
|
|
868
|
+
const externalSchemaImports = generateExternalSchemaImports(
|
|
869
|
+
model.externalSchemaDependencies
|
|
870
|
+
);
|
|
871
|
+
const content = `${arktypeImport}${enumImports}${externalSchemaImports}export const ${model.name}Plain = type(${model.stringified});
|
|
699
872
|
`;
|
|
700
873
|
modelMap.set(`${model.name}Plain`, content);
|
|
701
874
|
}
|
|
702
875
|
for (const model of processedRelations) {
|
|
703
|
-
const
|
|
876
|
+
const modelImports = generateModelImports(model.modelDependencies);
|
|
877
|
+
const content = `${arktypeImport}${modelImports}export const ${model.name}Relations = type(${model.stringified});
|
|
704
878
|
`;
|
|
705
879
|
modelMap.set(`${model.name}Relations`, content);
|
|
706
880
|
}
|
|
@@ -723,25 +897,37 @@ export const ${plain.name} = ${plain.name}Plain;
|
|
|
723
897
|
}
|
|
724
898
|
for (const model of processedWhere) {
|
|
725
899
|
const enumImports = generateEnumImports(model.enumDependencies);
|
|
726
|
-
const
|
|
900
|
+
const externalSchemaImports = generateExternalSchemaImports(
|
|
901
|
+
model.externalSchemaDependencies
|
|
902
|
+
);
|
|
903
|
+
const content = `${arktypeImport}${enumImports}${externalSchemaImports}export const ${model.name}Where = type(${model.stringified});
|
|
727
904
|
`;
|
|
728
905
|
modelMap.set(`${model.name}Where`, content);
|
|
729
906
|
}
|
|
730
907
|
for (const model of processedWhereUnique) {
|
|
731
908
|
const enumImports = generateEnumImports(model.enumDependencies);
|
|
732
|
-
const
|
|
909
|
+
const externalSchemaImports = generateExternalSchemaImports(
|
|
910
|
+
model.externalSchemaDependencies
|
|
911
|
+
);
|
|
912
|
+
const content = `${arktypeImport}${enumImports}${externalSchemaImports}export const ${model.name}WhereUnique = type(${model.stringified});
|
|
733
913
|
`;
|
|
734
914
|
modelMap.set(`${model.name}WhereUnique`, content);
|
|
735
915
|
}
|
|
736
916
|
for (const model of processedCreate) {
|
|
737
917
|
const enumImports = generateEnumImports(model.enumDependencies);
|
|
738
|
-
const
|
|
918
|
+
const externalSchemaImports = generateExternalSchemaImports(
|
|
919
|
+
model.externalSchemaDependencies
|
|
920
|
+
);
|
|
921
|
+
const content = `${arktypeImport}${enumImports}${externalSchemaImports}export const ${model.name}Create = type(${model.stringified});
|
|
739
922
|
`;
|
|
740
923
|
modelMap.set(`${model.name}Create`, content);
|
|
741
924
|
}
|
|
742
925
|
for (const model of processedUpdate) {
|
|
743
926
|
const enumImports = generateEnumImports(model.enumDependencies);
|
|
744
|
-
const
|
|
927
|
+
const externalSchemaImports = generateExternalSchemaImports(
|
|
928
|
+
model.externalSchemaDependencies
|
|
929
|
+
);
|
|
930
|
+
const content = `${arktypeImport}${enumImports}${externalSchemaImports}export const ${model.name}Update = type(${model.stringified});
|
|
745
931
|
`;
|
|
746
932
|
modelMap.set(`${model.name}Update`, content);
|
|
747
933
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-arktype",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Generate ArkType schemas from your Prisma schema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
"vitest": "^4.0.16"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
|
-
"build": "pkgroll",
|
|
58
|
+
"build": "pkgroll && prisma generate",
|
|
59
59
|
"dev": "pkgroll --watch",
|
|
60
60
|
"test": "vitest run",
|
|
61
61
|
"test:watch": "vitest",
|
|
62
|
-
"test:e2e": "pnpm build &&
|
|
62
|
+
"test:e2e": "pnpm build && vitest run",
|
|
63
63
|
"pretest": "pnpm build",
|
|
64
64
|
"postinstall": "lefthook install",
|
|
65
65
|
"lint": "biome check",
|