meno-core 1.0.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 (231) hide show
  1. package/bin/cli.ts +281 -0
  2. package/build-static.ts +298 -0
  3. package/bunfig.toml +39 -0
  4. package/entries/client-router.tsx +111 -0
  5. package/entries/server-router.tsx +71 -0
  6. package/lib/client/ClientInitializer.test.ts +9 -0
  7. package/lib/client/ClientInitializer.test.ts.skip +92 -0
  8. package/lib/client/ClientInitializer.ts +60 -0
  9. package/lib/client/ErrorBoundary.test.tsx +595 -0
  10. package/lib/client/ErrorBoundary.tsx +230 -0
  11. package/lib/client/componentRegistry.test.ts +165 -0
  12. package/lib/client/componentRegistry.ts +18 -0
  13. package/lib/client/contexts/ThemeContext.tsx +73 -0
  14. package/lib/client/core/ComponentBuilder.test.ts +677 -0
  15. package/lib/client/core/ComponentBuilder.ts +660 -0
  16. package/lib/client/core/ComponentRenderer.test.tsx +176 -0
  17. package/lib/client/core/ComponentRenderer.tsx +83 -0
  18. package/lib/client/core/cmsTemplateProcessor.ts +129 -0
  19. package/lib/client/elementRegistry.ts +81 -0
  20. package/lib/client/hmr/HMRManager.tsx +179 -0
  21. package/lib/client/hmr/index.ts +5 -0
  22. package/lib/client/hmrWebSocket.test.ts +9 -0
  23. package/lib/client/hmrWebSocket.ts +250 -0
  24. package/lib/client/hooks/useColorVariables.test.ts +166 -0
  25. package/lib/client/hooks/useColorVariables.ts +249 -0
  26. package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
  27. package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
  28. package/lib/client/hydration/HydrationUtils.test.ts +154 -0
  29. package/lib/client/hydration/HydrationUtils.ts +35 -0
  30. package/lib/client/i18nConfigService.test.ts +74 -0
  31. package/lib/client/i18nConfigService.ts +78 -0
  32. package/lib/client/index.ts +56 -0
  33. package/lib/client/navigation.test.ts +441 -0
  34. package/lib/client/navigation.ts +23 -0
  35. package/lib/client/responsiveStyleResolver.test.ts +491 -0
  36. package/lib/client/responsiveStyleResolver.ts +184 -0
  37. package/lib/client/routing/RouteLoader.test.ts +635 -0
  38. package/lib/client/routing/RouteLoader.ts +347 -0
  39. package/lib/client/routing/Router.tsx +382 -0
  40. package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
  41. package/lib/client/scripts/ScriptExecutor.ts +171 -0
  42. package/lib/client/scripts/formHandler.ts +103 -0
  43. package/lib/client/styleProcessor.test.ts +126 -0
  44. package/lib/client/styleProcessor.ts +92 -0
  45. package/lib/client/styles/StyleInjector.test.ts +354 -0
  46. package/lib/client/styles/StyleInjector.ts +154 -0
  47. package/lib/client/templateEngine.test.ts +660 -0
  48. package/lib/client/templateEngine.ts +667 -0
  49. package/lib/client/theme.test.ts +173 -0
  50. package/lib/client/theme.ts +159 -0
  51. package/lib/client/utils/toast.ts +46 -0
  52. package/lib/server/createServer.ts +170 -0
  53. package/lib/server/cssGenerator.test.ts +172 -0
  54. package/lib/server/cssGenerator.ts +58 -0
  55. package/lib/server/fileWatcher.ts +134 -0
  56. package/lib/server/index.ts +55 -0
  57. package/lib/server/jsonLoader.test.ts +103 -0
  58. package/lib/server/jsonLoader.ts +350 -0
  59. package/lib/server/middleware/cors.test.ts +177 -0
  60. package/lib/server/middleware/cors.ts +69 -0
  61. package/lib/server/middleware/errorHandler.test.ts +208 -0
  62. package/lib/server/middleware/errorHandler.ts +63 -0
  63. package/lib/server/middleware/index.ts +9 -0
  64. package/lib/server/middleware/logger.test.ts +233 -0
  65. package/lib/server/middleware/logger.ts +99 -0
  66. package/lib/server/pageCache.test.ts +167 -0
  67. package/lib/server/pageCache.ts +97 -0
  68. package/lib/server/projectContext.ts +51 -0
  69. package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
  70. package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
  71. package/lib/server/providers/fileSystemPageProvider.ts +83 -0
  72. package/lib/server/routes/api/cms.test.ts +177 -0
  73. package/lib/server/routes/api/cms.ts +82 -0
  74. package/lib/server/routes/api/colors.ts +59 -0
  75. package/lib/server/routes/api/components.ts +70 -0
  76. package/lib/server/routes/api/config.test.ts +9 -0
  77. package/lib/server/routes/api/config.ts +28 -0
  78. package/lib/server/routes/api/core-routes.ts +182 -0
  79. package/lib/server/routes/api/functions.ts +170 -0
  80. package/lib/server/routes/api/index.ts +69 -0
  81. package/lib/server/routes/api/pages.ts +95 -0
  82. package/lib/server/routes/api/shared.test.ts +81 -0
  83. package/lib/server/routes/api/shared.ts +31 -0
  84. package/lib/server/routes/editor.test.ts +9 -0
  85. package/lib/server/routes/index.ts +104 -0
  86. package/lib/server/routes/pages.ts +161 -0
  87. package/lib/server/routes/static.ts +107 -0
  88. package/lib/server/services/ColorService.ts +193 -0
  89. package/lib/server/services/cmsService.test.ts +388 -0
  90. package/lib/server/services/cmsService.ts +296 -0
  91. package/lib/server/services/componentService.test.ts +276 -0
  92. package/lib/server/services/componentService.ts +346 -0
  93. package/lib/server/services/configService.ts +156 -0
  94. package/lib/server/services/fileWatcherService.ts +67 -0
  95. package/lib/server/services/index.ts +10 -0
  96. package/lib/server/services/pageService.test.ts +258 -0
  97. package/lib/server/services/pageService.ts +240 -0
  98. package/lib/server/ssrRenderer.test.ts +1005 -0
  99. package/lib/server/ssrRenderer.ts +878 -0
  100. package/lib/server/utilityClassGenerator.ts +11 -0
  101. package/lib/server/utils/index.ts +5 -0
  102. package/lib/server/utils/jsonLineMapper.test.ts +100 -0
  103. package/lib/server/utils/jsonLineMapper.ts +166 -0
  104. package/lib/server/validateStyleCoverage.test.ts +9 -0
  105. package/lib/server/validateStyleCoverage.ts +167 -0
  106. package/lib/server/websocketManager.test.ts +9 -0
  107. package/lib/server/websocketManager.ts +95 -0
  108. package/lib/shared/attributeNodeUtils.test.ts +152 -0
  109. package/lib/shared/attributeNodeUtils.ts +50 -0
  110. package/lib/shared/breakpoints.test.ts +166 -0
  111. package/lib/shared/breakpoints.ts +65 -0
  112. package/lib/shared/colorProperties.test.ts +111 -0
  113. package/lib/shared/colorProperties.ts +40 -0
  114. package/lib/shared/colorVariableUtils.test.ts +319 -0
  115. package/lib/shared/colorVariableUtils.ts +97 -0
  116. package/lib/shared/constants.test.ts +175 -0
  117. package/lib/shared/constants.ts +116 -0
  118. package/lib/shared/cssGeneration.ts +481 -0
  119. package/lib/shared/cssProperties.test.ts +252 -0
  120. package/lib/shared/cssProperties.ts +338 -0
  121. package/lib/shared/elementUtils.test.ts +245 -0
  122. package/lib/shared/elementUtils.ts +90 -0
  123. package/lib/shared/fontLoader.ts +97 -0
  124. package/lib/shared/i18n.test.ts +313 -0
  125. package/lib/shared/i18n.ts +286 -0
  126. package/lib/shared/index.ts +50 -0
  127. package/lib/shared/interfaces/contentProvider.test.ts +9 -0
  128. package/lib/shared/interfaces/contentProvider.ts +121 -0
  129. package/lib/shared/nodeUtils.test.ts +320 -0
  130. package/lib/shared/nodeUtils.ts +220 -0
  131. package/lib/shared/pathArrayUtils.test.ts +315 -0
  132. package/lib/shared/pathArrayUtils.ts +17 -0
  133. package/lib/shared/pathUtils.test.ts +260 -0
  134. package/lib/shared/pathUtils.ts +244 -0
  135. package/lib/shared/paths/Path.test.ts +74 -0
  136. package/lib/shared/paths/Path.ts +23 -0
  137. package/lib/shared/paths/PathConverter.test.ts +232 -0
  138. package/lib/shared/paths/PathConverter.ts +141 -0
  139. package/lib/shared/paths/PathUtils.ts +290 -0
  140. package/lib/shared/paths/PathValidator.test.ts +193 -0
  141. package/lib/shared/paths/PathValidator.ts +53 -0
  142. package/lib/shared/paths/index.ts +48 -0
  143. package/lib/shared/propResolver.test.ts +639 -0
  144. package/lib/shared/propResolver.ts +124 -0
  145. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
  146. package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
  147. package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
  148. package/lib/shared/registry/ClientRegistry.test.ts +26 -0
  149. package/lib/shared/registry/ClientRegistry.ts +15 -0
  150. package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
  151. package/lib/shared/registry/ComponentRegistry.ts +100 -0
  152. package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
  153. package/lib/shared/registry/NodeTypeManager.ts +94 -0
  154. package/lib/shared/registry/RegistryManager.test.ts +58 -0
  155. package/lib/shared/registry/RegistryManager.ts +60 -0
  156. package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
  157. package/lib/shared/registry/SSRRegistry.test.ts +26 -0
  158. package/lib/shared/registry/SSRRegistry.ts +15 -0
  159. package/lib/shared/registry/createNodeType.ts +175 -0
  160. package/lib/shared/registry/defineNodeType.ts +73 -0
  161. package/lib/shared/registry/fieldPresets.ts +109 -0
  162. package/lib/shared/registry/index.ts +50 -0
  163. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
  164. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
  165. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
  166. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
  167. package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
  168. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
  169. package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
  170. package/lib/shared/registry/nodeTypes/index.ts +75 -0
  171. package/lib/shared/responsiveScaling.test.ts +268 -0
  172. package/lib/shared/responsiveScaling.ts +194 -0
  173. package/lib/shared/responsiveStyleUtils.test.ts +300 -0
  174. package/lib/shared/responsiveStyleUtils.ts +139 -0
  175. package/lib/shared/slugTranslator.test.ts +325 -0
  176. package/lib/shared/slugTranslator.ts +177 -0
  177. package/lib/shared/styleNodeUtils.test.ts +132 -0
  178. package/lib/shared/styleNodeUtils.ts +102 -0
  179. package/lib/shared/styleUtils.test.ts +238 -0
  180. package/lib/shared/styleUtils.ts +63 -0
  181. package/lib/shared/themeDefaults.test.ts +113 -0
  182. package/lib/shared/themeDefaults.ts +103 -0
  183. package/lib/shared/tree/PathBuilder.ts +383 -0
  184. package/lib/shared/treePathUtils.test.ts +539 -0
  185. package/lib/shared/treePathUtils.ts +339 -0
  186. package/lib/shared/types/api.ts +58 -0
  187. package/lib/shared/types/cms.ts +95 -0
  188. package/lib/shared/types/colors.ts +45 -0
  189. package/lib/shared/types/components.ts +121 -0
  190. package/lib/shared/types/errors.test.ts +103 -0
  191. package/lib/shared/types/errors.ts +69 -0
  192. package/lib/shared/types/index.ts +96 -0
  193. package/lib/shared/types/nodes.ts +20 -0
  194. package/lib/shared/types/rendering.ts +61 -0
  195. package/lib/shared/types/styles.ts +38 -0
  196. package/lib/shared/types.ts +11 -0
  197. package/lib/shared/utilityClassConfig.ts +287 -0
  198. package/lib/shared/utilityClassMapper.test.ts +140 -0
  199. package/lib/shared/utilityClassMapper.ts +229 -0
  200. package/lib/shared/utils/fileUtils.test.ts +99 -0
  201. package/lib/shared/utils/fileUtils.ts +56 -0
  202. package/lib/shared/utils.test.ts +261 -0
  203. package/lib/shared/utils.ts +84 -0
  204. package/lib/shared/validation/index.ts +7 -0
  205. package/lib/shared/validation/propValidator.test.ts +178 -0
  206. package/lib/shared/validation/propValidator.ts +238 -0
  207. package/lib/shared/validation/schemas.test.ts +177 -0
  208. package/lib/shared/validation/schemas.ts +401 -0
  209. package/lib/shared/validation/validators.test.ts +109 -0
  210. package/lib/shared/validation/validators.ts +304 -0
  211. package/lib/test-utils/dom-setup.ts +55 -0
  212. package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
  213. package/lib/test-utils/factories/DomMockFactory.ts +487 -0
  214. package/lib/test-utils/factories/EventMockFactory.ts +244 -0
  215. package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
  216. package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
  217. package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
  218. package/lib/test-utils/factories/index.ts +11 -0
  219. package/lib/test-utils/fixtures.ts +134 -0
  220. package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
  221. package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
  222. package/lib/test-utils/helpers/index.ts +6 -0
  223. package/lib/test-utils/helpers.test.ts +73 -0
  224. package/lib/test-utils/helpers.ts +90 -0
  225. package/lib/test-utils/index.ts +17 -0
  226. package/lib/test-utils/mockFactories.ts +92 -0
  227. package/lib/test-utils/mocks.ts +341 -0
  228. package/package.json +38 -0
  229. package/templates/index-router.html +34 -0
  230. package/tsconfig.json +14 -0
  231. package/vite.config.ts +43 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Path Utilities
3
+ * Unified path handling system
4
+ *
5
+ * This module provides a single source of truth for path operations.
6
+ * All path-related functionality is consolidated here.
7
+ */
8
+
9
+ // Export types
10
+ export type { Path } from './Path';
11
+
12
+ // Export constants
13
+ export {
14
+ ROOT_STRING,
15
+ ROOT_0_STRING,
16
+ CHILDREN_SEPARATOR,
17
+ } from './Path';
18
+
19
+ // Export validators
20
+ export {
21
+ validatePath,
22
+ isRootPath,
23
+ InvalidPathError,
24
+ PathConversionError,
25
+ } from './PathValidator';
26
+
27
+ // Export converters
28
+ export {
29
+ normalizePathInput,
30
+ pathToString,
31
+ stringToPath,
32
+ pathToLegacyString,
33
+ domPathStringToTreePath,
34
+ treePathToDomPathString,
35
+ } from './PathConverter';
36
+
37
+ // Export utilities
38
+ export {
39
+ getParentPath,
40
+ getChildPath,
41
+ isAncestorPath,
42
+ getPathDepth,
43
+ buildParentPaths,
44
+ pathsEqual,
45
+ convertPagePathToComponentPath,
46
+ convertComponentPathToPagePath,
47
+ } from './PathUtils';
48
+
@@ -0,0 +1,639 @@
1
+ import { test, expect, describe, beforeEach } from "bun:test";
2
+ import {
3
+ resolveComponentProps,
4
+ resolvePropsFromDefinition
5
+ } from "./propResolver";
6
+ import type { StructuredComponentDefinition, ComponentNode } from "./types";
7
+
8
+ describe("Prop Resolver - resolveComponentProps", () => {
9
+ describe("Default values", () => {
10
+ test("should apply default value when prop not provided", () => {
11
+ const propDefs = {
12
+ variant: {
13
+ type: "select",
14
+ default: "primary"
15
+ }
16
+ };
17
+ const passedProps = {};
18
+ const children: ComponentNode['children'] = [];
19
+
20
+ const result = resolveComponentProps({
21
+ propDefs,
22
+ passedProps,
23
+ children
24
+ });
25
+
26
+ expect(result.variant).toBe("primary");
27
+ });
28
+
29
+ test("should override default with passed prop", () => {
30
+ const propDefs = {
31
+ variant: {
32
+ type: "select",
33
+ default: "primary"
34
+ }
35
+ };
36
+ const passedProps = { variant: "secondary" };
37
+ const children: ComponentNode['children'] = [];
38
+
39
+ const result = resolveComponentProps({
40
+ propDefs,
41
+ passedProps,
42
+ children
43
+ });
44
+
45
+ expect(result.variant).toBe("secondary");
46
+ });
47
+
48
+ test("should handle undefined passed prop value (should use default)", () => {
49
+ const propDefs = {
50
+ variant: {
51
+ type: "select",
52
+ default: "primary"
53
+ }
54
+ };
55
+ const passedProps = { variant: undefined };
56
+ const children: ComponentNode['children'] = [];
57
+
58
+ const result = resolveComponentProps({
59
+ propDefs,
60
+ passedProps,
61
+ children
62
+ });
63
+
64
+ expect(result.variant).toBe("primary");
65
+ });
66
+
67
+ test("should apply multiple defaults", () => {
68
+ const propDefs = {
69
+ variant: {
70
+ type: "select",
71
+ default: "primary"
72
+ },
73
+ size: {
74
+ type: "select",
75
+ default: "medium"
76
+ }
77
+ };
78
+ const passedProps = {};
79
+ const children: ComponentNode['children'] = [];
80
+
81
+ const result = resolveComponentProps({
82
+ propDefs,
83
+ passedProps,
84
+ children
85
+ });
86
+
87
+ expect(result.variant).toBe("primary");
88
+ expect(result.size).toBe("medium");
89
+ });
90
+
91
+ test("should handle null default value", () => {
92
+ const propDefs = {
93
+ optional: {
94
+ type: "select",
95
+ default: null
96
+ }
97
+ };
98
+ const passedProps = {};
99
+ const children: ComponentNode['children'] = [];
100
+
101
+ const result = resolveComponentProps({
102
+ propDefs,
103
+ passedProps,
104
+ children
105
+ });
106
+
107
+ expect(result.optional).toBeNull();
108
+ });
109
+ });
110
+
111
+ describe("Prop types", () => {
112
+ test("should handle select type with default", () => {
113
+ const propDefs = {
114
+ variant: {
115
+ type: "select",
116
+ options: ["primary", "secondary"],
117
+ default: "primary"
118
+ }
119
+ };
120
+ const passedProps = {};
121
+ const children: ComponentNode['children'] = [];
122
+
123
+ const result = resolveComponentProps({
124
+ propDefs,
125
+ passedProps,
126
+ children
127
+ });
128
+
129
+ expect(result.variant).toBe("primary");
130
+ });
131
+
132
+ test("should handle boolean type with default true", () => {
133
+ const propDefs = {
134
+ disabled: {
135
+ type: "boolean",
136
+ default: true
137
+ }
138
+ };
139
+ const passedProps = {};
140
+ const children: ComponentNode['children'] = [];
141
+
142
+ const result = resolveComponentProps({
143
+ propDefs,
144
+ passedProps,
145
+ children
146
+ });
147
+
148
+ expect(result.disabled).toBe(true);
149
+ });
150
+
151
+ test("should handle boolean type with default false", () => {
152
+ const propDefs = {
153
+ disabled: {
154
+ type: "boolean",
155
+ default: false
156
+ }
157
+ };
158
+ const passedProps = {};
159
+ const children: ComponentNode['children'] = [];
160
+
161
+ const result = resolveComponentProps({
162
+ propDefs,
163
+ passedProps,
164
+ children
165
+ });
166
+
167
+ expect(result.disabled).toBe(false);
168
+ });
169
+
170
+ test("should handle slot type with default", () => {
171
+ const propDefs = {
172
+ children: {
173
+ type: "slot",
174
+ default: "Click me"
175
+ }
176
+ };
177
+ const passedProps = {};
178
+ const children: ComponentNode['children'] = [];
179
+
180
+ const result = resolveComponentProps({
181
+ propDefs,
182
+ passedProps,
183
+ children
184
+ });
185
+
186
+ expect(result.children).toBe("Click me");
187
+ });
188
+
189
+ test("should handle props without defaults", () => {
190
+ const propDefs = {
191
+ customProp: {
192
+ type: "string"
193
+ // No default
194
+ }
195
+ };
196
+ const passedProps = { customProp: "value" };
197
+ const children: ComponentNode['children'] = [];
198
+
199
+ const result = resolveComponentProps({
200
+ propDefs,
201
+ passedProps,
202
+ children
203
+ });
204
+
205
+ expect(result.customProp).toBe("value");
206
+ });
207
+
208
+ test("should return undefined for props without defaults when not passed", () => {
209
+ const propDefs = {
210
+ customProp: {
211
+ type: "string"
212
+ // No default
213
+ }
214
+ };
215
+ const passedProps = {};
216
+ const children: ComponentNode['children'] = [];
217
+
218
+ const result = resolveComponentProps({
219
+ propDefs,
220
+ passedProps,
221
+ children
222
+ });
223
+
224
+ expect(result.customProp).toBeUndefined();
225
+ });
226
+ });
227
+
228
+ describe("Children handling", () => {
229
+ test("should use passed children when provided", () => {
230
+ const propDefs = {
231
+ children: {
232
+ type: "slot",
233
+ default: "Default text"
234
+ }
235
+ };
236
+ const passedProps = {};
237
+ const children: ComponentNode['children'] = ["Custom text"];
238
+
239
+ const result = resolveComponentProps({
240
+ propDefs,
241
+ passedProps,
242
+ children
243
+ });
244
+
245
+ expect(result.children).toEqual(["Custom text"]);
246
+ });
247
+
248
+ test("should use default children when no children provided", () => {
249
+ const propDefs = {
250
+ children: {
251
+ type: "slot",
252
+ default: "Default text"
253
+ }
254
+ };
255
+ const passedProps = {};
256
+ const children: ComponentNode['children'] = [];
257
+
258
+ const result = resolveComponentProps({
259
+ propDefs,
260
+ passedProps,
261
+ children
262
+ });
263
+
264
+ expect(result.children).toBe("Default text");
265
+ });
266
+
267
+ test("should use default children when children array is empty", () => {
268
+ const propDefs = {
269
+ children: {
270
+ type: "slot",
271
+ default: "Click me"
272
+ }
273
+ };
274
+ const passedProps = {};
275
+ const children: ComponentNode['children'] = [];
276
+
277
+ const result = resolveComponentProps({
278
+ propDefs,
279
+ passedProps,
280
+ children
281
+ });
282
+
283
+ expect(result.children).toBe("Click me");
284
+ });
285
+
286
+ test("should handle empty children array when no default", () => {
287
+ const propDefs = {
288
+ children: {
289
+ type: "slot"
290
+ // No default
291
+ }
292
+ };
293
+ const passedProps = {};
294
+ const children: ComponentNode['children'] = [];
295
+
296
+ const result = resolveComponentProps({
297
+ propDefs,
298
+ passedProps,
299
+ children
300
+ });
301
+
302
+ expect(result.children).toBeUndefined();
303
+ });
304
+
305
+ test("should handle children with multiple elements", () => {
306
+ const propDefs = {
307
+ children: {
308
+ type: "slot",
309
+ default: "Default"
310
+ }
311
+ };
312
+ const passedProps = {};
313
+ const children: ComponentNode['children'] = [
314
+ { type: "span", children: ["First"] },
315
+ { type: "span", children: ["Second"] }
316
+ ];
317
+
318
+ const result = resolveComponentProps({
319
+ propDefs,
320
+ passedProps,
321
+ children
322
+ });
323
+
324
+ expect(Array.isArray(result.children)).toBe(true);
325
+ expect((result.children as ComponentNode[]).length).toBe(2);
326
+ });
327
+
328
+ test("should handle string children", () => {
329
+ const propDefs = {
330
+ children: {
331
+ type: "slot",
332
+ default: "Default"
333
+ }
334
+ };
335
+ const passedProps = {};
336
+ const children: ComponentNode['children'] = "String child";
337
+
338
+ const result = resolveComponentProps({
339
+ propDefs,
340
+ passedProps,
341
+ children
342
+ });
343
+
344
+ // String children are not arrays, so default is used
345
+ // (Implementation only uses passed children if Array.isArray(children) && children.length > 0)
346
+ expect(result.children).toBe("Default");
347
+ });
348
+
349
+ test("should handle null children", () => {
350
+ const propDefs = {
351
+ children: {
352
+ type: "slot",
353
+ default: "Default"
354
+ }
355
+ };
356
+ const passedProps = {};
357
+ const children: ComponentNode['children'] = null;
358
+
359
+ const result = resolveComponentProps({
360
+ propDefs,
361
+ passedProps,
362
+ children
363
+ });
364
+
365
+ // null is not an array with length > 0, so should use default
366
+ expect(result.children).toBe("Default");
367
+ });
368
+ });
369
+
370
+ describe("Mixed scenarios", () => {
371
+ test("should resolve Button component props (from Button.json example)", () => {
372
+ const propDefs = {
373
+ variant: {
374
+ type: "select",
375
+ options: ["primary", "secondary"],
376
+ default: "primary"
377
+ },
378
+ children: {
379
+ type: "slot",
380
+ default: "Click me"
381
+ },
382
+ size: {
383
+ type: "select",
384
+ default: "medium",
385
+ options: ["medium", "large"]
386
+ }
387
+ };
388
+ const passedProps = { variant: "secondary" };
389
+ const children: ComponentNode['children'] = [];
390
+
391
+ const result = resolveComponentProps({
392
+ propDefs,
393
+ passedProps,
394
+ children
395
+ });
396
+
397
+ expect(result.variant).toBe("secondary"); // Overridden
398
+ expect(result.children).toBe("Click me"); // Default used
399
+ expect(result.size).toBe("medium"); // Default used
400
+ });
401
+
402
+ test("should resolve Button component with all props provided", () => {
403
+ const propDefs = {
404
+ variant: {
405
+ type: "select",
406
+ default: "primary"
407
+ },
408
+ children: {
409
+ type: "slot",
410
+ default: "Click me"
411
+ },
412
+ size: {
413
+ type: "select",
414
+ default: "medium"
415
+ }
416
+ };
417
+ const passedProps = { variant: "secondary", size: "large" };
418
+ const children: ComponentNode['children'] = ["Custom Button"];
419
+
420
+ const result = resolveComponentProps({
421
+ propDefs,
422
+ passedProps,
423
+ children
424
+ });
425
+
426
+ expect(result.variant).toBe("secondary");
427
+ expect(result.children).toEqual(["Custom Button"]);
428
+ expect(result.size).toBe("large");
429
+ });
430
+
431
+ test("should handle props with different types mixed", () => {
432
+ const propDefs = {
433
+ title: {
434
+ type: "string",
435
+ default: "Default Title"
436
+ },
437
+ count: {
438
+ type: "number",
439
+ default: 0
440
+ },
441
+ isActive: {
442
+ type: "boolean",
443
+ default: false
444
+ }
445
+ };
446
+ const passedProps = { count: 10 };
447
+ const children: ComponentNode['children'] = [];
448
+
449
+ const result = resolveComponentProps({
450
+ propDefs,
451
+ passedProps,
452
+ children
453
+ });
454
+
455
+ expect(result.title).toBe("Default Title");
456
+ expect(result.count).toBe(10);
457
+ expect(result.isActive).toBe(false);
458
+ });
459
+ });
460
+
461
+ describe("Edge cases", () => {
462
+ test("should handle empty propDefs object", () => {
463
+ const propDefs = {};
464
+ const passedProps = {};
465
+ const children: ComponentNode['children'] = [];
466
+
467
+ const result = resolveComponentProps({
468
+ propDefs,
469
+ passedProps,
470
+ children
471
+ });
472
+
473
+ expect(Object.keys(result).length).toBe(0);
474
+ });
475
+
476
+ test("should handle propDefs with invalid structure", () => {
477
+ const propDefs = {
478
+ invalid: null as any,
479
+ valid: {
480
+ type: "string",
481
+ default: "value"
482
+ }
483
+ };
484
+ const passedProps = {};
485
+ const children: ComponentNode['children'] = [];
486
+
487
+ const result = resolveComponentProps({
488
+ propDefs,
489
+ passedProps,
490
+ children
491
+ });
492
+
493
+ // Should skip invalid propDefs and only process valid ones
494
+ expect(result.valid).toBe("value");
495
+ expect(result.invalid).toBeUndefined();
496
+ });
497
+
498
+ test("should handle propDefs without 'default' key", () => {
499
+ const propDefs = {
500
+ noDefault: {
501
+ type: "string"
502
+ // No 'default' key
503
+ }
504
+ };
505
+ const passedProps = {};
506
+ const children: ComponentNode['children'] = [];
507
+
508
+ const result = resolveComponentProps({
509
+ propDefs,
510
+ passedProps,
511
+ children
512
+ });
513
+
514
+ expect(result.noDefault).toBeUndefined();
515
+ });
516
+
517
+ test("should preserve passed props that are not in propDefs", () => {
518
+ const propDefs = {
519
+ variant: {
520
+ type: "select",
521
+ default: "primary"
522
+ }
523
+ };
524
+ const passedProps = {
525
+ variant: "secondary",
526
+ extraProp: "extra value"
527
+ };
528
+ const children: ComponentNode['children'] = [];
529
+
530
+ const result = resolveComponentProps({
531
+ propDefs,
532
+ passedProps,
533
+ children
534
+ });
535
+
536
+ expect(result.variant).toBe("secondary");
537
+ // Extra props are now preserved for backward compatibility (with validation)
538
+ expect(result.extraProp).toBe("extra value");
539
+ });
540
+ });
541
+ });
542
+
543
+ describe("Prop Resolver - resolvePropsFromDefinition", () => {
544
+ describe("Basic usage", () => {
545
+ test("should resolve props from structured component definition", () => {
546
+ const componentDef: StructuredComponentDefinition = {
547
+ interface: {
548
+ variant: {
549
+ type: "select",
550
+ default: "primary"
551
+ }
552
+ }
553
+ };
554
+ const passedProps = {};
555
+ const children: ComponentNode['children'] = [];
556
+
557
+ const result = resolvePropsFromDefinition(componentDef, passedProps, children);
558
+
559
+ expect(result.variant).toBe("primary");
560
+ });
561
+
562
+ test("should handle missing interface", () => {
563
+ const componentDef: StructuredComponentDefinition = {
564
+ // No interface
565
+ };
566
+ const passedProps = {};
567
+ const children: ComponentNode['children'] = [];
568
+
569
+ const result = resolvePropsFromDefinition(componentDef, passedProps, children);
570
+
571
+ expect(Object.keys(result).length).toBe(0);
572
+ });
573
+
574
+ test("should handle empty interface", () => {
575
+ const componentDef: StructuredComponentDefinition = {
576
+ interface: {}
577
+ };
578
+ const passedProps = {};
579
+ const children: ComponentNode['children'] = [];
580
+
581
+ const result = resolvePropsFromDefinition(componentDef, passedProps, children);
582
+
583
+ expect(Object.keys(result).length).toBe(0);
584
+ });
585
+
586
+ test("should use passed props and children", () => {
587
+ const componentDef: StructuredComponentDefinition = {
588
+ interface: {
589
+ variant: {
590
+ type: "select",
591
+ default: "primary"
592
+ },
593
+ children: {
594
+ type: "slot",
595
+ default: "Default"
596
+ }
597
+ }
598
+ };
599
+ const passedProps = { variant: "secondary" };
600
+ const children: ComponentNode['children'] = ["Custom"];
601
+
602
+ const result = resolvePropsFromDefinition(componentDef, passedProps, children);
603
+
604
+ expect(result.variant).toBe("secondary");
605
+ expect(result.children).toEqual(["Custom"]);
606
+ });
607
+ });
608
+
609
+ describe("Integration with Button.json structure", () => {
610
+ test("should resolve Button component props correctly", () => {
611
+ const componentDef: StructuredComponentDefinition = {
612
+ interface: {
613
+ variant: {
614
+ type: "select",
615
+ options: ["primary", "secondary"],
616
+ default: "primary"
617
+ },
618
+ children: {
619
+ type: "slot",
620
+ default: "Click me"
621
+ },
622
+ size: {
623
+ type: "select",
624
+ default: "medium",
625
+ options: ["medium", "large"]
626
+ }
627
+ }
628
+ };
629
+ const passedProps = { variant: "secondary" };
630
+ const children: ComponentNode['children'] = [];
631
+
632
+ const result = resolvePropsFromDefinition(componentDef, passedProps, children);
633
+
634
+ expect(result.variant).toBe("secondary");
635
+ expect(result.children).toBe("Click me");
636
+ expect(result.size).toBe("medium");
637
+ });
638
+ });
639
+ });