@@ -37,7 +37,7 @@ export type AsyncProps = Omit<React.SuspenseProps, "children">
|
|||||||
export const AsyncPrototype: AsyncPrototype = Object.freeze({
|
export const AsyncPrototype: AsyncPrototype = Object.freeze({
|
||||||
[AsyncTypeId]: AsyncTypeId,
|
[AsyncTypeId]: AsyncTypeId,
|
||||||
|
|
||||||
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Component.Signature>(
|
makeFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Component.Signature>(
|
||||||
this: Component.Component<P, A, E, R, F> & Async,
|
this: Component.Component<P, A, E, R, F> & Async,
|
||||||
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||||
import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect"
|
import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Refreshable from "./Refreshable.js"
|
|
||||||
import * as ScopeRegistry from "./ScopeRegistry.js"
|
import * as ScopeRegistry from "./ScopeRegistry.js"
|
||||||
|
|
||||||
|
|
||||||
@@ -45,11 +44,15 @@ export declare namespace Component {
|
|||||||
export interface ComponentImpl<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>
|
export interface ComponentImpl<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>
|
||||||
extends Component<P, A, E, R, F>, ComponentImplPrototype<R, F> {}
|
extends Component<P, A, E, R, F>, ComponentImplPrototype<R, F> {}
|
||||||
|
|
||||||
|
export declare namespace ComponentImpl {
|
||||||
|
export type Any = ComponentImpl<any, any, any, any, any>
|
||||||
|
}
|
||||||
|
|
||||||
export interface ComponentImplPrototype<R, F extends Component.Signature> {
|
export interface ComponentImplPrototype<R, F extends Component.Signature> {
|
||||||
readonly use: Effect.Effect<F, never, Exclude<R, Scope.Scope>>
|
readonly use: Effect.Effect<F, never, Exclude<R, Scope.Scope>>
|
||||||
|
|
||||||
asFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
asFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
||||||
asRefreshableFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
makeFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
||||||
setFunctionComponentName(f: F): void
|
setFunctionComponentName(f: F): void
|
||||||
transformFunctionComponent(f: F): F
|
transformFunctionComponent(f: F): F
|
||||||
}
|
}
|
||||||
@@ -57,7 +60,7 @@ export interface ComponentImplPrototype<R, F extends Component.Signature> {
|
|||||||
export const ComponentImplPrototype: ComponentImplPrototype<any, any> = Object.freeze({
|
export const ComponentImplPrototype: ComponentImplPrototype<any, any> = Object.freeze({
|
||||||
get use() { return use(this) },
|
get use() { return use(this) },
|
||||||
|
|
||||||
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
makeFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
||||||
this: ComponentImpl<P, A, E, R, F>,
|
this: ComponentImpl<P, A, E, R, F>,
|
||||||
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
||||||
) {
|
) {
|
||||||
@@ -69,41 +72,11 @@ export const ComponentImplPrototype: ComponentImplPrototype<any, any> = Object.f
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
asRefreshableFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
||||||
this: ComponentImpl<P, A, E, R, F>,
|
this: ComponentImpl<P, A, E, R, F>,
|
||||||
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
||||||
) {
|
) {
|
||||||
if (!Refreshable.isRefreshable(this))
|
return this.makeFunctionComponent(contextRef)
|
||||||
return this.asFunctionComponent(contextRef)
|
|
||||||
|
|
||||||
const cell = this[Refreshable.RefreshableTypeId]
|
|
||||||
|
|
||||||
let current = cell.current
|
|
||||||
let functionComponent = current.asFunctionComponent(contextRef)
|
|
||||||
|
|
||||||
// Calling the current renderer inside this stable component deliberately
|
|
||||||
// keeps its hooks on the same fiber until resetRevision changes.
|
|
||||||
const Implementation = (props: P) => {
|
|
||||||
if (current !== cell.current) {
|
|
||||||
current = cell.current
|
|
||||||
functionComponent = current.asFunctionComponent(contextRef)
|
|
||||||
}
|
|
||||||
return functionComponent(props)
|
|
||||||
}
|
|
||||||
|
|
||||||
const RefreshableComponent = (props: P) => {
|
|
||||||
const snapshot = React.useSyncExternalStore(
|
|
||||||
cell.subscribe,
|
|
||||||
cell.getSnapshot,
|
|
||||||
cell.getSnapshot,
|
|
||||||
)
|
|
||||||
return React.createElement(Implementation, {
|
|
||||||
...props,
|
|
||||||
key: snapshot.resetRevision,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return RefreshableComponent as F
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setFunctionComponentName<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
setFunctionComponentName<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
||||||
@@ -137,7 +110,7 @@ const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode
|
|||||||
!Equivalence.Array(Equivalence.strictEqual())(services, previousServicesRef.current)
|
!Equivalence.Array(Equivalence.strictEqual())(services, previousServicesRef.current)
|
||||||
) {
|
) {
|
||||||
previousServicesRef.current = services
|
previousServicesRef.current = services
|
||||||
const f = self.asRefreshableFunctionComponent(contextRef)
|
const f = self.asFunctionComponent(contextRef)
|
||||||
self.setFunctionComponentName(f)
|
self.setFunctionComponentName(f)
|
||||||
componentRef.current = self.transformFunctionComponent(f)
|
componentRef.current = self.transformFunctionComponent(f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import type { Context, Scope } from "effect"
|
||||||
|
import * as React from "react"
|
||||||
|
import type * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stable identifier used to associate an Effect View descriptor with its
|
* A stable identifier used to associate an Effect View descriptor with its
|
||||||
* development refresh cell.
|
* development refresh cell.
|
||||||
@@ -25,25 +30,62 @@ export interface Snapshot {
|
|||||||
*
|
*
|
||||||
* This low-level API is intended for development-server integrations.
|
* This low-level API is intended for development-server integrations.
|
||||||
*/
|
*/
|
||||||
export interface Cell<A extends object = object> {
|
export interface Cell {
|
||||||
current: A
|
current: Component.ComponentImpl.Any
|
||||||
signature: string
|
signature: string
|
||||||
forceReset: boolean
|
forceReset: boolean
|
||||||
snapshot: Snapshot
|
snapshot: Snapshot
|
||||||
readonly subscribe: (listener: () => void) => () => void
|
readonly subscribe: (listener: () => void) => () => void
|
||||||
readonly getSnapshot: () => Snapshot
|
readonly getSnapshot: () => Snapshot
|
||||||
readonly update: (
|
readonly update: (
|
||||||
component: A,
|
component: Component.Component.Any,
|
||||||
signature: string,
|
signature: string,
|
||||||
forceReset: boolean,
|
forceReset: boolean,
|
||||||
) => void
|
) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const RefreshablePrototype = Object.freeze({
|
||||||
|
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Component.Signature>(
|
||||||
|
this: Component.ComponentImpl<P, A, E, R, F> & Refreshable,
|
||||||
|
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
||||||
|
) {
|
||||||
|
const cell = this[RefreshableTypeId]
|
||||||
|
let current = cell.current
|
||||||
|
let functionComponent = current.makeFunctionComponent(contextRef)
|
||||||
|
|
||||||
|
// Calling the current renderer inside this stable component deliberately
|
||||||
|
// keeps its hooks on the same fiber until resetRevision changes.
|
||||||
|
const Implementation = (props: P) => {
|
||||||
|
if (current !== cell.current) {
|
||||||
|
current = cell.current
|
||||||
|
functionComponent = current.makeFunctionComponent(contextRef)
|
||||||
|
}
|
||||||
|
return functionComponent(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RefreshableComponent = (props: P) => {
|
||||||
|
const snapshot = React.useSyncExternalStore(
|
||||||
|
cell.subscribe,
|
||||||
|
cell.getSnapshot,
|
||||||
|
cell.getSnapshot,
|
||||||
|
)
|
||||||
|
return React.createElement(Implementation, {
|
||||||
|
...props,
|
||||||
|
key: snapshot.resetRevision,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshableComponent as F
|
||||||
|
},
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
export type RefreshablePrototype = typeof RefreshablePrototype
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A descriptor that can be connected to a development refresh cell.
|
* A descriptor that can be connected to a development refresh cell.
|
||||||
*/
|
*/
|
||||||
export interface Refreshable<A extends object = object> {
|
export interface Refreshable extends RefreshablePrototype {
|
||||||
readonly [RefreshableTypeId]: Cell<A>
|
readonly [RefreshableTypeId]: Cell
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,23 +94,23 @@ export interface Refreshable<A extends object = object> {
|
|||||||
*/
|
*/
|
||||||
export const isRefreshable = <A extends object>(
|
export const isRefreshable = <A extends object>(
|
||||||
value: A,
|
value: A,
|
||||||
): value is A & Refreshable<A> => Object.hasOwn(value, RefreshableTypeId)
|
): value is A & Refreshable => Object.hasOwn(value, RefreshableTypeId)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a refresh cell for an Effect View descriptor.
|
* Creates a refresh cell for an Effect View descriptor.
|
||||||
*
|
*
|
||||||
* This low-level API is intended for development-server integrations.
|
* This low-level API is intended for development-server integrations.
|
||||||
*/
|
*/
|
||||||
export const makeCell = <A extends object>(
|
export const makeCell = (
|
||||||
component: A,
|
component: Component.Component.Any,
|
||||||
signature: string,
|
signature: string,
|
||||||
forceReset: boolean,
|
forceReset: boolean,
|
||||||
): Cell<A> => {
|
): Cell => {
|
||||||
const listeners = new Set<() => void>()
|
const listeners = new Set<() => void>()
|
||||||
let notificationPending = false
|
let notificationPending = false
|
||||||
|
|
||||||
const cell: Cell<A> = {
|
const cell: Cell = {
|
||||||
current: component,
|
current: component as Component.ComponentImpl.Any,
|
||||||
signature,
|
signature,
|
||||||
forceReset,
|
forceReset,
|
||||||
snapshot: {
|
snapshot: {
|
||||||
@@ -89,7 +131,7 @@ export const makeCell = <A extends object>(
|
|||||||
|| nextForceReset
|
|| nextForceReset
|
||||||
|| cell.signature !== nextSignature
|
|| cell.signature !== nextSignature
|
||||||
|
|
||||||
cell.current = nextComponent
|
cell.current = nextComponent as Component.ComponentImpl.Any
|
||||||
cell.signature = nextSignature
|
cell.signature = nextSignature
|
||||||
cell.forceReset = nextForceReset
|
cell.forceReset = nextForceReset
|
||||||
cell.snapshot = {
|
cell.snapshot = {
|
||||||
@@ -116,14 +158,24 @@ export const makeCell = <A extends object>(
|
|||||||
*
|
*
|
||||||
* This low-level API is intended for development-server integrations.
|
* This low-level API is intended for development-server integrations.
|
||||||
*/
|
*/
|
||||||
export const attach = <A extends object>(
|
export const attach = <A extends Component.Component.Any>(
|
||||||
component: A,
|
component: A,
|
||||||
cell: Cell<A>,
|
cell: Cell,
|
||||||
): A & Refreshable<A> => {
|
): A & Refreshable => {
|
||||||
|
if (!isRefreshable(component)) {
|
||||||
|
Object.setPrototypeOf(
|
||||||
|
component,
|
||||||
|
Object.freeze(Object.setPrototypeOf(
|
||||||
|
Object.assign({}, RefreshablePrototype),
|
||||||
|
Object.getPrototypeOf(component),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(component, RefreshableTypeId, {
|
Object.defineProperty(component, RefreshableTypeId, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
value: cell,
|
value: cell,
|
||||||
})
|
})
|
||||||
return component as A & Refreshable<A>
|
return component as A & Refreshable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { describe, expect, it, vi } from "vitest"
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
import type * as Component from "../src/Component.js"
|
||||||
import * as Refreshable from "../src/Refreshable.js"
|
import * as Refreshable from "../src/Refreshable.js"
|
||||||
|
|
||||||
|
|
||||||
describe("Refreshable", () => {
|
describe("Refreshable", () => {
|
||||||
it("attaches a cell and notifies subscribers after a compatible update", async () => {
|
it("attaches a cell and notifies subscribers after a compatible update", async () => {
|
||||||
const first = {}
|
const first = {} as Component.Component.Any
|
||||||
const cell = Refreshable.makeCell(first, "hooks", false)
|
const cell = Refreshable.makeCell(first, "hooks", false)
|
||||||
const listener = vi.fn()
|
const listener = vi.fn()
|
||||||
|
|
||||||
@@ -13,9 +14,11 @@ describe("Refreshable", () => {
|
|||||||
expect(attached).toBe(first)
|
expect(attached).toBe(first)
|
||||||
expect(Refreshable.isRefreshable(first)).toBe(true)
|
expect(Refreshable.isRefreshable(first)).toBe(true)
|
||||||
expect(attached[Refreshable.RefreshableTypeId]).toBe(cell)
|
expect(attached[Refreshable.RefreshableTypeId]).toBe(cell)
|
||||||
|
expect(Object.getPrototypeOf(attached).asFunctionComponent)
|
||||||
|
.toBe(Refreshable.RefreshablePrototype.asFunctionComponent)
|
||||||
|
|
||||||
cell.subscribe(listener)
|
cell.subscribe(listener)
|
||||||
const second = {}
|
const second = {} as Component.Component.Any
|
||||||
cell.update(second, "hooks", false)
|
cell.update(second, "hooks", false)
|
||||||
|
|
||||||
expect(cell.current).toBe(second)
|
expect(cell.current).toBe(second)
|
||||||
@@ -29,12 +32,29 @@ describe("Refreshable", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("requests a remount when a signature changes or reset is forced", () => {
|
it("requests a remount when a signature changes or reset is forced", () => {
|
||||||
const cell = Refreshable.makeCell({}, "one", false)
|
const component = {} as Component.Component.Any
|
||||||
|
const cell = Refreshable.makeCell(component, "one", false)
|
||||||
|
|
||||||
cell.update({}, "two", false)
|
cell.update({} as Component.Component.Any, "two", false)
|
||||||
expect(cell.snapshot.resetRevision).toBe(1)
|
expect(cell.snapshot.resetRevision).toBe(1)
|
||||||
|
|
||||||
cell.update({}, "two", true)
|
cell.update({} as Component.Component.Any, "two", true)
|
||||||
expect(cell.snapshot.resetRevision).toBe(2)
|
expect(cell.snapshot.resetRevision).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("builds the refresh shell from the current descriptor implementation", () => {
|
||||||
|
const implementation = () => null
|
||||||
|
const makeFunctionComponent = vi.fn(() => implementation)
|
||||||
|
const component = {
|
||||||
|
makeFunctionComponent,
|
||||||
|
} as unknown as Component.ComponentImpl.Any
|
||||||
|
const contextRef = {
|
||||||
|
current: {},
|
||||||
|
} as never
|
||||||
|
const cell = Refreshable.makeCell(component, "hooks", false)
|
||||||
|
const attached = Refreshable.attach(component, cell)
|
||||||
|
|
||||||
|
expect(attached.asFunctionComponent(contextRef)).not.toBe(implementation)
|
||||||
|
expect(makeFunctionComponent).toHaveBeenCalledWith(contextRef)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type * as Component from "effect-fc-next/Component"
|
||||||
import * as Refreshable from "effect-fc-next/Refreshable"
|
import * as Refreshable from "effect-fc-next/Refreshable"
|
||||||
|
|
||||||
export interface HotContext {
|
export interface HotContext {
|
||||||
@@ -7,11 +8,11 @@ export interface HotContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RefreshData {
|
interface RefreshData {
|
||||||
readonly cells: Map<string, Refreshable.Cell<any>>
|
readonly cells: Map<string, Refreshable.Cell>
|
||||||
ids?: readonly string[]
|
ids?: readonly string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const hotDataKey = "__effectFcRefresh"
|
const hotDataKey = "__effectViewRefresh"
|
||||||
|
|
||||||
const getRefreshData = (hot: HotContext): RefreshData => {
|
const getRefreshData = (hot: HotContext): RefreshData => {
|
||||||
const data = hot.data as Record<string, unknown>
|
const data = hot.data as Record<string, unknown>
|
||||||
@@ -32,7 +33,7 @@ const getRefreshData = (hot: HotContext): RefreshData => {
|
|||||||
* This API is injected by `@effect-view/vite`; applications should not need to
|
* This API is injected by `@effect-view/vite`; applications should not need to
|
||||||
* call it directly.
|
* call it directly.
|
||||||
*/
|
*/
|
||||||
export const register = <A extends object>(
|
export const register = <A extends Component.Component.Any>(
|
||||||
component: A,
|
component: A,
|
||||||
hot: HotContext | undefined,
|
hot: HotContext | undefined,
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
|
import type * as Component from "effect-fc-next/Component"
|
||||||
import * as Refreshable from "effect-fc-next/Refreshable"
|
import * as Refreshable from "effect-fc-next/Refreshable"
|
||||||
import { describe, expect, it, vi } from "vitest"
|
import { describe, expect, it, vi } from "vitest"
|
||||||
import { accept, register } from "../src/runtime.js"
|
import { accept, register } from "../src/runtime.js"
|
||||||
|
|
||||||
|
|
||||||
describe("refresh runtime", () => {
|
describe("refresh runtime", () => {
|
||||||
|
const component = <A extends object>(value: A): A & Component.Component.Any =>
|
||||||
|
value as A & Component.Component.Any
|
||||||
|
|
||||||
it("retains a cell and notifies subscribers for compatible updates", async () => {
|
it("retains a cell and notifies subscribers for compatible updates", async () => {
|
||||||
const hot = {
|
const hot = {
|
||||||
data: {},
|
data: {},
|
||||||
accept: vi.fn(),
|
accept: vi.fn(),
|
||||||
invalidate: vi.fn(),
|
invalidate: vi.fn(),
|
||||||
}
|
}
|
||||||
const first = register({ body: "first" }, hot, "module:View", "hooks")
|
const first = register(component({ body: "first" }), hot, "module:View", "hooks")
|
||||||
const cell = (first as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId] as Refreshable.Cell
|
const cell = (first as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId] as Refreshable.Cell
|
||||||
const listener = vi.fn()
|
const listener = vi.fn()
|
||||||
cell.subscribe(listener)
|
cell.subscribe(listener)
|
||||||
|
|
||||||
const second = register({ body: "second" }, hot, "module:View", "hooks")
|
const second = register(component({ body: "second" }), hot, "module:View", "hooks")
|
||||||
const secondCell = (second as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId]
|
const secondCell = (second as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId]
|
||||||
|
|
||||||
expect(secondCell).toBe(cell)
|
expect(secondCell).toBe(cell)
|
||||||
@@ -35,20 +39,20 @@ describe("refresh runtime", () => {
|
|||||||
accept: vi.fn(),
|
accept: vi.fn(),
|
||||||
invalidate: vi.fn(),
|
invalidate: vi.fn(),
|
||||||
}
|
}
|
||||||
const first = register({}, hot, "module:View", "one")
|
const first = register(component({}), hot, "module:View", "one")
|
||||||
const cell = (first as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId] as Refreshable.Cell
|
const cell = (first as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId] as Refreshable.Cell
|
||||||
|
|
||||||
register({}, hot, "module:View", "two")
|
register(component({}), hot, "module:View", "two")
|
||||||
expect(cell.snapshot.resetRevision).toBe(1)
|
expect(cell.snapshot.resetRevision).toBe(1)
|
||||||
|
|
||||||
register({}, hot, "module:View", "two", true)
|
register(component({}), hot, "module:View", "two", true)
|
||||||
expect(cell.snapshot.resetRevision).toBe(2)
|
expect(cell.snapshot.resetRevision).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("is inert outside a Vite hot context", () => {
|
it("is inert outside a Vite hot context", () => {
|
||||||
const component = {}
|
const descriptor = component({})
|
||||||
expect(register(component, undefined, "module:View", "hooks")).toBe(component)
|
expect(register(descriptor, undefined, "module:View", "hooks")).toBe(descriptor)
|
||||||
expect((component as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId]).toBeUndefined()
|
expect((descriptor as Record<PropertyKey, unknown>)[Refreshable.RefreshableTypeId]).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("invalidates when the module's View IDs change", () => {
|
it("invalidates when the module's View IDs change", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user