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,252 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import {
3
+ CSS_PROPERTIES,
4
+ CSS_PROPERTIES_DEFINITION,
5
+ filterCSSProperties,
6
+ getPropertyValues,
7
+ filterPropertyValues,
8
+ getPropertyType
9
+ } from './cssProperties';
10
+
11
+ describe('cssProperties', () => {
12
+ describe('CSS_PROPERTIES', () => {
13
+ test('is an array of property names', () => {
14
+ expect(Array.isArray(CSS_PROPERTIES)).toBe(true);
15
+ expect(CSS_PROPERTIES.length).toBeGreaterThan(0);
16
+ });
17
+
18
+ test('contains common CSS properties', () => {
19
+ expect(CSS_PROPERTIES).toContain('display');
20
+ expect(CSS_PROPERTIES).toContain('flexDirection');
21
+ expect(CSS_PROPERTIES).toContain('padding');
22
+ expect(CSS_PROPERTIES).toContain('backgroundColor');
23
+ });
24
+ });
25
+
26
+ describe('CSS_PROPERTIES_DEFINITION', () => {
27
+ test('contains display property with values', () => {
28
+ expect(CSS_PROPERTIES_DEFINITION.display).toBeDefined();
29
+ expect(CSS_PROPERTIES_DEFINITION.display.values).toContain('flex');
30
+ expect(CSS_PROPERTIES_DEFINITION.display.type).toBe('select');
31
+ });
32
+
33
+ test('contains position property', () => {
34
+ expect(CSS_PROPERTIES_DEFINITION.position).toBeDefined();
35
+ expect(CSS_PROPERTIES_DEFINITION.position.values).toContain('relative');
36
+ });
37
+
38
+ test('contains flexbox properties', () => {
39
+ expect(CSS_PROPERTIES_DEFINITION.flexDirection).toBeDefined();
40
+ expect(CSS_PROPERTIES_DEFINITION.justifyContent).toBeDefined();
41
+ expect(CSS_PROPERTIES_DEFINITION.alignItems).toBeDefined();
42
+ });
43
+
44
+ test('contains dimension properties', () => {
45
+ expect(CSS_PROPERTIES_DEFINITION.width).toBeDefined();
46
+ expect(CSS_PROPERTIES_DEFINITION.height).toBeDefined();
47
+ expect(CSS_PROPERTIES_DEFINITION.width.type).toBe('string');
48
+ });
49
+
50
+ test('contains cursor property with values', () => {
51
+ expect(CSS_PROPERTIES_DEFINITION.cursor.values).toContain('pointer');
52
+ expect(CSS_PROPERTIES_DEFINITION.cursor.values).toContain('default');
53
+ });
54
+ });
55
+
56
+ describe('filterCSSProperties', () => {
57
+ test('returns first 15 properties for empty input', () => {
58
+ const result = filterCSSProperties('');
59
+ expect(result.length).toBe(15);
60
+ });
61
+
62
+ test('filters properties by exact prefix match', () => {
63
+ const result = filterCSSProperties('flex');
64
+ expect(result.every(prop => prop.toLowerCase().startsWith('flex'))).toBe(true);
65
+ });
66
+
67
+ test('filters properties case-insensitively', () => {
68
+ const result = filterCSSProperties('FLEX');
69
+ expect(result.length).toBeGreaterThan(0);
70
+ expect(result.every(prop => prop.toLowerCase().startsWith('flex'))).toBe(true);
71
+ });
72
+
73
+ test('supports camelCase abbreviation matching', () => {
74
+ const result = filterCSSProperties('bC');
75
+ expect(result).toContain('backgroundColor');
76
+ expect(result).toContain('borderColor');
77
+ });
78
+
79
+ test('prioritizes startsWith matches over abbreviation matches', () => {
80
+ const result = filterCSSProperties('b');
81
+ const startsWithB = result.filter(p => p.toLowerCase().startsWith('b'));
82
+ expect(startsWithB.length).toBeGreaterThan(0);
83
+ // startsWith matches should come first
84
+ expect(result.indexOf('border')).toBeLessThan(result.indexOf('backgroundColor') || Infinity);
85
+ });
86
+
87
+ test('returns empty array for non-matching input', () => {
88
+ const result = filterCSSProperties('zzzzz');
89
+ expect(result.length).toBe(0);
90
+ });
91
+
92
+ test('handles single character input', () => {
93
+ const result = filterCSSProperties('p');
94
+ expect(result.some(prop => prop.toLowerCase().startsWith('p'))).toBe(true);
95
+ });
96
+
97
+ test('trims whitespace from input', () => {
98
+ const result = filterCSSProperties(' flex ');
99
+ expect(result.length).toBeGreaterThan(0);
100
+ });
101
+
102
+ test('matches abbreviation with multiple capitals', () => {
103
+ const result = filterCSSProperties('fD');
104
+ expect(result).toContain('flexDirection');
105
+ });
106
+
107
+ test('matches abbreviation case-insensitively', () => {
108
+ const result = filterCSSProperties('fd');
109
+ expect(result).toContain('flexDirection');
110
+ });
111
+ });
112
+
113
+ describe('getPropertyValues', () => {
114
+ test('returns values for display property', () => {
115
+ const values = getPropertyValues('display');
116
+ expect(values).toContain('flex');
117
+ expect(values).toContain('grid');
118
+ expect(values).toContain('block');
119
+ });
120
+
121
+ test('returns values for position property', () => {
122
+ const values = getPropertyValues('position');
123
+ expect(values).toContain('relative');
124
+ expect(values).toContain('absolute');
125
+ expect(values).toContain('fixed');
126
+ });
127
+
128
+ test('returns empty array for property without predefined values', () => {
129
+ const values = getPropertyValues('width');
130
+ expect(values).toEqual([]);
131
+ });
132
+
133
+ test('returns empty array for non-existent property', () => {
134
+ const values = getPropertyValues('nonExistentProperty');
135
+ expect(values).toEqual([]);
136
+ });
137
+
138
+ test('returns values for flexDirection', () => {
139
+ const values = getPropertyValues('flexDirection');
140
+ expect(values).toContain('row');
141
+ expect(values).toContain('column');
142
+ });
143
+
144
+ test('returns values for cursor', () => {
145
+ const values = getPropertyValues('cursor');
146
+ expect(values).toContain('pointer');
147
+ expect(values).toContain('default');
148
+ });
149
+
150
+ test('returns array copy (not reference)', () => {
151
+ const values1 = getPropertyValues('display');
152
+ const values2 = getPropertyValues('display');
153
+ expect(values1).toEqual(values2);
154
+ expect(values1).not.toBe(values2);
155
+ });
156
+ });
157
+
158
+ describe('filterPropertyValues', () => {
159
+ test('returns all values when input is empty', () => {
160
+ const values = filterPropertyValues('display', '');
161
+ expect(values).toContain('flex');
162
+ expect(values).toContain('grid');
163
+ });
164
+
165
+ test('filters values by substring match', () => {
166
+ const values = filterPropertyValues('display', 'flex');
167
+ expect(values).toContain('flex');
168
+ expect(values).toContain('inline-flex');
169
+ expect(values).not.toContain('grid');
170
+ });
171
+
172
+ test('filters case-insensitively', () => {
173
+ const values = filterPropertyValues('display', 'FLEX');
174
+ expect(values).toContain('flex');
175
+ expect(values).toContain('inline-flex');
176
+ });
177
+
178
+ test('returns empty array for property without values', () => {
179
+ const values = filterPropertyValues('width', 'auto');
180
+ expect(values).toEqual([]);
181
+ });
182
+
183
+ test('returns empty array for non-matching input', () => {
184
+ const values = filterPropertyValues('display', 'zzzzz');
185
+ expect(values).toEqual([]);
186
+ });
187
+
188
+ test('handles whitespace in input', () => {
189
+ const values = filterPropertyValues('display', ' flex ');
190
+ expect(values.length).toBeGreaterThan(0);
191
+ });
192
+
193
+ test('filters position values', () => {
194
+ const values = filterPropertyValues('position', 'relative');
195
+ expect(values).toContain('relative');
196
+ expect(values).not.toContain('absolute');
197
+ });
198
+
199
+ test('filters flexDirection values', () => {
200
+ const values = filterPropertyValues('flexDirection', 'row');
201
+ expect(values).toContain('row');
202
+ expect(values).toContain('row-reverse');
203
+ expect(values).not.toContain('column');
204
+ });
205
+
206
+ test('handles partial matches', () => {
207
+ const values = filterPropertyValues('justifyContent', 'space');
208
+ expect(values).toContain('space-between');
209
+ expect(values).toContain('space-around');
210
+ expect(values).toContain('space-evenly');
211
+ });
212
+
213
+ test('returns empty for non-existent property', () => {
214
+ const values = filterPropertyValues('nonExistent', 'test');
215
+ expect(values).toEqual([]);
216
+ });
217
+ });
218
+
219
+ describe('getPropertyType', () => {
220
+ test('returns select type for display', () => {
221
+ expect(getPropertyType('display')).toBe('select');
222
+ });
223
+
224
+ test('returns string type for width', () => {
225
+ expect(getPropertyType('width')).toBe('string');
226
+ });
227
+
228
+ test('returns number type for zIndex', () => {
229
+ expect(getPropertyType('zIndex')).toBe('number');
230
+ });
231
+
232
+ test('returns number type for opacity', () => {
233
+ expect(getPropertyType('opacity')).toBe('number');
234
+ });
235
+
236
+ test('returns select type for cursor', () => {
237
+ expect(getPropertyType('cursor')).toBe('select');
238
+ });
239
+
240
+ test('returns undefined for non-existent property', () => {
241
+ expect(getPropertyType('nonExistent')).toBeUndefined();
242
+ });
243
+
244
+ test('returns select type for flexDirection', () => {
245
+ expect(getPropertyType('flexDirection')).toBe('select');
246
+ });
247
+
248
+ test('returns string type for padding', () => {
249
+ expect(getPropertyType('padding')).toBe('string');
250
+ });
251
+ });
252
+ });
@@ -0,0 +1,338 @@
1
+ /**
2
+ * CSS Property Definition structure
3
+ */
4
+ interface CSSPropertyDefinition {
5
+ values?: readonly string[];
6
+ type?: 'string' | 'select' | 'boolean' | 'number';
7
+ }
8
+
9
+ /**
10
+ * CSS Properties with their valid values and metadata
11
+ * Used for autocomplete suggestions for both property names and values
12
+ */
13
+ export const CSS_PROPERTIES_DEFINITION: Record<string, CSSPropertyDefinition> = {
14
+ // Display & Layout
15
+ display: {
16
+ values: ['block', 'flex', 'grid', 'inline', 'inline-block', 'inline-flex', 'inline-grid', 'none'],
17
+ type: 'select',
18
+ },
19
+ position: {
20
+ values: ['static', 'relative', 'absolute', 'fixed', 'sticky'],
21
+ type: 'select',
22
+ },
23
+ top: { type: 'string' },
24
+ right: { type: 'string' },
25
+ bottom: { type: 'string' },
26
+ left: { type: 'string' },
27
+ zIndex: { type: 'number' },
28
+
29
+ // Dimensions
30
+ width: { type: 'string' },
31
+ height: { type: 'string' },
32
+ minWidth: { type: 'string' },
33
+ maxWidth: { type: 'string' },
34
+ minHeight: { type: 'string' },
35
+ maxHeight: { type: 'string' },
36
+
37
+ // Spacing
38
+ margin: { type: 'string' },
39
+ marginTop: { type: 'string' },
40
+ marginRight: { type: 'string' },
41
+ marginBottom: { type: 'string' },
42
+ marginLeft: { type: 'string' },
43
+ padding: { type: 'string' },
44
+ paddingTop: { type: 'string' },
45
+ paddingRight: { type: 'string' },
46
+ paddingBottom: { type: 'string' },
47
+ paddingLeft: { type: 'string' },
48
+ gap: { type: 'string' },
49
+ rowGap: { type: 'string' },
50
+ columnGap: { type: 'string' },
51
+
52
+ // Borders
53
+ border: { type: 'string' },
54
+ borderWidth: { type: 'string' },
55
+ borderStyle: {
56
+ values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'none'],
57
+ type: 'select',
58
+ },
59
+ borderColor: { type: 'string' },
60
+ borderTop: { type: 'string' },
61
+ borderRight: { type: 'string' },
62
+ borderBottom: { type: 'string' },
63
+ borderLeft: { type: 'string' },
64
+ borderRadius: { type: 'string' },
65
+ borderTopLeftRadius: { type: 'string' },
66
+ borderTopRightRadius: { type: 'string' },
67
+ borderBottomLeftRadius: { type: 'string' },
68
+ borderBottomRightRadius: { type: 'string' },
69
+
70
+ // Background & Colors
71
+ background: { type: 'string' },
72
+ backgroundColor: { type: 'string' },
73
+ backgroundImage: { type: 'string' },
74
+ backgroundSize: {
75
+ values: ['auto', 'cover', 'contain'],
76
+ type: 'select',
77
+ },
78
+ backgroundPosition: {
79
+ values: ['top', 'bottom', 'left', 'right', 'center'],
80
+ type: 'select',
81
+ },
82
+ backgroundRepeat: {
83
+ values: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
84
+ type: 'select',
85
+ },
86
+ color: { type: 'string' },
87
+ opacity: { type: 'number' },
88
+
89
+ // Flexbox
90
+ flex: { type: 'string' },
91
+ flexDirection: {
92
+ values: ['row', 'column', 'row-reverse', 'column-reverse'],
93
+ type: 'select',
94
+ },
95
+ flexWrap: {
96
+ values: ['nowrap', 'wrap', 'wrap-reverse'],
97
+ type: 'select',
98
+ },
99
+ flexFlow: { type: 'string' },
100
+ justifyContent: {
101
+ values: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'],
102
+ type: 'select',
103
+ },
104
+ alignItems: {
105
+ values: ['flex-start', 'flex-end', 'center', 'stretch', 'baseline'],
106
+ type: 'select',
107
+ },
108
+ alignContent: {
109
+ values: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'],
110
+ type: 'select',
111
+ },
112
+ flexGrow: { type: 'number' },
113
+ flexShrink: { type: 'number' },
114
+ flexBasis: { type: 'string' },
115
+ order: { type: 'number' },
116
+
117
+ // Grid
118
+ grid: { type: 'string' },
119
+ gridTemplateColumns: { type: 'string' },
120
+ gridTemplateRows: { type: 'string' },
121
+ gridGap: { type: 'string' },
122
+ gridColumn: { type: 'string' },
123
+ gridRow: { type: 'string' },
124
+ gridAutoFlow: {
125
+ values: ['row', 'column', 'row dense', 'column dense'],
126
+ type: 'select',
127
+ },
128
+
129
+ // Text & Font
130
+ fontSize: { type: 'string' },
131
+ fontWeight: {
132
+ values: ['100', '200', '300', '400', '500', '600', '700', '800', '900', 'normal', 'bold'],
133
+ type: 'select',
134
+ },
135
+ fontFamily: { type: 'string' },
136
+ fontStyle: {
137
+ values: ['normal', 'italic', 'oblique'],
138
+ type: 'select',
139
+ },
140
+ lineHeight: { type: 'string' },
141
+ textAlign: {
142
+ values: ['left', 'right', 'center', 'justify', 'start', 'end'],
143
+ type: 'select',
144
+ },
145
+ textDecoration: {
146
+ values: ['none', 'underline', 'overline', 'line-through'],
147
+ type: 'select',
148
+ },
149
+ textTransform: {
150
+ values: ['none', 'capitalize', 'uppercase', 'lowercase'],
151
+ type: 'select',
152
+ },
153
+ letterSpacing: { type: 'string' },
154
+ wordSpacing: { type: 'string' },
155
+
156
+ // Box Shadow & Effects
157
+ boxShadow: { type: 'string' },
158
+ textShadow: { type: 'string' },
159
+ filter: { type: 'string' },
160
+ transform: { type: 'string' },
161
+ transition: { type: 'string' },
162
+ animation: { type: 'string' },
163
+
164
+ // Overflow & Content
165
+ overflow: {
166
+ values: ['visible', 'hidden', 'scroll', 'auto'],
167
+ type: 'select',
168
+ },
169
+ overflowX: {
170
+ values: ['visible', 'hidden', 'scroll', 'auto'],
171
+ type: 'select',
172
+ },
173
+ overflowY: {
174
+ values: ['visible', 'hidden', 'scroll', 'auto'],
175
+ type: 'select',
176
+ },
177
+ whiteSpace: {
178
+ values: ['normal', 'nowrap', 'pre', 'pre-wrap', 'pre-line'],
179
+ type: 'select',
180
+ },
181
+ textOverflow: {
182
+ values: ['clip', 'ellipsis'],
183
+ type: 'select',
184
+ },
185
+ visibility: {
186
+ values: ['visible', 'hidden', 'collapse'],
187
+ type: 'select',
188
+ },
189
+ content: { type: 'string' },
190
+
191
+ // Cursor & Interaction
192
+ cursor: {
193
+ values: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'not-allowed', 'help'],
194
+ type: 'select',
195
+ },
196
+ pointerEvents: {
197
+ values: ['auto', 'none'],
198
+ type: 'select',
199
+ },
200
+ userSelect: {
201
+ values: ['auto', 'none', 'text', 'all'],
202
+ type: 'select',
203
+ },
204
+
205
+ // Miscellaneous
206
+ float: {
207
+ values: ['left', 'right', 'none'],
208
+ type: 'select',
209
+ },
210
+ clear: {
211
+ values: ['left', 'right', 'both', 'none'],
212
+ type: 'select',
213
+ },
214
+ boxSizing: {
215
+ values: ['content-box', 'border-box'],
216
+ type: 'select',
217
+ },
218
+ objectFit: {
219
+ values: ['fill', 'contain', 'cover', 'scale-down'],
220
+ type: 'select',
221
+ },
222
+ objectPosition: {
223
+ values: ['top', 'bottom', 'left', 'right', 'center'],
224
+ type: 'select',
225
+ },
226
+ };
227
+
228
+ /**
229
+ * Common CSS properties for autocomplete suggestions (camelCase for React/JS)
230
+ * Derived from CSS_PROPERTIES_DEFINITION keys
231
+ */
232
+ export const CSS_PROPERTIES = Object.keys(CSS_PROPERTIES_DEFINITION);
233
+
234
+ /**
235
+ * Check if property matches the abbreviation pattern
236
+ * For example, "bC" matches "backgroundColor" (b→b, C→C capital letter)
237
+ */
238
+ function matchesAbbreviation(property: string, input: string): boolean {
239
+ if (!input) return false;
240
+
241
+ let propertyIndex = 0;
242
+
243
+ for (let inputIndex = 0; inputIndex < input.length; inputIndex++) {
244
+ const inputChar = input[inputIndex];
245
+ let found = false;
246
+
247
+ // Search from current position to end of property for a match at a word boundary
248
+ while (propertyIndex < property.length) {
249
+ const propChar = property[propertyIndex];
250
+ // Word boundary = first character OR capital letter
251
+ const isWordBoundary = propertyIndex === 0 ||
252
+ (propChar === propChar.toUpperCase() && propChar !== propChar.toLowerCase());
253
+
254
+ if (isWordBoundary && inputChar.toLowerCase() === propChar.toLowerCase()) {
255
+ // Match found at word boundary
256
+ found = true;
257
+ propertyIndex++; // Move past this character for next search
258
+ break;
259
+ }
260
+
261
+ propertyIndex++;
262
+ }
263
+
264
+ if (!found) {
265
+ return false; // Couldn't find this input character at a word boundary
266
+ }
267
+ }
268
+
269
+ return true;
270
+ }
271
+
272
+ /**
273
+ * Filter CSS properties based on input value
274
+ * Supports both startsWith matching and camelCase abbreviation matching
275
+ */
276
+ export function filterCSSProperties(input: string): string[] {
277
+ const normalizedInput = input.trim();
278
+ if (!normalizedInput) {
279
+ return CSS_PROPERTIES.slice(0, 15); // Show first 15 by default
280
+ }
281
+
282
+ // First, try startsWith matching (exact prefix match)
283
+ const startsWithMatches = CSS_PROPERTIES.filter(prop =>
284
+ prop.toLowerCase().startsWith(normalizedInput.toLowerCase())
285
+ );
286
+
287
+ // Then, try abbreviation/fuzzy matching
288
+ const abbreviationMatches = CSS_PROPERTIES.filter(prop =>
289
+ matchesAbbreviation(prop, normalizedInput) &&
290
+ !startsWithMatches.includes(prop) // Don't duplicate startsWith matches
291
+ );
292
+
293
+ // Combine results: exact matches first, then fuzzy matches
294
+ return [...startsWithMatches, ...abbreviationMatches];
295
+ }
296
+
297
+ /**
298
+ * Get available values for a specific CSS property
299
+ * @param propertyName - The CSS property name (e.g., 'flexDirection')
300
+ * @returns Array of valid values for the property, or empty array if property has no predefined values
301
+ */
302
+ export function getPropertyValues(propertyName: string): string[] {
303
+ const values = CSS_PROPERTIES_DEFINITION[propertyName]?.values;
304
+ return values ? Array.from(values) : [];
305
+ }
306
+
307
+ /**
308
+ * Filter values for a specific CSS property based on input
309
+ * Supports substring matching and returns values that contain the input
310
+ * @param propertyName - The CSS property name (e.g., 'flexDirection')
311
+ * @param input - The user input to filter values by
312
+ * @returns Filtered array of valid values for the property
313
+ */
314
+ export function filterPropertyValues(propertyName: string, input: string): string[] {
315
+ const values = getPropertyValues(propertyName);
316
+ if (!values.length) {
317
+ return []; // No predefined values for this property
318
+ }
319
+
320
+ const normalizedInput = input.trim().toLowerCase();
321
+ if (!normalizedInput) {
322
+ return values; // Return all values if no input
323
+ }
324
+
325
+ // Filter values that contain the input (substring match)
326
+ return values.filter(value =>
327
+ value.toLowerCase().includes(normalizedInput)
328
+ );
329
+ }
330
+
331
+ /**
332
+ * Get the property type for a CSS property
333
+ * @param propertyName - The CSS property name
334
+ * @returns The property type ('string' | 'select' | 'boolean' | 'number') or undefined
335
+ */
336
+ export function getPropertyType(propertyName: string): 'string' | 'select' | 'boolean' | 'number' | undefined {
337
+ return CSS_PROPERTIES_DEFINITION[propertyName]?.type;
338
+ }