@navios/di-react 0.1.0 → 0.2.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.
- package/CHANGELOG.md +39 -0
- package/README.md +609 -28
- package/lib/index.d.mts +297 -18
- package/lib/index.d.mts.map +1 -0
- package/lib/index.d.ts +297 -18
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +536 -346
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +533 -345
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/project.json +2 -2
- package/src/hooks/__tests__/use-service.spec.mts +1 -1
- package/src/hooks/index.mts +8 -2
- package/src/hooks/use-container.mts +47 -2
- package/src/hooks/use-invalidate.mts +3 -3
- package/src/hooks/use-optional-service.mts +59 -34
- package/src/hooks/use-scope.mts +66 -5
- package/src/hooks/use-service.mts +44 -18
- package/src/hooks/use-suspense-service.mts +48 -29
- package/src/providers/__tests__/scope-provider.spec.mts +84 -1
- package/src/providers/container-provider.mts +2 -6
- package/src/providers/context.mts +11 -1
- package/src/providers/index.mts +2 -2
- package/src/providers/scope-provider.mts +34 -25
- package/{tsup.config.mts → tsdown.config.mts} +4 -3
- package/lib/_tsup-dts-rollup.d.mts +0 -304
- package/lib/_tsup-dts-rollup.d.ts +0 -304
|
@@ -9,12 +9,11 @@ import type {
|
|
|
9
9
|
} from '@navios/di'
|
|
10
10
|
import type { z, ZodType } from 'zod/v4'
|
|
11
11
|
|
|
12
|
-
import { useCallback,
|
|
12
|
+
import { useCallback, useEffect, useReducer, useRef, useState } from 'react'
|
|
13
13
|
|
|
14
14
|
import type { Join, UnionToArray } from '../types.mjs'
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
import { useContainer } from './use-container.mjs'
|
|
16
|
+
import { useContainer, useRootContainer } from './use-container.mjs'
|
|
18
17
|
|
|
19
18
|
type ServiceState<T> =
|
|
20
19
|
| { status: 'idle' }
|
|
@@ -102,10 +101,28 @@ export function useService(
|
|
|
102
101
|
| FactoryInjectionToken<any, any>,
|
|
103
102
|
args?: unknown,
|
|
104
103
|
): UseServiceResult<any> {
|
|
104
|
+
// useContainer returns ScopedContainer if inside ScopeProvider, otherwise Container
|
|
105
|
+
// This automatically handles request-scoped services correctly
|
|
105
106
|
const container = useContainer()
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
107
|
+
const rootContainer = useRootContainer()
|
|
108
|
+
const serviceLocator = rootContainer.getServiceLocator()
|
|
109
|
+
|
|
110
|
+
// Try to get the instance synchronously first for better performance
|
|
111
|
+
// This avoids the async loading state when the instance is already cached
|
|
112
|
+
// We use a ref to track this so it doesn't cause effect re-runs
|
|
113
|
+
const initialSyncInstanceRef = useRef<any>(undefined)
|
|
114
|
+
const isFirstRenderRef = useRef(true)
|
|
115
|
+
|
|
116
|
+
if (isFirstRenderRef.current) {
|
|
117
|
+
initialSyncInstanceRef.current = container.tryGetSync(token, args)
|
|
118
|
+
isFirstRenderRef.current = false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const initialState: ServiceState<any> = initialSyncInstanceRef.current
|
|
122
|
+
? { status: 'success', data: initialSyncInstanceRef.current }
|
|
123
|
+
: { status: 'idle' }
|
|
124
|
+
|
|
125
|
+
const [state, dispatch] = useReducer(serviceReducer, initialState)
|
|
109
126
|
const instanceNameRef = useRef<string | null>(null)
|
|
110
127
|
const [refetchCounter, setRefetchCounter] = useState(0)
|
|
111
128
|
|
|
@@ -136,15 +153,7 @@ export function useService(
|
|
|
136
153
|
// Fetch the service and set up subscription
|
|
137
154
|
const fetchAndSubscribe = async () => {
|
|
138
155
|
try {
|
|
139
|
-
//
|
|
140
|
-
// This ensures request-scoped services are resolved in the correct scope
|
|
141
|
-
if (scopeId) {
|
|
142
|
-
const requestContexts = serviceLocator.getRequestContexts()
|
|
143
|
-
if (requestContexts.has(scopeId)) {
|
|
144
|
-
container.setCurrentRequestContext(scopeId)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
156
|
+
// The container (either ScopedContainer or Container) handles resolution correctly
|
|
148
157
|
const instance = await container.get(
|
|
149
158
|
// @ts-expect-error - token is valid
|
|
150
159
|
token as AnyInjectableType,
|
|
@@ -177,14 +186,31 @@ export function useService(
|
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
// If we already have a sync instance from initial render, just set up subscription
|
|
190
|
+
// Otherwise, fetch async
|
|
191
|
+
const syncInstance = initialSyncInstanceRef.current
|
|
192
|
+
if (syncInstance && refetchCounter === 0) {
|
|
193
|
+
const instanceName = serviceLocator.getInstanceIdentifier(
|
|
194
|
+
token as AnyInjectableType,
|
|
195
|
+
args,
|
|
196
|
+
)
|
|
197
|
+
instanceNameRef.current = instanceName
|
|
198
|
+
unsubscribe = eventBus.on(instanceName, 'destroy', () => {
|
|
199
|
+
if (isMounted) {
|
|
200
|
+
dispatch({ type: 'loading' })
|
|
201
|
+
void fetchAndSubscribe()
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
} else {
|
|
205
|
+
dispatch({ type: 'loading' })
|
|
206
|
+
void fetchAndSubscribe()
|
|
207
|
+
}
|
|
182
208
|
|
|
183
209
|
return () => {
|
|
184
210
|
isMounted = false
|
|
185
211
|
unsubscribe?.()
|
|
186
212
|
}
|
|
187
|
-
}, [container, serviceLocator, token, args,
|
|
213
|
+
}, [container, serviceLocator, token, args, refetchCounter])
|
|
188
214
|
|
|
189
215
|
const refetch = useCallback(() => {
|
|
190
216
|
setRefetchCounter((c) => c + 1)
|
|
@@ -13,7 +13,7 @@ import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'
|
|
|
13
13
|
|
|
14
14
|
import type { Join, UnionToArray } from '../types.mjs'
|
|
15
15
|
|
|
16
|
-
import { useContainer } from './use-container.mjs'
|
|
16
|
+
import { useContainer, useRootContainer } from './use-container.mjs'
|
|
17
17
|
|
|
18
18
|
// Cache entry for suspense
|
|
19
19
|
interface CacheEntry<T> {
|
|
@@ -47,6 +47,30 @@ function getCache(container: object): Map<string, CacheEntry<any>> {
|
|
|
47
47
|
return cache
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Sets up invalidation subscription for a cache entry if not already subscribed.
|
|
52
|
+
* When the service is destroyed, clears the cache and notifies subscribers.
|
|
53
|
+
*/
|
|
54
|
+
function setupInvalidationSubscription(
|
|
55
|
+
entry: CacheEntry<any>,
|
|
56
|
+
serviceLocator: ReturnType<
|
|
57
|
+
import('@navios/di').Container['getServiceLocator']
|
|
58
|
+
>,
|
|
59
|
+
): void {
|
|
60
|
+
if (entry.unsubscribe || !entry.instanceName) return
|
|
61
|
+
|
|
62
|
+
const eventBus = serviceLocator.getEventBus()
|
|
63
|
+
entry.unsubscribe = eventBus.on(entry.instanceName, 'destroy', () => {
|
|
64
|
+
// Clear cache and notify subscribers to re-fetch
|
|
65
|
+
entry.result = undefined
|
|
66
|
+
entry.error = undefined
|
|
67
|
+
entry.status = 'pending'
|
|
68
|
+
entry.promise = null
|
|
69
|
+
// Notify all subscribers
|
|
70
|
+
entry.subscribers.forEach((callback) => callback())
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
// #1 Simple class
|
|
51
75
|
export function useSuspenseService<T extends ClassType>(
|
|
52
76
|
token: T,
|
|
@@ -86,8 +110,10 @@ export function useSuspenseService(
|
|
|
86
110
|
| FactoryInjectionToken<any, any>,
|
|
87
111
|
args?: unknown,
|
|
88
112
|
): any {
|
|
113
|
+
// useContainer returns ScopedContainer if inside ScopeProvider, otherwise Container
|
|
89
114
|
const container = useContainer()
|
|
90
|
-
const
|
|
115
|
+
const rootContainer = useRootContainer()
|
|
116
|
+
const serviceLocator = rootContainer.getServiceLocator()
|
|
91
117
|
const cache = getCache(container)
|
|
92
118
|
const cacheKey = getCacheKey(token, args)
|
|
93
119
|
const entryRef = useRef<CacheEntry<any> | null>(null)
|
|
@@ -111,14 +137,20 @@ export function useSuspenseService(
|
|
|
111
137
|
|
|
112
138
|
// Initialize or get cache entry
|
|
113
139
|
if (!cache.has(cacheKey)) {
|
|
140
|
+
// Try to get the instance synchronously first for better performance
|
|
141
|
+
// This avoids suspense when the instance is already cached
|
|
142
|
+
const syncInstance = container.tryGetSync(token, args)
|
|
143
|
+
|
|
114
144
|
const entry: CacheEntry<any> = {
|
|
115
145
|
promise: null,
|
|
116
|
-
result: undefined,
|
|
146
|
+
result: syncInstance ?? undefined,
|
|
117
147
|
error: undefined,
|
|
118
|
-
status: 'pending',
|
|
148
|
+
status: syncInstance ? 'resolved' : 'pending',
|
|
119
149
|
version: 0,
|
|
120
150
|
subscribers: new Set(),
|
|
121
|
-
instanceName:
|
|
151
|
+
instanceName: syncInstance
|
|
152
|
+
? serviceLocator.getInstanceIdentifier(token as AnyInjectableType, args)
|
|
153
|
+
: null,
|
|
122
154
|
unsubscribe: undefined,
|
|
123
155
|
}
|
|
124
156
|
cache.set(cacheKey, entry)
|
|
@@ -144,22 +176,7 @@ export function useSuspenseService(
|
|
|
144
176
|
)
|
|
145
177
|
|
|
146
178
|
// Subscribe to invalidation events if not already subscribed
|
|
147
|
-
|
|
148
|
-
const eventBus = serviceLocator.getEventBus()
|
|
149
|
-
currentEntry.unsubscribe = eventBus.on(
|
|
150
|
-
currentEntry.instanceName,
|
|
151
|
-
'destroy',
|
|
152
|
-
() => {
|
|
153
|
-
// Clear cache and notify subscribers to re-fetch
|
|
154
|
-
currentEntry.result = undefined
|
|
155
|
-
currentEntry.error = undefined
|
|
156
|
-
currentEntry.status = 'pending'
|
|
157
|
-
currentEntry.promise = null
|
|
158
|
-
// Notify all subscribers
|
|
159
|
-
currentEntry.subscribers.forEach((callback) => callback())
|
|
160
|
-
},
|
|
161
|
-
)
|
|
162
|
-
}
|
|
179
|
+
setupInvalidationSubscription(currentEntry, serviceLocator)
|
|
163
180
|
|
|
164
181
|
// Notify subscribers
|
|
165
182
|
currentEntry.subscribers.forEach((callback) => callback())
|
|
@@ -193,16 +210,18 @@ export function useSuspenseService(
|
|
|
193
210
|
// Use sync external store to track cache state
|
|
194
211
|
useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
|
195
212
|
|
|
196
|
-
//
|
|
213
|
+
// Set up subscription for sync instances that don't have one yet
|
|
197
214
|
useEffect(() => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
const currentEntry = entryRef.current
|
|
216
|
+
if (
|
|
217
|
+
currentEntry &&
|
|
218
|
+
currentEntry.status === 'resolved' &&
|
|
219
|
+
currentEntry.instanceName &&
|
|
220
|
+
!currentEntry.unsubscribe
|
|
221
|
+
) {
|
|
222
|
+
setupInvalidationSubscription(currentEntry, serviceLocator)
|
|
204
223
|
}
|
|
205
|
-
}, [entry])
|
|
224
|
+
}, [serviceLocator, entry])
|
|
206
225
|
|
|
207
226
|
// Start fetching if not already
|
|
208
227
|
if (entry.status === 'pending' && !entry.promise) {
|
|
@@ -5,7 +5,7 @@ import { createElement } from 'react'
|
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
6
|
|
|
7
7
|
import { useService } from '../../hooks/use-service.mjs'
|
|
8
|
-
import { useScope } from '../../hooks/use-scope.mjs'
|
|
8
|
+
import { useScope, useScopeMetadata, useScopedContainer } from '../../hooks/use-scope.mjs'
|
|
9
9
|
import { ContainerProvider } from '../container-provider.mjs'
|
|
10
10
|
import { ScopeProvider } from '../scope-provider.mjs'
|
|
11
11
|
|
|
@@ -276,5 +276,88 @@ describe('ScopeProvider', () => {
|
|
|
276
276
|
|
|
277
277
|
expect(scopeValue).toBe('test-scope')
|
|
278
278
|
})
|
|
279
|
+
|
|
280
|
+
it('should provide metadata via useScopeMetadata', () => {
|
|
281
|
+
let userId: string | undefined
|
|
282
|
+
let role: string | undefined
|
|
283
|
+
let missing: string | undefined
|
|
284
|
+
|
|
285
|
+
function TestComponent() {
|
|
286
|
+
userId = useScopeMetadata<string>('userId')
|
|
287
|
+
role = useScopeMetadata<string>('role')
|
|
288
|
+
missing = useScopeMetadata<string>('nonexistent')
|
|
289
|
+
return createElement('div', { 'data-testid': 'test' }, 'Test')
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
render(
|
|
293
|
+
createWrapper(
|
|
294
|
+
createElement(
|
|
295
|
+
ScopeProvider,
|
|
296
|
+
// @ts-expect-error - props are not typed
|
|
297
|
+
{
|
|
298
|
+
scopeId: 'test-scope',
|
|
299
|
+
metadata: { userId: '123', role: 'admin' },
|
|
300
|
+
},
|
|
301
|
+
createElement(TestComponent),
|
|
302
|
+
),
|
|
303
|
+
),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
expect(userId).toBe('123')
|
|
307
|
+
expect(role).toBe('admin')
|
|
308
|
+
expect(missing).toBeUndefined()
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('should return undefined for useScopeMetadata outside ScopeProvider', () => {
|
|
312
|
+
let userId: string | undefined = 'initial'
|
|
313
|
+
|
|
314
|
+
function TestComponent() {
|
|
315
|
+
userId = useScopeMetadata<string>('userId')
|
|
316
|
+
return createElement('div', { 'data-testid': 'test' }, 'Test')
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
render(createWrapper(createElement(TestComponent)))
|
|
320
|
+
|
|
321
|
+
expect(userId).toBeUndefined()
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
describe('useScopedContainer', () => {
|
|
326
|
+
it('should return null when not inside ScopeProvider', () => {
|
|
327
|
+
let scopedContainer: unknown = 'not-set'
|
|
328
|
+
|
|
329
|
+
function TestComponent() {
|
|
330
|
+
scopedContainer = useScopedContainer()
|
|
331
|
+
return createElement('div', { 'data-testid': 'test' }, 'Test')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
render(createWrapper(createElement(TestComponent)))
|
|
335
|
+
|
|
336
|
+
expect(scopedContainer).toBeNull()
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('should return ScopedContainer when inside ScopeProvider', () => {
|
|
340
|
+
let scopedContainer: unknown = null
|
|
341
|
+
|
|
342
|
+
function TestComponent() {
|
|
343
|
+
scopedContainer = useScopedContainer()
|
|
344
|
+
return createElement('div', { 'data-testid': 'test' }, 'Test')
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
render(
|
|
348
|
+
createWrapper(
|
|
349
|
+
createElement(
|
|
350
|
+
ScopeProvider,
|
|
351
|
+
// @ts-expect-error - props are not typed
|
|
352
|
+
{ scopeId: 'test-scope' },
|
|
353
|
+
createElement(TestComponent),
|
|
354
|
+
),
|
|
355
|
+
),
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
expect(scopedContainer).not.toBeNull()
|
|
359
|
+
expect(typeof (scopedContainer as any).getRequestId).toBe('function')
|
|
360
|
+
expect((scopedContainer as any).getRequestId()).toBe('test-scope')
|
|
361
|
+
})
|
|
279
362
|
})
|
|
280
363
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Container } from '@navios/di'
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { jsx } from 'react/jsx-runtime'
|
|
5
5
|
|
|
6
6
|
import { ContainerContext } from './context.mjs'
|
|
7
7
|
|
|
@@ -14,9 +14,5 @@ export function ContainerProvider({
|
|
|
14
14
|
container,
|
|
15
15
|
children,
|
|
16
16
|
}: ContainerProviderProps) {
|
|
17
|
-
return
|
|
18
|
-
ContainerContext.Provider,
|
|
19
|
-
{ value: container },
|
|
20
|
-
children,
|
|
21
|
-
)
|
|
17
|
+
return jsx(ContainerContext.Provider, { value: container, children })
|
|
22
18
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { createContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import type { Container } from '@navios/di'
|
|
3
|
+
import type { Container, ScopedContainer } from '@navios/di'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Context for the root Container.
|
|
7
|
+
* This is set by ContainerProvider and provides the base container.
|
|
8
|
+
*/
|
|
5
9
|
export const ContainerContext = createContext<Container | null>(null)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Context for the current ScopedContainer (if inside a ScopeProvider).
|
|
13
|
+
* This allows nested components to access request-scoped services.
|
|
14
|
+
*/
|
|
15
|
+
export const ScopedContainerContext = createContext<ScopedContainer | null>(null)
|
package/src/providers/index.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { ContainerContext } from './context.mjs'
|
|
1
|
+
export { ContainerContext, ScopedContainerContext } from './context.mjs'
|
|
2
2
|
export { ContainerProvider } from './container-provider.mjs'
|
|
3
3
|
export type { ContainerProviderProps } from './container-provider.mjs'
|
|
4
|
-
export {
|
|
4
|
+
export { ScopeProvider } from './scope-provider.mjs'
|
|
5
5
|
export type { ScopeProviderProps } from './scope-provider.mjs'
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
+
import type { ScopedContainer } from '@navios/di'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { useContext, useEffect, useId, useRef } from 'react'
|
|
5
|
+
import { jsx } from 'react/jsx-runtime'
|
|
4
6
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Context for the current scope ID.
|
|
9
|
-
* This allows nested components to access the current request scope.
|
|
10
|
-
*/
|
|
11
|
-
export const ScopeContext = createContext<string | null>(null)
|
|
7
|
+
import { ContainerContext, ScopedContainerContext } from './context.mjs'
|
|
12
8
|
|
|
13
9
|
export interface ScopeProviderProps {
|
|
14
10
|
/**
|
|
@@ -45,8 +41,8 @@ export interface ScopeProviderProps {
|
|
|
45
41
|
* ```tsx
|
|
46
42
|
* // Each row gets its own RowStateService instance
|
|
47
43
|
* {rows.map(row => (
|
|
48
|
-
* <ScopeProvider key={row.id} scopeId={row.id}>
|
|
49
|
-
* <TableRow
|
|
44
|
+
* <ScopeProvider key={row.id} scopeId={row.id} metadata={{ rowData: row }}>
|
|
45
|
+
* <TableRow />
|
|
50
46
|
* </ScopeProvider>
|
|
51
47
|
* ))}
|
|
52
48
|
* ```
|
|
@@ -57,32 +53,45 @@ export function ScopeProvider({
|
|
|
57
53
|
priority = 100,
|
|
58
54
|
children,
|
|
59
55
|
}: ScopeProviderProps) {
|
|
60
|
-
const container =
|
|
56
|
+
const container = useContext(ContainerContext)
|
|
57
|
+
if (!container) {
|
|
58
|
+
throw new Error('ScopeProvider must be used within a ContainerProvider')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
61
|
const generatedId = useId()
|
|
62
62
|
const effectiveScopeId = scopeId ?? generatedId
|
|
63
|
-
const
|
|
63
|
+
const scopedContainerRef = useRef<ScopedContainer | null>(null)
|
|
64
64
|
|
|
65
|
-
//
|
|
65
|
+
// Create ScopedContainer on first render only
|
|
66
66
|
// We use a ref to track initialization to handle React StrictMode double-renders
|
|
67
|
-
if (!
|
|
68
|
-
// Check if
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
if (!scopedContainerRef.current) {
|
|
68
|
+
// Check if this request ID already exists (e.g., from StrictMode double render)
|
|
69
|
+
if (!container.hasActiveRequest(effectiveScopeId)) {
|
|
70
|
+
scopedContainerRef.current = container.beginRequest(
|
|
71
|
+
effectiveScopeId,
|
|
72
|
+
metadata,
|
|
73
|
+
priority,
|
|
74
|
+
)
|
|
72
75
|
}
|
|
73
|
-
isInitializedRef.current = true
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
// End request context on unmount
|
|
77
79
|
useEffect(() => {
|
|
80
|
+
const scopedContainer = scopedContainerRef.current
|
|
78
81
|
return () => {
|
|
79
|
-
|
|
82
|
+
if (scopedContainer) {
|
|
83
|
+
void scopedContainer.endRequest()
|
|
84
|
+
}
|
|
80
85
|
}
|
|
81
|
-
}, [
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
// If we don't have a scoped container (shouldn't happen normally), don't render
|
|
89
|
+
if (!scopedContainerRef.current) {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
82
92
|
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
{ value: effectiveScopeId },
|
|
93
|
+
return jsx(ScopedContainerContext.Provider, {
|
|
94
|
+
value: scopedContainerRef.current,
|
|
86
95
|
children,
|
|
87
|
-
)
|
|
96
|
+
})
|
|
88
97
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { defineConfig } from '
|
|
1
|
+
import { defineConfig } from 'tsdown'
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
entry: ['src/index.mts'],
|
|
5
5
|
outDir: 'lib',
|
|
6
6
|
format: ['esm', 'cjs'],
|
|
7
7
|
clean: true,
|
|
8
|
-
treeshake:
|
|
8
|
+
treeshake: true,
|
|
9
9
|
sourcemap: true,
|
|
10
10
|
platform: 'browser',
|
|
11
|
-
|
|
11
|
+
dts: true,
|
|
12
|
+
target: 'es2022',
|
|
12
13
|
external: ['react', '@navios/di'],
|
|
13
14
|
})
|