packages/core/src/lib/element/create-element.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import {
2 copyElementCloneMetadata,
3 getElementMetadata,
4 getPrimitiveDefinition,
5 setElementMetadata,
6 type PibblElementSource,
7 } from './metadata.js';
8 import type {
9 PibblElement,
10 PibblElementType,
11 PibblKey,
12 } from './types.js';
13 import { isDevelopment } from '../development.js';
14 import { capturePlacementHandoff } from '../style/positioning.js';
15
16 const PIBBL_ELEMENT_TYPE = Symbol.for('@pibbl/core.element');
17
18 type ElementProps = Record<PropertyKey, unknown>;
19 type UnionKeys<T> = T extends unknown ? keyof T : never;
20 type ElementInput<P> = 0 extends (1 & P) ?
21 P & { key?: PibblKey } :
22 'key' extends UnionKeys<P> ? never :
23 P & { key?: PibblKey };
24 type ElementInputOrNull<P> = [ElementInput<P>] extends [never] ?
25 never :
26 ElementInput<P> | null;
27 type ChildrenForProps<P> = P extends unknown ?
28 'children' extends keyof P ? P['children'] : never :
29 never;
30 type MultipleChildren<C> = C extends unknown ?
31 C extends readonly (infer Child)[] ?
32 Child[] extends C ? [Child, Child, ...Child[]] : never :
33 C extends Iterable<infer Child> ?
34 Child[] extends C ? [Child, Child, ...Child[]] : never :
35 never :
36 never;
37 type ChildrenArguments<P> = P extends unknown ?
38 'children' extends keyof P ?
39 | []
40 | [ChildrenForProps<P>]
41 | MultipleChildren<ChildrenForProps<P>> :
42 [] :
43 never;
44
45 /**
46 * Public constructor used by direct element authoring.
47 *
48 * @param type - Pibbl component or fragment to instantiate. See {@link PibblElementType}.
49 * @param props - Authored props, or null when allowed by the component's input type. See
50 * {@link ElementInputOrNull} .
51 * @param children - Child nodes supplied in source order. See {@link ChildrenArguments}.
52 * @returns A branded Pibbl element descriptor; rendering occurs when it is mounted. See
53 * {@link PibblElement} .
54 *
55 * @see {@link PibblElementType}
56 * @see {@link PibblElement}
57 */
58 export function createElement<P>(
59 type: PibblElementType<P>,
60 props: ElementInputOrNull<P>,
61 ...children: ChildrenArguments<P>
62 ): PibblElement<P> {
63 const withChildren = children.length === 0 ? props : {
64 ...(props as object | null),
65 children: children.length === 1 ? children[0] : children,
66 };
67 return createPibblElement(type, withChildren as P | null, undefined, undefined);
68 }
69
70 /** @internal Shared construction path for direct and compiler element creation. */
71 export function createPibblElement<P>(
72 type: PibblElementType<P>,
73 incomingProps: P | null,
74 compilerKey: PibblKey | null | undefined,
75 source: PibblElementSource | undefined,
76 ): PibblElement<P> {
77 const rawProps = (incomingProps ?? {}) as ElementProps;
78 const defensiveKey = Object.prototype.hasOwnProperty.call(rawProps, 'key') ?
79 rawProps.key :
80 undefined;
81 const rawKey = compilerKey === undefined ? defensiveKey : compilerKey;
82 const { key: _key, ...props } = rawProps;
83 const primitiveProgramProps = getPrimitiveDefinition(type) === undefined ?
84 undefined :
85 preparePrimitiveProgramProps(props);
86 const normalizedKey = rawKey === undefined || rawKey === null ?
87 undefined :
88 String(rawKey);
89 return finalizePibblElement(type, props as P, normalizedKey, {
90 source,
91 primitiveProgramProps,
92 });
93 }
94
95 function preparePrimitiveProgramProps(
96 props: Readonly<ElementProps>,
97 ): Readonly<ElementProps> {
98 if (!Object.prototype.hasOwnProperty.call(props, 'style')) return props;
99 const { style: _style, ...programProps } = props;
100 return programProps;
101 }
102
103 function finalizePibblElement<P>(
104 type: PibblElementType<P>,
105 props: P,
106 key: string | undefined,
107 metadata: Parameters<typeof setElementMetadata>[1],
108 ): PibblElement<P> {
109 const element = (key === undefined ?
110 { type, props } :
111 { type, props, key }) as PibblElement<P>;
112
113 setElementMetadata(element, metadata);
114 Object.defineProperties(element, {
115 $$typeof: {
116 configurable: false,
117 enumerable: false,
118 value: PIBBL_ELEMENT_TYPE,
119 writable: false,
120 },
121 });
122
123 capturePlacementHandoff(
124 element,
125 (props as { readonly style?: unknown }).style,
126 );
127 if (isDevelopment) {
128 Object.freeze(props);
129 Object.freeze(element);
130 }
131 return element;
132 }
133
134 /** @internal */
135 export function isPibblElement(value: unknown): value is PibblElement<any> {
136 return !!value &&
137 typeof value === 'object' &&
138 (value as { $$typeof?: unknown }).$$typeof === PIBBL_ELEMENT_TYPE;
139 }
140
141 /** @internal Creates another immutable descriptor while retaining private annotations. */
142 export function clonePibblElement<P>(element: PibblElement<P>): PibblElement<P> {
143 const metadata = getElementMetadata(element);
144 const clone = finalizePibblElement(
145 element.type,
146 element.props,
147 element.key,
148 metadata ? { ...metadata } : {},
149 );
150 copyElementCloneMetadata(element, clone);
151 return clone;
152 }
153
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.