@lwc/engine-core 3.7.0 → 3.7.2

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.
@@ -6,5 +6,6 @@ import { RendererAPI } from '../renderer';
6
6
  * @param root - the root element
7
7
  * @param vnode - the parent VStatic
8
8
  * @param renderer - the renderer to use
9
+ * @param mount - true this is a first (mount) render as opposed to a subsequent (patch) render
9
10
  */
10
- export declare function applyStaticParts(root: Element, vnode: VStatic, renderer: RendererAPI): void;
11
+ export declare function applyStaticParts(root: Element, vnode: VStatic, renderer: RendererAPI, mount: boolean): void;
@@ -172,4 +172,5 @@ export declare function runFormAssociatedCallback(elm: HTMLElement): void;
172
172
  export declare function runFormDisabledCallback(elm: HTMLElement): void;
173
173
  export declare function runFormResetCallback(elm: HTMLElement): void;
174
174
  export declare function runFormStateRestoreCallback(elm: HTMLElement): void;
175
+ export declare function resetRefVNodes(vm: VM): void;
175
176
  export {};
package/dist/index.cjs.js CHANGED
@@ -1458,12 +1458,31 @@ const formAssociatedProps = new Set([
1458
1458
  'labels',
1459
1459
  ]);
1460
1460
  // Verify that access to a form-associated property of the ElementInternals proxy has formAssociated set in the LWC.
1461
- function assertFormAssociatedPropertySet(propertyKey, isFormAssociated) {
1462
- if (formAssociatedProps.has(propertyKey) && !isFormAssociated) {
1461
+ function verifyPropForFormAssociation(propertyKey, isFormAssociated) {
1462
+ if (shared.isString(propertyKey) && formAssociatedProps.has(propertyKey) && !isFormAssociated) {
1463
1463
  //Note this error message mirrors Chrome and Firefox error messages, in Safari the error is slightly different.
1464
1464
  throw new DOMException(`Failed to execute '${propertyKey}' on 'ElementInternals': The target element is not a form-associated custom element.`);
1465
1465
  }
1466
1466
  }
1467
+ const elementInternalsAccessorAllowList = new Set(['shadowRoot', 'role', ...formAssociatedProps]);
1468
+ // Prevent access to properties not defined in the HTML spec in case browsers decide to
1469
+ // provide new APIs that provide access to form associated properties.
1470
+ // This can be removed along with UpgradeableConstructor.
1471
+ function isAllowedElementInternalAccessor(propertyKey) {
1472
+ let isAllowedAccessor = false;
1473
+ // As of this writing all ElementInternal property keys as described in the spec are implemented with strings
1474
+ // in Chrome, Firefox, and Safari
1475
+ if (shared.isString(propertyKey)) {
1476
+ // Allow list is based on HTML spec:
1477
+ // https://html.spec.whatwg.org/multipage/custom-elements.html#the-elementinternals-interface
1478
+ isAllowedAccessor =
1479
+ elementInternalsAccessorAllowList.has(propertyKey) || /^aria/.test(propertyKey);
1480
+ if (!isAllowedAccessor && process.env.NODE_ENV !== 'production') {
1481
+ logWarn('Only properties defined in the ElementInternals HTML spec are available.');
1482
+ }
1483
+ }
1484
+ return isAllowedAccessor;
1485
+ }
1467
1486
  // Wrap all ElementInternal objects in a proxy to prevent form association when `formAssociated` is not set on an LWC.
1468
1487
  // This is needed because the 1UpgradeableConstructor1 always sets `formAssociated=true`, which means all
1469
1488
  // ElementInternal objects will have form-associated properties set when an LWC is placed in a form.
@@ -1472,19 +1491,28 @@ function assertFormAssociatedPropertySet(propertyKey, isFormAssociated) {
1472
1491
  function createElementInternalsProxy(elementInternals, isFormAssociated) {
1473
1492
  const elementInternalsProxy = new Proxy(elementInternals, {
1474
1493
  set(target, propertyKey, newValue) {
1475
- // ElementInternals implementation uses strings as property keys exclusively in chrome, firefox, and safari
1476
- assertFormAssociatedPropertySet(propertyKey, isFormAssociated);
1477
- return Reflect.set(target, propertyKey, newValue);
1494
+ if (isAllowedElementInternalAccessor(propertyKey)) {
1495
+ // Verify that formAssociated is set for form associated properties
1496
+ verifyPropForFormAssociation(propertyKey, isFormAssociated);
1497
+ return Reflect.set(target, propertyKey, newValue);
1498
+ }
1499
+ // As of this writing ElementInternals do not have non-string properties that can be set.
1500
+ return false;
1478
1501
  },
1479
1502
  get(target, propertyKey) {
1480
- // ElementInternals implementation uses strings as property keys exclusively in chrome, firefox, and safari
1481
- assertFormAssociatedPropertySet(propertyKey, isFormAssociated);
1482
- const internalsPropertyValue = Reflect.get(target, propertyKey);
1483
- // Bind the property value to the target so that function invocations are called with the
1484
- // correct context ('this' value).
1485
- return typeof internalsPropertyValue === 'function'
1486
- ? internalsPropertyValue.bind(target)
1487
- : internalsPropertyValue;
1503
+ if (
1504
+ // Pass through Object.prototype methods such as toString()
1505
+ shared.hasOwnProperty.call(Object.prototype, propertyKey) ||
1506
+ // As of this writing, ElementInternals only uses Symbol.toStringTag which is called
1507
+ // on Object.hasOwnProperty invocations
1508
+ Symbol.for('Symbol.toStringTag') === propertyKey ||
1509
+ // ElementInternals allow listed properties
1510
+ isAllowedElementInternalAccessor(propertyKey)) {
1511
+ // Verify that formAssociated is set for form associated properties
1512
+ verifyPropForFormAssociation(propertyKey, isFormAssociated);
1513
+ const propertyValue = Reflect.get(target, propertyKey);
1514
+ return shared.isFunction(propertyValue) ? propertyValue.bind(target) : propertyValue;
1515
+ }
1488
1516
  },
1489
1517
  });
1490
1518
  return elementInternalsProxy;
@@ -3768,8 +3796,9 @@ function traverseAndSetElements(root, parts, renderer) {
3768
3796
  * @param root - the root element
3769
3797
  * @param vnode - the parent VStatic
3770
3798
  * @param renderer - the renderer to use
3799
+ * @param mount - true this is a first (mount) render as opposed to a subsequent (patch) render
3771
3800
  */
3772
- function applyStaticParts(root, vnode, renderer) {
3801
+ function applyStaticParts(root, vnode, renderer, mount) {
3773
3802
  // On the server, we don't support ref (because it relies on renderedCallback), nor do we
3774
3803
  // support event listeners (no interactivity), so traversing parts makes no sense
3775
3804
  if (!process.env.IS_BROWSER) {
@@ -3779,11 +3808,17 @@ function applyStaticParts(root, vnode, renderer) {
3779
3808
  if (shared.isUndefined(parts)) {
3780
3809
  return;
3781
3810
  }
3782
- traverseAndSetElements(root, parts, renderer); // this adds `part.elm` to each `part`
3811
+ // This adds `part.elm` to each `part`. We have to do this on every mount/patch because the `parts`
3812
+ // array is recreated from scratch every time, so each `part.elm` is now undefined.
3813
+ // TODO [#3800]: avoid calling traverseAndSetElements on every re-render
3814
+ traverseAndSetElements(root, parts, renderer);
3815
+ // Currently only event listeners and refs are supported for static vnodes
3783
3816
  for (const part of parts) {
3784
- // Event listeners are only applied once when mounting, so they are allowed for static vnodes
3785
- applyEventListeners(part, renderer);
3786
- // Refs are allowed as well
3817
+ if (mount) {
3818
+ // Event listeners only need to be applied once when mounting
3819
+ applyEventListeners(part, renderer);
3820
+ }
3821
+ // Refs must be updated after every render due to refVNodes getting reset before every render
3787
3822
  applyRefs(part, owner);
3788
3823
  }
3789
3824
  }
@@ -3830,7 +3865,7 @@ function patch(n1, n2, parent, renderer) {
3830
3865
  patchComment(n1, n2, renderer);
3831
3866
  break;
3832
3867
  case 4 /* VNodeType.Static */:
3833
- n2.elm = n1.elm;
3868
+ patchStatic(n1, n2, renderer);
3834
3869
  break;
3835
3870
  case 5 /* VNodeType.Fragment */:
3836
3871
  patchFragment(n1, n2, parent, renderer);
@@ -3928,6 +3963,11 @@ function mountElement(vnode, parent, anchor, renderer) {
3928
3963
  insertNode(elm, parent, anchor, renderer);
3929
3964
  mountVNodes(vnode.children, elm, renderer, null);
3930
3965
  }
3966
+ function patchStatic(n1, n2, renderer) {
3967
+ const elm = (n2.elm = n1.elm);
3968
+ // The `refs` object is blown away in every re-render, so we always need to re-apply them
3969
+ applyStaticParts(elm, n2, renderer, false);
3970
+ }
3931
3971
  function patchElement(n1, n2, renderer) {
3932
3972
  const elm = (n2.elm = n1.elm);
3933
3973
  patchElementPropsAndAttrsAndRefs$1(n1, n2, renderer);
@@ -3947,7 +3987,7 @@ function mountStatic(vnode, parent, anchor, renderer) {
3947
3987
  }
3948
3988
  }
3949
3989
  insertNode(elm, parent, anchor, renderer);
3950
- applyStaticParts(elm, vnode, renderer);
3990
+ applyStaticParts(elm, vnode, renderer, true);
3951
3991
  }
3952
3992
  function mountCustomElement(vnode, parent, anchor, renderer) {
3953
3993
  const { sel, owner } = vnode;
@@ -5373,8 +5413,6 @@ function evaluateTemplate(vm, html) {
5373
5413
  // add the VM to the list of host VMs that can be re-rendered if html is swapped
5374
5414
  setActiveVM(vm);
5375
5415
  }
5376
- // reset the refs; they will be set during the tmpl() instantiation
5377
- vm.refVNodes = html.hasRefs ? shared.create(null) : null;
5378
5416
  // right before producing the vnodes, we clear up all internal references
5379
5417
  // to custom elements from the template.
5380
5418
  vm.velements = [];
@@ -5875,6 +5913,8 @@ function rehydrate(vm) {
5875
5913
  }
5876
5914
  function patchShadowRoot(vm, newCh) {
5877
5915
  const { renderRoot, children: oldCh, renderer } = vm;
5916
+ // reset the refs; they will be set during `patchChildren`
5917
+ resetRefVNodes(vm);
5878
5918
  // caching the new children collection
5879
5919
  vm.children = newCh;
5880
5920
  if (newCh.length > 0 || oldCh.length > 0) {
@@ -6097,7 +6137,12 @@ function runWithBoundaryProtection(vm, owner, pre, job, post) {
6097
6137
  if (!shared.isUndefined(error)) {
6098
6138
  addErrorComponentStack(vm, error);
6099
6139
  const errorBoundaryVm = shared.isNull(owner) ? undefined : getErrorBoundaryVM(owner);
6100
- if (shared.isUndefined(errorBoundaryVm)) {
6140
+ // Error boundaries are not in effect when server-side rendering. `errorCallback`
6141
+ // is intended to allow recovery from errors - changing the state of a component
6142
+ // and instigating a re-render. That is at odds with the single-pass, synchronous
6143
+ // nature of SSR. For that reason, all errors bubble up to the `renderComponent`
6144
+ // call site.
6145
+ if (!process.env.IS_BROWSER || shared.isUndefined(errorBoundaryVm)) {
6101
6146
  throw error; // eslint-disable-line no-unsafe-finally
6102
6147
  }
6103
6148
  resetComponentRoot(vm); // remove offenders
@@ -6167,6 +6212,10 @@ function runFormStateRestoreCallback(elm) {
6167
6212
  runFormAssociatedCustomElementCallback(vm, formStateRestoreCallback);
6168
6213
  }
6169
6214
  }
6215
+ function resetRefVNodes(vm) {
6216
+ const { cmpTemplate } = vm;
6217
+ vm.refVNodes = !shared.isNull(cmpTemplate) && cmpTemplate.hasRefs ? shared.create(null) : null;
6218
+ }
6170
6219
 
6171
6220
  /*
6172
6221
  * Copyright (c) 2018, salesforce.com, inc.
@@ -6453,6 +6502,8 @@ function hydrateRoot(vm) {
6453
6502
  function hydrateVM(vm) {
6454
6503
  const children = renderComponent(vm);
6455
6504
  vm.children = children;
6505
+ // reset the refs; they will be set during `hydrateChildren`
6506
+ resetRefVNodes(vm);
6456
6507
  const { renderRoot: parentNode, renderer: { getFirstChild }, } = vm;
6457
6508
  hydrateChildren(getFirstChild(parentNode), children, parentNode, vm);
6458
6509
  runRenderedCallback(vm);
@@ -6561,7 +6612,7 @@ function hydrateStaticElement(elm, vnode, renderer) {
6561
6612
  return handleMismatch(elm, vnode, renderer);
6562
6613
  }
6563
6614
  vnode.elm = elm;
6564
- applyStaticParts(elm, vnode, renderer);
6615
+ applyStaticParts(elm, vnode, renderer, true);
6565
6616
  return elm;
6566
6617
  }
6567
6618
  function hydrateFragment(elm, vnode, renderer) {
@@ -7218,5 +7269,5 @@ exports.swapTemplate = swapTemplate;
7218
7269
  exports.track = track;
7219
7270
  exports.unwrap = unwrap;
7220
7271
  exports.wire = wire;
7221
- /** version: 3.7.0 */
7272
+ /** version: 3.7.2 */
7222
7273
  //# sourceMappingURL=index.cjs.js.map