embedded-react 0.1.1 → 0.2.1
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/LICENSE +201 -201
- package/README.md +268 -238
- package/aot/compile.mjs +2 -2
- package/aot/screenshot-smoke.mjs +110 -110
- package/assets/emit-pack.mjs +1 -1
- package/cli.mjs +147 -0
- package/package.json +17 -7
- package/persist-transform.mjs +1 -1
- package/sim/embedded-react.js +2 -0
- package/sim/embedded-react.wasm +0 -0
- package/sim/index.html +387 -0
- package/sim-server.mjs +242 -0
- package/src/embedded-react/Animated.js +357 -352
- package/src/embedded-react/AppRegistry.js +49 -49
- package/src/embedded-react/Easing.js +39 -39
- package/src/embedded-react/LayoutAnimation.js +45 -45
- package/src/embedded-react/Platform.js +26 -26
- package/src/embedded-react/StyleSheet.js +36 -36
- package/src/embedded-react/components.js +44 -44
- package/src/embedded-react/index.js +52 -52
- package/src/embedded-react/layout-anim-config.js +91 -91
- package/src/embedded-react/split-style.js +58 -58
- package/src/embedded-react/usePersistentState.js +2 -2
- package/src/host-config.js +196 -196
- package/src/native-ui.js +24 -24
- package/src/props.js +183 -183
- package/src/renderer.js +57 -57
package/src/props.js
CHANGED
|
@@ -1,183 +1,183 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2026 Cory Lamming
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
// Pure prop-marshalling helpers. No engine / NativeUI dependency, so these are unit-testable in
|
|
18
|
-
// plain Node (see src/__tests__/props.unit.test.js). The host config wires them to NativeUI.
|
|
19
|
-
|
|
20
|
-
// Top-level props NativeUI understands directly (not part of `style`). Everything else style-ish is
|
|
21
|
-
// expected inside props.style. Event handlers (on*) are routed separately via setEvent.
|
|
22
|
-
export const PASSTHROUGH = [
|
|
23
|
-
'numberOfLines',
|
|
24
|
-
'ellipsizeMode',
|
|
25
|
-
'value',
|
|
26
|
-
'placeholder',
|
|
27
|
-
'placeholderTextColor',
|
|
28
|
-
'editable',
|
|
29
|
-
'animating',
|
|
30
|
-
'visible',
|
|
31
|
-
'resizeMode',
|
|
32
|
-
'tintColor',
|
|
33
|
-
'imageName',
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Flattens RN-style `style` (object or nested array) into the `out` object.
|
|
38
|
-
*/
|
|
39
|
-
export function flattenStyle(style, out) {
|
|
40
|
-
if (!style) return;
|
|
41
|
-
if (Array.isArray(style)) {
|
|
42
|
-
for (const s of style) flattenStyle(s, out);
|
|
43
|
-
} else {
|
|
44
|
-
Object.assign(out, style);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Flattens a style (object/array) into a fresh plain object. */
|
|
49
|
-
export function flattenStyleObj(style) {
|
|
50
|
-
const out = {};
|
|
51
|
-
flattenStyle(style, out);
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Style fields the engine honors per inline text span (everything else inherits from the node).
|
|
56
|
-
const SPAN_STYLE_KEYS = ['color', 'fontSize', 'fontWeight', 'fontStyle', 'textDecorationLine', 'letterSpacing'];
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* True when a <Text>'s children can be flattened inline — i.e. every leaf is a string/number or a
|
|
60
|
-
* nested <Text> element. A non-Text element (View, a component) makes it false, so the reconciler
|
|
61
|
-
* falls back to mounting children as separate instances.
|
|
62
|
-
*/
|
|
63
|
-
export function isTextContent(children) {
|
|
64
|
-
if (children == null || children === false || children === true) return true;
|
|
65
|
-
if (typeof children === 'string' || typeof children === 'number') return true;
|
|
66
|
-
if (Array.isArray(children)) return children.every(isTextContent);
|
|
67
|
-
// A React element: only nested <Text> participates in inline flattening (type is the 'Text' tag).
|
|
68
|
-
if (children && children.props !== undefined) return children.type === 'Text';
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Walks a <Text>'s children into an ordered list of { text, style } segments. Primitive siblings
|
|
74
|
-
* share the parent's (base) style object by reference; a nested <Text> with its own style produces
|
|
75
|
-
* a new merged style object. Adjacent segments with the same style reference are coalesced — so
|
|
76
|
-
* plain interpolation like `Hi {name}` collapses to a single segment (no spans needed).
|
|
77
|
-
*/
|
|
78
|
-
export function flattenTextChildren(children, baseStyle) {
|
|
79
|
-
const segments = [];
|
|
80
|
-
const push = (text, style) => {
|
|
81
|
-
if (text === '') return;
|
|
82
|
-
const last = segments[segments.length - 1];
|
|
83
|
-
if (last && last.style === style) last.text += text;
|
|
84
|
-
else segments.push({ text, style });
|
|
85
|
-
};
|
|
86
|
-
const walk = (node, style) => {
|
|
87
|
-
if (node == null || node === false || node === true) return;
|
|
88
|
-
if (typeof node === 'string' || typeof node === 'number') {
|
|
89
|
-
push(String(node), style);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (Array.isArray(node)) {
|
|
93
|
-
for (const c of node) walk(c, style);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (node && node.props !== undefined) {
|
|
97
|
-
// Nested <Text>: merge its style over the inherited one (new object only when it has style,
|
|
98
|
-
// so an unstyled nested <Text> keeps the same reference and still coalesces with siblings).
|
|
99
|
-
const merged = node.props.style ? Object.assign({}, style, flattenStyleObj(node.props.style)) : style;
|
|
100
|
-
walk(node.props.children, merged);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
walk(children, baseStyle);
|
|
104
|
-
return segments;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Builds the inline-span array for a <Text> (for NativeUI.setTextSpans). Returns [] when the content
|
|
109
|
-
* is uniformly the base style (plain text — no spans needed); otherwise one span per styled segment,
|
|
110
|
-
* each carrying only the span-relevant style keys (the engine inherits the rest from the node).
|
|
111
|
-
*/
|
|
112
|
-
export function buildTextSpans(props) {
|
|
113
|
-
if (!isTextContent(props.children)) return [];
|
|
114
|
-
const baseStyle = flattenStyleObj(props.style);
|
|
115
|
-
const segments = flattenTextChildren(props.children, baseStyle);
|
|
116
|
-
if (!segments.some((s) => s.style !== baseStyle)) return [];
|
|
117
|
-
return segments.map((s) => {
|
|
118
|
-
const span = { text: s.text };
|
|
119
|
-
for (const k of SPAN_STYLE_KEYS) {
|
|
120
|
-
if (s.style && s.style[k] !== undefined) span[k] = s.style[k];
|
|
121
|
-
}
|
|
122
|
-
return span;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Builds the flat prop bag NativeUI.setProps expects: resolved style + passthrough props + text.
|
|
128
|
-
*/
|
|
129
|
-
/**
|
|
130
|
-
* Resolves an <Image source> to the engine asset name (the string registered via er_image_load and
|
|
131
|
-
* stored on the node as imageName). Accepts a bare name string — which is also what the bundler's
|
|
132
|
-
* image plugin turns `import logo from './logo.png'` into (the file's basename) — or an RN-style
|
|
133
|
-
* { uri } object. Returns null for anything unresolvable (e.g., a numeric require() id).
|
|
134
|
-
*/
|
|
135
|
-
export function resolveImageSource(source) {
|
|
136
|
-
if (typeof source === 'string') return source;
|
|
137
|
-
if (source && typeof source === 'object' && typeof source.uri === 'string') return source.uri;
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function buildProps(type, props) {
|
|
142
|
-
const flat = {};
|
|
143
|
-
flattenStyle(props.style, flat);
|
|
144
|
-
for (const k of PASSTHROUGH) {
|
|
145
|
-
if (props[k] !== undefined) flat[k] = props[k];
|
|
146
|
-
}
|
|
147
|
-
// <Svg> takes width/height as DIRECT props (the react-native-svg convention) — the engine sizes the
|
|
148
|
-
// vector node from them. Fold them into the resolved style (an explicit style width/height still wins)
|
|
149
|
-
// so Flow A matches the Flow B AOT, which reads svg.props.width/height directly (compile.mjs emitSvgBox).
|
|
150
|
-
if (type === 'Svg') {
|
|
151
|
-
if (flat.width === undefined && props.width !== undefined) flat.width = props.width;
|
|
152
|
-
if (flat.height === undefined && props.height !== undefined) flat.height = props.height;
|
|
153
|
-
}
|
|
154
|
-
// <Image source={...}> resolves to the engine asset name unless imageName was set explicitly.
|
|
155
|
-
if (flat.imageName === undefined && props.source != null) {
|
|
156
|
-
const name = resolveImageSource(props.source);
|
|
157
|
-
if (name != null) flat.imageName = name;
|
|
158
|
-
}
|
|
159
|
-
// <Text> content: flatten string/number/nested-<Text> children into the node's full-text string.
|
|
160
|
-
// The engine renders this when no spans are set; with spans (buildTextSpans) it carries the same
|
|
161
|
-
// text for the plain-text fallback. Non-flattenable children fall back to mounted child instances.
|
|
162
|
-
if (type === 'Text' && isTextContent(props.children)) {
|
|
163
|
-
const base = flattenStyleObj(props.style);
|
|
164
|
-
flat.text = flattenTextChildren(props.children, base)
|
|
165
|
-
.map((s) => s.text)
|
|
166
|
-
.join('');
|
|
167
|
-
}
|
|
168
|
-
return flat;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* True for an `onXxx` prop whose value is a function (an event handler to route via setEvent).
|
|
173
|
-
*/
|
|
174
|
-
export function isEventProp(key, value) {
|
|
175
|
-
return (
|
|
176
|
-
key.length > 2 &&
|
|
177
|
-
key[0] === 'o' &&
|
|
178
|
-
key[1] === 'n' &&
|
|
179
|
-
key[2] >= 'A' &&
|
|
180
|
-
key[2] <= 'Z' &&
|
|
181
|
-
typeof value === 'function'
|
|
182
|
-
);
|
|
183
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Cory Lamming
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Pure prop-marshalling helpers. No engine / NativeUI dependency, so these are unit-testable in
|
|
18
|
+
// plain Node (see src/__tests__/props.unit.test.js). The host config wires them to NativeUI.
|
|
19
|
+
|
|
20
|
+
// Top-level props NativeUI understands directly (not part of `style`). Everything else style-ish is
|
|
21
|
+
// expected inside props.style. Event handlers (on*) are routed separately via setEvent.
|
|
22
|
+
export const PASSTHROUGH = [
|
|
23
|
+
'numberOfLines',
|
|
24
|
+
'ellipsizeMode',
|
|
25
|
+
'value',
|
|
26
|
+
'placeholder',
|
|
27
|
+
'placeholderTextColor',
|
|
28
|
+
'editable',
|
|
29
|
+
'animating',
|
|
30
|
+
'visible',
|
|
31
|
+
'resizeMode',
|
|
32
|
+
'tintColor',
|
|
33
|
+
'imageName',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Flattens RN-style `style` (object or nested array) into the `out` object.
|
|
38
|
+
*/
|
|
39
|
+
export function flattenStyle(style, out) {
|
|
40
|
+
if (!style) return;
|
|
41
|
+
if (Array.isArray(style)) {
|
|
42
|
+
for (const s of style) flattenStyle(s, out);
|
|
43
|
+
} else {
|
|
44
|
+
Object.assign(out, style);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Flattens a style (object/array) into a fresh plain object. */
|
|
49
|
+
export function flattenStyleObj(style) {
|
|
50
|
+
const out = {};
|
|
51
|
+
flattenStyle(style, out);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Style fields the engine honors per inline text span (everything else inherits from the node).
|
|
56
|
+
const SPAN_STYLE_KEYS = ['color', 'fontSize', 'fontWeight', 'fontStyle', 'textDecorationLine', 'letterSpacing'];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* True when a <Text>'s children can be flattened inline — i.e. every leaf is a string/number or a
|
|
60
|
+
* nested <Text> element. A non-Text element (View, a component) makes it false, so the reconciler
|
|
61
|
+
* falls back to mounting children as separate instances.
|
|
62
|
+
*/
|
|
63
|
+
export function isTextContent(children) {
|
|
64
|
+
if (children == null || children === false || children === true) return true;
|
|
65
|
+
if (typeof children === 'string' || typeof children === 'number') return true;
|
|
66
|
+
if (Array.isArray(children)) return children.every(isTextContent);
|
|
67
|
+
// A React element: only nested <Text> participates in inline flattening (type is the 'Text' tag).
|
|
68
|
+
if (children && children.props !== undefined) return children.type === 'Text';
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Walks a <Text>'s children into an ordered list of { text, style } segments. Primitive siblings
|
|
74
|
+
* share the parent's (base) style object by reference; a nested <Text> with its own style produces
|
|
75
|
+
* a new merged style object. Adjacent segments with the same style reference are coalesced — so
|
|
76
|
+
* plain interpolation like `Hi {name}` collapses to a single segment (no spans needed).
|
|
77
|
+
*/
|
|
78
|
+
export function flattenTextChildren(children, baseStyle) {
|
|
79
|
+
const segments = [];
|
|
80
|
+
const push = (text, style) => {
|
|
81
|
+
if (text === '') return;
|
|
82
|
+
const last = segments[segments.length - 1];
|
|
83
|
+
if (last && last.style === style) last.text += text;
|
|
84
|
+
else segments.push({ text, style });
|
|
85
|
+
};
|
|
86
|
+
const walk = (node, style) => {
|
|
87
|
+
if (node == null || node === false || node === true) return;
|
|
88
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
89
|
+
push(String(node), style);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(node)) {
|
|
93
|
+
for (const c of node) walk(c, style);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (node && node.props !== undefined) {
|
|
97
|
+
// Nested <Text>: merge its style over the inherited one (new object only when it has style,
|
|
98
|
+
// so an unstyled nested <Text> keeps the same reference and still coalesces with siblings).
|
|
99
|
+
const merged = node.props.style ? Object.assign({}, style, flattenStyleObj(node.props.style)) : style;
|
|
100
|
+
walk(node.props.children, merged);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
walk(children, baseStyle);
|
|
104
|
+
return segments;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Builds the inline-span array for a <Text> (for NativeUI.setTextSpans). Returns [] when the content
|
|
109
|
+
* is uniformly the base style (plain text — no spans needed); otherwise one span per styled segment,
|
|
110
|
+
* each carrying only the span-relevant style keys (the engine inherits the rest from the node).
|
|
111
|
+
*/
|
|
112
|
+
export function buildTextSpans(props) {
|
|
113
|
+
if (!isTextContent(props.children)) return [];
|
|
114
|
+
const baseStyle = flattenStyleObj(props.style);
|
|
115
|
+
const segments = flattenTextChildren(props.children, baseStyle);
|
|
116
|
+
if (!segments.some((s) => s.style !== baseStyle)) return [];
|
|
117
|
+
return segments.map((s) => {
|
|
118
|
+
const span = { text: s.text };
|
|
119
|
+
for (const k of SPAN_STYLE_KEYS) {
|
|
120
|
+
if (s.style && s.style[k] !== undefined) span[k] = s.style[k];
|
|
121
|
+
}
|
|
122
|
+
return span;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Builds the flat prop bag NativeUI.setProps expects: resolved style + passthrough props + text.
|
|
128
|
+
*/
|
|
129
|
+
/**
|
|
130
|
+
* Resolves an <Image source> to the engine asset name (the string registered via er_image_load and
|
|
131
|
+
* stored on the node as imageName). Accepts a bare name string — which is also what the bundler's
|
|
132
|
+
* image plugin turns `import logo from './logo.png'` into (the file's basename) — or an RN-style
|
|
133
|
+
* { uri } object. Returns null for anything unresolvable (e.g., a numeric require() id).
|
|
134
|
+
*/
|
|
135
|
+
export function resolveImageSource(source) {
|
|
136
|
+
if (typeof source === 'string') return source;
|
|
137
|
+
if (source && typeof source === 'object' && typeof source.uri === 'string') return source.uri;
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function buildProps(type, props) {
|
|
142
|
+
const flat = {};
|
|
143
|
+
flattenStyle(props.style, flat);
|
|
144
|
+
for (const k of PASSTHROUGH) {
|
|
145
|
+
if (props[k] !== undefined) flat[k] = props[k];
|
|
146
|
+
}
|
|
147
|
+
// <Svg> takes width/height as DIRECT props (the react-native-svg convention) — the engine sizes the
|
|
148
|
+
// vector node from them. Fold them into the resolved style (an explicit style width/height still wins)
|
|
149
|
+
// so Flow A matches the Flow B AOT, which reads svg.props.width/height directly (compile.mjs emitSvgBox).
|
|
150
|
+
if (type === 'Svg') {
|
|
151
|
+
if (flat.width === undefined && props.width !== undefined) flat.width = props.width;
|
|
152
|
+
if (flat.height === undefined && props.height !== undefined) flat.height = props.height;
|
|
153
|
+
}
|
|
154
|
+
// <Image source={...}> resolves to the engine asset name unless imageName was set explicitly.
|
|
155
|
+
if (flat.imageName === undefined && props.source != null) {
|
|
156
|
+
const name = resolveImageSource(props.source);
|
|
157
|
+
if (name != null) flat.imageName = name;
|
|
158
|
+
}
|
|
159
|
+
// <Text> content: flatten string/number/nested-<Text> children into the node's full-text string.
|
|
160
|
+
// The engine renders this when no spans are set; with spans (buildTextSpans) it carries the same
|
|
161
|
+
// text for the plain-text fallback. Non-flattenable children fall back to mounted child instances.
|
|
162
|
+
if (type === 'Text' && isTextContent(props.children)) {
|
|
163
|
+
const base = flattenStyleObj(props.style);
|
|
164
|
+
flat.text = flattenTextChildren(props.children, base)
|
|
165
|
+
.map((s) => s.text)
|
|
166
|
+
.join('');
|
|
167
|
+
}
|
|
168
|
+
return flat;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* True for an `onXxx` prop whose value is a function (an event handler to route via setEvent).
|
|
173
|
+
*/
|
|
174
|
+
export function isEventProp(key, value) {
|
|
175
|
+
return (
|
|
176
|
+
key.length > 2 &&
|
|
177
|
+
key[0] === 'o' &&
|
|
178
|
+
key[1] === 'n' &&
|
|
179
|
+
key[2] >= 'A' &&
|
|
180
|
+
key[2] <= 'Z' &&
|
|
181
|
+
typeof value === 'function'
|
|
182
|
+
);
|
|
183
|
+
}
|
package/src/renderer.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2026 Cory Lamming
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
// Public renderer API: createRoot(...).render(<App/>).
|
|
18
|
-
//
|
|
19
|
-
// The container is a real engine View node set as the scene root; React children mount into it.
|
|
20
|
-
// We use LegacyRoot (synchronous) mode so the initial render flushes without depending on the
|
|
21
|
-
// async scheduler/timers — the host's frame loop then paints whatever the tree became.
|
|
22
|
-
import Reconciler from 'react-reconciler';
|
|
23
|
-
import { hostConfig } from './host-config.js';
|
|
24
|
-
import { NativeUI } from './native-ui.js';
|
|
25
|
-
|
|
26
|
-
const reconciler = Reconciler(hostConfig);
|
|
27
|
-
|
|
28
|
-
const LegacyRoot = 0;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Creates a root bound to a screen-sized container node.
|
|
32
|
-
*
|
|
33
|
-
* @param {object} containerProps Props for the container View (e.g. width/height/backgroundColor).
|
|
34
|
-
* @returns {{ render: (element: any) => void }}
|
|
35
|
-
*/
|
|
36
|
-
export function createRoot(containerProps) {
|
|
37
|
-
const container = NativeUI.createNode('View');
|
|
38
|
-
NativeUI.setProps(container, containerProps || {});
|
|
39
|
-
NativeUI.setRoot(container);
|
|
40
|
-
|
|
41
|
-
const fiberRoot = reconciler.createContainer(
|
|
42
|
-
container, // containerInfo — our root node handle
|
|
43
|
-
LegacyRoot,
|
|
44
|
-
null, // hydration callbacks
|
|
45
|
-
false, // isStrictMode
|
|
46
|
-
null, // concurrentUpdatesByDefaultOverride
|
|
47
|
-
'', // identifierPrefix
|
|
48
|
-
(error) => console.error('react recoverable error:', error),
|
|
49
|
-
null, // transitionCallbacks
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
render(element) {
|
|
54
|
-
reconciler.updateContainer(element, fiberRoot, null, null);
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Cory Lamming
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Public renderer API: createRoot(...).render(<App/>).
|
|
18
|
+
//
|
|
19
|
+
// The container is a real engine View node set as the scene root; React children mount into it.
|
|
20
|
+
// We use LegacyRoot (synchronous) mode so the initial render flushes without depending on the
|
|
21
|
+
// async scheduler/timers — the host's frame loop then paints whatever the tree became.
|
|
22
|
+
import Reconciler from 'react-reconciler';
|
|
23
|
+
import { hostConfig } from './host-config.js';
|
|
24
|
+
import { NativeUI } from './native-ui.js';
|
|
25
|
+
|
|
26
|
+
const reconciler = Reconciler(hostConfig);
|
|
27
|
+
|
|
28
|
+
const LegacyRoot = 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a root bound to a screen-sized container node.
|
|
32
|
+
*
|
|
33
|
+
* @param {object} containerProps Props for the container View (e.g. width/height/backgroundColor).
|
|
34
|
+
* @returns {{ render: (element: any) => void }}
|
|
35
|
+
*/
|
|
36
|
+
export function createRoot(containerProps) {
|
|
37
|
+
const container = NativeUI.createNode('View');
|
|
38
|
+
NativeUI.setProps(container, containerProps || {});
|
|
39
|
+
NativeUI.setRoot(container);
|
|
40
|
+
|
|
41
|
+
const fiberRoot = reconciler.createContainer(
|
|
42
|
+
container, // containerInfo — our root node handle
|
|
43
|
+
LegacyRoot,
|
|
44
|
+
null, // hydration callbacks
|
|
45
|
+
false, // isStrictMode
|
|
46
|
+
null, // concurrentUpdatesByDefaultOverride
|
|
47
|
+
'', // identifierPrefix
|
|
48
|
+
(error) => console.error('react recoverable error:', error),
|
|
49
|
+
null, // transitionCallbacks
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
render(element) {
|
|
54
|
+
reconciler.updateContainer(element, fiberRoot, null, null);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|