@sprlab/wccompiler 0.9.3 → 0.9.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.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * React browser-side hooks for WCC custom elements.
3
+ * Bridges CustomEvent to React's ref-based event system.
4
+ *
5
+ * @module @sprlab/wccompiler/adapters/react
6
+ *
7
+ * IMPORTANT: Import hooks from THIS file (not from integrations/react).
8
+ * The integrations/react file is for vite.config.js only (contains Babel).
9
+ *
10
+ * Usage:
11
+ * import { useWccEvent, useWccModel } from '@sprlab/wccompiler/adapters/react'
12
+ *
13
+ * // Listen to custom events
14
+ * const ref = useWccEvent('change', (e) => console.log(e.detail))
15
+ * <wcc-counter ref={ref}></wcc-counter>
16
+ *
17
+ * // Two-way binding with defineModel
18
+ * const [text, setText] = useState('')
19
+ * const inputRef = useWccModel('value', text, setText)
20
+ * <wcc-input ref={inputRef}></wcc-input>
21
+ */
22
+
23
+ import { useRef, useEffect } from 'react'
24
+
25
+ /**
26
+ * Hook that attaches a CustomEvent listener to a DOM element via ref.
27
+ *
28
+ * Supports two calling conventions:
29
+ * - useWccEvent(ref, eventName, handler) — uses an existing ref
30
+ * - useWccEvent(eventName, handler) — creates and returns a new ref
31
+ *
32
+ * @param {import('react').RefObject<HTMLElement> | string} refOrEventName
33
+ * @param {string | ((event: CustomEvent) => void)} eventNameOrHandler
34
+ * @param {((event: CustomEvent) => void)} [handler]
35
+ * @returns {import('react').RefObject<HTMLElement> | void}
36
+ */
37
+ export function useWccEvent(refOrEventName, eventNameOrHandler, handler) {
38
+ const isRefForm = typeof refOrEventName !== 'string'
39
+ const elementRef = isRefForm ? refOrEventName : useRef(null)
40
+ const eventName = isRefForm ? eventNameOrHandler : refOrEventName
41
+ const callback = isRefForm ? handler : eventNameOrHandler
42
+
43
+ const handlerRef = useRef(callback)
44
+ handlerRef.current = callback
45
+
46
+ useEffect(() => {
47
+ const el = elementRef.current
48
+ if (!el) return
49
+ const listener = (e) => handlerRef.current(e)
50
+ el.addEventListener(eventName, listener)
51
+ return () => el.removeEventListener(eventName, listener)
52
+ }, [eventName])
53
+
54
+ if (!isRefForm) return elementRef
55
+ }
56
+
57
+ /**
58
+ * Hook for two-way binding with WCC defineModel props.
59
+ *
60
+ * @param {string} propName - The model prop name (e.g., 'value', 'count')
61
+ * @param {*} value - Current React state value
62
+ * @param {(newValue: *) => void} setValue - React state setter
63
+ * @param {import('react').RefObject<HTMLElement>} [existingRef] - Optional existing ref
64
+ * @returns {import('react').RefObject<HTMLElement>} Ref to attach to the WCC element
65
+ *
66
+ * @example
67
+ * const [text, setText] = useState('')
68
+ * const inputRef = useWccModel('value', text, setText)
69
+ * <wcc-input ref={inputRef}></wcc-input>
70
+ */
71
+ export function useWccModel(propName, value, setValue, existingRef) {
72
+ const internalRef = useRef(null)
73
+ const elementRef = existingRef || internalRef
74
+
75
+ const setValueRef = useRef(setValue)
76
+ setValueRef.current = setValue
77
+
78
+ useEffect(() => {
79
+ const el = elementRef.current
80
+ if (!el) return
81
+
82
+ const listener = (e) => {
83
+ if (e.detail && e.detail.prop === propName) {
84
+ setValueRef.current(e.detail.value)
85
+ }
86
+ }
87
+
88
+ el.addEventListener('wcc:model', listener)
89
+ return () => el.removeEventListener('wcc:model', listener)
90
+ }, [propName])
91
+
92
+ useEffect(() => {
93
+ const el = elementRef.current
94
+ if (!el) return
95
+ if (value != null) {
96
+ el.setAttribute(propName, String(value))
97
+ } else {
98
+ el.removeAttribute(propName)
99
+ }
100
+ }, [propName, value])
101
+
102
+ return elementRef
103
+ }
@@ -1,126 +1,30 @@
1
1
  /**
2
- * React hook for WCC custom element events.
3
- * Bridges CustomEvent to React's ref-based event system.
2
+ * React Vite plugin for WCC custom elements.
3
+ * Transforms idiomatic React JSX slot patterns into WCC-compatible slot markup.
4
4
  *
5
5
  * @module @sprlab/wccompiler/integrations/react
6
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>
7
+ * IMPORTANT: This file is for vite.config.js (Node.js context) ONLY.
8
+ * For browser-side hooks, import from '@sprlab/wccompiler/adapters/react'.
12
9
  *
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>
10
+ * @example vite.config.js
11
+ * ```js
12
+ * import { wccReactPlugin } from '@sprlab/wccompiler/integrations/react'
13
+ * export default { plugins: [wccReactPlugin()] }
14
+ * ```
16
15
  *
17
- * // Form 3: Two-way binding with defineModel
18
- * const [value, setValue] = useState('')
19
- * const ref = useWccModel('value', value, setValue)
20
- * <wcc-input ref={ref}></wcc-input>
16
+ * @example Component (browser import hooks from adapters)
17
+ * ```jsx
18
+ * import { useWccEvent, useWccModel } from '@sprlab/wccompiler/adapters/react'
19
+ * ```
21
20
  */
22
21
 
23
- import { useRef, useEffect, useCallback } from 'react'
24
22
  import { parse } from '@babel/parser'
25
23
  import _traverse from '@babel/traverse'
26
24
  import _generate from '@babel/generator'
27
25
  const traverse = _traverse.default || _traverse
28
26
  const generate = _generate.default || _generate
29
27
 
30
- /**
31
- * Hook that attaches a CustomEvent listener to a DOM element via ref.
32
- *
33
- * Supports two calling conventions:
34
- * - useWccEvent(ref, eventName, handler) — uses an existing ref
35
- * - useWccEvent(eventName, handler) — creates and returns a new ref
36
- *
37
- * @param {import('react').RefObject<HTMLElement> | string} refOrEventName
38
- * @param {string | ((event: CustomEvent) => void)} eventNameOrHandler
39
- * @param {((event: CustomEvent) => void)} [handler]
40
- * @returns {import('react').RefObject<HTMLElement> | void}
41
- */
42
- export function useWccEvent(refOrEventName, eventNameOrHandler, handler) {
43
- // Detect calling convention
44
- const isRefForm = typeof refOrEventName !== 'string'
45
- const elementRef = isRefForm ? refOrEventName : useRef(null)
46
- const eventName = isRefForm ? eventNameOrHandler : refOrEventName
47
- const callback = isRefForm ? handler : eventNameOrHandler
48
-
49
- const handlerRef = useRef(callback)
50
- handlerRef.current = callback
51
-
52
- useEffect(() => {
53
- const el = elementRef.current
54
- if (!el) return
55
- const listener = (e) => handlerRef.current(e)
56
- el.addEventListener(eventName, listener)
57
- return () => el.removeEventListener(eventName, listener)
58
- }, [eventName])
59
-
60
- // Only return ref if we created it (Form 2)
61
- if (!isRefForm) return elementRef
62
- }
63
-
64
-
65
- /**
66
- * Hook for two-way binding with WCC defineModel props.
67
- *
68
- * Listens for `wcc:model` events on the element and calls the setter
69
- * when the matching prop changes internally. Also syncs the React state
70
- * to the element's attribute when the value changes externally.
71
- *
72
- * @param {string} propName - The model prop name (e.g., 'value', 'count')
73
- * @param {*} value - Current React state value
74
- * @param {(newValue: *) => void} setValue - React state setter
75
- * @param {import('react').RefObject<HTMLElement>} [existingRef] - Optional existing ref
76
- * @returns {import('react').RefObject<HTMLElement>} Ref to attach to the WCC element
77
- *
78
- * @example
79
- * ```jsx
80
- * function App() {
81
- * const [text, setText] = useState('')
82
- * const inputRef = useWccModel('value', text, setText)
83
- * return <wcc-input ref={inputRef}></wcc-input>
84
- * }
85
- * ```
86
- */
87
- export function useWccModel(propName, value, setValue, existingRef) {
88
- const internalRef = useRef(null)
89
- const elementRef = existingRef || internalRef
90
-
91
- const setValueRef = useRef(setValue)
92
- setValueRef.current = setValue
93
-
94
- // Listen for wcc:model events from the component (child → parent)
95
- useEffect(() => {
96
- const el = elementRef.current
97
- if (!el) return
98
-
99
- const listener = (e) => {
100
- if (e.detail && e.detail.prop === propName) {
101
- setValueRef.current(e.detail.value)
102
- }
103
- }
104
-
105
- el.addEventListener('wcc:model', listener)
106
- return () => el.removeEventListener('wcc:model', listener)
107
- }, [propName])
108
-
109
- // Sync React state to the element's attribute (parent → child)
110
- useEffect(() => {
111
- const el = elementRef.current
112
- if (!el) return
113
- if (value != null) {
114
- el.setAttribute(propName, String(value))
115
- } else {
116
- el.removeAttribute(propName)
117
- }
118
- }, [propName, value])
119
-
120
- return elementRef
121
- }
122
-
123
-
124
28
  /**
125
29
  * JSX attribute name to HTML attribute name mapping.
126
30
  * React uses camelCase for some attributes that HTML uses lowercase.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprlab/wccompiler",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
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": {
@@ -9,7 +9,8 @@
9
9
  "./integrations/react": "./integrations/react.js",
10
10
  "./integrations/angular": "./integrations/angular.js",
11
11
  "./adapters/vue": "./adapters/vue.js",
12
- "./adapters/angular": "./adapters/angular.js"
12
+ "./adapters/angular": "./adapters/angular.js",
13
+ "./adapters/react": "./adapters/react.js"
13
14
  },
14
15
  "bin": {
15
16
  "wcc": "./bin/wcc.js"