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,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Provider Interfaces
|
|
3
|
+
* Abstractions for data sources (file system, CMS, API, etc.)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentDefinition } from '../types/components';
|
|
7
|
+
import type { CMSSchema, CMSItem } from '../types/cms';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Page Provider Interface
|
|
11
|
+
* Abstracts page data loading from the underlying storage mechanism
|
|
12
|
+
*/
|
|
13
|
+
export interface PageProvider {
|
|
14
|
+
/**
|
|
15
|
+
* Load all pages from the data source
|
|
16
|
+
* @returns Map of page path to raw JSON string content
|
|
17
|
+
*/
|
|
18
|
+
loadAll(): Promise<Map<string, string>>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get a single page by path
|
|
22
|
+
* @param path - Page path (e.g., "/" or "/about")
|
|
23
|
+
* @returns Raw JSON string content, or null if not found
|
|
24
|
+
*/
|
|
25
|
+
get(path: string): Promise<string | null>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Save page content
|
|
29
|
+
* @param path - Page path
|
|
30
|
+
* @param content - Raw JSON string content
|
|
31
|
+
*/
|
|
32
|
+
save(path: string, content: string): Promise<void>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Delete a page
|
|
36
|
+
* @param path - Page path to delete
|
|
37
|
+
*/
|
|
38
|
+
delete(path: string): Promise<void>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if a page exists
|
|
42
|
+
* @param path - Page path
|
|
43
|
+
* @returns True if page exists
|
|
44
|
+
*/
|
|
45
|
+
exists(path: string): Promise<boolean>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Component Provider Interface
|
|
50
|
+
* Abstracts component definition loading
|
|
51
|
+
*/
|
|
52
|
+
export interface ComponentProvider {
|
|
53
|
+
/**
|
|
54
|
+
* Load all component definitions
|
|
55
|
+
* @returns Map of component name to definition
|
|
56
|
+
*/
|
|
57
|
+
loadAll(): Promise<Map<string, ComponentDefinition>>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get a single component by name
|
|
61
|
+
* @param name - Component name
|
|
62
|
+
* @returns Component definition or null if not found
|
|
63
|
+
*/
|
|
64
|
+
get(name: string): Promise<ComponentDefinition | null>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get JavaScript code for a component
|
|
68
|
+
* @param name - Component name
|
|
69
|
+
* @returns JavaScript string or null if not found
|
|
70
|
+
*/
|
|
71
|
+
getJS(name: string): Promise<string | null>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get CSS code for a component
|
|
75
|
+
* @param name - Component name
|
|
76
|
+
* @returns CSS string or null if not found
|
|
77
|
+
*/
|
|
78
|
+
getCSS(name: string): Promise<string | null>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Save component definition
|
|
82
|
+
* @param name - Component name
|
|
83
|
+
* @param definition - Component definition
|
|
84
|
+
*/
|
|
85
|
+
save(name: string, definition: ComponentDefinition): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// CMS Provider
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/** Schema info extracted from a page file */
|
|
93
|
+
export interface CMSSchemaInfo {
|
|
94
|
+
schema: CMSSchema;
|
|
95
|
+
pagePath: string; // Path to the page file containing this schema
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* CMS Provider Interface
|
|
100
|
+
* Abstracts CMS data loading from the underlying storage mechanism
|
|
101
|
+
* (follows same pattern as PageProvider)
|
|
102
|
+
*/
|
|
103
|
+
export interface CMSProvider {
|
|
104
|
+
/** Load all CMS schemas (extracted from page files with source: 'cms') */
|
|
105
|
+
getAllSchemas(): Promise<Map<string, CMSSchemaInfo>>;
|
|
106
|
+
|
|
107
|
+
/** Load all items for a collection */
|
|
108
|
+
getItems(collection: string): Promise<CMSItem[]>;
|
|
109
|
+
|
|
110
|
+
/** Get single item by slug */
|
|
111
|
+
getItemBySlug(collection: string, slug: string): Promise<CMSItem | null>;
|
|
112
|
+
|
|
113
|
+
/** Get single item by ID */
|
|
114
|
+
getItemById(collection: string, id: string): Promise<CMSItem | null>;
|
|
115
|
+
|
|
116
|
+
/** Save item (for editor) - saves to cms/<collection>/<slug>.json */
|
|
117
|
+
saveItem(collection: string, item: CMSItem): Promise<void>;
|
|
118
|
+
|
|
119
|
+
/** Delete item by slug */
|
|
120
|
+
deleteItem(collection: string, slug: string): Promise<void>;
|
|
121
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
isComponentNode,
|
|
4
|
+
isHtmlNode,
|
|
5
|
+
getComponentName,
|
|
6
|
+
getTagName,
|
|
7
|
+
getDisplayName,
|
|
8
|
+
extractNodeProperties,
|
|
9
|
+
isLikelyComponentTag,
|
|
10
|
+
isSlotMarker,
|
|
11
|
+
isEmbedNode,
|
|
12
|
+
isObjectLinkNode,
|
|
13
|
+
isLocaleListNode,
|
|
14
|
+
isValidNodeType,
|
|
15
|
+
canHaveChildren,
|
|
16
|
+
canHaveStyle,
|
|
17
|
+
isStructuredComponentDefinition,
|
|
18
|
+
hasComponentStructure,
|
|
19
|
+
hasComponentCss,
|
|
20
|
+
} from './nodeUtils';
|
|
21
|
+
import { NODE_TYPE } from './constants';
|
|
22
|
+
import type { ComponentNode, ComponentInstanceNode, HtmlNode, SlotMarker, EmbedNode, ObjectLinkNode, LocaleListNode } from './types';
|
|
23
|
+
import { registerBuiltInNodeTypes } from './registry';
|
|
24
|
+
|
|
25
|
+
describe('nodeUtils', () => {
|
|
26
|
+
beforeAll(() => {
|
|
27
|
+
registerBuiltInNodeTypes();
|
|
28
|
+
});
|
|
29
|
+
describe('isComponentNode', () => {
|
|
30
|
+
test('returns true for component instance nodes', () => {
|
|
31
|
+
const node: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
32
|
+
expect(isComponentNode(node)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('returns false for HTML nodes', () => {
|
|
36
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
37
|
+
expect(isComponentNode(node)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('returns false for null or undefined', () => {
|
|
41
|
+
expect(isComponentNode(null)).toBe(false);
|
|
42
|
+
expect(isComponentNode(undefined)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('isHtmlNode', () => {
|
|
47
|
+
test('returns true for HTML element nodes', () => {
|
|
48
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
49
|
+
expect(isHtmlNode(node)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('returns false for component nodes', () => {
|
|
53
|
+
const node: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
54
|
+
expect(isHtmlNode(node)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('returns false for null or undefined', () => {
|
|
58
|
+
expect(isHtmlNode(null)).toBe(false);
|
|
59
|
+
expect(isHtmlNode(undefined)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('getComponentName', () => {
|
|
64
|
+
test('returns component name for component nodes', () => {
|
|
65
|
+
const node: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
66
|
+
expect(getComponentName(node)).toBe('Button');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('returns undefined for HTML nodes', () => {
|
|
70
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
71
|
+
expect(getComponentName(node)).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('getTagName', () => {
|
|
76
|
+
test('returns tag name for HTML nodes', () => {
|
|
77
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
78
|
+
expect(getTagName(node)).toBe('div');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('returns undefined for component nodes', () => {
|
|
82
|
+
const node: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
83
|
+
expect(getTagName(node)).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('getDisplayName', () => {
|
|
88
|
+
test('returns component name for component nodes', () => {
|
|
89
|
+
const node: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
90
|
+
expect(getDisplayName(node)).toBe('Button');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('returns tag name for HTML nodes', () => {
|
|
94
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
95
|
+
expect(getDisplayName(node)).toBe('div');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('returns "Embed" for embed nodes', () => {
|
|
99
|
+
const node: EmbedNode = { type: NODE_TYPE.EMBED, html: '<div>test</div>' };
|
|
100
|
+
expect(getDisplayName(node)).toBe('Embed');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('returns "Link" for object link nodes', () => {
|
|
104
|
+
const node: ObjectLinkNode = { type: NODE_TYPE.OBJECT_LINK, object: 'page', id: '123' };
|
|
105
|
+
expect(getDisplayName(node)).toBe('Link');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('returns "LocaleList" for locale list nodes', () => {
|
|
109
|
+
const node: LocaleListNode = { type: NODE_TYPE.LOCALE_LIST, locales: {} };
|
|
110
|
+
expect(getDisplayName(node)).toBe('LocaleList');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('returns "Unknown" for unrecognized nodes', () => {
|
|
114
|
+
const node = { type: 'unknown' } as any;
|
|
115
|
+
expect(getDisplayName(node)).toBe('Unknown');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('extractNodeProperties', () => {
|
|
120
|
+
test('extracts component name and props from component node', () => {
|
|
121
|
+
const node: ComponentInstanceNode = {
|
|
122
|
+
type: NODE_TYPE.COMPONENT,
|
|
123
|
+
component: 'Button',
|
|
124
|
+
props: { text: 'Click me' }
|
|
125
|
+
};
|
|
126
|
+
expect(extractNodeProperties(node)).toEqual({
|
|
127
|
+
componentName: 'Button',
|
|
128
|
+
props: { text: 'Click me' }
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('extracts tag from HTML node', () => {
|
|
133
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
134
|
+
expect(extractNodeProperties(node)).toEqual({
|
|
135
|
+
tag: 'div',
|
|
136
|
+
props: {}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('returns empty props for unknown node types', () => {
|
|
141
|
+
const node = { type: 'unknown' } as any;
|
|
142
|
+
expect(extractNodeProperties(node)).toEqual({ props: {} });
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('isLikelyComponentTag', () => {
|
|
147
|
+
test('returns true for capitalized tags', () => {
|
|
148
|
+
expect(isLikelyComponentTag('Button')).toBe(true);
|
|
149
|
+
expect(isLikelyComponentTag('Card')).toBe(true);
|
|
150
|
+
expect(isLikelyComponentTag('MyComponent')).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('returns false for lowercase tags', () => {
|
|
154
|
+
expect(isLikelyComponentTag('div')).toBe(false);
|
|
155
|
+
expect(isLikelyComponentTag('span')).toBe(false);
|
|
156
|
+
expect(isLikelyComponentTag('button')).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('returns false for empty string', () => {
|
|
160
|
+
expect(isLikelyComponentTag('')).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('isSlotMarker', () => {
|
|
165
|
+
test('returns true for slot marker nodes', () => {
|
|
166
|
+
const node: SlotMarker = { type: NODE_TYPE.SLOT, name: 'default' };
|
|
167
|
+
expect(isSlotMarker(node)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('returns false for non-slot nodes', () => {
|
|
171
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
172
|
+
expect(isSlotMarker(node)).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('returns false for null or undefined', () => {
|
|
176
|
+
expect(isSlotMarker(null)).toBe(false);
|
|
177
|
+
expect(isSlotMarker(undefined)).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('isEmbedNode', () => {
|
|
182
|
+
test('returns true for embed nodes', () => {
|
|
183
|
+
const node: EmbedNode = { type: NODE_TYPE.EMBED, url: 'https://example.com' };
|
|
184
|
+
expect(isEmbedNode(node)).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('returns false for non-embed nodes', () => {
|
|
188
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
189
|
+
expect(isEmbedNode(node)).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('isObjectLinkNode', () => {
|
|
194
|
+
test('returns true for object link nodes', () => {
|
|
195
|
+
const node: ObjectLinkNode = { type: NODE_TYPE.OBJECT_LINK, object: 'page', id: '123' };
|
|
196
|
+
expect(isObjectLinkNode(node)).toBe(true);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('returns false for non-object-link nodes', () => {
|
|
200
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
201
|
+
expect(isObjectLinkNode(node)).toBe(false);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('isLocaleListNode', () => {
|
|
206
|
+
test('returns true for locale list nodes', () => {
|
|
207
|
+
const node: LocaleListNode = { type: NODE_TYPE.LOCALE_LIST, locales: {} };
|
|
208
|
+
expect(isLocaleListNode(node)).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('returns false for non-locale-list nodes', () => {
|
|
212
|
+
const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
213
|
+
expect(isLocaleListNode(node)).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('isValidNodeType', () => {
|
|
218
|
+
test('returns true for valid node types', () => {
|
|
219
|
+
expect(isValidNodeType(NODE_TYPE.NODE)).toBe(true);
|
|
220
|
+
expect(isValidNodeType(NODE_TYPE.COMPONENT)).toBe(true);
|
|
221
|
+
expect(isValidNodeType(NODE_TYPE.SLOT)).toBe(true);
|
|
222
|
+
expect(isValidNodeType(NODE_TYPE.EMBED)).toBe(true);
|
|
223
|
+
expect(isValidNodeType(NODE_TYPE.OBJECT_LINK)).toBe(true);
|
|
224
|
+
expect(isValidNodeType(NODE_TYPE.LOCALE_LIST)).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('returns false for invalid node types', () => {
|
|
228
|
+
expect(isValidNodeType('invalid')).toBe(false);
|
|
229
|
+
expect(isValidNodeType('unknown')).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('canHaveChildren', () => {
|
|
234
|
+
test('returns true for nodes that can have children', () => {
|
|
235
|
+
const htmlNode: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
236
|
+
expect(canHaveChildren(htmlNode)).toBe(true);
|
|
237
|
+
|
|
238
|
+
const componentNode: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
239
|
+
expect(canHaveChildren(componentNode)).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('returns false for slot markers', () => {
|
|
243
|
+
const slotNode: SlotMarker = { type: NODE_TYPE.SLOT, name: 'default' };
|
|
244
|
+
expect(canHaveChildren(slotNode)).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('returns false for null or undefined', () => {
|
|
248
|
+
expect(canHaveChildren(null)).toBe(false);
|
|
249
|
+
expect(canHaveChildren(undefined)).toBe(false);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('canHaveStyle', () => {
|
|
254
|
+
test('returns true for nodes that can have style', () => {
|
|
255
|
+
const htmlNode: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
|
|
256
|
+
expect(canHaveStyle(htmlNode)).toBe(true);
|
|
257
|
+
|
|
258
|
+
const componentNode: ComponentInstanceNode = { type: NODE_TYPE.COMPONENT, component: 'Button', props: {} };
|
|
259
|
+
expect(canHaveStyle(componentNode)).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('returns false for slot markers', () => {
|
|
263
|
+
const slotNode: SlotMarker = { type: NODE_TYPE.SLOT, name: 'default' };
|
|
264
|
+
expect(canHaveStyle(slotNode)).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('returns false for null or undefined', () => {
|
|
268
|
+
expect(canHaveStyle(null)).toBe(false);
|
|
269
|
+
expect(canHaveStyle(undefined)).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('isStructuredComponentDefinition', () => {
|
|
274
|
+
test('returns true for structured component definitions', () => {
|
|
275
|
+
expect(isStructuredComponentDefinition({ interface: [] })).toBe(true);
|
|
276
|
+
expect(isStructuredComponentDefinition({ structure: {} })).toBe(true);
|
|
277
|
+
expect(isStructuredComponentDefinition({ javascript: 'code' })).toBe(true);
|
|
278
|
+
expect(isStructuredComponentDefinition({ css: 'styles' })).toBe(true);
|
|
279
|
+
expect(isStructuredComponentDefinition({ interface: [], structure: {} })).toBe(true);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('returns false for invalid structures', () => {
|
|
283
|
+
expect(isStructuredComponentDefinition(null)).toBe(false);
|
|
284
|
+
expect(isStructuredComponentDefinition(undefined)).toBe(false);
|
|
285
|
+
expect(isStructuredComponentDefinition('string')).toBe(false);
|
|
286
|
+
expect(isStructuredComponentDefinition(123)).toBe(false);
|
|
287
|
+
expect(isStructuredComponentDefinition({})).toBe(false);
|
|
288
|
+
expect(isStructuredComponentDefinition({ structure: 'invalid' })).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe('hasComponentStructure', () => {
|
|
293
|
+
test('returns true if component has structure', () => {
|
|
294
|
+
const comp = { component: { structure: {} } };
|
|
295
|
+
expect(hasComponentStructure(comp as any)).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('returns false if component has no structure', () => {
|
|
299
|
+
const comp = { component: {} };
|
|
300
|
+
expect(hasComponentStructure(comp as any)).toBe(false);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('hasComponentCss', () => {
|
|
305
|
+
test('returns true if component has CSS string', () => {
|
|
306
|
+
const comp = { component: { css: 'body { color: red; }' } };
|
|
307
|
+
expect(hasComponentCss(comp as any)).toBe(true);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('returns false if component has no CSS', () => {
|
|
311
|
+
const comp = { component: {} };
|
|
312
|
+
expect(hasComponentCss(comp as any)).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('returns false if CSS is not a string', () => {
|
|
316
|
+
const comp = { component: { css: 123 } };
|
|
317
|
+
expect(hasComponentCss(comp as any)).toBe(false);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node type utilities and type guards
|
|
3
|
+
* Provides helper functions for working with ComponentNode types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentNode, ComponentInstanceNode, HtmlNode, SlotMarker, EmbedNode, ObjectLinkNode, LocaleListNode, ComponentDefinition, StructuredComponentDefinition } from './types';
|
|
7
|
+
import { NODE_TYPE } from './constants';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Type guard to check if a node is a component instance
|
|
11
|
+
*/
|
|
12
|
+
export function isComponentNode(node: ComponentNode | null | undefined): node is ComponentInstanceNode {
|
|
13
|
+
return node?.type === NODE_TYPE.COMPONENT;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Type guard to check if a node is an HTML element
|
|
18
|
+
*/
|
|
19
|
+
export function isHtmlNode(node: ComponentNode | null | undefined): node is HtmlNode {
|
|
20
|
+
return node?.type === NODE_TYPE.NODE;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the component name from a ComponentNode (for component instances)
|
|
25
|
+
*/
|
|
26
|
+
export function getComponentName(node: ComponentNode): string | undefined {
|
|
27
|
+
return isComponentNode(node) ? node.component : undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the tag name from a ComponentNode (for HTML nodes)
|
|
32
|
+
*/
|
|
33
|
+
export function getTagName(node: ComponentNode): string | undefined {
|
|
34
|
+
return isHtmlNode(node) ? node.tag : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the display name (tag or component name) from a ComponentNode
|
|
39
|
+
* Uses registry for automatic display name resolution
|
|
40
|
+
*/
|
|
41
|
+
export function getDisplayName(node: ComponentNode): string {
|
|
42
|
+
// Dynamic names for component/html nodes
|
|
43
|
+
if (isComponentNode(node)) {
|
|
44
|
+
return node.component;
|
|
45
|
+
}
|
|
46
|
+
if (isHtmlNode(node)) {
|
|
47
|
+
return node.tag;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Use registry for all other node types
|
|
51
|
+
const { globalNodeTypeManager } = require('./registry');
|
|
52
|
+
const definition = globalNodeTypeManager.getClient().findByNode(node);
|
|
53
|
+
return definition?.displayName || 'Unknown';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extract node properties (tag/component, props) based on node type
|
|
58
|
+
* Returns an object with tag, componentName, and props
|
|
59
|
+
*/
|
|
60
|
+
export function extractNodeProperties(node: ComponentNode): {
|
|
61
|
+
tag?: string;
|
|
62
|
+
componentName?: string;
|
|
63
|
+
props: Record<string, unknown>;
|
|
64
|
+
} {
|
|
65
|
+
if (isComponentNode(node)) {
|
|
66
|
+
return {
|
|
67
|
+
componentName: node.component,
|
|
68
|
+
props: node.props || {}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (isHtmlNode(node)) {
|
|
72
|
+
return {
|
|
73
|
+
tag: node.tag,
|
|
74
|
+
props: {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { props: {} };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a tag name is likely a component (as opposed to HTML element)
|
|
82
|
+
* This is a heuristic based on naming conventions:
|
|
83
|
+
* - Components typically start with uppercase (e.g., "Card", "Button")
|
|
84
|
+
* - HTML elements are lowercase (e.g., "div", "span")
|
|
85
|
+
*
|
|
86
|
+
* Note: This is not foolproof, but works for most cases.
|
|
87
|
+
* For accurate detection, use component registry lookup.
|
|
88
|
+
*/
|
|
89
|
+
export function isLikelyComponentTag(tag: string): boolean {
|
|
90
|
+
if (!tag) return false;
|
|
91
|
+
// Components typically start with uppercase letter
|
|
92
|
+
return tag.length > 0 && tag[0] === tag[0].toUpperCase() && tag[0] !== tag[0].toLowerCase();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Type guard to check if a node is a slot marker
|
|
97
|
+
*/
|
|
98
|
+
export function isSlotMarker(node: ComponentNode | null | undefined): node is SlotMarker {
|
|
99
|
+
return node?.type === NODE_TYPE.SLOT;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Type guard to check if a node is an embed node
|
|
104
|
+
*/
|
|
105
|
+
export function isEmbedNode(node: ComponentNode | null | undefined): node is EmbedNode {
|
|
106
|
+
return node?.type === NODE_TYPE.EMBED;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Type guard to check if a node is an object link node
|
|
111
|
+
*/
|
|
112
|
+
export function isObjectLinkNode(node: ComponentNode | null | undefined): node is ObjectLinkNode {
|
|
113
|
+
return node?.type === NODE_TYPE.OBJECT_LINK;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Type guard to check if a node is a locale list node
|
|
118
|
+
*/
|
|
119
|
+
export function isLocaleListNode(node: ComponentNode | null | undefined): node is LocaleListNode {
|
|
120
|
+
return node?.type === NODE_TYPE.LOCALE_LIST;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validate that a node type is valid
|
|
125
|
+
*/
|
|
126
|
+
export function isValidNodeType(type: string): type is typeof NODE_TYPE[keyof typeof NODE_TYPE] {
|
|
127
|
+
return type === NODE_TYPE.NODE || type === NODE_TYPE.COMPONENT || type === NODE_TYPE.SLOT ||
|
|
128
|
+
type === NODE_TYPE.EMBED || type === NODE_TYPE.OBJECT_LINK || type === NODE_TYPE.LOCALE_LIST;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if a node can have children property (excludes SlotMarker)
|
|
133
|
+
*/
|
|
134
|
+
export function canHaveChildren(node: ComponentNode | null | undefined): node is Exclude<ComponentNode, SlotMarker> {
|
|
135
|
+
return node !== null && node !== undefined && !isSlotMarker(node);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if a node can have style property (excludes SlotMarker)
|
|
140
|
+
*/
|
|
141
|
+
export function canHaveStyle(node: ComponentNode | null | undefined): node is Exclude<ComponentNode, SlotMarker> {
|
|
142
|
+
return node !== null && node !== undefined && !isSlotMarker(node);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Type guard to check if a value is a StructuredComponentDefinition
|
|
147
|
+
*/
|
|
148
|
+
export function isStructuredComponentDefinition(comp: unknown): comp is StructuredComponentDefinition {
|
|
149
|
+
if (!comp || typeof comp !== 'object') {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
// Check for required structure properties (interface, structure are optional but structure should be an object if present)
|
|
153
|
+
const obj = comp as Record<string, unknown>;
|
|
154
|
+
// Must have at least one of: interface, structure, javascript, css
|
|
155
|
+
return (
|
|
156
|
+
('interface' in obj || 'structure' in obj || 'javascript' in obj || 'css' in obj) &&
|
|
157
|
+
(obj.structure === undefined || (typeof obj.structure === 'object' && obj.structure !== null))
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if a ComponentDefinition has a structure property
|
|
163
|
+
*/
|
|
164
|
+
export function hasComponentStructure(comp: ComponentDefinition): boolean {
|
|
165
|
+
return comp.component?.structure !== undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if a ComponentDefinition has a css property
|
|
170
|
+
*/
|
|
171
|
+
export function hasComponentCss(comp: ComponentDefinition): boolean {
|
|
172
|
+
return comp.component?.css !== undefined && typeof comp.component.css === 'string';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get the node type definition from the registry
|
|
177
|
+
* This is a convenience function that uses the global node type registry
|
|
178
|
+
* to find the type definition for a node.
|
|
179
|
+
*
|
|
180
|
+
* @param node - The node to get the type definition for
|
|
181
|
+
* @returns The node type definition, or undefined if not found
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const node = { type: 'image', src: 'test.jpg' };
|
|
185
|
+
* const definition = getNodeTypeDefinition(node);
|
|
186
|
+
* if (definition) {
|
|
187
|
+
* console.log(definition.displayName); // "Image"
|
|
188
|
+
* }
|
|
189
|
+
*/
|
|
190
|
+
export function getNodeTypeDefinition(node: ComponentNode | null | undefined) {
|
|
191
|
+
if (!node) return undefined;
|
|
192
|
+
// Import dynamically to avoid circular dependencies
|
|
193
|
+
// The globalNodeTypeManager will be initialized by the app entry point
|
|
194
|
+
const { globalNodeTypeManager } = require('./registry');
|
|
195
|
+
return globalNodeTypeManager.getClient().findByNode(node);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Check if a node matches a specific type using the registry
|
|
200
|
+
* This is an alternative to the individual type guards that uses
|
|
201
|
+
* the centralized node type registry.
|
|
202
|
+
*
|
|
203
|
+
* @param node - The node to check
|
|
204
|
+
* @param nodeType - The node type constant (e.g., NODE_TYPE.EMBED)
|
|
205
|
+
* @returns true if the node matches the type
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* import { NODE_TYPE } from './constants';
|
|
209
|
+
* const node = { type: 'embed', html: '<div>test</div>' };
|
|
210
|
+
* if (isNodeType(node, NODE_TYPE.EMBED)) {
|
|
211
|
+
* // node is an EmbedNode
|
|
212
|
+
* }
|
|
213
|
+
*/
|
|
214
|
+
export function isNodeType(node: ComponentNode | null | undefined, nodeType: string): boolean {
|
|
215
|
+
if (!node) return false;
|
|
216
|
+
// Import dynamically to avoid circular dependencies
|
|
217
|
+
const { globalNodeTypeManager } = require('./registry');
|
|
218
|
+
return globalNodeTypeManager.getClient().isType(node, nodeType);
|
|
219
|
+
}
|
|
220
|
+
|