@lwc/engine-core 6.3.3 → 6.4.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/dist/framework/api.d.ts +3 -3
- package/dist/framework/main.d.ts +1 -0
- package/dist/framework/modules/attrs.d.ts +2 -2
- package/dist/framework/modules/computed-class-attr.d.ts +3 -2
- package/dist/framework/modules/computed-style-attr.d.ts +3 -2
- package/dist/framework/modules/events.d.ts +2 -2
- package/dist/framework/modules/refs.d.ts +2 -2
- package/dist/framework/modules/static-parts.d.ts +17 -3
- package/dist/framework/modules/text.d.ts +5 -0
- package/dist/framework/template.d.ts +2 -2
- package/dist/framework/utils.d.ts +0 -20
- package/dist/framework/vm.d.ts +8 -6
- package/dist/framework/vnodes.d.ts +19 -1
- package/dist/index.cjs.js +353 -110
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.js +354 -111
- package/dist/index.js.map +1 -1
- package/dist/libs/signal-tracker/index.d.ts +1 -1
- package/package.json +5 -5
package/dist/index.cjs.js
CHANGED
|
@@ -503,29 +503,6 @@ function assertNotProd() {
|
|
|
503
503
|
throw new ReferenceError();
|
|
504
504
|
}
|
|
505
505
|
}
|
|
506
|
-
// Temporary fix for when the LWC v5 compiler is used in conjunction with a v6+ engine
|
|
507
|
-
// The old compiler format used the "slot" attribute in the `data` bag, whereas the new
|
|
508
|
-
// format uses the special `slotAssignment` key.
|
|
509
|
-
// This should be removed when the LWC v5 compiler is not used anywhere where it could be mismatched
|
|
510
|
-
// with another LWC engine version.
|
|
511
|
-
// TODO [#3974]: remove temporary logic to support v5 compiler + v6+ engine
|
|
512
|
-
function applyTemporaryCompilerV5SlotFix(data) {
|
|
513
|
-
if (lwcRuntimeFlags.DISABLE_TEMPORARY_V5_COMPILER_SUPPORT) {
|
|
514
|
-
return data;
|
|
515
|
-
}
|
|
516
|
-
const { attrs } = data;
|
|
517
|
-
if (!shared.isUndefined(attrs)) {
|
|
518
|
-
const { slot } = attrs;
|
|
519
|
-
if (!shared.isUndefined(slot) && !shared.isNull(slot)) {
|
|
520
|
-
return {
|
|
521
|
-
...data,
|
|
522
|
-
attrs: cloneAndOmitKey(attrs, 'slot'),
|
|
523
|
-
slotAssignment: String(slot),
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
return data;
|
|
528
|
-
}
|
|
529
506
|
function shouldBeFormAssociated(Ctor) {
|
|
530
507
|
const ctorFormAssociated = Boolean(Ctor.formAssociated);
|
|
531
508
|
const apiVersion = getComponentAPIVersion(Ctor);
|
|
@@ -3672,6 +3649,12 @@ function isVScopedSlotFragment(vnode) {
|
|
|
3672
3649
|
function isVStatic(vnode) {
|
|
3673
3650
|
return vnode.type === 4 /* VNodeType.Static */;
|
|
3674
3651
|
}
|
|
3652
|
+
function isVStaticPartElement(vnode) {
|
|
3653
|
+
return vnode.type === 1 /* VStaticPartType.Element */;
|
|
3654
|
+
}
|
|
3655
|
+
function isVStaticPartText(vnode) {
|
|
3656
|
+
return vnode.type === 0 /* VStaticPartType.Text */;
|
|
3657
|
+
}
|
|
3675
3658
|
|
|
3676
3659
|
/*
|
|
3677
3660
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
@@ -3681,7 +3664,8 @@ function isVStatic(vnode) {
|
|
|
3681
3664
|
*/
|
|
3682
3665
|
const ColonCharCode = 58;
|
|
3683
3666
|
function patchAttributes(oldVnode, vnode, renderer) {
|
|
3684
|
-
const {
|
|
3667
|
+
const { data, elm } = vnode;
|
|
3668
|
+
const { attrs } = data;
|
|
3685
3669
|
if (shared.isUndefined(attrs)) {
|
|
3686
3670
|
return;
|
|
3687
3671
|
}
|
|
@@ -3690,7 +3674,8 @@ function patchAttributes(oldVnode, vnode, renderer) {
|
|
|
3690
3674
|
if (oldAttrs === attrs) {
|
|
3691
3675
|
return;
|
|
3692
3676
|
}
|
|
3693
|
-
|
|
3677
|
+
// Note VStaticPartData does not contain the external property so it will always default to false.
|
|
3678
|
+
const external = 'external' in data ? data.external : false;
|
|
3694
3679
|
const { setAttribute, removeAttribute, setProperty } = renderer;
|
|
3695
3680
|
for (const key in attrs) {
|
|
3696
3681
|
const cur = attrs[key];
|
|
@@ -3794,8 +3779,7 @@ function patchProps(oldVnode, vnode, renderer) {
|
|
|
3794
3779
|
*/
|
|
3795
3780
|
const classNameToClassMap = shared.create(null);
|
|
3796
3781
|
function getMapFromClassName(className) {
|
|
3797
|
-
|
|
3798
|
-
if (className == null) {
|
|
3782
|
+
if (shared.isUndefined(className) || shared.isNull(className) || className === '') {
|
|
3799
3783
|
return EmptyObject;
|
|
3800
3784
|
}
|
|
3801
3785
|
// computed class names must be string
|
|
@@ -3834,10 +3818,16 @@ function patchClassAttribute(oldVnode, vnode, renderer) {
|
|
|
3834
3818
|
if (oldClass === newClass) {
|
|
3835
3819
|
return;
|
|
3836
3820
|
}
|
|
3837
|
-
const { getClassList } = renderer;
|
|
3838
|
-
const classList = getClassList(elm);
|
|
3839
3821
|
const newClassMap = getMapFromClassName(newClass);
|
|
3840
3822
|
const oldClassMap = getMapFromClassName(oldClass);
|
|
3823
|
+
if (oldClassMap === newClassMap) {
|
|
3824
|
+
// These objects are cached by className string (`classNameToClassMap`), so we can only get here if there is
|
|
3825
|
+
// a key collision due to types, e.g. oldClass is `undefined` and newClass is `""` (empty string), or oldClass
|
|
3826
|
+
// is `1` (number) and newClass is `"1"` (string).
|
|
3827
|
+
return;
|
|
3828
|
+
}
|
|
3829
|
+
const { getClassList } = renderer;
|
|
3830
|
+
const classList = getClassList(elm);
|
|
3841
3831
|
let name;
|
|
3842
3832
|
for (name in oldClassMap) {
|
|
3843
3833
|
// remove only if it is not in the new class collection and it is not set from within the instance
|
|
@@ -3859,8 +3849,13 @@ function patchClassAttribute(oldVnode, vnode, renderer) {
|
|
|
3859
3849
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3860
3850
|
*/
|
|
3861
3851
|
// The style property is a string when defined via an expression in the template.
|
|
3862
|
-
function patchStyleAttribute(oldVnode, vnode, renderer) {
|
|
3852
|
+
function patchStyleAttribute(oldVnode, vnode, renderer, owner) {
|
|
3863
3853
|
const { elm, data: { style: newStyle }, } = vnode;
|
|
3854
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
3855
|
+
if (!shared.isNull(newStyle) && !shared.isUndefined(newStyle) && !shared.isString(newStyle)) {
|
|
3856
|
+
logError(`Invalid 'style' attribute passed to <${elm.tagName.toLowerCase()}> is ignored. This attribute must be a string value.`, owner);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3864
3859
|
const oldStyle = shared.isNull(oldVnode) ? undefined : oldVnode.data.style;
|
|
3865
3860
|
if (oldStyle === newStyle) {
|
|
3866
3861
|
return;
|
|
@@ -3881,8 +3876,8 @@ function patchStyleAttribute(oldVnode, vnode, renderer) {
|
|
|
3881
3876
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3882
3877
|
*/
|
|
3883
3878
|
function applyEventListeners(vnode, renderer) {
|
|
3884
|
-
const { elm } = vnode;
|
|
3885
|
-
const on =
|
|
3879
|
+
const { elm, data } = vnode;
|
|
3880
|
+
const { on } = data;
|
|
3886
3881
|
if (shared.isUndefined(on)) {
|
|
3887
3882
|
return;
|
|
3888
3883
|
}
|
|
@@ -3959,12 +3954,47 @@ function applyRefs(vnode, owner) {
|
|
|
3959
3954
|
refVNodes[ref] = vnode;
|
|
3960
3955
|
}
|
|
3961
3956
|
|
|
3957
|
+
/*
|
|
3958
|
+
* Copyright (c) 2024, salesforce.com, inc.
|
|
3959
|
+
* All rights reserved.
|
|
3960
|
+
* SPDX-License-Identifier: MIT
|
|
3961
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3962
|
+
*/
|
|
3963
|
+
function patchTextVNode(n1, n2, renderer) {
|
|
3964
|
+
n2.elm = n1.elm;
|
|
3965
|
+
if (n2.text !== n1.text) {
|
|
3966
|
+
updateTextContent$1(n2, renderer);
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
function patchTextVStaticPart(n1, n2, renderer) {
|
|
3970
|
+
if (shared.isNull(n1) || n2.text !== n1.text) {
|
|
3971
|
+
updateTextContent$1(n2, renderer);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
function updateTextContent$1(vnode, renderer) {
|
|
3975
|
+
const { elm, text } = vnode;
|
|
3976
|
+
const { setText } = renderer;
|
|
3977
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
3978
|
+
unlockDomMutation();
|
|
3979
|
+
}
|
|
3980
|
+
setText(elm, text);
|
|
3981
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
3982
|
+
lockDomMutation();
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3962
3986
|
/*
|
|
3963
3987
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
3964
3988
|
* All rights reserved.
|
|
3965
3989
|
* SPDX-License-Identifier: MIT
|
|
3966
3990
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3967
3991
|
*/
|
|
3992
|
+
/**
|
|
3993
|
+
* Given an array of static parts, mounts the DOM element to the part based on the staticPartId
|
|
3994
|
+
* @param root the root element
|
|
3995
|
+
* @param parts an array of VStaticParts
|
|
3996
|
+
* @param renderer the renderer to use
|
|
3997
|
+
*/
|
|
3968
3998
|
function traverseAndSetElements(root, parts, renderer) {
|
|
3969
3999
|
const numParts = parts.length;
|
|
3970
4000
|
// Optimization given that, in most cases, there will be one part, and it's just the root
|
|
@@ -4030,18 +4060,30 @@ function mountStaticParts(root, vnode, renderer) {
|
|
|
4030
4060
|
traverseAndSetElements(root, parts, renderer);
|
|
4031
4061
|
// Currently only event listeners and refs are supported for static vnodes
|
|
4032
4062
|
for (const part of parts) {
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4063
|
+
if (isVStaticPartElement(part)) {
|
|
4064
|
+
// Event listeners only need to be applied once when mounting
|
|
4065
|
+
applyEventListeners(part, renderer);
|
|
4066
|
+
// Refs must be updated after every render due to refVNodes getting reset before every render
|
|
4067
|
+
applyRefs(part, owner);
|
|
4068
|
+
patchAttributes(null, part, renderer);
|
|
4069
|
+
patchClassAttribute(null, part, renderer);
|
|
4070
|
+
patchStyleAttribute(null, part, renderer, owner);
|
|
4071
|
+
}
|
|
4072
|
+
else {
|
|
4073
|
+
if (process.env.NODE_ENV !== 'production' && !isVStaticPartText(part)) {
|
|
4074
|
+
throw new Error(`LWC internal error, encountered unknown static part type: ${part.type}`);
|
|
4075
|
+
}
|
|
4076
|
+
patchTextVStaticPart(null, part, renderer);
|
|
4077
|
+
}
|
|
4037
4078
|
}
|
|
4038
4079
|
}
|
|
4039
4080
|
/**
|
|
4040
|
-
*
|
|
4081
|
+
* Updates the static elements based on the content of the VStaticParts
|
|
4041
4082
|
* @param n1 the previous VStatic vnode
|
|
4042
4083
|
* @param n2 the current VStatic vnode
|
|
4084
|
+
* @param renderer the renderer to use
|
|
4043
4085
|
*/
|
|
4044
|
-
function patchStaticParts(n1, n2) {
|
|
4086
|
+
function patchStaticParts(n1, n2, renderer) {
|
|
4045
4087
|
// On the server, we don't support ref (because it relies on renderedCallback), nor do we
|
|
4046
4088
|
// support event listeners (no interactivity), so traversing parts makes no sense
|
|
4047
4089
|
if (!process.env.IS_BROWSER) {
|
|
@@ -4056,12 +4098,49 @@ function patchStaticParts(n1, n2) {
|
|
|
4056
4098
|
shared.assert.isTrue(currParts.length === prevParts?.length, 'Expected static parts to be the same for the same element. This is an error with the LWC framework itself.');
|
|
4057
4099
|
}
|
|
4058
4100
|
for (let i = 0; i < currParts.length; i++) {
|
|
4101
|
+
const prevPart = prevParts[i];
|
|
4059
4102
|
const part = currParts[i];
|
|
4060
4103
|
// Patch only occurs if the vnode is newly generated, which means the part.elm is always undefined
|
|
4061
4104
|
// Since the vnode and elements are the same we can safely assume that prevParts[i].elm is defined.
|
|
4062
|
-
part.elm =
|
|
4063
|
-
|
|
4064
|
-
|
|
4105
|
+
part.elm = prevPart.elm;
|
|
4106
|
+
if (process.env.NODE_ENV !== 'production' && prevPart.type !== part.type) {
|
|
4107
|
+
throw new Error(`LWC internal error, static part types do not match. Previous type was ${prevPart.type} and current type is ${part.type}`);
|
|
4108
|
+
}
|
|
4109
|
+
if (isVStaticPartElement(part)) {
|
|
4110
|
+
// Refs must be updated after every render due to refVNodes getting reset before every render
|
|
4111
|
+
applyRefs(part, currPartsOwner);
|
|
4112
|
+
patchAttributes(prevPart, part, renderer);
|
|
4113
|
+
patchClassAttribute(prevPart, part, renderer);
|
|
4114
|
+
patchStyleAttribute(prevPart, part, renderer, currPartsOwner);
|
|
4115
|
+
}
|
|
4116
|
+
else {
|
|
4117
|
+
patchTextVStaticPart(null, part, renderer);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
/**
|
|
4122
|
+
* Mounts the hydration specific attributes
|
|
4123
|
+
* @param vnode the parent VStatic node
|
|
4124
|
+
* @param renderer the renderer to use
|
|
4125
|
+
*/
|
|
4126
|
+
function hydrateStaticParts(vnode, renderer) {
|
|
4127
|
+
if (!process.env.IS_BROWSER) {
|
|
4128
|
+
return;
|
|
4129
|
+
}
|
|
4130
|
+
const { parts, owner } = vnode;
|
|
4131
|
+
if (shared.isUndefined(parts)) {
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
// Note, hydration doesn't patch attributes because hydration validation occurs before this routine
|
|
4135
|
+
// which guarantees that the elements are the same.
|
|
4136
|
+
// We only need to apply the parts for things that cannot be done on the server.
|
|
4137
|
+
for (const part of parts) {
|
|
4138
|
+
if (isVStaticPartElement(part)) {
|
|
4139
|
+
// Event listeners only need to be applied once when mounting
|
|
4140
|
+
applyEventListeners(part, renderer);
|
|
4141
|
+
// Refs must be updated after every render due to refVNodes getting reset before every render
|
|
4142
|
+
applyRefs(part, owner);
|
|
4143
|
+
}
|
|
4065
4144
|
}
|
|
4066
4145
|
}
|
|
4067
4146
|
|
|
@@ -4099,7 +4178,7 @@ function patch(n1, n2, parent, renderer) {
|
|
|
4099
4178
|
switch (n2.type) {
|
|
4100
4179
|
case 0 /* VNodeType.Text */:
|
|
4101
4180
|
// VText has no special capability, fallback to the owner's renderer
|
|
4102
|
-
|
|
4181
|
+
patchTextVNode(n1, n2, renderer);
|
|
4103
4182
|
break;
|
|
4104
4183
|
case 1 /* VNodeType.Comment */:
|
|
4105
4184
|
// VComment has no special capability, fallback to the owner's renderer
|
|
@@ -4146,12 +4225,6 @@ function mount(node, parent, renderer, anchor) {
|
|
|
4146
4225
|
break;
|
|
4147
4226
|
}
|
|
4148
4227
|
}
|
|
4149
|
-
function patchText(n1, n2, renderer) {
|
|
4150
|
-
n2.elm = n1.elm;
|
|
4151
|
-
if (n2.text !== n1.text) {
|
|
4152
|
-
updateTextContent(n2, renderer);
|
|
4153
|
-
}
|
|
4154
|
-
}
|
|
4155
4228
|
function mountText(vnode, parent, anchor, renderer) {
|
|
4156
4229
|
const { owner } = vnode;
|
|
4157
4230
|
const { createText } = renderer;
|
|
@@ -4164,7 +4237,7 @@ function patchComment(n1, n2, renderer) {
|
|
|
4164
4237
|
// FIXME: Comment nodes should be static, we shouldn't need to diff them together. However
|
|
4165
4238
|
// it is the case today.
|
|
4166
4239
|
if (n2.text !== n1.text) {
|
|
4167
|
-
updateTextContent(n2, renderer);
|
|
4240
|
+
updateTextContent$1(n2, renderer);
|
|
4168
4241
|
}
|
|
4169
4242
|
}
|
|
4170
4243
|
function mountComment(vnode, parent, anchor, renderer) {
|
|
@@ -4208,7 +4281,7 @@ function patchStatic(n1, n2, renderer) {
|
|
|
4208
4281
|
// slotAssignments can only apply to the top level element, never to a static part.
|
|
4209
4282
|
patchSlotAssignment(n1, n2, renderer);
|
|
4210
4283
|
// The `refs` object is blown away in every re-render, so we always need to re-apply them
|
|
4211
|
-
patchStaticParts(n1, n2);
|
|
4284
|
+
patchStaticParts(n1, n2, renderer);
|
|
4212
4285
|
}
|
|
4213
4286
|
function patchElement(n1, n2, renderer) {
|
|
4214
4287
|
const elm = (n2.elm = n1.elm);
|
|
@@ -4219,19 +4292,20 @@ function mountStatic(vnode, parent, anchor, renderer) {
|
|
|
4219
4292
|
const { owner } = vnode;
|
|
4220
4293
|
const { cloneNode, isSyntheticShadowDefined } = renderer;
|
|
4221
4294
|
const elm = (vnode.elm = cloneNode(vnode.fragment, true));
|
|
4295
|
+
// Define the root node shadow resolver
|
|
4222
4296
|
linkNodeToShadow(elm, owner, renderer);
|
|
4223
4297
|
applyElementRestrictions(elm, vnode);
|
|
4224
|
-
// Marks this node as Static to propagate the shadow resolver. must happen after elm is assigned to the proper shadow
|
|
4225
4298
|
const { renderMode, shadowMode } = owner;
|
|
4226
4299
|
if (isSyntheticShadowDefined) {
|
|
4300
|
+
// Marks this node as Static to propagate the shadow resolver. must happen after elm is assigned to the proper shadow
|
|
4227
4301
|
if (shadowMode === 1 /* ShadowMode.Synthetic */ || renderMode === 0 /* RenderMode.Light */) {
|
|
4228
4302
|
elm[shared.KEY__SHADOW_STATIC] = true;
|
|
4229
4303
|
}
|
|
4230
4304
|
}
|
|
4231
4305
|
// slotAssignments can only apply to the top level element, never to a static part.
|
|
4232
4306
|
patchSlotAssignment(null, vnode, renderer);
|
|
4233
|
-
insertNode(elm, parent, anchor, renderer);
|
|
4234
4307
|
mountStaticParts(elm, vnode, renderer);
|
|
4308
|
+
insertNode(elm, parent, anchor, renderer);
|
|
4235
4309
|
}
|
|
4236
4310
|
function mountCustomElement(vnode, parent, anchor, renderer) {
|
|
4237
4311
|
const { sel, owner, ctor } = vnode;
|
|
@@ -4396,17 +4470,6 @@ function linkNodeToShadow(elm, owner, renderer) {
|
|
|
4396
4470
|
}
|
|
4397
4471
|
}
|
|
4398
4472
|
}
|
|
4399
|
-
function updateTextContent(vnode, renderer) {
|
|
4400
|
-
const { elm, text } = vnode;
|
|
4401
|
-
const { setText } = renderer;
|
|
4402
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
4403
|
-
unlockDomMutation();
|
|
4404
|
-
}
|
|
4405
|
-
setText(elm, text);
|
|
4406
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
4407
|
-
lockDomMutation();
|
|
4408
|
-
}
|
|
4409
|
-
}
|
|
4410
4473
|
function insertFragmentOrNode(vnode, parent, anchor, renderer) {
|
|
4411
4474
|
if (process.env.NODE_ENV !== 'production') {
|
|
4412
4475
|
unlockDomMutation();
|
|
@@ -4451,15 +4514,16 @@ function patchElementPropsAndAttrsAndRefs$1(oldVnode, vnode, renderer) {
|
|
|
4451
4514
|
applyStaticClassAttribute(vnode, renderer);
|
|
4452
4515
|
applyStaticStyleAttribute(vnode, renderer);
|
|
4453
4516
|
}
|
|
4517
|
+
const { owner } = vnode;
|
|
4454
4518
|
// Attrs need to be applied to element before props IE11 will wipe out value on radio inputs if
|
|
4455
4519
|
// value is set before type=radio.
|
|
4456
4520
|
patchClassAttribute(oldVnode, vnode, renderer);
|
|
4457
|
-
patchStyleAttribute(oldVnode, vnode, renderer);
|
|
4521
|
+
patchStyleAttribute(oldVnode, vnode, renderer, owner);
|
|
4458
4522
|
patchAttributes(oldVnode, vnode, renderer);
|
|
4459
4523
|
patchProps(oldVnode, vnode, renderer);
|
|
4460
4524
|
patchSlotAssignment(oldVnode, vnode, renderer);
|
|
4461
4525
|
// The `refs` object is blown away in every re-render, so we always need to re-apply them
|
|
4462
|
-
applyRefs(vnode,
|
|
4526
|
+
applyRefs(vnode, owner);
|
|
4463
4527
|
}
|
|
4464
4528
|
function applyStyleScoping(elm, owner, renderer) {
|
|
4465
4529
|
const { getClassList } = renderer;
|
|
@@ -4861,10 +4925,14 @@ function addVNodeToChildLWC(vnode) {
|
|
|
4861
4925
|
shared.ArrayPush.call(getVMBeingRendered().velements, vnode);
|
|
4862
4926
|
}
|
|
4863
4927
|
// [s]tatic [p]art
|
|
4864
|
-
function sp(partId, data) {
|
|
4928
|
+
function sp(partId, data, text) {
|
|
4929
|
+
// Static part will always have either text or data, it's guaranteed by the compiler.
|
|
4930
|
+
const type = shared.isNull(text) ? 1 /* VStaticPartType.Element */ : 0 /* VStaticPartType.Text */;
|
|
4865
4931
|
return {
|
|
4932
|
+
type,
|
|
4866
4933
|
partId,
|
|
4867
4934
|
data,
|
|
4935
|
+
text,
|
|
4868
4936
|
elm: undefined, // elm is defined later
|
|
4869
4937
|
};
|
|
4870
4938
|
}
|
|
@@ -4881,8 +4949,9 @@ function ssf(slotName, factory) {
|
|
|
4881
4949
|
};
|
|
4882
4950
|
}
|
|
4883
4951
|
// [st]atic node
|
|
4884
|
-
function st(
|
|
4952
|
+
function st(fragmentFactory, key, parts) {
|
|
4885
4953
|
const owner = getVMBeingRendered();
|
|
4954
|
+
const fragment = fragmentFactory(parts);
|
|
4886
4955
|
const vnode = {
|
|
4887
4956
|
type: 4 /* VNodeType.Static */,
|
|
4888
4957
|
sel: undefined,
|
|
@@ -4924,9 +4993,6 @@ function h(sel, data, children = EmptyArray) {
|
|
|
4924
4993
|
// checking reserved internal data properties
|
|
4925
4994
|
shared.assert.isFalse(data.className && data.classMap, `vnode.data.className and vnode.data.classMap ambiguous declaration.`);
|
|
4926
4995
|
shared.assert.isFalse(data.styleDecls && data.style, `vnode.data.styleDecls and vnode.data.style ambiguous declaration.`);
|
|
4927
|
-
if (data.style && !shared.isString(data.style)) {
|
|
4928
|
-
logError(`Invalid 'style' attribute passed to <${sel}> is ignored. This attribute must be a string value.`, vmBeingRendered);
|
|
4929
|
-
}
|
|
4930
4996
|
shared.forEach.call(children, (childVnode) => {
|
|
4931
4997
|
if (childVnode != null) {
|
|
4932
4998
|
shared.assert.isTrue('type' in childVnode &&
|
|
@@ -4936,8 +5002,6 @@ function h(sel, data, children = EmptyArray) {
|
|
|
4936
5002
|
}
|
|
4937
5003
|
});
|
|
4938
5004
|
}
|
|
4939
|
-
// TODO [#3974]: remove temporary logic to support v5 compiler + v6+ engine
|
|
4940
|
-
data = applyTemporaryCompilerV5SlotFix(data);
|
|
4941
5005
|
const { key, slotAssignment } = data;
|
|
4942
5006
|
const vnode = {
|
|
4943
5007
|
type: 2 /* VNodeType.Element */,
|
|
@@ -4974,8 +5038,6 @@ function s(slotName, data, children, slotset) {
|
|
|
4974
5038
|
}
|
|
4975
5039
|
const vmBeingRendered = getVMBeingRendered();
|
|
4976
5040
|
const { renderMode, apiVersion } = vmBeingRendered;
|
|
4977
|
-
// TODO [#3974]: remove temporary logic to support v5 compiler + v6+ engine
|
|
4978
|
-
data = applyTemporaryCompilerV5SlotFix(data);
|
|
4979
5041
|
if (!shared.isUndefined(slotset) &&
|
|
4980
5042
|
!shared.isUndefined(slotset.slotAssignments) &&
|
|
4981
5043
|
!shared.isUndefined(slotset.slotAssignments[slotName]) &&
|
|
@@ -5079,8 +5141,6 @@ function c(sel, Ctor, data, children = EmptyArray) {
|
|
|
5079
5141
|
});
|
|
5080
5142
|
}
|
|
5081
5143
|
}
|
|
5082
|
-
// TODO [#3974]: remove temporary logic to support v5 compiler + v6+ engine
|
|
5083
|
-
data = applyTemporaryCompilerV5SlotFix(data);
|
|
5084
5144
|
const { key, slotAssignment } = data;
|
|
5085
5145
|
let elm, aChildren, vm;
|
|
5086
5146
|
const vnode = {
|
|
@@ -5142,16 +5202,19 @@ function i(iterable, factory) {
|
|
|
5142
5202
|
if (process.env.NODE_ENV !== 'production') {
|
|
5143
5203
|
const vnodes = shared.isArray(vnode) ? vnode : [vnode];
|
|
5144
5204
|
shared.forEach.call(vnodes, (childVnode) => {
|
|
5145
|
-
|
|
5205
|
+
// Check that the child vnode is either an element or VStatic
|
|
5206
|
+
if (!shared.isNull(childVnode) && (isVBaseElement(childVnode) || isVStatic(childVnode))) {
|
|
5146
5207
|
const { key } = childVnode;
|
|
5208
|
+
// In @lwc/engine-server the fragment doesn't have a tagName, default to the VM's tagName.
|
|
5209
|
+
const { tagName } = vmBeingRendered;
|
|
5147
5210
|
if (shared.isString(key) || shared.isNumber(key)) {
|
|
5148
5211
|
if (keyMap[key] === 1 && shared.isUndefined(iterationError)) {
|
|
5149
|
-
iterationError = `Duplicated "key" attribute value
|
|
5212
|
+
iterationError = `Duplicated "key" attribute value in "<${tagName}>" for item number ${j}. A key with value "${key}" appears more than once in the iteration. Key values must be unique numbers or strings.`;
|
|
5150
5213
|
}
|
|
5151
5214
|
keyMap[key] = 1;
|
|
5152
5215
|
}
|
|
5153
5216
|
else if (shared.isUndefined(iterationError)) {
|
|
5154
|
-
iterationError = `Invalid "key" attribute value in "<${
|
|
5217
|
+
iterationError = `Invalid "key" attribute value in "<${tagName}>" for item number ${j}. Set a unique "key" value on all iterated child elements.`;
|
|
5155
5218
|
}
|
|
5156
5219
|
}
|
|
5157
5220
|
});
|
|
@@ -5550,6 +5613,111 @@ function validateLightDomTemplate(template, vm) {
|
|
|
5550
5613
|
}
|
|
5551
5614
|
}
|
|
5552
5615
|
}
|
|
5616
|
+
const browserExpressionSerializer = (partToken, classAttrToken) => {
|
|
5617
|
+
// This will insert the scoped style token as a static class attribute in the fragment
|
|
5618
|
+
// bypassing the need to call applyStyleScoping when mounting static parts.
|
|
5619
|
+
const type = shared.StringCharAt.call(partToken, 0);
|
|
5620
|
+
switch (type) {
|
|
5621
|
+
case "c" /* STATIC_PART_TOKEN_ID.CLASS */:
|
|
5622
|
+
return classAttrToken;
|
|
5623
|
+
case "t" /* STATIC_PART_TOKEN_ID.TEXT */:
|
|
5624
|
+
// Using a single space here gives us a single empty text node
|
|
5625
|
+
return ' ';
|
|
5626
|
+
default:
|
|
5627
|
+
return '';
|
|
5628
|
+
}
|
|
5629
|
+
};
|
|
5630
|
+
const serializerNoop = () => {
|
|
5631
|
+
throw new Error('LWC internal error, attempted to serialize partToken without static parts');
|
|
5632
|
+
};
|
|
5633
|
+
// This function serializes the expressions generated by static content optimization.
|
|
5634
|
+
// Currently this is only needed for SSR.
|
|
5635
|
+
// TODO [#4078]: Split the implementation between @lwc/engine-dom and @lwc/engine-server
|
|
5636
|
+
function buildSerializeExpressionFn(parts) {
|
|
5637
|
+
if (process.env.IS_BROWSER) {
|
|
5638
|
+
return browserExpressionSerializer;
|
|
5639
|
+
}
|
|
5640
|
+
if (shared.isUndefined(parts)) {
|
|
5641
|
+
// Technically this should not be reachable, if there are no parts there should be no partTokens
|
|
5642
|
+
// and this function should never be invoked.
|
|
5643
|
+
return serializerNoop;
|
|
5644
|
+
}
|
|
5645
|
+
const partIdsToParts = new Map();
|
|
5646
|
+
for (const staticPart of parts) {
|
|
5647
|
+
partIdsToParts.set(`${staticPart.partId}`, staticPart);
|
|
5648
|
+
}
|
|
5649
|
+
const parsePartToken = (partToken) => {
|
|
5650
|
+
// The partTokens are split into 3 section:
|
|
5651
|
+
// 1. The first character represents the expression type (attribute, class, style, or text).
|
|
5652
|
+
// 2. For attributes, the characters from index 1 to the first occurrence of a ':' is the partId.
|
|
5653
|
+
// 3. Everything after the first ':' represents the attribute name.
|
|
5654
|
+
// 4. For non-attributes everything from index 1 to the string length is the partId.
|
|
5655
|
+
// Ex, attribute: a0:data-name, a = an attribute, 0 = partId, data-name = attribute name.
|
|
5656
|
+
// Ex, style: s0, s = a style attribute, 0 = partId.
|
|
5657
|
+
const type = shared.StringCharAt.call(partToken, 0);
|
|
5658
|
+
let delimiterIndex = partToken.length;
|
|
5659
|
+
let attrName = '';
|
|
5660
|
+
if (type === "a" /* STATIC_PART_TOKEN_ID.ATTRIBUTE */) {
|
|
5661
|
+
delimiterIndex = partToken.indexOf(':');
|
|
5662
|
+
// Only VStaticPartData.attrs have an attribute name
|
|
5663
|
+
attrName = partToken.substring(delimiterIndex + 1);
|
|
5664
|
+
}
|
|
5665
|
+
const partId = partToken.substring(1, delimiterIndex);
|
|
5666
|
+
const part = partIdsToParts.get(partId) ?? EmptyObject;
|
|
5667
|
+
return { type, part, attrName };
|
|
5668
|
+
};
|
|
5669
|
+
return (partToken, classToken) => {
|
|
5670
|
+
const { type, part, attrName } = parsePartToken(partToken);
|
|
5671
|
+
switch (type) {
|
|
5672
|
+
case "a" /* STATIC_PART_TOKEN_ID.ATTRIBUTE */:
|
|
5673
|
+
return serializeAttribute(part, attrName);
|
|
5674
|
+
case "c" /* STATIC_PART_TOKEN_ID.CLASS */: // class
|
|
5675
|
+
return serializeClassAttribute(part, classToken);
|
|
5676
|
+
case "s" /* STATIC_PART_TOKEN_ID.STYLE */: // style
|
|
5677
|
+
return serializeStyleAttribute(part);
|
|
5678
|
+
case "t" /* STATIC_PART_TOKEN_ID.TEXT */: // text
|
|
5679
|
+
return serializeTextContent(part);
|
|
5680
|
+
default:
|
|
5681
|
+
// This should not be reachable
|
|
5682
|
+
throw new Error(`LWC internal error, unrecognized part token during serialization ${partToken}`);
|
|
5683
|
+
}
|
|
5684
|
+
};
|
|
5685
|
+
}
|
|
5686
|
+
function serializeTextContent(part) {
|
|
5687
|
+
const { text } = part;
|
|
5688
|
+
if (text === '') {
|
|
5689
|
+
return '\u200D'; // Special serialization for empty text nodes
|
|
5690
|
+
}
|
|
5691
|
+
// Note the serialization logic doesn't need to validate against the style tag as in serializeTextContent
|
|
5692
|
+
// because style tags are always inserted through the engine.
|
|
5693
|
+
// User input of style tags are blocked, furthermore, all dynamic text is escaped at this point.
|
|
5694
|
+
return shared.htmlEscape(text);
|
|
5695
|
+
}
|
|
5696
|
+
function serializeStyleAttribute(part) {
|
|
5697
|
+
const { data: { style }, } = part;
|
|
5698
|
+
// This is designed to mirror logic patchStyleAttribute
|
|
5699
|
+
return shared.isString(style) && style.length ? ` style="${shared.htmlEscape(style, true)}"` : '';
|
|
5700
|
+
}
|
|
5701
|
+
function serializeAttribute(part, name) {
|
|
5702
|
+
const { data: { attrs = {} }, } = part;
|
|
5703
|
+
const rawValue = attrs[name];
|
|
5704
|
+
let value = '';
|
|
5705
|
+
// The undefined and null checks here are designed to match patchAttributes routine.
|
|
5706
|
+
if (!shared.isUndefined(rawValue) && !shared.isNull(rawValue)) {
|
|
5707
|
+
const stringifiedValue = String(rawValue);
|
|
5708
|
+
value = stringifiedValue.length
|
|
5709
|
+
? ` ${name}="${shared.htmlEscape(stringifiedValue, true)}"`
|
|
5710
|
+
: ` ${name}`;
|
|
5711
|
+
}
|
|
5712
|
+
return value;
|
|
5713
|
+
}
|
|
5714
|
+
function serializeClassAttribute(part, classToken) {
|
|
5715
|
+
const classMap = getMapFromClassName(part.data?.className);
|
|
5716
|
+
// Trim the leading and trailing whitespace here because classToken contains a leading space and
|
|
5717
|
+
// there will be a trailing space if classMap is empty.
|
|
5718
|
+
const computedClassName = `${classToken} ${shared.keys(classMap).join(' ')}`.trim();
|
|
5719
|
+
return computedClassName.length ? ` class="${shared.htmlEscape(computedClassName, true)}"` : '';
|
|
5720
|
+
}
|
|
5553
5721
|
// This should be a no-op outside of LWC's Karma tests, where it's not needed
|
|
5554
5722
|
let registerFragmentCache = shared.noop;
|
|
5555
5723
|
// Only used in LWC's Karma tests
|
|
@@ -5571,7 +5739,7 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5571
5739
|
return (strings, ...keys) => {
|
|
5572
5740
|
const cache = shared.create(null);
|
|
5573
5741
|
registerFragmentCache(cache);
|
|
5574
|
-
return function () {
|
|
5742
|
+
return function (parts) {
|
|
5575
5743
|
const { context: { hasScopedStyles, stylesheetToken, legacyStylesheetToken }, shadowMode, renderer, } = getVMBeingRendered();
|
|
5576
5744
|
const hasStyleToken = !shared.isUndefined(stylesheetToken);
|
|
5577
5745
|
const isSyntheticShadow = shadowMode === 1 /* ShadowMode.Synthetic */;
|
|
@@ -5583,14 +5751,27 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5583
5751
|
if (hasStyleToken && isSyntheticShadow) {
|
|
5584
5752
|
cacheKey |= 2 /* FragmentCache.SHADOW_MODE_SYNTHETIC */;
|
|
5585
5753
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5754
|
+
// Cache is only here to prevent calling innerHTML multiple times which doesn't happen on the server.
|
|
5755
|
+
if (process.env.IS_BROWSER) {
|
|
5756
|
+
// Disable this on the server to prevent cache poisoning when expressions are used.
|
|
5757
|
+
const cached = cache[cacheKey];
|
|
5758
|
+
if (!shared.isUndefined(cached)) {
|
|
5759
|
+
return cached;
|
|
5760
|
+
}
|
|
5588
5761
|
}
|
|
5589
5762
|
// If legacy stylesheet tokens are required, then add them to the rendered string
|
|
5590
5763
|
const stylesheetTokenToRender = stylesheetToken + (hasLegacyToken ? ` ${legacyStylesheetToken}` : '');
|
|
5591
5764
|
const classToken = hasScopedStyles && hasStyleToken ? ' ' + stylesheetTokenToRender : '';
|
|
5592
5765
|
const classAttrToken = hasScopedStyles && hasStyleToken ? ` class="${stylesheetTokenToRender}"` : '';
|
|
5593
5766
|
const attrToken = hasStyleToken && isSyntheticShadow ? ' ' + stylesheetTokenToRender : '';
|
|
5767
|
+
// In the browser, we provide the entire class attribute as a perf optimization to avoid applying it on mount.
|
|
5768
|
+
// The remaining class expression will be applied when the static parts are mounted.
|
|
5769
|
+
// In SSR, the entire class attribute (expression included) is assembled along with the fragment.
|
|
5770
|
+
// This is why in the browser we provide the entire class attribute and in SSR we only provide the class token.
|
|
5771
|
+
const exprClassToken = process.env.IS_BROWSER ? classAttrToken : classToken;
|
|
5772
|
+
// TODO [#3624]: The implementation of this function should be specific to @lwc/engine-dom and @lwc/engine-server.
|
|
5773
|
+
// Find a way to split this in a future refactor.
|
|
5774
|
+
const serializeExpression = buildSerializeExpressionFn(parts);
|
|
5594
5775
|
let htmlFragment = '';
|
|
5595
5776
|
for (let i = 0, n = keys.length; i < n; i++) {
|
|
5596
5777
|
switch (keys[i]) {
|
|
@@ -5606,6 +5787,10 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5606
5787
|
case 3: // ${1}${2}
|
|
5607
5788
|
htmlFragment += strings[i] + classAttrToken + attrToken;
|
|
5608
5789
|
break;
|
|
5790
|
+
default: // expressions ${partId:attributeName/textId}
|
|
5791
|
+
htmlFragment +=
|
|
5792
|
+
strings[i] + serializeExpression(keys[i], exprClassToken);
|
|
5793
|
+
break;
|
|
5609
5794
|
}
|
|
5610
5795
|
}
|
|
5611
5796
|
htmlFragment += strings[strings.length - 1];
|
|
@@ -6488,25 +6673,25 @@ function forceRehydration(vm) {
|
|
|
6488
6673
|
scheduleRehydration(vm);
|
|
6489
6674
|
}
|
|
6490
6675
|
}
|
|
6491
|
-
function runFormAssociatedCustomElementCallback(vm, faceCb) {
|
|
6676
|
+
function runFormAssociatedCustomElementCallback(vm, faceCb, args) {
|
|
6492
6677
|
const { renderMode, shadowMode } = vm;
|
|
6493
6678
|
if (shadowMode === 1 /* ShadowMode.Synthetic */ && renderMode !== 0 /* RenderMode.Light */) {
|
|
6494
6679
|
throw new Error('Form associated lifecycle methods are not available in synthetic shadow. Please use native shadow or light DOM.');
|
|
6495
6680
|
}
|
|
6496
|
-
invokeComponentCallback(vm, faceCb);
|
|
6681
|
+
invokeComponentCallback(vm, faceCb, args);
|
|
6497
6682
|
}
|
|
6498
|
-
function runFormAssociatedCallback(elm) {
|
|
6683
|
+
function runFormAssociatedCallback(elm, form) {
|
|
6499
6684
|
const vm = getAssociatedVM(elm);
|
|
6500
6685
|
const { formAssociatedCallback } = vm.def;
|
|
6501
6686
|
if (!shared.isUndefined(formAssociatedCallback)) {
|
|
6502
|
-
runFormAssociatedCustomElementCallback(vm, formAssociatedCallback);
|
|
6687
|
+
runFormAssociatedCustomElementCallback(vm, formAssociatedCallback, [form]);
|
|
6503
6688
|
}
|
|
6504
6689
|
}
|
|
6505
|
-
function runFormDisabledCallback(elm) {
|
|
6690
|
+
function runFormDisabledCallback(elm, disabled) {
|
|
6506
6691
|
const vm = getAssociatedVM(elm);
|
|
6507
6692
|
const { formDisabledCallback } = vm.def;
|
|
6508
6693
|
if (!shared.isUndefined(formDisabledCallback)) {
|
|
6509
|
-
runFormAssociatedCustomElementCallback(vm, formDisabledCallback);
|
|
6694
|
+
runFormAssociatedCustomElementCallback(vm, formDisabledCallback, [disabled]);
|
|
6510
6695
|
}
|
|
6511
6696
|
}
|
|
6512
6697
|
function runFormResetCallback(elm) {
|
|
@@ -6516,11 +6701,11 @@ function runFormResetCallback(elm) {
|
|
|
6516
6701
|
runFormAssociatedCustomElementCallback(vm, formResetCallback);
|
|
6517
6702
|
}
|
|
6518
6703
|
}
|
|
6519
|
-
function runFormStateRestoreCallback(elm) {
|
|
6704
|
+
function runFormStateRestoreCallback(elm, state, reason) {
|
|
6520
6705
|
const vm = getAssociatedVM(elm);
|
|
6521
6706
|
const { formStateRestoreCallback } = vm.def;
|
|
6522
6707
|
if (!shared.isUndefined(formStateRestoreCallback)) {
|
|
6523
|
-
runFormAssociatedCustomElementCallback(vm, formStateRestoreCallback);
|
|
6708
|
+
runFormAssociatedCustomElementCallback(vm, formStateRestoreCallback, [state, reason]);
|
|
6524
6709
|
}
|
|
6525
6710
|
}
|
|
6526
6711
|
function resetRefVNodes(vm) {
|
|
@@ -6888,9 +7073,12 @@ function hydrateText(node, vnode, renderer) {
|
|
|
6888
7073
|
if (!hasCorrectNodeType(vnode, node, 3 /* EnvNodeTypes.TEXT */, renderer)) {
|
|
6889
7074
|
return handleMismatch(node, vnode, renderer);
|
|
6890
7075
|
}
|
|
7076
|
+
return updateTextContent(node, vnode, vnode.owner, renderer);
|
|
7077
|
+
}
|
|
7078
|
+
function updateTextContent(node, vnode, owner, renderer) {
|
|
6891
7079
|
if (process.env.NODE_ENV !== 'production') {
|
|
6892
7080
|
if (!textNodeContentsAreEqual(node, vnode, renderer)) {
|
|
6893
|
-
logWarn('Hydration mismatch: text values do not match, will recover from the difference',
|
|
7081
|
+
logWarn('Hydration mismatch: text values do not match, will recover from the difference', owner);
|
|
6894
7082
|
}
|
|
6895
7083
|
}
|
|
6896
7084
|
const { setText } = renderer;
|
|
@@ -6916,11 +7104,24 @@ function hydrateComment(node, vnode, renderer) {
|
|
|
6916
7104
|
}
|
|
6917
7105
|
function hydrateStaticElement(elm, vnode, renderer) {
|
|
6918
7106
|
if (!hasCorrectNodeType(vnode, elm, 1 /* EnvNodeTypes.ELEMENT */, renderer) ||
|
|
6919
|
-
!
|
|
7107
|
+
!areCompatibleStaticNodes(vnode.fragment, elm, vnode, renderer)) {
|
|
7108
|
+
return handleMismatch(elm, vnode, renderer);
|
|
7109
|
+
}
|
|
7110
|
+
return hydrateStaticElementParts(elm, vnode, renderer);
|
|
7111
|
+
}
|
|
7112
|
+
function hydrateStaticElementParts(elm, vnode, renderer) {
|
|
7113
|
+
const { parts } = vnode;
|
|
7114
|
+
if (!shared.isUndefined(parts)) {
|
|
7115
|
+
// Elements must first be set on the static part to validate against.
|
|
7116
|
+
traverseAndSetElements(elm, parts, renderer);
|
|
7117
|
+
}
|
|
7118
|
+
if (!haveCompatibleStaticParts(vnode, renderer)) {
|
|
6920
7119
|
return handleMismatch(elm, vnode, renderer);
|
|
6921
7120
|
}
|
|
6922
7121
|
vnode.elm = elm;
|
|
6923
|
-
|
|
7122
|
+
// Hydration only requires applying event listeners and refs.
|
|
7123
|
+
// All other expressions should be applied during SSR or through the handleMismatch routine.
|
|
7124
|
+
hydrateStaticParts(vnode, renderer);
|
|
6924
7125
|
return elm;
|
|
6925
7126
|
}
|
|
6926
7127
|
function hydrateFragment(elm, vnode, renderer) {
|
|
@@ -7096,12 +7297,13 @@ function isMatchingElement(vnode, elm, renderer, shouldValidateAttr = () => true
|
|
|
7096
7297
|
}
|
|
7097
7298
|
return false;
|
|
7098
7299
|
}
|
|
7099
|
-
const
|
|
7300
|
+
const { data } = vnode;
|
|
7301
|
+
const hasCompatibleAttrs = validateAttrs(vnode, elm, data, renderer, shouldValidateAttr);
|
|
7100
7302
|
const hasCompatibleClass = shouldValidateAttr('class')
|
|
7101
|
-
? validateClassAttr(vnode, elm, renderer)
|
|
7303
|
+
? validateClassAttr(vnode, elm, data, renderer)
|
|
7102
7304
|
: true;
|
|
7103
7305
|
const hasCompatibleStyle = shouldValidateAttr('style')
|
|
7104
|
-
? validateStyleAttr(vnode, elm, renderer)
|
|
7306
|
+
? validateStyleAttr(vnode, elm, data, renderer)
|
|
7105
7307
|
: true;
|
|
7106
7308
|
return hasCompatibleAttrs && hasCompatibleClass && hasCompatibleStyle;
|
|
7107
7309
|
}
|
|
@@ -7118,8 +7320,8 @@ function attributeValuesAreEqual(vnodeValue, value) {
|
|
|
7118
7320
|
// In all other cases, the two values are not considered equal
|
|
7119
7321
|
return false;
|
|
7120
7322
|
}
|
|
7121
|
-
function validateAttrs(vnode, elm, renderer, shouldValidateAttr) {
|
|
7122
|
-
const {
|
|
7323
|
+
function validateAttrs(vnode, elm, data, renderer, shouldValidateAttr) {
|
|
7324
|
+
const { attrs = {} } = data;
|
|
7123
7325
|
let nodesAreCompatible = true;
|
|
7124
7326
|
// Validate attributes, though we could always recovery from those by running the update mods.
|
|
7125
7327
|
// Note: intentionally ONLY matching vnodes.attrs to elm.attrs, in case SSR is adding extra attributes.
|
|
@@ -7127,21 +7329,22 @@ function validateAttrs(vnode, elm, renderer, shouldValidateAttr) {
|
|
|
7127
7329
|
if (!shouldValidateAttr(attrName)) {
|
|
7128
7330
|
continue;
|
|
7129
7331
|
}
|
|
7130
|
-
const { owner } = vnode;
|
|
7131
7332
|
const { getAttribute } = renderer;
|
|
7132
7333
|
const elmAttrValue = getAttribute(elm, attrName);
|
|
7133
7334
|
if (!attributeValuesAreEqual(attrValue, elmAttrValue)) {
|
|
7134
7335
|
if (process.env.NODE_ENV !== 'production') {
|
|
7135
7336
|
const { getProperty } = renderer;
|
|
7136
|
-
logError(`Mismatch hydrating element <${getProperty(elm, 'tagName').toLowerCase()}>: attribute "${attrName}" has different values, expected "${attrValue}" but found ${shared.isNull(elmAttrValue) ? 'null' : `"${elmAttrValue}"`}`, owner);
|
|
7337
|
+
logError(`Mismatch hydrating element <${getProperty(elm, 'tagName').toLowerCase()}>: attribute "${attrName}" has different values, expected "${attrValue}" but found ${shared.isNull(elmAttrValue) ? 'null' : `"${elmAttrValue}"`}`, vnode.owner);
|
|
7137
7338
|
}
|
|
7138
7339
|
nodesAreCompatible = false;
|
|
7139
7340
|
}
|
|
7140
7341
|
}
|
|
7141
7342
|
return nodesAreCompatible;
|
|
7142
7343
|
}
|
|
7143
|
-
function validateClassAttr(vnode, elm, renderer) {
|
|
7144
|
-
const {
|
|
7344
|
+
function validateClassAttr(vnode, elm, data, renderer) {
|
|
7345
|
+
const { owner } = vnode;
|
|
7346
|
+
// classMap is never available on VStaticPartData so it can default to undefined
|
|
7347
|
+
// casting to prevent TS error.
|
|
7145
7348
|
let { className, classMap } = data;
|
|
7146
7349
|
const { getProperty, getClassList, getAttribute } = renderer;
|
|
7147
7350
|
// we don't care about legacy for hydration. it's a new use case
|
|
@@ -7214,8 +7417,9 @@ function validateClassAttr(vnode, elm, renderer) {
|
|
|
7214
7417
|
}
|
|
7215
7418
|
return nodesAreCompatible;
|
|
7216
7419
|
}
|
|
7217
|
-
function validateStyleAttr(vnode, elm, renderer) {
|
|
7218
|
-
|
|
7420
|
+
function validateStyleAttr(vnode, elm, data, renderer) {
|
|
7421
|
+
// Note styleDecls is always undefined for VStaticPartData, casting here to default it to undefined
|
|
7422
|
+
const { style, styleDecls } = data;
|
|
7219
7423
|
const { getAttribute } = renderer;
|
|
7220
7424
|
const elmStyle = getAttribute(elm, 'style') || '';
|
|
7221
7425
|
let vnodeStyle;
|
|
@@ -7255,7 +7459,7 @@ function validateStyleAttr(vnode, elm, renderer) {
|
|
|
7255
7459
|
}
|
|
7256
7460
|
return nodesAreCompatible;
|
|
7257
7461
|
}
|
|
7258
|
-
function
|
|
7462
|
+
function areCompatibleStaticNodes(client, ssr, vnode, renderer) {
|
|
7259
7463
|
const { getProperty, getAttribute } = renderer;
|
|
7260
7464
|
if (getProperty(client, 'nodeType') === 3 /* EnvNodeTypes.TEXT */) {
|
|
7261
7465
|
if (!hasCorrectNodeType(vnode, ssr, 3 /* EnvNodeTypes.TEXT */, renderer)) {
|
|
@@ -7272,24 +7476,63 @@ function areCompatibleNodes(client, ssr, vnode, renderer) {
|
|
|
7272
7476
|
if (!hasCorrectNodeType(vnode, ssr, 1 /* EnvNodeTypes.ELEMENT */, renderer)) {
|
|
7273
7477
|
return false;
|
|
7274
7478
|
}
|
|
7479
|
+
const { owner, parts } = vnode;
|
|
7275
7480
|
let isCompatibleElements = true;
|
|
7276
7481
|
if (getProperty(client, 'tagName') !== getProperty(ssr, 'tagName')) {
|
|
7277
7482
|
if (process.env.NODE_ENV !== 'production') {
|
|
7278
|
-
logError(`Hydration mismatch: expecting element with tag "${getProperty(client, 'tagName').toLowerCase()}" but found "${getProperty(ssr, 'tagName').toLowerCase()}".`,
|
|
7483
|
+
logError(`Hydration mismatch: expecting element with tag "${getProperty(client, 'tagName').toLowerCase()}" but found "${getProperty(ssr, 'tagName').toLowerCase()}".`, owner);
|
|
7279
7484
|
}
|
|
7280
7485
|
return false;
|
|
7281
7486
|
}
|
|
7282
7487
|
const clientAttrsNames = getProperty(client, 'getAttributeNames').call(client);
|
|
7283
7488
|
clientAttrsNames.forEach((attrName) => {
|
|
7284
7489
|
if (getAttribute(client, attrName) !== getAttribute(ssr, attrName)) {
|
|
7285
|
-
if
|
|
7286
|
-
|
|
7490
|
+
// Check if the root element attributes have expressions, if it does then we need to delegate hydration
|
|
7491
|
+
// validation to haveCompatibleStaticParts.
|
|
7492
|
+
// Note if there are no parts then it is a fully static fragment.
|
|
7493
|
+
// partId === 0 will always refer to the root element, this is guaranteed by the compiler.
|
|
7494
|
+
if (parts?.[0].partId !== 0) {
|
|
7495
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
7496
|
+
logError(`Mismatch hydrating element <${getProperty(client, 'tagName').toLowerCase()}>: attribute "${attrName}" has different values, expected "${getAttribute(client, attrName)}" but found "${getAttribute(ssr, attrName)}"`, owner);
|
|
7497
|
+
}
|
|
7498
|
+
isCompatibleElements = false;
|
|
7287
7499
|
}
|
|
7288
|
-
isCompatibleElements = false;
|
|
7289
7500
|
}
|
|
7290
7501
|
});
|
|
7291
7502
|
return isCompatibleElements;
|
|
7292
7503
|
}
|
|
7504
|
+
function haveCompatibleStaticParts(vnode, renderer) {
|
|
7505
|
+
const { parts, owner } = vnode;
|
|
7506
|
+
if (shared.isUndefined(parts)) {
|
|
7507
|
+
return true;
|
|
7508
|
+
}
|
|
7509
|
+
// The validation here relies on 2 key invariants:
|
|
7510
|
+
// 1. It's never the case that `parts` is undefined on the server but defined on the client (or vice-versa)
|
|
7511
|
+
// 2. It's never the case that `parts` has one length on the server but another on the client
|
|
7512
|
+
for (const part of parts) {
|
|
7513
|
+
const { elm } = part;
|
|
7514
|
+
if (isVStaticPartElement(part)) {
|
|
7515
|
+
if (!hasCorrectNodeType(vnode, elm, 1 /* EnvNodeTypes.ELEMENT */, renderer)) {
|
|
7516
|
+
return false;
|
|
7517
|
+
}
|
|
7518
|
+
const { data } = part;
|
|
7519
|
+
const hasMatchingAttrs = validateAttrs(vnode, elm, data, renderer, () => true);
|
|
7520
|
+
const hasMatchingStyleAttr = validateStyleAttr(vnode, elm, data, renderer);
|
|
7521
|
+
const hasMatchingClass = validateClassAttr(vnode, elm, data, renderer);
|
|
7522
|
+
if (shared.isFalse(hasMatchingAttrs && hasMatchingStyleAttr && hasMatchingClass)) {
|
|
7523
|
+
return false;
|
|
7524
|
+
}
|
|
7525
|
+
}
|
|
7526
|
+
else {
|
|
7527
|
+
// VStaticPartText
|
|
7528
|
+
if (!hasCorrectNodeType(vnode, elm, 3 /* EnvNodeTypes.TEXT */, renderer)) {
|
|
7529
|
+
return false;
|
|
7530
|
+
}
|
|
7531
|
+
updateTextContent(elm, part, owner, renderer);
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
return true;
|
|
7535
|
+
}
|
|
7293
7536
|
|
|
7294
7537
|
/*
|
|
7295
7538
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
@@ -7604,5 +7847,5 @@ exports.swapTemplate = swapTemplate;
|
|
|
7604
7847
|
exports.track = track;
|
|
7605
7848
|
exports.unwrap = unwrap;
|
|
7606
7849
|
exports.wire = wire;
|
|
7607
|
-
/** version: 6.
|
|
7850
|
+
/** version: 6.4.0 */
|
|
7608
7851
|
//# sourceMappingURL=index.cjs.js.map
|