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