@zeus-js/output-vue-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-vue-wrapper.cjs.js +324 -139
- package/dist/output-vue-wrapper.cjs.prod.js +324 -139
- package/dist/output-vue-wrapper.d.ts +31 -3
- package/dist/output-vue-wrapper.esm-bundler.js +324 -140
- package/dist/runtime/index.cjs.js +114 -0
- package/dist/runtime/index.cjs.prod.js +114 -0
- package/dist/runtime/index.d.ts +26 -0
- package/dist/runtime/index.js +110 -0
- package/dist/runtime/index.prod.js +110 -0
- package/package.json +15 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* output-vue-wrapper v0.1.0-beta.
|
|
2
|
+
* output-vue-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 vue = require("vue");
|
|
12
13
|
//#region packages/web-c/output-vue-wrapper/src/generateVueIndex.ts
|
|
13
14
|
function generateVueIndex(components, options) {
|
|
14
15
|
const lines = [];
|
|
@@ -22,163 +23,192 @@ function generateVueIndex(components, options) {
|
|
|
22
23
|
//#endregion
|
|
23
24
|
//#region packages/web-c/output-vue-wrapper/src/generateVueWrapper.ts
|
|
24
25
|
function generateVueWrapper(input) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return generateEventBridgeVueWrapper(input);
|
|
26
|
+
var _input$component$mode;
|
|
27
|
+
return input.mode === "runtime" ? generateRuntimeVueWrapper(input) : 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
28
|
}
|
|
29
29
|
function generateMinimalVueWrapper(input) {
|
|
30
30
|
const { component, wcModuleId } = input;
|
|
31
|
-
const slotNames =
|
|
31
|
+
const slotNames = getNamedSlots(component);
|
|
32
32
|
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
33
|
return `
|
|
56
|
-
import { ${
|
|
34
|
+
import { ${hasNamedSlots ? "cloneVNode, defineComponent, h" : "defineComponent, h"} } from 'vue';
|
|
57
35
|
|
|
58
36
|
import ${JSON.stringify(wcModuleId)};
|
|
59
|
-
|
|
60
|
-
const NAMED_SLOTS = ${JSON.stringify(slotNames)};
|
|
61
|
-
|
|
37
|
+
${hasNamedSlots ? `\nconst NAMED_SLOTS = ${JSON.stringify(slotNames)};\n` : ""}
|
|
62
38
|
export const ${component.name} = defineComponent({
|
|
63
39
|
name: ${JSON.stringify(component.name)},
|
|
64
40
|
inheritAttrs: false,
|
|
65
41
|
|
|
66
42
|
setup(_props, { attrs, slots }) {
|
|
67
43
|
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
|
-
);
|
|
44
|
+
${generateVueChildren(slotNames)}
|
|
45
|
+
return h(${JSON.stringify(component.tag)}, attrs, children);
|
|
81
46
|
};
|
|
82
47
|
},
|
|
83
48
|
});
|
|
84
|
-
${
|
|
49
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
85
50
|
`.trimStart();
|
|
86
51
|
}
|
|
87
52
|
function generateEventBridgeVueWrapper(input) {
|
|
88
53
|
const { component, wcModuleId } = input;
|
|
89
|
-
const
|
|
90
|
-
const eventNames =
|
|
91
|
-
|
|
92
|
-
const
|
|
54
|
+
const capabilities = getCapabilities(component);
|
|
55
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
56
|
+
if (!propNames.length && !eventNames.length) return generateMinimalVueWrapper(input);
|
|
57
|
+
const hasProps = propNames.length > 0;
|
|
58
|
+
const hasEvents = eventNames.length > 0;
|
|
59
|
+
const emitNames = Array.from(new Set([...eventNames, ...models.map((model) => model.updateEvent)]));
|
|
60
|
+
const hasNamedSlots = slotNames.length > 0;
|
|
93
61
|
return `
|
|
94
62
|
import {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
63
|
+
${[
|
|
64
|
+
...hasNamedSlots ? ["cloneVNode"] : [],
|
|
65
|
+
"defineComponent",
|
|
66
|
+
...hasProps ? ["getCurrentInstance"] : [],
|
|
67
|
+
"h",
|
|
68
|
+
...hasEvents ? ["onBeforeUnmount"] : [],
|
|
69
|
+
...hasProps || hasEvents ? ["onMounted"] : [],
|
|
70
|
+
...hasProps ? ["onUpdated"] : [],
|
|
71
|
+
"ref"
|
|
72
|
+
].join(",\n ")},
|
|
103
73
|
} from 'vue';
|
|
104
74
|
|
|
105
75
|
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
|
-
|
|
76
|
+
${generateVueConstants(capabilities)}
|
|
112
77
|
export const ${component.name} = defineComponent({
|
|
113
78
|
name: ${JSON.stringify(component.name)},
|
|
114
79
|
inheritAttrs: false,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
emits: EVENT_NAMES,
|
|
121
|
-
|
|
122
|
-
setup(props, { attrs, slots, emit }) {
|
|
80
|
+
${hasProps ? `\n props: {\n ${generateVueProps(component)}\n },\n` : ""}
|
|
81
|
+
${hasEvents ? ` emits: ${models.length ? JSON.stringify(emitNames) : "EVENT_NAMES"},\n` : ""}
|
|
82
|
+
setup(${hasProps ? "props" : "_props"}, { attrs, slots${hasEvents ? ", emit" : ""} }) {
|
|
123
83
|
const elRef = ref(null);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
84
|
+
${generateVuePropSetup(hasProps)}
|
|
85
|
+
${generateVueEventSetup(hasEvents, models.length > 0)}
|
|
86
|
+
${generateVueMountHook(hasProps, hasEvents)}
|
|
87
|
+
${hasProps ? " onUpdated(syncProps);\n" : ""}
|
|
88
|
+
${generateVueUnmountHook(hasEvents)}
|
|
89
|
+
return () => {
|
|
90
|
+
${generateVueChildren(slotNames)}
|
|
91
|
+
const hostProps = Object.assign({}, attrs);
|
|
92
|
+
hostProps.ref = elRef;
|
|
131
93
|
|
|
132
|
-
return
|
|
133
|
-
Object.prototype.hasOwnProperty.call(rawProps, key),
|
|
134
|
-
);
|
|
94
|
+
return h(${JSON.stringify(component.tag)}, hostProps, children);
|
|
135
95
|
};
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
${hasProps ? VUE_PROP_HELPERS : ""}
|
|
99
|
+
${hasNamedSlots ? VUE_NAMED_SLOT_HELPERS : ""}
|
|
100
|
+
${models.length ? VUE_MODEL_HELPERS : ""}
|
|
101
|
+
`.trimStart();
|
|
102
|
+
}
|
|
103
|
+
function getCapabilities(component) {
|
|
104
|
+
var _component$models;
|
|
105
|
+
const models = ((_component$models = component.models) !== null && _component$models !== void 0 ? _component$models : []).map((model) => ({
|
|
106
|
+
event: model.event,
|
|
107
|
+
eventPath: model.eventPath,
|
|
108
|
+
updateEvent: `update:${model.prop}`
|
|
109
|
+
}));
|
|
110
|
+
return {
|
|
111
|
+
propNames: Object.keys(component.props),
|
|
112
|
+
eventNames: Array.from(new Set([...Object.entries(component.events).map(([key, event]) => {
|
|
113
|
+
var _event$name, _event$key;
|
|
114
|
+
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);
|
|
115
|
+
}), ...models.map((model) => model.event)])),
|
|
116
|
+
models,
|
|
117
|
+
slotNames: getNamedSlots(component)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function getNamedSlots(component) {
|
|
121
|
+
return Object.keys(component.slots).filter((name) => name !== "default");
|
|
122
|
+
}
|
|
123
|
+
function generateVueConstants(capabilities) {
|
|
124
|
+
const { eventNames, models, propNames, slotNames } = capabilities;
|
|
125
|
+
const lines = [];
|
|
126
|
+
if (propNames.length) {
|
|
127
|
+
lines.push(`const PROP_KEYS = ${JSON.stringify(propNames)};`);
|
|
128
|
+
lines.push(`const PROP_INPUT_KEYS = ${JSON.stringify(createVuePropInputKeys(propNames))};`);
|
|
129
|
+
lines.push("const EMPTY_PROPS = {};");
|
|
130
|
+
}
|
|
131
|
+
if (eventNames.length) lines.push(`const EVENT_NAMES = ${JSON.stringify(eventNames)};`);
|
|
132
|
+
if (models.length) lines.push(`const MODEL_BINDINGS = ${JSON.stringify(models)};`);
|
|
133
|
+
if (slotNames.length) lines.push(`const NAMED_SLOTS = ${JSON.stringify(slotNames)};`);
|
|
134
|
+
return lines.length ? `${lines.join("\n")}\n` : "";
|
|
135
|
+
}
|
|
136
|
+
function generateVuePropSetup(hasProps) {
|
|
137
|
+
if (!hasProps) return "";
|
|
138
|
+
return ` const instance = getCurrentInstance();
|
|
139
|
+
const syncedPropPresence = [];
|
|
140
|
+
const syncedPropValues = [];
|
|
136
141
|
|
|
137
142
|
const syncProps = () => {
|
|
138
143
|
const el = elRef.value;
|
|
139
144
|
if (!el) return;
|
|
140
145
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
const rawProps = instance?.vnode.props || EMPTY_PROPS;
|
|
147
|
+
|
|
148
|
+
for (let index = 0; index < PROP_KEYS.length; index += 1) {
|
|
149
|
+
const name = PROP_KEYS[index];
|
|
150
|
+
|
|
151
|
+
if (hasRawProp(rawProps, name)) {
|
|
152
|
+
const nextValue = props[name];
|
|
153
|
+
if (
|
|
154
|
+
!syncedPropPresence[index] ||
|
|
155
|
+
!Object.is(syncedPropValues[index], nextValue)
|
|
156
|
+
) {
|
|
157
|
+
el[name] = nextValue;
|
|
158
|
+
syncedPropValues[index] = nextValue;
|
|
159
|
+
}
|
|
160
|
+
syncedPropPresence[index] = true;
|
|
161
|
+
} else if (syncedPropPresence[index]) {
|
|
149
162
|
el[name] = undefined;
|
|
150
|
-
|
|
163
|
+
syncedPropPresence[index] = false;
|
|
164
|
+
syncedPropValues[index] = undefined;
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
167
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for (const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cleanups.push(() => el.removeEventListener(eventName, handler));
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
function generateVueEventSetup(hasEvents, hasModels) {
|
|
171
|
+
if (!hasEvents) return "";
|
|
172
|
+
return ` const eventHandlers = EVENT_NAMES.map(eventName => event => {
|
|
173
|
+
emit(eventName, event);
|
|
174
|
+
${hasModels ? `
|
|
175
|
+
for (const model of MODEL_BINDINGS) {
|
|
176
|
+
if (model.event !== eventName) continue;
|
|
177
|
+
emit(model.updateEvent, readEventPath(event, model.eventPath));
|
|
165
178
|
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
` : ""} });
|
|
180
|
+
let mountedEl = null;
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
183
|
+
function generateVueMountHook(hasProps, hasEvents) {
|
|
184
|
+
if (!hasProps && !hasEvents) return "";
|
|
185
|
+
return ` onMounted(() => {
|
|
186
|
+
${hasProps ? " syncProps();\n" : ""}${hasEvents ? `
|
|
187
|
+
mountedEl = elRef.value;
|
|
188
|
+
if (!mountedEl) return;
|
|
189
|
+
|
|
190
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
191
|
+
mountedEl.addEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
192
|
+
}
|
|
193
|
+
` : ""} });
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
|
+
function generateVueUnmountHook(hasEvents) {
|
|
197
|
+
if (!hasEvents) return "";
|
|
198
|
+
return ` onBeforeUnmount(() => {
|
|
199
|
+
if (!mountedEl) return;
|
|
178
200
|
|
|
179
|
-
|
|
180
|
-
|
|
201
|
+
for (let index = 0; index < EVENT_NAMES.length; index += 1) {
|
|
202
|
+
mountedEl.removeEventListener(EVENT_NAMES[index], eventHandlers[index]);
|
|
181
203
|
}
|
|
204
|
+
mountedEl = null;
|
|
205
|
+
});
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
function generateVueChildren(slotNames) {
|
|
209
|
+
if (!slotNames.length) return ` const children = slots.default ? slots.default() : undefined;
|
|
210
|
+
`;
|
|
211
|
+
return ` const children = slots.default ? slots.default() : [];
|
|
182
212
|
|
|
183
213
|
for (const name of NAMED_SLOTS) {
|
|
184
214
|
const slot = slots[name];
|
|
@@ -188,35 +218,49 @@ export const ${component.name} = defineComponent({
|
|
|
188
218
|
children.push(withSlot(name, vnode));
|
|
189
219
|
}
|
|
190
220
|
}
|
|
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();
|
|
221
|
+
`;
|
|
214
222
|
}
|
|
215
223
|
function generateVueProps(component) {
|
|
216
224
|
return Object.entries(component.props).map(([name, prop]) => {
|
|
217
225
|
return `${JSON.stringify(name)}: ${toVuePropOption(prop)}`;
|
|
218
226
|
}).join(",\n ");
|
|
219
227
|
}
|
|
228
|
+
function generateRuntimeVueWrapper(input) {
|
|
229
|
+
var _component$models2;
|
|
230
|
+
const { component } = input;
|
|
231
|
+
const propNames = Object.keys(component.props);
|
|
232
|
+
const eventNames = getEventNames(component);
|
|
233
|
+
const slotNames = Object.keys(component.slots).filter((name) => name !== "default");
|
|
234
|
+
const model = (_component$models2 = component.models) === null || _component$models2 === void 0 ? void 0 : _component$models2[0];
|
|
235
|
+
return [
|
|
236
|
+
`import { defineContainer } from '@zeus-js/output-vue-wrapper/runtime'`,
|
|
237
|
+
`import { defineCustomElement } from '../wc/loader.js'`,
|
|
238
|
+
``,
|
|
239
|
+
`export const ${component.name} = defineContainer({`,
|
|
240
|
+
` tagName: ${JSON.stringify(component.tag)},`,
|
|
241
|
+
` displayName: ${JSON.stringify(component.name)},`,
|
|
242
|
+
` defineCustomElement: () => defineCustomElement(${JSON.stringify(component.tag)}),`,
|
|
243
|
+
` props: ${JSON.stringify(propNames)},`,
|
|
244
|
+
` events: ${JSON.stringify(eventNames)},`,
|
|
245
|
+
` slots: ${JSON.stringify(slotNames)},`,
|
|
246
|
+
` model: ${model ? formatModel(model) : "undefined"},`,
|
|
247
|
+
`})`,
|
|
248
|
+
``
|
|
249
|
+
].join("\n");
|
|
250
|
+
}
|
|
251
|
+
function getEventNames(component) {
|
|
252
|
+
return Array.from(new Set(Object.entries(component.events).map(([key, event]) => {
|
|
253
|
+
var _event$name2, _event$key2;
|
|
254
|
+
return (_event$name2 = event.name) !== null && _event$name2 !== void 0 ? _event$name2 : toKebabCase((_event$key2 = event.key) !== null && _event$key2 !== void 0 ? _event$key2 : key);
|
|
255
|
+
})));
|
|
256
|
+
}
|
|
257
|
+
function formatModel(model) {
|
|
258
|
+
return `{
|
|
259
|
+
prop: ${JSON.stringify(model.prop)},
|
|
260
|
+
event: ${JSON.stringify(model.event)},
|
|
261
|
+
eventPath: ${JSON.stringify(model.eventPath)},
|
|
262
|
+
}`;
|
|
263
|
+
}
|
|
220
264
|
function toVuePropOption(prop) {
|
|
221
265
|
var _typeMap$prop$type;
|
|
222
266
|
return `{ type: ${(_typeMap$prop$type = {
|
|
@@ -225,6 +269,7 @@ function toVuePropOption(prop) {
|
|
|
225
269
|
boolean: "Boolean",
|
|
226
270
|
object: "Object",
|
|
227
271
|
array: "Array",
|
|
272
|
+
function: "Function",
|
|
228
273
|
unknown: "null"
|
|
229
274
|
}[prop.type]) !== null && _typeMap$prop$type !== void 0 ? _typeMap$prop$type : "null"}, required: ${prop.required === true ? "true" : "false"} }`;
|
|
230
275
|
}
|
|
@@ -234,6 +279,145 @@ function createVuePropInputKeys(propNames) {
|
|
|
234
279
|
function toKebabCase(value) {
|
|
235
280
|
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
236
281
|
}
|
|
282
|
+
const VUE_PROP_HELPERS = `
|
|
283
|
+
function hasRawProp(rawProps, name) {
|
|
284
|
+
const keys = PROP_INPUT_KEYS[name];
|
|
285
|
+
for (const key of keys) {
|
|
286
|
+
if (hasOwn(rawProps, key)) return true;
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function hasOwn(source, key) {
|
|
292
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
293
|
+
}
|
|
294
|
+
`;
|
|
295
|
+
const VUE_NAMED_SLOT_HELPERS = `
|
|
296
|
+
function withSlot(name, vnode) {
|
|
297
|
+
if (!vnode) return vnode;
|
|
298
|
+
|
|
299
|
+
if (typeof vnode === 'string') {
|
|
300
|
+
return h('span', { slot: name, style: 'display: contents' }, vnode);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return cloneVNode(vnode, { slot: name });
|
|
304
|
+
}
|
|
305
|
+
`;
|
|
306
|
+
const VUE_MODEL_HELPERS = `
|
|
307
|
+
function readEventPath(event, path) {
|
|
308
|
+
if (!path) return event.detail;
|
|
309
|
+
|
|
310
|
+
let value = event;
|
|
311
|
+
for (const segment of path.split('.')) {
|
|
312
|
+
if (value == null) return undefined;
|
|
313
|
+
value = value[segment];
|
|
314
|
+
}
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
`;
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region packages/web-c/output-vue-wrapper/src/runtime/defineContainer.ts
|
|
320
|
+
const EMPTY_PROP = Symbol();
|
|
321
|
+
const DEFAULT_EMPTY_PROP = { default: EMPTY_PROP };
|
|
322
|
+
const UPDATE_MODEL_VALUE_EVENT = "update:modelValue";
|
|
323
|
+
const MODEL_VALUE = "modelValue";
|
|
324
|
+
function defineContainer(options) {
|
|
325
|
+
const { tagName, displayName, defineCustomElement, props: componentProps = [], events: emitProps = [], slots: slotNames = [], model, transformTag } = options;
|
|
326
|
+
defineCustomElement === null || defineCustomElement === void 0 || defineCustomElement();
|
|
327
|
+
const emits = [...emitProps];
|
|
328
|
+
const componentPropsMap = {};
|
|
329
|
+
for (const prop of componentProps) componentPropsMap[prop] = DEFAULT_EMPTY_PROP;
|
|
330
|
+
if (model) {
|
|
331
|
+
const updateEvent = getModelUpdateEvent(model.prop);
|
|
332
|
+
emits.push(updateEvent);
|
|
333
|
+
if (updateEvent !== UPDATE_MODEL_VALUE_EVENT) emits.push(UPDATE_MODEL_VALUE_EVENT);
|
|
334
|
+
componentPropsMap[MODEL_VALUE] = DEFAULT_EMPTY_PROP;
|
|
335
|
+
}
|
|
336
|
+
return (0, vue.defineComponent)((propsValue, { attrs, slots: allSlots, emit }) => {
|
|
337
|
+
const containerRef = (0, vue.ref)();
|
|
338
|
+
const listeners = [];
|
|
339
|
+
(0, vue.onMounted)(() => {
|
|
340
|
+
const el = containerRef.value;
|
|
341
|
+
if (!el) return;
|
|
342
|
+
for (const eventName of emitProps) {
|
|
343
|
+
const listener = (event) => {
|
|
344
|
+
emit(eventName, event);
|
|
345
|
+
};
|
|
346
|
+
el.addEventListener(eventName, listener);
|
|
347
|
+
listeners.push({
|
|
348
|
+
eventName,
|
|
349
|
+
listener
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
(0, vue.onBeforeUnmount)(() => {
|
|
354
|
+
const el = containerRef.value;
|
|
355
|
+
if (!el) return;
|
|
356
|
+
for (const item of listeners) el.removeEventListener(item.eventName, item.listener);
|
|
357
|
+
listeners.length = 0;
|
|
358
|
+
});
|
|
359
|
+
const vModelDirective = { created: (el) => {
|
|
360
|
+
if (!model) return;
|
|
361
|
+
for (const eventName of toArray(model.event)) el.addEventListener(eventName, (event) => {
|
|
362
|
+
var _model$eventPath;
|
|
363
|
+
if (event.target.tagName !== el.tagName) return;
|
|
364
|
+
const value = readEventPath(event, (_model$eventPath = model.eventPath) !== null && _model$eventPath !== void 0 ? _model$eventPath : `target.${model.prop}`);
|
|
365
|
+
emit(getModelUpdateEvent(model.prop), value);
|
|
366
|
+
if (propsValue[MODEL_VALUE] !== EMPTY_PROP) emit(UPDATE_MODEL_VALUE_EVENT, value);
|
|
367
|
+
});
|
|
368
|
+
} };
|
|
369
|
+
return () => {
|
|
370
|
+
const propsToAdd = { ref: containerRef };
|
|
371
|
+
for (const key in propsValue) {
|
|
372
|
+
const value = propsValue[key];
|
|
373
|
+
if (value !== EMPTY_PROP) propsToAdd[key] = value;
|
|
374
|
+
}
|
|
375
|
+
for (const key in attrs) propsToAdd[key] = attrs[key];
|
|
376
|
+
if (model) {
|
|
377
|
+
const modelValue = propsValue[MODEL_VALUE];
|
|
378
|
+
const modelPropValue = propsValue[model.prop];
|
|
379
|
+
if (modelValue !== EMPTY_PROP) propsToAdd[model.prop] = modelValue;
|
|
380
|
+
else if (modelPropValue !== EMPTY_PROP) propsToAdd[model.prop] = modelPropValue;
|
|
381
|
+
}
|
|
382
|
+
const children = createChildren(allSlots, slotNames);
|
|
383
|
+
const node = (0, vue.h)(transformTag ? transformTag(tagName) : tagName, propsToAdd, children);
|
|
384
|
+
return model ? (0, vue.withDirectives)(node, [[vModelDirective]]) : node;
|
|
385
|
+
};
|
|
386
|
+
}, {
|
|
387
|
+
name: displayName !== null && displayName !== void 0 ? displayName : tagName,
|
|
388
|
+
props: componentPropsMap,
|
|
389
|
+
emits
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
function createChildren(slots, slotNames) {
|
|
393
|
+
const children = slots.default ? slots.default() : [];
|
|
394
|
+
for (const name of slotNames) {
|
|
395
|
+
const slot = slots[name];
|
|
396
|
+
if (!slot) continue;
|
|
397
|
+
for (const vnode of slot()) children.push(withSlot(name, vnode));
|
|
398
|
+
}
|
|
399
|
+
return children;
|
|
400
|
+
}
|
|
401
|
+
function withSlot(name, vnode) {
|
|
402
|
+
if (!vnode) return vnode;
|
|
403
|
+
if (typeof vnode === "string") return (0, vue.h)("span", {
|
|
404
|
+
slot: name,
|
|
405
|
+
style: "display: contents"
|
|
406
|
+
}, vnode);
|
|
407
|
+
return (0, vue.cloneVNode)(vnode, { slot: name });
|
|
408
|
+
}
|
|
409
|
+
function getModelUpdateEvent(prop) {
|
|
410
|
+
return `update:${prop}`;
|
|
411
|
+
}
|
|
412
|
+
function toArray(value) {
|
|
413
|
+
return Array.isArray(value) ? value : [value];
|
|
414
|
+
}
|
|
415
|
+
function readEventPath(event, path) {
|
|
416
|
+
return path.split(".").reduce((value, key) => {
|
|
417
|
+
if (value == null) return void 0;
|
|
418
|
+
return value[key];
|
|
419
|
+
}, event);
|
|
420
|
+
}
|
|
237
421
|
//#endregion
|
|
238
422
|
//#region packages/web-c/output-vue-wrapper/src/index.ts
|
|
239
423
|
function vueWrapper(options = {}) {
|
|
@@ -245,7 +429,7 @@ function vueWrapper(options = {}) {
|
|
|
245
429
|
dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
|
|
246
430
|
globalDts: (_options$globalDts = options.globalDts) !== null && _options$globalDts !== void 0 ? _options$globalDts : true,
|
|
247
431
|
index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
|
|
248
|
-
wrapper: (_options$wrapper = options.wrapper) !== null && _options$wrapper !== void 0 ? _options$wrapper : "
|
|
432
|
+
wrapper: (_options$wrapper = options.wrapper) !== null && _options$wrapper !== void 0 ? _options$wrapper : "runtime"
|
|
249
433
|
};
|
|
250
434
|
return {
|
|
251
435
|
name: "zeus-output-vue-wrapper",
|
|
@@ -294,3 +478,4 @@ function vueWrapper(options = {}) {
|
|
|
294
478
|
}
|
|
295
479
|
//#endregion
|
|
296
480
|
exports.default = vueWrapper;
|
|
481
|
+
exports.defineContainer = defineContainer;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DtsMode, ZeusComponentPlugin } from '@zeus-js/bundler-plugin';
|
|
2
|
+
import * as vue from 'vue';
|
|
2
3
|
|
|
3
|
-
type VueWrapperMode = 'minimal' | 'event-bridge';
|
|
4
|
+
type VueWrapperMode = 'runtime' | 'minimal' | 'event-bridge';
|
|
4
5
|
export interface OutputVueWrapperOptions {
|
|
5
6
|
/**
|
|
6
7
|
* Vue wrapper output directory.
|
|
@@ -37,16 +38,43 @@ export interface OutputVueWrapperOptions {
|
|
|
37
38
|
*/
|
|
38
39
|
index?: boolean;
|
|
39
40
|
/**
|
|
41
|
+
* runtime:
|
|
42
|
+
* Default. Generates thin proxies powered by @zeus-js/output-vue-wrapper/runtime.
|
|
43
|
+
* No watch, no onMounted, no addEventListener — Vue-native props/events/model/slots.
|
|
44
|
+
*
|
|
40
45
|
* minimal:
|
|
41
|
-
*
|
|
46
|
+
* Vue wrapper only renders the custom element tag.
|
|
42
47
|
* No watch, no prop sync, no event listeners.
|
|
43
48
|
*
|
|
44
49
|
* event-bridge:
|
|
45
|
-
* Additional mode
|
|
50
|
+
* Additional mode with explicit prop syncing and CustomEvent bridging.
|
|
46
51
|
*/
|
|
47
52
|
wrapper?: VueWrapperMode;
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
export interface ZeusVueModelOptions {
|
|
56
|
+
prop: string;
|
|
57
|
+
event: string | string[];
|
|
58
|
+
eventPath?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ZeusVueContainerOptions {
|
|
61
|
+
tagName: string;
|
|
62
|
+
displayName?: string;
|
|
63
|
+
defineCustomElement?: () => void;
|
|
64
|
+
props?: string[];
|
|
65
|
+
events?: string[];
|
|
66
|
+
slots?: string[];
|
|
67
|
+
model?: ZeusVueModelOptions;
|
|
68
|
+
transformTag?: (tagName: string) => string;
|
|
69
|
+
}
|
|
70
|
+
export declare function defineContainer(options: ZeusVueContainerOptions): vue.DefineSetupFnComponent<{
|
|
71
|
+
[x: string]: /*elided*/ any;
|
|
72
|
+
}, string[], {}, {
|
|
73
|
+
[x: string]: /*elided*/ any;
|
|
74
|
+
} & {
|
|
75
|
+
[x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
|
|
76
|
+
}, vue.PublicProps>;
|
|
77
|
+
|
|
50
78
|
export declare function vueWrapper(options?: OutputVueWrapperOptions): ZeusComponentPlugin;
|
|
51
79
|
|
|
52
80
|
export { vueWrapper as default };
|