Skip to content

Commit 00fa51d

Browse files
committed
refactor(archetype): extract helper functions into separate module
Split archetype.ts by extracting reusable helper functions into archetype-helpers.ts, reducing code duplication and improving maintainability. The main changes include: - Extract isRelationType type guard for relation type checking - Extract findMatchingDontFragmentRelations for dontFragment lookups - Extract buildCacheKey, getWildcardRelationDataSource, and component value builders (buildSingleComponent, buildWildcardRelationValue, buildRegularComponentValue) - Simplify Archetype class methods using extracted helpers - Use optional chaining and map() for cleaner code
1 parent 757b8bc commit 00fa51d

File tree

2 files changed

+238
-372
lines changed

2 files changed

+238
-372
lines changed

src/core/archetype-helpers.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { MISSING_COMPONENT } from "./archetype";
2+
import type { ComponentId, EntityId, WildcardRelationId } from "./entity";
3+
import { getComponentIdFromRelationId, getDetailedIdType, getIdType, getTargetIdFromRelationId } from "./entity";
4+
import { isOptionalEntityId, type ComponentType } from "./types";
5+
6+
type DetailedIdType = ReturnType<typeof getDetailedIdType>;
7+
8+
type RelationDetailedType =
9+
| { type: "entity-relation"; componentId: ComponentId<any>; targetId: EntityId<any> }
10+
| { type: "component-relation"; componentId: ComponentId<any>; targetId: ComponentId<any> };
11+
12+
/**
13+
* Check if a detailed type represents a relation (entity or component)
14+
*/
15+
export function isRelationType(detailedType: DetailedIdType): detailedType is RelationDetailedType {
16+
return detailedType.type === "entity-relation" || detailedType.type === "component-relation";
17+
}
18+
19+
/**
20+
* Check if a component type matches a given component ID for relations
21+
*/
22+
export function matchesRelationComponentId(componentType: EntityId<any>, componentId: EntityId<any>): boolean {
23+
const detailedType = getDetailedIdType(componentType);
24+
return isRelationType(detailedType) && detailedType.componentId === componentId;
25+
}
26+
27+
/**
28+
* Find all relations in dontFragment data that match a component ID
29+
*/
30+
export function findMatchingDontFragmentRelations(
31+
dontFragmentData: Map<EntityId<any>, any> | undefined,
32+
componentId: EntityId<any>,
33+
): [EntityId<unknown>, any][] {
34+
const relations: [EntityId<unknown>, any][] = [];
35+
if (!dontFragmentData) return relations;
36+
37+
for (const [relType, data] of dontFragmentData) {
38+
const relDetailed = getDetailedIdType(relType);
39+
if (isRelationType(relDetailed) && relDetailed.componentId === componentId) {
40+
relations.push([relDetailed.targetId, data]);
41+
}
42+
}
43+
return relations;
44+
}
45+
46+
/**
47+
* Build cache key for component types
48+
*/
49+
export function buildCacheKey(componentTypes: readonly ComponentType<any>[]): string {
50+
return componentTypes.map((id) => (isOptionalEntityId(id) ? `opt(${id.optional})` : `${id}`)).join(",");
51+
}
52+
53+
/**
54+
* Get data source for wildcard relations from component types
55+
*/
56+
export function getWildcardRelationDataSource(
57+
componentTypes: EntityId<any>[],
58+
componentId: EntityId<any>,
59+
optional: boolean,
60+
): EntityId<any>[] | undefined {
61+
const matchingRelations = componentTypes.filter((ct) => matchesRelationComponentId(ct, componentId));
62+
return optional ? (matchingRelations.length > 0 ? matchingRelations : undefined) : matchingRelations;
63+
}
64+
65+
/**
66+
* Build wildcard relation value from matching relations
67+
*/
68+
export function buildWildcardRelationValue(
69+
wildcardRelationType: WildcardRelationId<any>,
70+
matchingRelations: EntityId<any>[] | undefined,
71+
getDataAtIndex: (relType: EntityId<any>) => any,
72+
dontFragmentData: Map<EntityId<any>, any> | undefined,
73+
entityId: EntityId,
74+
optional: boolean,
75+
): any {
76+
const relations: [EntityId<unknown>, any][] = [];
77+
const targetComponentId = getComponentIdFromRelationId(wildcardRelationType);
78+
79+
// Add regular archetype relations
80+
for (const relType of matchingRelations || []) {
81+
const data = getDataAtIndex(relType);
82+
const targetId = getTargetIdFromRelationId(relType)!;
83+
relations.push([targetId, data === MISSING_COMPONENT ? undefined : data]);
84+
}
85+
86+
// Add dontFragment relations
87+
if (targetComponentId !== undefined) {
88+
relations.push(...findMatchingDontFragmentRelations(dontFragmentData, targetComponentId));
89+
}
90+
91+
if (relations.length === 0) {
92+
if (!optional) {
93+
const componentId = getComponentIdFromRelationId(wildcardRelationType);
94+
throw new Error(
95+
`No matching relations found for mandatory wildcard relation component ${componentId} on entity ${entityId}`,
96+
);
97+
}
98+
return undefined;
99+
}
100+
101+
return optional ? { value: relations } : relations;
102+
}
103+
104+
/**
105+
* Build regular component value from data source
106+
*/
107+
export function buildRegularComponentValue(dataSource: any[] | undefined, entityIndex: number, optional: boolean): any {
108+
if (dataSource === undefined) {
109+
if (optional) return undefined;
110+
throw new Error(`Component data not found for mandatory component type`);
111+
}
112+
113+
const data = dataSource[entityIndex];
114+
const result = data === MISSING_COMPONENT ? undefined : data;
115+
return optional ? { value: result } : result;
116+
}
117+
118+
/**
119+
* Build a single component value based on its type
120+
*/
121+
export function buildSingleComponent(
122+
compType: ComponentType<any>,
123+
dataSource: any[] | EntityId<any>[] | undefined,
124+
entityIndex: number,
125+
entityId: EntityId,
126+
getComponentData: (type: EntityId<any>) => any[],
127+
dontFragmentRelations: Map<EntityId, Map<EntityId<any>, any>>,
128+
): any {
129+
const optional = isOptionalEntityId(compType);
130+
const actualType = optional ? compType.optional : compType;
131+
132+
if (getIdType(actualType) === "wildcard-relation") {
133+
return buildWildcardRelationValue(
134+
actualType as WildcardRelationId<any>,
135+
dataSource as EntityId<any>[] | undefined,
136+
(relType) => getComponentData(relType)[entityIndex],
137+
dontFragmentRelations.get(entityId),
138+
entityId,
139+
optional,
140+
);
141+
} else {
142+
return buildRegularComponentValue(dataSource as any[] | undefined, entityIndex, optional);
143+
}
144+
}

0 commit comments

Comments
 (0)