@lwc/engine-core 6.3.4 → 6.4.1
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/vm.d.ts +8 -6
- package/dist/framework/vnodes.d.ts +19 -1
- package/dist/index.cjs.js +353 -81
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.js +354 -82
- 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
|
@@ -3649,6 +3649,12 @@ function isVScopedSlotFragment(vnode) {
|
|
|
3649
3649
|
function isVStatic(vnode) {
|
|
3650
3650
|
return vnode.type === 4 /* VNodeType.Static */;
|
|
3651
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
|
+
}
|
|
3652
3658
|
|
|
3653
3659
|
/*
|
|
3654
3660
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
@@ -3658,7 +3664,8 @@ function isVStatic(vnode) {
|
|
|
3658
3664
|
*/
|
|
3659
3665
|
const ColonCharCode = 58;
|
|
3660
3666
|
function patchAttributes(oldVnode, vnode, renderer) {
|
|
3661
|
-
const {
|
|
3667
|
+
const { data, elm } = vnode;
|
|
3668
|
+
const { attrs } = data;
|
|
3662
3669
|
if (shared.isUndefined(attrs)) {
|
|
3663
3670
|
return;
|
|
3664
3671
|
}
|
|
@@ -3667,7 +3674,8 @@ function patchAttributes(oldVnode, vnode, renderer) {
|
|
|
3667
3674
|
if (oldAttrs === attrs) {
|
|
3668
3675
|
return;
|
|
3669
3676
|
}
|
|
3670
|
-
|
|
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;
|
|
3671
3679
|
const { setAttribute, removeAttribute, setProperty } = renderer;
|
|
3672
3680
|
for (const key in attrs) {
|
|
3673
3681
|
const cur = attrs[key];
|
|
@@ -3771,8 +3779,7 @@ function patchProps(oldVnode, vnode, renderer) {
|
|
|
3771
3779
|
*/
|
|
3772
3780
|
const classNameToClassMap = shared.create(null);
|
|
3773
3781
|
function getMapFromClassName(className) {
|
|
3774
|
-
|
|
3775
|
-
if (className == null) {
|
|
3782
|
+
if (shared.isUndefined(className) || shared.isNull(className) || className === '') {
|
|
3776
3783
|
return EmptyObject;
|
|
3777
3784
|
}
|
|
3778
3785
|
// computed class names must be string
|
|
@@ -3811,10 +3818,16 @@ function patchClassAttribute(oldVnode, vnode, renderer) {
|
|
|
3811
3818
|
if (oldClass === newClass) {
|
|
3812
3819
|
return;
|
|
3813
3820
|
}
|
|
3814
|
-
const { getClassList } = renderer;
|
|
3815
|
-
const classList = getClassList(elm);
|
|
3816
3821
|
const newClassMap = getMapFromClassName(newClass);
|
|
3817
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);
|
|
3818
3831
|
let name;
|
|
3819
3832
|
for (name in oldClassMap) {
|
|
3820
3833
|
// remove only if it is not in the new class collection and it is not set from within the instance
|
|
@@ -3836,8 +3849,13 @@ function patchClassAttribute(oldVnode, vnode, renderer) {
|
|
|
3836
3849
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3837
3850
|
*/
|
|
3838
3851
|
// The style property is a string when defined via an expression in the template.
|
|
3839
|
-
function patchStyleAttribute(oldVnode, vnode, renderer) {
|
|
3852
|
+
function patchStyleAttribute(oldVnode, vnode, renderer, owner) {
|
|
3840
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
|
+
}
|
|
3841
3859
|
const oldStyle = shared.isNull(oldVnode) ? undefined : oldVnode.data.style;
|
|
3842
3860
|
if (oldStyle === newStyle) {
|
|
3843
3861
|
return;
|
|
@@ -3858,8 +3876,8 @@ function patchStyleAttribute(oldVnode, vnode, renderer) {
|
|
|
3858
3876
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3859
3877
|
*/
|
|
3860
3878
|
function applyEventListeners(vnode, renderer) {
|
|
3861
|
-
const { elm } = vnode;
|
|
3862
|
-
const on =
|
|
3879
|
+
const { elm, data } = vnode;
|
|
3880
|
+
const { on } = data;
|
|
3863
3881
|
if (shared.isUndefined(on)) {
|
|
3864
3882
|
return;
|
|
3865
3883
|
}
|
|
@@ -3936,12 +3954,47 @@ function applyRefs(vnode, owner) {
|
|
|
3936
3954
|
refVNodes[ref] = vnode;
|
|
3937
3955
|
}
|
|
3938
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
|
+
|
|
3939
3986
|
/*
|
|
3940
3987
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
3941
3988
|
* All rights reserved.
|
|
3942
3989
|
* SPDX-License-Identifier: MIT
|
|
3943
3990
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
3944
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
|
+
*/
|
|
3945
3998
|
function traverseAndSetElements(root, parts, renderer) {
|
|
3946
3999
|
const numParts = parts.length;
|
|
3947
4000
|
// Optimization given that, in most cases, there will be one part, and it's just the root
|
|
@@ -4007,18 +4060,30 @@ function mountStaticParts(root, vnode, renderer) {
|
|
|
4007
4060
|
traverseAndSetElements(root, parts, renderer);
|
|
4008
4061
|
// Currently only event listeners and refs are supported for static vnodes
|
|
4009
4062
|
for (const part of parts) {
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
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
|
+
}
|
|
4014
4078
|
}
|
|
4015
4079
|
}
|
|
4016
4080
|
/**
|
|
4017
|
-
*
|
|
4081
|
+
* Updates the static elements based on the content of the VStaticParts
|
|
4018
4082
|
* @param n1 the previous VStatic vnode
|
|
4019
4083
|
* @param n2 the current VStatic vnode
|
|
4084
|
+
* @param renderer the renderer to use
|
|
4020
4085
|
*/
|
|
4021
|
-
function patchStaticParts(n1, n2) {
|
|
4086
|
+
function patchStaticParts(n1, n2, renderer) {
|
|
4022
4087
|
// On the server, we don't support ref (because it relies on renderedCallback), nor do we
|
|
4023
4088
|
// support event listeners (no interactivity), so traversing parts makes no sense
|
|
4024
4089
|
if (!process.env.IS_BROWSER) {
|
|
@@ -4033,12 +4098,49 @@ function patchStaticParts(n1, n2) {
|
|
|
4033
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.');
|
|
4034
4099
|
}
|
|
4035
4100
|
for (let i = 0; i < currParts.length; i++) {
|
|
4101
|
+
const prevPart = prevParts[i];
|
|
4036
4102
|
const part = currParts[i];
|
|
4037
4103
|
// Patch only occurs if the vnode is newly generated, which means the part.elm is always undefined
|
|
4038
4104
|
// Since the vnode and elements are the same we can safely assume that prevParts[i].elm is defined.
|
|
4039
|
-
part.elm =
|
|
4040
|
-
|
|
4041
|
-
|
|
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
|
+
}
|
|
4042
4144
|
}
|
|
4043
4145
|
}
|
|
4044
4146
|
|
|
@@ -4076,7 +4178,7 @@ function patch(n1, n2, parent, renderer) {
|
|
|
4076
4178
|
switch (n2.type) {
|
|
4077
4179
|
case 0 /* VNodeType.Text */:
|
|
4078
4180
|
// VText has no special capability, fallback to the owner's renderer
|
|
4079
|
-
|
|
4181
|
+
patchTextVNode(n1, n2, renderer);
|
|
4080
4182
|
break;
|
|
4081
4183
|
case 1 /* VNodeType.Comment */:
|
|
4082
4184
|
// VComment has no special capability, fallback to the owner's renderer
|
|
@@ -4123,12 +4225,6 @@ function mount(node, parent, renderer, anchor) {
|
|
|
4123
4225
|
break;
|
|
4124
4226
|
}
|
|
4125
4227
|
}
|
|
4126
|
-
function patchText(n1, n2, renderer) {
|
|
4127
|
-
n2.elm = n1.elm;
|
|
4128
|
-
if (n2.text !== n1.text) {
|
|
4129
|
-
updateTextContent(n2, renderer);
|
|
4130
|
-
}
|
|
4131
|
-
}
|
|
4132
4228
|
function mountText(vnode, parent, anchor, renderer) {
|
|
4133
4229
|
const { owner } = vnode;
|
|
4134
4230
|
const { createText } = renderer;
|
|
@@ -4141,7 +4237,7 @@ function patchComment(n1, n2, renderer) {
|
|
|
4141
4237
|
// FIXME: Comment nodes should be static, we shouldn't need to diff them together. However
|
|
4142
4238
|
// it is the case today.
|
|
4143
4239
|
if (n2.text !== n1.text) {
|
|
4144
|
-
updateTextContent(n2, renderer);
|
|
4240
|
+
updateTextContent$1(n2, renderer);
|
|
4145
4241
|
}
|
|
4146
4242
|
}
|
|
4147
4243
|
function mountComment(vnode, parent, anchor, renderer) {
|
|
@@ -4185,7 +4281,7 @@ function patchStatic(n1, n2, renderer) {
|
|
|
4185
4281
|
// slotAssignments can only apply to the top level element, never to a static part.
|
|
4186
4282
|
patchSlotAssignment(n1, n2, renderer);
|
|
4187
4283
|
// The `refs` object is blown away in every re-render, so we always need to re-apply them
|
|
4188
|
-
patchStaticParts(n1, n2);
|
|
4284
|
+
patchStaticParts(n1, n2, renderer);
|
|
4189
4285
|
}
|
|
4190
4286
|
function patchElement(n1, n2, renderer) {
|
|
4191
4287
|
const elm = (n2.elm = n1.elm);
|
|
@@ -4196,19 +4292,20 @@ function mountStatic(vnode, parent, anchor, renderer) {
|
|
|
4196
4292
|
const { owner } = vnode;
|
|
4197
4293
|
const { cloneNode, isSyntheticShadowDefined } = renderer;
|
|
4198
4294
|
const elm = (vnode.elm = cloneNode(vnode.fragment, true));
|
|
4295
|
+
// Define the root node shadow resolver
|
|
4199
4296
|
linkNodeToShadow(elm, owner, renderer);
|
|
4200
4297
|
applyElementRestrictions(elm, vnode);
|
|
4201
|
-
// Marks this node as Static to propagate the shadow resolver. must happen after elm is assigned to the proper shadow
|
|
4202
4298
|
const { renderMode, shadowMode } = owner;
|
|
4203
4299
|
if (isSyntheticShadowDefined) {
|
|
4300
|
+
// Marks this node as Static to propagate the shadow resolver. must happen after elm is assigned to the proper shadow
|
|
4204
4301
|
if (shadowMode === 1 /* ShadowMode.Synthetic */ || renderMode === 0 /* RenderMode.Light */) {
|
|
4205
4302
|
elm[shared.KEY__SHADOW_STATIC] = true;
|
|
4206
4303
|
}
|
|
4207
4304
|
}
|
|
4208
4305
|
// slotAssignments can only apply to the top level element, never to a static part.
|
|
4209
4306
|
patchSlotAssignment(null, vnode, renderer);
|
|
4210
|
-
insertNode(elm, parent, anchor, renderer);
|
|
4211
4307
|
mountStaticParts(elm, vnode, renderer);
|
|
4308
|
+
insertNode(elm, parent, anchor, renderer);
|
|
4212
4309
|
}
|
|
4213
4310
|
function mountCustomElement(vnode, parent, anchor, renderer) {
|
|
4214
4311
|
const { sel, owner, ctor } = vnode;
|
|
@@ -4373,17 +4470,6 @@ function linkNodeToShadow(elm, owner, renderer) {
|
|
|
4373
4470
|
}
|
|
4374
4471
|
}
|
|
4375
4472
|
}
|
|
4376
|
-
function updateTextContent(vnode, renderer) {
|
|
4377
|
-
const { elm, text } = vnode;
|
|
4378
|
-
const { setText } = renderer;
|
|
4379
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
4380
|
-
unlockDomMutation();
|
|
4381
|
-
}
|
|
4382
|
-
setText(elm, text);
|
|
4383
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
4384
|
-
lockDomMutation();
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
4473
|
function insertFragmentOrNode(vnode, parent, anchor, renderer) {
|
|
4388
4474
|
if (process.env.NODE_ENV !== 'production') {
|
|
4389
4475
|
unlockDomMutation();
|
|
@@ -4428,15 +4514,16 @@ function patchElementPropsAndAttrsAndRefs$1(oldVnode, vnode, renderer) {
|
|
|
4428
4514
|
applyStaticClassAttribute(vnode, renderer);
|
|
4429
4515
|
applyStaticStyleAttribute(vnode, renderer);
|
|
4430
4516
|
}
|
|
4517
|
+
const { owner } = vnode;
|
|
4431
4518
|
// Attrs need to be applied to element before props IE11 will wipe out value on radio inputs if
|
|
4432
4519
|
// value is set before type=radio.
|
|
4433
4520
|
patchClassAttribute(oldVnode, vnode, renderer);
|
|
4434
|
-
patchStyleAttribute(oldVnode, vnode, renderer);
|
|
4521
|
+
patchStyleAttribute(oldVnode, vnode, renderer, owner);
|
|
4435
4522
|
patchAttributes(oldVnode, vnode, renderer);
|
|
4436
4523
|
patchProps(oldVnode, vnode, renderer);
|
|
4437
4524
|
patchSlotAssignment(oldVnode, vnode, renderer);
|
|
4438
4525
|
// The `refs` object is blown away in every re-render, so we always need to re-apply them
|
|
4439
|
-
applyRefs(vnode,
|
|
4526
|
+
applyRefs(vnode, owner);
|
|
4440
4527
|
}
|
|
4441
4528
|
function applyStyleScoping(elm, owner, renderer) {
|
|
4442
4529
|
const { getClassList } = renderer;
|
|
@@ -4838,10 +4925,14 @@ function addVNodeToChildLWC(vnode) {
|
|
|
4838
4925
|
shared.ArrayPush.call(getVMBeingRendered().velements, vnode);
|
|
4839
4926
|
}
|
|
4840
4927
|
// [s]tatic [p]art
|
|
4841
|
-
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 */;
|
|
4842
4931
|
return {
|
|
4932
|
+
type,
|
|
4843
4933
|
partId,
|
|
4844
4934
|
data,
|
|
4935
|
+
text,
|
|
4845
4936
|
elm: undefined, // elm is defined later
|
|
4846
4937
|
};
|
|
4847
4938
|
}
|
|
@@ -4858,8 +4949,9 @@ function ssf(slotName, factory) {
|
|
|
4858
4949
|
};
|
|
4859
4950
|
}
|
|
4860
4951
|
// [st]atic node
|
|
4861
|
-
function st(
|
|
4952
|
+
function st(fragmentFactory, key, parts) {
|
|
4862
4953
|
const owner = getVMBeingRendered();
|
|
4954
|
+
const fragment = fragmentFactory(parts);
|
|
4863
4955
|
const vnode = {
|
|
4864
4956
|
type: 4 /* VNodeType.Static */,
|
|
4865
4957
|
sel: undefined,
|
|
@@ -4901,9 +4993,6 @@ function h(sel, data, children = EmptyArray) {
|
|
|
4901
4993
|
// checking reserved internal data properties
|
|
4902
4994
|
shared.assert.isFalse(data.className && data.classMap, `vnode.data.className and vnode.data.classMap ambiguous declaration.`);
|
|
4903
4995
|
shared.assert.isFalse(data.styleDecls && data.style, `vnode.data.styleDecls and vnode.data.style ambiguous declaration.`);
|
|
4904
|
-
if (data.style && !shared.isString(data.style)) {
|
|
4905
|
-
logError(`Invalid 'style' attribute passed to <${sel}> is ignored. This attribute must be a string value.`, vmBeingRendered);
|
|
4906
|
-
}
|
|
4907
4996
|
shared.forEach.call(children, (childVnode) => {
|
|
4908
4997
|
if (childVnode != null) {
|
|
4909
4998
|
shared.assert.isTrue('type' in childVnode &&
|
|
@@ -5113,16 +5202,19 @@ function i(iterable, factory) {
|
|
|
5113
5202
|
if (process.env.NODE_ENV !== 'production') {
|
|
5114
5203
|
const vnodes = shared.isArray(vnode) ? vnode : [vnode];
|
|
5115
5204
|
shared.forEach.call(vnodes, (childVnode) => {
|
|
5116
|
-
|
|
5205
|
+
// Check that the child vnode is either an element or VStatic
|
|
5206
|
+
if (!shared.isNull(childVnode) && (isVBaseElement(childVnode) || isVStatic(childVnode))) {
|
|
5117
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;
|
|
5118
5210
|
if (shared.isString(key) || shared.isNumber(key)) {
|
|
5119
5211
|
if (keyMap[key] === 1 && shared.isUndefined(iterationError)) {
|
|
5120
|
-
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.`;
|
|
5121
5213
|
}
|
|
5122
5214
|
keyMap[key] = 1;
|
|
5123
5215
|
}
|
|
5124
5216
|
else if (shared.isUndefined(iterationError)) {
|
|
5125
|
-
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.`;
|
|
5126
5218
|
}
|
|
5127
5219
|
}
|
|
5128
5220
|
});
|
|
@@ -5521,6 +5613,111 @@ function validateLightDomTemplate(template, vm) {
|
|
|
5521
5613
|
}
|
|
5522
5614
|
}
|
|
5523
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
|
+
}
|
|
5524
5721
|
// This should be a no-op outside of LWC's Karma tests, where it's not needed
|
|
5525
5722
|
let registerFragmentCache = shared.noop;
|
|
5526
5723
|
// Only used in LWC's Karma tests
|
|
@@ -5542,7 +5739,7 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5542
5739
|
return (strings, ...keys) => {
|
|
5543
5740
|
const cache = shared.create(null);
|
|
5544
5741
|
registerFragmentCache(cache);
|
|
5545
|
-
return function () {
|
|
5742
|
+
return function (parts) {
|
|
5546
5743
|
const { context: { hasScopedStyles, stylesheetToken, legacyStylesheetToken }, shadowMode, renderer, } = getVMBeingRendered();
|
|
5547
5744
|
const hasStyleToken = !shared.isUndefined(stylesheetToken);
|
|
5548
5745
|
const isSyntheticShadow = shadowMode === 1 /* ShadowMode.Synthetic */;
|
|
@@ -5554,14 +5751,27 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5554
5751
|
if (hasStyleToken && isSyntheticShadow) {
|
|
5555
5752
|
cacheKey |= 2 /* FragmentCache.SHADOW_MODE_SYNTHETIC */;
|
|
5556
5753
|
}
|
|
5557
|
-
|
|
5558
|
-
|
|
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
|
+
}
|
|
5559
5761
|
}
|
|
5560
5762
|
// If legacy stylesheet tokens are required, then add them to the rendered string
|
|
5561
5763
|
const stylesheetTokenToRender = stylesheetToken + (hasLegacyToken ? ` ${legacyStylesheetToken}` : '');
|
|
5562
5764
|
const classToken = hasScopedStyles && hasStyleToken ? ' ' + stylesheetTokenToRender : '';
|
|
5563
5765
|
const classAttrToken = hasScopedStyles && hasStyleToken ? ` class="${stylesheetTokenToRender}"` : '';
|
|
5564
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);
|
|
5565
5775
|
let htmlFragment = '';
|
|
5566
5776
|
for (let i = 0, n = keys.length; i < n; i++) {
|
|
5567
5777
|
switch (keys[i]) {
|
|
@@ -5577,6 +5787,10 @@ function buildParseFragmentFn(createFragmentFn) {
|
|
|
5577
5787
|
case 3: // ${1}${2}
|
|
5578
5788
|
htmlFragment += strings[i] + classAttrToken + attrToken;
|
|
5579
5789
|
break;
|
|
5790
|
+
default: // expressions ${partId:attributeName/textId}
|
|
5791
|
+
htmlFragment +=
|
|
5792
|
+
strings[i] + serializeExpression(keys[i], exprClassToken);
|
|
5793
|
+
break;
|
|
5580
5794
|
}
|
|
5581
5795
|
}
|
|
5582
5796
|
htmlFragment += strings[strings.length - 1];
|
|
@@ -6459,25 +6673,25 @@ function forceRehydration(vm) {
|
|
|
6459
6673
|
scheduleRehydration(vm);
|
|
6460
6674
|
}
|
|
6461
6675
|
}
|
|
6462
|
-
function runFormAssociatedCustomElementCallback(vm, faceCb) {
|
|
6676
|
+
function runFormAssociatedCustomElementCallback(vm, faceCb, args) {
|
|
6463
6677
|
const { renderMode, shadowMode } = vm;
|
|
6464
6678
|
if (shadowMode === 1 /* ShadowMode.Synthetic */ && renderMode !== 0 /* RenderMode.Light */) {
|
|
6465
6679
|
throw new Error('Form associated lifecycle methods are not available in synthetic shadow. Please use native shadow or light DOM.');
|
|
6466
6680
|
}
|
|
6467
|
-
invokeComponentCallback(vm, faceCb);
|
|
6681
|
+
invokeComponentCallback(vm, faceCb, args);
|
|
6468
6682
|
}
|
|
6469
|
-
function runFormAssociatedCallback(elm) {
|
|
6683
|
+
function runFormAssociatedCallback(elm, form) {
|
|
6470
6684
|
const vm = getAssociatedVM(elm);
|
|
6471
6685
|
const { formAssociatedCallback } = vm.def;
|
|
6472
6686
|
if (!shared.isUndefined(formAssociatedCallback)) {
|
|
6473
|
-
runFormAssociatedCustomElementCallback(vm, formAssociatedCallback);
|
|
6687
|
+
runFormAssociatedCustomElementCallback(vm, formAssociatedCallback, [form]);
|
|
6474
6688
|
}
|
|
6475
6689
|
}
|
|
6476
|
-
function runFormDisabledCallback(elm) {
|
|
6690
|
+
function runFormDisabledCallback(elm, disabled) {
|
|
6477
6691
|
const vm = getAssociatedVM(elm);
|
|
6478
6692
|
const { formDisabledCallback } = vm.def;
|
|
6479
6693
|
if (!shared.isUndefined(formDisabledCallback)) {
|
|
6480
|
-
runFormAssociatedCustomElementCallback(vm, formDisabledCallback);
|
|
6694
|
+
runFormAssociatedCustomElementCallback(vm, formDisabledCallback, [disabled]);
|
|
6481
6695
|
}
|
|
6482
6696
|
}
|
|
6483
6697
|
function runFormResetCallback(elm) {
|
|
@@ -6487,11 +6701,11 @@ function runFormResetCallback(elm) {
|
|
|
6487
6701
|
runFormAssociatedCustomElementCallback(vm, formResetCallback);
|
|
6488
6702
|
}
|
|
6489
6703
|
}
|
|
6490
|
-
function runFormStateRestoreCallback(elm) {
|
|
6704
|
+
function runFormStateRestoreCallback(elm, state, reason) {
|
|
6491
6705
|
const vm = getAssociatedVM(elm);
|
|
6492
6706
|
const { formStateRestoreCallback } = vm.def;
|
|
6493
6707
|
if (!shared.isUndefined(formStateRestoreCallback)) {
|
|
6494
|
-
runFormAssociatedCustomElementCallback(vm, formStateRestoreCallback);
|
|
6708
|
+
runFormAssociatedCustomElementCallback(vm, formStateRestoreCallback, [state, reason]);
|
|
6495
6709
|
}
|
|
6496
6710
|
}
|
|
6497
6711
|
function resetRefVNodes(vm) {
|
|
@@ -6859,9 +7073,12 @@ function hydrateText(node, vnode, renderer) {
|
|
|
6859
7073
|
if (!hasCorrectNodeType(vnode, node, 3 /* EnvNodeTypes.TEXT */, renderer)) {
|
|
6860
7074
|
return handleMismatch(node, vnode, renderer);
|
|
6861
7075
|
}
|
|
7076
|
+
return updateTextContent(node, vnode, vnode.owner, renderer);
|
|
7077
|
+
}
|
|
7078
|
+
function updateTextContent(node, vnode, owner, renderer) {
|
|
6862
7079
|
if (process.env.NODE_ENV !== 'production') {
|
|
6863
7080
|
if (!textNodeContentsAreEqual(node, vnode, renderer)) {
|
|
6864
|
-
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);
|
|
6865
7082
|
}
|
|
6866
7083
|
}
|
|
6867
7084
|
const { setText } = renderer;
|
|
@@ -6887,11 +7104,24 @@ function hydrateComment(node, vnode, renderer) {
|
|
|
6887
7104
|
}
|
|
6888
7105
|
function hydrateStaticElement(elm, vnode, renderer) {
|
|
6889
7106
|
if (!hasCorrectNodeType(vnode, elm, 1 /* EnvNodeTypes.ELEMENT */, renderer) ||
|
|
6890
|
-
!
|
|
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)) {
|
|
6891
7119
|
return handleMismatch(elm, vnode, renderer);
|
|
6892
7120
|
}
|
|
6893
7121
|
vnode.elm = elm;
|
|
6894
|
-
|
|
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);
|
|
6895
7125
|
return elm;
|
|
6896
7126
|
}
|
|
6897
7127
|
function hydrateFragment(elm, vnode, renderer) {
|
|
@@ -7067,12 +7297,13 @@ function isMatchingElement(vnode, elm, renderer, shouldValidateAttr = () => true
|
|
|
7067
7297
|
}
|
|
7068
7298
|
return false;
|
|
7069
7299
|
}
|
|
7070
|
-
const
|
|
7300
|
+
const { data } = vnode;
|
|
7301
|
+
const hasCompatibleAttrs = validateAttrs(vnode, elm, data, renderer, shouldValidateAttr);
|
|
7071
7302
|
const hasCompatibleClass = shouldValidateAttr('class')
|
|
7072
|
-
? validateClassAttr(vnode, elm, renderer)
|
|
7303
|
+
? validateClassAttr(vnode, elm, data, renderer)
|
|
7073
7304
|
: true;
|
|
7074
7305
|
const hasCompatibleStyle = shouldValidateAttr('style')
|
|
7075
|
-
? validateStyleAttr(vnode, elm, renderer)
|
|
7306
|
+
? validateStyleAttr(vnode, elm, data, renderer)
|
|
7076
7307
|
: true;
|
|
7077
7308
|
return hasCompatibleAttrs && hasCompatibleClass && hasCompatibleStyle;
|
|
7078
7309
|
}
|
|
@@ -7089,8 +7320,8 @@ function attributeValuesAreEqual(vnodeValue, value) {
|
|
|
7089
7320
|
// In all other cases, the two values are not considered equal
|
|
7090
7321
|
return false;
|
|
7091
7322
|
}
|
|
7092
|
-
function validateAttrs(vnode, elm, renderer, shouldValidateAttr) {
|
|
7093
|
-
const {
|
|
7323
|
+
function validateAttrs(vnode, elm, data, renderer, shouldValidateAttr) {
|
|
7324
|
+
const { attrs = {} } = data;
|
|
7094
7325
|
let nodesAreCompatible = true;
|
|
7095
7326
|
// Validate attributes, though we could always recovery from those by running the update mods.
|
|
7096
7327
|
// Note: intentionally ONLY matching vnodes.attrs to elm.attrs, in case SSR is adding extra attributes.
|
|
@@ -7098,21 +7329,22 @@ function validateAttrs(vnode, elm, renderer, shouldValidateAttr) {
|
|
|
7098
7329
|
if (!shouldValidateAttr(attrName)) {
|
|
7099
7330
|
continue;
|
|
7100
7331
|
}
|
|
7101
|
-
const { owner } = vnode;
|
|
7102
7332
|
const { getAttribute } = renderer;
|
|
7103
7333
|
const elmAttrValue = getAttribute(elm, attrName);
|
|
7104
7334
|
if (!attributeValuesAreEqual(attrValue, elmAttrValue)) {
|
|
7105
7335
|
if (process.env.NODE_ENV !== 'production') {
|
|
7106
7336
|
const { getProperty } = renderer;
|
|
7107
|
-
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);
|
|
7108
7338
|
}
|
|
7109
7339
|
nodesAreCompatible = false;
|
|
7110
7340
|
}
|
|
7111
7341
|
}
|
|
7112
7342
|
return nodesAreCompatible;
|
|
7113
7343
|
}
|
|
7114
|
-
function validateClassAttr(vnode, elm, renderer) {
|
|
7115
|
-
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.
|
|
7116
7348
|
let { className, classMap } = data;
|
|
7117
7349
|
const { getProperty, getClassList, getAttribute } = renderer;
|
|
7118
7350
|
// we don't care about legacy for hydration. it's a new use case
|
|
@@ -7185,8 +7417,9 @@ function validateClassAttr(vnode, elm, renderer) {
|
|
|
7185
7417
|
}
|
|
7186
7418
|
return nodesAreCompatible;
|
|
7187
7419
|
}
|
|
7188
|
-
function validateStyleAttr(vnode, elm, renderer) {
|
|
7189
|
-
|
|
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;
|
|
7190
7423
|
const { getAttribute } = renderer;
|
|
7191
7424
|
const elmStyle = getAttribute(elm, 'style') || '';
|
|
7192
7425
|
let vnodeStyle;
|
|
@@ -7226,7 +7459,7 @@ function validateStyleAttr(vnode, elm, renderer) {
|
|
|
7226
7459
|
}
|
|
7227
7460
|
return nodesAreCompatible;
|
|
7228
7461
|
}
|
|
7229
|
-
function
|
|
7462
|
+
function areCompatibleStaticNodes(client, ssr, vnode, renderer) {
|
|
7230
7463
|
const { getProperty, getAttribute } = renderer;
|
|
7231
7464
|
if (getProperty(client, 'nodeType') === 3 /* EnvNodeTypes.TEXT */) {
|
|
7232
7465
|
if (!hasCorrectNodeType(vnode, ssr, 3 /* EnvNodeTypes.TEXT */, renderer)) {
|
|
@@ -7243,24 +7476,63 @@ function areCompatibleNodes(client, ssr, vnode, renderer) {
|
|
|
7243
7476
|
if (!hasCorrectNodeType(vnode, ssr, 1 /* EnvNodeTypes.ELEMENT */, renderer)) {
|
|
7244
7477
|
return false;
|
|
7245
7478
|
}
|
|
7479
|
+
const { owner, parts } = vnode;
|
|
7246
7480
|
let isCompatibleElements = true;
|
|
7247
7481
|
if (getProperty(client, 'tagName') !== getProperty(ssr, 'tagName')) {
|
|
7248
7482
|
if (process.env.NODE_ENV !== 'production') {
|
|
7249
|
-
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);
|
|
7250
7484
|
}
|
|
7251
7485
|
return false;
|
|
7252
7486
|
}
|
|
7253
7487
|
const clientAttrsNames = getProperty(client, 'getAttributeNames').call(client);
|
|
7254
7488
|
clientAttrsNames.forEach((attrName) => {
|
|
7255
7489
|
if (getAttribute(client, attrName) !== getAttribute(ssr, attrName)) {
|
|
7256
|
-
if
|
|
7257
|
-
|
|
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;
|
|
7258
7499
|
}
|
|
7259
|
-
isCompatibleElements = false;
|
|
7260
7500
|
}
|
|
7261
7501
|
});
|
|
7262
7502
|
return isCompatibleElements;
|
|
7263
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
|
+
}
|
|
7264
7536
|
|
|
7265
7537
|
/*
|
|
7266
7538
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
@@ -7575,5 +7847,5 @@ exports.swapTemplate = swapTemplate;
|
|
|
7575
7847
|
exports.track = track;
|
|
7576
7848
|
exports.unwrap = unwrap;
|
|
7577
7849
|
exports.wire = wire;
|
|
7578
|
-
/** version: 6.
|
|
7850
|
+
/** version: 6.4.1 */
|
|
7579
7851
|
//# sourceMappingURL=index.cjs.js.map
|