@lwc/engine-core 2.3.7 → 2.5.2-canary1

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.
@@ -2312,12 +2312,6 @@ const LightningElement = function () {
2312
2312
  associateVM(component, vm);
2313
2313
  associateVM(elm, vm);
2314
2314
 
2315
- if (!features.runtimeFlags.ENABLE_LIGHT_DOM_COMPONENTS) {
2316
- shared.assert.isTrue(def.renderMode !== 0
2317
- /* Light */
2318
- , `${def.name || 'Anonymous class'} is an invalid LWC component. Light DOM components are not available in this environment.`);
2319
- }
2320
-
2321
2315
  if (vm.renderMode === 1
2322
2316
  /* Shadow */
2323
2317
  ) {
@@ -2949,18 +2943,33 @@ function createObservedFieldPropertyDescriptor(key) {
2949
2943
 
2950
2944
  function getClassDescriptorType(descriptor) {
2951
2945
  if (shared.isFunction(descriptor.value)) {
2952
- return 'method';
2946
+ return "method"
2947
+ /* Method */
2948
+ ;
2953
2949
  } else if (shared.isFunction(descriptor.set) || shared.isFunction(descriptor.get)) {
2954
- return 'accessor';
2950
+ return "accessor"
2951
+ /* Accessor */
2952
+ ;
2955
2953
  } else {
2956
- return 'field';
2954
+ return "field"
2955
+ /* Field */
2956
+ ;
2957
2957
  }
2958
2958
  }
2959
2959
 
2960
2960
  function validateObservedField(Ctor, fieldName, descriptor) {
2961
2961
  if (!shared.isUndefined(descriptor)) {
2962
2962
  const type = getClassDescriptorType(descriptor);
2963
- shared.assert.fail(`Invalid observed ${fieldName} field. Found a duplicate ${type} with the same name.`);
2963
+ const message = `Invalid observed ${fieldName} field. Found a duplicate ${type} with the same name.`; // [W-9927596] Ideally we always throw an error when detecting duplicate observed field.
2964
+ // This branch is only here for backward compatibility reasons.
2965
+
2966
+ if (type === "accessor"
2967
+ /* Accessor */
2968
+ ) {
2969
+ logError(message);
2970
+ } else {
2971
+ shared.assert.fail(message);
2972
+ }
2964
2973
  }
2965
2974
  }
2966
2975
 
@@ -2987,7 +2996,16 @@ function validateMethodDecoratedWithWire(Ctor, methodName, descriptor) {
2987
2996
  function validateFieldDecoratedWithApi(Ctor, fieldName, descriptor) {
2988
2997
  if (!shared.isUndefined(descriptor)) {
2989
2998
  const type = getClassDescriptorType(descriptor);
2990
- shared.assert.fail(`Invalid @api ${fieldName} field. Found a duplicate ${type} with the same name.`);
2999
+ const message = `Invalid @api ${fieldName} field. Found a duplicate ${type} with the same name.`; // [W-9927596] Ideally we always throw an error when detecting duplicate public properties.
3000
+ // This branch is only here for backward compatibility reasons.
3001
+
3002
+ if (type === "accessor"
3003
+ /* Accessor */
3004
+ ) {
3005
+ logError(message);
3006
+ } else {
3007
+ shared.assert.fail(message);
3008
+ }
2991
3009
  }
2992
3010
  }
2993
3011
 
@@ -3050,9 +3068,16 @@ function registerDecorators(Ctor, meta) {
3050
3068
  // field declaration
3051
3069
  if (process.env.NODE_ENV !== 'production') {
3052
3070
  validateFieldDecoratedWithApi(Ctor, fieldName, descriptor);
3053
- }
3071
+ } // [W-9927596] If a component has both a public property and a private setter/getter
3072
+ // with the same name, the property is defined as a public accessor. This branch is
3073
+ // only here for backward compatibility reasons.
3074
+
3054
3075
 
3055
- descriptor = createPublicPropertyDescriptor(fieldName);
3076
+ if (!shared.isUndefined(descriptor) && !shared.isUndefined(descriptor.get)) {
3077
+ descriptor = createPublicAccessorDescriptor(fieldName, descriptor);
3078
+ } else {
3079
+ descriptor = createPublicPropertyDescriptor(fieldName);
3080
+ }
3056
3081
  }
3057
3082
 
3058
3083
  apiFields[fieldName] = descriptor;
@@ -3132,9 +3157,16 @@ function registerDecorators(Ctor, meta) {
3132
3157
 
3133
3158
  if (process.env.NODE_ENV !== 'production') {
3134
3159
  validateObservedField(Ctor, fieldName, descriptor);
3135
- }
3160
+ } // [W-9927596] Only mark a field as observed whenever it isn't a duplicated public nor
3161
+ // tracked property. This is only here for backward compatibility purposes.
3162
+
3163
+
3164
+ const isDuplicatePublicProp = !shared.isUndefined(publicProps) && fieldName in publicProps;
3165
+ const isDuplicateTrackedProp = !shared.isUndefined(track) && fieldName in track;
3136
3166
 
3137
- observedFields[fieldName] = createObservedFieldPropertyDescriptor(fieldName);
3167
+ if (!isDuplicatePublicProp && !isDuplicateTrackedProp) {
3168
+ observedFields[fieldName] = createObservedFieldPropertyDescriptor(fieldName);
3169
+ }
3138
3170
  }
3139
3171
  }
3140
3172
 
@@ -3966,8 +3998,20 @@ function observeElementChildNodes(elm) {
3966
3998
 
3967
3999
  function setElementShadowToken(elm, token) {
3968
4000
  elm.$shadowToken$ = token;
3969
- }
4001
+ } // Set the scope token class for *.scoped.css styles
3970
4002
 
4003
+
4004
+ function setScopeTokenClassIfNecessary(elm, owner) {
4005
+ const {
4006
+ cmpTemplate,
4007
+ context
4008
+ } = owner;
4009
+ const token = cmpTemplate === null || cmpTemplate === void 0 ? void 0 : cmpTemplate.stylesheetToken;
4010
+
4011
+ if (!shared.isUndefined(token) && context.hasScopedStyles) {
4012
+ owner.renderer.getClassList(elm).add(token);
4013
+ }
4014
+ }
3971
4015
  function updateNodeHook(oldVnode, vnode) {
3972
4016
  const {
3973
4017
  elm,
@@ -4031,10 +4075,21 @@ function createElmHook(vnode) {
4031
4075
  modComputedClassName.create(vnode);
4032
4076
  modComputedStyle.create(vnode);
4033
4077
  }
4078
+ function hydrateElmHook(vnode) {
4079
+ modEvents.create(vnode); // Attrs are already on the element.
4080
+ // modAttrs.create(vnode);
4081
+
4082
+ modProps.create(vnode); // Already set.
4083
+ // modStaticClassName.create(vnode);
4084
+ // modStaticStyle.create(vnode);
4085
+ // modComputedClassName.create(vnode);
4086
+ // modComputedStyle.create(vnode);
4087
+ }
4034
4088
  function fallbackElmHook(elm, vnode) {
4035
4089
  const {
4036
4090
  owner
4037
4091
  } = vnode;
4092
+ setScopeTokenClassIfNecessary(elm, owner);
4038
4093
 
4039
4094
  if (owner.shadowMode === 1
4040
4095
  /* Synthetic */
@@ -4045,7 +4100,7 @@ function fallbackElmHook(elm, vnode) {
4045
4100
  }
4046
4101
  } = vnode;
4047
4102
  const {
4048
- shadowAttribute
4103
+ stylesheetToken
4049
4104
  } = owner.context;
4050
4105
 
4051
4106
  if (!shared.isUndefined(context) && !shared.isUndefined(context.lwc) && context.lwc.dom === "manual"
@@ -4057,7 +4112,7 @@ function fallbackElmHook(elm, vnode) {
4057
4112
  // into each element from the template, so they can be styled accordingly.
4058
4113
 
4059
4114
 
4060
- setElementShadowToken(elm, shadowAttribute);
4115
+ setElementShadowToken(elm, stylesheetToken);
4061
4116
  }
4062
4117
 
4063
4118
  if (process.env.NODE_ENV !== 'production') {
@@ -4142,16 +4197,17 @@ function createViewModelHook(elm, vnode) {
4142
4197
  ctor,
4143
4198
  owner
4144
4199
  } = vnode;
4200
+ setScopeTokenClassIfNecessary(elm, owner);
4145
4201
 
4146
4202
  if (owner.shadowMode === 1
4147
4203
  /* Synthetic */
4148
4204
  ) {
4149
4205
  const {
4150
- shadowAttribute
4206
+ stylesheetToken
4151
4207
  } = owner.context; // when running in synthetic shadow mode, we need to set the shadowToken value
4152
4208
  // into each element from the template, so they can be styled accordingly.
4153
4209
 
4154
- setElementShadowToken(elm, shadowAttribute);
4210
+ setElementShadowToken(elm, stylesheetToken);
4155
4211
  }
4156
4212
 
4157
4213
  const def = getComponentInternalDef(ctor);
@@ -4193,6 +4249,172 @@ function createChildrenHook(vnode) {
4193
4249
  }
4194
4250
  }
4195
4251
  }
4252
+
4253
+ function isElementNode(node) {
4254
+ // @todo: should the hydrate be part of engine-dom? can we move hydrate out of the hooks?
4255
+ // eslint-disable-next-line lwc-internal/no-global-node
4256
+ return node.nodeType === Node.ELEMENT_NODE;
4257
+ }
4258
+
4259
+ function vnodesAndElementHaveCompatibleAttrs(vnode, elm) {
4260
+ const {
4261
+ data: {
4262
+ attrs = {}
4263
+ },
4264
+ owner: {
4265
+ renderer
4266
+ }
4267
+ } = vnode;
4268
+ let nodesAreCompatible = true; // Validate attributes, though we could always recovery from those by running the update mods.
4269
+ // Note: intentionally ONLY matching vnodes.attrs to elm.attrs, in case SSR is adding extra attributes.
4270
+
4271
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
4272
+ const elmAttrValue = renderer.getAttribute(elm, attrName);
4273
+
4274
+ if (attrValue !== elmAttrValue) {
4275
+ logError(`Error hydrating element: attribute "${attrName}" has different values, expected "${attrValue}" but found "${elmAttrValue}"`, vnode.owner);
4276
+ nodesAreCompatible = false;
4277
+ }
4278
+ }
4279
+
4280
+ return nodesAreCompatible;
4281
+ }
4282
+
4283
+ function vnodesAndElementHaveCompatibleClass(vnode, elm) {
4284
+ const {
4285
+ data: {
4286
+ className,
4287
+ classMap
4288
+ },
4289
+ owner: {
4290
+ renderer
4291
+ }
4292
+ } = vnode;
4293
+ let nodesAreCompatible = true;
4294
+
4295
+ if (!shared.isUndefined(className) && className !== elm.className) {
4296
+ // @todo: not sure if the above comparison is correct, maybe we should normalize to classlist
4297
+ // className is used when class is bound to an expr.
4298
+ logError(`Mismatch hydrating element: attribute "class" has different values, expected "${className}" but found "${elm.className}"`, vnode.owner);
4299
+ nodesAreCompatible = false;
4300
+ } else if (!shared.isUndefined(classMap)) {
4301
+ // classMap is used when class is set to static value.
4302
+ // @todo: there might be a simpler method to do this.
4303
+ const classList = renderer.getClassList(elm);
4304
+ let hasClassMismatch = false;
4305
+ let computedClassName = ''; // all classes from the vnode should be in the element.classList
4306
+
4307
+ for (const name in classMap) {
4308
+ computedClassName += ' ' + name;
4309
+
4310
+ if (!classList.contains(name)) {
4311
+ nodesAreCompatible = false;
4312
+ hasClassMismatch = true;
4313
+ }
4314
+ } // all classes from element.classList should be in the vnode classMap
4315
+
4316
+
4317
+ classList.forEach(name => {
4318
+ if (!classMap[name]) {
4319
+ nodesAreCompatible = false;
4320
+ hasClassMismatch = true;
4321
+ }
4322
+ });
4323
+
4324
+ if (hasClassMismatch) {
4325
+ logError(`Mismatch hydrating element: attribute "class" has different values, expected "${computedClassName.trim()}" but found "${elm.className}"`, vnode.owner);
4326
+ }
4327
+ }
4328
+
4329
+ return nodesAreCompatible;
4330
+ }
4331
+
4332
+ function vnodesAndElementHaveCompatibleStyle(vnode, elm) {
4333
+ const {
4334
+ data: {
4335
+ style,
4336
+ styleDecls
4337
+ },
4338
+ owner: {
4339
+ renderer
4340
+ }
4341
+ } = vnode;
4342
+ const elmStyle = renderer.getAttribute(elm, 'style');
4343
+ let nodesAreCompatible = true; // @todo: question: would it be the same or is there a chance that the browser tweak the result of elm.setAttribute('style', ...)?
4344
+ // ex: such "str" exist that after elm.setAttribute('style', str), elm.getAttribute('style') !== str.
4345
+
4346
+ if (!shared.isUndefined(style) && style !== elmStyle) {
4347
+ // style is used when class is bound to an expr.
4348
+ logError(`Mismatch hydrating element: attribute "style" has different values, expected "${style}" but found "${elmStyle}".`, vnode.owner);
4349
+ nodesAreCompatible = false;
4350
+ } else if (!shared.isUndefined(styleDecls)) {
4351
+ // styleMap is used when class is set to static value.
4352
+ for (let i = 0; i < styleDecls.length; i++) {
4353
+ const [prop, value, important] = styleDecls[i];
4354
+ const elmPropValue = elm.style.getPropertyValue(prop);
4355
+ const elmPropPriority = elm.style.getPropertyPriority(prop);
4356
+
4357
+ if (value !== elmPropValue || important !== (elmPropPriority === 'important')) {
4358
+ nodesAreCompatible = false;
4359
+ }
4360
+ } // questions: is there a way to check that only those props in styleMap are set in the element?
4361
+ // how to generate the style?
4362
+
4363
+
4364
+ logError('Error hydrating element: attribute "style" has different values.', vnode.owner);
4365
+ }
4366
+
4367
+ return nodesAreCompatible;
4368
+ }
4369
+
4370
+ function throwHydrationError() {
4371
+ // @todo: maybe create a type for these type of hydration errors
4372
+ shared.assert.fail('Server rendered elements do not match client side generated elements');
4373
+ }
4374
+
4375
+ function hydrateChildrenHook(elmChildren, children, vm) {
4376
+ var _a;
4377
+
4378
+ if (process.env.NODE_ENV !== 'production') {
4379
+ const filteredVNodes = shared.ArrayFilter.call(children, vnode => !!vnode);
4380
+
4381
+ if (elmChildren.length !== filteredVNodes.length) {
4382
+ logError(`Hydration mismatch: incorrect number of rendered elements, expected ${filteredVNodes.length} but found ${elmChildren.length}.`, vm);
4383
+ throwHydrationError();
4384
+ }
4385
+ }
4386
+
4387
+ let elmCurrentChildIdx = 0;
4388
+
4389
+ for (let j = 0, n = children.length; j < n; j++) {
4390
+ const ch = children[j];
4391
+
4392
+ if (ch != null) {
4393
+ const childNode = elmChildren[elmCurrentChildIdx];
4394
+
4395
+ if (process.env.NODE_ENV !== 'production') {
4396
+ // VComments and VTexts validation is handled in their hooks
4397
+ if (isElementNode(childNode)) {
4398
+ if (((_a = ch.sel) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== childNode.tagName.toLowerCase()) {
4399
+ logError(`Hydration mismatch: expecting element with tag "${ch.sel}" but found "${childNode.tagName}".`, vm);
4400
+ throwHydrationError();
4401
+ } // Note: props are not yet set
4402
+
4403
+
4404
+ const isVNodeAndElementCompatible = vnodesAndElementHaveCompatibleAttrs(ch, childNode) && vnodesAndElementHaveCompatibleClass(ch, childNode) && vnodesAndElementHaveCompatibleStyle(ch, childNode);
4405
+
4406
+ if (!isVNodeAndElementCompatible) {
4407
+ logError(`Hydration mismatch: incompatible attributes for element with tag "${childNode.tagName}".`, vm);
4408
+ throwHydrationError();
4409
+ }
4410
+ }
4411
+ }
4412
+
4413
+ ch.hook.hydrate(ch, childNode);
4414
+ elmCurrentChildIdx++;
4415
+ }
4416
+ }
4417
+ }
4196
4418
  function updateCustomElmHook(oldVnode, vnode) {
4197
4419
  // Attrs need to be applied to element before props
4198
4420
  // IE11 will wipe out value on radio inputs if value
@@ -4289,7 +4511,27 @@ const TextHook = {
4289
4511
  update: updateNodeHook,
4290
4512
  insert: insertNodeHook,
4291
4513
  move: insertNodeHook,
4292
- remove: removeNodeHook
4514
+ remove: removeNodeHook,
4515
+ hydrate: (vNode, node) => {
4516
+ var _a; // @todo tests.
4517
+
4518
+
4519
+ if (process.env.NODE_ENV !== 'production') {
4520
+ // eslint-disable-next-line lwc-internal/no-global-node
4521
+ if (node.nodeType !== Node.TEXT_NODE) {
4522
+ logError('Hydration mismatch: incorrect node type received', vNode.owner);
4523
+ shared.assert.fail('Hydration mismatch: incorrect node type received.');
4524
+ }
4525
+
4526
+ if (node.nodeValue !== vNode.text) {
4527
+ logError('Hydration mismatch: text values do not match, will recover from the difference', vNode.owner);
4528
+ }
4529
+ } // always set the text value to the one from the vnode.
4530
+
4531
+
4532
+ node.nodeValue = (_a = vNode.text) !== null && _a !== void 0 ? _a : null;
4533
+ vNode.elm = node;
4534
+ }
4293
4535
  };
4294
4536
  const CommentHook = {
4295
4537
  create: vnode => {
@@ -4307,7 +4549,23 @@ const CommentHook = {
4307
4549
  update: updateNodeHook,
4308
4550
  insert: insertNodeHook,
4309
4551
  move: insertNodeHook,
4310
- remove: removeNodeHook
4552
+ remove: removeNodeHook,
4553
+ hydrate: (vNode, node) => {
4554
+ var _a; // @todo tests.
4555
+
4556
+
4557
+ if (process.env.NODE_ENV !== 'production') {
4558
+ // eslint-disable-next-line lwc-internal/no-global-node
4559
+ if (node.nodeType !== Node.COMMENT_NODE) {
4560
+ logError('Hydration mismatch: incorrect node type received', vNode.owner);
4561
+ shared.assert.fail('Hydration mismatch: incorrect node type received.');
4562
+ }
4563
+ } // always set the text value to the one from the vnode.
4564
+
4565
+
4566
+ node.nodeValue = (_a = vNode.text) !== null && _a !== void 0 ? _a : null;
4567
+ vNode.elm = node;
4568
+ }
4311
4569
  }; // insert is called after update, which is used somewhere else (via a module)
4312
4570
  // to mark the vm as inserted, that means we cannot use update as the main channel
4313
4571
  // to rehydrate when dirty, because sometimes the element is not inserted just yet,
@@ -4347,6 +4605,12 @@ const ElementHook = {
4347
4605
  remove: (vnode, parentNode) => {
4348
4606
  removeNodeHook(vnode, parentNode);
4349
4607
  removeElmHook(vnode);
4608
+ },
4609
+ hydrate: (vnode, node) => {
4610
+ vnode.elm = node;
4611
+ hydrateElmHook(vnode); // hydrate children hook
4612
+
4613
+ hydrateChildrenHook(vnode.elm.childNodes, vnode.children, vnode.owner);
4350
4614
  }
4351
4615
  };
4352
4616
  const CustomElementHook = {
@@ -4438,6 +4702,52 @@ const CustomElementHook = {
4438
4702
  // will take care of disconnecting any child VM attached to its shadow as well.
4439
4703
  removeVM(vm);
4440
4704
  }
4705
+ },
4706
+ hydrate: (vnode, elm) => {
4707
+ // the element is created, but the vm is not
4708
+ const {
4709
+ sel,
4710
+ mode,
4711
+ ctor,
4712
+ owner
4713
+ } = vnode;
4714
+ const def = getComponentInternalDef(ctor);
4715
+ createVM(elm, def, {
4716
+ mode,
4717
+ owner,
4718
+ tagName: sel,
4719
+ renderer: owner.renderer
4720
+ });
4721
+ vnode.elm = elm;
4722
+ const vm = getAssociatedVMIfPresent(elm);
4723
+
4724
+ if (vm) {
4725
+ allocateChildrenHook(vnode, vm);
4726
+ }
4727
+
4728
+ hydrateElmHook(vnode); // Insert hook section:
4729
+
4730
+ if (vm) {
4731
+ if (process.env.NODE_ENV !== 'production') {
4732
+ shared.assert.isTrue(vm.state === 0
4733
+ /* created */
4734
+ , `${vm} cannot be recycled.`);
4735
+ }
4736
+
4737
+ runConnectedCallback(vm);
4738
+ }
4739
+
4740
+ if (!(vm && vm.renderMode === 0
4741
+ /* Light */
4742
+ )) {
4743
+ // VM is not rendering in Light DOM, we can proceed and hydrate the slotted content.
4744
+ // Note: for Light DOM, this is handled while hydrating the VM
4745
+ hydrateChildrenHook(vnode.elm.childNodes, vnode.children, vm);
4746
+ }
4747
+
4748
+ if (vm) {
4749
+ hydrateVM(vm);
4750
+ }
4441
4751
  }
4442
4752
  };
4443
4753
 
@@ -4926,6 +5236,10 @@ var api = /*#__PURE__*/Object.freeze({
4926
5236
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
4927
5237
  */
4928
5238
 
5239
+ function makeHostToken(token) {
5240
+ return `${token}-host`;
5241
+ }
5242
+
4929
5243
  function createInlineStyleVNode(content) {
4930
5244
  return h('style', {
4931
5245
  key: 'style',
@@ -4935,59 +5249,100 @@ function createInlineStyleVNode(content) {
4935
5249
  }, [t(content)]);
4936
5250
  }
4937
5251
 
4938
- function updateSyntheticShadowAttributes(vm, template) {
5252
+ function updateStylesheetToken(vm, template) {
4939
5253
  const {
4940
5254
  elm,
4941
5255
  context,
4942
5256
  renderer,
4943
- renderMode
5257
+ renderMode,
5258
+ shadowMode
4944
5259
  } = vm;
4945
5260
  const {
4946
5261
  stylesheets: newStylesheets,
4947
- stylesheetTokens: newStylesheetTokens
5262
+ stylesheetToken: newStylesheetToken
4948
5263
  } = template;
4949
- let newTokens; // Reset the styling token applied to the host element.
5264
+ const isSyntheticShadow = renderMode === 1
5265
+ /* Shadow */
5266
+ && shadowMode === 1
5267
+ /* Synthetic */
5268
+ ;
5269
+ const {
5270
+ hasScopedStyles
5271
+ } = context;
5272
+ let newToken;
5273
+ let newHasTokenInClass;
5274
+ let newHasTokenInAttribute; // Reset the styling token applied to the host element.
4950
5275
 
4951
- const oldHostAttribute = context.hostAttribute;
5276
+ const {
5277
+ stylesheetToken: oldToken,
5278
+ hasTokenInClass: oldHasTokenInClass,
5279
+ hasTokenInAttribute: oldHasTokenInAttribute
5280
+ } = context;
4952
5281
 
4953
- if (!shared.isUndefined(oldHostAttribute)) {
4954
- renderer.removeAttribute(elm, oldHostAttribute);
5282
+ if (oldHasTokenInClass) {
5283
+ renderer.getClassList(elm).remove(makeHostToken(oldToken));
5284
+ }
5285
+
5286
+ if (oldHasTokenInAttribute) {
5287
+ renderer.removeAttribute(elm, makeHostToken(oldToken));
4955
5288
  } // Apply the new template styling token to the host element, if the new template has any
4956
- // associated stylesheets.
5289
+ // associated stylesheets. In the case of light DOM, also ensure there is at least one scoped stylesheet.
4957
5290
 
4958
5291
 
4959
- if (!shared.isUndefined(newStylesheets) && newStylesheets.length !== 0 && renderMode === 1
4960
- /* Shadow */
4961
- ) {
4962
- newTokens = newStylesheetTokens;
4963
- }
5292
+ if (!shared.isUndefined(newStylesheets) && newStylesheets.length !== 0) {
5293
+ newToken = newStylesheetToken;
5294
+ } // Set the new styling token on the host element
5295
+
5296
+
5297
+ if (!shared.isUndefined(newToken)) {
5298
+ if (hasScopedStyles) {
5299
+ renderer.getClassList(elm).add(makeHostToken(newToken));
5300
+ newHasTokenInClass = true;
5301
+ }
4964
5302
 
4965
- if (!shared.isUndefined(newTokens)) {
4966
- renderer.setAttribute(elm, newTokens.hostAttribute, '');
5303
+ if (isSyntheticShadow) {
5304
+ renderer.setAttribute(elm, makeHostToken(newToken), '');
5305
+ newHasTokenInAttribute = true;
5306
+ }
4967
5307
  } // Update the styling tokens present on the context object.
4968
5308
 
4969
5309
 
4970
- context.hostAttribute = newTokens === null || newTokens === void 0 ? void 0 : newTokens.hostAttribute;
4971
- context.shadowAttribute = newTokens === null || newTokens === void 0 ? void 0 : newTokens.shadowAttribute;
5310
+ context.stylesheetToken = newToken;
5311
+ context.hasTokenInClass = newHasTokenInClass;
5312
+ context.hasTokenInAttribute = newHasTokenInAttribute;
4972
5313
  }
4973
5314
 
4974
- function evaluateStylesheetsContent(stylesheets, hostSelector, shadowSelector, nativeShadow) {
5315
+ function evaluateStylesheetsContent(stylesheets, stylesheetToken, vm) {
4975
5316
  const content = [];
4976
5317
 
4977
5318
  for (let i = 0; i < stylesheets.length; i++) {
4978
5319
  let stylesheet = stylesheets[i];
4979
5320
 
4980
5321
  if (shared.isArray(stylesheet)) {
4981
- shared.ArrayPush.apply(content, evaluateStylesheetsContent(stylesheet, hostSelector, shadowSelector, nativeShadow));
5322
+ shared.ArrayPush.apply(content, evaluateStylesheetsContent(stylesheet, stylesheetToken, vm));
4982
5323
  } else {
4983
5324
  if (process.env.NODE_ENV !== 'production') {
4984
5325
  // in dev-mode, we support hot swapping of stylesheet, which means that
4985
5326
  // the component instance might be attempting to use an old version of
4986
5327
  // the stylesheet, while internally, we have a replacement for it.
4987
5328
  stylesheet = getStyleOrSwappedStyle(stylesheet);
4988
- }
5329
+ } // Use the actual `:host` selector if we're rendering global CSS for light DOM, or if we're rendering
5330
+ // native shadow DOM. Synthetic shadow DOM never uses `:host`.
5331
+
4989
5332
 
4990
- shared.ArrayPush.call(content, stylesheet(hostSelector, shadowSelector, nativeShadow));
5333
+ const isScopedCss = stylesheet[shared.KEY__SCOPED_CSS];
5334
+ const useActualHostSelector = vm.renderMode === 0
5335
+ /* Light */
5336
+ ? !isScopedCss : vm.shadowMode === 0
5337
+ /* Native */
5338
+ ; // Apply the scope token only if the stylesheet itself is scoped, or if we're rendering synthetic shadow.
5339
+
5340
+ const scopeToken = isScopedCss || vm.shadowMode === 1
5341
+ /* Synthetic */
5342
+ && vm.renderMode === 1
5343
+ /* Shadow */
5344
+ ? stylesheetToken : undefined;
5345
+ shared.ArrayPush.call(content, stylesheet(useActualHostSelector, scopeToken));
4991
5346
  }
4992
5347
  }
4993
5348
 
@@ -4997,34 +5352,12 @@ function evaluateStylesheetsContent(stylesheets, hostSelector, shadowSelector, n
4997
5352
  function getStylesheetsContent(vm, template) {
4998
5353
  const {
4999
5354
  stylesheets,
5000
- stylesheetTokens
5355
+ stylesheetToken
5001
5356
  } = template;
5002
- const {
5003
- renderMode,
5004
- shadowMode
5005
- } = vm;
5006
5357
  let content = [];
5007
5358
 
5008
5359
  if (!shared.isUndefined(stylesheets) && stylesheets.length !== 0) {
5009
- let hostSelector;
5010
- let shadowSelector; // Scoping with the tokens is only necessary for synthetic shadow. For both
5011
- // light DOM elements and native shadow, we just render the CSS as-is.
5012
-
5013
- if (renderMode === 1
5014
- /* Shadow */
5015
- && shadowMode === 1
5016
- /* Synthetic */
5017
- && !shared.isUndefined(stylesheetTokens)) {
5018
- hostSelector = `[${stylesheetTokens.hostAttribute}]`;
5019
- shadowSelector = `[${stylesheetTokens.shadowAttribute}]`;
5020
- } else {
5021
- hostSelector = '';
5022
- shadowSelector = '';
5023
- }
5024
-
5025
- content = evaluateStylesheetsContent(stylesheets, hostSelector, shadowSelector, shadowMode === 0
5026
- /* Native */
5027
- );
5360
+ content = evaluateStylesheetsContent(stylesheets, stylesheetToken, vm);
5028
5361
  }
5029
5362
 
5030
5363
  return content;
@@ -5065,7 +5398,7 @@ function createStylesheet(vm, stylesheets) {
5065
5398
  for (let i = 0; i < stylesheets.length; i++) {
5066
5399
  renderer.insertGlobalStylesheet(stylesheets[i]);
5067
5400
  }
5068
- } else if (renderer.ssr) {
5401
+ } else if (renderer.ssr || renderer.isHydrating) {
5069
5402
  // native shadow or light DOM, SSR
5070
5403
  const combinedStylesheetContent = shared.ArrayJoin.call(stylesheets, '\n');
5071
5404
  return createInlineStyleVNode(combinedStylesheetContent);
@@ -5251,9 +5584,9 @@ function validateLightDomTemplate(template, vm) {
5251
5584
  if (vm.renderMode === 0
5252
5585
  /* Light */
5253
5586
  ) {
5254
- shared.assert.isTrue(template.renderMode === 'light', `Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive on the root template tag.`);
5587
+ shared.assert.isTrue(template.renderMode === 'light', `Light DOM components can't render shadow DOM templates. Add an 'lwc:render-mode="light"' directive to the root template tag of ${getComponentTag(vm)}.`);
5255
5588
  } else {
5256
- shared.assert.isTrue(shared.isUndefined(template.renderMode), `Shadow DOM components template can't render light DOM templates. Either remove the 'lwc:render-mode' directive or set it to 'lwc:render-mode="shadow"`);
5589
+ shared.assert.isTrue(shared.isUndefined(template.renderMode), `Shadow DOM components template can't render light DOM templates. Either remove the 'lwc:render-mode' directive from ${getComponentTag(vm)} or set it to 'lwc:render-mode="shadow"`);
5257
5590
  }
5258
5591
  }
5259
5592
 
@@ -5282,8 +5615,7 @@ function evaluateTemplate(vm, html) {
5282
5615
  context,
5283
5616
  cmpSlots,
5284
5617
  cmpTemplate,
5285
- tro,
5286
- shadowMode
5618
+ tro
5287
5619
  } = vm;
5288
5620
  tro.observe(() => {
5289
5621
  // Reset the cache memoizer for template when needed.
@@ -5308,15 +5640,12 @@ function evaluateTemplate(vm, html) {
5308
5640
 
5309
5641
  vm.cmpTemplate = html; // Create a brand new template cache for the swapped templated.
5310
5642
 
5311
- context.tplCache = shared.create(null); // Update the synthetic shadow attributes on the host element if necessary.
5643
+ context.tplCache = shared.create(null); // Set the computeHasScopedStyles property in the context, to avoid recomputing it repeatedly.
5312
5644
 
5313
- if (shadowMode === 1
5314
- /* Synthetic */
5315
- ) {
5316
- updateSyntheticShadowAttributes(vm, html);
5317
- } // Evaluate, create stylesheet and cache the produced VNode for future
5318
- // re-rendering.
5645
+ context.hasScopedStyles = computeHasScopedStyles(html); // Update the scoping token on the host element.
5319
5646
 
5647
+ updateStylesheetToken(vm, html); // Evaluate, create stylesheet and cache the produced VNode for future
5648
+ // re-rendering.
5320
5649
 
5321
5650
  const stylesheetsContent = getStylesheetsContent(vm, html);
5322
5651
  context.styleVNode = stylesheetsContent.length === 0 ? null : createStylesheet(vm, stylesheetsContent);
@@ -5358,6 +5687,21 @@ function evaluateTemplate(vm, html) {
5358
5687
 
5359
5688
  return vnodes;
5360
5689
  }
5690
+ function computeHasScopedStyles(template) {
5691
+ const {
5692
+ stylesheets
5693
+ } = template;
5694
+
5695
+ if (!shared.isUndefined(stylesheets)) {
5696
+ for (let i = 0; i < stylesheets.length; i++) {
5697
+ if (shared.isTrue(stylesheets[i][shared.KEY__SCOPED_CSS])) {
5698
+ return true;
5699
+ }
5700
+ }
5701
+ }
5702
+
5703
+ return false;
5704
+ }
5361
5705
 
5362
5706
  /*
5363
5707
  * Copyright (c) 2018, salesforce.com, inc.
@@ -5651,12 +5995,28 @@ function connectRootElement(elm) {
5651
5995
  /* GlobalHydrate */
5652
5996
  , vm);
5653
5997
  }
5998
+ function hydrateRootElement(elm) {
5999
+ const vm = getAssociatedVM(elm); // Usually means moving the element from one place to another, which is observable via
6000
+ // life-cycle hooks.
6001
+
6002
+ if (vm.state === 1
6003
+ /* connected */
6004
+ ) {
6005
+ disconnectRootElement(elm);
6006
+ }
6007
+
6008
+ runConnectedCallback(vm);
6009
+ hydrateVM(vm); // should we hydrate the root children? light dom case.
6010
+ }
5654
6011
  function disconnectRootElement(elm) {
5655
6012
  const vm = getAssociatedVM(elm);
5656
6013
  resetComponentStateWhenRemoved(vm);
5657
6014
  }
5658
6015
  function appendVM(vm) {
5659
6016
  rehydrate(vm);
6017
+ }
6018
+ function hydrateVM(vm) {
6019
+ hydrate(vm);
5660
6020
  } // just in case the component comes back, with this we guarantee re-rendering it
5661
6021
  // while preventing any attempt to rehydration until after reinsertion.
5662
6022
 
@@ -5747,8 +6107,10 @@ function createVM(elm, def, options) {
5747
6107
  renderMode: def.renderMode,
5748
6108
  shadowMode: null,
5749
6109
  context: {
5750
- hostAttribute: undefined,
5751
- shadowAttribute: undefined,
6110
+ stylesheetToken: undefined,
6111
+ hasTokenInClass: undefined,
6112
+ hasTokenInAttribute: undefined,
6113
+ hasScopedStyles: undefined,
5752
6114
  styleVNode: null,
5753
6115
  tplCache: EmptyObject,
5754
6116
  wiredConnecting: EmptyArray,
@@ -5879,6 +6241,22 @@ function rehydrate(vm) {
5879
6241
  }
5880
6242
  }
5881
6243
 
6244
+ function hydrate(vm) {
6245
+ if (shared.isTrue(vm.isDirty)) {
6246
+ // manually diffing/patching here.
6247
+ // This routine is:
6248
+ // patchShadowRoot(vm, children);
6249
+ // -> addVnodes.
6250
+ const children = renderComponent(vm);
6251
+ vm.children = children;
6252
+ const vmChildren = vm.renderMode === 0
6253
+ /* Light */
6254
+ ? vm.elm.childNodes : vm.elm.shadowRoot.childNodes;
6255
+ hydrateChildrenHook(vmChildren, children, vm);
6256
+ runRenderedCallback(vm);
6257
+ }
6258
+ }
6259
+
5882
6260
  function patchShadowRoot(vm, newCh) {
5883
6261
  const {
5884
6262
  children: oldCh
@@ -6705,6 +7083,7 @@ exports.getAssociatedVMIfPresent = getAssociatedVMIfPresent;
6705
7083
  exports.getComponentDef = getComponentDef;
6706
7084
  exports.getComponentInternalDef = getComponentInternalDef;
6707
7085
  exports.getUpgradableConstructor = getUpgradableConstructor;
7086
+ exports.hydrateRootElement = hydrateRootElement;
6708
7087
  exports.isComponentConstructor = isComponentConstructor;
6709
7088
  exports.readonly = readonly;
6710
7089
  exports.register = register;
@@ -6718,4 +7097,4 @@ exports.swapTemplate = swapTemplate;
6718
7097
  exports.track = track;
6719
7098
  exports.unwrap = unwrap;
6720
7099
  exports.wire = wire;
6721
- /* version: 2.3.7 */
7100
+ /* version: 2.5.2-canary1 */