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,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
+ });