@contember/bindx-react 0.1.46 → 0.1.48
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/dist/hooks/ItemAccessorCache.d.ts +36 -0
- package/dist/hooks/ItemAccessorCache.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useEntityCount.d.ts.map +1 -1
- package/dist/hooks/useEntityList.d.ts.map +1 -1
- package/dist/hooks/useFields.d.ts +25 -0
- package/dist/hooks/useFields.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/jsx/analyzer.d.ts.map +1 -1
- package/dist/jsx/componentBuilder.d.ts +3 -1
- package/dist/jsx/componentBuilder.d.ts.map +1 -1
- package/dist/jsx/componentBuilder.types.d.ts +44 -8
- package/dist/jsx/componentBuilder.types.d.ts.map +1 -1
- package/dist/jsx/componentBuilderCompat.d.ts.map +1 -1
- package/dist/jsx/componentFactory.d.ts +1 -1
- package/dist/jsx/componentFactory.d.ts.map +1 -1
- package/dist/jsx/components/Field.d.ts.map +1 -1
- package/dist/jsx/components/If.d.ts.map +1 -1
- package/dist/jsx/components/Switch.d.ts.map +1 -1
- package/dist/jsx/standaloneCreateComponent.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/hooks/ItemAccessorCache.ts +71 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useEntity.ts +48 -22
- package/src/hooks/useEntityCount.ts +0 -1
- package/src/hooks/useEntityList.ts +44 -16
- package/src/hooks/useFields.ts +118 -0
- package/src/index.ts +1 -1
- package/src/jsx/analyzer.ts +32 -7
- package/src/jsx/componentBuilder.ts +19 -2
- package/src/jsx/componentBuilder.types.ts +62 -8
- package/src/jsx/componentBuilderCompat.ts +0 -2
- package/src/jsx/componentFactory.ts +109 -42
- package/src/jsx/components/Attribute.tsx +2 -2
- package/src/jsx/components/Entity.tsx +12 -12
- package/src/jsx/components/Field.tsx +2 -4
- package/src/jsx/components/If.tsx +6 -0
- package/src/jsx/components/Switch.tsx +20 -9
- package/src/jsx/standaloneCreateComponent.ts +0 -2
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
SelectionMeta,
|
|
16
16
|
SelectionBuilder,
|
|
17
17
|
AnyBrand,
|
|
18
|
-
EntityRef,
|
|
19
18
|
SchemaDefinition,
|
|
20
19
|
} from '@contember/bindx'
|
|
21
20
|
import {
|
|
@@ -33,7 +32,7 @@ import { FIELD_REF_META, BINDX_COMPONENT, SCOPE_REF } from './types.js'
|
|
|
33
32
|
import { createCollectorProxy } from './proxy.js'
|
|
34
33
|
import { collectSelection } from './analyzer.js'
|
|
35
34
|
import { type Condition, evaluateCondition } from './conditions.js'
|
|
36
|
-
import {
|
|
35
|
+
import { useRefSubscription } from '../hooks/useFields.js'
|
|
37
36
|
|
|
38
37
|
// ============================================================================
|
|
39
38
|
// Symbols
|
|
@@ -70,17 +69,19 @@ export interface EntityConfig {
|
|
|
70
69
|
// ============================================================================
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
|
-
* Converts
|
|
74
|
-
* Called with a fixed list of prop names — hook count is stable across renders.
|
|
72
|
+
* Converts entity ref props to accessors while subscribing with one fixed hook.
|
|
75
73
|
*/
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
function readProperty(target: object, name: string): unknown {
|
|
75
|
+
return Reflect.get(target, name)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function useRenderProps<TProps extends object>(props: TProps, entityPropNames: string[]): TProps {
|
|
79
|
+
const refs = entityPropNames.map(name => readProperty(props, name))
|
|
80
|
+
const accessors = useRefSubscription(refs)
|
|
81
|
+
const accessorProps = Object.fromEntries(
|
|
82
|
+
entityPropNames.map((name, index) => [name, accessors[index]]),
|
|
83
|
+
)
|
|
84
|
+
return Object.assign({}, props, accessorProps)
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// ============================================================================
|
|
@@ -101,8 +102,10 @@ export function buildComponent<TProps extends object>(
|
|
|
101
102
|
schemaRegistry: SchemaRegistry<Record<string, object>> | null,
|
|
102
103
|
conditionFn: ((props: TProps) => Condition) | null,
|
|
103
104
|
slotNames: readonly string[],
|
|
105
|
+
useFns: readonly ((props: TProps) => object)[],
|
|
104
106
|
): unknown {
|
|
105
107
|
const selectionsMap = new Map<string, SelectionPropMeta>()
|
|
108
|
+
const componentDisplayName = `BindxComponent(${[...entityConfigs.keys()].join(', ')})`
|
|
106
109
|
|
|
107
110
|
// Generate unique brand for this component
|
|
108
111
|
const componentBrand = new ComponentBrand(`component_${Math.random().toString(36).slice(2)}`)
|
|
@@ -121,34 +124,58 @@ export function buildComponent<TProps extends object>(
|
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
// Every entity prop is subscribed, selector or not: accessor identity is stable, so a
|
|
128
|
+
// memo()-wrapped component only learns about its entity through its own subscription.
|
|
129
|
+
// Interface props are appended during lazy collection, before the first runtime render.
|
|
130
|
+
const entityPropNames = [...entityConfigs.keys()]
|
|
128
131
|
|
|
129
132
|
// 2. Implicit entities - collect lazily to avoid TDZ errors
|
|
130
133
|
const implicitConfigs = [...entityConfigs.entries()].filter(([_, c]) => !c.selector)
|
|
131
|
-
|
|
134
|
+
// Tri-state: 'collecting' terminates self-recursive components
|
|
135
|
+
let collectionState: 'idle' | 'collecting' | 'done' = 'idle'
|
|
132
136
|
|
|
137
|
+
// Runs only from the static-analysis surface (getSelection, $propName fragment
|
|
138
|
+
// getters) — never from render. Render bodies stay pure runtime code.
|
|
133
139
|
function ensureImplicitCollected(): void {
|
|
134
140
|
// In interfaces mode, we also need to collect even if no explicit implicit configs exist
|
|
135
141
|
// (because interface props may be discovered dynamically)
|
|
136
142
|
// Also collect if there's a conditionFn (it may access entity fields)
|
|
137
|
-
if (
|
|
143
|
+
if (collectionState !== 'idle' || (implicitConfigs.length === 0 && !hasInterfacesMode && !conditionFn)) {
|
|
138
144
|
return
|
|
139
145
|
}
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
collectionState = 'collecting'
|
|
147
|
+
try {
|
|
148
|
+
collectImplicitSelections(implicitConfigs, renderFn, selectionsMap, componentBrand, roles, hasInterfacesMode, schemaRegistry, conditionFn)
|
|
149
|
+
} catch (error) {
|
|
150
|
+
// Analysis is deterministic, so retrying is pointless — degrade loudly
|
|
151
|
+
// to the fields captured before the throw (scopes record eagerly).
|
|
152
|
+
console.error(
|
|
153
|
+
`[bindx] Implicit selection analysis of <${componentDisplayName}> failed — `
|
|
154
|
+
+ 'only fields accessed before the error were collected; fields used after it may be missing from queries.',
|
|
155
|
+
error,
|
|
156
|
+
)
|
|
157
|
+
} finally {
|
|
158
|
+
collectionState = 'done'
|
|
159
|
+
// Subscribe the props collection discovered too, incl. a degraded partial pass.
|
|
160
|
+
for (const propName of selectionsMap.keys()) {
|
|
161
|
+
if (!entityPropNames.includes(propName)) entityPropNames.push(propName)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
142
164
|
}
|
|
143
165
|
|
|
144
166
|
// 3. Create React component
|
|
145
167
|
function ComponentImpl(props: TProps): ReactNode {
|
|
146
168
|
ensureImplicitCollected()
|
|
147
169
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
// Subscribe every entity ref prop; an empty fixed list is a no-op.
|
|
171
|
+
let renderProps: TProps = useRenderProps(props, entityPropNames)
|
|
172
|
+
|
|
173
|
+
// Runtime-only values from .use() — hooks are allowed here; static analysis
|
|
174
|
+
// never executes these (their outputs are mocked in the collection pass).
|
|
175
|
+
for (const useFn of useFns) {
|
|
176
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- stable iteration count (useFns is fixed at build time)
|
|
177
|
+
renderProps = { ...renderProps, ...useFn(renderProps) }
|
|
178
|
+
}
|
|
152
179
|
|
|
153
180
|
// Evaluate condition at runtime
|
|
154
181
|
if (conditionFn) {
|
|
@@ -161,6 +188,8 @@ export function buildComponent<TProps extends object>(
|
|
|
161
188
|
}
|
|
162
189
|
|
|
163
190
|
const MemoizedComponent = memo(ComponentImpl)
|
|
191
|
+
// Names for selection-analysis error attribution; users can override
|
|
192
|
+
MemoizedComponent.displayName = componentDisplayName
|
|
164
193
|
|
|
165
194
|
// 4. Attach metadata
|
|
166
195
|
const comp = MemoizedComponent as unknown as Record<symbol | string, unknown>
|
|
@@ -262,6 +291,34 @@ function createExplicitPropMock(): unknown {
|
|
|
262
291
|
})
|
|
263
292
|
}
|
|
264
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Creates a tolerant stand-in for scalar (non-entity) props during collection.
|
|
296
|
+
* Render bodies may call it (`t('key')`), read nested properties
|
|
297
|
+
* (`labels.heading`) or coerce it to a primitive — all are no-ops so the
|
|
298
|
+
* collection pass keeps capturing entity field accesses (see issue #57).
|
|
299
|
+
*/
|
|
300
|
+
function createScalarPropMock(): unknown {
|
|
301
|
+
const mock: unknown = new Proxy(function () {}, {
|
|
302
|
+
get(_target, prop): unknown {
|
|
303
|
+
if (prop === Symbol.toPrimitive || prop === 'toString' || prop === 'valueOf') {
|
|
304
|
+
return (): string => ''
|
|
305
|
+
}
|
|
306
|
+
if (prop === Symbol.iterator) {
|
|
307
|
+
return function* (): Generator<never> {}
|
|
308
|
+
}
|
|
309
|
+
// undefined keeps JSON.stringify from recursing via a callable toJSON
|
|
310
|
+
if (prop === 'toJSON') {
|
|
311
|
+
return undefined
|
|
312
|
+
}
|
|
313
|
+
return mock
|
|
314
|
+
},
|
|
315
|
+
apply(): unknown {
|
|
316
|
+
return mock
|
|
317
|
+
},
|
|
318
|
+
})
|
|
319
|
+
return mock
|
|
320
|
+
}
|
|
321
|
+
|
|
265
322
|
/**
|
|
266
323
|
* Collects selections from JSX for implicit entity props.
|
|
267
324
|
*
|
|
@@ -327,18 +384,38 @@ function collectImplicitSelections<TProps extends object>(
|
|
|
327
384
|
return createCollectorProxy(scope, null, schemaRegistry)
|
|
328
385
|
}
|
|
329
386
|
|
|
330
|
-
// Scalar prop -
|
|
331
|
-
return
|
|
387
|
+
// Scalar prop — tolerant stand-in so render bodies using it survive
|
|
388
|
+
return createScalarPropMock()
|
|
332
389
|
},
|
|
333
390
|
})
|
|
334
391
|
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
392
|
+
// Create fragments for captured entities
|
|
393
|
+
const finalizeScopes = (): void => {
|
|
394
|
+
for (const [propName, scope] of propScopes) {
|
|
395
|
+
if (scope.hasFields()) {
|
|
396
|
+
const selection = scope.toSelectionMeta()
|
|
397
|
+
|
|
398
|
+
selectionsMap.set(propName, {
|
|
399
|
+
selection,
|
|
400
|
+
fragment: createFragment(selection, componentBrand, roles),
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
}
|
|
338
404
|
}
|
|
339
405
|
|
|
340
|
-
|
|
341
|
-
|
|
406
|
+
let jsx: ReactNode
|
|
407
|
+
try {
|
|
408
|
+
// Execute conditionFn + render to capture field accesses
|
|
409
|
+
if (conditionFn) {
|
|
410
|
+
conditionFn(propsProxy)
|
|
411
|
+
}
|
|
412
|
+
jsx = renderFn(propsProxy)
|
|
413
|
+
} catch (error) {
|
|
414
|
+
// Scopes capture accesses eagerly, so a throw mid-render still leaves a
|
|
415
|
+
// usable partial selection — keep it, then let the caller report the error.
|
|
416
|
+
finalizeScopes()
|
|
417
|
+
throw error
|
|
418
|
+
}
|
|
342
419
|
|
|
343
420
|
// Analyze JSX tree for component-level selections (handles nested createComponent)
|
|
344
421
|
try {
|
|
@@ -349,17 +426,7 @@ function collectImplicitSelections<TProps extends object>(
|
|
|
349
426
|
// Field accesses are still captured in the scope tree via proxy.
|
|
350
427
|
}
|
|
351
428
|
|
|
352
|
-
|
|
353
|
-
for (const [propName, scope] of propScopes) {
|
|
354
|
-
if (scope.hasFields()) {
|
|
355
|
-
const selection = scope.toSelectionMeta()
|
|
356
|
-
|
|
357
|
-
selectionsMap.set(propName, {
|
|
358
|
-
selection,
|
|
359
|
-
fragment: createFragment(selection, componentBrand, roles),
|
|
360
|
-
})
|
|
361
|
-
}
|
|
362
|
-
}
|
|
429
|
+
finalizeScopes()
|
|
363
430
|
}
|
|
364
431
|
|
|
365
432
|
// ============================================================================
|
|
@@ -34,11 +34,11 @@ export interface AttributeProps<T> {
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
function AttributeImpl<T>({ field, format, children }: AttributeProps<T>): ReactElement | null {
|
|
37
|
-
|
|
37
|
+
const accessor = useField(field ?? null)
|
|
38
|
+
if (accessor === null) {
|
|
38
39
|
return children
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const accessor = useField(field)
|
|
42
42
|
const extraProps = {
|
|
43
43
|
...format(accessor),
|
|
44
44
|
...(isDevAnnotationsEnabled() ? { 'data-field': field[FIELD_REF_META]?.fieldName } : {}),
|
|
@@ -319,9 +319,13 @@ interface EntityHandleRendererProps {
|
|
|
319
319
|
* Shared component that creates an EntityHandle and renders children with it.
|
|
320
320
|
* Used by both EntityByMode and EntityCreateMode.
|
|
321
321
|
*
|
|
322
|
-
* Subscribes to entity snapshot
|
|
323
|
-
*
|
|
324
|
-
*
|
|
322
|
+
* Subscribes to the entity snapshot so this host re-renders on data changes,
|
|
323
|
+
* which keeps inline reads in `children` fresh. The EntityHandle itself has a
|
|
324
|
+
* stable identity — it is intentionally NOT recreated on data changes — because
|
|
325
|
+
* it is a stateless live view over the store (see BaseHandle). Memoized leaf
|
|
326
|
+
* components (<Field>, <HasOne>, <HasMany>) re-render through their own store
|
|
327
|
+
* subscription (useField/useAccessor), not through a changing handle reference,
|
|
328
|
+
* so a data change re-renders only the leaves that read the changed data.
|
|
325
329
|
*/
|
|
326
330
|
function EntityHandleRenderer({
|
|
327
331
|
entityId,
|
|
@@ -342,11 +346,14 @@ function EntityHandleRenderer({
|
|
|
342
346
|
[store, entityType, entityId],
|
|
343
347
|
)
|
|
344
348
|
|
|
345
|
-
|
|
349
|
+
// Subscribe so this host re-renders on data changes (keeps inline reads in `children` fresh).
|
|
350
|
+
// The version is deliberately NOT fed into the handle memo below: the handle is a stable live
|
|
351
|
+
// view and must keep a stable identity across data changes.
|
|
352
|
+
useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
|
346
353
|
|
|
347
354
|
const rawHandle = useMemo(
|
|
348
355
|
() => EntityHandle.createRaw(entityId, entityType, store, dispatcher, schemaRegistry, undefined, selection),
|
|
349
|
-
[entityId, entityType, store, dispatcher, schemaRegistry, selection
|
|
356
|
+
[entityId, entityType, store, dispatcher, schemaRegistry, selection],
|
|
350
357
|
)
|
|
351
358
|
|
|
352
359
|
const handle = useMemo(
|
|
@@ -354,13 +361,6 @@ function EntityHandleRenderer({
|
|
|
354
361
|
[rawHandle],
|
|
355
362
|
)
|
|
356
363
|
|
|
357
|
-
// `version` is a useMemo dep, so `rawHandle` is recreated on every entity data change (to give
|
|
358
|
-
// memoized children a fresh reference). Superseded handles need no cleanup: EntityHandle and its
|
|
359
|
-
// child Field/HasMany handles are stateless live views that own no resources (see BaseHandle),
|
|
360
|
-
// so they are reclaimed by GC once unreferenced — and a consumer that still holds an accessor
|
|
361
|
-
// from an earlier render (e.g. the Slate block editor dispatching `setValue` from an async
|
|
362
|
-
// onChange after the version bumped) can keep using it.
|
|
363
|
-
|
|
364
364
|
const result = children(handle as EntityAccessor<unknown>)
|
|
365
365
|
return <>{annotateElement(result, { 'data-entity': entityType, 'data-entity-id': entityId })}</>
|
|
366
366
|
}
|
|
@@ -22,13 +22,11 @@ import { annotateElement, isDevAnnotationsEnabled } from '../devAnnotations.js'
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
function FieldImpl<T>({ field, children, format }: FieldProps<T>): ReactElement | null {
|
|
25
|
-
|
|
26
|
-
if (
|
|
25
|
+
const accessor = useField(field ?? null)
|
|
26
|
+
if (accessor === null) {
|
|
27
27
|
return null
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// useField() subscribes to store and returns FieldAccessor with .value access
|
|
31
|
-
const accessor = useField(field)
|
|
32
30
|
const fieldName = field[FIELD_REF_META]?.fieldName
|
|
33
31
|
|
|
34
32
|
if (children) {
|
|
@@ -3,6 +3,7 @@ import type { IfProps, SelectionFieldMeta, SelectionMeta, SelectionProvider, Fie
|
|
|
3
3
|
import { FIELD_REF_META, BINDX_COMPONENT } from '../types.js'
|
|
4
4
|
import { mergeSelections, createEmptySelection } from '../SelectionMeta.js'
|
|
5
5
|
import { useField } from '../../hooks/useField.js'
|
|
6
|
+
import { useRefSubscription } from '../../hooks/useFields.js'
|
|
6
7
|
import {
|
|
7
8
|
type Condition,
|
|
8
9
|
isCondition,
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
CONDITION_META,
|
|
12
13
|
} from '../conditions.js'
|
|
13
14
|
|
|
15
|
+
const NO_CONDITION_FIELDS: readonly unknown[] = []
|
|
16
|
+
|
|
14
17
|
/**
|
|
15
18
|
* If component - conditional rendering that ensures both branches are analyzed
|
|
16
19
|
*
|
|
@@ -55,6 +58,9 @@ import {
|
|
|
55
58
|
function IfImpl({ condition, then: thenBranch, else: elseBranch }: IfProps): ReactElement | null {
|
|
56
59
|
const fieldRef = typeof condition !== 'boolean' && !isCondition(condition) ? condition : null
|
|
57
60
|
const fieldAccessor = useField(fieldRef)
|
|
61
|
+
// A Condition is evaluated against live data, so it needs a subscription of its own —
|
|
62
|
+
// a memoized <If> is not re-rendered by its parent.
|
|
63
|
+
useRefSubscription(isCondition(condition) ? collectConditionFields(condition) : NO_CONDITION_FIELDS)
|
|
58
64
|
|
|
59
65
|
let conditionValue: boolean
|
|
60
66
|
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
SelectionProvider,
|
|
14
14
|
} from '../types.js'
|
|
15
15
|
import { FIELD_REF_META, BINDX_COMPONENT } from '../types.js'
|
|
16
|
-
import {
|
|
16
|
+
import { useFields, useRefSubscription } from '../../hooks/useFields.js'
|
|
17
17
|
import {
|
|
18
18
|
type Condition,
|
|
19
19
|
isCondition,
|
|
@@ -132,6 +132,20 @@ function resolveTriggerField(props: CaseProps<unknown>): FieldRef<unknown> | nul
|
|
|
132
132
|
return null
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Fields read by `if={cond...}` cases. They are evaluated against live data, so they need
|
|
137
|
+
* a subscription of their own — a memoized <Switch> is not re-rendered by its parent.
|
|
138
|
+
*/
|
|
139
|
+
function collectConditionRefs(cases: readonly CaseEntry[]): unknown[] {
|
|
140
|
+
const refs: unknown[] = []
|
|
141
|
+
for (const { props } of cases) {
|
|
142
|
+
if ('if' in props && props.if !== undefined && isCondition(props.if)) {
|
|
143
|
+
refs.push(...collectConditionFields(props.if))
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return refs
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
// =============================================================================
|
|
136
150
|
// Runtime
|
|
137
151
|
// =============================================================================
|
|
@@ -139,17 +153,14 @@ function resolveTriggerField(props: CaseProps<unknown>): FieldRef<unknown> | nul
|
|
|
139
153
|
function SwitchImpl({ children }: SwitchProps): ReactElement | null {
|
|
140
154
|
const entries = extractEntries(children)
|
|
141
155
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
147
|
-
accessors.push(useField(ref))
|
|
148
|
-
}
|
|
156
|
+
// Two fixed hooks cover every case, so the hook count never depends on how many
|
|
157
|
+
// <Case> children are rendered (a conditional <Case> used to crash React).
|
|
158
|
+
const accessors = useFields(entries.cases.map(entry => resolveTriggerField(entry.props)))
|
|
159
|
+
useRefSubscription(collectConditionRefs(entries.cases))
|
|
149
160
|
|
|
150
161
|
for (let i = 0; i < entries.cases.length; i++) {
|
|
151
162
|
const { props } = entries.cases[i]!
|
|
152
|
-
const accessor = accessors[i]
|
|
163
|
+
const accessor = accessors[i]
|
|
153
164
|
|
|
154
165
|
if ('show' in props && props.show !== undefined) {
|
|
155
166
|
const value = accessor?.value
|
|
@@ -32,11 +32,9 @@ import { createComponentBuilder } from './componentBuilder.js'
|
|
|
32
32
|
* ))
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
36
35
|
export function createComponent(): ComponentBuilder<ComponentBuilderState<{}, object, readonly string[]>>
|
|
37
36
|
export function createComponent<const TRoles extends readonly string[]>(
|
|
38
37
|
options: CreateComponentOptions<TRoles>,
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
40
38
|
): ComponentBuilder<ComponentBuilderState<{}, object, TRoles>>
|
|
41
39
|
export function createComponent(options?: CreateComponentOptions<readonly string[]>): ComponentBuilder<ComponentBuilderState> {
|
|
42
40
|
const roles = options?.roles ?? []
|