@postxl/generator 0.63.1 → 0.63.3

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.
@@ -15,7 +15,7 @@ export type ModelAttributes = {
15
15
  * @internal
16
16
  * Note: Prisma has it's own schema attribute - but does not expose it in the DMMF.
17
17
  */
18
- databaseSchema?: string;
18
+ databaseSchema: string;
19
19
  /**
20
20
  * Schema tag: ´@@InMemoryOnly()`
21
21
  * Whether the model should be stored in the database or only in memory.
@@ -1,3 +1,4 @@
1
+ import z from 'zod';
1
2
  /**
2
3
  * Prisma generator often overwrites any error message because of some concurrency issues.
3
4
  * By showing the error via console.log and throwing it again, we ensure that the error message is shown.
@@ -7,3 +8,10 @@ export declare function throwError(message: string): never;
7
8
  * Extracts the error message from an error object or any other object.
8
9
  */
9
10
  export declare function extractError(error: unknown): string;
11
+ /**
12
+ * Tries to extract and format a Zod error to a nice log string, color highlighting
13
+ * the object properties that failed validation and their respective error messages.
14
+ *
15
+ * As a fallback, it returns a JSON string representation of the error.
16
+ */
17
+ export declare function formatZodError(e: z.ZodError): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractError = exports.throwError = void 0;
3
+ exports.formatZodError = exports.extractError = exports.throwError = void 0;
4
4
  /**
5
5
  * Prisma generator often overwrites any error message because of some concurrency issues.
6
6
  * By showing the error via console.log and throwing it again, we ensure that the error message is shown.
@@ -21,3 +21,33 @@ function extractError(error) {
21
21
  return JSON.stringify(error);
22
22
  }
23
23
  exports.extractError = extractError;
24
+ function extractNestedErrors(value) {
25
+ if (!value._errors) {
26
+ return '';
27
+ }
28
+ return value._errors.join(' or ');
29
+ }
30
+ /**
31
+ * Tries to extract and format a Zod error to a nice log string, color highlighting
32
+ * the object properties that failed validation and their respective error messages.
33
+ *
34
+ * As a fallback, it returns a JSON string representation of the error.
35
+ */
36
+ function formatZodError(e) {
37
+ console.log(e);
38
+ const formatted = e.format();
39
+ if (typeof formatted === 'string') {
40
+ return formatted;
41
+ }
42
+ if (typeof formatted !== 'object') {
43
+ return JSON.stringify(e.format(), null, 2);
44
+ }
45
+ if (formatted._errors && formatted._errors.length > 0) {
46
+ return `\t${formatted._errors.join(' or ')}`;
47
+ }
48
+ return Object.entries(formatted)
49
+ .filter(([key]) => key !== '_errors')
50
+ .map(([key, value]) => `\t${key}:\t ${extractNestedErrors(value)}`)
51
+ .join('\n');
52
+ }
53
+ exports.formatZodError = formatZodError;
@@ -9,3 +9,9 @@ export interface Logger {
9
9
  error(message: unknown, ...optionalParams: unknown[]): void;
10
10
  query(message: unknown, ...optionalParams: unknown[]): void;
11
11
  }
12
+ /**
13
+ * Highlights a string cyan
14
+ *
15
+ * NOTE: We would normally use `chalk.cyan` here, but this causes an error in the Prisma generator, so we use this workaround.
16
+ */
17
+ export declare function highlight(str: string): string;
@@ -1,2 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.highlight = void 0;
4
+ /**
5
+ * Highlights a string cyan
6
+ *
7
+ * NOTE: We would normally use `chalk.cyan` here, but this causes an error in the Prisma generator, so we use this workaround.
8
+ */
9
+ function highlight(str) {
10
+ return `\u001B[36m${str}\u001B[39m`;
11
+ }
12
+ exports.highlight = highlight;
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getFieldAttributes = exports.getEnumAttributes = exports.getModelAttributes = exports.parseArgumentToStringOrStringArray = exports.parseAttributesFromDocumentation = void 0;
7
7
  const zod_1 = __importDefault(require("zod"));
8
+ const error_1 = require("../lib/utils/error");
9
+ const logger_1 = require("../lib/utils/logger");
8
10
  const string_1 = require("../lib/utils/string");
9
11
  /**
10
12
  * Parses attributes from a given string using provided prefix.
@@ -71,7 +73,9 @@ function getModelAttributes(model) {
71
73
  ignore: blankStringBooleanDecoder,
72
74
  inMemoryOnly: blankStringBooleanDecoder,
73
75
  description: zod_1.default.string().optional(),
74
- schema: zod_1.default.string().optional(),
76
+ schema: zod_1.default.string({
77
+ required_error: `The PostXL attribute ${(0, logger_1.highlight)('`///@@Schema`')} attribute must be provided (in addition to Prisma's @@schema attribute)!`,
78
+ }),
75
79
  index: zod_1.default.array(zod_1.default.string()).optional(),
76
80
  seed: zod_1.default.string().optional(),
77
81
  systemUser: zod_1.default
@@ -90,7 +94,7 @@ function getModelAttributes(model) {
90
94
  }));
91
95
  const result = decoder.safeParse(attributes);
92
96
  if (!result.success) {
93
- throw new Error(`Model ${model.name} has invalid model attributes: ${result.error.toString()}`);
97
+ throw new Error((0, error_1.formatZodError)(result.error));
94
98
  }
95
99
  return result.data;
96
100
  }
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.parsePrismaSchema = void 0;
27
27
  const Types = __importStar(require("../lib/schema/types"));
28
28
  const error_1 = require("../lib/utils/error");
29
+ const logger_1 = require("../lib/utils/logger");
29
30
  const string_1 = require("../lib/utils/string");
30
31
  const attributes_1 = require("./attributes");
31
32
  /**
@@ -54,7 +55,7 @@ function parsePrismaSchema({ datamodel: { enums: enumsRaw, models: modelsRaw },
54
55
  const referencedModel = modelsWithFields.find((m) => m.sourceName === field.relationToModel.sourceName);
55
56
  if (!referencedModel) {
56
57
  // NOTE: This should never happen because Prisma should validate the validity of the relation.
57
- (0, error_1.throwError)(`Model ${highlight(mwf.name)} not found!`);
58
+ (0, error_1.throwError)(`Model ${(0, logger_1.highlight)(mwf.name)} not found!`);
58
59
  }
59
60
  referencedModel.references.push({
60
61
  referencingModel: mwf,
@@ -68,7 +69,7 @@ exports.parsePrismaSchema = parsePrismaSchema;
68
69
  function ensureRequiredModelsExists(models) {
69
70
  for (const requiredModel of REQUIRED_MODELS) {
70
71
  if (!models.find((m) => m.name === requiredModel)) {
71
- (0, error_1.throwError)(`Required model ${highlight(requiredModel)} not found in schema!`);
72
+ (0, error_1.throwError)(`Required model ${(0, logger_1.highlight)(requiredModel)} not found in schema!`);
72
73
  }
73
74
  }
74
75
  }
@@ -89,13 +90,13 @@ function ensureConsistency({ models, enums }) {
89
90
  }
90
91
  for (const model of models) {
91
92
  if ((0, string_1.isPlural)(model.name)) {
92
- errors.push(`Model ${highlight(model.name)} is plural. Please use singular names for models.`);
93
+ errors.push(`Model ${(0, logger_1.highlight)(model.name)} is plural. Please use singular names for models.`);
93
94
  }
94
95
  try {
95
96
  (0, attributes_1.getModelAttributes)(model);
96
97
  }
97
98
  catch (e) {
98
- errors.push(`Model ${highlight(model.name)} has invalid model attributes: ${(0, error_1.extractError)(e)}`);
99
+ errors.push(`Model ${(0, logger_1.highlight)(model.name)} has invalid model attributes: ${(0, error_1.extractError)(e)}`);
99
100
  }
100
101
  }
101
102
  for (const model of models) {
@@ -104,7 +105,7 @@ function ensureConsistency({ models, enums }) {
104
105
  (0, attributes_1.getFieldAttributes)(field);
105
106
  }
106
107
  catch (e) {
107
- errors.push(`Model ${highlight(model.name)} has invalid attributes for field ${highlight(field.name)}:
108
+ errors.push(`Model ${(0, logger_1.highlight)(model.name)} has invalid attributes for field ${(0, logger_1.highlight)(field.name)}:
108
109
  ${(0, error_1.extractError)(e)}`);
109
110
  }
110
111
  }
@@ -114,7 +115,7 @@ function ensureConsistency({ models, enums }) {
114
115
  (0, attributes_1.getEnumAttributes)(enumDef);
115
116
  }
116
117
  catch (e) {
117
- errors.push(`Enum ${highlight(enumDef.name)} has invalid attributes:
118
+ errors.push(`Enum ${(0, logger_1.highlight)(enumDef.name)} has invalid attributes:
118
119
  ${(0, error_1.extractError)(e)}`);
119
120
  }
120
121
  }
@@ -168,17 +169,17 @@ function parseModel({ dmmfModel, enums, models, config, }) {
168
169
  }
169
170
  const referencedModel = models.find((m) => m.sourceName === dmmfField.type);
170
171
  if (!referencedModel) {
171
- (0, error_1.throwError)(`Field ${highlight(`${dmmfModel.name}.${dmmfField.name}`)} references unknown model ${highlight(dmmfField.type)}.`);
172
+ (0, error_1.throwError)(`Field ${(0, logger_1.highlight)(`${dmmfModel.name}.${dmmfField.name}`)} references unknown model ${(0, logger_1.highlight)(dmmfField.type)}.`);
172
173
  }
173
174
  if (!dmmfField.relationFromFields || dmmfField.relationFromFields.length === 0) {
174
175
  // NOTE: This field has no foreign-key values in this model so it must be a back-relation.
175
176
  continue;
176
177
  }
177
178
  if (dmmfField.relationFromFields.length > 1) {
178
- (0, error_1.throwError)(`Field ${highlight(`${dmmfModel.name}.${dmmfField.relationName}`)} has more than one "from" field`);
179
+ (0, error_1.throwError)(`Field ${(0, logger_1.highlight)(`${dmmfModel.name}.${dmmfField.relationName}`)} has more than one "from" field`);
179
180
  }
180
181
  if (dmmfField.relationOnDelete && dmmfField.relationOnDelete !== 'NoAction') {
181
- (0, error_1.throwError)(`Investigate model ${highlight(dmmfModel.name)}: "onDelete" attribute for relationship ${highlight(dmmfField.relationName)} must be "NoAction": any deletes must be handled in the application layer, e.g. to update repository and search caches!`);
182
+ (0, error_1.throwError)(`Investigate model ${(0, logger_1.highlight)(dmmfModel.name)}: "onDelete" attribute for relationship ${(0, logger_1.highlight)(dmmfField.relationName)} must be "NoAction": any deletes must be handled in the application layer, e.g. to update repository and search caches!`);
182
183
  }
183
184
  // NOTE: At this point, we only have the `ModelCore`. After all models are parsed, we need to updated
184
185
  // the relations with the full `Model`. This is done in the `linkModels` function.
@@ -202,7 +203,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
202
203
  })
203
204
  .map((dmmfField) => {
204
205
  const attributes = (0, attributes_1.getFieldAttributes)(dmmfField);
205
- const fieldName = highlight(`${dmmfModel.name}.${dmmfField.name}`);
206
+ const fieldName = (0, logger_1.highlight)(`${dmmfModel.name}.${dmmfField.name}`);
206
207
  const shared = {
207
208
  name: Types.toFieldName((0, string_1.toCamelCase)(dmmfField.name)),
208
209
  description: attributes.description,
@@ -217,7 +218,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
217
218
  const refModel = referencedModels[dmmfField.name];
218
219
  const refField = relationFields[dmmfField.name];
219
220
  if (!refField) {
220
- (0, error_1.throwError)(`${fieldName}: Relation field ${highlight(dmmfField.name)} not found.`);
221
+ (0, error_1.throwError)(`${fieldName}: Relation field ${(0, logger_1.highlight)(dmmfField.name)} not found.`);
221
222
  }
222
223
  const _field = Object.assign(Object.assign({ kind: 'relation' }, shared), { relationFieldName: Types.toFieldName(refField.name), unbrandedTypeName: getTsTypeForId(dmmfField), relationToModel: refModel });
223
224
  return _field;
@@ -244,7 +245,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
244
245
  if (dmmfField.kind === 'enum') {
245
246
  const fieldEnumDef = enums.find((e) => e.sourceName === dmmfField.type);
246
247
  if (!fieldEnumDef) {
247
- (0, error_1.throwError)(`${fieldName}: Field references unknown enum ${highlight(dmmfField.type)}.`);
248
+ (0, error_1.throwError)(`${fieldName}: Field references unknown enum ${(0, logger_1.highlight)(dmmfField.type)}.`);
248
249
  }
249
250
  const _field = Object.assign(Object.assign({ kind: 'enum' }, shared), { typeName: getTsTypeForEnum(dmmfField), enumerator: fieldEnumDef });
250
251
  return _field;
@@ -274,7 +275,7 @@ function validateFields({ fields, model: { name } }) {
274
275
  let nameField = undefined;
275
276
  let defaultField = undefined;
276
277
  for (const field of fields) {
277
- const fieldName = highlight(`${name}.${field.name}`);
278
+ const fieldName = (0, logger_1.highlight)(`${name}.${field.name}`);
278
279
  switch (field.kind) {
279
280
  case 'scalar':
280
281
  if (field.name === 'name') {
@@ -320,7 +321,7 @@ function validateFields({ fields, model: { name } }) {
320
321
  }
321
322
  }
322
323
  if (!idField) {
323
- (0, error_1.throwError)(`Model ${highlight(name)} does not have an id field`);
324
+ (0, error_1.throwError)(`Model ${(0, logger_1.highlight)(name)} does not have an id field`);
324
325
  }
325
326
  return {
326
327
  idField,
@@ -402,12 +403,12 @@ function getTsTypeForScalar(field) {
402
403
  return Types.toTypeName('number');
403
404
  case 'Json':
404
405
  case 'Bytes':
405
- (0, error_1.throwError)(`Field ${highlight(field.name)}: Type ${field.type} Not implemented yet`);
406
+ (0, error_1.throwError)(`Field ${(0, logger_1.highlight)(field.name)}: Type ${field.type} Not implemented yet`);
406
407
  // While TypeScript understands that throwError never returns, eslint doesn't and complains.
407
408
  // Hence we ignore the fallthrough error.
408
409
  // eslint-disable-next-line no-fallthrough
409
410
  default:
410
- (0, error_1.throwError)(`Investigate: 'default' case in getTsTypeForScalar for field ${highlight(field.name)} of type ${field.type}`);
411
+ (0, error_1.throwError)(`Investigate: 'default' case in getTsTypeForScalar for field ${(0, logger_1.highlight)(field.name)} of type ${field.type}`);
411
412
  }
412
413
  }
413
414
  /**
@@ -425,7 +426,7 @@ function getTsTypeForId(field) {
425
426
  case 'Int':
426
427
  return Types.toTypeName('number');
427
428
  default:
428
- (0, error_1.throwError)(`The id field ${highlight(field.name)} is of type ${field.type} - but only BigInt, Boolean, Decimal, Float, Int and String are supported for Ids.`);
429
+ (0, error_1.throwError)(`The id field ${(0, logger_1.highlight)(field.name)} is of type ${field.type} - but only BigInt, Boolean, Decimal, Float, Int and String are supported for Ids.`);
429
430
  }
430
431
  }
431
432
  /**
@@ -440,17 +441,9 @@ function getTsTypeForEnum(field) {
440
441
  case 'Decimal':
441
442
  case 'Float':
442
443
  case 'Int':
443
- (0, error_1.throwError)(`The enum field ${highlight(field.name)} is of type ${field.type} - but only String fields are supported for enums so far.`);
444
+ (0, error_1.throwError)(`The enum field ${(0, logger_1.highlight)(field.name)} is of type ${field.type} - but only String fields are supported for enums so far.`);
444
445
  break;
445
446
  default:
446
447
  return Types.toTypeName(field.type);
447
448
  }
448
449
  }
449
- /**
450
- * Highlights a string cyan
451
- *
452
- * NOTE: We would normally use `chalk.cyan` here, but this causes an error in the generator, so we use this workaround.
453
- */
454
- function highlight(str) {
455
- return `\u001B[36m${str}\u001B[39m`;
456
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.63.1",
3
+ "version": "0.63.3",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {