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