@lwc/engine-core 2.38.1 → 2.39.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.
@@ -2748,6 +2748,93 @@ if (process.env.IS_BROWSER) {
2748
2748
  shared.freeze(BaseBridgeElement);
2749
2749
  shared.seal(BaseBridgeElement.prototype);
2750
2750
 
2751
+ /*
2752
+ * Copyright (c) 2023, salesforce.com, inc.
2753
+ * All rights reserved.
2754
+ * SPDX-License-Identifier: MIT
2755
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
2756
+ */
2757
+ const supportsWeakRefs = typeof WeakRef === 'function' && typeof FinalizationRegistry === 'function';
2758
+ // In browsers that doesn't support WeakRefs, the values will still leak, but at least the keys won't
2759
+ class LegacyWeakMultiMap {
2760
+ constructor() {
2761
+ this._map = new WeakMap();
2762
+ }
2763
+ _getValues(key) {
2764
+ let values = this._map.get(key);
2765
+ if (shared.isUndefined(values)) {
2766
+ values = new Set();
2767
+ this._map.set(key, values);
2768
+ }
2769
+ return values;
2770
+ }
2771
+ get(key) {
2772
+ return this._getValues(key);
2773
+ }
2774
+ add(key, vm) {
2775
+ const set = this._getValues(key);
2776
+ set.add(vm);
2777
+ }
2778
+ delete(key) {
2779
+ this._map.delete(key);
2780
+ }
2781
+ }
2782
+ // This implementation relies on the WeakRef/FinalizationRegistry proposal.
2783
+ // For some background, see: https://github.com/tc39/proposal-weakrefs
2784
+ class ModernWeakMultiMap {
2785
+ constructor() {
2786
+ this._map = new WeakMap();
2787
+ this._registry = new FinalizationRegistry((weakRefs) => {
2788
+ // This should be considered an optional cleanup method to remove GC'ed values from their respective arrays.
2789
+ // JS VMs are not obligated to call FinalizationRegistry callbacks.
2790
+ // Work backwards, removing stale VMs
2791
+ for (let i = weakRefs.length - 1; i >= 0; i--) {
2792
+ const vm = weakRefs[i].deref();
2793
+ if (shared.isUndefined(vm)) {
2794
+ shared.ArraySplice.call(weakRefs, i, 1); // remove
2795
+ }
2796
+ }
2797
+ });
2798
+ }
2799
+ _getWeakRefs(key) {
2800
+ let weakRefs = this._map.get(key);
2801
+ if (shared.isUndefined(weakRefs)) {
2802
+ weakRefs = [];
2803
+ this._map.set(key, weakRefs);
2804
+ }
2805
+ return weakRefs;
2806
+ }
2807
+ get(key) {
2808
+ const weakRefs = this._getWeakRefs(key);
2809
+ const result = new Set();
2810
+ for (const weakRef of weakRefs) {
2811
+ const vm = weakRef.deref();
2812
+ if (!shared.isUndefined(vm)) {
2813
+ result.add(vm);
2814
+ }
2815
+ }
2816
+ return result;
2817
+ }
2818
+ add(key, value) {
2819
+ const weakRefs = this._getWeakRefs(key);
2820
+ // We could check for duplicate values here, but it doesn't seem worth it.
2821
+ // We transform the output into a Set anyway
2822
+ shared.ArrayPush.call(weakRefs, new WeakRef(value));
2823
+ // It's important here not to leak the second argument, which is the "held value." The FinalizationRegistry
2824
+ // effectively creates a strong reference between the first argument (the "target") and the held value. When
2825
+ // the target is GC'ed, the callback is called, and then the held value is GC'ed.
2826
+ // Putting the key here would mean the key is not GC'ed until the value is GC'ed, which defeats the purpose
2827
+ // of the WeakMap. Whereas putting the weakRefs array here is fine, because it doesn't have a strong reference
2828
+ // to anything. See also this example:
2829
+ // https://gist.github.com/nolanlawson/79a3d36e8e6cc25c5048bb17c1795aea
2830
+ this._registry.register(value, weakRefs);
2831
+ }
2832
+ delete(key) {
2833
+ this._map.delete(key);
2834
+ }
2835
+ }
2836
+ const WeakMultiMap = supportsWeakRefs ? ModernWeakMultiMap : LegacyWeakMultiMap;
2837
+
2751
2838
  /*
2752
2839
  * Copyright (c) 2020, salesforce.com, inc.
2753
2840
  * All rights reserved.
@@ -2757,67 +2844,62 @@ shared.seal(BaseBridgeElement.prototype);
2757
2844
  const swappedTemplateMap = new WeakMap();
2758
2845
  const swappedComponentMap = new WeakMap();
2759
2846
  const swappedStyleMap = new WeakMap();
2760
- const activeTemplates = new WeakMap();
2761
- const activeComponents = new WeakMap();
2762
- const activeStyles = new WeakMap();
2847
+ // The important thing here is the weak values – VMs are transient (one per component instance) and should be GC'ed,
2848
+ // so we don't want to create strong references to them.
2849
+ // The weak keys are kind of useless, because Templates, LightningElementConstructors, and StylesheetFactories are
2850
+ // never GC'ed. But maybe they will be someday, so we may as well use weak keys too.
2851
+ const activeTemplates = new WeakMultiMap();
2852
+ const activeComponents = new WeakMultiMap();
2853
+ const activeStyles = new WeakMultiMap();
2763
2854
  function rehydrateHotTemplate(tpl) {
2764
2855
  const list = activeTemplates.get(tpl);
2765
- if (!shared.isUndefined(list)) {
2766
- list.forEach((vm) => {
2767
- if (shared.isFalse(vm.isDirty)) {
2768
- // forcing the vm to rehydrate in the micro-task:
2769
- markComponentAsDirty(vm);
2770
- scheduleRehydration(vm);
2771
- }
2772
- });
2773
- // resetting the Set to release the memory of those vm references
2774
- // since they are not longer related to this template, instead
2775
- // they will get re-associated once these instances are rehydrated.
2776
- list.clear();
2856
+ for (const vm of list) {
2857
+ if (shared.isFalse(vm.isDirty)) {
2858
+ // forcing the vm to rehydrate in the micro-task:
2859
+ markComponentAsDirty(vm);
2860
+ scheduleRehydration(vm);
2861
+ }
2777
2862
  }
2863
+ // Resetting the Set since these VMs are no longer related to this template, instead
2864
+ // they will get re-associated once these instances are rehydrated.
2865
+ activeTemplates.delete(tpl);
2778
2866
  return true;
2779
2867
  }
2780
2868
  function rehydrateHotStyle(style) {
2781
2869
  const list = activeStyles.get(style);
2782
- if (!shared.isUndefined(list)) {
2783
- list.forEach((vm) => {
2784
- // if a style definition is swapped, we must reset
2785
- // vm's template content in the next micro-task:
2786
- forceRehydration(vm);
2787
- });
2788
- // resetting the Set to release the memory of those vm references
2789
- // since they are not longer related to this style, instead
2790
- // they will get re-associated once these instances are rehydrated.
2791
- list.clear();
2792
- }
2870
+ for (const vm of list) {
2871
+ // if a style definition is swapped, we must reset
2872
+ // vm's template content in the next micro-task:
2873
+ forceRehydration(vm);
2874
+ }
2875
+ // Resetting the Set since these VMs are no longer related to this style, instead
2876
+ // they will get re-associated once these instances are rehydrated.
2877
+ activeStyles.delete(style);
2793
2878
  return true;
2794
2879
  }
2795
2880
  function rehydrateHotComponent(Ctor) {
2796
2881
  const list = activeComponents.get(Ctor);
2797
2882
  let canRefreshAllInstances = true;
2798
- if (!shared.isUndefined(list)) {
2799
- list.forEach((vm) => {
2800
- const { owner } = vm;
2801
- if (!shared.isNull(owner)) {
2802
- // if a component class definition is swapped, we must reset
2803
- // owner's template content in the next micro-task:
2804
- forceRehydration(owner);
2805
- }
2806
- else {
2807
- // the hot swapping for components only work for instances of components
2808
- // created from a template, root elements can't be swapped because we
2809
- // don't have a way to force the creation of the element with the same state
2810
- // of the current element.
2811
- // Instead, we can report the problem to the caller so it can take action,
2812
- // for example: reload the entire page.
2813
- canRefreshAllInstances = false;
2814
- }
2815
- });
2816
- // resetting the Set to release the memory of those vm references
2817
- // since they are not longer related to this constructor, instead
2818
- // they will get re-associated once these instances are rehydrated.
2819
- list.clear();
2820
- }
2883
+ for (const vm of list) {
2884
+ const { owner } = vm;
2885
+ if (!shared.isNull(owner)) {
2886
+ // if a component class definition is swapped, we must reset
2887
+ // owner's template content in the next micro-task:
2888
+ forceRehydration(owner);
2889
+ }
2890
+ else {
2891
+ // the hot swapping for components only work for instances of components
2892
+ // created from a template, root elements can't be swapped because we
2893
+ // don't have a way to force the creation of the element with the same state
2894
+ // of the current element.
2895
+ // Instead, we can report the problem to the caller so it can take action,
2896
+ // for example: reload the entire page.
2897
+ canRefreshAllInstances = false;
2898
+ }
2899
+ }
2900
+ // resetting the Set since these VMs are no longer related to this constructor, instead
2901
+ // they will get re-associated once these instances are rehydrated.
2902
+ activeComponents.delete(Ctor);
2821
2903
  return canRefreshAllInstances;
2822
2904
  }
2823
2905
  function getTemplateOrSwappedTemplate(tpl) {
@@ -2851,72 +2933,27 @@ function setActiveVM(vm) {
2851
2933
  assertNotProd(); // this method should never leak to prod
2852
2934
  // tracking active component
2853
2935
  const Ctor = vm.def.ctor;
2854
- let componentVMs = activeComponents.get(Ctor);
2855
- if (shared.isUndefined(componentVMs)) {
2856
- componentVMs = new Set();
2857
- activeComponents.set(Ctor, componentVMs);
2858
- }
2859
2936
  // this will allow us to keep track of the hot components
2860
- componentVMs.add(vm);
2937
+ activeComponents.add(Ctor, vm);
2861
2938
  // tracking active template
2862
2939
  const tpl = vm.cmpTemplate;
2863
2940
  if (tpl) {
2864
- let templateVMs = activeTemplates.get(tpl);
2865
- if (shared.isUndefined(templateVMs)) {
2866
- templateVMs = new Set();
2867
- activeTemplates.set(tpl, templateVMs);
2868
- }
2869
2941
  // this will allow us to keep track of the templates that are
2870
2942
  // being used by a hot component
2871
- templateVMs.add(vm);
2943
+ activeTemplates.add(tpl, vm);
2872
2944
  // tracking active styles associated to template
2873
2945
  const stylesheets = tpl.stylesheets;
2874
2946
  if (!shared.isUndefined(stylesheets)) {
2875
- flattenStylesheets(stylesheets).forEach((stylesheet) => {
2947
+ for (const stylesheet of flattenStylesheets(stylesheets)) {
2876
2948
  // this is necessary because we don't hold the list of styles
2877
2949
  // in the vm, we only hold the selected (already swapped template)
2878
2950
  // but the styles attached to the template might not be the actual
2879
2951
  // active ones, but the swapped versions of those.
2880
- stylesheet = getStyleOrSwappedStyle(stylesheet);
2881
- let stylesheetVMs = activeStyles.get(stylesheet);
2882
- if (shared.isUndefined(stylesheetVMs)) {
2883
- stylesheetVMs = new Set();
2884
- activeStyles.set(stylesheet, stylesheetVMs);
2885
- }
2952
+ const swappedStylesheet = getStyleOrSwappedStyle(stylesheet);
2886
2953
  // this will allow us to keep track of the stylesheet that are
2887
2954
  // being used by a hot component
2888
- stylesheetVMs.add(vm);
2889
- });
2890
- }
2891
- }
2892
- }
2893
- function removeActiveVM(vm) {
2894
- assertNotProd(); // this method should never leak to prod
2895
- // tracking inactive component
2896
- const Ctor = vm.def.ctor;
2897
- let list = activeComponents.get(Ctor);
2898
- if (!shared.isUndefined(list)) {
2899
- // deleting the vm from the set to avoid leaking memory
2900
- list.delete(vm);
2901
- }
2902
- // removing inactive template
2903
- const tpl = vm.cmpTemplate;
2904
- if (tpl) {
2905
- list = activeTemplates.get(tpl);
2906
- if (!shared.isUndefined(list)) {
2907
- // deleting the vm from the set to avoid leaking memory
2908
- list.delete(vm);
2909
- }
2910
- // removing active styles associated to template
2911
- const styles = tpl.stylesheets;
2912
- if (!shared.isUndefined(styles)) {
2913
- flattenStylesheets(styles).forEach((style) => {
2914
- list = activeStyles.get(style);
2915
- if (!shared.isUndefined(list)) {
2916
- // deleting the vm from the set to avoid leaking memory
2917
- list.delete(vm);
2918
- }
2919
- });
2955
+ activeStyles.add(swappedStylesheet, vm);
2956
+ }
2920
2957
  }
2921
2958
  }
2922
2959
  }
@@ -3678,7 +3715,12 @@ function patch(n1, n2, parent, renderer) {
3678
3715
  return;
3679
3716
  }
3680
3717
  if (process.env.NODE_ENV !== 'production') {
3681
- if (!isSameVnode(n1, n2)) {
3718
+ if (!isSameVnode(n1, n2) &&
3719
+ // Currently the only scenario when patch does not receive the same vnodes are for
3720
+ // dynamic components. When a dynamic component's constructor changes, the value of its
3721
+ // tag name (sel) will be different. The engine will unmount the previous element
3722
+ // and mount the new one using the new constructor in patchCustomElement.
3723
+ !(isVCustomElement(n1) && isVCustomElement(n2))) {
3682
3724
  throw new Error('Expected these VNodes to be the same: ' +
3683
3725
  JSON.stringify({ sel: n1.sel, key: n1.key }) +
3684
3726
  ', ' +
@@ -3876,8 +3918,9 @@ function mountCustomElement(vnode, parent, anchor, renderer) {
3876
3918
  }
3877
3919
  }
3878
3920
  function patchCustomElement(n1, n2, parent, renderer) {
3921
+ // TODO [#3331]: This if branch should be removed in 246 with lwc:dynamic
3879
3922
  if (n1.ctor !== n2.ctor) {
3880
- // If the constructor, unmount the current component and mount a new one using the new
3923
+ // If the constructor differs, unmount the current component and mount a new one using the new
3881
3924
  // constructor.
3882
3925
  const anchor = renderer.nextSibling(n1.elm);
3883
3926
  unmount(n1, parent, renderer, true);
@@ -4370,9 +4413,17 @@ function updateStaticChildren(c1, c2, parent, renderer) {
4370
4413
  if (n2 !== n1) {
4371
4414
  if (isVNode(n1)) {
4372
4415
  if (isVNode(n2)) {
4373
- // both vnodes are equivalent, and we just need to patch them
4374
- patch(n1, n2, parent, renderer);
4375
- anchor = n2.elm;
4416
+ if (isSameVnode(n1, n2)) {
4417
+ // both vnodes are equivalent, and we just need to patch them
4418
+ patch(n1, n2, parent, renderer);
4419
+ anchor = n2.elm;
4420
+ }
4421
+ else {
4422
+ // removing the old vnode since the new one is different
4423
+ unmount(n1, parent, renderer, true);
4424
+ mount(n2, parent, renderer, anchor);
4425
+ anchor = n2.elm;
4426
+ }
4376
4427
  }
4377
4428
  else {
4378
4429
  // removing the old vnode since the new one is null
@@ -4774,16 +4825,18 @@ function fid(url) {
4774
4825
  return url;
4775
4826
  }
4776
4827
  /**
4777
- * create a dynamic component via `<x-foo lwc:dynamic={Ctor}>`
4828
+ * [ddc] - create a (deprecated) dynamic component via `<x-foo lwc:dynamic={Ctor}>`
4829
+ *
4830
+ * TODO [#3331]: remove usage of lwc:dynamic in 246
4778
4831
  */
4779
- function dc(sel, Ctor, data, children = EmptyArray) {
4832
+ function ddc(sel, Ctor, data, children = EmptyArray) {
4780
4833
  if (process.env.NODE_ENV !== 'production') {
4781
4834
  shared.assert.isTrue(shared.isString(sel), `dc() 1st argument sel must be a string.`);
4782
4835
  shared.assert.isTrue(shared.isObject(data), `dc() 3nd argument data must be an object.`);
4783
4836
  shared.assert.isTrue(arguments.length === 3 || shared.isArray(children), `dc() 4nd argument data must be an array.`);
4784
4837
  }
4785
4838
  // null or undefined values should produce a null value in the VNodes
4786
- if (Ctor == null) {
4839
+ if (shared.isNull(Ctor) || shared.isUndefined(Ctor)) {
4787
4840
  return null;
4788
4841
  }
4789
4842
  if (!isComponentConstructor(Ctor)) {
@@ -4791,6 +4844,30 @@ function dc(sel, Ctor, data, children = EmptyArray) {
4791
4844
  }
4792
4845
  return c(sel, Ctor, data, children);
4793
4846
  }
4847
+ /**
4848
+ * [dc] - create a dynamic component via `<lwc:component lwc:is={Ctor}>`
4849
+ */
4850
+ function dc(Ctor, data, children = EmptyArray) {
4851
+ if (process.env.NODE_ENV !== 'production') {
4852
+ shared.assert.isTrue(shared.isObject(data), `dc() 2nd argument data must be an object.`);
4853
+ shared.assert.isTrue(arguments.length === 3 || shared.isArray(children), `dc() 3rd argument data must be an array.`);
4854
+ }
4855
+ // Null or undefined values should produce a null value in the VNodes.
4856
+ // This is the only value at compile time as the constructor will not be known.
4857
+ if (shared.isNull(Ctor) || shared.isUndefined(Ctor)) {
4858
+ return null;
4859
+ }
4860
+ if (!isComponentConstructor(Ctor)) {
4861
+ throw new Error(`Invalid constructor ${shared.toString(Ctor)} is not a LightningElement constructor.`);
4862
+ }
4863
+ // Look up the dynamic component's name at runtime once the constructor is available.
4864
+ // This information is only known at runtime and is stored as part of registerComponent.
4865
+ const sel = getComponentRegisteredName(Ctor);
4866
+ if (shared.isUndefined(sel) || sel === '') {
4867
+ throw new Error(`Invalid LWC constructor ${shared.toString(Ctor)} does not have a registered name`);
4868
+ }
4869
+ return c(sel, Ctor, data, children);
4870
+ }
4794
4871
  /**
4795
4872
  * slow children collection marking mechanism. this API allows the compiler to signal
4796
4873
  * to the engine that a particular collection of children must be diffed using the slow
@@ -4853,6 +4930,7 @@ const api = shared.freeze({
4853
4930
  fid,
4854
4931
  shc,
4855
4932
  ssf,
4933
+ ddc,
4856
4934
  });
4857
4935
 
4858
4936
  /*
@@ -5252,28 +5330,34 @@ function invokeEventListener(vm, fn, thisValue, event) {
5252
5330
  * SPDX-License-Identifier: MIT
5253
5331
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
5254
5332
  */
5255
- const signedTemplateMap = new Map();
5333
+ const registeredComponentMap = new Map();
5256
5334
  /**
5257
5335
  * INTERNAL: This function can only be invoked by compiled code. The compiler
5258
5336
  * will prevent this function from being imported by userland code.
5259
5337
  */
5260
5338
  function registerComponent(
5261
5339
  // We typically expect a LightningElementConstructor, but technically you can call this with anything
5262
- Ctor, { tmpl }) {
5340
+ Ctor, metadata) {
5263
5341
  if (shared.isFunction(Ctor)) {
5264
5342
  if (process.env.NODE_ENV !== 'production') {
5265
5343
  // There is no point in running this in production, because the version mismatch check relies
5266
5344
  // on code comments which are stripped out in production by minifiers
5267
5345
  checkVersionMismatch(Ctor, 'component');
5268
5346
  }
5269
- signedTemplateMap.set(Ctor, tmpl);
5347
+ // TODO [#3331]: add validation to check the value of metadata.sel is not an empty string.
5348
+ registeredComponentMap.set(Ctor, metadata);
5270
5349
  }
5271
5350
  // chaining this method as a way to wrap existing assignment of component constructor easily,
5272
5351
  // without too much transformation
5273
5352
  return Ctor;
5274
5353
  }
5275
5354
  function getComponentRegisteredTemplate(Ctor) {
5276
- return signedTemplateMap.get(Ctor);
5355
+ var _a;
5356
+ return (_a = registeredComponentMap.get(Ctor)) === null || _a === void 0 ? void 0 : _a.tmpl;
5357
+ }
5358
+ function getComponentRegisteredName(Ctor) {
5359
+ var _a;
5360
+ return (_a = registeredComponentMap.get(Ctor)) === null || _a === void 0 ? void 0 : _a.sel;
5277
5361
  }
5278
5362
  function getTemplateReactiveObserver(vm) {
5279
5363
  return createReactiveObserver(() => {
@@ -5409,9 +5493,6 @@ function resetComponentStateWhenRemoved(vm) {
5409
5493
  runChildNodesDisconnectedCallback(vm);
5410
5494
  runLightChildNodesDisconnectedCallback(vm);
5411
5495
  }
5412
- if (process.env.NODE_ENV !== 'production') {
5413
- removeActiveVM(vm);
5414
- }
5415
5496
  }
5416
5497
  // this method is triggered by the diffing algo only when a vnode from the
5417
5498
  // old vnode.children is removed from the DOM.
@@ -6908,5 +6989,5 @@ exports.swapTemplate = swapTemplate;
6908
6989
  exports.track = track;
6909
6990
  exports.unwrap = unwrap;
6910
6991
  exports.wire = wire;
6911
- /* version: 2.38.1 */
6992
+ /* version: 2.39.1 */
6912
6993
  //# sourceMappingURL=engine-core.cjs.js.map