@carlonicora/nextjs-jsonapi 1.1.1 → 1.3.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.
Files changed (230) hide show
  1. package/dist/{BlockNoteEditor-6NVBI7XL.js → BlockNoteEditor-CNXQ3WL3.js} +7 -7
  2. package/dist/{BlockNoteEditor-6NVBI7XL.js.map → BlockNoteEditor-CNXQ3WL3.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-4VOBTXWC.mjs → BlockNoteEditor-DC33K4IF.mjs} +3 -3
  4. package/dist/{chunk-KPRHIJO6.js → chunk-KDFDGXCX.js} +53 -53
  5. package/dist/{chunk-KPRHIJO6.js.map → chunk-KDFDGXCX.js.map} +1 -1
  6. package/dist/{chunk-FYRFMABS.js → chunk-L6LH7WA4.js} +21 -2
  7. package/dist/chunk-L6LH7WA4.js.map +1 -0
  8. package/dist/{chunk-MA2L2PL2.mjs → chunk-MHVXFWZY.mjs} +20 -1
  9. package/dist/chunk-MHVXFWZY.mjs.map +1 -0
  10. package/dist/{chunk-HP4Q5ZVG.mjs → chunk-Y4XI3AZR.mjs} +3 -3
  11. package/dist/client/index.js +3 -3
  12. package/dist/client/index.mjs +2 -2
  13. package/dist/components/index.js +3 -3
  14. package/dist/components/index.mjs +2 -2
  15. package/dist/contexts/index.js +3 -3
  16. package/dist/contexts/index.mjs +2 -2
  17. package/dist/features/index.d.mts +3 -1
  18. package/dist/features/index.d.ts +3 -1
  19. package/dist/features/index.js +4 -2
  20. package/dist/features/index.js.map +1 -1
  21. package/dist/features/index.mjs +3 -1
  22. package/dist/hooks/index.js +3 -3
  23. package/dist/hooks/index.mjs +2 -2
  24. package/dist/scripts/generate-web-module/generator.d.ts +15 -0
  25. package/dist/scripts/generate-web-module/generator.d.ts.map +1 -0
  26. package/dist/scripts/generate-web-module/generator.js +320 -0
  27. package/dist/scripts/generate-web-module/generator.js.map +1 -0
  28. package/dist/scripts/generate-web-module/index.d.ts +16 -0
  29. package/dist/scripts/generate-web-module/index.d.ts.map +1 -0
  30. package/dist/scripts/generate-web-module/index.js +80 -0
  31. package/dist/scripts/generate-web-module/index.js.map +1 -0
  32. package/dist/scripts/generate-web-module/templates/components/container.template.d.ts +14 -0
  33. package/dist/scripts/generate-web-module/templates/components/container.template.d.ts.map +1 -0
  34. package/dist/scripts/generate-web-module/templates/components/container.template.js +124 -0
  35. package/dist/scripts/generate-web-module/templates/components/container.template.js.map +1 -0
  36. package/dist/scripts/generate-web-module/templates/components/content.template.d.ts +15 -0
  37. package/dist/scripts/generate-web-module/templates/components/content.template.d.ts.map +1 -0
  38. package/dist/scripts/generate-web-module/templates/components/content.template.js +39 -0
  39. package/dist/scripts/generate-web-module/templates/components/content.template.js.map +1 -0
  40. package/dist/scripts/generate-web-module/templates/components/deleter.template.d.ts +14 -0
  41. package/dist/scripts/generate-web-module/templates/components/deleter.template.d.ts.map +1 -0
  42. package/dist/scripts/generate-web-module/templates/components/deleter.template.js +51 -0
  43. package/dist/scripts/generate-web-module/templates/components/deleter.template.js.map +1 -0
  44. package/dist/scripts/generate-web-module/templates/components/details.template.d.ts +14 -0
  45. package/dist/scripts/generate-web-module/templates/components/details.template.d.ts.map +1 -0
  46. package/dist/scripts/generate-web-module/templates/components/details.template.js +109 -0
  47. package/dist/scripts/generate-web-module/templates/components/details.template.js.map +1 -0
  48. package/dist/scripts/generate-web-module/templates/components/editor.template.d.ts +14 -0
  49. package/dist/scripts/generate-web-module/templates/components/editor.template.d.ts.map +1 -0
  50. package/dist/scripts/generate-web-module/templates/components/editor.template.js +459 -0
  51. package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -0
  52. package/dist/scripts/generate-web-module/templates/components/index.d.ts +15 -0
  53. package/dist/scripts/generate-web-module/templates/components/index.d.ts.map +1 -0
  54. package/dist/scripts/generate-web-module/templates/components/index.js +29 -0
  55. package/dist/scripts/generate-web-module/templates/components/index.js.map +1 -0
  56. package/dist/scripts/generate-web-module/templates/components/list-container.template.d.ts +14 -0
  57. package/dist/scripts/generate-web-module/templates/components/list-container.template.d.ts.map +1 -0
  58. package/dist/scripts/generate-web-module/templates/components/list-container.template.js +30 -0
  59. package/dist/scripts/generate-web-module/templates/components/list-container.template.js.map +1 -0
  60. package/dist/scripts/generate-web-module/templates/components/list.template.d.ts +14 -0
  61. package/dist/scripts/generate-web-module/templates/components/list.template.d.ts.map +1 -0
  62. package/dist/scripts/generate-web-module/templates/components/list.template.js +84 -0
  63. package/dist/scripts/generate-web-module/templates/components/list.template.js.map +1 -0
  64. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts +14 -0
  65. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts.map +1 -0
  66. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js +184 -0
  67. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js.map +1 -0
  68. package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts +14 -0
  69. package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts.map +1 -0
  70. package/dist/scripts/generate-web-module/templates/components/selector.template.js +199 -0
  71. package/dist/scripts/generate-web-module/templates/components/selector.template.js.map +1 -0
  72. package/dist/scripts/generate-web-module/templates/context.template.d.ts +14 -0
  73. package/dist/scripts/generate-web-module/templates/context.template.d.ts.map +1 -0
  74. package/dist/scripts/generate-web-module/templates/context.template.js +121 -0
  75. package/dist/scripts/generate-web-module/templates/context.template.js.map +1 -0
  76. package/dist/scripts/generate-web-module/templates/data/fields.template.d.ts +14 -0
  77. package/dist/scripts/generate-web-module/templates/data/fields.template.d.ts.map +1 -0
  78. package/dist/scripts/generate-web-module/templates/data/fields.template.js +55 -0
  79. package/dist/scripts/generate-web-module/templates/data/fields.template.js.map +1 -0
  80. package/dist/scripts/generate-web-module/templates/data/index.d.ts +10 -0
  81. package/dist/scripts/generate-web-module/templates/data/index.d.ts.map +1 -0
  82. package/dist/scripts/generate-web-module/templates/data/index.js +26 -0
  83. package/dist/scripts/generate-web-module/templates/data/index.js.map +1 -0
  84. package/dist/scripts/generate-web-module/templates/data/interface.template.d.ts +14 -0
  85. package/dist/scripts/generate-web-module/templates/data/interface.template.d.ts.map +1 -0
  86. package/dist/scripts/generate-web-module/templates/data/interface.template.js +116 -0
  87. package/dist/scripts/generate-web-module/templates/data/interface.template.js.map +1 -0
  88. package/dist/scripts/generate-web-module/templates/data/model.template.d.ts +14 -0
  89. package/dist/scripts/generate-web-module/templates/data/model.template.d.ts.map +1 -0
  90. package/dist/scripts/generate-web-module/templates/data/model.template.js +274 -0
  91. package/dist/scripts/generate-web-module/templates/data/model.template.js.map +1 -0
  92. package/dist/scripts/generate-web-module/templates/data/service.template.d.ts +14 -0
  93. package/dist/scripts/generate-web-module/templates/data/service.template.d.ts.map +1 -0
  94. package/dist/scripts/generate-web-module/templates/data/service.template.js +168 -0
  95. package/dist/scripts/generate-web-module/templates/data/service.template.js.map +1 -0
  96. package/dist/scripts/generate-web-module/templates/index.d.ts +12 -0
  97. package/dist/scripts/generate-web-module/templates/index.d.ts.map +1 -0
  98. package/dist/scripts/generate-web-module/templates/index.js +37 -0
  99. package/dist/scripts/generate-web-module/templates/index.js.map +1 -0
  100. package/dist/scripts/generate-web-module/templates/module.template.d.ts +14 -0
  101. package/dist/scripts/generate-web-module/templates/module.template.d.ts.map +1 -0
  102. package/dist/scripts/generate-web-module/templates/module.template.js +64 -0
  103. package/dist/scripts/generate-web-module/templates/module.template.js.map +1 -0
  104. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.d.ts +14 -0
  105. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.d.ts.map +1 -0
  106. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js +65 -0
  107. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js.map +1 -0
  108. package/dist/scripts/generate-web-module/templates/pages/index.d.ts +8 -0
  109. package/dist/scripts/generate-web-module/templates/pages/index.d.ts.map +1 -0
  110. package/dist/scripts/generate-web-module/templates/pages/index.js +13 -0
  111. package/dist/scripts/generate-web-module/templates/pages/index.js.map +1 -0
  112. package/dist/scripts/generate-web-module/templates/pages/list-page.template.d.ts +14 -0
  113. package/dist/scripts/generate-web-module/templates/pages/list-page.template.d.ts.map +1 -0
  114. package/dist/scripts/generate-web-module/templates/pages/list-page.template.js +37 -0
  115. package/dist/scripts/generate-web-module/templates/pages/list-page.template.js.map +1 -0
  116. package/dist/scripts/generate-web-module/templates/table-hook.template.d.ts +14 -0
  117. package/dist/scripts/generate-web-module/templates/table-hook.template.d.ts.map +1 -0
  118. package/dist/scripts/generate-web-module/templates/table-hook.template.js +174 -0
  119. package/dist/scripts/generate-web-module/templates/table-hook.template.js.map +1 -0
  120. package/dist/scripts/generate-web-module/transformers/field-mapper.d.ts +55 -0
  121. package/dist/scripts/generate-web-module/transformers/field-mapper.d.ts.map +1 -0
  122. package/dist/scripts/generate-web-module/transformers/field-mapper.js +179 -0
  123. package/dist/scripts/generate-web-module/transformers/field-mapper.js.map +1 -0
  124. package/dist/scripts/generate-web-module/transformers/i18n-generator.d.ts +78 -0
  125. package/dist/scripts/generate-web-module/transformers/i18n-generator.d.ts.map +1 -0
  126. package/dist/scripts/generate-web-module/transformers/i18n-generator.js +182 -0
  127. package/dist/scripts/generate-web-module/transformers/i18n-generator.js.map +1 -0
  128. package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts +106 -0
  129. package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts.map +1 -0
  130. package/dist/scripts/generate-web-module/transformers/import-resolver.js +193 -0
  131. package/dist/scripts/generate-web-module/transformers/import-resolver.js.map +1 -0
  132. package/dist/scripts/generate-web-module/transformers/index.d.ts +12 -0
  133. package/dist/scripts/generate-web-module/transformers/index.d.ts.map +1 -0
  134. package/dist/scripts/generate-web-module/transformers/index.js +28 -0
  135. package/dist/scripts/generate-web-module/transformers/index.js.map +1 -0
  136. package/dist/scripts/generate-web-module/transformers/name-transformer.d.ts +60 -0
  137. package/dist/scripts/generate-web-module/transformers/name-transformer.d.ts.map +1 -0
  138. package/dist/scripts/generate-web-module/transformers/name-transformer.js +115 -0
  139. package/dist/scripts/generate-web-module/transformers/name-transformer.js.map +1 -0
  140. package/dist/scripts/generate-web-module/transformers/parent-detector.d.ts +57 -0
  141. package/dist/scripts/generate-web-module/transformers/parent-detector.d.ts.map +1 -0
  142. package/dist/scripts/generate-web-module/transformers/parent-detector.js +88 -0
  143. package/dist/scripts/generate-web-module/transformers/parent-detector.js.map +1 -0
  144. package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts +68 -0
  145. package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts.map +1 -0
  146. package/dist/scripts/generate-web-module/transformers/relationship-resolver.js +219 -0
  147. package/dist/scripts/generate-web-module/transformers/relationship-resolver.js.map +1 -0
  148. package/dist/scripts/generate-web-module/types/field-mapping.types.d.ts +68 -0
  149. package/dist/scripts/generate-web-module/types/field-mapping.types.d.ts.map +1 -0
  150. package/dist/scripts/generate-web-module/types/field-mapping.types.js +129 -0
  151. package/dist/scripts/generate-web-module/types/field-mapping.types.js.map +1 -0
  152. package/dist/scripts/generate-web-module/types/index.d.ts +9 -0
  153. package/dist/scripts/generate-web-module/types/index.d.ts.map +1 -0
  154. package/dist/scripts/generate-web-module/types/index.js +25 -0
  155. package/dist/scripts/generate-web-module/types/index.js.map +1 -0
  156. package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts +40 -0
  157. package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts.map +1 -0
  158. package/dist/scripts/generate-web-module/types/json-schema.interface.js +10 -0
  159. package/dist/scripts/generate-web-module/types/json-schema.interface.js.map +1 -0
  160. package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +128 -0
  161. package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -0
  162. package/dist/scripts/generate-web-module/types/template-data.interface.js +9 -0
  163. package/dist/scripts/generate-web-module/types/template-data.interface.js.map +1 -0
  164. package/dist/scripts/generate-web-module/utils/bootstrapper-updater.d.ts +29 -0
  165. package/dist/scripts/generate-web-module/utils/bootstrapper-updater.d.ts.map +1 -0
  166. package/dist/scripts/generate-web-module/utils/bootstrapper-updater.js +153 -0
  167. package/dist/scripts/generate-web-module/utils/bootstrapper-updater.js.map +1 -0
  168. package/dist/scripts/generate-web-module/utils/file-writer.d.ts +38 -0
  169. package/dist/scripts/generate-web-module/utils/file-writer.d.ts.map +1 -0
  170. package/dist/scripts/generate-web-module/utils/file-writer.js +126 -0
  171. package/dist/scripts/generate-web-module/utils/file-writer.js.map +1 -0
  172. package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts +28 -0
  173. package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts.map +1 -0
  174. package/dist/scripts/generate-web-module/utils/i18n-updater.js +122 -0
  175. package/dist/scripts/generate-web-module/utils/i18n-updater.js.map +1 -0
  176. package/dist/scripts/generate-web-module/utils/index.d.ts +9 -0
  177. package/dist/scripts/generate-web-module/utils/index.d.ts.map +1 -0
  178. package/dist/scripts/generate-web-module/utils/index.js +20 -0
  179. package/dist/scripts/generate-web-module/utils/index.js.map +1 -0
  180. package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts +46 -0
  181. package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts.map +1 -0
  182. package/dist/scripts/generate-web-module/validators/json-schema-validator.js +265 -0
  183. package/dist/scripts/generate-web-module/validators/json-schema-validator.js.map +1 -0
  184. package/package.json +27 -21
  185. package/scripts/generate-web-module/generator.ts +363 -0
  186. package/scripts/generate-web-module/index.ts +49 -0
  187. package/scripts/generate-web-module/templates/components/container.template.ts +129 -0
  188. package/scripts/generate-web-module/templates/components/content.template.ts +40 -0
  189. package/scripts/generate-web-module/templates/components/deleter.template.ts +51 -0
  190. package/scripts/generate-web-module/templates/components/details.template.ts +115 -0
  191. package/scripts/generate-web-module/templates/components/editor.template.ts +495 -0
  192. package/scripts/generate-web-module/templates/components/index.ts +18 -0
  193. package/scripts/generate-web-module/templates/components/list-container.template.ts +30 -0
  194. package/scripts/generate-web-module/templates/components/list.template.ts +91 -0
  195. package/scripts/generate-web-module/templates/components/multi-selector.template.ts +184 -0
  196. package/scripts/generate-web-module/templates/components/selector.template.ts +199 -0
  197. package/scripts/generate-web-module/templates/context.template.ts +121 -0
  198. package/scripts/generate-web-module/templates/data/fields.template.ts +64 -0
  199. package/scripts/generate-web-module/templates/data/index.ts +10 -0
  200. package/scripts/generate-web-module/templates/data/interface.template.ts +136 -0
  201. package/scripts/generate-web-module/templates/data/model.template.ts +327 -0
  202. package/scripts/generate-web-module/templates/data/service.template.ts +185 -0
  203. package/scripts/generate-web-module/templates/index.ts +34 -0
  204. package/scripts/generate-web-module/templates/module.template.ts +69 -0
  205. package/scripts/generate-web-module/templates/pages/detail-page.template.ts +65 -0
  206. package/scripts/generate-web-module/templates/pages/index.ts +8 -0
  207. package/scripts/generate-web-module/templates/pages/list-page.template.ts +37 -0
  208. package/scripts/generate-web-module/templates/table-hook.template.ts +182 -0
  209. package/scripts/generate-web-module/transformers/field-mapper.ts +201 -0
  210. package/scripts/generate-web-module/transformers/i18n-generator.ts +199 -0
  211. package/scripts/generate-web-module/transformers/import-resolver.ts +250 -0
  212. package/scripts/generate-web-module/transformers/index.ts +12 -0
  213. package/scripts/generate-web-module/transformers/name-transformer.ts +115 -0
  214. package/scripts/generate-web-module/transformers/parent-detector.ts +87 -0
  215. package/scripts/generate-web-module/transformers/relationship-resolver.ts +221 -0
  216. package/scripts/generate-web-module/tsconfig.json +24 -0
  217. package/scripts/generate-web-module/types/field-mapping.types.ts +141 -0
  218. package/scripts/generate-web-module/types/index.ts +9 -0
  219. package/scripts/generate-web-module/types/json-schema.interface.ts +42 -0
  220. package/scripts/generate-web-module/types/template-data.interface.ts +164 -0
  221. package/scripts/generate-web-module/utils/bootstrapper-updater.ts +145 -0
  222. package/scripts/generate-web-module/utils/file-writer.ts +115 -0
  223. package/scripts/generate-web-module/utils/i18n-updater.ts +108 -0
  224. package/scripts/generate-web-module/utils/index.ts +9 -0
  225. package/scripts/generate-web-module/validators/json-schema-validator.ts +306 -0
  226. package/src/features/user/index.ts +1 -1
  227. package/dist/chunk-FYRFMABS.js.map +0 -1
  228. package/dist/chunk-MA2L2PL2.mjs.map +0 -1
  229. /package/dist/{BlockNoteEditor-4VOBTXWC.mjs.map → BlockNoteEditor-DC33K4IF.mjs.map} +0 -0
  230. /package/dist/{chunk-HP4Q5ZVG.mjs.map → chunk-Y4XI3AZR.mjs.map} +0 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * i18n Generator
3
+ *
4
+ * Generates internationalization keys and messages for a module.
5
+ */
6
+
7
+ import { FrontendField, FrontendRelationship, I18nKeySet, NameTransforms } from "../types/template-data.interface";
8
+ import { toTitleCase, pluralize, toCamelCase, toKebabCase } from "./name-transformer";
9
+
10
+ /**
11
+ * Generate complete i18n key set for a module
12
+ *
13
+ * @param names - Name transforms
14
+ * @param fields - Array of frontend fields
15
+ * @param relationships - Array of frontend relationships
16
+ * @returns i18n key set
17
+ */
18
+ export function generateI18nKeys(
19
+ names: NameTransforms,
20
+ fields: FrontendField[],
21
+ relationships: FrontendRelationship[]
22
+ ): I18nKeySet {
23
+ const lowerModuleName = names.camelCase;
24
+
25
+ // Generate field keys
26
+ const fieldKeys: I18nKeySet["fields"] = {};
27
+ fields.forEach((field) => {
28
+ fieldKeys[field.name] = {
29
+ label: toTitleCase(field.name),
30
+ placeholder: `Enter ${toTitleCase(field.name).toLowerCase()}`,
31
+ error: `${toTitleCase(field.name)} is required`,
32
+ };
33
+ });
34
+
35
+ // Generate relationship keys
36
+ const relationshipKeys: I18nKeySet["relationships"] = {};
37
+ relationships.forEach((rel) => {
38
+ const effectiveName = rel.variant || rel.name;
39
+ const effectiveKey = toCamelCase(effectiveName);
40
+ relationshipKeys[effectiveKey] = {
41
+ label: toTitleCase(effectiveName),
42
+ placeholder: `Select ${toTitleCase(effectiveName).toLowerCase()}`,
43
+ error: `${toTitleCase(effectiveName)} is required`,
44
+ list: pluralize(toTitleCase(rel.name)),
45
+ };
46
+ });
47
+
48
+ // Generate type keys
49
+ const singularTitle = names.titleCase;
50
+ const pluralTitle = names.pluralTitleCase;
51
+
52
+ return {
53
+ moduleName: lowerModuleName,
54
+ fields: fieldKeys,
55
+ relationships: relationshipKeys,
56
+ type: {
57
+ singular: singularTitle,
58
+ plural: pluralTitle,
59
+ icuPlural: `{count, plural, =1 {${singularTitle}} other {${pluralTitle}}}`,
60
+ },
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Build the i18n message object to merge into en.json
66
+ *
67
+ * @param i18nKeys - i18n key set
68
+ * @returns Object structure for en.json
69
+ */
70
+ export function buildI18nMessages(i18nKeys: I18nKeySet): Record<string, any> {
71
+ return {
72
+ features: {
73
+ [i18nKeys.moduleName]: {
74
+ fields: i18nKeys.fields,
75
+ relationships: i18nKeys.relationships,
76
+ },
77
+ },
78
+ types: {
79
+ [i18nKeys.moduleName + "s"]: i18nKeys.type.icuPlural,
80
+ },
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Get the translation key path for a field label
86
+ *
87
+ * @param moduleName - Module name (lowercase)
88
+ * @param fieldName - Field name
89
+ * @returns Translation key path
90
+ */
91
+ export function getFieldLabelKey(moduleName: string, fieldName: string): string {
92
+ return `features.${moduleName}.fields.${fieldName}.label`;
93
+ }
94
+
95
+ /**
96
+ * Get the translation key path for a field placeholder
97
+ *
98
+ * @param moduleName - Module name (lowercase)
99
+ * @param fieldName - Field name
100
+ * @returns Translation key path
101
+ */
102
+ export function getFieldPlaceholderKey(moduleName: string, fieldName: string): string {
103
+ return `features.${moduleName}.fields.${fieldName}.placeholder`;
104
+ }
105
+
106
+ /**
107
+ * Get the translation key path for a field error
108
+ *
109
+ * @param moduleName - Module name (lowercase)
110
+ * @param fieldName - Field name
111
+ * @returns Translation key path
112
+ */
113
+ export function getFieldErrorKey(moduleName: string, fieldName: string): string {
114
+ return `features.${moduleName}.fields.${fieldName}.error`;
115
+ }
116
+
117
+ /**
118
+ * Get the translation key path for type pluralization
119
+ *
120
+ * @param pluralKebab - Plural kebab-case name
121
+ * @returns Translation key path
122
+ */
123
+ export function getTypeKey(pluralKebab: string): string {
124
+ return `types.${pluralKebab}`;
125
+ }
126
+
127
+ /**
128
+ * Generate translation call for use in templates
129
+ *
130
+ * @param keyPath - Full translation key path
131
+ * @param params - Optional parameters for ICU messages
132
+ * @returns Translation function call as string
133
+ */
134
+ export function generateTranslationCall(
135
+ keyPath: string,
136
+ params?: Record<string, any>
137
+ ): string {
138
+ if (params) {
139
+ const paramStr = JSON.stringify(params);
140
+ return `t(\`${keyPath}\`, ${paramStr})`;
141
+ }
142
+ return `t(\`${keyPath}\`)`;
143
+ }
144
+
145
+ /**
146
+ * Merge i18n messages into existing messages object
147
+ * This performs a deep merge, preserving existing keys
148
+ *
149
+ * @param existing - Existing messages object
150
+ * @param newMessages - New messages to merge
151
+ * @returns Merged messages object
152
+ */
153
+ export function mergeI18nMessages(
154
+ existing: Record<string, any>,
155
+ newMessages: Record<string, any>
156
+ ): Record<string, any> {
157
+ const result = { ...existing };
158
+
159
+ function deepMerge(target: Record<string, any>, source: Record<string, any>): void {
160
+ for (const key of Object.keys(source)) {
161
+ if (
162
+ source[key] &&
163
+ typeof source[key] === "object" &&
164
+ !Array.isArray(source[key])
165
+ ) {
166
+ if (!target[key]) {
167
+ target[key] = {};
168
+ }
169
+ deepMerge(target[key], source[key]);
170
+ } else {
171
+ target[key] = source[key];
172
+ }
173
+ }
174
+ }
175
+
176
+ deepMerge(result, newMessages);
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Sort object keys alphabetically (for consistent output)
182
+ *
183
+ * @param obj - Object to sort
184
+ * @returns New object with sorted keys
185
+ */
186
+ export function sortObjectKeys(obj: Record<string, any>): Record<string, any> {
187
+ const sorted: Record<string, any> = {};
188
+ const keys = Object.keys(obj).sort();
189
+
190
+ for (const key of keys) {
191
+ if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
192
+ sorted[key] = sortObjectKeys(obj[key]);
193
+ } else {
194
+ sorted[key] = obj[key];
195
+ }
196
+ }
197
+
198
+ return sorted;
199
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Import Resolver
3
+ *
4
+ * Builds import paths for selectors, models, and related modules.
5
+ */
6
+
7
+ import { FrontendRelationship, ImportStatements, FrontendTemplateData } from "../types/template-data.interface";
8
+ import { AUTHOR_VARIANT } from "../types/field-mapping.types";
9
+ import { toKebabCase, pluralize } from "./name-transformer";
10
+
11
+ /**
12
+ * Build all import statements needed for a module
13
+ *
14
+ * @param relationships - Array of frontend relationships
15
+ * @param extendsContent - Whether the module extends Content
16
+ * @returns Import statements grouped by category
17
+ */
18
+ export function buildImportStatements(
19
+ relationships: FrontendRelationship[],
20
+ extendsContent: boolean
21
+ ): ImportStatements {
22
+ return {
23
+ models: buildModelImports(relationships),
24
+ selectors: buildSelectorImports(relationships),
25
+ library: buildLibraryImports(extendsContent),
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Build model/interface import statements
31
+ *
32
+ * @param relationships - Array of frontend relationships
33
+ * @returns Array of import statements for models
34
+ */
35
+ export function buildModelImports(relationships: FrontendRelationship[]): string[] {
36
+ const imports: string[] = [];
37
+
38
+ relationships.forEach((rel) => {
39
+ // Import interface for relationships
40
+ imports.push(
41
+ `import { ${rel.interfaceName} } from "${rel.interfaceImportPath}";`
42
+ );
43
+ });
44
+
45
+ return [...new Set(imports)]; // Deduplicate
46
+ }
47
+
48
+ /**
49
+ * Build selector component import statements
50
+ *
51
+ * @param relationships - Array of frontend relationships
52
+ * @returns Array of import statements for selectors
53
+ */
54
+ export function buildSelectorImports(relationships: FrontendRelationship[]): string[] {
55
+ const imports: string[] = [];
56
+
57
+ relationships.forEach((rel) => {
58
+ // Skip author - handled specially or auto-set
59
+ if (rel.variant === AUTHOR_VARIANT) {
60
+ return;
61
+ }
62
+
63
+ const componentName = rel.single ? `${rel.name}Selector` : `${rel.name}MultiSelector`;
64
+ imports.push(
65
+ `import ${componentName} from "${rel.importPath}";`
66
+ );
67
+ });
68
+
69
+ return [...new Set(imports)]; // Deduplicate
70
+ }
71
+
72
+ /**
73
+ * Build library import statements
74
+ *
75
+ * @param extendsContent - Whether the module extends Content
76
+ * @returns Array of import statements for library
77
+ */
78
+ export function buildLibraryImports(extendsContent: boolean): string[] {
79
+ const imports: string[] = [];
80
+
81
+ // Core imports always needed
82
+ imports.push(
83
+ `import { AbstractApiData } from "@carlonicora/nextjs-jsonapi/core";`
84
+ );
85
+
86
+ // Content import if extending
87
+ if (extendsContent) {
88
+ imports.push(
89
+ `import { Content, ContentInterface, ContentInput } from "@carlonicora/nextjs-jsonapi/core";`
90
+ );
91
+ }
92
+
93
+ return imports;
94
+ }
95
+
96
+ /**
97
+ * Build import path for a module's own files
98
+ *
99
+ * @param targetDir - Target directory
100
+ * @param moduleName - Module name (PascalCase)
101
+ * @param fileName - File name without extension
102
+ * @returns Import path
103
+ */
104
+ export function buildModuleImportPath(
105
+ targetDir: string,
106
+ moduleName: string,
107
+ fileName: string
108
+ ): string {
109
+ const kebab = toKebabCase(moduleName);
110
+ return `@/features/${targetDir}/${kebab}/data/${fileName}`;
111
+ }
112
+
113
+ /**
114
+ * Build import path for a related module's selector
115
+ *
116
+ * @param rel - Frontend relationship
117
+ * @returns Import path for selector
118
+ */
119
+ export function buildSelectorImportPath(rel: FrontendRelationship): string {
120
+ const componentName = rel.single ? `${rel.name}Selector` : `${rel.name}MultiSelector`;
121
+ return `@/features/${rel.directory}/${rel.modelKebab}/components/forms/${componentName}`;
122
+ }
123
+
124
+ /**
125
+ * Get the web base path for generated files
126
+ *
127
+ * @param targetDir - Target directory (features or foundations)
128
+ * @param moduleName - Module name (PascalCase)
129
+ * @returns Base path for generated files
130
+ */
131
+ export function getWebBasePath(targetDir: string, moduleName: string): string {
132
+ const kebab = toKebabCase(moduleName);
133
+ return `apps/web/src/features/${targetDir}/${kebab}`;
134
+ }
135
+
136
+ /**
137
+ * Get the page path for generated pages
138
+ *
139
+ * @param pluralKebab - Plural kebab-case name (e.g., "comments")
140
+ * @returns Base path for page files
141
+ */
142
+ export function getPageBasePath(pluralKebab: string): string {
143
+ return `apps/web/src/app/[locale]/(main)/(features)/${pluralKebab}`;
144
+ }
145
+
146
+ /**
147
+ * File paths interface
148
+ */
149
+ export interface ModuleFilePaths {
150
+ interface: string;
151
+ model: string;
152
+ service: string;
153
+ fields: string;
154
+ editor: string;
155
+ deleter: string;
156
+ selector: string;
157
+ multiSelector: string;
158
+ list: string;
159
+ details: string;
160
+ content: string;
161
+ container: string;
162
+ listContainer: string;
163
+ context: string;
164
+ tableHook: string;
165
+ module: string;
166
+ listPage: string;
167
+ detailPage: string;
168
+ }
169
+
170
+ /**
171
+ * Build all file paths for a module from template data
172
+ *
173
+ * @param data - Frontend template data
174
+ * @param rootPath - Root path for the monorepo (optional, defaults to "")
175
+ * @returns Object with all file paths
176
+ */
177
+ export function buildFilePaths(
178
+ data: FrontendTemplateData,
179
+ rootPath: string = ""
180
+ ): ModuleFilePaths {
181
+ const { names, targetDir } = data;
182
+ const prefix = rootPath ? `${rootPath}/` : "";
183
+ const basePath = `${prefix}apps/web/src/features/${targetDir}/${names.kebabCase}`;
184
+ const pagePath = `${prefix}apps/web/src/app/[locale]/(main)/(features)/${names.pluralKebab}`;
185
+
186
+ return {
187
+ // Data layer
188
+ interface: `${basePath}/data/${names.pascalCase}Interface.ts`,
189
+ model: `${basePath}/data/${names.pascalCase}.ts`,
190
+ service: `${basePath}/data/${names.pascalCase}Service.ts`,
191
+ fields: `${basePath}/data/${names.pascalCase}Fields.ts`,
192
+
193
+ // Components
194
+ editor: `${basePath}/components/forms/${names.pascalCase}Editor.tsx`,
195
+ deleter: `${basePath}/components/forms/${names.pascalCase}Deleter.tsx`,
196
+ selector: `${basePath}/components/forms/${names.pascalCase}Selector.tsx`,
197
+ multiSelector: `${basePath}/components/forms/${names.pascalCase}MultiSelector.tsx`,
198
+ list: `${basePath}/components/lists/${names.pascalCase}List.tsx`,
199
+ details: `${basePath}/components/details/${names.pascalCase}Details.tsx`,
200
+ content: `${basePath}/components/details/${names.pascalCase}Content.tsx`,
201
+ container: `${basePath}/components/containers/${names.pascalCase}Container.tsx`,
202
+ listContainer: `${basePath}/components/containers/${names.pascalCase}ListContainer.tsx`,
203
+
204
+ // Context & Hooks
205
+ context: `${basePath}/contexts/${names.pascalCase}Context.tsx`,
206
+ tableHook: `${basePath}/hooks/use${names.pascalCase}TableStructure.tsx`,
207
+
208
+ // Module
209
+ module: `${basePath}/${names.pascalCase}Module.ts`,
210
+
211
+ // Pages
212
+ listPage: `${pagePath}/page.tsx`,
213
+ detailPage: `${pagePath}/[id]/page.tsx`,
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Build relative import path between two files
219
+ *
220
+ * @param fromPath - Source file path
221
+ * @param toPath - Target file path
222
+ * @returns Relative import path
223
+ */
224
+ export function buildRelativeImport(fromPath: string, toPath: string): string {
225
+ // For files in same feature, use @ alias
226
+ if (toPath.startsWith("@/")) {
227
+ return toPath;
228
+ }
229
+
230
+ // Calculate relative path
231
+ const fromParts = fromPath.split("/");
232
+ const toParts = toPath.split("/");
233
+
234
+ // Find common prefix
235
+ let commonLength = 0;
236
+ for (let i = 0; i < Math.min(fromParts.length, toParts.length); i++) {
237
+ if (fromParts[i] === toParts[i]) {
238
+ commonLength++;
239
+ } else {
240
+ break;
241
+ }
242
+ }
243
+
244
+ // Build relative path
245
+ const upCount = fromParts.length - commonLength - 1; // -1 for the file itself
246
+ const relativeParts = toParts.slice(commonLength);
247
+ const prefix = upCount > 0 ? "../".repeat(upCount) : "./";
248
+
249
+ return prefix + relativeParts.join("/");
250
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Transformers Index
3
+ *
4
+ * Re-exports all transformer modules.
5
+ */
6
+
7
+ export * from "./name-transformer";
8
+ export * from "./parent-detector";
9
+ export * from "./field-mapper";
10
+ export * from "./relationship-resolver";
11
+ export * from "./import-resolver";
12
+ export * from "./i18n-generator";
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Name Transformer
3
+ *
4
+ * Transforms module names to various case conventions
5
+ * needed for code generation.
6
+ */
7
+
8
+ import { NameTransforms } from "../types/template-data.interface";
9
+
10
+ /**
11
+ * Transform a module name to all necessary case conventions
12
+ *
13
+ * @param moduleName - PascalCase module name (e.g., "Comment")
14
+ * @param endpoint - Optional kebab-case plural endpoint (e.g., "comments"), derived from moduleName if not provided
15
+ * @returns All name transformations
16
+ */
17
+ export function transformNames(moduleName: string, endpoint?: string): NameTransforms {
18
+ const pluralPascal = toPascalCase(pluralize(moduleName));
19
+ const pluralCamel = toCamelCase(pluralPascal);
20
+ const pluralKebab = endpoint || toKebabCase(pluralize(moduleName));
21
+
22
+ return {
23
+ pascalCase: moduleName,
24
+ camelCase: toCamelCase(moduleName),
25
+ kebabCase: toKebabCase(moduleName),
26
+ pluralPascal,
27
+ pluralCamel,
28
+ pluralKebab,
29
+ titleCase: toTitleCase(moduleName),
30
+ pluralTitleCase: toTitleCase(pluralPascal),
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Convert PascalCase to camelCase
36
+ *
37
+ * @param str - PascalCase string
38
+ * @returns camelCase string
39
+ */
40
+ export function toCamelCase(str: string): string {
41
+ return str.charAt(0).toLowerCase() + str.slice(1);
42
+ }
43
+
44
+ /**
45
+ * Convert PascalCase to kebab-case
46
+ *
47
+ * @param str - PascalCase string
48
+ * @returns kebab-case string
49
+ */
50
+ export function toKebabCase(str: string): string {
51
+ return str
52
+ .replace(/([A-Z])/g, "-$1")
53
+ .toLowerCase()
54
+ .replace(/^-/, "");
55
+ }
56
+
57
+ /**
58
+ * Convert string to PascalCase
59
+ *
60
+ * @param str - Any case string
61
+ * @returns PascalCase string
62
+ */
63
+ export function toPascalCase(str: string): string {
64
+ return str.charAt(0).toUpperCase() + str.slice(1);
65
+ }
66
+
67
+ /**
68
+ * Convert PascalCase/camelCase to Title Case with spaces
69
+ * e.g., "firstName" -> "First Name", "UserProfile" -> "User Profile"
70
+ *
71
+ * @param str - PascalCase or camelCase string
72
+ * @returns Title Case string with spaces
73
+ */
74
+ export function toTitleCase(str: string): string {
75
+ return str
76
+ .replace(/([A-Z])/g, " $1")
77
+ .replace(/^./, (char) => char.toUpperCase())
78
+ .trim();
79
+ }
80
+
81
+ /**
82
+ * Simple pluralization
83
+ * Note: This is a basic implementation. For production, consider using a library like 'pluralize'
84
+ *
85
+ * @param str - Singular form
86
+ * @returns Plural form
87
+ */
88
+ export function pluralize(str: string): string {
89
+ if (str.endsWith("s")) {
90
+ return str + "es";
91
+ }
92
+ if (str.endsWith("y") && !["a", "e", "i", "o", "u"].includes(str[str.length - 2])) {
93
+ return str.slice(0, -1) + "ies";
94
+ }
95
+ return str + "s";
96
+ }
97
+
98
+ /**
99
+ * Simple singularization (reverse of pluralize)
100
+ *
101
+ * @param str - Plural form
102
+ * @returns Singular form
103
+ */
104
+ export function singularize(str: string): string {
105
+ if (str.endsWith("ies")) {
106
+ return str.slice(0, -3) + "y";
107
+ }
108
+ if (str.endsWith("ses") || str.endsWith("xes") || str.endsWith("zes")) {
109
+ return str.slice(0, -2);
110
+ }
111
+ if (str.endsWith("s") && !str.endsWith("ss")) {
112
+ return str.slice(0, -1);
113
+ }
114
+ return str;
115
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Parent Class Detector
3
+ *
4
+ * Determines whether a module should extend Content or AbstractApiData
5
+ * based on the presence of Content-specific fields.
6
+ */
7
+
8
+ import { JsonFieldDefinition } from "../types/json-schema.interface";
9
+ import { CONTENT_INDICATOR_FIELDS } from "../types/field-mapping.types";
10
+
11
+ /**
12
+ * Check if a module should extend Content based on its fields
13
+ *
14
+ * @param fields - Array of field definitions from JSON schema
15
+ * @returns true if the module should extend Content
16
+ */
17
+ export function detectExtendsContent(fields: JsonFieldDefinition[]): boolean {
18
+ const fieldNames = fields.map((f) => f.name);
19
+ return CONTENT_INDICATOR_FIELDS.some((indicator) => fieldNames.includes(indicator));
20
+ }
21
+
22
+ /**
23
+ * Get the parent class name based on detection result
24
+ *
25
+ * @param extendsContent - Whether the module extends Content
26
+ * @returns Parent class name
27
+ */
28
+ export function getParentClassName(extendsContent: boolean): string {
29
+ return extendsContent ? "Content" : "AbstractApiData";
30
+ }
31
+
32
+ /**
33
+ * Get the parent interface name based on detection result
34
+ *
35
+ * @param extendsContent - Whether the module extends Content
36
+ * @returns Parent interface name
37
+ */
38
+ export function getParentInterfaceName(extendsContent: boolean): string {
39
+ return extendsContent ? "ContentInterface" : "BaseInterface";
40
+ }
41
+
42
+ /**
43
+ * Get the parent input type name based on detection result
44
+ *
45
+ * @param extendsContent - Whether the module extends Content
46
+ * @returns Parent input type or null if standalone
47
+ */
48
+ export function getParentInputType(extendsContent: boolean): string | null {
49
+ return extendsContent ? "ContentInput" : null;
50
+ }
51
+
52
+ /**
53
+ * Get the import path for the parent class
54
+ *
55
+ * @param extendsContent - Whether the module extends Content
56
+ * @returns Import path for parent class
57
+ */
58
+ export function getParentClassImport(extendsContent: boolean): string {
59
+ if (extendsContent) {
60
+ return '@carlonicora/nextjs-jsonapi/core';
61
+ }
62
+ return '@carlonicora/nextjs-jsonapi/core';
63
+ }
64
+
65
+ /**
66
+ * Check if the module needs to call super.addContentInput() in createJsonApi
67
+ *
68
+ * @param extendsContent - Whether the module extends Content
69
+ * @returns true if super.addContentInput() should be called
70
+ */
71
+ export function needsAddContentInput(extendsContent: boolean): boolean {
72
+ return extendsContent;
73
+ }
74
+
75
+ /**
76
+ * Get fields that are inherited from Content and should not be declared
77
+ *
78
+ * @param extendsContent - Whether the module extends Content
79
+ * @returns Array of field names to exclude from generation
80
+ */
81
+ export function getInheritedFields(extendsContent: boolean): string[] {
82
+ if (extendsContent) {
83
+ // These fields are already in Content, don't redeclare them
84
+ return ["name", "tldr", "abstract", "relevance"];
85
+ }
86
+ return [];
87
+ }