@zeus-js/output-react-wrapper 0.1.0-beta.3 → 0.1.0-beta.5
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/dist/output-react-wrapper.cjs.js +294 -141
- package/dist/output-react-wrapper.cjs.prod.js +294 -141
- package/dist/output-react-wrapper.d.ts +44 -2
- package/dist/output-react-wrapper.esm-bundler.js +294 -142
- package/dist/runtime/index.cjs.js +57 -0
- package/dist/runtime/index.cjs.prod.js +57 -0
- package/dist/runtime/index.d.ts +39 -0
- package/dist/runtime/index.js +53 -0
- package/dist/runtime/index.prod.js +53 -0
- package/package.json +20 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* output-react-wrapper v0.1.0-beta.
|
|
2
|
+
* output-react-wrapper v0.1.0-beta.5
|
|
3
3
|
* (c) 2026 baicie
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
**/
|
|
@@ -9,6 +9,7 @@ Object.defineProperties(exports, {
|
|
|
9
9
|
});
|
|
10
10
|
let _zeus_js_bundler_plugin = require("@zeus-js/bundler-plugin");
|
|
11
11
|
let _zeus_js_component_dts = require("@zeus-js/component-dts");
|
|
12
|
+
let _lit_react = require("@lit/react");
|
|
12
13
|
//#region packages/web-c/output-react-wrapper/src/generateReactIndex.ts
|
|
13
14
|
function generateReactIndex(components, options) {
|
|
14
15
|
const lines = [];
|
|
@@ -20,211 +21,362 @@ function generateReactIndex(components, options) {
|
|
|
20
21
|
return lines.join("\n");
|
|
21
22
|
}
|
|
22
23
|
//#endregion
|
|
23
|
-
//#region packages/web-c/output-react-wrapper/src/naming.ts
|
|
24
|
-
function toReactEventProp(eventName) {
|
|
25
|
-
return "on" + eventName.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
26
|
-
}
|
|
27
|
-
//#endregion
|
|
28
24
|
//#region packages/web-c/output-react-wrapper/src/generateReactWrapper.ts
|
|
29
25
|
function generateReactWrapper(input) {
|
|
30
|
-
|
|
31
|
-
if (mode === "minimal") return generateMinimalReactWrapper(input);
|
|
32
|
-
return generateEventBridgeReactWrapper(input);
|
|
33
|
-
}
|
|
34
|
-
function createBindings(names, prefix) {
|
|
35
|
-
return names.map((sourceName, index) => ({
|
|
36
|
-
sourceName,
|
|
37
|
-
localName: `${prefix}${index}`
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
function createEventBindings(eventNames) {
|
|
41
|
-
return eventNames.map((eventName, index) => ({
|
|
42
|
-
eventName,
|
|
43
|
-
sourceName: toReactEventProp(eventName),
|
|
44
|
-
localName: `eventHandler${index}`
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
function generateDestructuredBindings(bindings) {
|
|
48
|
-
if (!bindings.length) return "";
|
|
49
|
-
return bindings.map(({ sourceName, localName }) => {
|
|
50
|
-
return `${JSON.stringify(sourceName)}: ${localName},`;
|
|
51
|
-
}).join("\n ");
|
|
26
|
+
return input.mode === "runtime" ? generateRuntimeReactWrapper(input) : input.mode === "event-bridge" ? generateEventBridgeReactWrapper(input) : generateMinimalReactWrapper(input);
|
|
52
27
|
}
|
|
53
28
|
function generateMinimalReactWrapper(input) {
|
|
54
29
|
const { component, namedSlots, wcModuleId } = input;
|
|
55
30
|
const slotBindings = createBindings(getNamedSlots(component, namedSlots), "slotValue");
|
|
56
|
-
const
|
|
31
|
+
const omittedKeys = ["children", ...slotBindings.map(({ sourceName }) => sourceName)];
|
|
32
|
+
const slotAssignments = generatePropAssignments(slotBindings);
|
|
57
33
|
const namedSlotLines = generateMinimalNamedSlots(slotBindings);
|
|
34
|
+
const childSetup = slotBindings.length ? `${namedSlotLines}
|
|
35
|
+
const childArgs = [];
|
|
36
|
+
pushAll(childArgs, slotNodes);
|
|
37
|
+
if (children != null) childArgs.push(children);` : "";
|
|
38
|
+
const render = slotBindings.length ? `React.createElement.apply(
|
|
39
|
+
React,
|
|
40
|
+
[${JSON.stringify(component.tag)}, rest].concat(childArgs),
|
|
41
|
+
)` : `React.createElement(${JSON.stringify(component.tag)}, rest, children)`;
|
|
58
42
|
return `
|
|
59
43
|
import * as React from 'react';
|
|
60
44
|
|
|
61
45
|
import ${JSON.stringify(wcModuleId)};
|
|
62
46
|
|
|
47
|
+
const OMITTED_PROPS = new Set(${JSON.stringify(omittedKeys)});
|
|
48
|
+
|
|
63
49
|
export const ${component.name} = React.forwardRef(
|
|
64
|
-
function ${component.name}({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
ref,
|
|
74
|
-
},
|
|
75
|
-
${namedSlotLines ? " ...slotNodes,\n" : ""} children,
|
|
76
|
-
);
|
|
50
|
+
function ${component.name}(inputProps, ref) {
|
|
51
|
+
const props = inputProps || {};
|
|
52
|
+
const children = props.children;
|
|
53
|
+
${slotAssignments}
|
|
54
|
+
const rest = omitProps(props);
|
|
55
|
+
rest.ref = ref;
|
|
56
|
+
${childSetup}
|
|
57
|
+
|
|
58
|
+
return ${render};
|
|
77
59
|
},
|
|
78
60
|
);
|
|
61
|
+
|
|
62
|
+
function omitProps(source) {
|
|
63
|
+
const output = {};
|
|
64
|
+
for (const key in source) {
|
|
65
|
+
if (hasOwn(source, key) && !OMITTED_PROPS.has(key)) {
|
|
66
|
+
output[key] = source[key];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return output;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function hasOwn(source, key) {
|
|
73
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
74
|
+
}
|
|
75
|
+
${slotBindings.length ? PUSH_ALL_HELPER : ""}
|
|
79
76
|
`.trimStart();
|
|
80
77
|
}
|
|
81
78
|
function generateEventBridgeReactWrapper(input) {
|
|
82
79
|
const { component, namedSlots, wcModuleId } = input;
|
|
83
80
|
const propBindings = createBindings(Object.keys(component.props), "propValue");
|
|
84
|
-
const eventBindings = createEventBindings(
|
|
81
|
+
const eventBindings = createEventBindings(component.events);
|
|
82
|
+
if (!propBindings.length && !eventBindings.length) return generateMinimalReactWrapper(input);
|
|
85
83
|
const slotBindings = createBindings(getNamedSlots(component, namedSlots), "slotValue");
|
|
86
|
-
const
|
|
84
|
+
const destructuredBindings = [
|
|
87
85
|
...propBindings,
|
|
88
86
|
...eventBindings,
|
|
89
87
|
...slotBindings
|
|
90
|
-
]
|
|
88
|
+
];
|
|
89
|
+
const omittedKeys = [
|
|
90
|
+
"children",
|
|
91
|
+
"className",
|
|
92
|
+
"style",
|
|
93
|
+
...destructuredBindings.map(({ sourceName }) => sourceName)
|
|
94
|
+
];
|
|
91
95
|
return `
|
|
92
96
|
import {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
${[
|
|
98
|
+
"createElement",
|
|
99
|
+
...slotBindings.length ? [
|
|
100
|
+
"cloneElement",
|
|
101
|
+
"Fragment",
|
|
102
|
+
"isValidElement"
|
|
103
|
+
] : [],
|
|
104
|
+
"forwardRef",
|
|
105
|
+
...propBindings.length || eventBindings.length ? ["useEffect"] : [],
|
|
106
|
+
"useImperativeHandle",
|
|
107
|
+
"useRef"
|
|
108
|
+
].join(",\n ")},
|
|
101
109
|
} from 'react';
|
|
102
110
|
|
|
103
111
|
import ${JSON.stringify(wcModuleId)};
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
children,
|
|
108
|
-
className,
|
|
109
|
-
style,
|
|
110
|
-
${destructuredProps}
|
|
111
|
-
...rest
|
|
112
|
-
} = props;
|
|
113
|
-
|
|
114
|
-
const innerRef = useRef(null);
|
|
115
|
-
const previousPropKeysRef = useRef(new Set());
|
|
116
|
-
|
|
117
|
-
useImperativeHandle(ref, () => innerRef.current);
|
|
118
|
-
|
|
119
|
-
${generatePropSyncLines(propBindings)}
|
|
120
|
-
|
|
121
|
-
${generateEventEffects(eventBindings)}
|
|
113
|
+
const OMITTED_PROPS = new Set(${JSON.stringify(omittedKeys)});
|
|
114
|
+
${eventBindings.length ? `const EVENT_NAMES = ${JSON.stringify(eventBindings.map((binding) => binding.eventName))};` : ""}
|
|
122
115
|
|
|
123
|
-
|
|
116
|
+
export const ${component.name} = forwardRef(function ${component.name}(inputProps, ref) {
|
|
117
|
+
const props = inputProps || {};
|
|
118
|
+
const children = props.children;
|
|
119
|
+
const className = props.className;
|
|
120
|
+
const style = props.style;
|
|
121
|
+
${generatePropAssignments(destructuredBindings)}
|
|
122
|
+
${generatePropPresenceAssignments(propBindings)}
|
|
123
|
+
const rest = omitProps(props);
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
const innerRef = useRef(null);
|
|
126
|
+
${generatePropRefs(propBindings)}
|
|
127
|
+
${generateEventRefs(eventBindings)}
|
|
128
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
${generatePropSyncEffect(propBindings)}
|
|
131
|
+
${generateEventEffect(eventBindings)}
|
|
132
|
+
${generateChildrenSetup(slotBindings)}
|
|
133
|
+
rest.ref = innerRef;
|
|
134
|
+
rest.className = className;
|
|
135
|
+
rest.style = style;
|
|
130
136
|
|
|
131
|
-
return
|
|
132
|
-
${JSON.stringify(component.tag)},
|
|
133
|
-
{
|
|
134
|
-
...rest,
|
|
135
|
-
ref: innerRef,
|
|
136
|
-
className,
|
|
137
|
-
style,
|
|
138
|
-
},
|
|
139
|
-
...slotChildren,
|
|
140
|
-
);
|
|
137
|
+
return ${generateReactRender(component.tag, slotBindings)};
|
|
141
138
|
});
|
|
142
|
-
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
) {
|
|
150
|
-
return cloneElement(value, { slot: name });
|
|
139
|
+
${slotBindings.length ? NAMED_SLOT_HELPER : ""}
|
|
140
|
+
function omitProps(source) {
|
|
141
|
+
const output = {};
|
|
142
|
+
for (const key in source) {
|
|
143
|
+
if (hasOwn(source, key) && !OMITTED_PROPS.has(key)) {
|
|
144
|
+
output[key] = source[key];
|
|
145
|
+
}
|
|
151
146
|
}
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
152
149
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
{
|
|
156
|
-
slot: name,
|
|
157
|
-
style: { display: 'contents' },
|
|
158
|
-
},
|
|
159
|
-
value,
|
|
160
|
-
);
|
|
150
|
+
function hasOwn(source, key) {
|
|
151
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
161
152
|
}
|
|
162
153
|
`.trimStart();
|
|
163
154
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
155
|
+
function createBindings(names, prefix) {
|
|
156
|
+
return names.map((sourceName, index) => ({
|
|
157
|
+
sourceName,
|
|
158
|
+
localName: `${prefix}${index}`
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
function createEventBindings(events) {
|
|
162
|
+
return Object.entries(events).map(([key, event], index) => {
|
|
163
|
+
var _event$key, _event$name, _event$reactName;
|
|
164
|
+
const sourceEventName = (_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key;
|
|
165
|
+
return {
|
|
166
|
+
eventName: (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase(sourceEventName),
|
|
167
|
+
sourceName: (_event$reactName = event.reactName) !== null && _event$reactName !== void 0 ? _event$reactName : toReactEventProp(sourceEventName),
|
|
168
|
+
localName: `eventHandler${index}`
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function generatePropAssignments(bindings) {
|
|
173
|
+
return bindings.map(({ sourceName, localName }) => ` const ${localName} = props[${JSON.stringify(sourceName)}];`).join("\n");
|
|
174
|
+
}
|
|
175
|
+
function generatePropPresenceAssignments(bindings) {
|
|
176
|
+
return bindings.map(({ sourceName }, index) => ` const propPresent${index} = hasOwn(props, ${JSON.stringify(sourceName)});`).join("\n");
|
|
177
|
+
}
|
|
178
|
+
function generatePropRefs(bindings) {
|
|
179
|
+
if (!bindings.length) return "";
|
|
180
|
+
return ` const previousPropPresenceRef = useRef([]);
|
|
181
|
+
const previousPropValuesRef = useRef([]);`;
|
|
182
|
+
}
|
|
183
|
+
function generateEventRefs(bindings) {
|
|
184
|
+
if (!bindings.length) return "";
|
|
185
|
+
return ` const eventHandlersRef = useRef([]);
|
|
186
|
+
${bindings.map(({ localName }, index) => ` eventHandlersRef.current[${index}] = ${localName};`).join("\n")}`;
|
|
187
|
+
}
|
|
188
|
+
function generatePropSyncEffect(bindings) {
|
|
189
|
+
if (!bindings.length) return "";
|
|
190
|
+
return ` useEffect(() => {
|
|
167
191
|
const el = innerRef.current;
|
|
168
192
|
if (!el) return;
|
|
169
193
|
|
|
170
|
-
const
|
|
194
|
+
const previousPropPresence = previousPropPresenceRef.current;
|
|
195
|
+
const previousPropValues = previousPropValuesRef.current;
|
|
171
196
|
|
|
172
|
-
|
|
197
|
+
${bindings.map(({ sourceName, localName }, index) => {
|
|
173
198
|
const key = JSON.stringify(sourceName);
|
|
174
|
-
return `if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
199
|
+
return ` if (propPresent${index}) {
|
|
200
|
+
if (
|
|
201
|
+
!previousPropPresence[${index}] ||
|
|
202
|
+
!Object.is(previousPropValues[${index}], ${localName})
|
|
203
|
+
) {
|
|
204
|
+
el[${key}] = ${localName};
|
|
205
|
+
previousPropValues[${index}] = ${localName};
|
|
206
|
+
}
|
|
207
|
+
previousPropPresence[${index}] = true;
|
|
208
|
+
} else if (previousPropPresence[${index}]) {
|
|
178
209
|
el[${key}] = undefined;
|
|
179
|
-
|
|
210
|
+
previousPropPresence[${index}] = false;
|
|
211
|
+
previousPropValues[${index}] = undefined;
|
|
180
212
|
}`;
|
|
181
|
-
}).join("\n\n
|
|
182
|
-
}, [
|
|
213
|
+
}).join("\n\n")}
|
|
214
|
+
}, [${bindings.flatMap((binding, index) => [`propPresent${index}`, binding.localName]).join(", ")}]);
|
|
215
|
+
`;
|
|
183
216
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
useEffect(() => {
|
|
217
|
+
function generateEventEffect(bindings) {
|
|
218
|
+
if (!bindings.length) return "";
|
|
219
|
+
return ` useEffect(() => {
|
|
188
220
|
const el = innerRef.current;
|
|
189
|
-
if (!el
|
|
221
|
+
if (!el) return;
|
|
190
222
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
223
|
+
const listeners = EVENT_NAMES.map(
|
|
224
|
+
(_eventName, index) => event => {
|
|
225
|
+
const handler = eventHandlersRef.current[index];
|
|
226
|
+
if (handler) handler(event);
|
|
227
|
+
},
|
|
228
|
+
);
|
|
194
229
|
|
|
195
|
-
|
|
230
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
231
|
+
el.addEventListener(EVENT_NAMES[index], listeners[index]);
|
|
232
|
+
}
|
|
196
233
|
|
|
197
234
|
return () => {
|
|
198
|
-
|
|
235
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
236
|
+
el.removeEventListener(EVENT_NAMES[index], listeners[index]);
|
|
237
|
+
}
|
|
199
238
|
};
|
|
200
|
-
}, [
|
|
239
|
+
}, []);
|
|
201
240
|
`;
|
|
202
|
-
}).join("");
|
|
203
|
-
}
|
|
204
|
-
function getNamedSlots(component, namedSlots) {
|
|
205
|
-
if (namedSlots === "none") return [];
|
|
206
|
-
return Object.keys(component.slots).filter((name) => name !== "default");
|
|
207
241
|
}
|
|
208
|
-
function
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
{
|
|
242
|
+
function generateChildrenSetup(bindings) {
|
|
243
|
+
if (!bindings.length) return "";
|
|
244
|
+
return ` const slotChildren = [];
|
|
245
|
+
${bindings.map(({ sourceName, localName }) => ` {
|
|
212
246
|
const node = createNamedSlot(${JSON.stringify(sourceName)}, ${localName});
|
|
213
247
|
if (node != null) slotChildren.push(node);
|
|
214
|
-
}
|
|
248
|
+
}`).join("\n")}
|
|
249
|
+
if (children != null) slotChildren.push(children);
|
|
215
250
|
`;
|
|
216
|
-
|
|
251
|
+
}
|
|
252
|
+
function generateReactRender(tag, slotBindings) {
|
|
253
|
+
if (!slotBindings.length) return `createElement(${JSON.stringify(tag)}, rest, children)`;
|
|
254
|
+
return `createElement.apply(
|
|
255
|
+
null,
|
|
256
|
+
[${JSON.stringify(tag)}, rest].concat(slotChildren),
|
|
257
|
+
)`;
|
|
258
|
+
}
|
|
259
|
+
function getNamedSlots(component, namedSlots) {
|
|
260
|
+
return namedSlots === "none" ? [] : Object.keys(component.slots).filter((name) => name !== "default");
|
|
217
261
|
}
|
|
218
262
|
function generateMinimalNamedSlots(bindings) {
|
|
219
263
|
if (!bindings.length) return "";
|
|
220
|
-
return bindings.map(({ sourceName, localName }, index) => {
|
|
221
|
-
return ` const
|
|
264
|
+
return `${bindings.map(({ sourceName, localName }, index) => {
|
|
265
|
+
return ` const slotNode${index} = ${localName} != null && ${localName} !== false
|
|
222
266
|
? (React.isValidElement(${localName}) && ${localName}.type !== React.Fragment
|
|
223
267
|
? React.cloneElement(${localName}, { slot: ${JSON.stringify(sourceName)} })
|
|
224
268
|
: React.createElement('span', { slot: ${JSON.stringify(sourceName)}, style: { display: 'contents' } }, ${localName}))
|
|
225
|
-
: null
|
|
269
|
+
: null;`;
|
|
270
|
+
}).join("\n")}
|
|
271
|
+
const slotNodes = [${bindings.map((_, index) => `slotNode${index}`).join(", ")}].filter(Boolean);`;
|
|
272
|
+
}
|
|
273
|
+
function toKebabCase(value) {
|
|
274
|
+
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
275
|
+
}
|
|
276
|
+
function toReactEventProp(value) {
|
|
277
|
+
return `on${value.split("-").filter(Boolean).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("")}`;
|
|
278
|
+
}
|
|
279
|
+
function generateRuntimeReactWrapper(input) {
|
|
280
|
+
const { component, namedSlots } = input;
|
|
281
|
+
const events = createRuntimeEventMap(component);
|
|
282
|
+
const slots = getNamedSlots(component, namedSlots);
|
|
283
|
+
return [
|
|
284
|
+
`import React from 'react'`,
|
|
285
|
+
`import { createComponent } from '@zeus-js/output-react-wrapper/runtime'`,
|
|
286
|
+
`import { defineCustomElement } from '../wc/loader.js'`,
|
|
287
|
+
``,
|
|
288
|
+
`export const ${component.name} = createComponent({`,
|
|
289
|
+
` react: React,`,
|
|
290
|
+
` tagName: ${JSON.stringify(component.tag)},`,
|
|
291
|
+
` defineCustomElement: () => defineCustomElement(${JSON.stringify(component.tag)}),`,
|
|
292
|
+
` events: ${formatEventObject(events)},`,
|
|
293
|
+
` slots: ${JSON.stringify(slots)},`,
|
|
294
|
+
` displayName: ${JSON.stringify(component.name)},`,
|
|
295
|
+
`})`,
|
|
296
|
+
``
|
|
297
|
+
].join("\n");
|
|
298
|
+
}
|
|
299
|
+
function createRuntimeEventMap(component) {
|
|
300
|
+
const events = {};
|
|
301
|
+
for (const [key, event] of Object.entries(component.events)) {
|
|
302
|
+
var _event$key2, _event$name2, _event$reactName2;
|
|
303
|
+
const sourceEventName = (_event$key2 = event.key) !== null && _event$key2 !== void 0 ? _event$key2 : key;
|
|
304
|
+
const domEventName = (_event$name2 = event.name) !== null && _event$name2 !== void 0 ? _event$name2 : toKebabCase(sourceEventName);
|
|
305
|
+
const reactPropName = (_event$reactName2 = event.reactName) !== null && _event$reactName2 !== void 0 ? _event$reactName2 : toReactEventProp(sourceEventName);
|
|
306
|
+
events[reactPropName] = domEventName;
|
|
307
|
+
}
|
|
308
|
+
return events;
|
|
309
|
+
}
|
|
310
|
+
function formatEventObject(value) {
|
|
311
|
+
const entries = Object.entries(value);
|
|
312
|
+
if (!entries.length) return "{}";
|
|
313
|
+
return `{\n${entries.map(([key, item]) => ` ${JSON.stringify(key)}: ${JSON.stringify(item)}`).join(",\n")}\n }`;
|
|
314
|
+
}
|
|
315
|
+
const PUSH_ALL_HELPER = `
|
|
316
|
+
function pushAll(target, values) {
|
|
317
|
+
for (const value of values) target.push(value);
|
|
318
|
+
}
|
|
319
|
+
`;
|
|
320
|
+
const NAMED_SLOT_HELPER = `
|
|
321
|
+
function createNamedSlot(name, value) {
|
|
322
|
+
if (value == null || value === false) return null;
|
|
323
|
+
|
|
324
|
+
if (isValidElement(value) && value.type !== Fragment) {
|
|
325
|
+
return cloneElement(value, { slot: name });
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return createElement(
|
|
329
|
+
'span',
|
|
330
|
+
{ slot: name, style: { display: 'contents' } },
|
|
331
|
+
value,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
226
334
|
`;
|
|
227
|
-
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region packages/web-c/output-react-wrapper/src/runtime/createComponent.ts
|
|
337
|
+
function createComponent(options) {
|
|
338
|
+
var _customElements$get;
|
|
339
|
+
const { defineCustomElement, react, tagName, transformTag, elementClass, events = {}, slots = [], displayName } = options;
|
|
340
|
+
const finalTagName = transformTag ? transformTag(tagName) : tagName;
|
|
341
|
+
defineCustomElement === null || defineCustomElement === void 0 || defineCustomElement();
|
|
342
|
+
const LitComponent = (0, _lit_react.createComponent)({
|
|
343
|
+
react,
|
|
344
|
+
tagName: finalTagName,
|
|
345
|
+
elementClass: elementClass !== null && elementClass !== void 0 ? elementClass : typeof customElements === "undefined" ? HTMLElement : (_customElements$get = customElements.get(finalTagName)) !== null && _customElements$get !== void 0 ? _customElements$get : HTMLElement,
|
|
346
|
+
events,
|
|
347
|
+
displayName
|
|
348
|
+
});
|
|
349
|
+
if (!slots.length) return LitComponent;
|
|
350
|
+
const slotSet = new Set(slots);
|
|
351
|
+
return react.forwardRef((inputProps, ref) => {
|
|
352
|
+
const rest = {};
|
|
353
|
+
const slottedChildren = [];
|
|
354
|
+
if (inputProps == null) {
|
|
355
|
+
rest.ref = ref;
|
|
356
|
+
return react.createElement(LitComponent, rest);
|
|
357
|
+
}
|
|
358
|
+
const props = inputProps;
|
|
359
|
+
for (const key in props) {
|
|
360
|
+
if (!Object.prototype.hasOwnProperty.call(props, key)) continue;
|
|
361
|
+
const value = props[key];
|
|
362
|
+
if (slotSet.has(key)) {
|
|
363
|
+
const child = createNamedSlot(react, key, value);
|
|
364
|
+
if (child != null) slottedChildren.push(child);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
rest[key] = value;
|
|
368
|
+
}
|
|
369
|
+
rest.ref = ref;
|
|
370
|
+
if (props.children != null) slottedChildren.push(props.children);
|
|
371
|
+
return react.createElement(LitComponent, rest, ...slottedChildren);
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
function createNamedSlot(react, name, value) {
|
|
375
|
+
if (value == null || value === false) return null;
|
|
376
|
+
return react.createElement("span", {
|
|
377
|
+
slot: name,
|
|
378
|
+
style: { display: "contents" }
|
|
379
|
+
}, value);
|
|
228
380
|
}
|
|
229
381
|
//#endregion
|
|
230
382
|
//#region packages/web-c/output-react-wrapper/src/index.ts
|
|
@@ -237,7 +389,7 @@ function reactWrapper(options = {}) {
|
|
|
237
389
|
dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
|
|
238
390
|
index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
|
|
239
391
|
namedSlots: (_options$namedSlots = options.namedSlots) !== null && _options$namedSlots !== void 0 ? _options$namedSlots : "props",
|
|
240
|
-
wrapper: (_options$wrapper = options.wrapper) !== null && _options$wrapper !== void 0 ? _options$wrapper : "
|
|
392
|
+
wrapper: (_options$wrapper = options.wrapper) !== null && _options$wrapper !== void 0 ? _options$wrapper : "runtime"
|
|
241
393
|
};
|
|
242
394
|
return {
|
|
243
395
|
name: "zeus-output-react-wrapper",
|
|
@@ -280,4 +432,5 @@ function reactWrapper(options = {}) {
|
|
|
280
432
|
};
|
|
281
433
|
}
|
|
282
434
|
//#endregion
|
|
435
|
+
exports.createComponent = createComponent;
|
|
283
436
|
exports.default = reactWrapper;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DtsMode, ZeusComponentPlugin } from '@zeus-js/bundler-plugin';
|
|
2
2
|
|
|
3
|
-
type ReactWrapperMode = 'minimal' | 'event-bridge';
|
|
3
|
+
type ReactWrapperMode = 'runtime' | 'minimal' | 'event-bridge';
|
|
4
4
|
export interface OutputReactWrapperOptions {
|
|
5
5
|
/**
|
|
6
6
|
* React wrapper output directory.
|
|
@@ -31,8 +31,12 @@ export interface OutputReactWrapperOptions {
|
|
|
31
31
|
*/
|
|
32
32
|
index?: boolean;
|
|
33
33
|
/**
|
|
34
|
+
* runtime:
|
|
35
|
+
* Default. Generates thin proxies powered by @zeus-js/output-react-wrapper/runtime.
|
|
36
|
+
* No useEffect, no addEventListener, no prop sync — delegates to @lit/react.
|
|
37
|
+
*
|
|
34
38
|
* minimal:
|
|
35
|
-
*
|
|
39
|
+
* Requires React 19+.
|
|
36
40
|
* React wrapper only renders the custom element tag.
|
|
37
41
|
* No useEffect prop sync, no event listeners.
|
|
38
42
|
*
|
|
@@ -55,6 +59,44 @@ export interface OutputReactWrapperOptions {
|
|
|
55
59
|
namedSlots?: 'props' | 'none';
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
export type EventName<T extends Event = Event> = string & {
|
|
63
|
+
__eventType: T;
|
|
64
|
+
};
|
|
65
|
+
type EventNames = Record<string, EventName | string>;
|
|
66
|
+
type ElementProps<I extends HTMLElement> = Partial<Omit<I, keyof HTMLElement>>;
|
|
67
|
+
type ReactRef<I extends HTMLElement> = ((instance: I | null) => void) | {
|
|
68
|
+
current: I | null;
|
|
69
|
+
} | null;
|
|
70
|
+
export interface ReactModule {
|
|
71
|
+
createElement: (...args: unknown[]) => unknown;
|
|
72
|
+
forwardRef: (...args: unknown[]) => unknown;
|
|
73
|
+
}
|
|
74
|
+
type EventListeners<E extends EventNames> = {
|
|
75
|
+
[K in keyof E]?: E[K] extends EventName<infer T> ? (event: T) => void : (event: Event) => void;
|
|
76
|
+
};
|
|
77
|
+
type ComponentProps<I extends HTMLElement, E extends EventNames> = {
|
|
78
|
+
children?: unknown;
|
|
79
|
+
className?: string;
|
|
80
|
+
ref?: ReactRef<I>;
|
|
81
|
+
style?: unknown;
|
|
82
|
+
} & {
|
|
83
|
+
[key: string]: unknown;
|
|
84
|
+
} & EventListeners<E> & ElementProps<I>;
|
|
85
|
+
export type ZeusReactComponent<I extends HTMLElement, E extends EventNames = {}> = (props: ComponentProps<I, E>) => unknown;
|
|
86
|
+
export interface ZeusReactCreateComponentOptions<I extends HTMLElement, E extends EventNames> {
|
|
87
|
+
tagName: string;
|
|
88
|
+
react: ReactModule;
|
|
89
|
+
defineCustomElement?: () => void;
|
|
90
|
+
elementClass?: {
|
|
91
|
+
new (): I;
|
|
92
|
+
};
|
|
93
|
+
events?: E;
|
|
94
|
+
slots?: string[];
|
|
95
|
+
displayName?: string;
|
|
96
|
+
transformTag?: (tagName: string) => string;
|
|
97
|
+
}
|
|
98
|
+
export declare function createComponent<I extends HTMLElement, E extends EventNames = {}>(options: ZeusReactCreateComponentOptions<I, E>): ZeusReactComponent<I, E>;
|
|
99
|
+
|
|
58
100
|
export declare function reactWrapper(options?: OutputReactWrapperOptions): ZeusComponentPlugin;
|
|
59
101
|
|
|
60
102
|
export { reactWrapper as default };
|