@sprlab/wccompiler 0.10.1 → 0.10.2
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/adapters/react.js +84 -0
- package/lib/codegen.js +10 -0
- package/package.json +1 -1
package/adapters/react.js
CHANGED
|
@@ -249,3 +249,87 @@ export function createWccWrapper(tagName, config = {}) {
|
|
|
249
249
|
return WccWrapper
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Creates a React wrapper from a WCC component class that has `static __meta`.
|
|
256
|
+
*
|
|
257
|
+
* Unlike `createWccWrapper` which requires manual event/model configuration,
|
|
258
|
+
* this function reads the metadata directly from the compiled component class.
|
|
259
|
+
*
|
|
260
|
+
* @param {Function} WccClass - The WCC custom element class (must have static __meta)
|
|
261
|
+
* @returns {import('react').ForwardRefExoticComponent} A React component
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* import { wrapWccComponent } from '@sprlab/wccompiler/adapters/react'
|
|
265
|
+
* import '../wcc-components/wcc-counter.js' // registers the custom element
|
|
266
|
+
*
|
|
267
|
+
* // Read metadata directly from the registered class
|
|
268
|
+
* const WccCounter = wrapWccComponent(customElements.get('wcc-counter'))
|
|
269
|
+
*
|
|
270
|
+
* // Use idiomatically — no manual config needed
|
|
271
|
+
* <WccCounter count={count} onCountChanged={setCount} onChange={handler} />
|
|
272
|
+
*/
|
|
273
|
+
export function wrapWccComponent(WccClass) {
|
|
274
|
+
const meta = WccClass?.__meta
|
|
275
|
+
if (!meta) {
|
|
276
|
+
throw new Error(`wrapWccComponent: class does not have static __meta. Is it a compiled WCC component?`)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return createWccWrapper(meta.tag, {
|
|
280
|
+
events: meta.events || [],
|
|
281
|
+
models: meta.models || [],
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Creates React wrappers for all registered WCC custom elements matching a prefix.
|
|
287
|
+
*
|
|
288
|
+
* Scans the custom elements registry for components with `static __meta` and
|
|
289
|
+
* generates typed wrapper components for each one.
|
|
290
|
+
*
|
|
291
|
+
* @param {Object} [options]
|
|
292
|
+
* @param {string} [options.prefix='wcc-'] - Tag prefix to filter components
|
|
293
|
+
* @returns {Record<string, import('react').ForwardRefExoticComponent>} Map of PascalCase name → React component
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* // In your app entry point, after importing all WCC components:
|
|
297
|
+
* import '../wcc-components/wcc-counter.js'
|
|
298
|
+
* import '../wcc-components/wcc-card.js'
|
|
299
|
+
* import { createWccWrappers } from '@sprlab/wccompiler/adapters/react'
|
|
300
|
+
*
|
|
301
|
+
* export const { WccCounter, WccCard } = createWccWrappers()
|
|
302
|
+
*
|
|
303
|
+
* // Then use anywhere:
|
|
304
|
+
* <WccCounter count={count} onCountChanged={setCount} />
|
|
305
|
+
* <WccCard><div slot="header">Title</div></WccCard>
|
|
306
|
+
*/
|
|
307
|
+
export function createWccWrappers(options = {}) {
|
|
308
|
+
const { prefix = 'wcc-' } = options
|
|
309
|
+
const wrappers = {}
|
|
310
|
+
|
|
311
|
+
// Note: customElements registry doesn't have a list API,
|
|
312
|
+
// so we need the component files to be imported first (which registers them).
|
|
313
|
+
// This function is meant to be called after all component imports.
|
|
314
|
+
|
|
315
|
+
// We'll use a Proxy that lazily creates wrappers on first access
|
|
316
|
+
return new Proxy(wrappers, {
|
|
317
|
+
get(target, prop) {
|
|
318
|
+
if (typeof prop !== 'string') return undefined
|
|
319
|
+
if (prop in target) return target[prop]
|
|
320
|
+
|
|
321
|
+
// Convert PascalCase to kebab-case: WccCounter → wcc-counter
|
|
322
|
+
const kebab = prop.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '')
|
|
323
|
+
|
|
324
|
+
// Check if it starts with the prefix
|
|
325
|
+
if (!kebab.startsWith(prefix)) return undefined
|
|
326
|
+
|
|
327
|
+
const ctor = customElements.get(kebab)
|
|
328
|
+
if (!ctor || !(ctor).__meta) return undefined
|
|
329
|
+
|
|
330
|
+
const wrapper = wrapWccComponent(ctor)
|
|
331
|
+
target[prop] = wrapper
|
|
332
|
+
return wrapper
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
}
|
package/lib/codegen.js
CHANGED
|
@@ -970,6 +970,16 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
970
970
|
lines.push('');
|
|
971
971
|
}
|
|
972
972
|
|
|
973
|
+
// Static __meta — component metadata for framework adapters (React wrappers, Angular events, etc.)
|
|
974
|
+
{
|
|
975
|
+
const metaProps = propDefs.map(p => `{ name: '${p.name}', default: ${p.default} }`).join(', ');
|
|
976
|
+
const metaEvents = emits.map(e => `'${e}'`).join(', ');
|
|
977
|
+
const metaModels = modelDefs.map(m => `'${m.name}'`).join(', ');
|
|
978
|
+
const metaSlots = slots.filter(s => s.name).map(s => `'${s.name}'`).join(', ');
|
|
979
|
+
lines.push(` static __meta = { tag: '${tagName}', props: [${metaProps}], events: [${metaEvents}], models: [${metaModels}], slots: [${metaSlots}] };`);
|
|
980
|
+
lines.push('');
|
|
981
|
+
}
|
|
982
|
+
|
|
973
983
|
// Constructor — reactive state only (no DOM manipulation per Custom Elements spec)
|
|
974
984
|
lines.push(' constructor() {');
|
|
975
985
|
lines.push(' super();');
|
package/package.json
CHANGED