@zeus-js/output-wc 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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.3
2
+ * output-wc v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -9,7 +9,7 @@ Object.defineProperties(exports, {
9
9
  });
10
10
  const ZEUS_OUTPUT_WC_CAPABILITIES = {
11
11
  packageName: "@zeus-js/output-wc",
12
- version: "0.1.0-beta.3",
12
+ version: "0.1.0-beta.4",
13
13
  output: {
14
14
  webComponent: true,
15
15
  customElements: true,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.3
2
+ * output-wc v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -9,7 +9,7 @@ Object.defineProperties(exports, {
9
9
  });
10
10
  const ZEUS_OUTPUT_WC_CAPABILITIES = {
11
11
  packageName: "@zeus-js/output-wc",
12
- version: "0.1.0-beta.3",
12
+ version: "0.1.0-beta.4",
13
13
  output: {
14
14
  webComponent: true,
15
15
  customElements: true,
@@ -1,6 +1,6 @@
1
1
  const ZEUS_OUTPUT_WC_CAPABILITIES = {
2
2
  packageName: "@zeus-js/output-wc",
3
- version: "0.1.0-beta.3",
3
+ version: "0.1.0-beta.4",
4
4
  output: {
5
5
  webComponent: true,
6
6
  customElements: true,
@@ -1,6 +1,6 @@
1
1
  const ZEUS_OUTPUT_WC_CAPABILITIES = {
2
2
  packageName: "@zeus-js/output-wc",
3
- version: "0.1.0-beta.3",
3
+ version: "0.1.0-beta.4",
4
4
  output: {
5
5
  webComponent: true,
6
6
  customElements: true,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.3
2
+ * output-wc v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -54,7 +54,10 @@ function generateCustomElementsJson(options) {
54
54
  events: generateEvents(component),
55
55
  slots: generateSlots(component),
56
56
  cssParts: component.cssParts.map((name) => ({ name })),
57
- cssProperties: component.cssVars.map((name) => ({ name }))
57
+ cssProperties: getCssVars(component).map((variable) => ({
58
+ name: variable.name,
59
+ description: variable.description
60
+ }))
58
61
  }],
59
62
  exports: [{
60
63
  kind: "js",
@@ -84,7 +87,8 @@ function generateAttributes(component) {
84
87
  return result;
85
88
  }
86
89
  function generateMembers(component) {
87
- return Object.entries(component.props).map(([name, prop]) => {
90
+ var _component$methods;
91
+ const fields = Object.entries(component.props).map(([name, prop]) => {
88
92
  return {
89
93
  kind: "field",
90
94
  name,
@@ -93,18 +97,28 @@ function generateMembers(component) {
93
97
  default: prop.default === void 0 ? void 0 : JSON.stringify(prop.default)
94
98
  };
95
99
  });
100
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {}).map((name) => {
101
+ return {
102
+ kind: "method",
103
+ name
104
+ };
105
+ });
106
+ return [...fields, ...methods];
96
107
  }
97
108
  function generateEvents(component) {
98
- return Object.entries(component.events).map(([name, event]) => {
109
+ return Object.entries(component.events).map(([key, event]) => {
110
+ var _event$name, _event$key;
99
111
  return {
100
- name,
112
+ name: (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase$1((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key),
101
113
  description: event.description,
102
114
  type: { text: event.detail ? `CustomEvent<${formatDetailType(event.detail)}>` : "CustomEvent" }
103
115
  };
104
116
  });
105
117
  }
106
118
  function generateSlots(component) {
107
- return Object.entries(component.slots).map(([name, slot]) => {
119
+ return Object.entries(component.slots).map(([key, slot]) => {
120
+ var _slot$name;
121
+ const name = (_slot$name = slot.name) !== null && _slot$name !== void 0 ? _slot$name : key;
108
122
  return {
109
123
  name: name === "default" ? "" : name,
110
124
  description: slot.description
@@ -114,6 +128,7 @@ function generateSlots(component) {
114
128
  function getAttributeName(propName, prop) {
115
129
  if (prop.attr === false) return false;
116
130
  if (typeof prop.attr === "string") return prop.attr;
131
+ if (!isAttributeBackedType$1(prop.type)) return false;
117
132
  return propName.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
118
133
  }
119
134
  function formatPropType(prop) {
@@ -125,15 +140,25 @@ function formatPropType(prop) {
125
140
  case "boolean": return "boolean";
126
141
  case "array": return "unknown[]";
127
142
  case "object": return "Record<string, unknown>";
143
+ case "function": return "Function";
128
144
  default: return "unknown";
129
145
  }
130
146
  }
147
+ function isAttributeBackedType$1(type) {
148
+ return type === "string" || type === "number" || type === "boolean";
149
+ }
131
150
  function formatDetailType(detail) {
132
151
  return `{ ${Object.entries(detail).map(([name, type]) => `${name}: ${type}`).join("; ")} }`;
133
152
  }
134
153
  function normalizeModulePath(value) {
135
154
  return value.replace(/\\/g, "/");
136
155
  }
156
+ function getCssVars(component) {
157
+ return Object.values(component.cssVars);
158
+ }
159
+ function toKebabCase$1(value) {
160
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
161
+ }
137
162
  //#endregion
138
163
  //#region packages/web-c/output-wc/src/imports.ts
139
164
  function toAbsoluteImportPath(root, source) {
@@ -186,7 +211,11 @@ function generateLazyEntry(options) {
186
211
  "",
187
212
  `export function createComponent(hostRef) {`,
188
213
  ` let mounted;`,
189
- ` const mountState = {};`,
214
+ ` const mountState = {`,
215
+ ` attributeProps: hostRef.attributeProps,`,
216
+ ` internals: hostRef.internals,`,
217
+ ` reflectingAttrs: hostRef.reflectingAttrs,`,
218
+ ` };`,
190
219
  "",
191
220
  ` return {`,
192
221
  ` connected() {`,
@@ -208,6 +237,22 @@ function generateLazyEntry(options) {
208
237
  ` propertyChanged(name, oldValue, newValue) {`,
209
238
  ` mounted?.propertyChanged(name, oldValue, newValue);`,
210
239
  ` },`,
240
+ "",
241
+ ` formAssociated(form) {`,
242
+ ` mounted?.formAssociated(form);`,
243
+ ` },`,
244
+ "",
245
+ ` formDisabled(disabled) {`,
246
+ ` mounted?.formDisabled(disabled);`,
247
+ ` },`,
248
+ "",
249
+ ` formReset() {`,
250
+ ` mounted?.formReset();`,
251
+ ` },`,
252
+ "",
253
+ ` formStateRestore(state, mode) {`,
254
+ ` mounted?.formStateRestore(state, mode);`,
255
+ ` },`,
211
256
  ` };`,
212
257
  `}`,
213
258
  "",
@@ -229,15 +274,19 @@ function generateLazyManifest(options) {
229
274
  const { components, getEntryFileName } = options;
230
275
  return `export const components = [
231
276
  ${components.map((component) => {
232
- var _component$runtimePro, _component$meta$shado, _component$meta;
277
+ var _component$runtimePro, _component$methods, _component$meta$shado, _component$meta, _component$meta$formA, _component$meta2;
233
278
  const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
234
279
  const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
280
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {});
235
281
  const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
282
+ const methodLine = methods.length ? ` methods: ${JSON.stringify(methods)},\n` : "";
236
283
  return ` {
237
284
  tagName: ${JSON.stringify(component.tag)},
238
285
  shadow: ${(_component$meta$shado = (_component$meta = component.meta) === null || _component$meta === void 0 ? void 0 : _component$meta.shadow) !== null && _component$meta$shado !== void 0 ? _component$meta$shado : false},
286
+ formAssociated: ${(_component$meta$formA = (_component$meta2 = component.meta) === null || _component$meta2 === void 0 ? void 0 : _component$meta2.formAssociated) !== null && _component$meta$formA !== void 0 ? _component$meta$formA : false},
239
287
  load: () => import(${importPath}),
240
288
  props: ${props},
289
+ ${methodLine}
241
290
  }`;
242
291
  }).join(",\n")}
243
292
  ];
@@ -248,14 +297,16 @@ function generatePropsArray(props) {
248
297
  if (entries.length === 0) return "[]";
249
298
  return `[\n${entries.map(([name, prop]) => {
250
299
  const parts = [`name: ${JSON.stringify(name)}`];
251
- if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
300
+ if (!isAttributeBackedType(prop.type) && !prop.deserialize || prop.attr === false) parts.push("attrName: false");
252
301
  else {
253
302
  var _prop$attr;
254
303
  const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
255
304
  if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
256
305
  }
257
306
  parts.push(`type: ${JSON.stringify(prop.type)}`);
258
- if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
307
+ if (prop.reflect && (isAttributeBackedType(prop.type) || Boolean(prop.serialize))) parts.push("reflect: true");
308
+ if (prop.serialize) parts.push("serialize: true");
309
+ if (prop.deserialize) parts.push("deserialize: true");
259
310
  return ` { ${parts.join(", ")} }`;
260
311
  }).join(",\n")}\n ]`;
261
312
  }
@@ -319,8 +370,8 @@ function wc(options = {}) {
319
370
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
320
371
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
321
372
  fileName: options.fileName,
322
- manifestFile: registerMode === "lazy" ? false : (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
323
- customElementsFile: registerMode === "lazy" ? false : (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
373
+ manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
374
+ customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
324
375
  dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
325
376
  jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
326
377
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
@@ -349,15 +400,15 @@ function wc(options = {}) {
349
400
  const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
350
401
  for (const [name, prop] of Object.entries(runtimeProps)) {
351
402
  if (isAttributeBackedRuntimeProp(prop.type)) continue;
352
- if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
403
+ if (prop.attr !== void 0 && prop.attr !== false && !prop.deserialize) ctx.error([
353
404
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use attr:${JSON.stringify(prop.attr)} with register:"lazy".`,
354
- "Lazy Web-C only supports attributes for string, number and boolean props.",
355
- "Use attr:false and pass object/array/function values as properties instead."
405
+ "Non-primitive lazy attributes require a custom deserialize function.",
406
+ "Add deserialize or use attr:false and pass the value as a property."
356
407
  ].join("\n"));
357
- if (prop.reflect) ctx.error([
408
+ if (prop.reflect && !prop.serialize) ctx.error([
358
409
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use reflect:true with register:"lazy".`,
359
- "Lazy Web-C only reflects string, number and boolean props.",
360
- "Use a string/number/boolean prop or remove reflect:true."
410
+ "Non-primitive lazy reflection requires a custom serialize function.",
411
+ "Add serialize or remove reflect:true."
361
412
  ].join("\n"));
362
413
  }
363
414
  }
@@ -404,7 +455,7 @@ function wc(options = {}) {
404
455
  }
405
456
  for (const component of ctx.manifest.components) if (isLazy) {
406
457
  /**
407
- * Compatibility module consumed by Vue / React wrappers.
458
+ * Registration bridge module consumed by Vue / React wrappers.
408
459
  *
409
460
  * It only registers lazy Proxy Elements.
410
461
  * It does not import the real component implementation.
@@ -489,17 +540,18 @@ export {};
489
540
  fileName: joinPath("types/jsx.d.ts"),
490
541
  source: (0, _zeus_js_component_dts.generateWCJsxDts)(ctx.manifest)
491
542
  });
492
- } else if (normalized.manifestFile) files.push({
543
+ }
544
+ if (normalized.manifestFile) files.push({
493
545
  type: "asset",
494
546
  fileName: normalized.manifestFile,
495
547
  source: generateZeusComponentsManifest(ctx.manifest)
496
548
  });
497
- if (!isLazy && normalized.customElementsFile) files.push({
549
+ if (normalized.customElementsFile) files.push({
498
550
  type: "asset",
499
551
  fileName: normalized.customElementsFile,
500
552
  source: generateCustomElementsJson({
501
553
  manifest: ctx.manifest,
502
- getModulePath: (component) => joinPath(getFileName(component.tag))
554
+ getModulePath: (component) => isLazy ? joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag)) : joinPath(getFileName(component.tag))
503
555
  })
504
556
  });
505
557
  if (!isLazy && (dts || jsxDts)) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.3
2
+ * output-wc v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -54,7 +54,10 @@ function generateCustomElementsJson(options) {
54
54
  events: generateEvents(component),
55
55
  slots: generateSlots(component),
56
56
  cssParts: component.cssParts.map((name) => ({ name })),
57
- cssProperties: component.cssVars.map((name) => ({ name }))
57
+ cssProperties: getCssVars(component).map((variable) => ({
58
+ name: variable.name,
59
+ description: variable.description
60
+ }))
58
61
  }],
59
62
  exports: [{
60
63
  kind: "js",
@@ -84,7 +87,8 @@ function generateAttributes(component) {
84
87
  return result;
85
88
  }
86
89
  function generateMembers(component) {
87
- return Object.entries(component.props).map(([name, prop]) => {
90
+ var _component$methods;
91
+ const fields = Object.entries(component.props).map(([name, prop]) => {
88
92
  return {
89
93
  kind: "field",
90
94
  name,
@@ -93,18 +97,28 @@ function generateMembers(component) {
93
97
  default: prop.default === void 0 ? void 0 : JSON.stringify(prop.default)
94
98
  };
95
99
  });
100
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {}).map((name) => {
101
+ return {
102
+ kind: "method",
103
+ name
104
+ };
105
+ });
106
+ return [...fields, ...methods];
96
107
  }
97
108
  function generateEvents(component) {
98
- return Object.entries(component.events).map(([name, event]) => {
109
+ return Object.entries(component.events).map(([key, event]) => {
110
+ var _event$name, _event$key;
99
111
  return {
100
- name,
112
+ name: (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase$1((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key),
101
113
  description: event.description,
102
114
  type: { text: event.detail ? `CustomEvent<${formatDetailType(event.detail)}>` : "CustomEvent" }
103
115
  };
104
116
  });
105
117
  }
106
118
  function generateSlots(component) {
107
- return Object.entries(component.slots).map(([name, slot]) => {
119
+ return Object.entries(component.slots).map(([key, slot]) => {
120
+ var _slot$name;
121
+ const name = (_slot$name = slot.name) !== null && _slot$name !== void 0 ? _slot$name : key;
108
122
  return {
109
123
  name: name === "default" ? "" : name,
110
124
  description: slot.description
@@ -114,6 +128,7 @@ function generateSlots(component) {
114
128
  function getAttributeName(propName, prop) {
115
129
  if (prop.attr === false) return false;
116
130
  if (typeof prop.attr === "string") return prop.attr;
131
+ if (!isAttributeBackedType$1(prop.type)) return false;
117
132
  return propName.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
118
133
  }
119
134
  function formatPropType(prop) {
@@ -125,15 +140,25 @@ function formatPropType(prop) {
125
140
  case "boolean": return "boolean";
126
141
  case "array": return "unknown[]";
127
142
  case "object": return "Record<string, unknown>";
143
+ case "function": return "Function";
128
144
  default: return "unknown";
129
145
  }
130
146
  }
147
+ function isAttributeBackedType$1(type) {
148
+ return type === "string" || type === "number" || type === "boolean";
149
+ }
131
150
  function formatDetailType(detail) {
132
151
  return `{ ${Object.entries(detail).map(([name, type]) => `${name}: ${type}`).join("; ")} }`;
133
152
  }
134
153
  function normalizeModulePath(value) {
135
154
  return value.replace(/\\/g, "/");
136
155
  }
156
+ function getCssVars(component) {
157
+ return Object.values(component.cssVars);
158
+ }
159
+ function toKebabCase$1(value) {
160
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
161
+ }
137
162
  //#endregion
138
163
  //#region packages/web-c/output-wc/src/imports.ts
139
164
  function toAbsoluteImportPath(root, source) {
@@ -186,7 +211,11 @@ function generateLazyEntry(options) {
186
211
  "",
187
212
  `export function createComponent(hostRef) {`,
188
213
  ` let mounted;`,
189
- ` const mountState = {};`,
214
+ ` const mountState = {`,
215
+ ` attributeProps: hostRef.attributeProps,`,
216
+ ` internals: hostRef.internals,`,
217
+ ` reflectingAttrs: hostRef.reflectingAttrs,`,
218
+ ` };`,
190
219
  "",
191
220
  ` return {`,
192
221
  ` connected() {`,
@@ -208,6 +237,22 @@ function generateLazyEntry(options) {
208
237
  ` propertyChanged(name, oldValue, newValue) {`,
209
238
  ` mounted?.propertyChanged(name, oldValue, newValue);`,
210
239
  ` },`,
240
+ "",
241
+ ` formAssociated(form) {`,
242
+ ` mounted?.formAssociated(form);`,
243
+ ` },`,
244
+ "",
245
+ ` formDisabled(disabled) {`,
246
+ ` mounted?.formDisabled(disabled);`,
247
+ ` },`,
248
+ "",
249
+ ` formReset() {`,
250
+ ` mounted?.formReset();`,
251
+ ` },`,
252
+ "",
253
+ ` formStateRestore(state, mode) {`,
254
+ ` mounted?.formStateRestore(state, mode);`,
255
+ ` },`,
211
256
  ` };`,
212
257
  `}`,
213
258
  "",
@@ -229,15 +274,19 @@ function generateLazyManifest(options) {
229
274
  const { components, getEntryFileName } = options;
230
275
  return `export const components = [
231
276
  ${components.map((component) => {
232
- var _component$runtimePro, _component$meta$shado, _component$meta;
277
+ var _component$runtimePro, _component$methods, _component$meta$shado, _component$meta, _component$meta$formA, _component$meta2;
233
278
  const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
234
279
  const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
280
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {});
235
281
  const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
282
+ const methodLine = methods.length ? ` methods: ${JSON.stringify(methods)},\n` : "";
236
283
  return ` {
237
284
  tagName: ${JSON.stringify(component.tag)},
238
285
  shadow: ${(_component$meta$shado = (_component$meta = component.meta) === null || _component$meta === void 0 ? void 0 : _component$meta.shadow) !== null && _component$meta$shado !== void 0 ? _component$meta$shado : false},
286
+ formAssociated: ${(_component$meta$formA = (_component$meta2 = component.meta) === null || _component$meta2 === void 0 ? void 0 : _component$meta2.formAssociated) !== null && _component$meta$formA !== void 0 ? _component$meta$formA : false},
239
287
  load: () => import(${importPath}),
240
288
  props: ${props},
289
+ ${methodLine}
241
290
  }`;
242
291
  }).join(",\n")}
243
292
  ];
@@ -248,14 +297,16 @@ function generatePropsArray(props) {
248
297
  if (entries.length === 0) return "[]";
249
298
  return `[\n${entries.map(([name, prop]) => {
250
299
  const parts = [`name: ${JSON.stringify(name)}`];
251
- if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
300
+ if (!isAttributeBackedType(prop.type) && !prop.deserialize || prop.attr === false) parts.push("attrName: false");
252
301
  else {
253
302
  var _prop$attr;
254
303
  const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
255
304
  if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
256
305
  }
257
306
  parts.push(`type: ${JSON.stringify(prop.type)}`);
258
- if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
307
+ if (prop.reflect && (isAttributeBackedType(prop.type) || Boolean(prop.serialize))) parts.push("reflect: true");
308
+ if (prop.serialize) parts.push("serialize: true");
309
+ if (prop.deserialize) parts.push("deserialize: true");
259
310
  return ` { ${parts.join(", ")} }`;
260
311
  }).join(",\n")}\n ]`;
261
312
  }
@@ -319,8 +370,8 @@ function wc(options = {}) {
319
370
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
320
371
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
321
372
  fileName: options.fileName,
322
- manifestFile: registerMode === "lazy" ? false : (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
323
- customElementsFile: registerMode === "lazy" ? false : (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
373
+ manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
374
+ customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
324
375
  dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
325
376
  jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
326
377
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
@@ -349,15 +400,15 @@ function wc(options = {}) {
349
400
  const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
350
401
  for (const [name, prop] of Object.entries(runtimeProps)) {
351
402
  if (isAttributeBackedRuntimeProp(prop.type)) continue;
352
- if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
403
+ if (prop.attr !== void 0 && prop.attr !== false && !prop.deserialize) ctx.error([
353
404
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use attr:${JSON.stringify(prop.attr)} with register:"lazy".`,
354
- "Lazy Web-C only supports attributes for string, number and boolean props.",
355
- "Use attr:false and pass object/array/function values as properties instead."
405
+ "Non-primitive lazy attributes require a custom deserialize function.",
406
+ "Add deserialize or use attr:false and pass the value as a property."
356
407
  ].join("\n"));
357
- if (prop.reflect) ctx.error([
408
+ if (prop.reflect && !prop.serialize) ctx.error([
358
409
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use reflect:true with register:"lazy".`,
359
- "Lazy Web-C only reflects string, number and boolean props.",
360
- "Use a string/number/boolean prop or remove reflect:true."
410
+ "Non-primitive lazy reflection requires a custom serialize function.",
411
+ "Add serialize or remove reflect:true."
361
412
  ].join("\n"));
362
413
  }
363
414
  }
@@ -404,7 +455,7 @@ function wc(options = {}) {
404
455
  }
405
456
  for (const component of ctx.manifest.components) if (isLazy) {
406
457
  /**
407
- * Compatibility module consumed by Vue / React wrappers.
458
+ * Registration bridge module consumed by Vue / React wrappers.
408
459
  *
409
460
  * It only registers lazy Proxy Elements.
410
461
  * It does not import the real component implementation.
@@ -489,17 +540,18 @@ export {};
489
540
  fileName: joinPath("types/jsx.d.ts"),
490
541
  source: (0, _zeus_js_component_dts.generateWCJsxDts)(ctx.manifest)
491
542
  });
492
- } else if (normalized.manifestFile) files.push({
543
+ }
544
+ if (normalized.manifestFile) files.push({
493
545
  type: "asset",
494
546
  fileName: normalized.manifestFile,
495
547
  source: generateZeusComponentsManifest(ctx.manifest)
496
548
  });
497
- if (!isLazy && normalized.customElementsFile) files.push({
549
+ if (normalized.customElementsFile) files.push({
498
550
  type: "asset",
499
551
  fileName: normalized.customElementsFile,
500
552
  source: generateCustomElementsJson({
501
553
  manifest: ctx.manifest,
502
- getModulePath: (component) => joinPath(getFileName(component.tag))
554
+ getModulePath: (component) => isLazy ? joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag)) : joinPath(getFileName(component.tag))
503
555
  })
504
556
  });
505
557
  if (!isLazy && (dts || jsxDts)) {
@@ -65,7 +65,6 @@ export interface OutputWCOptions {
65
65
  *
66
66
  * side-effect:
67
67
  * Immediately registers full components on import.
68
- * Compatible with legacy behavior; not recommended as default.
69
68
  */
70
69
  register?: WebCRegisterMode;
71
70
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.3
2
+ * output-wc v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -27,7 +27,10 @@ function generateCustomElementsJson(options) {
27
27
  events: generateEvents(component),
28
28
  slots: generateSlots(component),
29
29
  cssParts: component.cssParts.map((name) => ({ name })),
30
- cssProperties: component.cssVars.map((name) => ({ name }))
30
+ cssProperties: getCssVars(component).map((variable) => ({
31
+ name: variable.name,
32
+ description: variable.description
33
+ }))
31
34
  }],
32
35
  exports: [{
33
36
  kind: "js",
@@ -57,7 +60,8 @@ function generateAttributes(component) {
57
60
  return result;
58
61
  }
59
62
  function generateMembers(component) {
60
- return Object.entries(component.props).map(([name, prop]) => {
63
+ var _component$methods;
64
+ const fields = Object.entries(component.props).map(([name, prop]) => {
61
65
  return {
62
66
  kind: "field",
63
67
  name,
@@ -66,18 +70,28 @@ function generateMembers(component) {
66
70
  default: prop.default === void 0 ? void 0 : JSON.stringify(prop.default)
67
71
  };
68
72
  });
73
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {}).map((name) => {
74
+ return {
75
+ kind: "method",
76
+ name
77
+ };
78
+ });
79
+ return [...fields, ...methods];
69
80
  }
70
81
  function generateEvents(component) {
71
- return Object.entries(component.events).map(([name, event]) => {
82
+ return Object.entries(component.events).map(([key, event]) => {
83
+ var _event$name, _event$key;
72
84
  return {
73
- name,
85
+ name: (_event$name = event.name) !== null && _event$name !== void 0 ? _event$name : toKebabCase$1((_event$key = event.key) !== null && _event$key !== void 0 ? _event$key : key),
74
86
  description: event.description,
75
87
  type: { text: event.detail ? `CustomEvent<${formatDetailType(event.detail)}>` : "CustomEvent" }
76
88
  };
77
89
  });
78
90
  }
79
91
  function generateSlots(component) {
80
- return Object.entries(component.slots).map(([name, slot]) => {
92
+ return Object.entries(component.slots).map(([key, slot]) => {
93
+ var _slot$name;
94
+ const name = (_slot$name = slot.name) !== null && _slot$name !== void 0 ? _slot$name : key;
81
95
  return {
82
96
  name: name === "default" ? "" : name,
83
97
  description: slot.description
@@ -87,6 +101,7 @@ function generateSlots(component) {
87
101
  function getAttributeName(propName, prop) {
88
102
  if (prop.attr === false) return false;
89
103
  if (typeof prop.attr === "string") return prop.attr;
104
+ if (!isAttributeBackedType$1(prop.type)) return false;
90
105
  return propName.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
91
106
  }
92
107
  function formatPropType(prop) {
@@ -98,15 +113,25 @@ function formatPropType(prop) {
98
113
  case "boolean": return "boolean";
99
114
  case "array": return "unknown[]";
100
115
  case "object": return "Record<string, unknown>";
116
+ case "function": return "Function";
101
117
  default: return "unknown";
102
118
  }
103
119
  }
120
+ function isAttributeBackedType$1(type) {
121
+ return type === "string" || type === "number" || type === "boolean";
122
+ }
104
123
  function formatDetailType(detail) {
105
124
  return `{ ${Object.entries(detail).map(([name, type]) => `${name}: ${type}`).join("; ")} }`;
106
125
  }
107
126
  function normalizeModulePath(value) {
108
127
  return value.replace(/\\/g, "/");
109
128
  }
129
+ function getCssVars(component) {
130
+ return Object.values(component.cssVars);
131
+ }
132
+ function toKebabCase$1(value) {
133
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
134
+ }
110
135
  //#endregion
111
136
  //#region packages/web-c/output-wc/src/imports.ts
112
137
  function toAbsoluteImportPath(root, source) {
@@ -159,7 +184,11 @@ function generateLazyEntry(options) {
159
184
  "",
160
185
  `export function createComponent(hostRef) {`,
161
186
  ` let mounted;`,
162
- ` const mountState = {};`,
187
+ ` const mountState = {`,
188
+ ` attributeProps: hostRef.attributeProps,`,
189
+ ` internals: hostRef.internals,`,
190
+ ` reflectingAttrs: hostRef.reflectingAttrs,`,
191
+ ` };`,
163
192
  "",
164
193
  ` return {`,
165
194
  ` connected() {`,
@@ -181,6 +210,22 @@ function generateLazyEntry(options) {
181
210
  ` propertyChanged(name, oldValue, newValue) {`,
182
211
  ` mounted?.propertyChanged(name, oldValue, newValue);`,
183
212
  ` },`,
213
+ "",
214
+ ` formAssociated(form) {`,
215
+ ` mounted?.formAssociated(form);`,
216
+ ` },`,
217
+ "",
218
+ ` formDisabled(disabled) {`,
219
+ ` mounted?.formDisabled(disabled);`,
220
+ ` },`,
221
+ "",
222
+ ` formReset() {`,
223
+ ` mounted?.formReset();`,
224
+ ` },`,
225
+ "",
226
+ ` formStateRestore(state, mode) {`,
227
+ ` mounted?.formStateRestore(state, mode);`,
228
+ ` },`,
184
229
  ` };`,
185
230
  `}`,
186
231
  "",
@@ -202,15 +247,19 @@ function generateLazyManifest(options) {
202
247
  const { components, getEntryFileName } = options;
203
248
  return `export const components = [
204
249
  ${components.map((component) => {
205
- var _component$runtimePro, _component$meta$shado, _component$meta;
250
+ var _component$runtimePro, _component$methods, _component$meta$shado, _component$meta, _component$meta$formA, _component$meta2;
206
251
  const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
207
252
  const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
253
+ const methods = Object.keys((_component$methods = component.methods) !== null && _component$methods !== void 0 ? _component$methods : {});
208
254
  const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
255
+ const methodLine = methods.length ? ` methods: ${JSON.stringify(methods)},\n` : "";
209
256
  return ` {
210
257
  tagName: ${JSON.stringify(component.tag)},
211
258
  shadow: ${(_component$meta$shado = (_component$meta = component.meta) === null || _component$meta === void 0 ? void 0 : _component$meta.shadow) !== null && _component$meta$shado !== void 0 ? _component$meta$shado : false},
259
+ formAssociated: ${(_component$meta$formA = (_component$meta2 = component.meta) === null || _component$meta2 === void 0 ? void 0 : _component$meta2.formAssociated) !== null && _component$meta$formA !== void 0 ? _component$meta$formA : false},
212
260
  load: () => import(${importPath}),
213
261
  props: ${props},
262
+ ${methodLine}
214
263
  }`;
215
264
  }).join(",\n")}
216
265
  ];
@@ -221,14 +270,16 @@ function generatePropsArray(props) {
221
270
  if (entries.length === 0) return "[]";
222
271
  return `[\n${entries.map(([name, prop]) => {
223
272
  const parts = [`name: ${JSON.stringify(name)}`];
224
- if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
273
+ if (!isAttributeBackedType(prop.type) && !prop.deserialize || prop.attr === false) parts.push("attrName: false");
225
274
  else {
226
275
  var _prop$attr;
227
276
  const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
228
277
  if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
229
278
  }
230
279
  parts.push(`type: ${JSON.stringify(prop.type)}`);
231
- if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
280
+ if (prop.reflect && (isAttributeBackedType(prop.type) || Boolean(prop.serialize))) parts.push("reflect: true");
281
+ if (prop.serialize) parts.push("serialize: true");
282
+ if (prop.deserialize) parts.push("deserialize: true");
232
283
  return ` { ${parts.join(", ")} }`;
233
284
  }).join(",\n")}\n ]`;
234
285
  }
@@ -292,8 +343,8 @@ function wc(options = {}) {
292
343
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
293
344
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
294
345
  fileName: options.fileName,
295
- manifestFile: registerMode === "lazy" ? false : (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
296
- customElementsFile: registerMode === "lazy" ? false : (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
346
+ manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
347
+ customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
297
348
  dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
298
349
  jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
299
350
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
@@ -322,15 +373,15 @@ function wc(options = {}) {
322
373
  const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
323
374
  for (const [name, prop] of Object.entries(runtimeProps)) {
324
375
  if (isAttributeBackedRuntimeProp(prop.type)) continue;
325
- if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
376
+ if (prop.attr !== void 0 && prop.attr !== false && !prop.deserialize) ctx.error([
326
377
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use attr:${JSON.stringify(prop.attr)} with register:"lazy".`,
327
- "Lazy Web-C only supports attributes for string, number and boolean props.",
328
- "Use attr:false and pass object/array/function values as properties instead."
378
+ "Non-primitive lazy attributes require a custom deserialize function.",
379
+ "Add deserialize or use attr:false and pass the value as a property."
329
380
  ].join("\n"));
330
- if (prop.reflect) ctx.error([
381
+ if (prop.reflect && !prop.serialize) ctx.error([
331
382
  `[zeus-output-wc] <${component.tag}> prop "${name}" cannot use reflect:true with register:"lazy".`,
332
- "Lazy Web-C only reflects string, number and boolean props.",
333
- "Use a string/number/boolean prop or remove reflect:true."
383
+ "Non-primitive lazy reflection requires a custom serialize function.",
384
+ "Add serialize or remove reflect:true."
334
385
  ].join("\n"));
335
386
  }
336
387
  }
@@ -377,7 +428,7 @@ function wc(options = {}) {
377
428
  }
378
429
  for (const component of ctx.manifest.components) if (isLazy) {
379
430
  /**
380
- * Compatibility module consumed by Vue / React wrappers.
431
+ * Registration bridge module consumed by Vue / React wrappers.
381
432
  *
382
433
  * It only registers lazy Proxy Elements.
383
434
  * It does not import the real component implementation.
@@ -462,17 +513,18 @@ export {};
462
513
  fileName: joinPath("types/jsx.d.ts"),
463
514
  source: generateWCJsxDts(ctx.manifest)
464
515
  });
465
- } else if (normalized.manifestFile) files.push({
516
+ }
517
+ if (normalized.manifestFile) files.push({
466
518
  type: "asset",
467
519
  fileName: normalized.manifestFile,
468
520
  source: generateZeusComponentsManifest(ctx.manifest)
469
521
  });
470
- if (!isLazy && normalized.customElementsFile) files.push({
522
+ if (normalized.customElementsFile) files.push({
471
523
  type: "asset",
472
524
  fileName: normalized.customElementsFile,
473
525
  source: generateCustomElementsJson({
474
526
  manifest: ctx.manifest,
475
- getModulePath: (component) => joinPath(getFileName(component.tag))
527
+ getModulePath: (component) => isLazy ? joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag)) : joinPath(getFileName(component.tag))
476
528
  })
477
529
  });
478
530
  if (!isLazy && (dts || jsxDts)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeus-js/output-wc",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Zeus Web Component output plugin",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -52,11 +52,11 @@
52
52
  ]
53
53
  },
54
54
  "dependencies": {
55
- "@zeus-js/bundler-plugin": "0.1.0-beta.3",
56
- "@zeus-js/component-analyzer": "0.1.0-beta.3",
57
- "@zeus-js/runtime-dom": "0.1.0-beta.3",
58
- "@zeus-js/component-dts": "0.1.0-beta.3",
59
- "@zeus-js/web-c-runtime": "0.1.0"
55
+ "@zeus-js/bundler-plugin": "0.1.0-beta.4",
56
+ "@zeus-js/component-analyzer": "0.1.0-beta.4",
57
+ "@zeus-js/runtime-dom": "0.1.0-beta.4",
58
+ "@zeus-js/component-dts": "0.1.0-beta.4",
59
+ "@zeus-js/web-c-runtime": "0.2.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "rollup": "^4.0.0"