@carlonicora/nextjs-jsonapi 1.7.6 → 1.8.1
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/dist/{BlockNoteEditor-CCOSI7TW.js → BlockNoteEditor-KSPPX6JO.js} +13 -13
- package/dist/{BlockNoteEditor-CCOSI7TW.js.map → BlockNoteEditor-KSPPX6JO.js.map} +1 -1
- package/dist/{BlockNoteEditor-KH7WWHBK.mjs → BlockNoteEditor-N534QVBR.mjs} +3 -3
- package/dist/{chunk-3HL4VFJ4.js → chunk-7Z7FEMEB.js} +56 -36
- package/dist/chunk-7Z7FEMEB.js.map +1 -0
- package/dist/{chunk-KFON36OE.js → chunk-B426TLJC.js} +358 -358
- package/dist/{chunk-KFON36OE.js.map → chunk-B426TLJC.js.map} +1 -1
- package/dist/{chunk-2O7ODHTG.mjs → chunk-CK5KLBZV.mjs} +21 -1
- package/dist/chunk-CK5KLBZV.mjs.map +1 -0
- package/dist/{chunk-2TCBJO4B.mjs → chunk-TLBZWOCU.mjs} +3 -3
- package/dist/client/index.js +3 -3
- package/dist/client/index.mjs +2 -2
- package/dist/components/index.js +3 -3
- package/dist/components/index.mjs +2 -2
- package/dist/contexts/index.js +3 -3
- package/dist/contexts/index.mjs +2 -2
- package/dist/core/index.d.mts +10 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/scripts/generate-web-module/templates/components/editor.template.js +118 -12
- package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/interface.template.js +15 -2
- package/dist/scripts/generate-web-module/templates/data/interface.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/model.template.js +32 -4
- package/dist/scripts/generate-web-module/templates/data/model.template.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/i18n-generator.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js +16 -3
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts +5 -2
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js +17 -7
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js.map +1 -1
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts +1 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +6 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/utils/i18n-updater.js +36 -13
- package/dist/scripts/generate-web-module/utils/i18n-updater.js.map +1 -1
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/package.json +1 -1
- package/scripts/generate-web-module/templates/components/editor.template.ts +125 -13
- package/scripts/generate-web-module/templates/data/interface.template.ts +18 -2
- package/scripts/generate-web-module/templates/data/model.template.ts +40 -6
- package/scripts/generate-web-module/transformers/i18n-generator.ts +18 -3
- package/scripts/generate-web-module/transformers/relationship-resolver.ts +18 -7
- package/scripts/generate-web-module/types/json-schema.interface.ts +1 -0
- package/scripts/generate-web-module/types/template-data.interface.ts +9 -0
- package/scripts/generate-web-module/utils/i18n-updater.ts +42 -15
- package/src/core/abstracts/AbstractApiData.ts +34 -0
- package/dist/chunk-2O7ODHTG.mjs.map +0 -1
- package/dist/chunk-3HL4VFJ4.js.map +0 -1
- /package/dist/{BlockNoteEditor-KH7WWHBK.mjs.map → BlockNoteEditor-N534QVBR.mjs.map} +0 -0
- /package/dist/{chunk-2TCBJO4B.mjs.map → chunk-TLBZWOCU.mjs.map} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var _chunk3ZPK4QOBjs = require('../chunk-3ZPK4QOB.js');
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
var
|
|
15
|
+
var _chunk7Z7FEMEBjs = require('../chunk-7Z7FEMEB.js');
|
|
16
16
|
require('../chunk-IBS6NI7D.js');
|
|
17
17
|
|
|
18
18
|
|
|
@@ -93,7 +93,7 @@ var ServerSession = class {
|
|
|
93
93
|
if (!rawModules) return false;
|
|
94
94
|
const modules = JSON.parse(_zlib2.default.gunzipSync(Buffer.from(rawModules, "base64")).toString());
|
|
95
95
|
const selectedModule = modules.find((module) => module.id === params.module.moduleId);
|
|
96
|
-
return
|
|
96
|
+
return _chunk7Z7FEMEBjs.checkPermissionsFromServer.call(void 0, {
|
|
97
97
|
module: params.module,
|
|
98
98
|
action: params.action,
|
|
99
99
|
data: params.data,
|
|
@@ -303,5 +303,5 @@ _chunk7QVYU63Ejs.__name.call(void 0, ServerJsonApiDelete, "ServerJsonApiDelete")
|
|
|
303
303
|
|
|
304
304
|
|
|
305
305
|
|
|
306
|
-
exports.ServerAuthService =
|
|
306
|
+
exports.ServerAuthService = _chunk7Z7FEMEBjs.AuthService; exports.ServerCompanyService = _chunk7Z7FEMEBjs.CompanyService; exports.ServerContentService = _chunk7Z7FEMEBjs.ContentService; exports.ServerFeatureService = _chunk7Z7FEMEBjs.FeatureService; exports.ServerJsonApiDelete = ServerJsonApiDelete; exports.ServerJsonApiGet = ServerJsonApiGet; exports.ServerJsonApiPatch = ServerJsonApiPatch; exports.ServerJsonApiPost = ServerJsonApiPost; exports.ServerJsonApiPut = ServerJsonApiPut; exports.ServerNotificationService = _chunk7Z7FEMEBjs.NotificationService; exports.ServerPushService = _chunk7Z7FEMEBjs.PushService; exports.ServerRoleService = _chunk7Z7FEMEBjs.RoleService; exports.ServerS3Service = _chunk7Z7FEMEBjs.S3Service; exports.ServerSession = ServerSession; exports.ServerUserService = _chunk7Z7FEMEBjs.UserService; exports.configureServerJsonApi = configureServerJsonApi; exports.getServerApiUrl = getServerApiUrl; exports.getServerAppUrl = getServerAppUrl; exports.getServerToken = _chunkYUO55Q5Ajs.getServerToken; exports.getServerTrackablePages = getServerTrackablePages; exports.invalidateCacheTag = invalidateCacheTag; exports.invalidateCacheTags = invalidateCacheTags; exports.serverRequest = _chunk3ZPK4QOBjs.serverRequest;
|
|
307
307
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ import { FrontendTemplateData, FrontendField, FrontendRelationship } from "../..
|
|
|
8
8
|
import { toCamelCase, pluralize, toPascalCase } from "../../transformers/name-transformer";
|
|
9
9
|
import { AUTHOR_VARIANT } from "../../types/field-mapping.types";
|
|
10
10
|
import { getFormFieldJsx } from "../../transformers/field-mapper";
|
|
11
|
-
import { getRelationshipFormJsx, getDefaultValueExpression, getPayloadMapping, isFoundationImport,
|
|
11
|
+
import { getRelationshipFormJsx, getDefaultValueExpression, getPayloadMapping, isFoundationImport, FOUNDATION_COMPONENTS_PACKAGE } from "../../transformers/relationship-resolver";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Generate the editor component file content
|
|
@@ -156,7 +156,7 @@ function generateImports(data: FrontendTemplateData): string {
|
|
|
156
156
|
const componentName = rel.single ? `${rel.name}Selector` : `${rel.name}MultiSelector`;
|
|
157
157
|
if (rel.isFoundation) {
|
|
158
158
|
// Foundation entities use named imports from the package
|
|
159
|
-
imports.push(`import { ${componentName} } from "${
|
|
159
|
+
imports.push(`import { ${componentName} } from "${FOUNDATION_COMPONENTS_PACKAGE}";`);
|
|
160
160
|
} else {
|
|
161
161
|
imports.push(`import ${componentName} from "${rel.importPath}";`);
|
|
162
162
|
}
|
|
@@ -188,6 +188,21 @@ function generateImports(data: FrontendTemplateData): string {
|
|
|
188
188
|
componentImports.push("FormInput");
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
// Check if any relationship has boolean or date fields that need specific form components
|
|
192
|
+
const hasRelBooleanFields = relationships.some((rel) =>
|
|
193
|
+
rel.fields?.some((f) => f.type === "boolean")
|
|
194
|
+
);
|
|
195
|
+
const hasRelDateFields = relationships.some((rel) =>
|
|
196
|
+
rel.fields?.some((f) => f.type === "date" || f.type === "datetime")
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (hasRelBooleanFields) {
|
|
200
|
+
componentImports.push("FormCheckbox");
|
|
201
|
+
}
|
|
202
|
+
if (hasRelDateFields) {
|
|
203
|
+
componentImports.push("FormDatePicker");
|
|
204
|
+
}
|
|
205
|
+
|
|
191
206
|
imports.push(`import {
|
|
192
207
|
${componentImports.join(",\n ")},
|
|
193
208
|
} from "@carlonicora/nextjs-jsonapi/components";`);
|
|
@@ -256,7 +271,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
256
271
|
// Add name field for Content-extending modules
|
|
257
272
|
if (extendsContent) {
|
|
258
273
|
schemaFields.push(` name: z.string().min(1, {
|
|
259
|
-
message: t(\`features.${names.camelCase}.fields.name.error\`),
|
|
274
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.fields.name.error\`),
|
|
260
275
|
}),`);
|
|
261
276
|
}
|
|
262
277
|
|
|
@@ -268,7 +283,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
268
283
|
schemaFields.push(` ${field.name}: z.string().optional(),`);
|
|
269
284
|
} else {
|
|
270
285
|
schemaFields.push(` ${field.name}: z.string().min(1, {
|
|
271
|
-
message: t(\`features.${names.camelCase}.fields.${field.name}.error\`),
|
|
286
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.error\`),
|
|
272
287
|
}),`);
|
|
273
288
|
}
|
|
274
289
|
} else {
|
|
@@ -279,6 +294,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
279
294
|
// Relationship fields
|
|
280
295
|
relationships.forEach((rel) => {
|
|
281
296
|
const fieldId = toCamelCase(rel.variant || rel.name);
|
|
297
|
+
const fieldIdLower = fieldId.toLowerCase();
|
|
282
298
|
if (rel.variant === AUTHOR_VARIANT) {
|
|
283
299
|
schemaFields.push(` ${fieldId}: userObjectSchema.refine((data) => data.id && data.id.length > 0, {
|
|
284
300
|
message: t(\`generic.relationships.author.error\`),
|
|
@@ -288,9 +304,34 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
288
304
|
schemaFields.push(` ${fieldId}: entityObjectSchema.optional(),`);
|
|
289
305
|
} else {
|
|
290
306
|
schemaFields.push(` ${fieldId}: entityObjectSchema.refine((data) => data.id && data.id.length > 0, {
|
|
291
|
-
message: t(\`
|
|
307
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.error\`),
|
|
292
308
|
}),`);
|
|
293
309
|
}
|
|
310
|
+
// Add relationship property fields to schema
|
|
311
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
312
|
+
rel.fields.forEach((field) => {
|
|
313
|
+
const optional = rel.nullable ? ".optional()" : "";
|
|
314
|
+
switch (field.type) {
|
|
315
|
+
case "number":
|
|
316
|
+
schemaFields.push(` ${field.name}: z.number()${optional},`);
|
|
317
|
+
break;
|
|
318
|
+
case "boolean":
|
|
319
|
+
schemaFields.push(` ${field.name}: z.boolean()${optional},`);
|
|
320
|
+
break;
|
|
321
|
+
case "date":
|
|
322
|
+
case "datetime":
|
|
323
|
+
schemaFields.push(` ${field.name}: z.coerce.date()${optional},`);
|
|
324
|
+
break;
|
|
325
|
+
case "any":
|
|
326
|
+
schemaFields.push(` ${field.name}: z.any()${optional},`);
|
|
327
|
+
break;
|
|
328
|
+
case "string":
|
|
329
|
+
default:
|
|
330
|
+
schemaFields.push(` ${field.name}: z.string()${optional},`);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
294
335
|
} else {
|
|
295
336
|
schemaFields.push(` ${fieldId}: z.array(entityObjectSchema).optional(),`);
|
|
296
337
|
}
|
|
@@ -349,6 +390,28 @@ function generateDefaultValues(data: FrontendTemplateData): string {
|
|
|
349
390
|
defaults.push(` ${fieldId}: ${names.camelCase}?.${propertyName}
|
|
350
391
|
? { id: ${names.camelCase}.${propertyName}.id, name: ${names.camelCase}.${propertyName}.name }
|
|
351
392
|
: undefined,`);
|
|
393
|
+
// Add relationship property field defaults
|
|
394
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
395
|
+
rel.fields.forEach((field) => {
|
|
396
|
+
switch (field.type) {
|
|
397
|
+
case "number":
|
|
398
|
+
defaults.push(` ${field.name}: ${names.camelCase}?.${propertyName}?.${field.name} ?? 0,`);
|
|
399
|
+
break;
|
|
400
|
+
case "boolean":
|
|
401
|
+
defaults.push(` ${field.name}: ${names.camelCase}?.${propertyName}?.${field.name} ?? false,`);
|
|
402
|
+
break;
|
|
403
|
+
case "date":
|
|
404
|
+
case "datetime":
|
|
405
|
+
case "any":
|
|
406
|
+
defaults.push(` ${field.name}: ${names.camelCase}?.${propertyName}?.${field.name},`);
|
|
407
|
+
break;
|
|
408
|
+
case "string":
|
|
409
|
+
default:
|
|
410
|
+
defaults.push(` ${field.name}: ${names.camelCase}?.${propertyName}?.${field.name} ?? "",`);
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
}
|
|
352
415
|
} else {
|
|
353
416
|
defaults.push(` ${fieldId}: ${names.camelCase}?.${pluralPropertyName}
|
|
354
417
|
? ${names.camelCase}.${pluralPropertyName}.map((item) => ({ id: item.id, name: item.name }))
|
|
@@ -394,6 +457,12 @@ function generateOnSubmit(data: FrontendTemplateData): string {
|
|
|
394
457
|
|
|
395
458
|
if (rel.single) {
|
|
396
459
|
payloadFields.push(` ${payloadKey}: values.${fieldId}?.id,`);
|
|
460
|
+
// Add relationship property fields to payload
|
|
461
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
462
|
+
rel.fields.forEach((field) => {
|
|
463
|
+
payloadFields.push(` ${field.name}: values.${field.name},`);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
397
466
|
} else {
|
|
398
467
|
payloadFields.push(` ${payloadKey}: values.${fieldId} ? values.${fieldId}.map((item) => item.id) : [],`);
|
|
399
468
|
}
|
|
@@ -435,8 +504,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
435
504
|
formElements.push(` <FormInput
|
|
436
505
|
form={form}
|
|
437
506
|
id="name"
|
|
438
|
-
name={t(\`features.${names.camelCase}.fields.name.label\`)}
|
|
439
|
-
placeholder={t(\`features.${names.camelCase}.fields.name.placeholder\`)}
|
|
507
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.fields.name.label\`)}
|
|
508
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.name.placeholder\`)}
|
|
440
509
|
isRequired
|
|
441
510
|
/>`);
|
|
442
511
|
}
|
|
@@ -448,7 +517,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
448
517
|
|
|
449
518
|
fieldsToInclude.forEach((field) => {
|
|
450
519
|
if (field.name === "content" || field.isContentField) {
|
|
451
|
-
formElements.push(` <FormContainerGeneric form={form} id="${field.name}" name={t(\`features.${names.camelCase}.fields.${field.name}.label\`)}>
|
|
520
|
+
formElements.push(` <FormContainerGeneric form={form} id="${field.name}" name={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.label\`)}>
|
|
452
521
|
<BlockNoteEditorContainer
|
|
453
522
|
id={form.getValues("id")}
|
|
454
523
|
type="${names.camelCase}"
|
|
@@ -456,7 +525,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
456
525
|
onChange={(content, isEmpty, hasUnresolvedDiff) => {
|
|
457
526
|
form.setValue("${field.name}", content);
|
|
458
527
|
}}
|
|
459
|
-
placeholder={t(\`features.${names.camelCase}.fields.${field.name}.placeholder\`)}
|
|
528
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.placeholder\`)}
|
|
460
529
|
bordered
|
|
461
530
|
/>
|
|
462
531
|
</FormContainerGeneric>`);
|
|
@@ -465,8 +534,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
465
534
|
formElements.push(` <FormInput
|
|
466
535
|
form={form}
|
|
467
536
|
id="${field.name}"
|
|
468
|
-
name={t(\`features.${names.camelCase}.fields.${field.name}.label\`)}
|
|
469
|
-
placeholder={t(\`features.${names.camelCase}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
537
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.label\`)}
|
|
538
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
470
539
|
/>`);
|
|
471
540
|
}
|
|
472
541
|
});
|
|
@@ -479,14 +548,57 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
479
548
|
}
|
|
480
549
|
|
|
481
550
|
const fieldId = toCamelCase(rel.variant || rel.name);
|
|
551
|
+
const fieldIdLower = fieldId.toLowerCase();
|
|
482
552
|
|
|
483
553
|
if (rel.single) {
|
|
484
554
|
formElements.push(` <${rel.name}Selector
|
|
485
555
|
id="${fieldId}"
|
|
486
556
|
form={form}
|
|
487
|
-
label={t(\`
|
|
488
|
-
placeholder={t(\`
|
|
557
|
+
label={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.label\`)}
|
|
558
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.placeholder\`)}${!rel.nullable ? "\n isRequired" : ""}
|
|
559
|
+
/>`);
|
|
560
|
+
// Add form inputs for relationship property fields
|
|
561
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
562
|
+
rel.fields.forEach((field) => {
|
|
563
|
+
const isRequired = !rel.nullable;
|
|
564
|
+
switch (field.type) {
|
|
565
|
+
case "number":
|
|
566
|
+
formElements.push(` <FormInput
|
|
567
|
+
form={form}
|
|
568
|
+
id="${field.name}"
|
|
569
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
570
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.placeholder\`)}
|
|
571
|
+
type="number"${isRequired ? "\n isRequired" : ""}
|
|
572
|
+
/>`);
|
|
573
|
+
break;
|
|
574
|
+
case "boolean":
|
|
575
|
+
formElements.push(` <FormCheckbox
|
|
576
|
+
form={form}
|
|
577
|
+
id="${field.name}"
|
|
578
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
489
579
|
/>`);
|
|
580
|
+
break;
|
|
581
|
+
case "date":
|
|
582
|
+
case "datetime":
|
|
583
|
+
formElements.push(` <FormDatePicker
|
|
584
|
+
form={form}
|
|
585
|
+
id="${field.name}"
|
|
586
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}${isRequired ? "\n isRequired" : ""}
|
|
587
|
+
/>`);
|
|
588
|
+
break;
|
|
589
|
+
case "string":
|
|
590
|
+
case "any":
|
|
591
|
+
default:
|
|
592
|
+
formElements.push(` <FormInput
|
|
593
|
+
form={form}
|
|
594
|
+
id="${field.name}"
|
|
595
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
596
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
597
|
+
/>`);
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
}
|
|
490
602
|
} else {
|
|
491
603
|
formElements.push(` <${rel.name}MultiSelector
|
|
492
604
|
id="${fieldId}"
|
|
@@ -74,13 +74,21 @@ function generateInputType(data: FrontendTemplateData): string {
|
|
|
74
74
|
fieldLines.push(` ${field.name}${optional}: ${field.tsType};`);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
// Add relationship IDs
|
|
77
|
+
// Add relationship IDs and relationship property fields
|
|
78
78
|
relationships.forEach((rel) => {
|
|
79
79
|
const effectiveName = rel.variant || rel.name;
|
|
80
80
|
if (rel.single) {
|
|
81
81
|
const key = `${toCamelCase(effectiveName)}Id`;
|
|
82
82
|
const optional = rel.nullable ? "?" : "";
|
|
83
83
|
fieldLines.push(` ${key}${optional}: string;`);
|
|
84
|
+
|
|
85
|
+
// Add relationship property fields to input type (match relationship optionality)
|
|
86
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
87
|
+
rel.fields.forEach((field) => {
|
|
88
|
+
const fieldOptional = rel.nullable ? "?" : "";
|
|
89
|
+
fieldLines.push(` ${field.name}${fieldOptional}: ${field.tsType};`);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
84
92
|
} else {
|
|
85
93
|
const key = `${toCamelCase(rel.name)}Ids`;
|
|
86
94
|
fieldLines.push(` ${key}?: string[];`);
|
|
@@ -120,7 +128,15 @@ function generateInterface(data: FrontendTemplateData): string {
|
|
|
120
128
|
const effectiveName = rel.variant || rel.name;
|
|
121
129
|
if (rel.single) {
|
|
122
130
|
const propertyName = toCamelCase(effectiveName);
|
|
123
|
-
|
|
131
|
+
|
|
132
|
+
// Build return type - use intersection if relationship has fields
|
|
133
|
+
let baseType = rel.interfaceName;
|
|
134
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
135
|
+
const metaFields = rel.fields.map(f => `${f.name}?: ${f.tsType}`).join("; ");
|
|
136
|
+
baseType = `${rel.interfaceName} & { ${metaFields} }`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const type = rel.nullable ? `(${baseType}) | undefined` : baseType;
|
|
124
140
|
getterLines.push(` get ${propertyName}(): ${type};`);
|
|
125
141
|
} else {
|
|
126
142
|
const propertyName = pluralize(toCamelCase(rel.name));
|
|
@@ -91,7 +91,13 @@ function generatePrivateFields(data: FrontendTemplateData): string {
|
|
|
91
91
|
const effectiveName = rel.variant || rel.name;
|
|
92
92
|
if (rel.single) {
|
|
93
93
|
const propName = toCamelCase(effectiveName);
|
|
94
|
-
|
|
94
|
+
// Use intersection type if relationship has fields
|
|
95
|
+
let typeDecl = rel.interfaceName;
|
|
96
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
97
|
+
const metaFields = rel.fields.map((f) => `${f.name}?: ${f.tsType}`).join("; ");
|
|
98
|
+
typeDecl = `${rel.interfaceName} & { ${metaFields} }`;
|
|
99
|
+
}
|
|
100
|
+
lines.push(` private _${propName}?: ${typeDecl};`);
|
|
95
101
|
} else {
|
|
96
102
|
const propName = pluralize(toCamelCase(rel.name));
|
|
97
103
|
lines.push(` private _${propName}?: ${rel.interfaceName}[];`);
|
|
@@ -127,12 +133,20 @@ function generateGetters(data: FrontendTemplateData): string {
|
|
|
127
133
|
const effectiveName = rel.variant || rel.name;
|
|
128
134
|
if (rel.single) {
|
|
129
135
|
const propName = toCamelCase(effectiveName);
|
|
136
|
+
|
|
137
|
+
// Build return type - use intersection if relationship has fields
|
|
138
|
+
let baseType = rel.interfaceName;
|
|
139
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
140
|
+
const metaFields = rel.fields.map((f) => `${f.name}?: ${f.tsType}`).join("; ");
|
|
141
|
+
baseType = `${rel.interfaceName} & { ${metaFields} }`;
|
|
142
|
+
}
|
|
143
|
+
|
|
130
144
|
if (rel.nullable) {
|
|
131
|
-
lines.push(` get ${propName}(): ${
|
|
145
|
+
lines.push(` get ${propName}(): (${baseType}) | undefined {
|
|
132
146
|
return this._${propName};
|
|
133
147
|
}`);
|
|
134
148
|
} else {
|
|
135
|
-
lines.push(` get ${propName}(): ${
|
|
149
|
+
lines.push(` get ${propName}(): ${baseType} {
|
|
136
150
|
if (this._${propName} === undefined) throw new Error("JsonApi error: ${data.names.camelCase} ${propName} is missing");
|
|
137
151
|
return this._${propName};
|
|
138
152
|
}`);
|
|
@@ -188,9 +202,18 @@ function generateRehydrateMethod(data: FrontendTemplateData): string {
|
|
|
188
202
|
if (rel.single) {
|
|
189
203
|
const propName = toCamelCase(effectiveName);
|
|
190
204
|
const relationshipKey = effectiveName.toLowerCase();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
205
|
+
|
|
206
|
+
// Use _readIncludedWithMeta for relationships with fields
|
|
207
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
208
|
+
const metaType = `{ ${rel.fields.map((f) => `${f.name}?: ${f.tsType}`).join("; ")} }`;
|
|
209
|
+
lines.push(
|
|
210
|
+
` this._${propName} = this._readIncludedWithMeta<${rel.interfaceName}, ${metaType}>(data, "${relationshipKey}", Modules.${rel.name});`,
|
|
211
|
+
);
|
|
212
|
+
} else {
|
|
213
|
+
lines.push(
|
|
214
|
+
` this._${propName} = this._readIncluded(data, "${relationshipKey}", Modules.${rel.name}) as ${rel.interfaceName}${rel.nullable ? " | undefined" : ""};`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
194
217
|
} else {
|
|
195
218
|
const propName = pluralize(toCamelCase(rel.name));
|
|
196
219
|
const relationshipKey = pluralize(rel.name.toLowerCase());
|
|
@@ -268,6 +291,17 @@ function generateCreateJsonApiMethod(data: FrontendTemplateData): string {
|
|
|
268
291
|
lines.push(` type: Modules.${rel.name}.name,`);
|
|
269
292
|
lines.push(` id: data.${payloadKey},`);
|
|
270
293
|
lines.push(` },`);
|
|
294
|
+
|
|
295
|
+
// Add meta for relationship fields
|
|
296
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
297
|
+
lines.push(` meta: {`);
|
|
298
|
+
rel.fields.forEach((field, i) => {
|
|
299
|
+
const comma = i < rel.fields!.length - 1 ? "," : "";
|
|
300
|
+
lines.push(` ${field.name}: data.${field.name}${comma}`);
|
|
301
|
+
});
|
|
302
|
+
lines.push(` },`);
|
|
303
|
+
}
|
|
304
|
+
|
|
271
305
|
lines.push(` };`);
|
|
272
306
|
lines.push(` }`);
|
|
273
307
|
} else {
|
|
@@ -20,7 +20,7 @@ export function generateI18nKeys(
|
|
|
20
20
|
fields: FrontendField[],
|
|
21
21
|
relationships: FrontendRelationship[]
|
|
22
22
|
): I18nKeySet {
|
|
23
|
-
const lowerModuleName = names.camelCase;
|
|
23
|
+
const lowerModuleName = names.camelCase.toLowerCase();
|
|
24
24
|
|
|
25
25
|
// Generate field keys
|
|
26
26
|
const fieldKeys: I18nKeySet["fields"] = {};
|
|
@@ -36,13 +36,25 @@ export function generateI18nKeys(
|
|
|
36
36
|
const relationshipKeys: I18nKeySet["relationships"] = {};
|
|
37
37
|
relationships.forEach((rel) => {
|
|
38
38
|
const effectiveName = rel.variant || rel.name;
|
|
39
|
-
const effectiveKey = toCamelCase(effectiveName);
|
|
39
|
+
const effectiveKey = toCamelCase(effectiveName).toLowerCase();
|
|
40
40
|
relationshipKeys[effectiveKey] = {
|
|
41
41
|
label: toTitleCase(effectiveName),
|
|
42
42
|
placeholder: `Select ${toTitleCase(effectiveName).toLowerCase()}`,
|
|
43
43
|
error: `${toTitleCase(effectiveName)} is required`,
|
|
44
44
|
list: pluralize(toTitleCase(rel.name)),
|
|
45
45
|
};
|
|
46
|
+
|
|
47
|
+
// Add fields for relationship edge properties
|
|
48
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
49
|
+
relationshipKeys[effectiveKey].fields = {};
|
|
50
|
+
rel.fields.forEach((field) => {
|
|
51
|
+
relationshipKeys[effectiveKey].fields![field.name] = {
|
|
52
|
+
label: toTitleCase(field.name),
|
|
53
|
+
placeholder: `Enter ${toTitleCase(field.name).toLowerCase()}`,
|
|
54
|
+
error: `${toTitleCase(field.name)} is required`,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
46
58
|
});
|
|
47
59
|
|
|
48
60
|
// Generate type keys
|
|
@@ -68,6 +80,9 @@ export function generateI18nKeys(
|
|
|
68
80
|
* @returns Object structure for en.json
|
|
69
81
|
*/
|
|
70
82
|
export function buildI18nMessages(i18nKeys: I18nKeySet): Record<string, any> {
|
|
83
|
+
// Use proper pluralization and lowercase for types key
|
|
84
|
+
const pluralLowercaseKey = pluralize(i18nKeys.moduleName).toLowerCase();
|
|
85
|
+
|
|
71
86
|
return {
|
|
72
87
|
features: {
|
|
73
88
|
[i18nKeys.moduleName]: {
|
|
@@ -76,7 +91,7 @@ export function buildI18nMessages(i18nKeys: I18nKeySet): Record<string, any> {
|
|
|
76
91
|
},
|
|
77
92
|
},
|
|
78
93
|
types: {
|
|
79
|
-
[
|
|
94
|
+
[pluralLowercaseKey]: i18nKeys.type.icuPlural,
|
|
80
95
|
},
|
|
81
96
|
};
|
|
82
97
|
}
|
|
@@ -6,14 +6,18 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { JsonRelationshipDefinition } from "../types/json-schema.interface";
|
|
9
|
-
import { FrontendRelationship, RelationshipServiceMethod } from "../types/template-data.interface";
|
|
9
|
+
import { FrontendRelationship, FrontendField, RelationshipServiceMethod } from "../types/template-data.interface";
|
|
10
10
|
import { AUTHOR_VARIANT, AUTHOR_ZOD_SCHEMA, ENTITY_ZOD_SCHEMA } from "../types/field-mapping.types";
|
|
11
11
|
import { toCamelCase, toKebabCase, pluralize, toPascalCase } from "./name-transformer";
|
|
12
|
+
import { mapFields } from "./field-mapper";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Foundation package
|
|
15
|
+
* Foundation package constants for web imports
|
|
16
|
+
* Components (selectors) come from /components
|
|
17
|
+
* Data (interfaces, services) come from /core
|
|
15
18
|
*/
|
|
16
|
-
export const
|
|
19
|
+
export const FOUNDATION_COMPONENTS_PACKAGE = "@carlonicora/nextjs-jsonapi/components";
|
|
20
|
+
export const FOUNDATION_CORE_PACKAGE = "@carlonicora/nextjs-jsonapi/core";
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* Check if a directory represents a foundation import (from the package)
|
|
@@ -78,9 +82,9 @@ export function resolveRelationship(rel: JsonRelationshipDefinition): FrontendRe
|
|
|
78
82
|
|
|
79
83
|
if (isFoundationImport(rel.directory)) {
|
|
80
84
|
// Foundation entities import from the package
|
|
81
|
-
importPath =
|
|
82
|
-
interfaceImportPath =
|
|
83
|
-
serviceImportPath =
|
|
85
|
+
importPath = FOUNDATION_COMPONENTS_PACKAGE; // Selectors from /components
|
|
86
|
+
interfaceImportPath = FOUNDATION_CORE_PACKAGE; // Interfaces from /core
|
|
87
|
+
serviceImportPath = FOUNDATION_CORE_PACKAGE; // Services from /core
|
|
84
88
|
} else {
|
|
85
89
|
// Feature entities use local paths
|
|
86
90
|
const webDirectory = mapDirectoryToWebPath(rel.directory);
|
|
@@ -89,6 +93,12 @@ export function resolveRelationship(rel: JsonRelationshipDefinition): FrontendRe
|
|
|
89
93
|
serviceImportPath = `@/features/${webDirectory}/${modelKebab}/data/${rel.name}Service`;
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
// Map relationship fields (only for single relationships)
|
|
97
|
+
let fields: FrontendField[] | undefined;
|
|
98
|
+
if (rel.single && rel.fields && rel.fields.length > 0) {
|
|
99
|
+
fields = mapFields(rel.fields, toCamelCase(rel.name));
|
|
100
|
+
}
|
|
101
|
+
|
|
92
102
|
return {
|
|
93
103
|
name: rel.name,
|
|
94
104
|
variant: rel.variant,
|
|
@@ -106,6 +116,7 @@ export function resolveRelationship(rel: JsonRelationshipDefinition): FrontendRe
|
|
|
106
116
|
serviceImportPath,
|
|
107
117
|
interfaceName: `${rel.name}Interface`,
|
|
108
118
|
modelKebab,
|
|
119
|
+
fields,
|
|
109
120
|
};
|
|
110
121
|
}
|
|
111
122
|
|
|
@@ -161,7 +172,7 @@ export function getSelectorImports(relationships: FrontendRelationship[]): strin
|
|
|
161
172
|
relationships.forEach((rel) => {
|
|
162
173
|
if (isFoundationImport(rel.directory)) {
|
|
163
174
|
// Foundation entities use named imports from the package
|
|
164
|
-
imports.add(`import { ${rel.selectorComponent} } from "${
|
|
175
|
+
imports.add(`import { ${rel.selectorComponent} } from "${FOUNDATION_COMPONENTS_PACKAGE}";`);
|
|
165
176
|
} else {
|
|
166
177
|
imports.add(`import ${rel.selectorComponent} from "${rel.importPath}";`);
|
|
167
178
|
}
|
|
@@ -26,6 +26,7 @@ export interface JsonRelationshipDefinition {
|
|
|
26
26
|
relationshipName: string; // Backend-specific, ignored in frontend
|
|
27
27
|
toNode: boolean; // Backend-specific, ignored in frontend
|
|
28
28
|
nullable: boolean;
|
|
29
|
+
fields?: JsonFieldDefinition[]; // Relationship property fields (stored on edges)
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -58,6 +58,7 @@ export interface FrontendRelationship {
|
|
|
58
58
|
serviceImportPath: string; // Full import path for service
|
|
59
59
|
interfaceName: string; // e.g., "UserInterface"
|
|
60
60
|
modelKebab: string; // e.g., "user"
|
|
61
|
+
fields?: FrontendField[]; // Relationship property fields (stored on edges)
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
|
@@ -80,6 +81,14 @@ export interface I18nKeySet {
|
|
|
80
81
|
placeholder: string;
|
|
81
82
|
error: string;
|
|
82
83
|
list: string;
|
|
84
|
+
fields?: Record<
|
|
85
|
+
string,
|
|
86
|
+
{
|
|
87
|
+
label: string;
|
|
88
|
+
placeholder: string;
|
|
89
|
+
error: string;
|
|
90
|
+
}
|
|
91
|
+
>;
|
|
83
92
|
}
|
|
84
93
|
>;
|
|
85
94
|
type: {
|
|
@@ -52,8 +52,47 @@ export function updateI18n(
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// Build the i18n messages for this module
|
|
56
|
+
const moduleMessages = buildI18nMessages(i18nKeys);
|
|
57
|
+
const lowercaseModuleName = names.camelCase.toLowerCase();
|
|
58
|
+
|
|
59
|
+
// Always ensure types section is updated (even if features already exist)
|
|
60
|
+
let typesUpdated = false;
|
|
61
|
+
if (!messages.types) {
|
|
62
|
+
messages.types = {};
|
|
63
|
+
}
|
|
64
|
+
const typesKey = Object.keys(moduleMessages.types)[0];
|
|
65
|
+
const lowercasePluralKey = names.pluralCamel.toLowerCase();
|
|
66
|
+
if (typesKey && !messages.types[lowercasePluralKey]) {
|
|
67
|
+
messages.types[lowercasePluralKey] = moduleMessages.types[typesKey];
|
|
68
|
+
typesUpdated = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
// Check if module already exists in features
|
|
56
|
-
|
|
72
|
+
const featuresAlreadyExist = messages.features && messages.features[lowercaseModuleName];
|
|
73
|
+
|
|
74
|
+
if (featuresAlreadyExist) {
|
|
75
|
+
// Features exist, but we may have added types
|
|
76
|
+
if (typesUpdated) {
|
|
77
|
+
if (dryRun) {
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
message: `[DRY RUN] Module ${names.camelCase} exists, would add types.${lowercasePluralKey}`,
|
|
81
|
+
alreadyExists: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Write updated content (types were added)
|
|
86
|
+
const updatedContent = JSON.stringify(messages, null, 2);
|
|
87
|
+
fs.writeFileSync(messagesPath, updatedContent, "utf-8");
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
message: `Module ${names.camelCase} exists, added types.${lowercasePluralKey}`,
|
|
92
|
+
alreadyExists: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
57
96
|
return {
|
|
58
97
|
success: true,
|
|
59
98
|
message: `Module ${names.camelCase} already exists in messages/${language}.json`,
|
|
@@ -61,23 +100,11 @@ export function updateI18n(
|
|
|
61
100
|
};
|
|
62
101
|
}
|
|
63
102
|
|
|
64
|
-
//
|
|
65
|
-
const moduleMessages = buildI18nMessages(i18nKeys);
|
|
66
|
-
|
|
67
|
-
// Add to features section
|
|
103
|
+
// Add to features section (new module)
|
|
68
104
|
if (!messages.features) {
|
|
69
105
|
messages.features = {};
|
|
70
106
|
}
|
|
71
|
-
messages.features[
|
|
72
|
-
|
|
73
|
-
// Add to types section (if not exists)
|
|
74
|
-
if (!messages.types) {
|
|
75
|
-
messages.types = {};
|
|
76
|
-
}
|
|
77
|
-
const typesKey = Object.keys(moduleMessages.types)[0];
|
|
78
|
-
if (typesKey && !messages.types[names.pluralCamel]) {
|
|
79
|
-
messages.types[names.pluralCamel] = moduleMessages.types[typesKey];
|
|
80
|
-
}
|
|
107
|
+
messages.features[lowercaseModuleName] = moduleMessages.features[i18nKeys.moduleName];
|
|
81
108
|
|
|
82
109
|
if (dryRun) {
|
|
83
110
|
return {
|