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.
- package/bin/cli.ts +281 -0
- package/build-static.ts +298 -0
- package/bunfig.toml +39 -0
- package/entries/client-router.tsx +111 -0
- package/entries/server-router.tsx +71 -0
- package/lib/client/ClientInitializer.test.ts +9 -0
- package/lib/client/ClientInitializer.test.ts.skip +92 -0
- package/lib/client/ClientInitializer.ts +60 -0
- package/lib/client/ErrorBoundary.test.tsx +595 -0
- package/lib/client/ErrorBoundary.tsx +230 -0
- package/lib/client/componentRegistry.test.ts +165 -0
- package/lib/client/componentRegistry.ts +18 -0
- package/lib/client/contexts/ThemeContext.tsx +73 -0
- package/lib/client/core/ComponentBuilder.test.ts +677 -0
- package/lib/client/core/ComponentBuilder.ts +660 -0
- package/lib/client/core/ComponentRenderer.test.tsx +176 -0
- package/lib/client/core/ComponentRenderer.tsx +83 -0
- package/lib/client/core/cmsTemplateProcessor.ts +129 -0
- package/lib/client/elementRegistry.ts +81 -0
- package/lib/client/hmr/HMRManager.tsx +179 -0
- package/lib/client/hmr/index.ts +5 -0
- package/lib/client/hmrWebSocket.test.ts +9 -0
- package/lib/client/hmrWebSocket.ts +250 -0
- package/lib/client/hooks/useColorVariables.test.ts +166 -0
- package/lib/client/hooks/useColorVariables.ts +249 -0
- package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
- package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
- package/lib/client/hydration/HydrationUtils.test.ts +154 -0
- package/lib/client/hydration/HydrationUtils.ts +35 -0
- package/lib/client/i18nConfigService.test.ts +74 -0
- package/lib/client/i18nConfigService.ts +78 -0
- package/lib/client/index.ts +56 -0
- package/lib/client/navigation.test.ts +441 -0
- package/lib/client/navigation.ts +23 -0
- package/lib/client/responsiveStyleResolver.test.ts +491 -0
- package/lib/client/responsiveStyleResolver.ts +184 -0
- package/lib/client/routing/RouteLoader.test.ts +635 -0
- package/lib/client/routing/RouteLoader.ts +347 -0
- package/lib/client/routing/Router.tsx +382 -0
- package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
- package/lib/client/scripts/ScriptExecutor.ts +171 -0
- package/lib/client/scripts/formHandler.ts +103 -0
- package/lib/client/styleProcessor.test.ts +126 -0
- package/lib/client/styleProcessor.ts +92 -0
- package/lib/client/styles/StyleInjector.test.ts +354 -0
- package/lib/client/styles/StyleInjector.ts +154 -0
- package/lib/client/templateEngine.test.ts +660 -0
- package/lib/client/templateEngine.ts +667 -0
- package/lib/client/theme.test.ts +173 -0
- package/lib/client/theme.ts +159 -0
- package/lib/client/utils/toast.ts +46 -0
- package/lib/server/createServer.ts +170 -0
- package/lib/server/cssGenerator.test.ts +172 -0
- package/lib/server/cssGenerator.ts +58 -0
- package/lib/server/fileWatcher.ts +134 -0
- package/lib/server/index.ts +55 -0
- package/lib/server/jsonLoader.test.ts +103 -0
- package/lib/server/jsonLoader.ts +350 -0
- package/lib/server/middleware/cors.test.ts +177 -0
- package/lib/server/middleware/cors.ts +69 -0
- package/lib/server/middleware/errorHandler.test.ts +208 -0
- package/lib/server/middleware/errorHandler.ts +63 -0
- package/lib/server/middleware/index.ts +9 -0
- package/lib/server/middleware/logger.test.ts +233 -0
- package/lib/server/middleware/logger.ts +99 -0
- package/lib/server/pageCache.test.ts +167 -0
- package/lib/server/pageCache.ts +97 -0
- package/lib/server/projectContext.ts +51 -0
- package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
- package/lib/server/providers/fileSystemPageProvider.ts +83 -0
- package/lib/server/routes/api/cms.test.ts +177 -0
- package/lib/server/routes/api/cms.ts +82 -0
- package/lib/server/routes/api/colors.ts +59 -0
- package/lib/server/routes/api/components.ts +70 -0
- package/lib/server/routes/api/config.test.ts +9 -0
- package/lib/server/routes/api/config.ts +28 -0
- package/lib/server/routes/api/core-routes.ts +182 -0
- package/lib/server/routes/api/functions.ts +170 -0
- package/lib/server/routes/api/index.ts +69 -0
- package/lib/server/routes/api/pages.ts +95 -0
- package/lib/server/routes/api/shared.test.ts +81 -0
- package/lib/server/routes/api/shared.ts +31 -0
- package/lib/server/routes/editor.test.ts +9 -0
- package/lib/server/routes/index.ts +104 -0
- package/lib/server/routes/pages.ts +161 -0
- package/lib/server/routes/static.ts +107 -0
- package/lib/server/services/ColorService.ts +193 -0
- package/lib/server/services/cmsService.test.ts +388 -0
- package/lib/server/services/cmsService.ts +296 -0
- package/lib/server/services/componentService.test.ts +276 -0
- package/lib/server/services/componentService.ts +346 -0
- package/lib/server/services/configService.ts +156 -0
- package/lib/server/services/fileWatcherService.ts +67 -0
- package/lib/server/services/index.ts +10 -0
- package/lib/server/services/pageService.test.ts +258 -0
- package/lib/server/services/pageService.ts +240 -0
- package/lib/server/ssrRenderer.test.ts +1005 -0
- package/lib/server/ssrRenderer.ts +878 -0
- package/lib/server/utilityClassGenerator.ts +11 -0
- package/lib/server/utils/index.ts +5 -0
- package/lib/server/utils/jsonLineMapper.test.ts +100 -0
- package/lib/server/utils/jsonLineMapper.ts +166 -0
- package/lib/server/validateStyleCoverage.test.ts +9 -0
- package/lib/server/validateStyleCoverage.ts +167 -0
- package/lib/server/websocketManager.test.ts +9 -0
- package/lib/server/websocketManager.ts +95 -0
- package/lib/shared/attributeNodeUtils.test.ts +152 -0
- package/lib/shared/attributeNodeUtils.ts +50 -0
- package/lib/shared/breakpoints.test.ts +166 -0
- package/lib/shared/breakpoints.ts +65 -0
- package/lib/shared/colorProperties.test.ts +111 -0
- package/lib/shared/colorProperties.ts +40 -0
- package/lib/shared/colorVariableUtils.test.ts +319 -0
- package/lib/shared/colorVariableUtils.ts +97 -0
- package/lib/shared/constants.test.ts +175 -0
- package/lib/shared/constants.ts +116 -0
- package/lib/shared/cssGeneration.ts +481 -0
- package/lib/shared/cssProperties.test.ts +252 -0
- package/lib/shared/cssProperties.ts +338 -0
- package/lib/shared/elementUtils.test.ts +245 -0
- package/lib/shared/elementUtils.ts +90 -0
- package/lib/shared/fontLoader.ts +97 -0
- package/lib/shared/i18n.test.ts +313 -0
- package/lib/shared/i18n.ts +286 -0
- package/lib/shared/index.ts +50 -0
- package/lib/shared/interfaces/contentProvider.test.ts +9 -0
- package/lib/shared/interfaces/contentProvider.ts +121 -0
- package/lib/shared/nodeUtils.test.ts +320 -0
- package/lib/shared/nodeUtils.ts +220 -0
- package/lib/shared/pathArrayUtils.test.ts +315 -0
- package/lib/shared/pathArrayUtils.ts +17 -0
- package/lib/shared/pathUtils.test.ts +260 -0
- package/lib/shared/pathUtils.ts +244 -0
- package/lib/shared/paths/Path.test.ts +74 -0
- package/lib/shared/paths/Path.ts +23 -0
- package/lib/shared/paths/PathConverter.test.ts +232 -0
- package/lib/shared/paths/PathConverter.ts +141 -0
- package/lib/shared/paths/PathUtils.ts +290 -0
- package/lib/shared/paths/PathValidator.test.ts +193 -0
- package/lib/shared/paths/PathValidator.ts +53 -0
- package/lib/shared/paths/index.ts +48 -0
- package/lib/shared/propResolver.test.ts +639 -0
- package/lib/shared/propResolver.ts +124 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
- package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
- package/lib/shared/registry/ClientRegistry.test.ts +26 -0
- package/lib/shared/registry/ClientRegistry.ts +15 -0
- package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
- package/lib/shared/registry/ComponentRegistry.ts +100 -0
- package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
- package/lib/shared/registry/NodeTypeManager.ts +94 -0
- package/lib/shared/registry/RegistryManager.test.ts +58 -0
- package/lib/shared/registry/RegistryManager.ts +60 -0
- package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
- package/lib/shared/registry/SSRRegistry.test.ts +26 -0
- package/lib/shared/registry/SSRRegistry.ts +15 -0
- package/lib/shared/registry/createNodeType.ts +175 -0
- package/lib/shared/registry/defineNodeType.ts +73 -0
- package/lib/shared/registry/fieldPresets.ts +109 -0
- package/lib/shared/registry/index.ts +50 -0
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
- package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
- package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
- package/lib/shared/registry/nodeTypes/index.ts +75 -0
- package/lib/shared/responsiveScaling.test.ts +268 -0
- package/lib/shared/responsiveScaling.ts +194 -0
- package/lib/shared/responsiveStyleUtils.test.ts +300 -0
- package/lib/shared/responsiveStyleUtils.ts +139 -0
- package/lib/shared/slugTranslator.test.ts +325 -0
- package/lib/shared/slugTranslator.ts +177 -0
- package/lib/shared/styleNodeUtils.test.ts +132 -0
- package/lib/shared/styleNodeUtils.ts +102 -0
- package/lib/shared/styleUtils.test.ts +238 -0
- package/lib/shared/styleUtils.ts +63 -0
- package/lib/shared/themeDefaults.test.ts +113 -0
- package/lib/shared/themeDefaults.ts +103 -0
- package/lib/shared/tree/PathBuilder.ts +383 -0
- package/lib/shared/treePathUtils.test.ts +539 -0
- package/lib/shared/treePathUtils.ts +339 -0
- package/lib/shared/types/api.ts +58 -0
- package/lib/shared/types/cms.ts +95 -0
- package/lib/shared/types/colors.ts +45 -0
- package/lib/shared/types/components.ts +121 -0
- package/lib/shared/types/errors.test.ts +103 -0
- package/lib/shared/types/errors.ts +69 -0
- package/lib/shared/types/index.ts +96 -0
- package/lib/shared/types/nodes.ts +20 -0
- package/lib/shared/types/rendering.ts +61 -0
- package/lib/shared/types/styles.ts +38 -0
- package/lib/shared/types.ts +11 -0
- package/lib/shared/utilityClassConfig.ts +287 -0
- package/lib/shared/utilityClassMapper.test.ts +140 -0
- package/lib/shared/utilityClassMapper.ts +229 -0
- package/lib/shared/utils/fileUtils.test.ts +99 -0
- package/lib/shared/utils/fileUtils.ts +56 -0
- package/lib/shared/utils.test.ts +261 -0
- package/lib/shared/utils.ts +84 -0
- package/lib/shared/validation/index.ts +7 -0
- package/lib/shared/validation/propValidator.test.ts +178 -0
- package/lib/shared/validation/propValidator.ts +238 -0
- package/lib/shared/validation/schemas.test.ts +177 -0
- package/lib/shared/validation/schemas.ts +401 -0
- package/lib/shared/validation/validators.test.ts +109 -0
- package/lib/shared/validation/validators.ts +304 -0
- package/lib/test-utils/dom-setup.ts +55 -0
- package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
- package/lib/test-utils/factories/DomMockFactory.ts +487 -0
- package/lib/test-utils/factories/EventMockFactory.ts +244 -0
- package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
- package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
- package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
- package/lib/test-utils/factories/index.ts +11 -0
- package/lib/test-utils/fixtures.ts +134 -0
- package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
- package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
- package/lib/test-utils/helpers/index.ts +6 -0
- package/lib/test-utils/helpers.test.ts +73 -0
- package/lib/test-utils/helpers.ts +90 -0
- package/lib/test-utils/index.ts +17 -0
- package/lib/test-utils/mockFactories.ts +92 -0
- package/lib/test-utils/mocks.ts +341 -0
- package/package.json +38 -0
- package/templates/index-router.html +34 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
isColorVariable,
|
|
4
|
+
extractVariableName,
|
|
5
|
+
createVariableReference,
|
|
6
|
+
resolveColorVariable,
|
|
7
|
+
resolveColorVariablesInStyles,
|
|
8
|
+
getColorVariablesFromStyles,
|
|
9
|
+
} from './colorVariableUtils';
|
|
10
|
+
|
|
11
|
+
describe('colorVariableUtils', () => {
|
|
12
|
+
describe('isColorVariable', () => {
|
|
13
|
+
test('should return true for valid color variable references', () => {
|
|
14
|
+
expect(isColorVariable('var(--primary-color)')).toBe(true);
|
|
15
|
+
expect(isColorVariable('var(--background)')).toBe(true);
|
|
16
|
+
expect(isColorVariable('var(--text-dark)')).toBe(true);
|
|
17
|
+
expect(isColorVariable('var(--btn-bg)')).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should handle variables with numbers', () => {
|
|
21
|
+
expect(isColorVariable('var(--color-1)')).toBe(true);
|
|
22
|
+
expect(isColorVariable('var(--bg-2-main)')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('should trim whitespace', () => {
|
|
26
|
+
expect(isColorVariable(' var(--primary) ')).toBe(true);
|
|
27
|
+
expect(isColorVariable('\tvar(--color)\t')).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should return false for invalid references', () => {
|
|
31
|
+
expect(isColorVariable('var(primary-color)')).toBe(false);
|
|
32
|
+
expect(isColorVariable('var(-primary)')).toBe(false);
|
|
33
|
+
expect(isColorVariable('var(--)')).toBe(false);
|
|
34
|
+
expect(isColorVariable('var(--primary-color')).toBe(false);
|
|
35
|
+
expect(isColorVariable('var--primary-color)')).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('should return false for non-string values', () => {
|
|
39
|
+
expect(isColorVariable(123)).toBe(false);
|
|
40
|
+
expect(isColorVariable(null)).toBe(false);
|
|
41
|
+
expect(isColorVariable(undefined)).toBe(false);
|
|
42
|
+
expect(isColorVariable({})).toBe(false);
|
|
43
|
+
expect(isColorVariable([])).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('should return false for empty string', () => {
|
|
47
|
+
expect(isColorVariable('')).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('should return false for plain color values', () => {
|
|
51
|
+
expect(isColorVariable('#ff0000')).toBe(false);
|
|
52
|
+
expect(isColorVariable('rgb(255, 0, 0)')).toBe(false);
|
|
53
|
+
expect(isColorVariable('red')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('extractVariableName', () => {
|
|
58
|
+
test('should extract variable name from valid reference', () => {
|
|
59
|
+
expect(extractVariableName('var(--primary-color)')).toBe('primary-color');
|
|
60
|
+
expect(extractVariableName('var(--background)')).toBe('background');
|
|
61
|
+
expect(extractVariableName('var(--text-dark)')).toBe('text-dark');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should handle variables with numbers', () => {
|
|
65
|
+
expect(extractVariableName('var(--color-1)')).toBe('color-1');
|
|
66
|
+
expect(extractVariableName('var(--bg-2-main)')).toBe('bg-2-main');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should return null for invalid references', () => {
|
|
70
|
+
expect(extractVariableName('var(primary-color)')).toBeNull();
|
|
71
|
+
expect(extractVariableName('primary-color')).toBeNull();
|
|
72
|
+
expect(extractVariableName('#ff0000')).toBeNull();
|
|
73
|
+
expect(extractVariableName('')).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should return null for malformed var syntax', () => {
|
|
77
|
+
expect(extractVariableName('var(--primary')).toBeNull();
|
|
78
|
+
expect(extractVariableName('var--primary)')).toBeNull();
|
|
79
|
+
expect(extractVariableName('var(--)')).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('createVariableReference', () => {
|
|
84
|
+
test('should create valid variable reference', () => {
|
|
85
|
+
expect(createVariableReference('primary-color')).toBe('var(--primary-color)');
|
|
86
|
+
expect(createVariableReference('background')).toBe('var(--background)');
|
|
87
|
+
expect(createVariableReference('text-dark')).toBe('var(--text-dark)');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('should handle variables with numbers', () => {
|
|
91
|
+
expect(createVariableReference('color-1')).toBe('var(--color-1)');
|
|
92
|
+
expect(createVariableReference('bg-2-main')).toBe('var(--bg-2-main)');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should handle single word names', () => {
|
|
96
|
+
expect(createVariableReference('primary')).toBe('var(--primary)');
|
|
97
|
+
expect(createVariableReference('bg')).toBe('var(--bg)');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should handle empty string', () => {
|
|
101
|
+
expect(createVariableReference('')).toBe('var(--)');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('resolveColorVariable', () => {
|
|
106
|
+
const colorMap = {
|
|
107
|
+
'primary-color': '#3b82f6',
|
|
108
|
+
'background': '#ffffff',
|
|
109
|
+
'text-dark': '#1f2937',
|
|
110
|
+
'accent': 'rgb(239, 68, 68)',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
test('should resolve valid variable to color value', () => {
|
|
114
|
+
expect(resolveColorVariable('var(--primary-color)', colorMap)).toBe('#3b82f6');
|
|
115
|
+
expect(resolveColorVariable('var(--background)', colorMap)).toBe('#ffffff');
|
|
116
|
+
expect(resolveColorVariable('var(--text-dark)', colorMap)).toBe('#1f2937');
|
|
117
|
+
expect(resolveColorVariable('var(--accent)', colorMap)).toBe('rgb(239, 68, 68)');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should return original value for non-variables', () => {
|
|
121
|
+
expect(resolveColorVariable('#ff0000', colorMap)).toBe('#ff0000');
|
|
122
|
+
expect(resolveColorVariable('red', colorMap)).toBe('red');
|
|
123
|
+
expect(resolveColorVariable('rgb(0, 0, 0)', colorMap)).toBe('rgb(0, 0, 0)');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('should return original reference for undefined variable', () => {
|
|
127
|
+
expect(resolveColorVariable('var(--unknown)', colorMap)).toBe('var(--unknown)');
|
|
128
|
+
expect(resolveColorVariable('var(--missing)', colorMap)).toBe('var(--missing)');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should handle non-string values', () => {
|
|
132
|
+
expect(resolveColorVariable(123, colorMap)).toBe('123');
|
|
133
|
+
expect(resolveColorVariable(null, colorMap)).toBe('null');
|
|
134
|
+
expect(resolveColorVariable(undefined, colorMap)).toBe('undefined');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('should handle empty color map', () => {
|
|
138
|
+
expect(resolveColorVariable('var(--primary)', {})).toBe('var(--primary)');
|
|
139
|
+
expect(resolveColorVariable('#ff0000', {})).toBe('#ff0000');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('resolveColorVariablesInStyles', () => {
|
|
144
|
+
const colorMap = {
|
|
145
|
+
'primary': '#3b82f6',
|
|
146
|
+
'background': '#ffffff',
|
|
147
|
+
'text': '#1f2937',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
test('should resolve all color variables in styles', () => {
|
|
151
|
+
const styles = {
|
|
152
|
+
color: 'var(--text)',
|
|
153
|
+
backgroundColor: 'var(--background)',
|
|
154
|
+
borderColor: 'var(--primary)',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const resolved = resolveColorVariablesInStyles(styles, colorMap);
|
|
158
|
+
|
|
159
|
+
expect(resolved).toEqual({
|
|
160
|
+
color: '#1f2937',
|
|
161
|
+
backgroundColor: '#ffffff',
|
|
162
|
+
borderColor: '#3b82f6',
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('should preserve non-variable values', () => {
|
|
167
|
+
const styles = {
|
|
168
|
+
color: 'var(--text)',
|
|
169
|
+
backgroundColor: '#ffffff',
|
|
170
|
+
borderColor: 'red',
|
|
171
|
+
fontSize: '16px',
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const resolved = resolveColorVariablesInStyles(styles, colorMap);
|
|
175
|
+
|
|
176
|
+
expect(resolved).toEqual({
|
|
177
|
+
color: '#1f2937',
|
|
178
|
+
backgroundColor: '#ffffff',
|
|
179
|
+
borderColor: 'red',
|
|
180
|
+
fontSize: '16px',
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('should handle empty styles object', () => {
|
|
185
|
+
const resolved = resolveColorVariablesInStyles({}, colorMap);
|
|
186
|
+
expect(resolved).toEqual({});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('should handle styles with no variables', () => {
|
|
190
|
+
const styles = {
|
|
191
|
+
color: '#000000',
|
|
192
|
+
backgroundColor: 'white',
|
|
193
|
+
padding: '10px',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const resolved = resolveColorVariablesInStyles(styles, colorMap);
|
|
197
|
+
|
|
198
|
+
expect(resolved).toEqual(styles);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('should preserve undefined variables', () => {
|
|
202
|
+
const styles = {
|
|
203
|
+
color: 'var(--unknown)',
|
|
204
|
+
backgroundColor: 'var(--background)',
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const resolved = resolveColorVariablesInStyles(styles, colorMap);
|
|
208
|
+
|
|
209
|
+
expect(resolved).toEqual({
|
|
210
|
+
color: 'var(--unknown)',
|
|
211
|
+
backgroundColor: '#ffffff',
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('should handle non-string style values', () => {
|
|
216
|
+
const styles = {
|
|
217
|
+
color: 'var(--text)',
|
|
218
|
+
zIndex: 10,
|
|
219
|
+
opacity: 0.5,
|
|
220
|
+
display: null as any,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const resolved = resolveColorVariablesInStyles(styles, colorMap);
|
|
224
|
+
|
|
225
|
+
expect(resolved.color).toBe('#1f2937');
|
|
226
|
+
expect(resolved.zIndex).toBe(10);
|
|
227
|
+
expect(resolved.opacity).toBe(0.5);
|
|
228
|
+
expect(resolved.display).toBeNull();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('getColorVariablesFromStyles', () => {
|
|
233
|
+
test('should extract all color variable names from styles', () => {
|
|
234
|
+
const styles = {
|
|
235
|
+
color: 'var(--text)',
|
|
236
|
+
backgroundColor: 'var(--background)',
|
|
237
|
+
borderColor: 'var(--primary)',
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
241
|
+
|
|
242
|
+
expect(variables).toContain('text');
|
|
243
|
+
expect(variables).toContain('background');
|
|
244
|
+
expect(variables).toContain('primary');
|
|
245
|
+
expect(variables.length).toBe(3);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('should ignore non-variable values', () => {
|
|
249
|
+
const styles = {
|
|
250
|
+
color: 'var(--text)',
|
|
251
|
+
backgroundColor: '#ffffff',
|
|
252
|
+
borderColor: 'red',
|
|
253
|
+
padding: '10px',
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
257
|
+
|
|
258
|
+
expect(variables).toEqual(['text']);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('should handle empty styles object', () => {
|
|
262
|
+
const variables = getColorVariablesFromStyles({});
|
|
263
|
+
expect(variables).toEqual([]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('should handle styles with no variables', () => {
|
|
267
|
+
const styles = {
|
|
268
|
+
color: '#000000',
|
|
269
|
+
backgroundColor: 'white',
|
|
270
|
+
padding: '10px',
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
274
|
+
expect(variables).toEqual([]);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('should deduplicate variable names', () => {
|
|
278
|
+
const styles = {
|
|
279
|
+
color: 'var(--primary)',
|
|
280
|
+
backgroundColor: 'var(--primary)',
|
|
281
|
+
borderColor: 'var(--primary)',
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
285
|
+
|
|
286
|
+
expect(variables).toEqual(['primary']);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('should handle mixed variable and non-variable values', () => {
|
|
290
|
+
const styles = {
|
|
291
|
+
color: 'var(--text)',
|
|
292
|
+
backgroundColor: 'var(--background)',
|
|
293
|
+
borderColor: 'red',
|
|
294
|
+
outlineColor: 'var(--accent)',
|
|
295
|
+
padding: '10px',
|
|
296
|
+
margin: 0,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
300
|
+
|
|
301
|
+
expect(variables).toContain('text');
|
|
302
|
+
expect(variables).toContain('background');
|
|
303
|
+
expect(variables).toContain('accent');
|
|
304
|
+
expect(variables.length).toBe(3);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('should handle invalid variable syntax gracefully', () => {
|
|
308
|
+
const styles = {
|
|
309
|
+
color: 'var(--text)',
|
|
310
|
+
backgroundColor: 'var(invalid)',
|
|
311
|
+
borderColor: 'var(--)',
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const variables = getColorVariablesFromStyles(styles);
|
|
315
|
+
|
|
316
|
+
expect(variables).toEqual(['text']);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Variable Utilities
|
|
3
|
+
* Handles resolution and validation of color variables in styles
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a value is a color variable reference
|
|
8
|
+
* @example "var(--primary-color)" -> true
|
|
9
|
+
*/
|
|
10
|
+
export function isColorVariable(value: string | unknown): value is string {
|
|
11
|
+
if (typeof value !== 'string') return false;
|
|
12
|
+
return /^var\(--[\w-]+\)$/.test(value.trim());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract variable name from a color variable reference
|
|
17
|
+
* @example "var(--primary-color)" -> "primary-color"
|
|
18
|
+
*/
|
|
19
|
+
export function extractVariableName(value: string): string | null {
|
|
20
|
+
const match = value.match(/^var\(--([^)]+)\)$/);
|
|
21
|
+
return match ? match[1] : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a color variable reference
|
|
26
|
+
* @example "primary-color" -> "var(--primary-color)"
|
|
27
|
+
*/
|
|
28
|
+
export function createVariableReference(variableName: string): string {
|
|
29
|
+
return `var(--${variableName})`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a color variable to its actual value
|
|
34
|
+
* @param value - The style value (may be a variable reference or actual color)
|
|
35
|
+
* @param colorMap - Map of variable names to color values
|
|
36
|
+
* @returns Resolved color value or original value if not a variable
|
|
37
|
+
*/
|
|
38
|
+
export function resolveColorVariable(
|
|
39
|
+
value: string | unknown,
|
|
40
|
+
colorMap: Record<string, string>
|
|
41
|
+
): string {
|
|
42
|
+
if (!isColorVariable(value)) {
|
|
43
|
+
return String(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const varName = extractVariableName(value);
|
|
47
|
+
if (!varName) {
|
|
48
|
+
return String(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return colorMap[varName] || String(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Replace color variables in a style object
|
|
56
|
+
* @param styles - Style object with potential variable references
|
|
57
|
+
* @param colorMap - Map of variable names to color values
|
|
58
|
+
* @returns New style object with variables resolved
|
|
59
|
+
*/
|
|
60
|
+
export function resolveColorVariablesInStyles(
|
|
61
|
+
styles: Record<string, any>,
|
|
62
|
+
colorMap: Record<string, string>
|
|
63
|
+
): Record<string, any> {
|
|
64
|
+
const resolved: Record<string, any> = {};
|
|
65
|
+
|
|
66
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
67
|
+
if (typeof value === 'string') {
|
|
68
|
+
resolved[key] = resolveColorVariable(value, colorMap);
|
|
69
|
+
} else {
|
|
70
|
+
resolved[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return resolved;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get all color variable references from a style object
|
|
79
|
+
* @param styles - Style object to search
|
|
80
|
+
* @returns Array of variable names found in styles
|
|
81
|
+
*/
|
|
82
|
+
export function getColorVariablesFromStyles(
|
|
83
|
+
styles: Record<string, any>
|
|
84
|
+
): string[] {
|
|
85
|
+
const variables = new Set<string>();
|
|
86
|
+
|
|
87
|
+
for (const value of Object.values(styles)) {
|
|
88
|
+
if (isColorVariable(value)) {
|
|
89
|
+
const varName = extractVariableName(value);
|
|
90
|
+
if (varName) {
|
|
91
|
+
variables.add(varName);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return Array.from(variables);
|
|
97
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
SERVER_PORT,
|
|
4
|
+
API_ROUTES,
|
|
5
|
+
HMR_ROUTE,
|
|
6
|
+
FILE_PATTERNS,
|
|
7
|
+
DEFAULT_TIMEOUT,
|
|
8
|
+
WEBSOCKET_STATES,
|
|
9
|
+
NOT_FOUND_TIMEOUT_MS,
|
|
10
|
+
TAB_SWITCH_DELAY_MS,
|
|
11
|
+
IFRAME_HIGHLIGHT_DELAY_MS,
|
|
12
|
+
TREE_SCROLL_DELAY_MS,
|
|
13
|
+
HOVER_HIGHLIGHT_DELAY_MS,
|
|
14
|
+
MAX_PORT_ATTEMPTS,
|
|
15
|
+
IFRAME_MESSAGE_TYPES,
|
|
16
|
+
NODE_TYPE,
|
|
17
|
+
SPECIAL_PATHS,
|
|
18
|
+
DEFAULT_COMPONENT_TYPES,
|
|
19
|
+
DEFAULT_ICON_COLOR,
|
|
20
|
+
} from './constants';
|
|
21
|
+
|
|
22
|
+
describe('constants', () => {
|
|
23
|
+
describe('SERVER_PORT', () => {
|
|
24
|
+
test('returns a valid port number', () => {
|
|
25
|
+
expect(typeof SERVER_PORT).toBe('number');
|
|
26
|
+
expect(SERVER_PORT).toBeGreaterThan(0);
|
|
27
|
+
expect(SERVER_PORT).toBeLessThan(65536);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('API_ROUTES', () => {
|
|
32
|
+
test('contains all expected route definitions', () => {
|
|
33
|
+
expect(API_ROUTES.PAGES).toBe('/api/pages');
|
|
34
|
+
expect(API_ROUTES.COMPONENTS).toBe('/api/components');
|
|
35
|
+
expect(API_ROUTES.YAML).toBe('/api/yaml');
|
|
36
|
+
expect(API_ROUTES.PAGE_DATA).toBe('/api/page-data');
|
|
37
|
+
expect(API_ROUTES.COMPONENT_DATA).toBe('/api/component-data');
|
|
38
|
+
expect(API_ROUTES.SAVE_PAGE).toBe('/api/save-page');
|
|
39
|
+
expect(API_ROUTES.SAVE_COMPONENT).toBe('/api/save-component');
|
|
40
|
+
expect(API_ROUTES.SAVE_COMPONENT_JS).toBe('/api/save-component-js');
|
|
41
|
+
expect(API_ROUTES.SAVE_COMPONENT_CSS).toBe('/api/save-component-css');
|
|
42
|
+
expect(API_ROUTES.COMPONENT_JS).toBe('/api/component-js');
|
|
43
|
+
expect(API_ROUTES.CONFIG).toBe('/api/config');
|
|
44
|
+
expect(API_ROUTES.SAVE_CONFIG).toBe('/api/save-config');
|
|
45
|
+
expect(API_ROUTES.SELECTION).toBe('/api/selection');
|
|
46
|
+
expect(API_ROUTES.COLORS_CONFIG).toBe('/api/colors-config');
|
|
47
|
+
expect(API_ROUTES.SAVE_COLORS).toBe('/api/save-colors');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('all routes are strings starting with /api/', () => {
|
|
51
|
+
Object.values(API_ROUTES).forEach(route => {
|
|
52
|
+
expect(typeof route).toBe('string');
|
|
53
|
+
expect(route.startsWith('/api/')).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('HMR_ROUTE', () => {
|
|
59
|
+
test('is defined correctly', () => {
|
|
60
|
+
expect(HMR_ROUTE).toBe('/hmr');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('FILE_PATTERNS', () => {
|
|
65
|
+
test('contains expected patterns', () => {
|
|
66
|
+
expect(FILE_PATTERNS.PAGES).toBe('./pages');
|
|
67
|
+
expect(FILE_PATTERNS.COMPONENTS).toBe('./components');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('DEFAULT_TIMEOUT', () => {
|
|
72
|
+
test('is a positive number', () => {
|
|
73
|
+
expect(typeof DEFAULT_TIMEOUT).toBe('number');
|
|
74
|
+
expect(DEFAULT_TIMEOUT).toBeGreaterThan(0);
|
|
75
|
+
expect(DEFAULT_TIMEOUT).toBe(5000);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('WEBSOCKET_STATES', () => {
|
|
80
|
+
test('matches WebSocket standard state values', () => {
|
|
81
|
+
expect(WEBSOCKET_STATES.CONNECTING).toBe(0);
|
|
82
|
+
expect(WEBSOCKET_STATES.OPEN).toBe(1);
|
|
83
|
+
expect(WEBSOCKET_STATES.CLOSING).toBe(2);
|
|
84
|
+
expect(WEBSOCKET_STATES.CLOSED).toBe(3);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('timeout constants', () => {
|
|
89
|
+
test('all timeout values are positive numbers', () => {
|
|
90
|
+
expect(NOT_FOUND_TIMEOUT_MS).toBe(300);
|
|
91
|
+
expect(TAB_SWITCH_DELAY_MS).toBe(100);
|
|
92
|
+
expect(IFRAME_HIGHLIGHT_DELAY_MS).toBe(100);
|
|
93
|
+
expect(TREE_SCROLL_DELAY_MS).toBe(200);
|
|
94
|
+
expect(HOVER_HIGHLIGHT_DELAY_MS).toBe(50);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('MAX_PORT_ATTEMPTS', () => {
|
|
99
|
+
test('is a positive number', () => {
|
|
100
|
+
expect(MAX_PORT_ATTEMPTS).toBe(10);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('IFRAME_MESSAGE_TYPES', () => {
|
|
105
|
+
test('contains all expected message types', () => {
|
|
106
|
+
expect(IFRAME_MESSAGE_TYPES.DELETE_ELEMENT).toBe('DELETE_ELEMENT');
|
|
107
|
+
expect(IFRAME_MESSAGE_TYPES.TOGGLE_SELECTING_MODE).toBe('TOGGLE_SELECTING_MODE');
|
|
108
|
+
expect(IFRAME_MESSAGE_TYPES.SET_TAB).toBe('SET_TAB');
|
|
109
|
+
expect(IFRAME_MESSAGE_TYPES.COPY_ELEMENT).toBe('COPY_ELEMENT');
|
|
110
|
+
expect(IFRAME_MESSAGE_TYPES.PASTE_ELEMENT).toBe('PASTE_ELEMENT');
|
|
111
|
+
expect(IFRAME_MESSAGE_TYPES.ARROW_UP).toBe('ARROW_UP');
|
|
112
|
+
expect(IFRAME_MESSAGE_TYPES.ARROW_DOWN).toBe('ARROW_DOWN');
|
|
113
|
+
expect(IFRAME_MESSAGE_TYPES.ARROW_LEFT).toBe('ARROW_LEFT');
|
|
114
|
+
expect(IFRAME_MESSAGE_TYPES.ARROW_RIGHT).toBe('ARROW_RIGHT');
|
|
115
|
+
expect(IFRAME_MESSAGE_TYPES.OPEN_COMMAND_PALETTE).toBe('OPEN_COMMAND_PALETTE');
|
|
116
|
+
expect(IFRAME_MESSAGE_TYPES.EDIT_COMPONENT).toBe('EDIT_COMPONENT');
|
|
117
|
+
expect(IFRAME_MESSAGE_TYPES.NAVIGATE_BACK).toBe('NAVIGATE_BACK');
|
|
118
|
+
expect(IFRAME_MESSAGE_TYPES.TOGGLE_ADDING_STYLE).toBe('TOGGLE_ADDING_STYLE');
|
|
119
|
+
expect(IFRAME_MESSAGE_TYPES.MOVE_ELEMENT_UP).toBe('MOVE_ELEMENT_UP');
|
|
120
|
+
expect(IFRAME_MESSAGE_TYPES.MOVE_ELEMENT_DOWN).toBe('MOVE_ELEMENT_DOWN');
|
|
121
|
+
expect(IFRAME_MESSAGE_TYPES.SELECTION_CHANGED).toBe('SELECTION_CHANGED');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('NODE_TYPE', () => {
|
|
126
|
+
test('contains all expected node types', () => {
|
|
127
|
+
expect(NODE_TYPE.NODE).toBe('node');
|
|
128
|
+
expect(NODE_TYPE.COMPONENT).toBe('component');
|
|
129
|
+
expect(NODE_TYPE.SLOT).toBe('slot');
|
|
130
|
+
expect(NODE_TYPE.EMBED).toBe('embed');
|
|
131
|
+
expect(NODE_TYPE.OBJECT_LINK).toBe('object-link');
|
|
132
|
+
expect(NODE_TYPE.LOCALE_LIST).toBe('locale-list');
|
|
133
|
+
expect(NODE_TYPE.TEXT).toBe('text');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('SPECIAL_PATHS', () => {
|
|
138
|
+
test('contains all expected special path identifiers', () => {
|
|
139
|
+
expect(SPECIAL_PATHS.COMPONENT_INTERFACE).toBe('component_interface');
|
|
140
|
+
expect(SPECIAL_PATHS.COMPONENT_JAVASCRIPT).toBe('component_javascript');
|
|
141
|
+
expect(SPECIAL_PATHS.COMPONENT_CSS).toBe('component_css');
|
|
142
|
+
expect(SPECIAL_PATHS.STRUCTURE_STYLE).toBe('structure_style');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('DEFAULT_COMPONENT_TYPES', () => {
|
|
147
|
+
test('is an array with expected component types', () => {
|
|
148
|
+
expect(Array.isArray(DEFAULT_COMPONENT_TYPES)).toBe(true);
|
|
149
|
+
expect(DEFAULT_COMPONENT_TYPES.length).toBe(3);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('contains ui, layout, and content types', () => {
|
|
153
|
+
const ids = DEFAULT_COMPONENT_TYPES.map(t => t.id);
|
|
154
|
+
expect(ids).toContain('ui');
|
|
155
|
+
expect(ids).toContain('layout');
|
|
156
|
+
expect(ids).toContain('content');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('each type has id, label, and color', () => {
|
|
160
|
+
DEFAULT_COMPONENT_TYPES.forEach(type => {
|
|
161
|
+
expect(typeof type.id).toBe('string');
|
|
162
|
+
expect(typeof type.label).toBe('string');
|
|
163
|
+
expect(typeof type.color).toBe('string');
|
|
164
|
+
expect(type.color.startsWith('#')).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('DEFAULT_ICON_COLOR', () => {
|
|
170
|
+
test('is a valid hex color', () => {
|
|
171
|
+
expect(DEFAULT_ICON_COLOR).toBe('#6b7280');
|
|
172
|
+
expect(DEFAULT_ICON_COLOR.startsWith('#')).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|