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,172 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { generateColorVariablesCSS, generateThemeColorVariablesCSS } from './cssGenerator';
|
|
3
|
+
import type { ColorVariables, ThemeConfig } from '../shared/types/colors';
|
|
4
|
+
|
|
5
|
+
describe('cssGenerator', () => {
|
|
6
|
+
describe('generateColorVariablesCSS', () => {
|
|
7
|
+
test('should generate CSS color variables', () => {
|
|
8
|
+
const colors: ColorVariables = {
|
|
9
|
+
colors: {
|
|
10
|
+
'primary': '#3b82f6',
|
|
11
|
+
'secondary': '#10b981',
|
|
12
|
+
'text': '#1f2937'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const css = generateColorVariablesCSS(colors);
|
|
17
|
+
|
|
18
|
+
expect(css).toContain(':root {');
|
|
19
|
+
expect(css).toContain('--primary: #3b82f6;');
|
|
20
|
+
expect(css).toContain('--secondary: #10b981;');
|
|
21
|
+
expect(css).toContain('--text: #1f2937;');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('should return empty string for no colors', () => {
|
|
25
|
+
const colors: ColorVariables = { colors: {} };
|
|
26
|
+
const css = generateColorVariablesCSS(colors);
|
|
27
|
+
expect(css).toBe('');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should handle single color', () => {
|
|
31
|
+
const colors: ColorVariables = {
|
|
32
|
+
colors: { 'primary': '#ff0000' }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const css = generateColorVariablesCSS(colors);
|
|
36
|
+
|
|
37
|
+
expect(css).toBe(':root {\n --primary: #ff0000;\n}');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should handle hyphenated color names', () => {
|
|
41
|
+
const colors: ColorVariables = {
|
|
42
|
+
colors: {
|
|
43
|
+
'primary-light': '#e0f2fe',
|
|
44
|
+
'text-secondary': '#6b7280'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const css = generateColorVariablesCSS(colors);
|
|
49
|
+
|
|
50
|
+
expect(css).toContain('--primary-light: #e0f2fe;');
|
|
51
|
+
expect(css).toContain('--text-secondary: #6b7280;');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('generateThemeColorVariablesCSS', () => {
|
|
56
|
+
test('should generate CSS for multiple themes', () => {
|
|
57
|
+
const themeConfig: ThemeConfig = {
|
|
58
|
+
default: 'light',
|
|
59
|
+
themes: {
|
|
60
|
+
light: {
|
|
61
|
+
name: 'Light',
|
|
62
|
+
colors: {
|
|
63
|
+
'background': '#ffffff',
|
|
64
|
+
'text': '#000000'
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
dark: {
|
|
68
|
+
name: 'Dark',
|
|
69
|
+
colors: {
|
|
70
|
+
'background': '#1f2937',
|
|
71
|
+
'text': '#f9fafb'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
78
|
+
|
|
79
|
+
expect(css).toContain(':root {');
|
|
80
|
+
expect(css).toContain('[theme="light"] {');
|
|
81
|
+
expect(css).toContain('[theme="dark"] {');
|
|
82
|
+
expect(css).toContain('--background: #ffffff;');
|
|
83
|
+
expect(css).toContain('--background: #1f2937;');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('should include default theme in :root', () => {
|
|
87
|
+
const themeConfig: ThemeConfig = {
|
|
88
|
+
default: 'light',
|
|
89
|
+
themes: {
|
|
90
|
+
light: {
|
|
91
|
+
name: 'Light',
|
|
92
|
+
colors: { 'primary': '#3b82f6' }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
98
|
+
|
|
99
|
+
expect(css).toContain(':root {');
|
|
100
|
+
expect(css).toContain('--primary: #3b82f6;');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should handle single theme', () => {
|
|
104
|
+
const themeConfig: ThemeConfig = {
|
|
105
|
+
default: 'default',
|
|
106
|
+
themes: {
|
|
107
|
+
default: {
|
|
108
|
+
name: 'Default',
|
|
109
|
+
colors: { 'color': '#000000' }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
115
|
+
|
|
116
|
+
expect(css).toContain(':root {');
|
|
117
|
+
expect(css).toContain('[theme="default"] {');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should handle empty themes', () => {
|
|
121
|
+
const themeConfig: ThemeConfig = {
|
|
122
|
+
default: 'default',
|
|
123
|
+
themes: {}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
127
|
+
|
|
128
|
+
expect(css).toBe('');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should skip themes with no colors', () => {
|
|
132
|
+
const themeConfig: ThemeConfig = {
|
|
133
|
+
default: 'light',
|
|
134
|
+
themes: {
|
|
135
|
+
light: {
|
|
136
|
+
name: 'Light',
|
|
137
|
+
colors: {}
|
|
138
|
+
},
|
|
139
|
+
dark: {
|
|
140
|
+
name: 'Dark',
|
|
141
|
+
colors: { 'primary': '#000' }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
147
|
+
|
|
148
|
+
expect(css).not.toContain('[theme="light"]');
|
|
149
|
+
expect(css).toContain('[theme="dark"]');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('should separate theme blocks with blank lines', () => {
|
|
153
|
+
const themeConfig: ThemeConfig = {
|
|
154
|
+
default: 'light',
|
|
155
|
+
themes: {
|
|
156
|
+
light: {
|
|
157
|
+
name: 'Light',
|
|
158
|
+
colors: { 'a': '#fff' }
|
|
159
|
+
},
|
|
160
|
+
dark: {
|
|
161
|
+
name: 'Dark',
|
|
162
|
+
colors: { 'b': '#000' }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const css = generateThemeColorVariablesCSS(themeConfig);
|
|
168
|
+
|
|
169
|
+
expect(css).toContain('\n\n');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Color Variables Generator
|
|
3
|
+
* Generates CSS color variable declarations for themes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ColorVariables, ThemeConfig } from '../shared/types/colors';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate CSS color variable declarations from color variables
|
|
10
|
+
* Creates :root { --variable-name: value; } CSS
|
|
11
|
+
*/
|
|
12
|
+
export function generateColorVariablesCSS(colors: ColorVariables): string {
|
|
13
|
+
const cssVars: string[] = [];
|
|
14
|
+
|
|
15
|
+
for (const [name, value] of Object.entries(colors.colors)) {
|
|
16
|
+
cssVars.push(` --${name}: ${value};`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (cssVars.length === 0) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return `:root {\n${cssVars.join('\n')}\n}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate CSS color variable declarations for multiple themes
|
|
28
|
+
* Creates [theme="dark"] { --variable-name: value; } selectors for each theme
|
|
29
|
+
*/
|
|
30
|
+
export function generateThemeColorVariablesCSS(themeConfig: ThemeConfig): string {
|
|
31
|
+
const cssBlocks: string[] = [];
|
|
32
|
+
|
|
33
|
+
for (const [themeName, theme] of Object.entries(themeConfig.themes)) {
|
|
34
|
+
const cssVars: string[] = [];
|
|
35
|
+
|
|
36
|
+
for (const [name, value] of Object.entries(theme.colors)) {
|
|
37
|
+
cssVars.push(` --${name}: ${value};`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (cssVars.length > 0) {
|
|
41
|
+
cssBlocks.push(`[theme="${themeName}"] {\n${cssVars.join('\n')}\n}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Also include :root with default theme colors
|
|
46
|
+
const defaultTheme = themeConfig.themes[themeConfig.default];
|
|
47
|
+
if (defaultTheme) {
|
|
48
|
+
const cssVars: string[] = [];
|
|
49
|
+
for (const [name, value] of Object.entries(defaultTheme.colors)) {
|
|
50
|
+
cssVars.push(` --${name}: ${value};`);
|
|
51
|
+
}
|
|
52
|
+
if (cssVars.length > 0) {
|
|
53
|
+
cssBlocks.unshift(`:root {\n${cssVars.join('\n')}\n}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return cssBlocks.join('\n\n');
|
|
58
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher
|
|
3
|
+
* Watches for changes in JSON files and triggers callbacks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { watch, existsSync } from 'fs';
|
|
7
|
+
import { dirname } from 'path';
|
|
8
|
+
import type { FSWatcher } from 'fs';
|
|
9
|
+
import { mapPageNameToPath } from './jsonLoader';
|
|
10
|
+
import { projectPaths, getProjectRoot } from './projectContext';
|
|
11
|
+
|
|
12
|
+
export interface FileWatchCallbacks {
|
|
13
|
+
onComponentChange?: () => Promise<void>;
|
|
14
|
+
onPageChange?: (pagePath: string) => Promise<void>;
|
|
15
|
+
onColorsChange?: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class FileWatcher {
|
|
19
|
+
private componentsWatcher: FSWatcher | null = null;
|
|
20
|
+
private pagesWatcher: FSWatcher | null = null;
|
|
21
|
+
private colorsWatcher: FSWatcher | null = null;
|
|
22
|
+
|
|
23
|
+
constructor(private callbacks: FileWatchCallbacks) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Start watching components directory
|
|
27
|
+
* Watches both .json and .js files to detect component definition and JavaScript changes
|
|
28
|
+
*/
|
|
29
|
+
watchComponents(dirPath: string = projectPaths.components()): void {
|
|
30
|
+
if (!existsSync(dirPath)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.componentsWatcher = watch(
|
|
35
|
+
dirPath,
|
|
36
|
+
{ recursive: true },
|
|
37
|
+
async (event, filename) => {
|
|
38
|
+
// Watch .json (component definitions), .js (component JavaScript), and .css (component CSS) files
|
|
39
|
+
if (filename && (filename.endsWith('.json') || filename.endsWith('.js') || filename.endsWith('.css'))) {
|
|
40
|
+
const fileType = filename.endsWith('.js') ? 'JavaScript' : filename.endsWith('.css') ? 'CSS' : 'component';
|
|
41
|
+
|
|
42
|
+
if (this.callbacks.onComponentChange) {
|
|
43
|
+
await this.callbacks.onComponentChange();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Start watching pages directory
|
|
53
|
+
*/
|
|
54
|
+
watchPages(dirPath: string = projectPaths.pages()): void {
|
|
55
|
+
if (!existsSync(dirPath)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.pagesWatcher = watch(
|
|
60
|
+
dirPath,
|
|
61
|
+
{ recursive: true },
|
|
62
|
+
async (event, filename) => {
|
|
63
|
+
if (filename && filename.endsWith('.json')) {
|
|
64
|
+
const pageName = filename.replace('.json', '');
|
|
65
|
+
const pagePath = mapPageNameToPath(pageName);
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if (this.callbacks.onPageChange) {
|
|
69
|
+
await this.callbacks.onPageChange(pagePath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Start watching colors.json file
|
|
79
|
+
*/
|
|
80
|
+
watchColors(filePath: string = projectPaths.colors()): void {
|
|
81
|
+
if (!existsSync(filePath)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Watch the file's directory for changes
|
|
86
|
+
const dirPath = getProjectRoot();
|
|
87
|
+
this.colorsWatcher = watch(
|
|
88
|
+
dirPath,
|
|
89
|
+
{ recursive: false },
|
|
90
|
+
async (event, filename) => {
|
|
91
|
+
if (filename === 'colors.json' && this.callbacks.onColorsChange) {
|
|
92
|
+
await this.callbacks.onColorsChange();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Start watching both directories
|
|
100
|
+
*/
|
|
101
|
+
watchAll(): void {
|
|
102
|
+
this.watchComponents();
|
|
103
|
+
this.watchPages();
|
|
104
|
+
this.watchColors();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Stop watching all directories
|
|
109
|
+
*/
|
|
110
|
+
stopAll(): void {
|
|
111
|
+
if (this.componentsWatcher) {
|
|
112
|
+
this.componentsWatcher.close();
|
|
113
|
+
this.componentsWatcher = null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (this.pagesWatcher) {
|
|
117
|
+
this.pagesWatcher.close();
|
|
118
|
+
this.pagesWatcher = null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.colorsWatcher) {
|
|
122
|
+
this.colorsWatcher.close();
|
|
123
|
+
this.colorsWatcher = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if watchers are active
|
|
129
|
+
*/
|
|
130
|
+
isWatching(): boolean {
|
|
131
|
+
return this.componentsWatcher !== null || this.pagesWatcher !== null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @meno/core/server exports
|
|
3
|
+
* Core server-side utilities for SSR and read APIs
|
|
4
|
+
* Does NOT include editor-specific routes (that's in @meno/studio)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Server factory
|
|
8
|
+
export { createServer, type ServerConfig } from './createServer';
|
|
9
|
+
|
|
10
|
+
// SSR Rendering
|
|
11
|
+
export * from './ssrRenderer';
|
|
12
|
+
|
|
13
|
+
// Services
|
|
14
|
+
export { PageService } from './services/pageService';
|
|
15
|
+
export { ComponentService } from './services/componentService';
|
|
16
|
+
export { CMSService } from './services/cmsService';
|
|
17
|
+
export { configService } from './services/configService';
|
|
18
|
+
export { ColorService } from './services/ColorService';
|
|
19
|
+
|
|
20
|
+
// Providers
|
|
21
|
+
export { FileSystemPageProvider } from './providers/fileSystemPageProvider';
|
|
22
|
+
export { FileSystemCMSProvider } from './providers/fileSystemCMSProvider';
|
|
23
|
+
|
|
24
|
+
// Core routes
|
|
25
|
+
export { handleCoreApiRoutes, type CoreRouteContext } from './routes/api/core-routes';
|
|
26
|
+
|
|
27
|
+
// Route utilities
|
|
28
|
+
export { jsonResponse, errorResponse } from './routes/api/shared';
|
|
29
|
+
|
|
30
|
+
// Note: Write route handlers moved to @meno/studio:
|
|
31
|
+
// - handleSavePageRoute
|
|
32
|
+
// - handleSaveComponentRoute, handleSaveComponentJavaScriptRoute, handleSaveComponentCSSRoute
|
|
33
|
+
// - handleSaveConfigRoute, handleSaveColorsRoute
|
|
34
|
+
// - handleCreateCollectionRoute, handleCreateItemRoute, handleUpdateItemRoute, handleDeleteItemRoute
|
|
35
|
+
|
|
36
|
+
// WebSocket
|
|
37
|
+
export { WebSocketManager } from './websocketManager';
|
|
38
|
+
|
|
39
|
+
// Page cache
|
|
40
|
+
export { PageCache } from './pageCache';
|
|
41
|
+
|
|
42
|
+
// Project context
|
|
43
|
+
export * from './projectContext';
|
|
44
|
+
|
|
45
|
+
// File watcher
|
|
46
|
+
export { FileWatcherService } from './services/fileWatcherService';
|
|
47
|
+
|
|
48
|
+
// Utilities
|
|
49
|
+
export * from './utils';
|
|
50
|
+
|
|
51
|
+
// Middleware
|
|
52
|
+
export * from './middleware';
|
|
53
|
+
|
|
54
|
+
// Font loader (legacy config loader for server entry points)
|
|
55
|
+
export { loadProjectConfig } from '../shared/fontLoader';
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
parseJSON,
|
|
4
|
+
mapPageNameToPath,
|
|
5
|
+
mapPathToPageName,
|
|
6
|
+
getBreakpointConfig,
|
|
7
|
+
setBreakpointConfig,
|
|
8
|
+
getResponsiveScalesConfig,
|
|
9
|
+
setResponsiveScalesConfig,
|
|
10
|
+
getI18nConfig,
|
|
11
|
+
setI18nConfig,
|
|
12
|
+
} from './jsonLoader';
|
|
13
|
+
import { DEFAULT_BREAKPOINTS } from '../shared/breakpoints';
|
|
14
|
+
import { DEFAULT_RESPONSIVE_SCALES } from '../shared/responsiveScaling';
|
|
15
|
+
import { DEFAULT_I18N_CONFIG } from '../shared/i18n';
|
|
16
|
+
|
|
17
|
+
describe('jsonLoader', () => {
|
|
18
|
+
describe('parseJSON', () => {
|
|
19
|
+
test('parses valid JSON', () => {
|
|
20
|
+
const result = parseJSON('{"key": "value"}');
|
|
21
|
+
expect(result).toEqual({ key: 'value' });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('parses array JSON', () => {
|
|
25
|
+
const result = parseJSON('[1, 2, 3]');
|
|
26
|
+
expect(result).toEqual([1, 2, 3]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('throws on invalid JSON', () => {
|
|
30
|
+
expect(() => parseJSON('invalid')).toThrow();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('mapPageNameToPath', () => {
|
|
35
|
+
test('maps index to root path', () => {
|
|
36
|
+
expect(mapPageNameToPath('index')).toBe('/');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('adds leading slash to page name', () => {
|
|
40
|
+
expect(mapPageNameToPath('about')).toBe('/about');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('handles nested page names', () => {
|
|
44
|
+
expect(mapPageNameToPath('blog/post')).toBe('/blog/post');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('mapPathToPageName', () => {
|
|
49
|
+
test('maps root path to index', () => {
|
|
50
|
+
expect(mapPathToPageName('/')).toBe('index');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('removes leading slash from path', () => {
|
|
54
|
+
expect(mapPathToPageName('/about')).toBe('about');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('handles nested paths', () => {
|
|
58
|
+
expect(mapPathToPageName('/blog/post')).toBe('blog/post');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('breakpoint config cache', () => {
|
|
63
|
+
test('returns default config initially', () => {
|
|
64
|
+
const config = getBreakpointConfig();
|
|
65
|
+
expect(config).toEqual(DEFAULT_BREAKPOINTS);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('returns cached config after set', () => {
|
|
69
|
+
const customConfig = { mobile: 500, tablet: 900 };
|
|
70
|
+
setBreakpointConfig(customConfig);
|
|
71
|
+
const config = getBreakpointConfig();
|
|
72
|
+
expect(config).toEqual(customConfig);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('responsive scales config cache', () => {
|
|
77
|
+
test('returns default config initially', () => {
|
|
78
|
+
const config = getResponsiveScalesConfig();
|
|
79
|
+
expect(config.enabled).toBe(DEFAULT_RESPONSIVE_SCALES.enabled);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('returns cached config after set', () => {
|
|
83
|
+
const customConfig = { enabled: true, baseReference: 20 };
|
|
84
|
+
setResponsiveScalesConfig(customConfig);
|
|
85
|
+
const config = getResponsiveScalesConfig();
|
|
86
|
+
expect(config.baseReference).toBe(20);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('i18n config cache', () => {
|
|
91
|
+
test('returns default config initially', () => {
|
|
92
|
+
const config = getI18nConfig();
|
|
93
|
+
expect(config.enabled).toBe(DEFAULT_I18N_CONFIG.enabled);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('returns cached config after set', () => {
|
|
97
|
+
const customConfig = { enabled: true, defaultLocale: 'fr', locales: [] };
|
|
98
|
+
setI18nConfig(customConfig);
|
|
99
|
+
const config = getI18nConfig();
|
|
100
|
+
expect(config.defaultLocale).toBe('fr');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|