@postxl/generator 0.72.0 → 0.73.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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateModelExportEncoder = void 0;
4
4
  const imports_1 = require("../../../lib/imports");
5
+ const meta_1 = require("../../../lib/meta");
5
6
  const types_1 = require("../../../lib/schema/types");
6
7
  /**
7
8
  * Creates an encoder for the Seed Excel template.
@@ -13,15 +14,13 @@ function generateModelExportEncoder({ model, meta }) {
13
14
  [meta.import.decoder.location.import]: [meta.import.decoder.rowDecoderName],
14
15
  });
15
16
  const { userFriendlyName: userFriendly, userFriendlyNamePlural: userFriendlyPlural } = meta;
17
+ const fieldEncoders = generateFieldEncoders({ model });
16
18
  /* prettier-ignore */
17
19
  return `
18
20
  import * as z from 'zod'
19
21
 
20
- import { capitalizeKeys } from '@postxl/runtime'
21
-
22
22
  ${imports.generate()}
23
23
 
24
-
25
24
  /**
26
25
  * The type for rows in the ${userFriendly} table
27
26
  */
@@ -31,7 +30,9 @@ function generateModelExportEncoder({ model, meta }) {
31
30
  /**
32
31
  * Converts a ${userFriendly} to an Excel row
33
32
  */
34
- export const ${itemEncoderFunctionName} = (item: ${model.typeName}): ${encodedExcelType} => capitalizeKeys(item)
33
+ export const ${itemEncoderFunctionName} = (item: ${model.typeName}): ${encodedExcelType} => ({
34
+ ${fieldEncoders.join(',\n')}
35
+ })
35
36
 
36
37
  /**
37
38
  * Converts a list of ${userFriendlyPlural} to an Excel table
@@ -40,3 +41,11 @@ function generateModelExportEncoder({ model, meta }) {
40
41
  `;
41
42
  }
42
43
  exports.generateModelExportEncoder = generateModelExportEncoder;
44
+ function generateFieldEncoders({ model }) {
45
+ const fieldEncoders = [];
46
+ for (const field of model.fields) {
47
+ const fieldMeta = (0, meta_1.getFieldMetadata)({ field });
48
+ fieldEncoders.push(`"${fieldMeta.excelColumnName}": item.${field.name}`);
49
+ }
50
+ return fieldEncoders;
51
+ }
@@ -17,11 +17,10 @@ function generateModelImportDecoder({ model, meta }) {
17
17
  });
18
18
  const { userFriendlyName: userFriendly, internalSingularNameCapitalized: singularCapitalized } = meta;
19
19
  const { fieldDecoders, blankFieldDecoders } = generateFieldDecoders({ model, meta, imports });
20
+ const fieldTransformers = generateFieldTransformers({ model });
20
21
  return `
21
22
  import * as z from 'zod'
22
23
 
23
- import { uncapitalizeKeys } from '@postxl/runtime'
24
-
25
24
  ${imports.generate()}
26
25
 
27
26
  /**
@@ -52,7 +51,10 @@ const blank${singularCapitalized}RowDecoder = z
52
51
  export const ${tableImportDecoder} = z
53
52
  .array(
54
53
  blank${singularCapitalized}RowDecoder
55
- .or(${meta.import.decoder.rowDecoderName}.transform(uncapitalizeKeys))
54
+ .or(${meta.import.decoder.rowDecoderName}
55
+ .transform(item => ({
56
+ ${fieldTransformers.join(',\n')}
57
+ })))
56
58
  )
57
59
  .transform((items) => items.filter(Boolean) as ${model.typeName}[])`;
58
60
  }
@@ -63,11 +65,11 @@ function generateFieldDecoders({ model, meta, imports, }) {
63
65
  for (const field of model.fields) {
64
66
  const fieldMeta = (0, meta_1.getFieldMetadata)({ field });
65
67
  const optionalModifier = field.attributes.isUpdatedAt || field.attributes.isCreatedAt ? '.optional()' : '';
66
- blankFieldDecoders.push(`${fieldMeta.excelColumnName}: excelNullOrBlankDecoder${optionalModifier}`);
68
+ blankFieldDecoders.push(`'${fieldMeta.excelColumnName}': excelNullOrBlankDecoder${optionalModifier}`);
67
69
  switch (field.kind) {
68
70
  case 'id': {
69
71
  imports.addImport({ items: [meta.types.toBrandedIdTypeFnName], from: meta.types.importPath });
70
- fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
72
+ fieldDecoders.push(`'${fieldMeta.excelColumnName}': ${toExcelDecoder({
71
73
  tsTypeName: field.unbrandedTypeName,
72
74
  nullable: false,
73
75
  imports,
@@ -75,7 +77,7 @@ function generateFieldDecoders({ model, meta, imports, }) {
75
77
  break;
76
78
  }
77
79
  case 'scalar': {
78
- fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
80
+ fieldDecoders.push(`'${fieldMeta.excelColumnName}': ${toExcelDecoder({
79
81
  tsTypeName: field.tsTypeName,
80
82
  nullable: !field.isRequired,
81
83
  imports,
@@ -86,7 +88,7 @@ function generateFieldDecoders({ model, meta, imports, }) {
86
88
  const refModel = field.relationToModel;
87
89
  const refMeta = (0, meta_1.getModelMetadata)({ model: refModel });
88
90
  imports.addImport({ items: [refMeta.types.toBrandedIdTypeFnName], from: refMeta.types.importPath });
89
- fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
91
+ fieldDecoders.push(`'${fieldMeta.excelColumnName}': ${toExcelDecoder({
90
92
  tsTypeName: field.unbrandedTypeName,
91
93
  nullable: !field.isRequired,
92
94
  imports,
@@ -98,7 +100,7 @@ function generateFieldDecoders({ model, meta, imports, }) {
98
100
  break;
99
101
  }
100
102
  case 'enum': {
101
- fieldDecoders.push(`${fieldMeta.excelColumnName}: z.enum([
103
+ fieldDecoders.push(`'${fieldMeta.excelColumnName}': z.enum([
102
104
  ${field.enumerator.values.map((v) => `'${v}'`).join(', ')}
103
105
  ])${field.isRequired ? '' : '.nullable()'}${optionalModifier}`);
104
106
  break;
@@ -136,3 +138,11 @@ function toExcelDecoder({ tsTypeName, nullable, imports, }) {
136
138
  throw new Error('Unknown type');
137
139
  }
138
140
  }
141
+ function generateFieldTransformers({ model }) {
142
+ const fieldTransformers = [];
143
+ for (const field of model.fields) {
144
+ const fieldMeta = (0, meta_1.getFieldMetadata)({ field });
145
+ fieldTransformers.push(`${field.name}: item['${fieldMeta.excelColumnName}']`);
146
+ }
147
+ return fieldTransformers;
148
+ }
@@ -574,7 +574,7 @@ function getFormFieldComponents({ model, testIdCollector, }) {
574
574
  continue;
575
575
  }
576
576
  const formikFieldName = getFormikFieldName(field.name);
577
- const label = StringUtils.toPascalCase(field.name);
577
+ const label = field.attributes.label;
578
578
  switch (field.kind) {
579
579
  case 'id': {
580
580
  // NOTE: We never show the ID field in the form even if it's in the type signature of the form input.
@@ -81,10 +81,21 @@ export type FieldAttributes = {
81
81
  */
82
82
  maxLength?: number;
83
83
  /**
84
- * Schema tag: `@@Label()`
84
+ * Schema tag: `@@IsNameField()`
85
85
  * The field that should be used as the default label for the model.
86
+ *
87
+ * If this is set, the repository will verify that exactly one record has this field set to true - and use it as the default label.
88
+ *
89
+ * If this attribute is set, this field will be used in the code/UI as the field that specifies the model's name. If not provided, we check for the existence of the`name` field. If that is not found, we use the `id` field as the name field.
90
+ */
91
+ isNameField?: boolean;
92
+ /**
93
+ * Schema tag: `@@Label("Label")`
94
+ * The human readable label/name of the field.
95
+ *
96
+ * If not provided, we use the field name.
86
97
  */
87
- isLabel?: boolean;
98
+ label: string;
88
99
  };
89
100
  export type EnumAttributes = {
90
101
  /**
package/dist/lib/meta.js CHANGED
@@ -402,7 +402,7 @@ function getFieldMetadata({ field }) {
402
402
  tsFieldName: Types.toVariableName((0, string_1.toCamelCase)(field.name)),
403
403
  getByForeignKeyMethodFnName: Types.toFunctionName(`getItemsFor${PascalCase}`),
404
404
  getByForeignKeyIdsMethodFnName: Types.toFunctionName(`getIdsFor${PascalCase}`),
405
- excelColumnName: (0, string_1.toPascalCase)(field.name),
405
+ excelColumnName: field.attributes.label,
406
406
  };
407
407
  }
408
408
  exports.getFieldMetadata = getFieldMetadata;
@@ -284,9 +284,9 @@ export type FieldScalar = Prettify<Omit<FieldCore, 'name'> & {
284
284
  export type FieldId = Prettify<Omit<FieldCore, 'name'> & {
285
285
  kind: 'id';
286
286
  /**
287
- * Name of the field and variable (e.g. id)
287
+ * Name of the field and variable (must be `id`)
288
288
  */
289
- name: Types.FieldName;
289
+ name: Types.IdFieldName;
290
290
  /**
291
291
  * Model that this ID field identifies.
292
292
  */
@@ -314,9 +314,9 @@ export type FieldId = Prettify<Omit<FieldCore, 'name'> & {
314
314
  export declare const isFieldId: (field: Field) => field is Prettify<Omit<FieldCore, "name"> & {
315
315
  kind: 'id';
316
316
  /**
317
- * Name of the field and variable (e.g. id)
317
+ * Name of the field and variable (must be `id`)
318
318
  */
319
- name: Types.FieldName;
319
+ name: Types.IdFieldName;
320
320
  /**
321
321
  * Model that this ID field identifies.
322
322
  */
@@ -71,6 +71,16 @@ export declare const isAnnotatedTypeName: (t: string | AnnotatedTypeName) => t i
71
71
  export type FieldName = string & {
72
72
  readonly ___type: 'FieldName';
73
73
  };
74
+ /**
75
+ * Brands a raw string to an IdFieldName identifier.
76
+ */
77
+ export declare const ID_FIELD_NAME: IdFieldName;
78
+ /**
79
+ * The name of the id field. Must be `id`.
80
+ */
81
+ export type IdFieldName = 'id' & {
82
+ readonly ___type: 'FieldName';
83
+ };
74
84
  /**
75
85
  * Brands a raw string to a FieldName identifier.
76
86
  */
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // MARK: - Generated content related types
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.toPackageName = exports.toPath = exports.toBackendModulePath = exports.toModuleLocation = exports.toFolderName = exports.toFileName = exports.toVariableName = exports.toFunctionName = exports.toEnumName = exports.toFieldName = exports.isAnnotatedTypeName = exports.toAnnotatedTypeName = exports.toTypeName = exports.toModelName = exports.toDiscriminantName = exports.toClassName = void 0;
4
+ exports.toPackageName = exports.toPath = exports.toBackendModulePath = exports.toModuleLocation = exports.toFolderName = exports.toFileName = exports.toVariableName = exports.toFunctionName = exports.toEnumName = exports.toFieldName = exports.ID_FIELD_NAME = exports.isAnnotatedTypeName = exports.toAnnotatedTypeName = exports.toTypeName = exports.toModelName = exports.toDiscriminantName = exports.toClassName = void 0;
5
5
  /**
6
6
  * Converts a raw string to a branded ClassName.
7
7
  */
@@ -35,6 +35,10 @@ exports.toAnnotatedTypeName = toAnnotatedTypeName;
35
35
  */
36
36
  const isAnnotatedTypeName = (t) => typeof t === 'object' && t.kind === 'TypeName';
37
37
  exports.isAnnotatedTypeName = isAnnotatedTypeName;
38
+ /**
39
+ * Brands a raw string to an IdFieldName identifier.
40
+ */
41
+ exports.ID_FIELD_NAME = 'id';
38
42
  /**
39
43
  * Brands a raw string to a FieldName identifier.
40
44
  */
@@ -34,7 +34,6 @@ function extractNestedErrors(value) {
34
34
  * As a fallback, it returns a JSON string representation of the error.
35
35
  */
36
36
  function formatZodError(e) {
37
- console.log(e);
38
37
  const formatted = e.format();
39
38
  if (typeof formatted === 'string') {
40
39
  return formatted;
@@ -138,7 +138,8 @@ function getFieldAttributes(field) {
138
138
  ignore: blankStringBooleanDecoder,
139
139
  description: zod_1.default.string().optional(),
140
140
  isDefault: blankStringBooleanDecoder,
141
- label: blankStringBooleanDecoder,
141
+ isNameField: blankStringBooleanDecoder,
142
+ label: zod_1.default.string().optional(),
142
143
  example: examplesDecoder,
143
144
  examples: examplesDecoder,
144
145
  maxLength: zod_1.default
@@ -148,7 +149,7 @@ function getFieldAttributes(field) {
148
149
  readonly: blankStringBooleanDecoder,
149
150
  })
150
151
  .transform((obj) => {
151
- var _a;
152
+ var _a, _b;
152
153
  if (isPrismaIgnored && !obj.ignore) {
153
154
  throw new Error(`Field ${field.name} is ignored by Prisma, but is missing the "ignore" PostXL attribute!`);
154
155
  }
@@ -156,11 +157,12 @@ function getFieldAttributes(field) {
156
157
  ignore: obj.ignore,
157
158
  description: obj.description,
158
159
  isDefaultField: obj.isDefault,
159
- isLabel: obj.label,
160
+ isNameField: obj.isNameField,
161
+ label: (_a = obj.label) !== null && _a !== void 0 ? _a : (0, string_1.toPascalCase)(field.name),
160
162
  examples: obj.examples || obj.example,
161
163
  maxLength: obj.maxLength,
162
164
  isReadonly: obj.readonly || field.isGenerated || field.isUpdatedAt || field.name === 'createdAt' || field.isId,
163
- isUpdatedAt: (_a = field.isUpdatedAt) !== null && _a !== void 0 ? _a : false,
165
+ isUpdatedAt: (_b = field.isUpdatedAt) !== null && _b !== void 0 ? _b : false,
164
166
  isCreatedAt: field.name === 'createdAt',
165
167
  };
166
168
  });
@@ -226,7 +226,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
226
226
  }
227
227
  if (dmmfField.isId) {
228
228
  const isGeneratedField = isAutoIncrementField(dmmfField) || isUUIDField(dmmfField);
229
- const _field = Object.assign(Object.assign({ kind: 'id' }, shared), { isUnique: isUniqueField(dmmfField), isGenerated: isGeneratedField, unbrandedTypeName: getTsTypeForId(dmmfField), model: core });
229
+ const _field = Object.assign(Object.assign({ kind: 'id' }, shared), { name: Types.ID_FIELD_NAME, isUnique: isUniqueField(dmmfField), isGenerated: isGeneratedField, unbrandedTypeName: getTsTypeForId(dmmfField), model: core });
230
230
  return _field;
231
231
  }
232
232
  if (dmmfField.kind === 'scalar') {
@@ -282,25 +282,25 @@ function validateFields({ fields, model: { name } }) {
282
282
  const fieldName = (0, logger_1.highlight)(`${name}.${field.name}`);
283
283
  switch (field.kind) {
284
284
  case 'scalar':
285
- if (field.name === 'name') {
285
+ if (field.name === 'name' && nameField === undefined) {
286
286
  nameField = field;
287
287
  }
288
288
  if (field.attributes.isCreatedAt) {
289
289
  if (createdAtField) {
290
- (0, error_1.throwError)(`${fieldName} has multiple createdAt fields`);
290
+ (0, error_1.throwError)(`${fieldName} has multiple createdAt fields - ${createdAtField.name} and ${field.name}`);
291
291
  }
292
292
  createdAtField = field;
293
293
  }
294
294
  if (field.attributes.isUpdatedAt) {
295
295
  if (updatedAtField) {
296
- (0, error_1.throwError)(`${fieldName} has multiple updatedAt fields`);
296
+ (0, error_1.throwError)(`${fieldName} has multiple updatedAt fields - ${updatedAtField.name} and ${field.name}`);
297
297
  }
298
298
  updatedAtField = field;
299
299
  }
300
300
  break;
301
301
  case 'id':
302
302
  if (idField) {
303
- (0, error_1.throwError)(`${fieldName} has multiple id fields`);
303
+ (0, error_1.throwError)(`${fieldName} has multiple id fields - ${idField.name} and ${field.name}`);
304
304
  }
305
305
  idField = field;
306
306
  break;
@@ -312,14 +312,20 @@ function validateFields({ fields, model: { name } }) {
312
312
  //handle default case
313
313
  if (field.attributes.isDefaultField && field.kind === 'scalar') {
314
314
  if (defaultField !== undefined) {
315
- (0, error_1.throwError)(`${fieldName} has multiple default fields`);
315
+ (0, error_1.throwError)(`${fieldName} has multiple default fields - ${defaultField.name} and ${field.name}`);
316
316
  }
317
317
  defaultField = field;
318
318
  }
319
319
  //handle name field
320
- if (field.attributes.isLabel) {
320
+ if (field.attributes.isNameField) {
321
321
  if (labelField !== undefined) {
322
- (0, error_1.throwError)(`${fieldName} has multiple name fields`);
322
+ // Note: In case we have a field called "name" but assign the "@@Label" attribute to another field, we ignore the "name" field.
323
+ if (labelField.name === 'name') {
324
+ labelField = field;
325
+ }
326
+ else {
327
+ (0, error_1.throwError)(`${fieldName} has multiple name fields - ${labelField.name} and ${field.name}`);
328
+ }
323
329
  }
324
330
  labelField = field;
325
331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.72.0",
3
+ "version": "0.73.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {