-
Notifications
You must be signed in to change notification settings - Fork 439
feat: CSR/SSRv1 context #5356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: CSR/SSRv1 context #5356
Changes from 16 commits
8097059
60040a9
f9a9acb
b394e9e
d395543
3267884
b95f94f
f857d10
4d7c32b
1181705
bfbb5ca
c5b29e4
8edc472
043eb29
24dcf50
094aeb8
5c4b25f
eed8082
4ae7f1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /* | ||
| * Copyright (c) 2025, salesforce.com, inc. | ||
| * All rights reserved. | ||
| * SPDX-License-Identifier: MIT | ||
| * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
| */ | ||
| import { | ||
| isUndefined, | ||
| getPrototypeOf, | ||
| keys, | ||
| getContextKeys, | ||
| ArrayFilter, | ||
| ContextEventName, | ||
| isTrustedContext, | ||
| } from '@lwc/shared'; | ||
| import { type VM } from '../vm'; | ||
| import { logErrorOnce } from '../../shared/logger'; | ||
| import type { Signal } from '@lwc/signals'; | ||
| import type { RendererAPI } from '../renderer'; | ||
| import type { ShouldContinueBubbling } from '../wiring/types'; | ||
|
|
||
| type ContextProvidedCallback = (contextSignal?: Signal<unknown>) => void; | ||
| type ContextVarieties = Map<unknown, Signal<unknown>>; | ||
|
|
||
| class ContextConnector<C extends object> { | ||
| component: C; | ||
| #renderer: RendererAPI; | ||
| #providedContextVarieties: ContextVarieties; | ||
| #elm: HTMLElement; | ||
|
|
||
| constructor(vm: VM, component: C, providedContextVarieties: ContextVarieties) { | ||
| this.component = component; | ||
| this.#renderer = vm.renderer; | ||
| this.#elm = vm.elm; | ||
| this.#providedContextVarieties = providedContextVarieties; | ||
| } | ||
|
|
||
| provideContext<V extends object>( | ||
| contextVariety: V, | ||
| providedContextSignal: Signal<unknown> | ||
| ): void { | ||
| // registerContextProvider is called one time when the component is first provided context. | ||
|
||
| // The component is then listening for consumers to consume the provided context. | ||
| if (this.#providedContextVarieties.size === 0) { | ||
| this.#renderer.registerContextProvider( | ||
| this.#elm, | ||
| ContextEventName, | ||
| (payload): ShouldContinueBubbling => { | ||
| // This callback is invoked when the provided context is consumed somewhere down | ||
| // in the component's subtree. | ||
| return payload.setNewContext(this.#providedContextVarieties); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| if (this.#providedContextVarieties.has(contextVariety)) { | ||
| logErrorOnce( | ||
| 'Multiple contexts of the same variety were provided. Only the first context will be used.' | ||
| ); | ||
| return; | ||
| } | ||
| this.#providedContextVarieties.set(contextVariety, providedContextSignal); | ||
| } | ||
|
|
||
| consumeContext<V extends object>( | ||
| contextVariety: V, | ||
| contextProvidedCallback: ContextProvidedCallback | ||
| ): void { | ||
| this.#renderer.registerContextConsumer(this.#elm, ContextEventName, { | ||
| setNewContext: (providerContextVarieties: ContextVarieties): ShouldContinueBubbling => { | ||
| // If the provider has the specified context variety, then it is consumed | ||
| // and true is returned to stop bubbling. | ||
| if (providerContextVarieties.has(contextVariety)) { | ||
| contextProvidedCallback(providerContextVarieties.get(contextVariety)); | ||
| return true; | ||
| } | ||
| // Return false as context has not been found/consumed | ||
| // and the consumer should continue traversing the context tree | ||
| return false; | ||
| }, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export function connectContext(vm: VM) { | ||
| const contextKeys = getContextKeys(); | ||
|
|
||
| if (isUndefined(contextKeys)) { | ||
| return; | ||
| } | ||
|
|
||
| const { connectContext } = contextKeys; | ||
| const { component } = vm; | ||
|
|
||
| const enumerableKeys = keys(getPrototypeOf(component)); | ||
| const contextfulKeys = ArrayFilter.call(enumerableKeys, (enumerableKey) => | ||
| isTrustedContext((component as any)[enumerableKey]) | ||
| ); | ||
|
|
||
| if (contextfulKeys.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const providedContextVarieties: ContextVarieties = new Map(); | ||
|
|
||
| for (let i = 0; i < contextfulKeys.length; i++) { | ||
| (component as any)[contextfulKeys[i]][connectContext]( | ||
| new ContextConnector(vm, component, providedContextVarieties) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export function disconnectContext(vm: VM) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The exposed functions look good. Let's just add docs, and I think they're finished.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are not exposed via engine-server/dom - IIUC:
|
||
| const contextKeys = getContextKeys(); | ||
|
|
||
| if (!contextKeys) { | ||
| return; | ||
| } | ||
|
|
||
| const { disconnectContext } = contextKeys; | ||
| const { component } = vm; | ||
|
|
||
| const enumerableKeys = keys(getPrototypeOf(component)); | ||
| const contextfulKeys = ArrayFilter.call(enumerableKeys, (enumerableKey) => | ||
| isTrustedContext((component as any)[enumerableKey]) | ||
| ); | ||
|
|
||
| if (contextfulKeys.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| for (let i = 0; i < contextfulKeys.length; i++) { | ||
| (component as any)[contextfulKeys[i]][disconnectContext](component); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.