@zeus-js/output-wc 0.1.0-beta.2 → 0.1.0-beta.3

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.2
2
+ * output-wc v0.1.0-beta.3
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.2",
12
+ version: "0.1.0-beta.3",
13
13
  output: {
14
14
  webComponent: true,
15
15
  customElements: true,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.2
2
+ * output-wc v0.1.0-beta.3
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.2",
12
+ version: "0.1.0-beta.3",
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.2",
3
+ version: "0.1.0-beta.3",
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.2",
3
+ version: "0.1.0-beta.3",
4
4
  output: {
5
5
  webComponent: true,
6
6
  customElements: true,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.2
2
+ * output-wc v0.1.0-beta.3
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -176,6 +176,136 @@ function getVirtualIndexId() {
176
176
  return "zeus:wc:index";
177
177
  }
178
178
  //#endregion
179
+ //#region packages/web-c/output-wc/src/generateLazyEntry.ts
180
+ function generateLazyEntry(options) {
181
+ const { component, outPath, sourceImport } = options;
182
+ const source = sourceImport !== null && sourceImport !== void 0 ? sourceImport : toRelativeImport(component.source, outPath);
183
+ return [
184
+ `import { mountElementDefinition } from "@zeus-js/runtime-dom";`,
185
+ `import { ${component.exportName} } from ${JSON.stringify(source)};`,
186
+ "",
187
+ `export function createComponent(hostRef) {`,
188
+ ` let mounted;`,
189
+ ` const mountState = {};`,
190
+ "",
191
+ ` return {`,
192
+ ` connected() {`,
193
+ ` if (mounted) return;`,
194
+ "",
195
+ ` mounted = mountElementDefinition(`,
196
+ ` ${component.exportName},`,
197
+ ` hostRef.host,`,
198
+ ` hostRef.values,`,
199
+ ` mountState,`,
200
+ ` );`,
201
+ ` },`,
202
+ "",
203
+ ` disconnected() {`,
204
+ ` mounted?.dispose();`,
205
+ ` mounted = undefined;`,
206
+ ` },`,
207
+ "",
208
+ ` propertyChanged(name, oldValue, newValue) {`,
209
+ ` mounted?.propertyChanged(name, oldValue, newValue);`,
210
+ ` },`,
211
+ ` };`,
212
+ `}`,
213
+ "",
214
+ `export default { createComponent };`,
215
+ ""
216
+ ].join("\n");
217
+ }
218
+ function toRelativeImport(source, outPath) {
219
+ const sourceParts = normalizePath(source).split("/");
220
+ const outParts = normalizePath(outPath).split("/");
221
+ let common = 0;
222
+ for (let i = 0; i < Math.min(sourceParts.length, outParts.length); i++) if (sourceParts[i] === outParts[i]) common++;
223
+ else break;
224
+ return [...outParts.slice(common).map(() => ".."), ...sourceParts.slice(common)].join("/");
225
+ }
226
+ //#endregion
227
+ //#region packages/web-c/output-wc/src/generateLazyManifest.ts
228
+ function generateLazyManifest(options) {
229
+ const { components, getEntryFileName } = options;
230
+ return `export const components = [
231
+ ${components.map((component) => {
232
+ var _component$runtimePro, _component$meta$shado, _component$meta;
233
+ const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
234
+ const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
235
+ const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
236
+ return ` {
237
+ tagName: ${JSON.stringify(component.tag)},
238
+ 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},
239
+ load: () => import(${importPath}),
240
+ props: ${props},
241
+ }`;
242
+ }).join(",\n")}
243
+ ];
244
+ `;
245
+ }
246
+ function generatePropsArray(props) {
247
+ const entries = Object.entries(props);
248
+ if (entries.length === 0) return "[]";
249
+ return `[\n${entries.map(([name, prop]) => {
250
+ const parts = [`name: ${JSON.stringify(name)}`];
251
+ if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
252
+ else {
253
+ var _prop$attr;
254
+ const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
255
+ if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
256
+ }
257
+ parts.push(`type: ${JSON.stringify(prop.type)}`);
258
+ if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
259
+ return ` { ${parts.join(", ")} }`;
260
+ }).join(",\n")}\n ]`;
261
+ }
262
+ function isAttributeBackedType(type) {
263
+ return type === "string" || type === "number" || type === "boolean";
264
+ }
265
+ function toKebabCase(value) {
266
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
267
+ }
268
+ //#endregion
269
+ //#region packages/web-c/output-wc/src/generateLoader.ts
270
+ function generateLoader() {
271
+ return `import { bootstrapLazy } from "@zeus-js/web-c-runtime";
272
+ import { components } from "./components.manifest.js";
273
+
274
+ let defined = false;
275
+
276
+ export function defineCustomElements() {
277
+ if (defined) {
278
+ return;
279
+ }
280
+
281
+ if (typeof customElements === "undefined") {
282
+ return;
283
+ }
284
+
285
+ bootstrapLazy(components);
286
+
287
+ defined = true;
288
+ }
289
+
290
+ export const defineLazyElements = defineCustomElements;
291
+ `;
292
+ }
293
+ function generateAutoEntry() {
294
+ return `import { defineCustomElements } from "./loader.js";
295
+
296
+ defineCustomElements();
297
+
298
+ export {};
299
+ `;
300
+ }
301
+ function generateLazyIndex() {
302
+ return `export {
303
+ defineCustomElements,
304
+ defineLazyElements,
305
+ } from "./loader.js";
306
+ `;
307
+ }
308
+ //#endregion
179
309
  //#region packages/web-c/output-wc/src/generateManifest.ts
180
310
  function generateZeusComponentsManifest(manifest) {
181
311
  return `${JSON.stringify(manifest, null, 2)}\n`;
@@ -183,17 +313,21 @@ function generateZeusComponentsManifest(manifest) {
183
313
  //#endregion
184
314
  //#region packages/web-c/output-wc/src/index.ts
185
315
  function wc(options = {}) {
186
- var _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa;
316
+ var _options$register, _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa, _options$auto, _options$entryFileNam;
317
+ const registerMode = (_options$register = options.register) !== null && _options$register !== void 0 ? _options$register : "lazy";
187
318
  const normalized = {
188
319
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
189
320
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
190
321
  fileName: options.fileName,
191
- manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
192
- customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
193
- dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : "auto",
194
- jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : "auto",
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",
324
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
325
+ jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
195
326
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
196
- warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true
327
+ warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true,
328
+ register: registerMode,
329
+ auto: (_options$auto = options.auto) !== null && _options$auto !== void 0 ? _options$auto : true,
330
+ entryFileName: (_options$entryFileNam = options.entryFileName) !== null && _options$entryFileNam !== void 0 ? _options$entryFileNam : ((tag) => `${tag}.entry`)
197
331
  };
198
332
  let _outDir = normalized.outDir;
199
333
  return {
@@ -208,7 +342,32 @@ function wc(options = {}) {
208
342
  },
209
343
  buildStart(ctx) {
210
344
  if (!ctx.manifest) return;
211
- checkFileNameCollisions(ctx.manifest.components, normalized, { warn: ctx.warn });
345
+ if (normalized.register === "lazy") for (const component of ctx.manifest.components) {
346
+ var _component$runtimePro, _component$runtimePro2;
347
+ const diagnostics = (_component$runtimePro = component.runtimePropsDiagnostics) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : [];
348
+ if (diagnostics.length > 0) ctx.error([`[zeus-output-wc] <${component.tag}> cannot be emitted with register:"lazy" because its runtime props are not statically analyzable.`, ...diagnostics.map((message) => `- ${message}`)].join("\n"));
349
+ const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
350
+ for (const [name, prop] of Object.entries(runtimeProps)) {
351
+ if (isAttributeBackedRuntimeProp(prop.type)) continue;
352
+ if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
353
+ `[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."
356
+ ].join("\n"));
357
+ if (prop.reflect) ctx.error([
358
+ `[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."
361
+ ].join("\n"));
362
+ }
363
+ }
364
+ checkFileNameCollisions(ctx.manifest.components, {
365
+ getFileName: (tag) => {
366
+ if (normalized.register === "lazy") return getLazyEntryFileName(normalized.entryFileName, tag);
367
+ return normalizeFileName(tag, normalized.stripPrefix, normalized.fileName);
368
+ },
369
+ warnOnFileNameCollision: normalized.warnOnFileNameCollision
370
+ }, { warn: ctx.warn });
212
371
  },
213
372
  virtualModules(ctx) {
214
373
  if (!ctx.manifest) return [];
@@ -222,7 +381,55 @@ function wc(options = {}) {
222
381
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
223
382
  return node_path.default.posix.join(_outDir, fileName);
224
383
  };
225
- for (const component of ctx.manifest.components) {
384
+ const isLazy = normalized.register === "lazy";
385
+ if (isLazy) {
386
+ modules.push({
387
+ id: "zeus:wc:components.manifest",
388
+ fileName: joinPath("components.manifest.js"),
389
+ code: generateLazyManifest({
390
+ components: ctx.manifest.components,
391
+ getEntryFileName: (tag) => getLazyEntryFileName(normalized.entryFileName, tag)
392
+ })
393
+ });
394
+ modules.push({
395
+ id: "zeus:wc:loader",
396
+ fileName: joinPath("loader.js"),
397
+ code: generateLoader()
398
+ });
399
+ if (normalized.auto) modules.push({
400
+ id: "zeus:wc:auto",
401
+ fileName: joinPath("auto.js"),
402
+ code: generateAutoEntry()
403
+ });
404
+ }
405
+ for (const component of ctx.manifest.components) if (isLazy) {
406
+ /**
407
+ * Compatibility module consumed by Vue / React wrappers.
408
+ *
409
+ * It only registers lazy Proxy Elements.
410
+ * It does not import the real component implementation.
411
+ */
412
+ modules.push({
413
+ id: getVirtualComponentId(component),
414
+ code: `
415
+ import { defineCustomElements } from "zeus:wc:loader";
416
+
417
+ defineCustomElements();
418
+
419
+ export {};
420
+ `.trimStart()
421
+ });
422
+ const fileName = joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag));
423
+ modules.push({
424
+ id: `zeus:wc:entry:${component.tag}`,
425
+ fileName,
426
+ code: generateLazyEntry({
427
+ component,
428
+ outPath: fileName,
429
+ sourceImport: toAbsoluteImportPath(ctx.root, component.source)
430
+ })
431
+ });
432
+ } else {
226
433
  const fileName = joinPath(getFileName(component.tag));
227
434
  modules.push({
228
435
  id: getVirtualComponentId(component),
@@ -233,7 +440,7 @@ function wc(options = {}) {
233
440
  })
234
441
  });
235
442
  }
236
- if (normalized.index) modules.push({
443
+ if (normalized.index && !isLazy) modules.push({
237
444
  id: getVirtualIndexId(),
238
445
  fileName: joinPath("index.js"),
239
446
  code: generateWCIndex({
@@ -241,16 +448,18 @@ function wc(options = {}) {
241
448
  getFileName
242
449
  })
243
450
  });
451
+ if (isLazy && normalized.index) modules.push({
452
+ id: getVirtualIndexId(),
453
+ fileName: joinPath("index.js"),
454
+ code: generateLazyIndex()
455
+ });
244
456
  return modules;
245
457
  },
246
458
  generateBundle(ctx) {
247
459
  if (!ctx.manifest) return [];
248
460
  const files = [];
249
- if (normalized.manifestFile) files.push({
250
- type: "asset",
251
- fileName: normalized.manifestFile,
252
- source: generateZeusComponentsManifest(ctx.manifest)
253
- });
461
+ const dts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.dts, ctx);
462
+ const jsxDts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.jsxDts, ctx);
254
463
  const hasOutputs = ctx.outputs.has("wc");
255
464
  const getFileName = (tag) => {
256
465
  if (hasOutputs) return ctx.outputs.getFileName("wc", tag);
@@ -260,7 +469,32 @@ function wc(options = {}) {
260
469
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
261
470
  return node_path.default.posix.join(_outDir, fileName);
262
471
  };
263
- if (normalized.customElementsFile) files.push({
472
+ const isLazy = normalized.register === "lazy";
473
+ if (isLazy) {
474
+ if (dts) {
475
+ const loaderDts = (0, _zeus_js_component_dts.generateLoaderDts)(ctx.manifest);
476
+ files.push({
477
+ type: "asset",
478
+ fileName: joinPath("loader.d.ts"),
479
+ source: loaderDts
480
+ });
481
+ if (normalized.index) files.push({
482
+ type: "asset",
483
+ fileName: joinPath("index.d.ts"),
484
+ source: loaderDts
485
+ });
486
+ }
487
+ if (jsxDts) files.push({
488
+ type: "asset",
489
+ fileName: joinPath("types/jsx.d.ts"),
490
+ source: (0, _zeus_js_component_dts.generateWCJsxDts)(ctx.manifest)
491
+ });
492
+ } else if (normalized.manifestFile) files.push({
493
+ type: "asset",
494
+ fileName: normalized.manifestFile,
495
+ source: generateZeusComponentsManifest(ctx.manifest)
496
+ });
497
+ if (!isLazy && normalized.customElementsFile) files.push({
264
498
  type: "asset",
265
499
  fileName: normalized.customElementsFile,
266
500
  source: generateCustomElementsJson({
@@ -268,9 +502,7 @@ function wc(options = {}) {
268
502
  getModulePath: (component) => joinPath(getFileName(component.tag))
269
503
  })
270
504
  });
271
- const dts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.dts, ctx);
272
- const jsxDts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.jsxDts, ctx);
273
- if (dts || jsxDts) {
505
+ if (!isLazy && (dts || jsxDts)) {
274
506
  const dtsFiles = (0, _zeus_js_component_dts.generateWCDtsFiles)(ctx.manifest, {
275
507
  outDir: _outDir,
276
508
  stripPrefix: normalized.stripPrefix,
@@ -299,12 +531,22 @@ function normalizeFileName(tag, stripPrefix, fileName) {
299
531
  if (!name.endsWith(".js")) name = `${name}.js`;
300
532
  return name;
301
533
  }
534
+ function normalizeLazyEntryFileName(fileName) {
535
+ const normalized = fileName.replace(/\\/g, "/");
536
+ return normalized.endsWith(".js") ? normalized : `${normalized}.js`;
537
+ }
538
+ function getLazyEntryFileName(entryFileName, tag) {
539
+ return normalizeLazyEntryFileName(entryFileName(tag));
540
+ }
541
+ function isAttributeBackedRuntimeProp(type) {
542
+ return type === "string" || type === "number" || type === "boolean";
543
+ }
302
544
  function checkFileNameCollisions(components, options, reporter) {
303
545
  if (options.warnOnFileNameCollision === false) return;
304
546
  const map = /* @__PURE__ */ new Map();
305
547
  for (const component of components) {
306
548
  var _map$get;
307
- const fileName = normalizeFileName(component.tag, options.stripPrefix, options.fileName);
549
+ const fileName = options.getFileName(component.tag);
308
550
  const list = (_map$get = map.get(fileName)) !== null && _map$get !== void 0 ? _map$get : [];
309
551
  list.push(component);
310
552
  map.set(fileName, list);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.2
2
+ * output-wc v0.1.0-beta.3
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -176,6 +176,136 @@ function getVirtualIndexId() {
176
176
  return "zeus:wc:index";
177
177
  }
178
178
  //#endregion
179
+ //#region packages/web-c/output-wc/src/generateLazyEntry.ts
180
+ function generateLazyEntry(options) {
181
+ const { component, outPath, sourceImport } = options;
182
+ const source = sourceImport !== null && sourceImport !== void 0 ? sourceImport : toRelativeImport(component.source, outPath);
183
+ return [
184
+ `import { mountElementDefinition } from "@zeus-js/runtime-dom";`,
185
+ `import { ${component.exportName} } from ${JSON.stringify(source)};`,
186
+ "",
187
+ `export function createComponent(hostRef) {`,
188
+ ` let mounted;`,
189
+ ` const mountState = {};`,
190
+ "",
191
+ ` return {`,
192
+ ` connected() {`,
193
+ ` if (mounted) return;`,
194
+ "",
195
+ ` mounted = mountElementDefinition(`,
196
+ ` ${component.exportName},`,
197
+ ` hostRef.host,`,
198
+ ` hostRef.values,`,
199
+ ` mountState,`,
200
+ ` );`,
201
+ ` },`,
202
+ "",
203
+ ` disconnected() {`,
204
+ ` mounted?.dispose();`,
205
+ ` mounted = undefined;`,
206
+ ` },`,
207
+ "",
208
+ ` propertyChanged(name, oldValue, newValue) {`,
209
+ ` mounted?.propertyChanged(name, oldValue, newValue);`,
210
+ ` },`,
211
+ ` };`,
212
+ `}`,
213
+ "",
214
+ `export default { createComponent };`,
215
+ ""
216
+ ].join("\n");
217
+ }
218
+ function toRelativeImport(source, outPath) {
219
+ const sourceParts = normalizePath(source).split("/");
220
+ const outParts = normalizePath(outPath).split("/");
221
+ let common = 0;
222
+ for (let i = 0; i < Math.min(sourceParts.length, outParts.length); i++) if (sourceParts[i] === outParts[i]) common++;
223
+ else break;
224
+ return [...outParts.slice(common).map(() => ".."), ...sourceParts.slice(common)].join("/");
225
+ }
226
+ //#endregion
227
+ //#region packages/web-c/output-wc/src/generateLazyManifest.ts
228
+ function generateLazyManifest(options) {
229
+ const { components, getEntryFileName } = options;
230
+ return `export const components = [
231
+ ${components.map((component) => {
232
+ var _component$runtimePro, _component$meta$shado, _component$meta;
233
+ const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
234
+ const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
235
+ const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
236
+ return ` {
237
+ tagName: ${JSON.stringify(component.tag)},
238
+ 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},
239
+ load: () => import(${importPath}),
240
+ props: ${props},
241
+ }`;
242
+ }).join(",\n")}
243
+ ];
244
+ `;
245
+ }
246
+ function generatePropsArray(props) {
247
+ const entries = Object.entries(props);
248
+ if (entries.length === 0) return "[]";
249
+ return `[\n${entries.map(([name, prop]) => {
250
+ const parts = [`name: ${JSON.stringify(name)}`];
251
+ if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
252
+ else {
253
+ var _prop$attr;
254
+ const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
255
+ if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
256
+ }
257
+ parts.push(`type: ${JSON.stringify(prop.type)}`);
258
+ if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
259
+ return ` { ${parts.join(", ")} }`;
260
+ }).join(",\n")}\n ]`;
261
+ }
262
+ function isAttributeBackedType(type) {
263
+ return type === "string" || type === "number" || type === "boolean";
264
+ }
265
+ function toKebabCase(value) {
266
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
267
+ }
268
+ //#endregion
269
+ //#region packages/web-c/output-wc/src/generateLoader.ts
270
+ function generateLoader() {
271
+ return `import { bootstrapLazy } from "@zeus-js/web-c-runtime";
272
+ import { components } from "./components.manifest.js";
273
+
274
+ let defined = false;
275
+
276
+ export function defineCustomElements() {
277
+ if (defined) {
278
+ return;
279
+ }
280
+
281
+ if (typeof customElements === "undefined") {
282
+ return;
283
+ }
284
+
285
+ bootstrapLazy(components);
286
+
287
+ defined = true;
288
+ }
289
+
290
+ export const defineLazyElements = defineCustomElements;
291
+ `;
292
+ }
293
+ function generateAutoEntry() {
294
+ return `import { defineCustomElements } from "./loader.js";
295
+
296
+ defineCustomElements();
297
+
298
+ export {};
299
+ `;
300
+ }
301
+ function generateLazyIndex() {
302
+ return `export {
303
+ defineCustomElements,
304
+ defineLazyElements,
305
+ } from "./loader.js";
306
+ `;
307
+ }
308
+ //#endregion
179
309
  //#region packages/web-c/output-wc/src/generateManifest.ts
180
310
  function generateZeusComponentsManifest(manifest) {
181
311
  return `${JSON.stringify(manifest, null, 2)}\n`;
@@ -183,17 +313,21 @@ function generateZeusComponentsManifest(manifest) {
183
313
  //#endregion
184
314
  //#region packages/web-c/output-wc/src/index.ts
185
315
  function wc(options = {}) {
186
- var _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa;
316
+ var _options$register, _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa, _options$auto, _options$entryFileNam;
317
+ const registerMode = (_options$register = options.register) !== null && _options$register !== void 0 ? _options$register : "lazy";
187
318
  const normalized = {
188
319
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
189
320
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
190
321
  fileName: options.fileName,
191
- manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
192
- customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
193
- dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : "auto",
194
- jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : "auto",
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",
324
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
325
+ jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
195
326
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
196
- warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true
327
+ warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true,
328
+ register: registerMode,
329
+ auto: (_options$auto = options.auto) !== null && _options$auto !== void 0 ? _options$auto : true,
330
+ entryFileName: (_options$entryFileNam = options.entryFileName) !== null && _options$entryFileNam !== void 0 ? _options$entryFileNam : ((tag) => `${tag}.entry`)
197
331
  };
198
332
  let _outDir = normalized.outDir;
199
333
  return {
@@ -208,7 +342,32 @@ function wc(options = {}) {
208
342
  },
209
343
  buildStart(ctx) {
210
344
  if (!ctx.manifest) return;
211
- checkFileNameCollisions(ctx.manifest.components, normalized, { warn: ctx.warn });
345
+ if (normalized.register === "lazy") for (const component of ctx.manifest.components) {
346
+ var _component$runtimePro, _component$runtimePro2;
347
+ const diagnostics = (_component$runtimePro = component.runtimePropsDiagnostics) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : [];
348
+ if (diagnostics.length > 0) ctx.error([`[zeus-output-wc] <${component.tag}> cannot be emitted with register:"lazy" because its runtime props are not statically analyzable.`, ...diagnostics.map((message) => `- ${message}`)].join("\n"));
349
+ const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
350
+ for (const [name, prop] of Object.entries(runtimeProps)) {
351
+ if (isAttributeBackedRuntimeProp(prop.type)) continue;
352
+ if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
353
+ `[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."
356
+ ].join("\n"));
357
+ if (prop.reflect) ctx.error([
358
+ `[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."
361
+ ].join("\n"));
362
+ }
363
+ }
364
+ checkFileNameCollisions(ctx.manifest.components, {
365
+ getFileName: (tag) => {
366
+ if (normalized.register === "lazy") return getLazyEntryFileName(normalized.entryFileName, tag);
367
+ return normalizeFileName(tag, normalized.stripPrefix, normalized.fileName);
368
+ },
369
+ warnOnFileNameCollision: normalized.warnOnFileNameCollision
370
+ }, { warn: ctx.warn });
212
371
  },
213
372
  virtualModules(ctx) {
214
373
  if (!ctx.manifest) return [];
@@ -222,7 +381,55 @@ function wc(options = {}) {
222
381
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
223
382
  return node_path.default.posix.join(_outDir, fileName);
224
383
  };
225
- for (const component of ctx.manifest.components) {
384
+ const isLazy = normalized.register === "lazy";
385
+ if (isLazy) {
386
+ modules.push({
387
+ id: "zeus:wc:components.manifest",
388
+ fileName: joinPath("components.manifest.js"),
389
+ code: generateLazyManifest({
390
+ components: ctx.manifest.components,
391
+ getEntryFileName: (tag) => getLazyEntryFileName(normalized.entryFileName, tag)
392
+ })
393
+ });
394
+ modules.push({
395
+ id: "zeus:wc:loader",
396
+ fileName: joinPath("loader.js"),
397
+ code: generateLoader()
398
+ });
399
+ if (normalized.auto) modules.push({
400
+ id: "zeus:wc:auto",
401
+ fileName: joinPath("auto.js"),
402
+ code: generateAutoEntry()
403
+ });
404
+ }
405
+ for (const component of ctx.manifest.components) if (isLazy) {
406
+ /**
407
+ * Compatibility module consumed by Vue / React wrappers.
408
+ *
409
+ * It only registers lazy Proxy Elements.
410
+ * It does not import the real component implementation.
411
+ */
412
+ modules.push({
413
+ id: getVirtualComponentId(component),
414
+ code: `
415
+ import { defineCustomElements } from "zeus:wc:loader";
416
+
417
+ defineCustomElements();
418
+
419
+ export {};
420
+ `.trimStart()
421
+ });
422
+ const fileName = joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag));
423
+ modules.push({
424
+ id: `zeus:wc:entry:${component.tag}`,
425
+ fileName,
426
+ code: generateLazyEntry({
427
+ component,
428
+ outPath: fileName,
429
+ sourceImport: toAbsoluteImportPath(ctx.root, component.source)
430
+ })
431
+ });
432
+ } else {
226
433
  const fileName = joinPath(getFileName(component.tag));
227
434
  modules.push({
228
435
  id: getVirtualComponentId(component),
@@ -233,7 +440,7 @@ function wc(options = {}) {
233
440
  })
234
441
  });
235
442
  }
236
- if (normalized.index) modules.push({
443
+ if (normalized.index && !isLazy) modules.push({
237
444
  id: getVirtualIndexId(),
238
445
  fileName: joinPath("index.js"),
239
446
  code: generateWCIndex({
@@ -241,16 +448,18 @@ function wc(options = {}) {
241
448
  getFileName
242
449
  })
243
450
  });
451
+ if (isLazy && normalized.index) modules.push({
452
+ id: getVirtualIndexId(),
453
+ fileName: joinPath("index.js"),
454
+ code: generateLazyIndex()
455
+ });
244
456
  return modules;
245
457
  },
246
458
  generateBundle(ctx) {
247
459
  if (!ctx.manifest) return [];
248
460
  const files = [];
249
- if (normalized.manifestFile) files.push({
250
- type: "asset",
251
- fileName: normalized.manifestFile,
252
- source: generateZeusComponentsManifest(ctx.manifest)
253
- });
461
+ const dts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.dts, ctx);
462
+ const jsxDts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.jsxDts, ctx);
254
463
  const hasOutputs = ctx.outputs.has("wc");
255
464
  const getFileName = (tag) => {
256
465
  if (hasOutputs) return ctx.outputs.getFileName("wc", tag);
@@ -260,7 +469,32 @@ function wc(options = {}) {
260
469
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
261
470
  return node_path.default.posix.join(_outDir, fileName);
262
471
  };
263
- if (normalized.customElementsFile) files.push({
472
+ const isLazy = normalized.register === "lazy";
473
+ if (isLazy) {
474
+ if (dts) {
475
+ const loaderDts = (0, _zeus_js_component_dts.generateLoaderDts)(ctx.manifest);
476
+ files.push({
477
+ type: "asset",
478
+ fileName: joinPath("loader.d.ts"),
479
+ source: loaderDts
480
+ });
481
+ if (normalized.index) files.push({
482
+ type: "asset",
483
+ fileName: joinPath("index.d.ts"),
484
+ source: loaderDts
485
+ });
486
+ }
487
+ if (jsxDts) files.push({
488
+ type: "asset",
489
+ fileName: joinPath("types/jsx.d.ts"),
490
+ source: (0, _zeus_js_component_dts.generateWCJsxDts)(ctx.manifest)
491
+ });
492
+ } else if (normalized.manifestFile) files.push({
493
+ type: "asset",
494
+ fileName: normalized.manifestFile,
495
+ source: generateZeusComponentsManifest(ctx.manifest)
496
+ });
497
+ if (!isLazy && normalized.customElementsFile) files.push({
264
498
  type: "asset",
265
499
  fileName: normalized.customElementsFile,
266
500
  source: generateCustomElementsJson({
@@ -268,9 +502,7 @@ function wc(options = {}) {
268
502
  getModulePath: (component) => joinPath(getFileName(component.tag))
269
503
  })
270
504
  });
271
- const dts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.dts, ctx);
272
- const jsxDts = (0, _zeus_js_bundler_plugin.resolvePluginDts)(normalized.jsxDts, ctx);
273
- if (dts || jsxDts) {
505
+ if (!isLazy && (dts || jsxDts)) {
274
506
  const dtsFiles = (0, _zeus_js_component_dts.generateWCDtsFiles)(ctx.manifest, {
275
507
  outDir: _outDir,
276
508
  stripPrefix: normalized.stripPrefix,
@@ -299,12 +531,22 @@ function normalizeFileName(tag, stripPrefix, fileName) {
299
531
  if (!name.endsWith(".js")) name = `${name}.js`;
300
532
  return name;
301
533
  }
534
+ function normalizeLazyEntryFileName(fileName) {
535
+ const normalized = fileName.replace(/\\/g, "/");
536
+ return normalized.endsWith(".js") ? normalized : `${normalized}.js`;
537
+ }
538
+ function getLazyEntryFileName(entryFileName, tag) {
539
+ return normalizeLazyEntryFileName(entryFileName(tag));
540
+ }
541
+ function isAttributeBackedRuntimeProp(type) {
542
+ return type === "string" || type === "number" || type === "boolean";
543
+ }
302
544
  function checkFileNameCollisions(components, options, reporter) {
303
545
  if (options.warnOnFileNameCollision === false) return;
304
546
  const map = /* @__PURE__ */ new Map();
305
547
  for (const component of components) {
306
548
  var _map$get;
307
- const fileName = normalizeFileName(component.tag, options.stripPrefix, options.fileName);
549
+ const fileName = options.getFileName(component.tag);
308
550
  const list = (_map$get = map.get(fileName)) !== null && _map$get !== void 0 ? _map$get : [];
309
551
  list.push(component);
310
552
  map.set(fileName, list);
@@ -1,5 +1,6 @@
1
1
  import { DtsMode, ZeusComponentPlugin } from '@zeus-js/bundler-plugin';
2
2
 
3
+ type WebCRegisterMode = 'lazy' | 'side-effect';
3
4
  export interface OutputWCOptions {
4
5
  /**
5
6
  * Web Component output directory.
@@ -35,13 +36,13 @@ export interface OutputWCOptions {
35
36
  /**
36
37
  * Generate WC d.ts.
37
38
  *
38
- * @default 'auto'
39
+ * @default true
39
40
  */
40
41
  dts?: DtsMode;
41
42
  /**
42
43
  * Generate JSX IntrinsicElements d.ts.
43
44
  *
44
- * @default 'auto'
45
+ * @default true
45
46
  */
46
47
  jsxDts?: DtsMode;
47
48
  /**
@@ -56,6 +57,30 @@ export interface OutputWCOptions {
56
57
  * @default true
57
58
  */
58
59
  warnOnFileNameCollision?: boolean;
60
+ /**
61
+ * lazy:
62
+ * Default. Generates Stencil-style lazy loader.
63
+ * On startup, registers lightweight ProxyClass; loads real component entry
64
+ * only when the element is connected to the DOM.
65
+ *
66
+ * side-effect:
67
+ * Immediately registers full components on import.
68
+ * Compatible with legacy behavior; not recommended as default.
69
+ */
70
+ register?: WebCRegisterMode;
71
+ /**
72
+ * Whether to generate the auto.js entry (lazy mode).
73
+ *
74
+ * @default true
75
+ */
76
+ auto?: boolean;
77
+ /**
78
+ * File name for lazy mode entry chunks.
79
+ * Receives the tag name, should return the file name (without .js).
80
+ *
81
+ * @default (tag) => `${tag}.entry`
82
+ */
83
+ entryFileName?: (tag: string) => string;
59
84
  }
60
85
 
61
86
  export declare function wc(options?: OutputWCOptions): ZeusComponentPlugin;
@@ -1,11 +1,11 @@
1
1
  /**
2
- * output-wc v0.1.0-beta.2
2
+ * output-wc v0.1.0-beta.3
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
6
6
  import path from "node:path";
7
7
  import { resolvePluginDts } from "@zeus-js/bundler-plugin";
8
- import { generateWCDtsFiles } from "@zeus-js/component-dts";
8
+ import { generateLoaderDts, generateWCDtsFiles, generateWCJsxDts } from "@zeus-js/component-dts";
9
9
  //#region packages/web-c/output-wc/src/generateCustomElementsJson.ts
10
10
  function generateCustomElementsJson(options) {
11
11
  const { manifest, getModulePath } = options;
@@ -149,6 +149,136 @@ function getVirtualIndexId() {
149
149
  return "zeus:wc:index";
150
150
  }
151
151
  //#endregion
152
+ //#region packages/web-c/output-wc/src/generateLazyEntry.ts
153
+ function generateLazyEntry(options) {
154
+ const { component, outPath, sourceImport } = options;
155
+ const source = sourceImport !== null && sourceImport !== void 0 ? sourceImport : toRelativeImport(component.source, outPath);
156
+ return [
157
+ `import { mountElementDefinition } from "@zeus-js/runtime-dom";`,
158
+ `import { ${component.exportName} } from ${JSON.stringify(source)};`,
159
+ "",
160
+ `export function createComponent(hostRef) {`,
161
+ ` let mounted;`,
162
+ ` const mountState = {};`,
163
+ "",
164
+ ` return {`,
165
+ ` connected() {`,
166
+ ` if (mounted) return;`,
167
+ "",
168
+ ` mounted = mountElementDefinition(`,
169
+ ` ${component.exportName},`,
170
+ ` hostRef.host,`,
171
+ ` hostRef.values,`,
172
+ ` mountState,`,
173
+ ` );`,
174
+ ` },`,
175
+ "",
176
+ ` disconnected() {`,
177
+ ` mounted?.dispose();`,
178
+ ` mounted = undefined;`,
179
+ ` },`,
180
+ "",
181
+ ` propertyChanged(name, oldValue, newValue) {`,
182
+ ` mounted?.propertyChanged(name, oldValue, newValue);`,
183
+ ` },`,
184
+ ` };`,
185
+ `}`,
186
+ "",
187
+ `export default { createComponent };`,
188
+ ""
189
+ ].join("\n");
190
+ }
191
+ function toRelativeImport(source, outPath) {
192
+ const sourceParts = normalizePath(source).split("/");
193
+ const outParts = normalizePath(outPath).split("/");
194
+ let common = 0;
195
+ for (let i = 0; i < Math.min(sourceParts.length, outParts.length); i++) if (sourceParts[i] === outParts[i]) common++;
196
+ else break;
197
+ return [...outParts.slice(common).map(() => ".."), ...sourceParts.slice(common)].join("/");
198
+ }
199
+ //#endregion
200
+ //#region packages/web-c/output-wc/src/generateLazyManifest.ts
201
+ function generateLazyManifest(options) {
202
+ const { components, getEntryFileName } = options;
203
+ return `export const components = [
204
+ ${components.map((component) => {
205
+ var _component$runtimePro, _component$meta$shado, _component$meta;
206
+ const entryFile = getEntryFileName(component.tag).replace(/\\/g, "/");
207
+ const props = generatePropsArray((_component$runtimePro = component.runtimeProps) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : component.props);
208
+ const importPath = entryFile.startsWith(".") ? JSON.stringify(entryFile) : JSON.stringify(`./${entryFile}`);
209
+ return ` {
210
+ tagName: ${JSON.stringify(component.tag)},
211
+ 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},
212
+ load: () => import(${importPath}),
213
+ props: ${props},
214
+ }`;
215
+ }).join(",\n")}
216
+ ];
217
+ `;
218
+ }
219
+ function generatePropsArray(props) {
220
+ const entries = Object.entries(props);
221
+ if (entries.length === 0) return "[]";
222
+ return `[\n${entries.map(([name, prop]) => {
223
+ const parts = [`name: ${JSON.stringify(name)}`];
224
+ if (!isAttributeBackedType(prop.type) || prop.attr === false) parts.push("attrName: false");
225
+ else {
226
+ var _prop$attr;
227
+ const attrName = (_prop$attr = prop.attr) !== null && _prop$attr !== void 0 ? _prop$attr : toKebabCase(name);
228
+ if (attrName !== toKebabCase(name)) parts.push(`attrName: ${JSON.stringify(attrName)}`);
229
+ }
230
+ parts.push(`type: ${JSON.stringify(prop.type)}`);
231
+ if (prop.reflect && isAttributeBackedType(prop.type)) parts.push("reflect: true");
232
+ return ` { ${parts.join(", ")} }`;
233
+ }).join(",\n")}\n ]`;
234
+ }
235
+ function isAttributeBackedType(type) {
236
+ return type === "string" || type === "number" || type === "boolean";
237
+ }
238
+ function toKebabCase(value) {
239
+ return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
240
+ }
241
+ //#endregion
242
+ //#region packages/web-c/output-wc/src/generateLoader.ts
243
+ function generateLoader() {
244
+ return `import { bootstrapLazy } from "@zeus-js/web-c-runtime";
245
+ import { components } from "./components.manifest.js";
246
+
247
+ let defined = false;
248
+
249
+ export function defineCustomElements() {
250
+ if (defined) {
251
+ return;
252
+ }
253
+
254
+ if (typeof customElements === "undefined") {
255
+ return;
256
+ }
257
+
258
+ bootstrapLazy(components);
259
+
260
+ defined = true;
261
+ }
262
+
263
+ export const defineLazyElements = defineCustomElements;
264
+ `;
265
+ }
266
+ function generateAutoEntry() {
267
+ return `import { defineCustomElements } from "./loader.js";
268
+
269
+ defineCustomElements();
270
+
271
+ export {};
272
+ `;
273
+ }
274
+ function generateLazyIndex() {
275
+ return `export {
276
+ defineCustomElements,
277
+ defineLazyElements,
278
+ } from "./loader.js";
279
+ `;
280
+ }
281
+ //#endregion
152
282
  //#region packages/web-c/output-wc/src/generateManifest.ts
153
283
  function generateZeusComponentsManifest(manifest) {
154
284
  return `${JSON.stringify(manifest, null, 2)}\n`;
@@ -156,17 +286,21 @@ function generateZeusComponentsManifest(manifest) {
156
286
  //#endregion
157
287
  //#region packages/web-c/output-wc/src/index.ts
158
288
  function wc(options = {}) {
159
- var _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa;
289
+ var _options$register, _options$outDir, _options$stripPrefix, _options$manifestFile, _options$customElemen, _options$dts, _options$jsxDts, _options$index, _options$warnOnFileNa, _options$auto, _options$entryFileNam;
290
+ const registerMode = (_options$register = options.register) !== null && _options$register !== void 0 ? _options$register : "lazy";
160
291
  const normalized = {
161
292
  outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "wc",
162
293
  stripPrefix: (_options$stripPrefix = options.stripPrefix) !== null && _options$stripPrefix !== void 0 ? _options$stripPrefix : false,
163
294
  fileName: options.fileName,
164
- manifestFile: (_options$manifestFile = options.manifestFile) !== null && _options$manifestFile !== void 0 ? _options$manifestFile : "zeus.components.json",
165
- customElementsFile: (_options$customElemen = options.customElementsFile) !== null && _options$customElemen !== void 0 ? _options$customElemen : "custom-elements.json",
166
- dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : "auto",
167
- jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : "auto",
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",
297
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true,
298
+ jsxDts: (_options$jsxDts = options.jsxDts) !== null && _options$jsxDts !== void 0 ? _options$jsxDts : true,
168
299
  index: (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : true,
169
- warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true
300
+ warnOnFileNameCollision: (_options$warnOnFileNa = options.warnOnFileNameCollision) !== null && _options$warnOnFileNa !== void 0 ? _options$warnOnFileNa : true,
301
+ register: registerMode,
302
+ auto: (_options$auto = options.auto) !== null && _options$auto !== void 0 ? _options$auto : true,
303
+ entryFileName: (_options$entryFileNam = options.entryFileName) !== null && _options$entryFileNam !== void 0 ? _options$entryFileNam : ((tag) => `${tag}.entry`)
170
304
  };
171
305
  let _outDir = normalized.outDir;
172
306
  return {
@@ -181,7 +315,32 @@ function wc(options = {}) {
181
315
  },
182
316
  buildStart(ctx) {
183
317
  if (!ctx.manifest) return;
184
- checkFileNameCollisions(ctx.manifest.components, normalized, { warn: ctx.warn });
318
+ if (normalized.register === "lazy") for (const component of ctx.manifest.components) {
319
+ var _component$runtimePro, _component$runtimePro2;
320
+ const diagnostics = (_component$runtimePro = component.runtimePropsDiagnostics) !== null && _component$runtimePro !== void 0 ? _component$runtimePro : [];
321
+ if (diagnostics.length > 0) ctx.error([`[zeus-output-wc] <${component.tag}> cannot be emitted with register:"lazy" because its runtime props are not statically analyzable.`, ...diagnostics.map((message) => `- ${message}`)].join("\n"));
322
+ const runtimeProps = (_component$runtimePro2 = component.runtimeProps) !== null && _component$runtimePro2 !== void 0 ? _component$runtimePro2 : {};
323
+ for (const [name, prop] of Object.entries(runtimeProps)) {
324
+ if (isAttributeBackedRuntimeProp(prop.type)) continue;
325
+ if (prop.attr !== void 0 && prop.attr !== false) ctx.error([
326
+ `[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."
329
+ ].join("\n"));
330
+ if (prop.reflect) ctx.error([
331
+ `[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."
334
+ ].join("\n"));
335
+ }
336
+ }
337
+ checkFileNameCollisions(ctx.manifest.components, {
338
+ getFileName: (tag) => {
339
+ if (normalized.register === "lazy") return getLazyEntryFileName(normalized.entryFileName, tag);
340
+ return normalizeFileName(tag, normalized.stripPrefix, normalized.fileName);
341
+ },
342
+ warnOnFileNameCollision: normalized.warnOnFileNameCollision
343
+ }, { warn: ctx.warn });
185
344
  },
186
345
  virtualModules(ctx) {
187
346
  if (!ctx.manifest) return [];
@@ -195,7 +354,55 @@ function wc(options = {}) {
195
354
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
196
355
  return path.posix.join(_outDir, fileName);
197
356
  };
198
- for (const component of ctx.manifest.components) {
357
+ const isLazy = normalized.register === "lazy";
358
+ if (isLazy) {
359
+ modules.push({
360
+ id: "zeus:wc:components.manifest",
361
+ fileName: joinPath("components.manifest.js"),
362
+ code: generateLazyManifest({
363
+ components: ctx.manifest.components,
364
+ getEntryFileName: (tag) => getLazyEntryFileName(normalized.entryFileName, tag)
365
+ })
366
+ });
367
+ modules.push({
368
+ id: "zeus:wc:loader",
369
+ fileName: joinPath("loader.js"),
370
+ code: generateLoader()
371
+ });
372
+ if (normalized.auto) modules.push({
373
+ id: "zeus:wc:auto",
374
+ fileName: joinPath("auto.js"),
375
+ code: generateAutoEntry()
376
+ });
377
+ }
378
+ for (const component of ctx.manifest.components) if (isLazy) {
379
+ /**
380
+ * Compatibility module consumed by Vue / React wrappers.
381
+ *
382
+ * It only registers lazy Proxy Elements.
383
+ * It does not import the real component implementation.
384
+ */
385
+ modules.push({
386
+ id: getVirtualComponentId(component),
387
+ code: `
388
+ import { defineCustomElements } from "zeus:wc:loader";
389
+
390
+ defineCustomElements();
391
+
392
+ export {};
393
+ `.trimStart()
394
+ });
395
+ const fileName = joinPath(getLazyEntryFileName(normalized.entryFileName, component.tag));
396
+ modules.push({
397
+ id: `zeus:wc:entry:${component.tag}`,
398
+ fileName,
399
+ code: generateLazyEntry({
400
+ component,
401
+ outPath: fileName,
402
+ sourceImport: toAbsoluteImportPath(ctx.root, component.source)
403
+ })
404
+ });
405
+ } else {
199
406
  const fileName = joinPath(getFileName(component.tag));
200
407
  modules.push({
201
408
  id: getVirtualComponentId(component),
@@ -206,7 +413,7 @@ function wc(options = {}) {
206
413
  })
207
414
  });
208
415
  }
209
- if (normalized.index) modules.push({
416
+ if (normalized.index && !isLazy) modules.push({
210
417
  id: getVirtualIndexId(),
211
418
  fileName: joinPath("index.js"),
212
419
  code: generateWCIndex({
@@ -214,16 +421,18 @@ function wc(options = {}) {
214
421
  getFileName
215
422
  })
216
423
  });
424
+ if (isLazy && normalized.index) modules.push({
425
+ id: getVirtualIndexId(),
426
+ fileName: joinPath("index.js"),
427
+ code: generateLazyIndex()
428
+ });
217
429
  return modules;
218
430
  },
219
431
  generateBundle(ctx) {
220
432
  if (!ctx.manifest) return [];
221
433
  const files = [];
222
- if (normalized.manifestFile) files.push({
223
- type: "asset",
224
- fileName: normalized.manifestFile,
225
- source: generateZeusComponentsManifest(ctx.manifest)
226
- });
434
+ const dts = resolvePluginDts(normalized.dts, ctx);
435
+ const jsxDts = resolvePluginDts(normalized.jsxDts, ctx);
227
436
  const hasOutputs = ctx.outputs.has("wc");
228
437
  const getFileName = (tag) => {
229
438
  if (hasOutputs) return ctx.outputs.getFileName("wc", tag);
@@ -233,7 +442,32 @@ function wc(options = {}) {
233
442
  if (hasOutputs) return ctx.outputs.join("wc", fileName);
234
443
  return path.posix.join(_outDir, fileName);
235
444
  };
236
- if (normalized.customElementsFile) files.push({
445
+ const isLazy = normalized.register === "lazy";
446
+ if (isLazy) {
447
+ if (dts) {
448
+ const loaderDts = generateLoaderDts(ctx.manifest);
449
+ files.push({
450
+ type: "asset",
451
+ fileName: joinPath("loader.d.ts"),
452
+ source: loaderDts
453
+ });
454
+ if (normalized.index) files.push({
455
+ type: "asset",
456
+ fileName: joinPath("index.d.ts"),
457
+ source: loaderDts
458
+ });
459
+ }
460
+ if (jsxDts) files.push({
461
+ type: "asset",
462
+ fileName: joinPath("types/jsx.d.ts"),
463
+ source: generateWCJsxDts(ctx.manifest)
464
+ });
465
+ } else if (normalized.manifestFile) files.push({
466
+ type: "asset",
467
+ fileName: normalized.manifestFile,
468
+ source: generateZeusComponentsManifest(ctx.manifest)
469
+ });
470
+ if (!isLazy && normalized.customElementsFile) files.push({
237
471
  type: "asset",
238
472
  fileName: normalized.customElementsFile,
239
473
  source: generateCustomElementsJson({
@@ -241,9 +475,7 @@ function wc(options = {}) {
241
475
  getModulePath: (component) => joinPath(getFileName(component.tag))
242
476
  })
243
477
  });
244
- const dts = resolvePluginDts(normalized.dts, ctx);
245
- const jsxDts = resolvePluginDts(normalized.jsxDts, ctx);
246
- if (dts || jsxDts) {
478
+ if (!isLazy && (dts || jsxDts)) {
247
479
  const dtsFiles = generateWCDtsFiles(ctx.manifest, {
248
480
  outDir: _outDir,
249
481
  stripPrefix: normalized.stripPrefix,
@@ -272,12 +504,22 @@ function normalizeFileName(tag, stripPrefix, fileName) {
272
504
  if (!name.endsWith(".js")) name = `${name}.js`;
273
505
  return name;
274
506
  }
507
+ function normalizeLazyEntryFileName(fileName) {
508
+ const normalized = fileName.replace(/\\/g, "/");
509
+ return normalized.endsWith(".js") ? normalized : `${normalized}.js`;
510
+ }
511
+ function getLazyEntryFileName(entryFileName, tag) {
512
+ return normalizeLazyEntryFileName(entryFileName(tag));
513
+ }
514
+ function isAttributeBackedRuntimeProp(type) {
515
+ return type === "string" || type === "number" || type === "boolean";
516
+ }
275
517
  function checkFileNameCollisions(components, options, reporter) {
276
518
  if (options.warnOnFileNameCollision === false) return;
277
519
  const map = /* @__PURE__ */ new Map();
278
520
  for (const component of components) {
279
521
  var _map$get;
280
- const fileName = normalizeFileName(component.tag, options.stripPrefix, options.fileName);
522
+ const fileName = options.getFileName(component.tag);
281
523
  const list = (_map$get = map.get(fileName)) !== null && _map$get !== void 0 ? _map$get : [];
282
524
  list.push(component);
283
525
  map.set(fileName, list);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeus-js/output-wc",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Zeus Web Component output plugin",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -13,24 +13,24 @@
13
13
  "exports": {
14
14
  ".": {
15
15
  "types": "./dist/output-wc.d.ts",
16
+ "module": "./dist/output-wc.esm-bundler.js",
17
+ "import": "./dist/output-wc.esm-bundler.js",
18
+ "require": "./index.js",
16
19
  "node": {
17
20
  "production": "./dist/output-wc.cjs.prod.js",
18
21
  "development": "./dist/output-wc.cjs.js",
19
22
  "default": "./index.js"
20
- },
21
- "module": "./dist/output-wc.esm-bundler.js",
22
- "import": "./dist/output-wc.esm-bundler.js",
23
- "require": "./index.js"
23
+ }
24
24
  },
25
25
  "./capabilities": {
26
26
  "types": "./dist/capabilities.d.ts",
27
+ "import": "./dist/capabilities.js",
28
+ "require": "./dist/capabilities.cjs.js",
27
29
  "node": {
28
30
  "production": "./dist/capabilities.cjs.prod.js",
29
31
  "development": "./dist/capabilities.cjs.js",
30
32
  "default": "./dist/capabilities.cjs.js"
31
- },
32
- "import": "./dist/capabilities.js",
33
- "require": "./dist/capabilities.cjs.js"
33
+ }
34
34
  }
35
35
  },
36
36
  "sideEffects": false,
@@ -52,9 +52,11 @@
52
52
  ]
53
53
  },
54
54
  "dependencies": {
55
- "@zeus-js/bundler-plugin": "0.1.0-beta.2",
56
- "@zeus-js/component-analyzer": "0.1.0-beta.2",
57
- "@zeus-js/component-dts": "0.1.0-beta.2"
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"
58
60
  },
59
61
  "peerDependencies": {
60
62
  "rollup": "^4.0.0"