@zeus-js/output-vue-wrapper 0.1.0-beta.3 → 0.1.0-beta.4
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-vue-wrapper.cjs.js +182 -138
- package/dist/output-vue-wrapper.cjs.prod.js +182 -138
- package/dist/output-vue-wrapper.esm-bundler.js +182 -138
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* output-vue-wrapper v0.1.0-beta.
|
|
2
|
+
* output-vue-wrapper v0.1.0-beta.4
|
|
3
3
|
* (c) 2026 baicie
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
**/
|
|
@@ -22,163 +22,192 @@ function generateVueIndex(components, options) {
|
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region packages/web-c/output-vue-wrapper/src/generateVueWrapper.ts
|
|
24
24
|
function generateVueWrapper(input) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return generateEventBridgeVueWrapper(input);
|
|
25
|
+
var _input$component$mode;
|
|
26
|
+
return input.mode === "event-bridge" || ((_input$component$mode = input.component.models) === null || _input$component$mode === void 0 ? void 0 : _input$component$mode.length) ? generateEventBridgeVueWrapper(input) : generateMinimalVueWrapper(input);
|
|
28
27
|
}
|
|
29
28
|
function generateMinimalVueWrapper(input) {
|
|
30
29
|
const { component, wcModuleId } = input;
|
|
31
|
-
const slotNames =
|
|
30
|
+
const slotNames = getNamedSlots(component);
|
|
32
31
|
const hasNamedSlots = slotNames.length > 0;
|
|
33
|
-
const vueImports = hasNamedSlots ? `cloneVNode, defineComponent, h` : `defineComponent, h`;
|
|
34
|
-
const namedSlotBlock = hasNamedSlots ? `
|
|
35
|
-
for (const name of NAMED_SLOTS) {
|
|
36
|
-
const slot = slots[name];
|
|
37
|
-
if (!slot) continue;
|
|
38
|
-
|
|
39
|
-
for (const vnode of slot()) {
|
|
40
|
-
children.push(withSlot(name, vnode));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
` : "";
|
|
44
|
-
const withSlotHelper = hasNamedSlots ? `
|
|
45
|
-
function withSlot(name, vnode) {
|
|
46
|
-
if (!vnode) return vnode;
|
|
47
|
-
|
|
48
|
-
if (typeof vnode === 'string') {
|
|
49
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return cloneVNode(vnode, { slot: name });
|
|
53
|
-
}
|
|
54
|
-
` : "";
|
|
55
32
|
return `
|
|
56
|
-
import { ${
|
|
33
|
+
import { ${hasNamedSlots ? "cloneVNode, defineComponent, h" : "defineComponent, h"} } from 'vue';
|
|
57
34
|
|
|
58
35
|
import ${JSON.stringify(wcModuleId)};
|
|
59
|
-
|
|
60
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
61
|
-
|
|
36
|
+
${hasNamedSlots ? `\nconst NAMED_SLOTS = ${JSON.stringify(slotNames)};\n` : ""}
|
|
62
37
|
export const ${component.name} = defineComponent({
|
|
63
38
|
name: ${JSON.stringify(component.name)},
|
|
64
39
|
inheritAttrs: false,
|
|
65
40
|
|
|
66
41
|
setup(_props, { attrs, slots }) {
|
|
67
42
|
return () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (slots.default) {
|
|
71
|
-
children.push(...slots.default());
|
|
72
|
-
}
|
|
73
|
-
${namedSlotBlock}
|
|
74
|
-
return h(
|
|
75
|
-
${JSON.stringify(component.tag)},
|
|
76
|
-
{
|
|
77
|
-
...attrs,
|
|
78
|
-
},
|
|
79
|
-
children,
|
|
80
|
-
);
|
|
43
|
+
${generateVueChildren(slotNames)}
|
|
44
|
+
return h(${JSON.stringify(component.tag)}, attrs, children);
|
|
81
45
|
};
|
|
82
46
|
},
|
|
83
47
|
});
|
|
84
|
-
${
|
|
48
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
85
49
|
`.trimStart();
|
|
86
50
|
}
|
|
87
51
|
function generateEventBridgeVueWrapper(input) {
|
|
88
52
|
const { component, wcModuleId } = input;
|
|
89
|
-
const
|
|
90
|
-
const eventNames =
|
|
91
|
-
|
|
92
|
-
const
|
|
53
|
+
const capabilities = getCapabilities(component);
|
|
54
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
55
|
+
if (!propNames.length && !eventNames.length) return generateMinimalVueWrapper(input);
|
|
56
|
+
const hasProps = propNames.length > 0;
|
|
57
|
+
const hasEvents = eventNames.length > 0;
|
|
58
|
+
const emitNames = Array.from(new Set([...eventNames, ...models.map((model) => model.updateEvent)]));
|
|
59
|
+
const hasNamedSlots = slotNames.length > 0;
|
|
93
60
|
return `
|
|
94
61
|
import {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
62
|
+
${[
|
|
63
|
+
...hasNamedSlots ? ["cloneVNode"] : [],
|
|
64
|
+
"defineComponent",
|
|
65
|
+
...hasProps ? ["getCurrentInstance"] : [],
|
|
66
|
+
"h",
|
|
67
|
+
...hasEvents ? ["onBeforeUnmount"] : [],
|
|
68
|
+
...hasProps || hasEvents ? ["onMounted"] : [],
|
|
69
|
+
...hasProps ? ["onUpdated"] : [],
|
|
70
|
+
"ref"
|
|
71
|
+
].join(",\n ")},
|
|
103
72
|
} from 'vue';
|
|
104
73
|
|
|
105
74
|
import ${JSON.stringify(wcModuleId)};
|
|
106
|
-
|
|
107
|
-
const PROP_KEYS = ${JSON.stringify(propNames)};
|
|
108
|
-
const PROP_INPUT_KEYS = ${JSON.stringify(propInputKeys)};
|
|
109
|
-
const EVENT_NAMES = ${JSON.stringify(eventNames)};
|
|
110
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
111
|
-
|
|
75
|
+
${generateVueConstants(capabilities)}
|
|
112
76
|
export const ${component.name} = defineComponent({
|
|
113
77
|
name: ${JSON.stringify(component.name)},
|
|
114
78
|
inheritAttrs: false,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
emits: EVENT_NAMES,
|
|
121
|
-
|
|
122
|
-
setup(props, { attrs, slots, emit }) {
|
|
79
|
+
${hasProps ? `\n props: {\n ${generateVueProps(component)}\n },\n` : ""}
|
|
80
|
+
${hasEvents ? ` emits: ${models.length ? JSON.stringify(emitNames) : "EVENT_NAMES"},\n` : ""}
|
|
81
|
+
setup(${hasProps ? "props" : "_props"}, { attrs, slots${hasEvents ? ", emit" : ""} }) {
|
|
123
82
|
const elRef = ref(null);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
83
|
+
${generateVuePropSetup(hasProps)}
|
|
84
|
+
${generateVueEventSetup(hasEvents, models.length > 0)}
|
|
85
|
+
${generateVueMountHook(hasProps, hasEvents)}
|
|
86
|
+
${hasProps ? " onUpdated(syncProps);\n" : ""}
|
|
87
|
+
${generateVueUnmountHook(hasEvents)}
|
|
88
|
+
return () => {
|
|
89
|
+
${generateVueChildren(slotNames)}
|
|
90
|
+
const hostProps = Object.assign({}, attrs);
|
|
91
|
+
hostProps.ref = elRef;
|
|
131
92
|
|
|
132
|
-
return
|
|
133
|
-
Object.prototype.hasOwnProperty.call(rawProps, key),
|
|
134
|
-
);
|
|
93
|
+
return h(${JSON.stringify(component.tag)}, hostProps, children);
|
|
135
94
|
};
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
${hasProps ? VUE_PROP_HELPERS : ""}
|
|
98
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
99
|
+
${models.length ? VUE_MODEL_HELPERS : ""}
|
|
100
|
+
`.trimStart();
|
|
101
|
+
}
|
|
102
|
+
function getCapabilities(component) {
|
|
103
|
+
var _component$models;
|
|
104
|
+
const models = ((_component$models = component.models) !== null && _component$models !== void 0 ? _component$models : []).map((model) => ({
|
|
105
|
+
event: model.event,
|
|
106
|
+
eventPath: model.eventPath,
|
|
107
|
+
updateEvent: `update:${model.prop}`
|
|
108
|
+
}));
|
|
109
|
+
return {
|
|
110
|
+
propNames: Object.keys(component.props),
|
|
111
|
+
eventNames: Array.from(new Set([...Object.entries(component.events).map(([key, event]) => {
|
|
112
|
+
var _event$name, _event$key;
|
|
113
|
+
return (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key);
|
|
114
|
+
}), ...models.map((model) => model.event)])),
|
|
115
|
+
models,
|
|
116
|
+
slotNames: getNamedSlots(component)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function getNamedSlots(component) {
|
|
120
|
+
return Object.keys(component.slots).filter((name) => name !== "default");
|
|
121
|
+
}
|
|
122
|
+
function generateVueConstants(capabilities) {
|
|
123
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
124
|
+
const lines = [];
|
|
125
|
+
if (propNames.length) {
|
|
126
|
+
lines.push(`const PROP_KEYS = ${JSON.stringify(propNames)};`);
|
|
127
|
+
lines.push(`const PROP_INPUT_KEYS = ${JSON.stringify(createVuePropInputKeys(propNames))};`);
|
|
128
|
+
lines.push("const EMPTY_PROPS = {};");
|
|
129
|
+
}
|
|
130
|
+
if (eventNames.length) lines.push(`const EVENT_NAMES = ${JSON.stringify(eventNames)};`);
|
|
131
|
+
if (models.length) lines.push(`const MODEL_BINDINGS = ${JSON.stringify(models)};`);
|
|
132
|
+
if (slotNames.length) lines.push(`const NAMED_SLOTS = ${JSON.stringify(slotNames)};`);
|
|
133
|
+
return lines.length ? `${lines.join("\n")}\n` : "";
|
|
134
|
+
}
|
|
135
|
+
function generateVuePropSetup(hasProps) {
|
|
136
|
+
if (!hasProps) return "";
|
|
137
|
+
return ` const instance = getCurrentInstance();
|
|
138
|
+
const syncedPropPresence = [];
|
|
139
|
+
const syncedPropValues = [];
|
|
136
140
|
|
|
137
141
|
const syncProps = () => {
|
|
138
142
|
const el = elRef.value;
|
|
139
143
|
if (!el) return;
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
const rawProps = instance?.vnode.props || EMPTY_PROPS;
|
|
146
|
+
|
|
147
|
+
for (let index = 0; index < PROP_KEYS.length; index += 1) {
|
|
148
|
+
const name = PROP_KEYS[index];
|
|
149
|
+
|
|
150
|
+
if (hasRawProp(rawProps, name)) {
|
|
151
|
+
const nextValue = props[name];
|
|
152
|
+
if (
|
|
153
|
+
!syncedPropPresence[index] ||
|
|
154
|
+
!Object.is(syncedPropValues[index], nextValue)
|
|
155
|
+
) {
|
|
156
|
+
el[name] = nextValue;
|
|
157
|
+
syncedPropValues[index] = nextValue;
|
|
158
|
+
}
|
|
159
|
+
syncedPropPresence[index] = true;
|
|
160
|
+
} else if (syncedPropPresence[index]) {
|
|
149
161
|
el[name] = undefined;
|
|
150
|
-
|
|
162
|
+
syncedPropPresence[index] = false;
|
|
163
|
+
syncedPropValues[index] = undefined;
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
166
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for (const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cleanups.push(() => el.removeEventListener(eventName, handler));
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
function generateVueEventSetup(hasEvents, hasModels) {
|
|
170
|
+
if (!hasEvents) return "";
|
|
171
|
+
return ` const eventHandlers = EVENT_NAMES.map(eventName => event => {
|
|
172
|
+
emit(eventName, event);
|
|
173
|
+
${hasModels ? `
|
|
174
|
+
for (const model of MODEL_BINDINGS) {
|
|
175
|
+
if (model.event !== eventName) continue;
|
|
176
|
+
emit(model.updateEvent, readEventPath(event, model.eventPath));
|
|
165
177
|
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
` : ""} });
|
|
179
|
+
let mountedEl = null;
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
function generateVueMountHook(hasProps, hasEvents) {
|
|
183
|
+
if (!hasProps && !hasEvents) return "";
|
|
184
|
+
return ` onMounted(() => {
|
|
185
|
+
${hasProps ? " syncProps();\n" : ""}${hasEvents ? `
|
|
186
|
+
mountedEl = elRef.value;
|
|
187
|
+
if (!mountedEl) return;
|
|
188
|
+
|
|
189
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
190
|
+
mountedEl.addEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
191
|
+
}
|
|
192
|
+
` : ""} });
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
function generateVueUnmountHook(hasEvents) {
|
|
196
|
+
if (!hasEvents) return "";
|
|
197
|
+
return ` onBeforeUnmount(() => {
|
|
198
|
+
if (!mountedEl) return;
|
|
178
199
|
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
201
|
+
mountedEl.removeEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
181
202
|
}
|
|
203
|
+
mountedEl = null;
|
|
204
|
+
});
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
function generateVueChildren(slotNames) {
|
|
208
|
+
if (!slotNames.length) return ` const children = slots.default ? slots.default() : undefined;
|
|
209
|
+
`;
|
|
210
|
+
return ` const children = slots.default ? slots.default() : [];
|
|
182
211
|
|
|
183
212
|
for (const name of NAMED_SLOTS) {
|
|
184
213
|
const slot = slots[name];
|
|
@@ -188,29 +217,7 @@ export const ${component.name} = defineComponent({
|
|
|
188
217
|
children.push(withSlot(name, vnode));
|
|
189
218
|
}
|
|
190
219
|
}
|
|
191
|
-
|
|
192
|
-
return h(
|
|
193
|
-
${JSON.stringify(component.tag)},
|
|
194
|
-
{
|
|
195
|
-
...attrs,
|
|
196
|
-
ref: elRef,
|
|
197
|
-
},
|
|
198
|
-
children,
|
|
199
|
-
);
|
|
200
|
-
};
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
function withSlot(name, vnode) {
|
|
205
|
-
if (!vnode) return vnode;
|
|
206
|
-
|
|
207
|
-
if (typeof vnode === 'string') {
|
|
208
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return cloneVNode(vnode, { slot: name });
|
|
212
|
-
}
|
|
213
|
-
`.trimStart();
|
|
220
|
+
`;
|
|
214
221
|
}
|
|
215
222
|
function generateVueProps(component) {
|
|
216
223
|
return Object.entries(component.props).map(([name, prop]) => {
|
|
@@ -225,6 +232,7 @@ function toVuePropOption(prop) {
|
|
|
225
232
|
boolean: "Boolean",
|
|
226
233
|
object: "Object",
|
|
227
234
|
array: "Array",
|
|
235
|
+
function: "Function",
|
|
228
236
|
unknown: "null"
|
|
229
237
|
}[prop.type]) !== null && _typeMap$prop$type !== void 0 ? _typeMap$prop$type : "null"}, required: ${prop.required === true ? "true" : "false"} }`;
|
|
230
238
|
}
|
|
@@ -234,6 +242,42 @@ function createVuePropInputKeys(propNames) {
|
|
|
234
242
|
function toKebabCase(value) {
|
|
235
243
|
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
236
244
|
}
|
|
245
|
+
const VUE_PROP_HELPERS = `
|
|
246
|
+
function hasRawProp(rawProps, name) {
|
|
247
|
+
const keys = PROP_INPUT_KEYS[name];
|
|
248
|
+
for (const key of keys) {
|
|
249
|
+
if (hasOwn(rawProps, key)) return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function hasOwn(source, key) {
|
|
255
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
const VUE_NAMED_SLOT_HELPERS = `
|
|
259
|
+
function withSlot(name, vnode) {
|
|
260
|
+
if (!vnode) return vnode;
|
|
261
|
+
|
|
262
|
+
if (typeof vnode === 'string') {
|
|
263
|
+
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return cloneVNode(vnode, { slot: name });
|
|
267
|
+
}
|
|
268
|
+
`;
|
|
269
|
+
const VUE_MODEL_HELPERS = `
|
|
270
|
+
function readEventPath(event, path) {
|
|
271
|
+
if (!path) return event.detail;
|
|
272
|
+
|
|
273
|
+
let value = event;
|
|
274
|
+
for (const segment of path.split('.')) {
|
|
275
|
+
if (value == null) return undefined;
|
|
276
|
+
value = value[segment];
|
|
277
|
+
}
|
|
278
|
+
return value;
|
|
279
|
+
}
|
|
280
|
+
`;
|
|
237
281
|
//#endregion
|
|
238
282
|
//#region packages/web-c/output-vue-wrapper/src/index.ts
|
|
239
283
|
function vueWrapper(options = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* output-vue-wrapper v0.1.0-beta.
|
|
2
|
+
* output-vue-wrapper v0.1.0-beta.4
|
|
3
3
|
* (c) 2026 baicie
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
**/
|
|
@@ -22,163 +22,192 @@ function generateVueIndex(components, options) {
|
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region packages/web-c/output-vue-wrapper/src/generateVueWrapper.ts
|
|
24
24
|
function generateVueWrapper(input) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return generateEventBridgeVueWrapper(input);
|
|
25
|
+
var _input$component$mode;
|
|
26
|
+
return input.mode === "event-bridge" || ((_input$component$mode = input.component.models) === null || _input$component$mode === void 0 ? void 0 : _input$component$mode.length) ? generateEventBridgeVueWrapper(input) : generateMinimalVueWrapper(input);
|
|
28
27
|
}
|
|
29
28
|
function generateMinimalVueWrapper(input) {
|
|
30
29
|
const { component, wcModuleId } = input;
|
|
31
|
-
const slotNames =
|
|
30
|
+
const slotNames = getNamedSlots(component);
|
|
32
31
|
const hasNamedSlots = slotNames.length > 0;
|
|
33
|
-
const vueImports = hasNamedSlots ? `cloneVNode, defineComponent, h` : `defineComponent, h`;
|
|
34
|
-
const namedSlotBlock = hasNamedSlots ? `
|
|
35
|
-
for (const name of NAMED_SLOTS) {
|
|
36
|
-
const slot = slots[name];
|
|
37
|
-
if (!slot) continue;
|
|
38
|
-
|
|
39
|
-
for (const vnode of slot()) {
|
|
40
|
-
children.push(withSlot(name, vnode));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
` : "";
|
|
44
|
-
const withSlotHelper = hasNamedSlots ? `
|
|
45
|
-
function withSlot(name, vnode) {
|
|
46
|
-
if (!vnode) return vnode;
|
|
47
|
-
|
|
48
|
-
if (typeof vnode === 'string') {
|
|
49
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return cloneVNode(vnode, { slot: name });
|
|
53
|
-
}
|
|
54
|
-
` : "";
|
|
55
32
|
return `
|
|
56
|
-
import { ${
|
|
33
|
+
import { ${hasNamedSlots ? "cloneVNode, defineComponent, h" : "defineComponent, h"} } from 'vue';
|
|
57
34
|
|
|
58
35
|
import ${JSON.stringify(wcModuleId)};
|
|
59
|
-
|
|
60
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
61
|
-
|
|
36
|
+
${hasNamedSlots ? `\nconst NAMED_SLOTS = ${JSON.stringify(slotNames)};\n` : ""}
|
|
62
37
|
export const ${component.name} = defineComponent({
|
|
63
38
|
name: ${JSON.stringify(component.name)},
|
|
64
39
|
inheritAttrs: false,
|
|
65
40
|
|
|
66
41
|
setup(_props, { attrs, slots }) {
|
|
67
42
|
return () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (slots.default) {
|
|
71
|
-
children.push(...slots.default());
|
|
72
|
-
}
|
|
73
|
-
${namedSlotBlock}
|
|
74
|
-
return h(
|
|
75
|
-
${JSON.stringify(component.tag)},
|
|
76
|
-
{
|
|
77
|
-
...attrs,
|
|
78
|
-
},
|
|
79
|
-
children,
|
|
80
|
-
);
|
|
43
|
+
${generateVueChildren(slotNames)}
|
|
44
|
+
return h(${JSON.stringify(component.tag)}, attrs, children);
|
|
81
45
|
};
|
|
82
46
|
},
|
|
83
47
|
});
|
|
84
|
-
${
|
|
48
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
85
49
|
`.trimStart();
|
|
86
50
|
}
|
|
87
51
|
function generateEventBridgeVueWrapper(input) {
|
|
88
52
|
const { component, wcModuleId } = input;
|
|
89
|
-
const
|
|
90
|
-
const eventNames =
|
|
91
|
-
|
|
92
|
-
const
|
|
53
|
+
const capabilities = getCapabilities(component);
|
|
54
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
55
|
+
if (!propNames.length && !eventNames.length) return generateMinimalVueWrapper(input);
|
|
56
|
+
const hasProps = propNames.length > 0;
|
|
57
|
+
const hasEvents = eventNames.length > 0;
|
|
58
|
+
const emitNames = Array.from(new Set([...eventNames, ...models.map((model) => model.updateEvent)]));
|
|
59
|
+
const hasNamedSlots = slotNames.length > 0;
|
|
93
60
|
return `
|
|
94
61
|
import {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
62
|
+
${[
|
|
63
|
+
...hasNamedSlots ? ["cloneVNode"] : [],
|
|
64
|
+
"defineComponent",
|
|
65
|
+
...hasProps ? ["getCurrentInstance"] : [],
|
|
66
|
+
"h",
|
|
67
|
+
...hasEvents ? ["onBeforeUnmount"] : [],
|
|
68
|
+
...hasProps || hasEvents ? ["onMounted"] : [],
|
|
69
|
+
...hasProps ? ["onUpdated"] : [],
|
|
70
|
+
"ref"
|
|
71
|
+
].join(",\n ")},
|
|
103
72
|
} from 'vue';
|
|
104
73
|
|
|
105
74
|
import ${JSON.stringify(wcModuleId)};
|
|
106
|
-
|
|
107
|
-
const PROP_KEYS = ${JSON.stringify(propNames)};
|
|
108
|
-
const PROP_INPUT_KEYS = ${JSON.stringify(propInputKeys)};
|
|
109
|
-
const EVENT_NAMES = ${JSON.stringify(eventNames)};
|
|
110
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
111
|
-
|
|
75
|
+
${generateVueConstants(capabilities)}
|
|
112
76
|
export const ${component.name} = defineComponent({
|
|
113
77
|
name: ${JSON.stringify(component.name)},
|
|
114
78
|
inheritAttrs: false,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
emits: EVENT_NAMES,
|
|
121
|
-
|
|
122
|
-
setup(props, { attrs, slots, emit }) {
|
|
79
|
+
${hasProps ? `\n props: {\n ${generateVueProps(component)}\n },\n` : ""}
|
|
80
|
+
${hasEvents ? ` emits: ${models.length ? JSON.stringify(emitNames) : "EVENT_NAMES"},\n` : ""}
|
|
81
|
+
setup(${hasProps ? "props" : "_props"}, { attrs, slots${hasEvents ? ", emit" : ""} }) {
|
|
123
82
|
const elRef = ref(null);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
83
|
+
${generateVuePropSetup(hasProps)}
|
|
84
|
+
${generateVueEventSetup(hasEvents, models.length > 0)}
|
|
85
|
+
${generateVueMountHook(hasProps, hasEvents)}
|
|
86
|
+
${hasProps ? " onUpdated(syncProps);\n" : ""}
|
|
87
|
+
${generateVueUnmountHook(hasEvents)}
|
|
88
|
+
return () => {
|
|
89
|
+
${generateVueChildren(slotNames)}
|
|
90
|
+
const hostProps = Object.assign({}, attrs);
|
|
91
|
+
hostProps.ref = elRef;
|
|
131
92
|
|
|
132
|
-
return
|
|
133
|
-
Object.prototype.hasOwnProperty.call(rawProps, key),
|
|
134
|
-
);
|
|
93
|
+
return h(${JSON.stringify(component.tag)}, hostProps, children);
|
|
135
94
|
};
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
${hasProps ? VUE_PROP_HELPERS : ""}
|
|
98
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
99
|
+
${models.length ? VUE_MODEL_HELPERS : ""}
|
|
100
|
+
`.trimStart();
|
|
101
|
+
}
|
|
102
|
+
function getCapabilities(component) {
|
|
103
|
+
var _component$models;
|
|
104
|
+
const models = ((_component$models = component.models) !== null && _component$models !== void 0 ? _component$models : []).map((model) => ({
|
|
105
|
+
event: model.event,
|
|
106
|
+
eventPath: model.eventPath,
|
|
107
|
+
updateEvent: `update:${model.prop}`
|
|
108
|
+
}));
|
|
109
|
+
return {
|
|
110
|
+
propNames: Object.keys(component.props),
|
|
111
|
+
eventNames: Array.from(new Set([...Object.entries(component.events).map(([key, event]) => {
|
|
112
|
+
var _event$name, _event$key;
|
|
113
|
+
return (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key);
|
|
114
|
+
}), ...models.map((model) => model.event)])),
|
|
115
|
+
models,
|
|
116
|
+
slotNames: getNamedSlots(component)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function getNamedSlots(component) {
|
|
120
|
+
return Object.keys(component.slots).filter((name) => name !== "default");
|
|
121
|
+
}
|
|
122
|
+
function generateVueConstants(capabilities) {
|
|
123
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
124
|
+
const lines = [];
|
|
125
|
+
if (propNames.length) {
|
|
126
|
+
lines.push(`const PROP_KEYS = ${JSON.stringify(propNames)};`);
|
|
127
|
+
lines.push(`const PROP_INPUT_KEYS = ${JSON.stringify(createVuePropInputKeys(propNames))};`);
|
|
128
|
+
lines.push("const EMPTY_PROPS = {};");
|
|
129
|
+
}
|
|
130
|
+
if (eventNames.length) lines.push(`const EVENT_NAMES = ${JSON.stringify(eventNames)};`);
|
|
131
|
+
if (models.length) lines.push(`const MODEL_BINDINGS = ${JSON.stringify(models)};`);
|
|
132
|
+
if (slotNames.length) lines.push(`const NAMED_SLOTS = ${JSON.stringify(slotNames)};`);
|
|
133
|
+
return lines.length ? `${lines.join("\n")}\n` : "";
|
|
134
|
+
}
|
|
135
|
+
function generateVuePropSetup(hasProps) {
|
|
136
|
+
if (!hasProps) return "";
|
|
137
|
+
return ` const instance = getCurrentInstance();
|
|
138
|
+
const syncedPropPresence = [];
|
|
139
|
+
const syncedPropValues = [];
|
|
136
140
|
|
|
137
141
|
const syncProps = () => {
|
|
138
142
|
const el = elRef.value;
|
|
139
143
|
if (!el) return;
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
const rawProps = instance?.vnode.props || EMPTY_PROPS;
|
|
146
|
+
|
|
147
|
+
for (let index = 0; index < PROP_KEYS.length; index += 1) {
|
|
148
|
+
const name = PROP_KEYS[index];
|
|
149
|
+
|
|
150
|
+
if (hasRawProp(rawProps, name)) {
|
|
151
|
+
const nextValue = props[name];
|
|
152
|
+
if (
|
|
153
|
+
!syncedPropPresence[index] ||
|
|
154
|
+
!Object.is(syncedPropValues[index], nextValue)
|
|
155
|
+
) {
|
|
156
|
+
el[name] = nextValue;
|
|
157
|
+
syncedPropValues[index] = nextValue;
|
|
158
|
+
}
|
|
159
|
+
syncedPropPresence[index] = true;
|
|
160
|
+
} else if (syncedPropPresence[index]) {
|
|
149
161
|
el[name] = undefined;
|
|
150
|
-
|
|
162
|
+
syncedPropPresence[index] = false;
|
|
163
|
+
syncedPropValues[index] = undefined;
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
166
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for (const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cleanups.push(() => el.removeEventListener(eventName, handler));
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
function generateVueEventSetup(hasEvents, hasModels) {
|
|
170
|
+
if (!hasEvents) return "";
|
|
171
|
+
return ` const eventHandlers = EVENT_NAMES.map(eventName => event => {
|
|
172
|
+
emit(eventName, event);
|
|
173
|
+
${hasModels ? `
|
|
174
|
+
for (const model of MODEL_BINDINGS) {
|
|
175
|
+
if (model.event !== eventName) continue;
|
|
176
|
+
emit(model.updateEvent, readEventPath(event, model.eventPath));
|
|
165
177
|
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
` : ""} });
|
|
179
|
+
let mountedEl = null;
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
function generateVueMountHook(hasProps, hasEvents) {
|
|
183
|
+
if (!hasProps && !hasEvents) return "";
|
|
184
|
+
return ` onMounted(() => {
|
|
185
|
+
${hasProps ? " syncProps();\n" : ""}${hasEvents ? `
|
|
186
|
+
mountedEl = elRef.value;
|
|
187
|
+
if (!mountedEl) return;
|
|
188
|
+
|
|
189
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
190
|
+
mountedEl.addEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
191
|
+
}
|
|
192
|
+
` : ""} });
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
function generateVueUnmountHook(hasEvents) {
|
|
196
|
+
if (!hasEvents) return "";
|
|
197
|
+
return ` onBeforeUnmount(() => {
|
|
198
|
+
if (!mountedEl) return;
|
|
178
199
|
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
201
|
+
mountedEl.removeEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
181
202
|
}
|
|
203
|
+
mountedEl = null;
|
|
204
|
+
});
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
function generateVueChildren(slotNames) {
|
|
208
|
+
if (!slotNames.length) return ` const children = slots.default ? slots.default() : undefined;
|
|
209
|
+
`;
|
|
210
|
+
return ` const children = slots.default ? slots.default() : [];
|
|
182
211
|
|
|
183
212
|
for (const name of NAMED_SLOTS) {
|
|
184
213
|
const slot = slots[name];
|
|
@@ -188,29 +217,7 @@ export const ${component.name} = defineComponent({
|
|
|
188
217
|
children.push(withSlot(name, vnode));
|
|
189
218
|
}
|
|
190
219
|
}
|
|
191
|
-
|
|
192
|
-
return h(
|
|
193
|
-
${JSON.stringify(component.tag)},
|
|
194
|
-
{
|
|
195
|
-
...attrs,
|
|
196
|
-
ref: elRef,
|
|
197
|
-
},
|
|
198
|
-
children,
|
|
199
|
-
);
|
|
200
|
-
};
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
function withSlot(name, vnode) {
|
|
205
|
-
if (!vnode) return vnode;
|
|
206
|
-
|
|
207
|
-
if (typeof vnode === 'string') {
|
|
208
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return cloneVNode(vnode, { slot: name });
|
|
212
|
-
}
|
|
213
|
-
`.trimStart();
|
|
220
|
+
`;
|
|
214
221
|
}
|
|
215
222
|
function generateVueProps(component) {
|
|
216
223
|
return Object.entries(component.props).map(([name, prop]) => {
|
|
@@ -225,6 +232,7 @@ function toVuePropOption(prop) {
|
|
|
225
232
|
boolean: "Boolean",
|
|
226
233
|
object: "Object",
|
|
227
234
|
array: "Array",
|
|
235
|
+
function: "Function",
|
|
228
236
|
unknown: "null"
|
|
229
237
|
}[prop.type]) !== null && _typeMap$prop$type !== void 0 ? _typeMap$prop$type : "null"}, required: ${prop.required === true ? "true" : "false"} }`;
|
|
230
238
|
}
|
|
@@ -234,6 +242,42 @@ function createVuePropInputKeys(propNames) {
|
|
|
234
242
|
function toKebabCase(value) {
|
|
235
243
|
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
236
244
|
}
|
|
245
|
+
const VUE_PROP_HELPERS = `
|
|
246
|
+
function hasRawProp(rawProps, name) {
|
|
247
|
+
const keys = PROP_INPUT_KEYS[name];
|
|
248
|
+
for (const key of keys) {
|
|
249
|
+
if (hasOwn(rawProps, key)) return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function hasOwn(source, key) {
|
|
255
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
const VUE_NAMED_SLOT_HELPERS = `
|
|
259
|
+
function withSlot(name, vnode) {
|
|
260
|
+
if (!vnode) return vnode;
|
|
261
|
+
|
|
262
|
+
if (typeof vnode === 'string') {
|
|
263
|
+
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return cloneVNode(vnode, { slot: name });
|
|
267
|
+
}
|
|
268
|
+
`;
|
|
269
|
+
const VUE_MODEL_HELPERS = `
|
|
270
|
+
function readEventPath(event, path) {
|
|
271
|
+
if (!path) return event.detail;
|
|
272
|
+
|
|
273
|
+
let value = event;
|
|
274
|
+
for (const segment of path.split('.')) {
|
|
275
|
+
if (value == null) return undefined;
|
|
276
|
+
value = value[segment];
|
|
277
|
+
}
|
|
278
|
+
return value;
|
|
279
|
+
}
|
|
280
|
+
`;
|
|
237
281
|
//#endregion
|
|
238
282
|
//#region packages/web-c/output-vue-wrapper/src/index.ts
|
|
239
283
|
function vueWrapper(options = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* output-vue-wrapper v0.1.0-beta.
|
|
2
|
+
* output-vue-wrapper v0.1.0-beta.4
|
|
3
3
|
* (c) 2026 baicie
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
**/
|
|
@@ -18,163 +18,192 @@ function generateVueIndex(components, options) {
|
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region packages/web-c/output-vue-wrapper/src/generateVueWrapper.ts
|
|
20
20
|
function generateVueWrapper(input) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return generateEventBridgeVueWrapper(input);
|
|
21
|
+
var _input$component$mode;
|
|
22
|
+
return input.mode === "event-bridge" || ((_input$component$mode = input.component.models) === null || _input$component$mode === void 0 ? void 0 : _input$component$mode.length) ? generateEventBridgeVueWrapper(input) : generateMinimalVueWrapper(input);
|
|
24
23
|
}
|
|
25
24
|
function generateMinimalVueWrapper(input) {
|
|
26
25
|
const { component, wcModuleId } = input;
|
|
27
|
-
const slotNames =
|
|
26
|
+
const slotNames = getNamedSlots(component);
|
|
28
27
|
const hasNamedSlots = slotNames.length > 0;
|
|
29
|
-
const vueImports = hasNamedSlots ? `cloneVNode, defineComponent, h` : `defineComponent, h`;
|
|
30
|
-
const namedSlotBlock = hasNamedSlots ? `
|
|
31
|
-
for (const name of NAMED_SLOTS) {
|
|
32
|
-
const slot = slots[name];
|
|
33
|
-
if (!slot) continue;
|
|
34
|
-
|
|
35
|
-
for (const vnode of slot()) {
|
|
36
|
-
children.push(withSlot(name, vnode));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
` : "";
|
|
40
|
-
const withSlotHelper = hasNamedSlots ? `
|
|
41
|
-
function withSlot(name, vnode) {
|
|
42
|
-
if (!vnode) return vnode;
|
|
43
|
-
|
|
44
|
-
if (typeof vnode === 'string') {
|
|
45
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return cloneVNode(vnode, { slot: name });
|
|
49
|
-
}
|
|
50
|
-
` : "";
|
|
51
28
|
return `
|
|
52
|
-
import { ${
|
|
29
|
+
import { ${hasNamedSlots ? "cloneVNode, defineComponent, h" : "defineComponent, h"} } from 'vue';
|
|
53
30
|
|
|
54
31
|
import ${JSON.stringify(wcModuleId)};
|
|
55
|
-
|
|
56
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
57
|
-
|
|
32
|
+
${hasNamedSlots ? `\nconst NAMED_SLOTS = ${JSON.stringify(slotNames)};\n` : ""}
|
|
58
33
|
export const ${component.name} = defineComponent({
|
|
59
34
|
name: ${JSON.stringify(component.name)},
|
|
60
35
|
inheritAttrs: false,
|
|
61
36
|
|
|
62
37
|
setup(_props, { attrs, slots }) {
|
|
63
38
|
return () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (slots.default) {
|
|
67
|
-
children.push(...slots.default());
|
|
68
|
-
}
|
|
69
|
-
${namedSlotBlock}
|
|
70
|
-
return h(
|
|
71
|
-
${JSON.stringify(component.tag)},
|
|
72
|
-
{
|
|
73
|
-
...attrs,
|
|
74
|
-
},
|
|
75
|
-
children,
|
|
76
|
-
);
|
|
39
|
+
${generateVueChildren(slotNames)}
|
|
40
|
+
return h(${JSON.stringify(component.tag)}, attrs, children);
|
|
77
41
|
};
|
|
78
42
|
},
|
|
79
43
|
});
|
|
80
|
-
${
|
|
44
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
81
45
|
`.trimStart();
|
|
82
46
|
}
|
|
83
47
|
function generateEventBridgeVueWrapper(input) {
|
|
84
48
|
const { component, wcModuleId } = input;
|
|
85
|
-
const
|
|
86
|
-
const eventNames =
|
|
87
|
-
|
|
88
|
-
const
|
|
49
|
+
const capabilities = getCapabilities(component);
|
|
50
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
51
|
+
if (!propNames.length && !eventNames.length) return generateMinimalVueWrapper(input);
|
|
52
|
+
const hasProps = propNames.length > 0;
|
|
53
|
+
const hasEvents = eventNames.length > 0;
|
|
54
|
+
const emitNames = Array.from(new Set([...eventNames, ...models.map((model) => model.updateEvent)]));
|
|
55
|
+
const hasNamedSlots = slotNames.length > 0;
|
|
89
56
|
return `
|
|
90
57
|
import {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
${[
|
|
59
|
+
...hasNamedSlots ? ["cloneVNode"] : [],
|
|
60
|
+
"defineComponent",
|
|
61
|
+
...hasProps ? ["getCurrentInstance"] : [],
|
|
62
|
+
"h",
|
|
63
|
+
...hasEvents ? ["onBeforeUnmount"] : [],
|
|
64
|
+
...hasProps || hasEvents ? ["onMounted"] : [],
|
|
65
|
+
...hasProps ? ["onUpdated"] : [],
|
|
66
|
+
"ref"
|
|
67
|
+
].join(",\n ")},
|
|
99
68
|
} from 'vue';
|
|
100
69
|
|
|
101
70
|
import ${JSON.stringify(wcModuleId)};
|
|
102
|
-
|
|
103
|
-
const PROP_KEYS = ${JSON.stringify(propNames)};
|
|
104
|
-
const PROP_INPUT_KEYS = ${JSON.stringify(propInputKeys)};
|
|
105
|
-
const EVENT_NAMES = ${JSON.stringify(eventNames)};
|
|
106
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
107
|
-
|
|
71
|
+
${generateVueConstants(capabilities)}
|
|
108
72
|
export const ${component.name} = defineComponent({
|
|
109
73
|
name: ${JSON.stringify(component.name)},
|
|
110
74
|
inheritAttrs: false,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
emits: EVENT_NAMES,
|
|
117
|
-
|
|
118
|
-
setup(props, { attrs, slots, emit }) {
|
|
75
|
+
${hasProps ? `\n props: {\n ${generateVueProps(component)}\n },\n` : ""}
|
|
76
|
+
${hasEvents ? ` emits: ${models.length ? JSON.stringify(emitNames) : "EVENT_NAMES"},\n` : ""}
|
|
77
|
+
setup(${hasProps ? "props" : "_props"}, { attrs, slots${hasEvents ? ", emit" : ""} }) {
|
|
119
78
|
const elRef = ref(null);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
79
|
+
${generateVuePropSetup(hasProps)}
|
|
80
|
+
${generateVueEventSetup(hasEvents, models.length > 0)}
|
|
81
|
+
${generateVueMountHook(hasProps, hasEvents)}
|
|
82
|
+
${hasProps ? " onUpdated(syncProps);\n" : ""}
|
|
83
|
+
${generateVueUnmountHook(hasEvents)}
|
|
84
|
+
return () => {
|
|
85
|
+
${generateVueChildren(slotNames)}
|
|
86
|
+
const hostProps = Object.assign({}, attrs);
|
|
87
|
+
hostProps.ref = elRef;
|
|
127
88
|
|
|
128
|
-
return
|
|
129
|
-
Object.prototype.hasOwnProperty.call(rawProps, key),
|
|
130
|
-
);
|
|
89
|
+
return h(${JSON.stringify(component.tag)}, hostProps, children);
|
|
131
90
|
};
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
${hasProps ? VUE_PROP_HELPERS : ""}
|
|
94
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
95
|
+
${models.length ? VUE_MODEL_HELPERS : ""}
|
|
96
|
+
`.trimStart();
|
|
97
|
+
}
|
|
98
|
+
function getCapabilities(component) {
|
|
99
|
+
var _component$models;
|
|
100
|
+
const models = ((_component$models = component.models) !== null && _component$models !== void 0 ? _component$models : []).map((model) => ({
|
|
101
|
+
event: model.event,
|
|
102
|
+
eventPath: model.eventPath,
|
|
103
|
+
updateEvent: `update:${model.prop}`
|
|
104
|
+
}));
|
|
105
|
+
return {
|
|
106
|
+
propNames: Object.keys(component.props),
|
|
107
|
+
eventNames: Array.from(new Set([...Object.entries(component.events).map(([key, event]) => {
|
|
108
|
+
var _event$name, _event$key;
|
|
109
|
+
return (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key);
|
|
110
|
+
}), ...models.map((model) => model.event)])),
|
|
111
|
+
models,
|
|
112
|
+
slotNames: getNamedSlots(component)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function getNamedSlots(component) {
|
|
116
|
+
return Object.keys(component.slots).filter((name) => name !== "default");
|
|
117
|
+
}
|
|
118
|
+
function generateVueConstants(capabilities) {
|
|
119
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
120
|
+
const lines = [];
|
|
121
|
+
if (propNames.length) {
|
|
122
|
+
lines.push(`const PROP_KEYS = ${JSON.stringify(propNames)};`);
|
|
123
|
+
lines.push(`const PROP_INPUT_KEYS = ${JSON.stringify(createVuePropInputKeys(propNames))};`);
|
|
124
|
+
lines.push("const EMPTY_PROPS = {};");
|
|
125
|
+
}
|
|
126
|
+
if (eventNames.length) lines.push(`const EVENT_NAMES = ${JSON.stringify(eventNames)};`);
|
|
127
|
+
if (models.length) lines.push(`const MODEL_BINDINGS = ${JSON.stringify(models)};`);
|
|
128
|
+
if (slotNames.length) lines.push(`const NAMED_SLOTS = ${JSON.stringify(slotNames)};`);
|
|
129
|
+
return lines.length ? `${lines.join("\n")}\n` : "";
|
|
130
|
+
}
|
|
131
|
+
function generateVuePropSetup(hasProps) {
|
|
132
|
+
if (!hasProps) return "";
|
|
133
|
+
return ` const instance = getCurrentInstance();
|
|
134
|
+
const syncedPropPresence = [];
|
|
135
|
+
const syncedPropValues = [];
|
|
132
136
|
|
|
133
137
|
const syncProps = () => {
|
|
134
138
|
const el = elRef.value;
|
|
135
139
|
if (!el) return;
|
|
136
140
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
const rawProps = instance?.vnode.props || EMPTY_PROPS;
|
|
142
|
+
|
|
143
|
+
for (let index = 0; index < PROP_KEYS.length; index += 1) {
|
|
144
|
+
const name = PROP_KEYS[index];
|
|
145
|
+
|
|
146
|
+
if (hasRawProp(rawProps, name)) {
|
|
147
|
+
const nextValue = props[name];
|
|
148
|
+
if (
|
|
149
|
+
!syncedPropPresence[index] ||
|
|
150
|
+
!Object.is(syncedPropValues[index], nextValue)
|
|
151
|
+
) {
|
|
152
|
+
el[name] = nextValue;
|
|
153
|
+
syncedPropValues[index] = nextValue;
|
|
154
|
+
}
|
|
155
|
+
syncedPropPresence[index] = true;
|
|
156
|
+
} else if (syncedPropPresence[index]) {
|
|
145
157
|
el[name] = undefined;
|
|
146
|
-
|
|
158
|
+
syncedPropPresence[index] = false;
|
|
159
|
+
syncedPropValues[index] = undefined;
|
|
147
160
|
}
|
|
148
161
|
}
|
|
149
162
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
for (const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
cleanups.push(() => el.removeEventListener(eventName, handler));
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
function generateVueEventSetup(hasEvents, hasModels) {
|
|
166
|
+
if (!hasEvents) return "";
|
|
167
|
+
return ` const eventHandlers = EVENT_NAMES.map(eventName => event => {
|
|
168
|
+
emit(eventName, event);
|
|
169
|
+
${hasModels ? `
|
|
170
|
+
for (const model of MODEL_BINDINGS) {
|
|
171
|
+
if (model.event !== eventName) continue;
|
|
172
|
+
emit(model.updateEvent, readEventPath(event, model.eventPath));
|
|
161
173
|
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
` : ""} });
|
|
175
|
+
let mountedEl = null;
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
function generateVueMountHook(hasProps, hasEvents) {
|
|
179
|
+
if (!hasProps && !hasEvents) return "";
|
|
180
|
+
return ` onMounted(() => {
|
|
181
|
+
${hasProps ? " syncProps();\n" : ""}${hasEvents ? `
|
|
182
|
+
mountedEl = elRef.value;
|
|
183
|
+
if (!mountedEl) return;
|
|
184
|
+
|
|
185
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
186
|
+
mountedEl.addEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
187
|
+
}
|
|
188
|
+
` : ""} });
|
|
189
|
+
`;
|
|
190
|
+
}
|
|
191
|
+
function generateVueUnmountHook(hasEvents) {
|
|
192
|
+
if (!hasEvents) return "";
|
|
193
|
+
return ` onBeforeUnmount(() => {
|
|
194
|
+
if (!mountedEl) return;
|
|
174
195
|
|
|
175
|
-
|
|
176
|
-
|
|
196
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
197
|
+
mountedEl.removeEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
177
198
|
}
|
|
199
|
+
mountedEl = null;
|
|
200
|
+
});
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
function generateVueChildren(slotNames) {
|
|
204
|
+
if (!slotNames.length) return ` const children = slots.default ? slots.default() : undefined;
|
|
205
|
+
`;
|
|
206
|
+
return ` const children = slots.default ? slots.default() : [];
|
|
178
207
|
|
|
179
208
|
for (const name of NAMED_SLOTS) {
|
|
180
209
|
const slot = slots[name];
|
|
@@ -184,29 +213,7 @@ export const ${component.name} = defineComponent({
|
|
|
184
213
|
children.push(withSlot(name, vnode));
|
|
185
214
|
}
|
|
186
215
|
}
|
|
187
|
-
|
|
188
|
-
return h(
|
|
189
|
-
${JSON.stringify(component.tag)},
|
|
190
|
-
{
|
|
191
|
-
...attrs,
|
|
192
|
-
ref: elRef,
|
|
193
|
-
},
|
|
194
|
-
children,
|
|
195
|
-
);
|
|
196
|
-
};
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
function withSlot(name, vnode) {
|
|
201
|
-
if (!vnode) return vnode;
|
|
202
|
-
|
|
203
|
-
if (typeof vnode === 'string') {
|
|
204
|
-
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return cloneVNode(vnode, { slot: name });
|
|
208
|
-
}
|
|
209
|
-
`.trimStart();
|
|
216
|
+
`;
|
|
210
217
|
}
|
|
211
218
|
function generateVueProps(component) {
|
|
212
219
|
return Object.entries(component.props).map(([name, prop]) => {
|
|
@@ -221,6 +228,7 @@ function toVuePropOption(prop) {
|
|
|
221
228
|
boolean: "Boolean",
|
|
222
229
|
object: "Object",
|
|
223
230
|
array: "Array",
|
|
231
|
+
function: "Function",
|
|
224
232
|
unknown: "null"
|
|
225
233
|
}[prop.type]) !== null && _typeMap$prop$type !== void 0 ? _typeMap$prop$type : "null"}, required: ${prop.required === true ? "true" : "false"} }`;
|
|
226
234
|
}
|
|
@@ -230,6 +238,42 @@ function createVuePropInputKeys(propNames) {
|
|
|
230
238
|
function toKebabCase(value) {
|
|
231
239
|
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
232
240
|
}
|
|
241
|
+
const VUE_PROP_HELPERS = `
|
|
242
|
+
function hasRawProp(rawProps, name) {
|
|
243
|
+
const keys = PROP_INPUT_KEYS[name];
|
|
244
|
+
for (const key of keys) {
|
|
245
|
+
if (hasOwn(rawProps, key)) return true;
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function hasOwn(source, key) {
|
|
251
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
252
|
+
}
|
|
253
|
+
`;
|
|
254
|
+
const VUE_NAMED_SLOT_HELPERS = `
|
|
255
|
+
function withSlot(name, vnode) {
|
|
256
|
+
if (!vnode) return vnode;
|
|
257
|
+
|
|
258
|
+
if (typeof vnode === 'string') {
|
|
259
|
+
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return cloneVNode(vnode, { slot: name });
|
|
263
|
+
}
|
|
264
|
+
`;
|
|
265
|
+
const VUE_MODEL_HELPERS = `
|
|
266
|
+
function readEventPath(event, path) {
|
|
267
|
+
if (!path) return event.detail;
|
|
268
|
+
|
|
269
|
+
let value = event;
|
|
270
|
+
for (const segment of path.split('.')) {
|
|
271
|
+
if (value == null) return undefined;
|
|
272
|
+
value = value[segment];
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
`;
|
|
233
277
|
//#endregion
|
|
234
278
|
//#region packages/web-c/output-vue-wrapper/src/index.ts
|
|
235
279
|
function vueWrapper(options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeus-js/output-vue-wrapper",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.4",
|
|
4
4
|
"description": "Zeus Vue wrapper output plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
]
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@zeus-js/bundler-plugin": "0.1.0-beta.
|
|
40
|
-
"@zeus-js/component-dts": "0.1.0-beta.
|
|
41
|
-
"@zeus-js/component-analyzer": "0.1.0-beta.
|
|
39
|
+
"@zeus-js/bundler-plugin": "0.1.0-beta.4",
|
|
40
|
+
"@zeus-js/component-dts": "0.1.0-beta.4",
|
|
41
|
+
"@zeus-js/component-analyzer": "0.1.0-beta.4"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"vue": ">=3"
|