@pyreon/vue-compat 0.18.0 → 0.20.0

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,6 +1,39 @@
1
1
  import { ComponentFn, ComponentFn as Component, Fragment, Props, VNodeChild, VNodeChild as VNode, h as pyreonH } from "@pyreon/core";
2
2
  import { batch } from "@pyreon/reactivity";
3
3
 
4
+ //#region src/jsx-runtime.d.ts
5
+ interface RenderContext {
6
+ hooks: unknown[];
7
+ scheduleRerender: () => void;
8
+ /** Effect entries pending execution after render */
9
+ pendingEffects: EffectEntry[];
10
+ /** Layout effect entries pending execution after render */
11
+ pendingLayoutEffects: EffectEntry[];
12
+ /** Set to true when the component is unmounted */
13
+ unmounted: boolean;
14
+ /** Callbacks to run on unmount (lifecycle + effect cleanups) */
15
+ unmountCallbacks: (() => void)[];
16
+ /**
17
+ * The props object the wrapped component was invoked with. Read by
18
+ * `getCurrentInstance()` / `useSlots()` / `useAttrs()` to derive slots
19
+ * + attrs. Set by the wrapper before each render.
20
+ */
21
+ _props?: Record<string, unknown>;
22
+ /**
23
+ * Names of props declared via `defineComponent({ props })`. Set by
24
+ * `defineComponent` so `useAttrs()` / `getCurrentInstance().attrs` can
25
+ * compute the Vue declared-vs-fallthrough split. Undefined when the
26
+ * component didn't declare props (then attrs = the full props object).
27
+ */
28
+ _declaredProps?: string[];
29
+ }
30
+ interface EffectEntry {
31
+ fn: () => (() => void) | void;
32
+ deps: unknown[] | undefined;
33
+ cleanup: (() => void) | undefined;
34
+ }
35
+ declare function getCurrentCtx(): RenderContext | null;
36
+ //#endregion
4
37
  //#region src/index.d.ts
5
38
  declare const V_IS_REF: unique symbol;
6
39
  interface Ref<T = unknown> {
@@ -270,11 +303,286 @@ declare function Teleport(props: {
270
303
  children?: VNodeChild;
271
304
  }): VNodeChild;
272
305
  /**
273
- * KeepAlive — not supported in Pyreon. Renders children as-is.
306
+ * KeepAlive — mounts its children once and keeps them alive (state preserved)
307
+ * even when hidden, instead of destroying/recreating them.
308
+ *
309
+ * Wraps `@pyreon/runtime-dom`'s real KeepAlive. Vue's `<KeepAlive>` keeps a
310
+ * cache of inactive component instances; this maps the common single-slot
311
+ * usage to Pyreon's `active`-accessor model.
312
+ *
313
+ * LIMITATIONS vs Vue 3:
314
+ * - Vue's `include` / `exclude` / `max` props are NOT supported. Pyreon's
315
+ * KeepAlive is a single always-mounted slot toggled by an `active`
316
+ * accessor — there is no per-component LRU cache to filter or bound.
317
+ * These props are accepted (so existing Vue code typechecks) but ignored.
318
+ * - Vue toggles activation via the dynamic child (`<component :is>`); here
319
+ * you pass an `active` accessor (`() => boolean`). When omitted, children
320
+ * are always mounted and visible (a faithful default — nothing is
321
+ * destroyed, matching KeepAlive's core guarantee).
322
+ *
323
+ * @example
324
+ * import { KeepAlive, ref } from "@pyreon/vue-compat"
325
+ *
326
+ * function App() {
327
+ * const showA = ref(true)
328
+ * return (
329
+ * <KeepAlive active={() => showA.value}>
330
+ * <ExpensiveTab />
331
+ * </KeepAlive>
332
+ * )
333
+ * }
274
334
  */
275
335
  declare function KeepAlive(props: {
336
+ active?: () => boolean; /** Accepted for Vue compatibility — ignored (no per-instance cache). */
337
+ include?: string | RegExp | (string | RegExp)[]; /** Accepted for Vue compatibility — ignored (no per-instance cache). */
338
+ exclude?: string | RegExp | (string | RegExp)[]; /** Accepted for Vue compatibility — ignored (no per-instance cache). */
339
+ max?: number;
340
+ children?: VNodeChild;
341
+ }): VNodeChild;
342
+ /**
343
+ * Transition — adds CSS enter/leave animation classes to a single child,
344
+ * controlled by a reactive `show` accessor.
345
+ *
346
+ * Wraps `@pyreon/runtime-dom`'s Transition. Vue's class-name conventions
347
+ * (`enter-from-class`, `enter-active-class`, …) are mapped onto Pyreon's
348
+ * (`enterFrom`, `enterActive`, …), and Vue's `@before-enter` / `@after-enter`
349
+ * style hooks are mapped onto Pyreon's `onBeforeEnter` / `onAfterEnter`.
350
+ *
351
+ * LIMITATIONS vs Vue 3:
352
+ * - Vue's `<Transition>` infers visibility from a `v-if` / `v-show` on its
353
+ * child. Pyreon has no template directives, so you MUST pass an explicit
354
+ * `show: () => boolean` accessor. Without it the child is shown
355
+ * unconditionally (no enter/leave is ever triggered).
356
+ * - `mode` ("out-in" / "in-out"), `css: false`, and JS-only hook-driven
357
+ * transitions are NOT supported — Pyreon's Transition is CSS-class based.
358
+ * The props are accepted for typechecking but ignored.
359
+ * - The Vue `name` convention (`name="fade"` → `fade-enter-from` …) is
360
+ * preserved 1:1 (Pyreon uses the identical class-name scheme).
361
+ *
362
+ * @example
363
+ * import { Transition, ref } from "@pyreon/vue-compat"
364
+ *
365
+ * function App() {
366
+ * const visible = ref(false)
367
+ * return (
368
+ * <Transition name="fade" show={() => visible.value}>
369
+ * <div class="modal">Hello</div>
370
+ * </Transition>
371
+ * )
372
+ * }
373
+ * // CSS:
374
+ * // .fade-enter-from, .fade-leave-to { opacity: 0; }
375
+ * // .fade-enter-active, .fade-leave-active { transition: opacity 300ms; }
376
+ */
377
+ declare function Transition(props: {
378
+ name?: string;
379
+ show?: () => boolean;
380
+ appear?: boolean; /** Vue class-name prop — mapped to Pyreon's `enterFrom`. */
381
+ enterFromClass?: string; /** Vue class-name prop — mapped to Pyreon's `enterActive`. */
382
+ enterActiveClass?: string; /** Vue class-name prop — mapped to Pyreon's `enterTo`. */
383
+ enterToClass?: string; /** Vue class-name prop — mapped to Pyreon's `leaveFrom`. */
384
+ leaveFromClass?: string; /** Vue class-name prop — mapped to Pyreon's `leaveActive`. */
385
+ leaveActiveClass?: string; /** Vue class-name prop — mapped to Pyreon's `leaveTo`. */
386
+ leaveToClass?: string; /** Accepted for Vue compatibility — ignored (CSS-class transitions only). */
387
+ mode?: 'in-out' | 'out-in' | 'default'; /** Accepted for Vue compatibility — ignored (CSS-class transitions only). */
388
+ css?: boolean;
389
+ onBeforeEnter?: (el: HTMLElement) => void;
390
+ onAfterEnter?: (el: HTMLElement) => void;
391
+ onBeforeLeave?: (el: HTMLElement) => void;
392
+ onAfterLeave?: (el: HTMLElement) => void;
393
+ children?: VNodeChild;
394
+ }): VNodeChild;
395
+ /**
396
+ * TransitionGroup — animates a keyed reactive list with CSS enter/leave plus
397
+ * FLIP move animations.
398
+ *
399
+ * Wraps `@pyreon/runtime-dom`'s TransitionGroup. Vue's class-name props are
400
+ * mapped onto Pyreon's, same as {@link Transition}.
401
+ *
402
+ * LIMITATIONS vs Vue 3:
403
+ * - Vue's `<TransitionGroup>` renders its children via slots and reads keys
404
+ * from the child VNode `key`. Pyreon's API is explicit: pass `items`
405
+ * (a reactive accessor), `keyFn` (stable key extractor), and `render`
406
+ * (returns one DOM-element VNode per item). This is the faithful Pyreon
407
+ * shape — the animation behavior (enter/leave/FLIP-move) is identical.
408
+ * - `mode` and `css: false` are NOT supported (CSS-class transitions only).
409
+ *
410
+ * @example
411
+ * import { TransitionGroup, ref } from "@pyreon/vue-compat"
412
+ *
413
+ * function App() {
414
+ * const items = ref([{ id: 1 }, { id: 2 }])
415
+ * return (
416
+ * <TransitionGroup
417
+ * tag="ul"
418
+ * name="list"
419
+ * items={() => items.value}
420
+ * keyFn={(it) => it.id}
421
+ * render={(it) => <li class="item">{it.id}</li>}
422
+ * />
423
+ * )
424
+ * }
425
+ */
426
+ declare function TransitionGroup<T = unknown>(props: {
427
+ tag?: string;
428
+ name?: string;
429
+ appear?: boolean;
430
+ enterFromClass?: string;
431
+ enterActiveClass?: string;
432
+ enterToClass?: string;
433
+ leaveFromClass?: string;
434
+ leaveActiveClass?: string;
435
+ leaveToClass?: string; /** Vue class-name prop — mapped to Pyreon's `moveClass`. */
436
+ moveClass?: string;
437
+ items: () => T[];
438
+ keyFn: (item: T, index: number) => string | number;
439
+ render: (item: T, index: number) => ReturnType<typeof pyreonH>;
440
+ onBeforeEnter?: (el: HTMLElement) => void;
441
+ onAfterEnter?: (el: HTMLElement) => void;
442
+ onBeforeLeave?: (el: HTMLElement) => void;
443
+ onAfterLeave?: (el: HTMLElement) => void;
444
+ }): VNodeChild;
445
+ /**
446
+ * Suspense — shows `fallback` content while an async (lazy) child is loading.
447
+ *
448
+ * Re-exports `@pyreon/core`'s Suspense. Vue 3's `<Suspense>` uses named
449
+ * `#default` / `#fallback` slots; this maps the `fallback` slot to Pyreon
450
+ * Suspense's `fallback` prop and the default slot to `children`.
451
+ *
452
+ * LIMITATIONS vs Vue 3:
453
+ * - Vue resolves `<Suspense>` against any `async setup()` in the subtree and
454
+ * supports `@resolve` / `@pending` / `@fallback` events plus the `timeout`
455
+ * prop. Pyreon's Suspense resolves against components carrying a
456
+ * `__loading` accessor (e.g. {@link defineAsyncComponent} output) and does
457
+ * not emit those events. The events / `timeout` prop are accepted for
458
+ * typechecking but ignored.
459
+ *
460
+ * @example
461
+ * import { Suspense, defineAsyncComponent } from "@pyreon/vue-compat"
462
+ *
463
+ * const AsyncPage = defineAsyncComponent(() => import("./Page"))
464
+ *
465
+ * function App() {
466
+ * return (
467
+ * <Suspense fallback={<div>Loading…</div>}>
468
+ * <AsyncPage />
469
+ * </Suspense>
470
+ * )
471
+ * }
472
+ */
473
+ declare function Suspense(props: {
474
+ fallback?: VNodeChild; /** Accepted for Vue compatibility — ignored (no timeout phase). */
475
+ timeout?: number;
276
476
  children?: VNodeChild;
277
477
  }): VNodeChild;
478
+ /**
479
+ * Vue-compatible component-instance handle.
480
+ *
481
+ * Backed by the compat hook-context. Only the fields commonly read by
482
+ * composable libraries are populated. See {@link getCurrentInstance}.
483
+ */
484
+ interface ComponentInternalInstance {
485
+ /** Monotonic per-instance id. */
486
+ uid: number;
487
+ /**
488
+ * Vue's `proxy` — the component public instance. In this shim it is an
489
+ * empty object (Pyreon components are plain functions; there is no
490
+ * `this`-bound options instance). Present so `inst.proxy` access doesn't
491
+ * throw; do not rely on reading reactive state off it.
492
+ */
493
+ proxy: Record<string, unknown>;
494
+ /** Slots derived from the current component's children. */
495
+ slots: Record<string, (() => VNodeChild) | undefined>;
496
+ /** Fallthrough attrs (declared props excluded — mirrors `useAttrs()`). */
497
+ attrs: Record<string, unknown>;
498
+ /**
499
+ * Emits an event by invoking the matching `on{Event}` prop handler —
500
+ * same behavior as the `emit` on `defineComponent`'s setup context.
501
+ * Libraries that call `instance.emit(...)` (vee-validate, etc.) work.
502
+ */
503
+ emit: (event: string, ...args: unknown[]) => void;
504
+ /** `true` — present for libraries that branch on `isMounted`-like flags. */
505
+ isMounted: boolean;
506
+ /** @internal — the underlying compat render context. */
507
+ _ctx: ReturnType<typeof getCurrentCtx>;
508
+ }
509
+ /**
510
+ * Returns a handle to the current component instance, or `null` if called
511
+ * outside a component setup.
512
+ *
513
+ * Vue 3's `getCurrentInstance()` is an internal API many composable libraries
514
+ * (vee-validate, vue-i18n, pinia plugins, …) read for `uid`, `proxy`, `slots`,
515
+ * `attrs`. This shim returns a minimal stable object with those fields so such
516
+ * libraries don't crash.
517
+ *
518
+ * LIMITATIONS vs Vue 3:
519
+ * - `proxy` is an empty object — Pyreon components are plain functions with
520
+ * no `this`-bound Options instance. Code that reads reactive state off
521
+ * `instance.proxy.$data` / `.$props` will not work; use `props` directly.
522
+ * - `instance.emit(event, ...args)` IS provided (invokes the matching
523
+ * `on{Event}` prop handler). `instance.attrs` is the fallthrough split
524
+ * (declared props excluded) when the component used `defineComponent({
525
+ * props })`; otherwise it is the full props object.
526
+ * - `appContext`, `parent`, `vnode`, `expose`, render internals are NOT
527
+ * provided. Libraries that walk the parent chain are not supported.
528
+ * - The same `uid` is stable across re-renders of the same instance
529
+ * (hook-indexed), matching Vue's per-instance-id guarantee.
530
+ *
531
+ * @example
532
+ * import { getCurrentInstance } from "@pyreon/vue-compat"
533
+ *
534
+ * function useUid() {
535
+ * const inst = getCurrentInstance()
536
+ * return inst ? inst.uid : -1
537
+ * }
538
+ */
539
+ declare function getCurrentInstance(): ComponentInternalInstance | null;
540
+ /**
541
+ * Returns the current component's slots — a map of slot-name → render
542
+ * function. Only the `default` slot is populated (derived from `children`),
543
+ * matching how `@pyreon/vue-compat` models slots elsewhere
544
+ * ({@link defineComponent}'s setup context).
545
+ *
546
+ * LIMITATIONS vs Vue 3:
547
+ * - Vue supports arbitrary named + scoped slots resolved from the parent
548
+ * template. Pyreon passes a single `children` payload, so only
549
+ * `slots.default` is available. Named/scoped slots are not modeled.
550
+ * - Returns an empty object (no `default`) when there are no children or
551
+ * when called outside a component.
552
+ *
553
+ * @example
554
+ * import { useSlots } from "@pyreon/vue-compat"
555
+ *
556
+ * function Wrapper() {
557
+ * const slots = useSlots()
558
+ * return <div class="box">{slots.default?.()}</div>
559
+ * }
560
+ */
561
+ declare function useSlots(): Record<string, (() => VNodeChild) | undefined>;
562
+ /**
563
+ * Returns the current component's non-prop attributes.
564
+ *
565
+ * In Vue, `useAttrs()` is the fallthrough attributes NOT declared in `props`.
566
+ * `@pyreon/vue-compat` does not do declared-prop separation (components are
567
+ * plain functions receiving one `props` object), so this returns the full
568
+ * props object — every consumer-supplied attribute is present.
569
+ *
570
+ * LIMITATIONS vs Vue 3:
571
+ * - No declared-vs-fallthrough split: `useAttrs()` here === the full props
572
+ * object (including any that Vue would have consumed as declared props).
573
+ * Read the specific keys you need; don't assume the result excludes
574
+ * declared props.
575
+ * - Returns an empty object when called outside a component.
576
+ *
577
+ * @example
578
+ * import { useAttrs } from "@pyreon/vue-compat"
579
+ *
580
+ * function Passthrough() {
581
+ * const attrs = useAttrs()
582
+ * return <input {...attrs} />
583
+ * }
584
+ */
585
+ declare function useAttrs(): Record<string, unknown>;
278
586
  /**
279
587
  * Runs a watchEffect that flushes after DOM updates.
280
588
  * In Pyreon, same as `watchEffect()`.
@@ -322,5 +630,5 @@ type InjectionKey<T> = symbol & {
322
630
  __type: T;
323
631
  };
324
632
  //#endregion
325
- export { type Component, ComputedRef, Directive, EffectScopeCompat, EmitsOptions, ExtractPropTypes, Fragment, InjectionKey, KeepAlive, Plugin, PropType, Ref, SetupContext, Teleport, type VNode, WatchOptions, WritableComputedRef, batch, computed, createApp, customRef, defineAsyncComponent, defineComponent, effectScope, getCurrentScope, pyreonH as h, inject, isProxy, isReactive, isReadonly, isRef, markRaw, nextTick, onBeforeMount, onBeforeUnmount, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onUnmounted, onUpdated, provide, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, toValue, triggerRef, unref, version, watch, watchEffect, watchPostEffect, watchSyncEffect };
633
+ export { type Component, ComponentInternalInstance, ComputedRef, Directive, EffectScopeCompat, EmitsOptions, ExtractPropTypes, Fragment, InjectionKey, KeepAlive, Plugin, PropType, Ref, SetupContext, Suspense, Teleport, Transition, TransitionGroup, type VNode, WatchOptions, WritableComputedRef, batch, computed, createApp, customRef, defineAsyncComponent, defineComponent, effectScope, getCurrentInstance, getCurrentScope, pyreonH as h, inject, isProxy, isReactive, isReadonly, isRef, markRaw, nextTick, onBeforeMount, onBeforeUnmount, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onUnmounted, onUpdated, provide, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, toValue, triggerRef, unref, useAttrs, useSlots, version, watch, watchEffect, watchPostEffect, watchSyncEffect };
326
634
  //# sourceMappingURL=index2.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/vue-compat",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Vue 3-compatible Composition API shim for Pyreon — write Vue-style code that runs on Pyreon's reactive engine",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/vue-compat#readme",
6
6
  "bugs": {
@@ -54,13 +54,13 @@
54
54
  "prepublishOnly": "bun run build"
55
55
  },
56
56
  "dependencies": {
57
- "@pyreon/core": "^0.18.0",
58
- "@pyreon/reactivity": "^0.18.0",
59
- "@pyreon/runtime-dom": "^0.18.0"
57
+ "@pyreon/core": "^0.20.0",
58
+ "@pyreon/reactivity": "^0.20.0",
59
+ "@pyreon/runtime-dom": "^0.20.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@happy-dom/global-registrator": "^20.8.9",
63
- "@pyreon/test-utils": "^0.13.5",
63
+ "@pyreon/test-utils": "^0.13.7",
64
64
  "@vitest/browser-playwright": "^4.1.4",
65
65
  "happy-dom": "^20.8.3"
66
66
  }