@sprlab/wccompiler 0.7.1 → 0.7.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.
package/README.md CHANGED
@@ -656,12 +656,21 @@ export default defineConfig({
656
656
 
657
657
  ### React
658
658
 
659
- React 19+ supports custom elements natively. For React 18, use the event hook:
659
+ React 19+ supports custom elements natively. For React 18, use the event hook to bridge CustomEvents:
660
660
 
661
661
  ```jsx
662
+ import { useRef } from 'react'
662
663
  import { useWccEvent } from '@sprlab/wccompiler/integrations/react'
663
664
 
664
665
  function App() {
666
+ // Form 1: Pass an existing ref
667
+ const counterRef = useRef(null)
668
+ useWccEvent(counterRef, 'change', (e) => console.log(e.detail))
669
+ return <wcc-counter ref={counterRef}></wcc-counter>
670
+ }
671
+
672
+ // Form 2: Let the hook create the ref
673
+ function App2() {
665
674
  const ref = useWccEvent('change', (e) => console.log(e.detail))
666
675
  return <wcc-counter ref={ref}></wcc-counter>
667
676
  }
@@ -669,19 +678,23 @@ function App() {
669
678
 
670
679
  ### Angular
671
680
 
681
+ Add `CUSTOM_ELEMENTS_SCHEMA` to your component or module — this is Angular's built-in way to allow custom elements:
682
+
672
683
  ```ts
673
- import { WCC_SCHEMAS } from '@sprlab/wccompiler/integrations/angular'
684
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
674
685
 
675
686
  // Standalone component (Angular 17+)
676
687
  @Component({
677
- schemas: WCC_SCHEMAS,
688
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
678
689
  template: `<wcc-counter></wcc-counter>`
679
690
  })
691
+ export class AppComponent {}
680
692
 
681
693
  // Or NgModule approach
682
694
  @NgModule({
683
- schemas: WCC_SCHEMAS,
695
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
684
696
  })
697
+ export class AppModule {}
685
698
  ```
686
699
 
687
700
  ### Vanilla
@@ -1,31 +1,48 @@
1
1
  /**
2
- * Angular schema helper for WCC custom elements.
3
- * Provides CUSTOM_ELEMENTS_SCHEMA configuration for Angular modules/components.
2
+ * Angular integration guide for WCC custom elements.
4
3
  *
5
4
  * @module @sprlab/wccompiler/integrations/angular
6
- */
7
-
8
- import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
9
-
10
- /**
11
- * Schema array for Angular components/modules that use WCC custom elements.
12
- * Use in @Component({ schemas: WCC_SCHEMAS }) or @NgModule({ schemas: WCC_SCHEMAS })
13
5
  *
14
- * @type {Array<import('@angular/core').SchemaMetadata>}
6
+ * Angular's AOT compiler requires schemas to be statically analyzable,
7
+ * so we cannot provide a re-exported schema constant that works at compile time.
8
+ * Instead, use Angular's built-in CUSTOM_ELEMENTS_SCHEMA directly:
9
+ *
10
+ * @example Standalone component (Angular 17+)
11
+ * ```ts
12
+ * import { Component } from '@angular/core';
13
+ * import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
14
+ *
15
+ * @Component({
16
+ * selector: 'app-root',
17
+ * schemas: [CUSTOM_ELEMENTS_SCHEMA],
18
+ * template: `<wcc-counter></wcc-counter>`
19
+ * })
20
+ * export class AppComponent {}
21
+ * ```
22
+ *
23
+ * @example NgModule approach
24
+ * ```ts
25
+ * import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
26
+ *
27
+ * @NgModule({
28
+ * schemas: [CUSTOM_ELEMENTS_SCHEMA],
29
+ * })
30
+ * export class AppModule {}
31
+ * ```
32
+ *
33
+ * That's it — one line of config. WCC components work as native custom elements
34
+ * in Angular without any additional wrapper or helper.
15
35
  */
16
- export const WCC_SCHEMAS = [CUSTOM_ELEMENTS_SCHEMA]
17
36
 
18
37
  /**
19
- * NgModule-compatible class that declares CUSTOM_ELEMENTS_SCHEMA.
20
- * Import this in your NgModule's imports array.
21
- *
22
- * Usage:
23
- * @NgModule({ imports: [WccModule] })
24
- * export class AppModule {}
38
+ * Configuration instructions for Angular projects using WCC components.
39
+ * This is a documentation-only export — Angular's AOT compiler requires
40
+ * CUSTOM_ELEMENTS_SCHEMA to be imported directly from @angular/core.
25
41
  *
26
- * Note: For standalone components (Angular 17+), use WCC_SCHEMAS directly:
27
- * @Component({ schemas: WCC_SCHEMAS })
42
+ * @type {{ schema: string, standalone: string, ngModule: string }}
28
43
  */
29
- export class WccModule {
30
- static schemas = WCC_SCHEMAS
44
+ export const WCC_ANGULAR_CONFIG = {
45
+ schema: "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'",
46
+ standalone: "@Component({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })",
47
+ ngModule: "@NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })",
31
48
  }
@@ -3,6 +3,16 @@
3
3
  * Bridges CustomEvent to React's ref-based event system.
4
4
  *
5
5
  * @module @sprlab/wccompiler/integrations/react
6
+ *
7
+ * Usage:
8
+ * // Form 1: Pass an existing ref
9
+ * const ref = useRef(null)
10
+ * useWccEvent(ref, 'change', (e) => console.log(e.detail))
11
+ * <wcc-counter ref={ref}></wcc-counter>
12
+ *
13
+ * // Form 2: Let the hook create the ref
14
+ * const ref = useWccEvent('change', (e) => console.log(e.detail))
15
+ * <wcc-counter ref={ref}></wcc-counter>
6
16
  */
7
17
 
8
18
  import { useRef, useEffect } from 'react'
@@ -10,22 +20,33 @@ import { useRef, useEffect } from 'react'
10
20
  /**
11
21
  * Hook that attaches a CustomEvent listener to a DOM element via ref.
12
22
  *
13
- * @param {string} eventName - The event name to listen for
14
- * @param {(event: CustomEvent) => void} handler - Event handler callback
15
- * @returns {import('react').RefObject<HTMLElement>}
23
+ * Supports two calling conventions:
24
+ * - useWccEvent(ref, eventName, handler) uses an existing ref
25
+ * - useWccEvent(eventName, handler) — creates and returns a new ref
26
+ *
27
+ * @param {import('react').RefObject<HTMLElement> | string} refOrEventName
28
+ * @param {string | ((event: CustomEvent) => void)} eventNameOrHandler
29
+ * @param {((event: CustomEvent) => void)} [handler]
30
+ * @returns {import('react').RefObject<HTMLElement> | void}
16
31
  */
17
- export function useWccEvent(eventName, handler) {
18
- const ref = useRef(null)
19
- const handlerRef = useRef(handler)
20
- handlerRef.current = handler
32
+ export function useWccEvent(refOrEventName, eventNameOrHandler, handler) {
33
+ // Detect calling convention
34
+ const isRefForm = typeof refOrEventName !== 'string'
35
+ const elementRef = isRefForm ? refOrEventName : useRef(null)
36
+ const eventName = isRefForm ? eventNameOrHandler : refOrEventName
37
+ const callback = isRefForm ? handler : eventNameOrHandler
38
+
39
+ const handlerRef = useRef(callback)
40
+ handlerRef.current = callback
21
41
 
22
42
  useEffect(() => {
23
- const el = ref.current
43
+ const el = elementRef.current
24
44
  if (!el) return
25
45
  const listener = (e) => handlerRef.current(e)
26
46
  el.addEventListener(eventName, listener)
27
47
  return () => el.removeEventListener(eventName, listener)
28
48
  }, [eventName])
29
49
 
30
- return ref
50
+ // Only return ref if we created it (Form 2)
51
+ if (!isRefForm) return elementRef
31
52
  }
package/lib/codegen.js CHANGED
@@ -1186,7 +1186,9 @@ export function generateComponent(parseResult, options = {}) {
1186
1186
  lines.push(' this.__disposers.push(__effect(() => {');
1187
1187
  lines.push(` const ${w.newParam} = ${watchRef};`);
1188
1188
  lines.push(` if (this.__prev_${w.target} !== undefined && this.__prev_${w.target} !== ${w.newParam}) {`);
1189
- lines.push(` const ${w.oldParam} = this.__prev_${w.target};`);
1189
+ if (w.oldParam) {
1190
+ lines.push(` const ${w.oldParam} = this.__prev_${w.target};`);
1191
+ }
1190
1192
  lines.push(' __untrack(() => {');
1191
1193
  const bodyLines = body.split('\n');
1192
1194
  for (const line of bodyLines) {
@@ -1203,7 +1205,9 @@ export function generateComponent(parseResult, options = {}) {
1203
1205
  lines.push(' this.__disposers.push(__effect(() => {');
1204
1206
  lines.push(` const ${w.newParam} = ${getterExpr};`);
1205
1207
  lines.push(` if (this.${prevName} !== undefined && this.${prevName} !== ${w.newParam}) {`);
1206
- lines.push(` const ${w.oldParam} = this.${prevName};`);
1208
+ if (w.oldParam) {
1209
+ lines.push(` const ${w.oldParam} = this.${prevName};`);
1210
+ }
1207
1211
  lines.push(' __untrack(() => {');
1208
1212
  const bodyLines2 = body.split('\n');
1209
1213
  for (const line of bodyLines2) {
@@ -782,10 +782,10 @@ export function extractWatchers(source) {
782
782
  while (i < lines.length) {
783
783
  const line = lines[i];
784
784
 
785
- // Form 2 — Getter function: watch(() => expr, (newVal, oldVal) => {
786
- const mGetter = line.match(/\bwatch\s*\(\s*\(\)\s*=>\s*(.+?)\s*,\s*\((\w+)\s*,\s*(\w+)\)\s*=>\s*\{/);
787
- // Form 1 — Signal direct: watch(identifier, (newVal, oldVal) => {
788
- const mSignal = !mGetter ? line.match(/\bwatch\s*\(\s*(\w+)\s*,\s*\((\w+)\s*,\s*(\w+)\)\s*=>\s*\{/) : null;
785
+ // Form 2 — Getter function: watch(() => expr, (newVal, oldVal) => { OR watch(() => expr, (newVal) => {
786
+ const mGetter = line.match(/\bwatch\s*\(\s*\(\)\s*=>\s*(.+?)\s*,\s*\((\w+)(?:\s*,\s*(\w+))?\)\s*=>\s*\{/);
787
+ // Form 1 — Signal direct: watch(identifier, (newVal, oldVal) => { OR watch(identifier, (newVal) => {
788
+ const mSignal = !mGetter ? line.match(/\bwatch\s*\(\s*(\w+)\s*,\s*\((\w+)(?:\s*,\s*(\w+))?\)\s*=>\s*\{/) : null;
789
789
 
790
790
  const m = mGetter || mSignal;
791
791
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprlab/wccompiler",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Zero-runtime compiler that transforms .wcc single-file components into native web components with signals-based reactivity",
5
5
  "type": "module",
6
6
  "exports": {