forked from salesforce/lwc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcomponent.ts
More file actions
162 lines (144 loc) · 6.33 KB
/
component.ts
File metadata and controls
162 lines (144 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* Copyright (c) 2018, 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 { assert, isFalse, isFunction, isUndefined, LOWEST_API_VERSION } from '@lwc/shared';
import { createReactiveObserver, unsubscribeFromSignals } from './mutation-tracker';
import { invokeComponentRenderMethod, isInvokingRender, invokeEventListener } from './invoker';
import { scheduleRehydration } from './vm';
import { isUpdatingTemplate, getVMBeingRendered } from './template';
import { checkVersionMismatch } from './check-version-mismatch';
import { associateReactiveObserverWithVM } from './mutation-logger';
import type { VM } from './vm';
import type { LightningElementConstructor } from './base-lightning-element';
import type { Template } from './template';
import type { VNodes } from './vnodes';
import type { ReactiveObserver } from './mutation-tracker';
import type { APIVersion } from '@lwc/shared';
type ComponentConstructorMetadata = {
tmpl: Template;
sel: string;
apiVersion: APIVersion;
enableSyntheticElementInternals?: boolean | undefined;
};
const registeredComponentMap: Map<LightningElementConstructor, ComponentConstructorMetadata> =
new Map();
/**
* INTERNAL: This function can only be invoked by compiled code. The compiler
* will prevent this function from being imported by userland code.
* @param Ctor
* @param metadata
*/
export function registerComponent(
// We typically expect a LightningElementConstructor, but technically you can call this with anything
Ctor: any,
metadata: ComponentConstructorMetadata
): any {
if (isFunction(Ctor)) {
if (process.env.NODE_ENV !== 'production') {
// There is no point in running this in production, because the version mismatch check relies
// on code comments which are stripped out in production by minifiers
checkVersionMismatch(Ctor, 'component');
}
// TODO [#3331]: add validation to check the value of metadata.sel is not an empty string.
registeredComponentMap.set(Ctor, metadata);
}
// chaining this method as a way to wrap existing assignment of component constructor easily,
// without too much transformation
return Ctor;
}
export function getComponentRegisteredTemplate(
Ctor: LightningElementConstructor
): Template | undefined {
return registeredComponentMap.get(Ctor)?.tmpl;
}
export function getComponentRegisteredName(Ctor: LightningElementConstructor): string | undefined {
return registeredComponentMap.get(Ctor)?.sel;
}
export function getComponentAPIVersion(Ctor: LightningElementConstructor): APIVersion {
const metadata = registeredComponentMap.get(Ctor);
const apiVersion: APIVersion | undefined = metadata?.apiVersion;
if (isUndefined(apiVersion)) {
// This should only occur in our Karma tests; in practice every component
// is registered, and so this code path should not get hit. But to be safe,
// return the lowest possible version.
return LOWEST_API_VERSION;
}
return apiVersion;
}
export function supportsSyntheticElementInternals(Ctor: LightningElementConstructor): boolean {
return registeredComponentMap.get(Ctor)?.enableSyntheticElementInternals || false;
}
export function getTemplateReactiveObserver(vm: VM): ReactiveObserver {
const reactiveObserver = createReactiveObserver(() => {
const { isDirty } = vm;
if (isFalse(isDirty)) {
markComponentAsDirty(vm);
scheduleRehydration(vm);
}
});
if (process.env.NODE_ENV !== 'production') {
associateReactiveObserverWithVM(reactiveObserver, vm);
}
return reactiveObserver;
}
export function resetTemplateObserverAndUnsubscribe(vm: VM) {
const { tro, component } = vm;
tro.reset();
// Unsubscribe every time the template reactive observer is reset.
if (lwcRuntimeFlags.ENABLE_EXPERIMENTAL_SIGNALS) {
unsubscribeFromSignals(component);
}
}
export function renderComponent(vm: VM): VNodes {
if (process.env.NODE_ENV !== 'production') {
assert.invariant(vm.isDirty, `${vm} is not dirty.`);
}
// The engine should only hold a subscription to a signal if it is rendered in the template.
// Because of the potential presence of conditional rendering logic, we unsubscribe on each render
// in the scenario where it is present in one condition but not the other.
// For example:
// 1. There is an lwc:if=true conditional where the signal is present on the template.
// 2. The lwc:if changes to false and the signal is no longer present on the template.
// If the signal is still subscribed to, the template will re-render when it receives a notification
// from the signal, even though we won't be using the new value.
resetTemplateObserverAndUnsubscribe(vm);
const vnodes = invokeComponentRenderMethod(vm);
vm.isDirty = false;
vm.isScheduled = false;
return vnodes;
}
export function markComponentAsDirty(vm: VM) {
if (process.env.NODE_ENV !== 'production') {
const vmBeingRendered = getVMBeingRendered();
assert.isFalse(
vm.isDirty,
`markComponentAsDirty() for ${vm} should not be called when the component is already dirty.`
);
assert.isFalse(
isInvokingRender,
`markComponentAsDirty() for ${vm} cannot be called during rendering of ${vmBeingRendered}.`
);
assert.isFalse(
isUpdatingTemplate,
`markComponentAsDirty() for ${vm} cannot be called while updating template of ${vmBeingRendered}.`
);
}
vm.isDirty = true;
}
const cmpEventListenerMap: WeakMap<EventListener, EventListener> = new WeakMap();
export function getWrappedComponentsListener(vm: VM, listener: EventListener): EventListener {
if (!isFunction(listener)) {
throw new TypeError('Expected an EventListener but received ' + typeof listener); // avoiding problems with non-valid listeners
}
let wrappedListener = cmpEventListenerMap.get(listener);
if (isUndefined(wrappedListener)) {
wrappedListener = function (event: Event) {
invokeEventListener(vm, listener, undefined, event);
};
cmpEventListenerMap.set(listener, wrappedListener);
}
return wrappedListener;
}