@lwc/engine-core 2.32.1 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@
3
3
 
4
4
  var features = require('@lwc/features');
5
5
  var shared = require('@lwc/shared');
6
+ var ariaReflection = require('@lwc/aria-reflection');
6
7
 
7
8
  /*
8
9
  * Copyright (c) 2018, salesforce.com, inc.
@@ -308,6 +309,7 @@ function log(method, message, vm) {
308
309
  if (!shared.isUndefined(vm)) {
309
310
  msg = `${msg}\n${getComponentStack(vm)}`;
310
311
  }
312
+ // In Jest tests, reduce the warning and error verbosity by not printing the callstack
311
313
  if (process.env.NODE_ENV === 'test') {
312
314
  /* eslint-disable-next-line no-console */
313
315
  console[method](msg);
@@ -377,6 +379,9 @@ function offsetPropertyErrorMessage(name) {
377
379
  // Global HTML Attributes & Properties
378
380
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
379
381
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
382
+ //
383
+ // If you update this list, check for test files that recapitulate the same list. Searching the codebase
384
+ // for e.g. "dropzone" should suffice.
380
385
  const globalHTMLProperties = shared.assign(shared.create(null), {
381
386
  accessKey: {
382
387
  attribute: 'accesskey',
@@ -1410,46 +1415,51 @@ function markLockerLiveObject(obj) {
1410
1415
  * for the Base Lightning Element, it also include the reactivity bit, so the standard property is reactive.
1411
1416
  */
1412
1417
  function createBridgeToElementDescriptor(propName, descriptor) {
1413
- const { get, set, enumerable, configurable } = descriptor;
1414
- if (!shared.isFunction(get)) {
1415
- if (process.env.NODE_ENV !== 'production') {
1416
- shared.assert.fail(`Detected invalid public property descriptor for HTMLElement.prototype.${propName} definition. Missing the standard getter.`);
1417
- }
1418
- throw new TypeError();
1418
+ const {
1419
+ get,
1420
+ set,
1421
+ enumerable,
1422
+ configurable
1423
+ } = descriptor;
1424
+ if (!shared.isFunction(get)) {
1425
+ if (process.env.NODE_ENV !== 'production') {
1426
+ shared.assert.fail(`Detected invalid public property descriptor for HTMLElement.prototype.${propName} definition. Missing the standard getter.`);
1419
1427
  }
1420
- if (!shared.isFunction(set)) {
1428
+ throw new TypeError();
1429
+ }
1430
+ if (!shared.isFunction(set)) {
1431
+ if (process.env.NODE_ENV !== 'production') {
1432
+ shared.assert.fail(`Detected invalid public property descriptor for HTMLElement.prototype.${propName} definition. Missing the standard setter.`);
1433
+ }
1434
+ throw new TypeError();
1435
+ }
1436
+ return {
1437
+ enumerable,
1438
+ configurable,
1439
+ get() {
1440
+ const vm = getAssociatedVM(this);
1441
+ if (isBeingConstructed(vm)) {
1421
1442
  if (process.env.NODE_ENV !== 'production') {
1422
- shared.assert.fail(`Detected invalid public property descriptor for HTMLElement.prototype.${propName} definition. Missing the standard setter.`);
1443
+ logError(`The value of property \`${propName}\` can't be read from the constructor because the owner component hasn't set the value yet. Instead, use the constructor to set a default value for the property.`, vm);
1423
1444
  }
1424
- throw new TypeError();
1445
+ return;
1446
+ }
1447
+ componentValueObserved(vm, propName);
1448
+ return get.call(vm.elm);
1449
+ },
1450
+ set(newValue) {
1451
+ const vm = getAssociatedVM(this);
1452
+ if (process.env.NODE_ENV !== 'production') {
1453
+ const vmBeingRendered = getVMBeingRendered();
1454
+ shared.assert.invariant(!isInvokingRender, `${vmBeingRendered}.render() method has side effects on the state of ${vm}.${propName}`);
1455
+ shared.assert.invariant(!isUpdatingTemplate, `When updating the template of ${vmBeingRendered}, one of the accessors used by the template has side effects on the state of ${vm}.${propName}`);
1456
+ shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1457
+ shared.assert.invariant(!shared.isObject(newValue) || shared.isNull(newValue), `Invalid value "${newValue}" for "${propName}" of ${vm}. Value cannot be an object, must be a primitive value.`);
1458
+ }
1459
+ updateComponentValue(vm, propName, newValue);
1460
+ return set.call(vm.elm, newValue);
1425
1461
  }
1426
- return {
1427
- enumerable,
1428
- configurable,
1429
- get() {
1430
- const vm = getAssociatedVM(this);
1431
- if (isBeingConstructed(vm)) {
1432
- if (process.env.NODE_ENV !== 'production') {
1433
- logError(`The value of property \`${propName}\` can't be read from the constructor because the owner component hasn't set the value yet. Instead, use the constructor to set a default value for the property.`, vm);
1434
- }
1435
- return;
1436
- }
1437
- componentValueObserved(vm, propName);
1438
- return get.call(vm.elm);
1439
- },
1440
- set(newValue) {
1441
- const vm = getAssociatedVM(this);
1442
- if (process.env.NODE_ENV !== 'production') {
1443
- const vmBeingRendered = getVMBeingRendered();
1444
- shared.assert.invariant(!isInvokingRender, `${vmBeingRendered}.render() method has side effects on the state of ${vm}.${propName}`);
1445
- shared.assert.invariant(!isUpdatingTemplate, `When updating the template of ${vmBeingRendered}, one of the accessors used by the template has side effects on the state of ${vm}.${propName}`);
1446
- shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1447
- shared.assert.invariant(!shared.isObject(newValue) || shared.isNull(newValue), `Invalid value "${newValue}" for "${propName}" of ${vm}. Value cannot be an object, must be a primitive value.`);
1448
- }
1449
- updateComponentValue(vm, propName, newValue);
1450
- return set.call(vm.elm, newValue);
1451
- },
1452
- };
1462
+ };
1453
1463
  }
1454
1464
  const EMPTY_REFS = shared.freeze(shared.create(null));
1455
1465
  const refsCache = new WeakMap();
@@ -1459,345 +1469,444 @@ const refsCache = new WeakMap();
1459
1469
  **/
1460
1470
  // @ts-ignore
1461
1471
  const LightningElement = function () {
1462
- // This should be as performant as possible, while any initialization should be done lazily
1463
- if (shared.isNull(vmBeingConstructed)) {
1464
- // Thrown when doing something like `new LightningElement()` or
1465
- // `class Foo extends LightningElement {}; new Foo()`
1466
- throw new TypeError('Illegal constructor');
1467
- }
1468
- const vm = vmBeingConstructed;
1469
- const { def, elm } = vm;
1470
- const { bridge } = def;
1472
+ // This should be as performant as possible, while any initialization should be done lazily
1473
+ if (shared.isNull(vmBeingConstructed)) {
1474
+ // Thrown when doing something like `new LightningElement()` or
1475
+ // `class Foo extends LightningElement {}; new Foo()`
1476
+ throw new TypeError('Illegal constructor');
1477
+ }
1478
+ const vm = vmBeingConstructed;
1479
+ const {
1480
+ def,
1481
+ elm
1482
+ } = vm;
1483
+ const {
1484
+ bridge
1485
+ } = def;
1486
+ if (process.env.NODE_ENV !== 'production') {
1487
+ const {
1488
+ assertInstanceOfHTMLElement
1489
+ } = vm.renderer;
1490
+ assertInstanceOfHTMLElement(vm.elm, `Component creation requires a DOM element to be associated to ${vm}.`);
1491
+ }
1492
+ const component = this;
1493
+ shared.setPrototypeOf(elm, bridge.prototype);
1494
+ vm.component = this;
1495
+ // Locker hooks assignment. When the LWC engine run with Locker, Locker intercepts all the new
1496
+ // component creation and passes hooks to instrument all the component interactions with the
1497
+ // engine. We are intentionally hiding this argument from the formal API of LightningElement
1498
+ // because we don't want folks to know about it just yet.
1499
+ if (arguments.length === 1) {
1500
+ const {
1501
+ callHook,
1502
+ setHook,
1503
+ getHook
1504
+ } = arguments[0];
1505
+ vm.callHook = callHook;
1506
+ vm.setHook = setHook;
1507
+ vm.getHook = getHook;
1508
+ }
1509
+ markLockerLiveObject(this);
1510
+ // Linking elm, shadow root and component with the VM.
1511
+ associateVM(component, vm);
1512
+ associateVM(elm, vm);
1513
+ if (vm.renderMode === 1 /* RenderMode.Shadow */) {
1514
+ vm.renderRoot = doAttachShadow(vm);
1515
+ } else {
1516
+ vm.renderRoot = elm;
1517
+ }
1518
+ // Adding extra guard rails in DEV mode.
1519
+ if (process.env.NODE_ENV !== 'production') {
1520
+ patchCustomElementWithRestrictions(elm);
1521
+ patchComponentWithRestrictions(component);
1522
+ }
1523
+ return this;
1524
+ };
1525
+ function doAttachShadow(vm) {
1526
+ const {
1527
+ elm,
1528
+ mode,
1529
+ shadowMode,
1530
+ def: {
1531
+ ctor
1532
+ },
1533
+ renderer: {
1534
+ attachShadow
1535
+ }
1536
+ } = vm;
1537
+ const shadowRoot = attachShadow(elm, {
1538
+ [shared.KEY__SYNTHETIC_MODE]: shadowMode === 1 /* ShadowMode.Synthetic */,
1539
+ delegatesFocus: Boolean(ctor.delegatesFocus),
1540
+ mode
1541
+ });
1542
+ vm.shadowRoot = shadowRoot;
1543
+ associateVM(shadowRoot, vm);
1544
+ if (process.env.NODE_ENV !== 'production') {
1545
+ patchShadowRootWithRestrictions(shadowRoot);
1546
+ }
1547
+ return shadowRoot;
1548
+ }
1549
+ function warnIfInvokedDuringConstruction(vm, methodOrPropName) {
1550
+ if (isBeingConstructed(vm)) {
1551
+ logError(`this.${methodOrPropName} should not be called during the construction of the custom element for ${getComponentTag(vm)} because the element is not yet in the DOM or has no children yet.`);
1552
+ }
1553
+ }
1554
+ // @ts-ignore
1555
+ LightningElement.prototype = {
1556
+ constructor: LightningElement,
1557
+ dispatchEvent(event) {
1558
+ const vm = getAssociatedVM(this);
1559
+ const {
1560
+ elm,
1561
+ renderer: {
1562
+ dispatchEvent
1563
+ }
1564
+ } = vm;
1565
+ return dispatchEvent(elm, event);
1566
+ },
1567
+ addEventListener(type, listener, options) {
1568
+ const vm = getAssociatedVM(this);
1569
+ const {
1570
+ elm,
1571
+ renderer: {
1572
+ addEventListener
1573
+ }
1574
+ } = vm;
1471
1575
  if (process.env.NODE_ENV !== 'production') {
1472
- const { assertInstanceOfHTMLElement } = vm.renderer;
1473
- assertInstanceOfHTMLElement(vm.elm, `Component creation requires a DOM element to be associated to ${vm}.`);
1474
- }
1475
- const component = this;
1476
- shared.setPrototypeOf(elm, bridge.prototype);
1477
- vm.component = this;
1478
- // Locker hooks assignment. When the LWC engine run with Locker, Locker intercepts all the new
1479
- // component creation and passes hooks to instrument all the component interactions with the
1480
- // engine. We are intentionally hiding this argument from the formal API of LightningElement
1481
- // because we don't want folks to know about it just yet.
1482
- if (arguments.length === 1) {
1483
- const { callHook, setHook, getHook } = arguments[0];
1484
- vm.callHook = callHook;
1485
- vm.setHook = setHook;
1486
- vm.getHook = getHook;
1487
- }
1488
- markLockerLiveObject(this);
1489
- // Linking elm, shadow root and component with the VM.
1490
- associateVM(component, vm);
1491
- associateVM(elm, vm);
1492
- if (vm.renderMode === 1 /* RenderMode.Shadow */) {
1493
- vm.renderRoot = doAttachShadow(vm);
1576
+ const vmBeingRendered = getVMBeingRendered();
1577
+ shared.assert.invariant(!isInvokingRender, `${vmBeingRendered}.render() method has side effects on the state of ${vm} by adding an event listener for "${type}".`);
1578
+ shared.assert.invariant(!isUpdatingTemplate, `Updating the template of ${vmBeingRendered} has side effects on the state of ${vm} by adding an event listener for "${type}".`);
1579
+ shared.assert.invariant(shared.isFunction(listener), `Invalid second argument for this.addEventListener() in ${vm} for event "${type}". Expected an EventListener but received ${listener}.`);
1580
+ }
1581
+ const wrappedListener = getWrappedComponentsListener(vm, listener);
1582
+ addEventListener(elm, type, wrappedListener, options);
1583
+ },
1584
+ removeEventListener(type, listener, options) {
1585
+ const vm = getAssociatedVM(this);
1586
+ const {
1587
+ elm,
1588
+ renderer: {
1589
+ removeEventListener
1590
+ }
1591
+ } = vm;
1592
+ const wrappedListener = getWrappedComponentsListener(vm, listener);
1593
+ removeEventListener(elm, type, wrappedListener, options);
1594
+ },
1595
+ hasAttribute(name) {
1596
+ const vm = getAssociatedVM(this);
1597
+ const {
1598
+ elm,
1599
+ renderer: {
1600
+ getAttribute
1601
+ }
1602
+ } = vm;
1603
+ return !shared.isNull(getAttribute(elm, name));
1604
+ },
1605
+ hasAttributeNS(namespace, name) {
1606
+ const vm = getAssociatedVM(this);
1607
+ const {
1608
+ elm,
1609
+ renderer: {
1610
+ getAttribute
1611
+ }
1612
+ } = vm;
1613
+ return !shared.isNull(getAttribute(elm, name, namespace));
1614
+ },
1615
+ removeAttribute(name) {
1616
+ const vm = getAssociatedVM(this);
1617
+ const {
1618
+ elm,
1619
+ renderer: {
1620
+ removeAttribute
1621
+ }
1622
+ } = vm;
1623
+ unlockAttribute(elm, name);
1624
+ removeAttribute(elm, name);
1625
+ lockAttribute();
1626
+ },
1627
+ removeAttributeNS(namespace, name) {
1628
+ const {
1629
+ elm,
1630
+ renderer: {
1631
+ removeAttribute
1632
+ }
1633
+ } = getAssociatedVM(this);
1634
+ unlockAttribute(elm, name);
1635
+ removeAttribute(elm, name, namespace);
1636
+ lockAttribute();
1637
+ },
1638
+ getAttribute(name) {
1639
+ const vm = getAssociatedVM(this);
1640
+ const {
1641
+ elm
1642
+ } = vm;
1643
+ const {
1644
+ getAttribute
1645
+ } = vm.renderer;
1646
+ return getAttribute(elm, name);
1647
+ },
1648
+ getAttributeNS(namespace, name) {
1649
+ const vm = getAssociatedVM(this);
1650
+ const {
1651
+ elm
1652
+ } = vm;
1653
+ const {
1654
+ getAttribute
1655
+ } = vm.renderer;
1656
+ return getAttribute(elm, name, namespace);
1657
+ },
1658
+ setAttribute(name, value) {
1659
+ const vm = getAssociatedVM(this);
1660
+ const {
1661
+ elm,
1662
+ renderer: {
1663
+ setAttribute
1664
+ }
1665
+ } = vm;
1666
+ if (process.env.NODE_ENV !== 'production') {
1667
+ shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1668
+ }
1669
+ unlockAttribute(elm, name);
1670
+ setAttribute(elm, name, value);
1671
+ lockAttribute();
1672
+ },
1673
+ setAttributeNS(namespace, name, value) {
1674
+ const vm = getAssociatedVM(this);
1675
+ const {
1676
+ elm,
1677
+ renderer: {
1678
+ setAttribute
1679
+ }
1680
+ } = vm;
1681
+ if (process.env.NODE_ENV !== 'production') {
1682
+ shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1683
+ }
1684
+ unlockAttribute(elm, name);
1685
+ setAttribute(elm, name, value, namespace);
1686
+ lockAttribute();
1687
+ },
1688
+ getBoundingClientRect() {
1689
+ const vm = getAssociatedVM(this);
1690
+ const {
1691
+ elm,
1692
+ renderer: {
1693
+ getBoundingClientRect
1694
+ }
1695
+ } = vm;
1696
+ if (process.env.NODE_ENV !== 'production') {
1697
+ warnIfInvokedDuringConstruction(vm, 'getBoundingClientRect()');
1494
1698
  }
1495
- else {
1496
- vm.renderRoot = elm;
1699
+ return getBoundingClientRect(elm);
1700
+ },
1701
+ get isConnected() {
1702
+ const vm = getAssociatedVM(this);
1703
+ const {
1704
+ elm,
1705
+ renderer: {
1706
+ isConnected
1707
+ }
1708
+ } = vm;
1709
+ return isConnected(elm);
1710
+ },
1711
+ get classList() {
1712
+ const vm = getAssociatedVM(this);
1713
+ const {
1714
+ elm,
1715
+ renderer: {
1716
+ getClassList
1717
+ }
1718
+ } = vm;
1719
+ if (process.env.NODE_ENV !== 'production') {
1720
+ // TODO [#1290]: this still fails in dev but works in production, eventually, we should
1721
+ // just throw in all modes
1722
+ shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct ${vm}: The result must not have attributes. Adding or tampering with classname in constructor is not allowed in a web component, use connectedCallback() instead.`);
1723
+ }
1724
+ return getClassList(elm);
1725
+ },
1726
+ get template() {
1727
+ const vm = getAssociatedVM(this);
1728
+ if (process.env.NODE_ENV !== 'production') {
1729
+ if (vm.renderMode === 0 /* RenderMode.Light */) {
1730
+ logError('`this.template` returns null for light DOM components. Since there is no shadow, the rendered content can be accessed via `this` itself. e.g. instead of `this.template.querySelector`, use `this.querySelector`.');
1731
+ }
1732
+ }
1733
+ return vm.shadowRoot;
1734
+ },
1735
+ get refs() {
1736
+ const vm = getAssociatedVM(this);
1737
+ if (isUpdatingTemplate) {
1738
+ if (process.env.NODE_ENV !== 'production') {
1739
+ logError(`this.refs should not be called while ${getComponentTag(vm)} is rendering. Use this.refs only when the DOM is stable, e.g. in renderedCallback().`);
1740
+ }
1741
+ // If the template is in the process of being updated, then we don't want to go through the normal
1742
+ // process of returning the refs and caching them, because the state of the refs is unstable.
1743
+ // This can happen if e.g. a template contains `<div class={foo}></div>` and `foo` is computed
1744
+ // based on `this.refs.bar`.
1745
+ return;
1497
1746
  }
1498
- // Adding extra guard rails in DEV mode.
1499
1747
  if (process.env.NODE_ENV !== 'production') {
1500
- patchCustomElementWithRestrictions(elm);
1501
- patchComponentWithRestrictions(component);
1748
+ warnIfInvokedDuringConstruction(vm, 'refs');
1502
1749
  }
1503
- return this;
1504
- };
1505
- function doAttachShadow(vm) {
1506
- const { elm, mode, shadowMode, def: { ctor }, renderer: { attachShadow }, } = vm;
1507
- const shadowRoot = attachShadow(elm, {
1508
- [shared.KEY__SYNTHETIC_MODE]: shadowMode === 1 /* ShadowMode.Synthetic */,
1509
- delegatesFocus: Boolean(ctor.delegatesFocus),
1510
- mode,
1750
+ const {
1751
+ refVNodes,
1752
+ hasRefVNodes,
1753
+ cmpTemplate
1754
+ } = vm;
1755
+ // If the `cmpTemplate` is null, that means that the template has not been rendered yet. Most likely this occurs
1756
+ // if `this.refs` is called during the `connectedCallback` phase. The DOM elements have not been rendered yet,
1757
+ // so log a warning. Note we also check `isBeingConstructed()` to avoid a double warning (due to
1758
+ // `warnIfInvokedDuringConstruction` above).
1759
+ if (process.env.NODE_ENV !== 'production' && shared.isNull(cmpTemplate) && !isBeingConstructed(vm)) {
1760
+ logError(`this.refs is undefined for ${getComponentTag(vm)}. This is either because the attached template has no "lwc:ref" directive, or this.refs was ` + `invoked before renderedCallback(). Use this.refs only when the referenced HTML elements have ` + `been rendered to the DOM, such as within renderedCallback() or disconnectedCallback().`);
1761
+ }
1762
+ // For backwards compatibility with component written before template refs
1763
+ // were introduced, we return undefined if the template has no refs defined
1764
+ // anywhere. This fixes components that may want to add an expando called `refs`
1765
+ // and are checking if it exists with `if (this.refs)` before adding it.
1766
+ // Note it is not sufficient to just check if `refVNodes` is null or empty,
1767
+ // because a template may have `lwc:ref` defined within a falsy `if:true` block.
1768
+ if (!hasRefVNodes) {
1769
+ return;
1770
+ }
1771
+ // For templates that are using `lwc:ref`, if there are no refs currently available
1772
+ // (e.g. refs inside of a falsy `if:true` block), we return an empty object.
1773
+ if (shared.isNull(refVNodes)) {
1774
+ return EMPTY_REFS;
1775
+ }
1776
+ // The refNodes can be cached based on the refVNodes, since the refVNodes
1777
+ // are recreated from scratch every time the template is rendered.
1778
+ // This happens with `vm.refVNodes = null` in `template.ts` in `@lwc/engine-core`.
1779
+ let refs = refsCache.get(refVNodes);
1780
+ if (shared.isUndefined(refs)) {
1781
+ refs = shared.create(null);
1782
+ for (const key of shared.keys(refVNodes)) {
1783
+ refs[key] = refVNodes[key].elm;
1784
+ }
1785
+ shared.freeze(refs);
1786
+ refsCache.set(refVNodes, refs);
1787
+ }
1788
+ return refs;
1789
+ },
1790
+ // For backwards compat, we allow component authors to set `refs` as an expando
1791
+ set refs(value) {
1792
+ shared.defineProperty(this, 'refs', {
1793
+ configurable: true,
1794
+ enumerable: true,
1795
+ writable: true,
1796
+ value
1511
1797
  });
1512
- vm.shadowRoot = shadowRoot;
1513
- associateVM(shadowRoot, vm);
1798
+ },
1799
+ get shadowRoot() {
1800
+ // From within the component instance, the shadowRoot is always reported as "closed".
1801
+ // Authors should rely on this.template instead.
1802
+ return null;
1803
+ },
1804
+ get children() {
1805
+ const vm = getAssociatedVM(this);
1806
+ const renderer = vm.renderer;
1514
1807
  if (process.env.NODE_ENV !== 'production') {
1515
- patchShadowRootWithRestrictions(shadowRoot);
1808
+ warnIfInvokedDuringConstruction(vm, 'children');
1516
1809
  }
1517
- return shadowRoot;
1518
- }
1519
- function warnIfInvokedDuringConstruction(vm, methodOrPropName) {
1520
- if (isBeingConstructed(vm)) {
1521
- logError(`this.${methodOrPropName} should not be called during the construction of the custom element for ${getComponentTag(vm)} because the element is not yet in the DOM or has no children yet.`);
1810
+ return renderer.getChildren(vm.elm);
1811
+ },
1812
+ get childNodes() {
1813
+ const vm = getAssociatedVM(this);
1814
+ const renderer = vm.renderer;
1815
+ if (process.env.NODE_ENV !== 'production') {
1816
+ warnIfInvokedDuringConstruction(vm, 'childNodes');
1522
1817
  }
1523
- }
1524
- // @ts-ignore
1525
- LightningElement.prototype = {
1526
- constructor: LightningElement,
1527
- dispatchEvent(event) {
1528
- const vm = getAssociatedVM(this);
1529
- const { elm, renderer: { dispatchEvent }, } = vm;
1530
- return dispatchEvent(elm, event);
1531
- },
1532
- addEventListener(type, listener, options) {
1533
- const vm = getAssociatedVM(this);
1534
- const { elm, renderer: { addEventListener }, } = vm;
1535
- if (process.env.NODE_ENV !== 'production') {
1536
- const vmBeingRendered = getVMBeingRendered();
1537
- shared.assert.invariant(!isInvokingRender, `${vmBeingRendered}.render() method has side effects on the state of ${vm} by adding an event listener for "${type}".`);
1538
- shared.assert.invariant(!isUpdatingTemplate, `Updating the template of ${vmBeingRendered} has side effects on the state of ${vm} by adding an event listener for "${type}".`);
1539
- shared.assert.invariant(shared.isFunction(listener), `Invalid second argument for this.addEventListener() in ${vm} for event "${type}". Expected an EventListener but received ${listener}.`);
1540
- }
1541
- const wrappedListener = getWrappedComponentsListener(vm, listener);
1542
- addEventListener(elm, type, wrappedListener, options);
1543
- },
1544
- removeEventListener(type, listener, options) {
1545
- const vm = getAssociatedVM(this);
1546
- const { elm, renderer: { removeEventListener }, } = vm;
1547
- const wrappedListener = getWrappedComponentsListener(vm, listener);
1548
- removeEventListener(elm, type, wrappedListener, options);
1549
- },
1550
- hasAttribute(name) {
1551
- const vm = getAssociatedVM(this);
1552
- const { elm, renderer: { getAttribute }, } = vm;
1553
- return !shared.isNull(getAttribute(elm, name));
1554
- },
1555
- hasAttributeNS(namespace, name) {
1556
- const vm = getAssociatedVM(this);
1557
- const { elm, renderer: { getAttribute }, } = vm;
1558
- return !shared.isNull(getAttribute(elm, name, namespace));
1559
- },
1560
- removeAttribute(name) {
1561
- const vm = getAssociatedVM(this);
1562
- const { elm, renderer: { removeAttribute }, } = vm;
1563
- unlockAttribute(elm, name);
1564
- removeAttribute(elm, name);
1565
- lockAttribute();
1566
- },
1567
- removeAttributeNS(namespace, name) {
1568
- const { elm, renderer: { removeAttribute }, } = getAssociatedVM(this);
1569
- unlockAttribute(elm, name);
1570
- removeAttribute(elm, name, namespace);
1571
- lockAttribute();
1572
- },
1573
- getAttribute(name) {
1574
- const vm = getAssociatedVM(this);
1575
- const { elm } = vm;
1576
- const { getAttribute } = vm.renderer;
1577
- return getAttribute(elm, name);
1578
- },
1579
- getAttributeNS(namespace, name) {
1580
- const vm = getAssociatedVM(this);
1581
- const { elm } = vm;
1582
- const { getAttribute } = vm.renderer;
1583
- return getAttribute(elm, name, namespace);
1584
- },
1585
- setAttribute(name, value) {
1586
- const vm = getAssociatedVM(this);
1587
- const { elm, renderer: { setAttribute }, } = vm;
1588
- if (process.env.NODE_ENV !== 'production') {
1589
- shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1590
- }
1591
- unlockAttribute(elm, name);
1592
- setAttribute(elm, name, value);
1593
- lockAttribute();
1594
- },
1595
- setAttributeNS(namespace, name, value) {
1596
- const vm = getAssociatedVM(this);
1597
- const { elm, renderer: { setAttribute }, } = vm;
1598
- if (process.env.NODE_ENV !== 'production') {
1599
- shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct '${getComponentTag(vm)}': The result must not have attributes.`);
1600
- }
1601
- unlockAttribute(elm, name);
1602
- setAttribute(elm, name, value, namespace);
1603
- lockAttribute();
1604
- },
1605
- getBoundingClientRect() {
1606
- const vm = getAssociatedVM(this);
1607
- const { elm, renderer: { getBoundingClientRect }, } = vm;
1608
- if (process.env.NODE_ENV !== 'production') {
1609
- warnIfInvokedDuringConstruction(vm, 'getBoundingClientRect()');
1610
- }
1611
- return getBoundingClientRect(elm);
1612
- },
1613
- get isConnected() {
1614
- const vm = getAssociatedVM(this);
1615
- const { elm, renderer: { isConnected }, } = vm;
1616
- return isConnected(elm);
1617
- },
1618
- get classList() {
1619
- const vm = getAssociatedVM(this);
1620
- const { elm, renderer: { getClassList }, } = vm;
1621
- if (process.env.NODE_ENV !== 'production') {
1622
- // TODO [#1290]: this still fails in dev but works in production, eventually, we should
1623
- // just throw in all modes
1624
- shared.assert.isFalse(isBeingConstructed(vm), `Failed to construct ${vm}: The result must not have attributes. Adding or tampering with classname in constructor is not allowed in a web component, use connectedCallback() instead.`);
1625
- }
1626
- return getClassList(elm);
1627
- },
1628
- get template() {
1629
- const vm = getAssociatedVM(this);
1630
- if (process.env.NODE_ENV !== 'production') {
1631
- if (vm.renderMode === 0 /* RenderMode.Light */) {
1632
- logError('`this.template` returns null for light DOM components. Since there is no shadow, the rendered content can be accessed via `this` itself. e.g. instead of `this.template.querySelector`, use `this.querySelector`.');
1633
- }
1634
- }
1635
- return vm.shadowRoot;
1636
- },
1637
- get refs() {
1638
- const vm = getAssociatedVM(this);
1639
- if (isUpdatingTemplate) {
1640
- if (process.env.NODE_ENV !== 'production') {
1641
- logError(`this.refs should not be called while ${getComponentTag(vm)} is rendering. Use this.refs only when the DOM is stable, e.g. in renderedCallback().`);
1642
- }
1643
- // If the template is in the process of being updated, then we don't want to go through the normal
1644
- // process of returning the refs and caching them, because the state of the refs is unstable.
1645
- // This can happen if e.g. a template contains `<div class={foo}></div>` and `foo` is computed
1646
- // based on `this.refs.bar`.
1647
- return;
1648
- }
1649
- if (process.env.NODE_ENV !== 'production') {
1650
- warnIfInvokedDuringConstruction(vm, 'refs');
1651
- }
1652
- const { refVNodes, hasRefVNodes, cmpTemplate } = vm;
1653
- // If the `cmpTemplate` is null, that means that the template has not been rendered yet. Most likely this occurs
1654
- // if `this.refs` is called during the `connectedCallback` phase. The DOM elements have not been rendered yet,
1655
- // so log a warning. Note we also check `isBeingConstructed()` to avoid a double warning (due to
1656
- // `warnIfInvokedDuringConstruction` above).
1657
- if (process.env.NODE_ENV !== 'production' &&
1658
- shared.isNull(cmpTemplate) &&
1659
- !isBeingConstructed(vm)) {
1660
- logError(`this.refs is undefined for ${getComponentTag(vm)}. This is either because the attached template has no "lwc:ref" directive, or this.refs was ` +
1661
- `invoked before renderedCallback(). Use this.refs only when the referenced HTML elements have ` +
1662
- `been rendered to the DOM, such as within renderedCallback() or disconnectedCallback().`);
1663
- }
1664
- // For backwards compatibility with component written before template refs
1665
- // were introduced, we return undefined if the template has no refs defined
1666
- // anywhere. This fixes components that may want to add an expando called `refs`
1667
- // and are checking if it exists with `if (this.refs)` before adding it.
1668
- // Note it is not sufficient to just check if `refVNodes` is null or empty,
1669
- // because a template may have `lwc:ref` defined within a falsy `if:true` block.
1670
- if (!hasRefVNodes) {
1671
- return;
1672
- }
1673
- // For templates that are using `lwc:ref`, if there are no refs currently available
1674
- // (e.g. refs inside of a falsy `if:true` block), we return an empty object.
1675
- if (shared.isNull(refVNodes)) {
1676
- return EMPTY_REFS;
1677
- }
1678
- // The refNodes can be cached based on the refVNodes, since the refVNodes
1679
- // are recreated from scratch every time the template is rendered.
1680
- // This happens with `vm.refVNodes = null` in `template.ts` in `@lwc/engine-core`.
1681
- let refs = refsCache.get(refVNodes);
1682
- if (shared.isUndefined(refs)) {
1683
- refs = shared.create(null);
1684
- for (const key of shared.keys(refVNodes)) {
1685
- refs[key] = refVNodes[key].elm;
1686
- }
1687
- shared.freeze(refs);
1688
- refsCache.set(refVNodes, refs);
1689
- }
1690
- return refs;
1691
- },
1692
- // For backwards compat, we allow component authors to set `refs` as an expando
1693
- set refs(value) {
1694
- shared.defineProperty(this, 'refs', {
1695
- configurable: true,
1696
- enumerable: true,
1697
- writable: true,
1698
- value,
1699
- });
1700
- },
1701
- get shadowRoot() {
1702
- // From within the component instance, the shadowRoot is always reported as "closed".
1703
- // Authors should rely on this.template instead.
1704
- return null;
1705
- },
1706
- get children() {
1707
- const vm = getAssociatedVM(this);
1708
- const renderer = vm.renderer;
1709
- if (process.env.NODE_ENV !== 'production') {
1710
- warnIfInvokedDuringConstruction(vm, 'children');
1711
- }
1712
- return renderer.getChildren(vm.elm);
1713
- },
1714
- get childNodes() {
1715
- const vm = getAssociatedVM(this);
1716
- const renderer = vm.renderer;
1717
- if (process.env.NODE_ENV !== 'production') {
1718
- warnIfInvokedDuringConstruction(vm, 'childNodes');
1719
- }
1720
- return renderer.getChildNodes(vm.elm);
1721
- },
1722
- get firstChild() {
1723
- const vm = getAssociatedVM(this);
1724
- const renderer = vm.renderer;
1725
- if (process.env.NODE_ENV !== 'production') {
1726
- warnIfInvokedDuringConstruction(vm, 'firstChild');
1727
- }
1728
- return renderer.getFirstChild(vm.elm);
1729
- },
1730
- get firstElementChild() {
1731
- const vm = getAssociatedVM(this);
1732
- const renderer = vm.renderer;
1733
- if (process.env.NODE_ENV !== 'production') {
1734
- warnIfInvokedDuringConstruction(vm, 'firstElementChild');
1735
- }
1736
- return renderer.getFirstElementChild(vm.elm);
1737
- },
1738
- get lastChild() {
1739
- const vm = getAssociatedVM(this);
1740
- const renderer = vm.renderer;
1741
- if (process.env.NODE_ENV !== 'production') {
1742
- warnIfInvokedDuringConstruction(vm, 'lastChild');
1743
- }
1744
- return renderer.getLastChild(vm.elm);
1745
- },
1746
- get lastElementChild() {
1747
- const vm = getAssociatedVM(this);
1748
- const renderer = vm.renderer;
1749
- if (process.env.NODE_ENV !== 'production') {
1750
- warnIfInvokedDuringConstruction(vm, 'lastElementChild');
1751
- }
1752
- return renderer.getLastElementChild(vm.elm);
1753
- },
1754
- render() {
1755
- const vm = getAssociatedVM(this);
1756
- return vm.def.template;
1757
- },
1758
- toString() {
1759
- const vm = getAssociatedVM(this);
1760
- return `[object ${vm.def.name}]`;
1761
- },
1818
+ return renderer.getChildNodes(vm.elm);
1819
+ },
1820
+ get firstChild() {
1821
+ const vm = getAssociatedVM(this);
1822
+ const renderer = vm.renderer;
1823
+ if (process.env.NODE_ENV !== 'production') {
1824
+ warnIfInvokedDuringConstruction(vm, 'firstChild');
1825
+ }
1826
+ return renderer.getFirstChild(vm.elm);
1827
+ },
1828
+ get firstElementChild() {
1829
+ const vm = getAssociatedVM(this);
1830
+ const renderer = vm.renderer;
1831
+ if (process.env.NODE_ENV !== 'production') {
1832
+ warnIfInvokedDuringConstruction(vm, 'firstElementChild');
1833
+ }
1834
+ return renderer.getFirstElementChild(vm.elm);
1835
+ },
1836
+ get lastChild() {
1837
+ const vm = getAssociatedVM(this);
1838
+ const renderer = vm.renderer;
1839
+ if (process.env.NODE_ENV !== 'production') {
1840
+ warnIfInvokedDuringConstruction(vm, 'lastChild');
1841
+ }
1842
+ return renderer.getLastChild(vm.elm);
1843
+ },
1844
+ get lastElementChild() {
1845
+ const vm = getAssociatedVM(this);
1846
+ const renderer = vm.renderer;
1847
+ if (process.env.NODE_ENV !== 'production') {
1848
+ warnIfInvokedDuringConstruction(vm, 'lastElementChild');
1849
+ }
1850
+ return renderer.getLastElementChild(vm.elm);
1851
+ },
1852
+ render() {
1853
+ const vm = getAssociatedVM(this);
1854
+ return vm.def.template;
1855
+ },
1856
+ toString() {
1857
+ const vm = getAssociatedVM(this);
1858
+ return `[object ${vm.def.name}]`;
1859
+ }
1762
1860
  };
1763
1861
  const queryAndChildGetterDescriptors = shared.create(null);
1764
- const queryMethods = [
1765
- 'getElementsByClassName',
1766
- 'getElementsByTagName',
1767
- 'querySelector',
1768
- 'querySelectorAll',
1769
- ];
1862
+ const queryMethods = ['getElementsByClassName', 'getElementsByTagName', 'querySelector', 'querySelectorAll'];
1770
1863
  // Generic passthrough for query APIs on HTMLElement to the relevant Renderer APIs
1771
1864
  for (const queryMethod of queryMethods) {
1772
- queryAndChildGetterDescriptors[queryMethod] = {
1773
- value(arg) {
1774
- const vm = getAssociatedVM(this);
1775
- const { elm, renderer } = vm;
1776
- if (process.env.NODE_ENV !== 'production') {
1777
- warnIfInvokedDuringConstruction(vm, `${queryMethod}()`);
1778
- }
1779
- return renderer[queryMethod](elm, arg);
1780
- },
1781
- configurable: true,
1782
- enumerable: true,
1783
- writable: true,
1784
- };
1865
+ queryAndChildGetterDescriptors[queryMethod] = {
1866
+ value(arg) {
1867
+ const vm = getAssociatedVM(this);
1868
+ const {
1869
+ elm,
1870
+ renderer
1871
+ } = vm;
1872
+ if (process.env.NODE_ENV !== 'production') {
1873
+ warnIfInvokedDuringConstruction(vm, `${queryMethod}()`);
1874
+ }
1875
+ return renderer[queryMethod](elm, arg);
1876
+ },
1877
+ configurable: true,
1878
+ enumerable: true,
1879
+ writable: true
1880
+ };
1785
1881
  }
1786
1882
  shared.defineProperties(LightningElement.prototype, queryAndChildGetterDescriptors);
1787
1883
  const lightningBasedDescriptors = shared.create(null);
1788
1884
  for (const propName in HTMLElementOriginalDescriptors) {
1789
- lightningBasedDescriptors[propName] = createBridgeToElementDescriptor(propName, HTMLElementOriginalDescriptors[propName]);
1885
+ lightningBasedDescriptors[propName] = createBridgeToElementDescriptor(propName, HTMLElementOriginalDescriptors[propName]);
1790
1886
  }
1791
1887
  shared.defineProperties(LightningElement.prototype, lightningBasedDescriptors);
1888
+ function applyAriaReflectionToLightningElement() {
1889
+ // If ARIA reflection is not applied globally to Element.prototype, or if we are running server-side,
1890
+ // apply it to LightningElement.prototype.
1891
+ // This allows `this.aria*` property accessors to work from inside a component, and to reflect `aria-*` attrs.
1892
+ ariaReflection.applyAriaReflection(LightningElement.prototype);
1893
+ }
1894
+ // The reason for this odd if/else branching is limitations in @lwc/features:
1895
+ // https://github.com/salesforce/lwc/blob/master/packages/%40lwc/features/README.md#only-works-with-if-statements
1896
+ if (features.lwcRuntimeFlags.DISABLE_ARIA_REFLECTION_POLYFILL) {
1897
+ applyAriaReflectionToLightningElement();
1898
+ } else if (!process.env.IS_BROWSER) {
1899
+ applyAriaReflectionToLightningElement();
1900
+ }
1792
1901
  shared.defineProperty(LightningElement, 'CustomElementConstructor', {
1793
- get() {
1794
- // If required, a runtime-specific implementation must be defined.
1795
- throw new ReferenceError('The current runtime does not support CustomElementConstructor.');
1796
- },
1797
- configurable: true,
1902
+ get() {
1903
+ // If required, a runtime-specific implementation must be defined.
1904
+ throw new ReferenceError('The current runtime does not support CustomElementConstructor.');
1905
+ },
1906
+ configurable: true
1798
1907
  });
1799
1908
  if (process.env.NODE_ENV !== 'production') {
1800
- patchLightningElementPrototypeWithRestrictions(LightningElement.prototype);
1909
+ patchLightningElementPrototypeWithRestrictions(LightningElement.prototype);
1801
1910
  }
1802
1911
 
1803
1912
  function createObservedFieldPropertyDescriptor(key) {
@@ -2257,138 +2366,161 @@ function sanitizeAttribute(tagName, namespaceUri, attrName, attrValue) {
2257
2366
  const cachedGetterByKey = shared.create(null);
2258
2367
  const cachedSetterByKey = shared.create(null);
2259
2368
  function createGetter(key) {
2260
- let fn = cachedGetterByKey[key];
2261
- if (shared.isUndefined(fn)) {
2262
- fn = cachedGetterByKey[key] = function () {
2263
- const vm = getAssociatedVM(this);
2264
- const { getHook } = vm;
2265
- return getHook(vm.component, key);
2266
- };
2267
- }
2268
- return fn;
2369
+ let fn = cachedGetterByKey[key];
2370
+ if (shared.isUndefined(fn)) {
2371
+ fn = cachedGetterByKey[key] = function () {
2372
+ const vm = getAssociatedVM(this);
2373
+ const {
2374
+ getHook
2375
+ } = vm;
2376
+ return getHook(vm.component, key);
2377
+ };
2378
+ }
2379
+ return fn;
2269
2380
  }
2270
2381
  function createSetter(key) {
2271
- let fn = cachedSetterByKey[key];
2272
- if (shared.isUndefined(fn)) {
2273
- fn = cachedSetterByKey[key] = function (newValue) {
2274
- const vm = getAssociatedVM(this);
2275
- const { setHook } = vm;
2276
- newValue = getReadOnlyProxy(newValue);
2277
- setHook(vm.component, key, newValue);
2278
- };
2279
- }
2280
- return fn;
2382
+ let fn = cachedSetterByKey[key];
2383
+ if (shared.isUndefined(fn)) {
2384
+ fn = cachedSetterByKey[key] = function (newValue) {
2385
+ const vm = getAssociatedVM(this);
2386
+ const {
2387
+ setHook
2388
+ } = vm;
2389
+ newValue = getReadOnlyProxy(newValue);
2390
+ setHook(vm.component, key, newValue);
2391
+ };
2392
+ }
2393
+ return fn;
2281
2394
  }
2282
2395
  function createMethodCaller(methodName) {
2283
- return function () {
2284
- const vm = getAssociatedVM(this);
2285
- const { callHook, component } = vm;
2286
- const fn = component[methodName];
2287
- return callHook(vm.component, fn, shared.ArraySlice.call(arguments));
2288
- };
2396
+ return function () {
2397
+ const vm = getAssociatedVM(this);
2398
+ const {
2399
+ callHook,
2400
+ component
2401
+ } = vm;
2402
+ const fn = component[methodName];
2403
+ return callHook(vm.component, fn, shared.ArraySlice.call(arguments));
2404
+ };
2289
2405
  }
2290
2406
  function createAttributeChangedCallback(attributeToPropMap, superAttributeChangedCallback) {
2291
- return function attributeChangedCallback(attrName, oldValue, newValue) {
2292
- if (oldValue === newValue) {
2293
- // Ignore same values.
2294
- return;
2295
- }
2296
- const propName = attributeToPropMap[attrName];
2297
- if (shared.isUndefined(propName)) {
2298
- if (!shared.isUndefined(superAttributeChangedCallback)) {
2299
- // delegate unknown attributes to the super.
2300
- // Typescript does not like it when you treat the `arguments` object as an array
2301
- // @ts-ignore type-mismatch
2302
- superAttributeChangedCallback.apply(this, arguments);
2303
- }
2304
- return;
2305
- }
2306
- if (!isAttributeLocked(this, attrName)) {
2307
- // Ignore changes triggered by the engine itself during:
2308
- // * diffing when public props are attempting to reflect to the DOM
2309
- // * component via `this.setAttribute()`, should never update the prop
2310
- // Both cases, the setAttribute call is always wrapped by the unlocking of the
2311
- // attribute to be changed
2312
- return;
2313
- }
2314
- // Reflect attribute change to the corresponding property when changed from outside.
2315
- this[propName] = newValue;
2316
- };
2317
- }
2318
- function HTMLBridgeElementFactory(SuperClass, props, methods) {
2319
- let HTMLBridgeElement;
2320
- /**
2321
- * Modern browsers will have all Native Constructors as regular Classes
2322
- * and must be instantiated with the new keyword. In older browsers,
2323
- * specifically IE11, those are objects with a prototype property defined,
2324
- * since they are not supposed to be extended or instantiated with the
2325
- * new keyword. This forking logic supports both cases, specifically because
2326
- * wc.ts relies on the construction path of the bridges to create new
2327
- * fully qualifying web components.
2328
- */
2329
- if (shared.isFunction(SuperClass)) {
2330
- HTMLBridgeElement = class extends SuperClass {
2331
- };
2332
- }
2333
- else {
2334
- HTMLBridgeElement = function () {
2335
- // Bridge classes are not supposed to be instantiated directly in
2336
- // browsers that do not support web components.
2337
- throw new TypeError('Illegal constructor');
2338
- };
2339
- // prototype inheritance dance
2340
- shared.setPrototypeOf(HTMLBridgeElement, SuperClass);
2341
- shared.setPrototypeOf(HTMLBridgeElement.prototype, SuperClass.prototype);
2342
- shared.defineProperty(HTMLBridgeElement.prototype, 'constructor', {
2343
- writable: true,
2344
- configurable: true,
2345
- value: HTMLBridgeElement,
2346
- });
2407
+ return function attributeChangedCallback(attrName, oldValue, newValue) {
2408
+ if (oldValue === newValue) {
2409
+ // Ignore same values.
2410
+ return;
2347
2411
  }
2348
- // generating the hash table for attributes to avoid duplicate fields and facilitate validation
2349
- // and false positives in case of inheritance.
2350
- const attributeToPropMap = shared.create(null);
2351
- const { attributeChangedCallback: superAttributeChangedCallback } = SuperClass.prototype;
2352
- const { observedAttributes: superObservedAttributes = [] } = SuperClass;
2353
- const descriptors = shared.create(null);
2354
- // expose getters and setters for each public props on the new Element Bridge
2355
- for (let i = 0, len = props.length; i < len; i += 1) {
2356
- const propName = props[i];
2357
- attributeToPropMap[shared.htmlPropertyToAttribute(propName)] = propName;
2358
- descriptors[propName] = {
2359
- get: createGetter(propName),
2360
- set: createSetter(propName),
2361
- enumerable: true,
2362
- configurable: true,
2363
- };
2412
+ const propName = attributeToPropMap[attrName];
2413
+ if (shared.isUndefined(propName)) {
2414
+ if (!shared.isUndefined(superAttributeChangedCallback)) {
2415
+ // delegate unknown attributes to the super.
2416
+ // Typescript does not like it when you treat the `arguments` object as an array
2417
+ // @ts-ignore type-mismatch
2418
+ superAttributeChangedCallback.apply(this, arguments);
2419
+ }
2420
+ return;
2364
2421
  }
2365
- // expose public methods as props on the new Element Bridge
2366
- for (let i = 0, len = methods.length; i < len; i += 1) {
2367
- const methodName = methods[i];
2368
- descriptors[methodName] = {
2369
- value: createMethodCaller(methodName),
2370
- writable: true,
2371
- configurable: true,
2372
- };
2422
+ if (!isAttributeLocked(this, attrName)) {
2423
+ // Ignore changes triggered by the engine itself during:
2424
+ // * diffing when public props are attempting to reflect to the DOM
2425
+ // * component via `this.setAttribute()`, should never update the prop
2426
+ // Both cases, the setAttribute call is always wrapped by the unlocking of the
2427
+ // attribute to be changed
2428
+ return;
2373
2429
  }
2374
- // creating a new attributeChangedCallback per bridge because they are bound to the corresponding
2375
- // map of attributes to props. We do this after all other props and methods to avoid the possibility
2376
- // of getting overrule by a class declaration in user-land, and we make it non-writable, non-configurable
2377
- // to preserve this definition.
2378
- descriptors.attributeChangedCallback = {
2379
- value: createAttributeChangedCallback(attributeToPropMap, superAttributeChangedCallback),
2430
+ // Reflect attribute change to the corresponding property when changed from outside.
2431
+ this[propName] = newValue;
2432
+ };
2433
+ }
2434
+ function HTMLBridgeElementFactory(SuperClass, props, methods) {
2435
+ let HTMLBridgeElement;
2436
+ /**
2437
+ * Modern browsers will have all Native Constructors as regular Classes
2438
+ * and must be instantiated with the new keyword. In older browsers,
2439
+ * specifically IE11, those are objects with a prototype property defined,
2440
+ * since they are not supposed to be extended or instantiated with the
2441
+ * new keyword. This forking logic supports both cases, specifically because
2442
+ * wc.ts relies on the construction path of the bridges to create new
2443
+ * fully qualifying web components.
2444
+ */
2445
+ if (shared.isFunction(SuperClass)) {
2446
+ HTMLBridgeElement = class extends SuperClass {};
2447
+ } else {
2448
+ HTMLBridgeElement = function () {
2449
+ // Bridge classes are not supposed to be instantiated directly in
2450
+ // browsers that do not support web components.
2451
+ throw new TypeError('Illegal constructor');
2380
2452
  };
2381
- // Specify attributes for which we want to reflect changes back to their corresponding
2382
- // properties via attributeChangedCallback.
2383
- shared.defineProperty(HTMLBridgeElement, 'observedAttributes', {
2384
- get() {
2385
- return [...superObservedAttributes, ...shared.keys(attributeToPropMap)];
2386
- },
2453
+ // prototype inheritance dance
2454
+ shared.setPrototypeOf(HTMLBridgeElement, SuperClass);
2455
+ shared.setPrototypeOf(HTMLBridgeElement.prototype, SuperClass.prototype);
2456
+ shared.defineProperty(HTMLBridgeElement.prototype, 'constructor', {
2457
+ writable: true,
2458
+ configurable: true,
2459
+ value: HTMLBridgeElement
2387
2460
  });
2388
- shared.defineProperties(HTMLBridgeElement.prototype, descriptors);
2389
- return HTMLBridgeElement;
2461
+ }
2462
+ // generating the hash table for attributes to avoid duplicate fields and facilitate validation
2463
+ // and false positives in case of inheritance.
2464
+ const attributeToPropMap = shared.create(null);
2465
+ const {
2466
+ attributeChangedCallback: superAttributeChangedCallback
2467
+ } = SuperClass.prototype;
2468
+ const {
2469
+ observedAttributes: superObservedAttributes = []
2470
+ } = SuperClass;
2471
+ const descriptors = shared.create(null);
2472
+ // expose getters and setters for each public props on the new Element Bridge
2473
+ for (let i = 0, len = props.length; i < len; i += 1) {
2474
+ const propName = props[i];
2475
+ attributeToPropMap[shared.htmlPropertyToAttribute(propName)] = propName;
2476
+ descriptors[propName] = {
2477
+ get: createGetter(propName),
2478
+ set: createSetter(propName),
2479
+ enumerable: true,
2480
+ configurable: true
2481
+ };
2482
+ }
2483
+ // expose public methods as props on the new Element Bridge
2484
+ for (let i = 0, len = methods.length; i < len; i += 1) {
2485
+ const methodName = methods[i];
2486
+ descriptors[methodName] = {
2487
+ value: createMethodCaller(methodName),
2488
+ writable: true,
2489
+ configurable: true
2490
+ };
2491
+ }
2492
+ // creating a new attributeChangedCallback per bridge because they are bound to the corresponding
2493
+ // map of attributes to props. We do this after all other props and methods to avoid the possibility
2494
+ // of getting overrule by a class declaration in user-land, and we make it non-writable, non-configurable
2495
+ // to preserve this definition.
2496
+ descriptors.attributeChangedCallback = {
2497
+ value: createAttributeChangedCallback(attributeToPropMap, superAttributeChangedCallback)
2498
+ };
2499
+ // Specify attributes for which we want to reflect changes back to their corresponding
2500
+ // properties via attributeChangedCallback.
2501
+ shared.defineProperty(HTMLBridgeElement, 'observedAttributes', {
2502
+ get() {
2503
+ return [...superObservedAttributes, ...shared.keys(attributeToPropMap)];
2504
+ }
2505
+ });
2506
+ shared.defineProperties(HTMLBridgeElement.prototype, descriptors);
2507
+ return HTMLBridgeElement;
2390
2508
  }
2391
2509
  const BaseBridgeElement = HTMLBridgeElementFactory(HTMLElementConstructor, shared.getOwnPropertyNames(HTMLElementOriginalDescriptors), []);
2510
+ if (process.env.IS_BROWSER) {
2511
+ // This ARIA reflection only really makes sense in the browser. On the server, there is no `renderedCallback()`,
2512
+ // so you cannot do e.g. `this.template.querySelector('x-child').ariaBusy = 'true'`. So we don't need to expose
2513
+ // ARIA props outside the LightningElement
2514
+ if (features.lwcRuntimeFlags.DISABLE_ARIA_REFLECTION_POLYFILL) {
2515
+ // If ARIA reflection is not applied globally to Element.prototype, apply it to HTMLBridgeElement.prototype.
2516
+ // This allows `elm.aria*` property accessors to work from outside a component, and to reflect `aria-*` attrs.
2517
+ // This is especially important because the template compiler compiles aria-* attrs on components to aria* props
2518
+ //
2519
+ // Also note that we apply this to BaseBridgeElement.prototype to avoid excessively redefining property
2520
+ // accessors inside the HTMLBridgeElementFactory.
2521
+ ariaReflection.applyAriaReflection(BaseBridgeElement.prototype);
2522
+ }
2523
+ }
2392
2524
  shared.freeze(BaseBridgeElement);
2393
2525
  shared.seal(BaseBridgeElement.prototype);
2394
2526
 
@@ -2844,6 +2976,9 @@ function updateStylesheetToken(vm, template) {
2844
2976
  stylesheets: newStylesheets,
2845
2977
  stylesheetToken: newStylesheetToken
2846
2978
  } = template;
2979
+ const {
2980
+ stylesheets: newVmStylesheets
2981
+ } = vm;
2847
2982
  const isSyntheticShadow = renderMode === 1 /* RenderMode.Shadow */ && shadowMode === 1 /* ShadowMode.Synthetic */;
2848
2983
  const {
2849
2984
  hasScopedStyles
@@ -2867,7 +3002,9 @@ function updateStylesheetToken(vm, template) {
2867
3002
  }
2868
3003
  // Apply the new template styling token to the host element, if the new template has any
2869
3004
  // associated stylesheets. In the case of light DOM, also ensure there is at least one scoped stylesheet.
2870
- if (!shared.isUndefined(newStylesheets) && newStylesheets.length !== 0) {
3005
+ const hasNewStylesheets = hasStyles(newStylesheets);
3006
+ const hasNewVmStylesheets = hasStyles(newVmStylesheets);
3007
+ if (hasNewStylesheets || hasNewVmStylesheets) {
2871
3008
  newToken = newStylesheetToken;
2872
3009
  }
2873
3010
  // Set the new styling token on the host element
@@ -2939,10 +3076,17 @@ function getStylesheetsContent(vm, template) {
2939
3076
  stylesheets,
2940
3077
  stylesheetToken
2941
3078
  } = template;
3079
+ const {
3080
+ stylesheets: vmStylesheets
3081
+ } = vm;
2942
3082
  let content = [];
2943
- if (!shared.isUndefined(stylesheets) && stylesheets.length !== 0) {
3083
+ if (hasStyles(stylesheets)) {
2944
3084
  content = evaluateStylesheetsContent(stylesheets, stylesheetToken, vm);
2945
3085
  }
3086
+ // VM (component) stylesheets apply after template stylesheets
3087
+ if (hasStyles(vmStylesheets)) {
3088
+ shared.ArrayPush.apply(content, evaluateStylesheetsContent(vmStylesheets, stylesheetToken, vm));
3089
+ }
2946
3090
  return content;
2947
3091
  }
2948
3092
  // It might be worth caching this to avoid doing the lookup repeatedly, but
@@ -2980,10 +3124,13 @@ function getStylesheetTokenHost(vnode) {
2980
3124
  const {
2981
3125
  template
2982
3126
  } = getComponentInternalDef(vnode.ctor);
3127
+ const {
3128
+ vm
3129
+ } = vnode;
2983
3130
  const {
2984
3131
  stylesheetToken
2985
3132
  } = template;
2986
- return !shared.isUndefined(stylesheetToken) && computeHasScopedStyles(template) ? makeHostToken(stylesheetToken) : null;
3133
+ return !shared.isUndefined(stylesheetToken) && computeHasScopedStyles(template, vm) ? makeHostToken(stylesheetToken) : null;
2987
3134
  }
2988
3135
  function getNearestNativeShadowComponent(vm) {
2989
3136
  const owner = getNearestShadowComponent(vm);
@@ -3600,6 +3747,28 @@ function patchCustomElement(n1, n2, parent, renderer) {
3600
3747
  // in fallback mode, the allocation will always set children to
3601
3748
  // empty and delegate the real allocation to the slot elements
3602
3749
  allocateChildren(n2, vm);
3750
+ // Solves an edge case with slotted VFragments in native shadow mode.
3751
+ //
3752
+ // During allocation, in native shadow, slotted VFragment nodes are flattened and their text delimiters are removed
3753
+ // to avoid interfering with native slot behavior. When this happens, if any of the fragments
3754
+ // were not stable, the children must go through the dynamic diffing algo.
3755
+ //
3756
+ // If the new children (n2.children) contain no VFragments, but the previous children (n1.children) were dynamic,
3757
+ // the new nodes must be marked dynamic so that all nodes are properly updated. The only indicator that the new
3758
+ // nodes need to be dynamic comes from the previous children, so we check that to determine whether we need to
3759
+ // mark the new children dynamic.
3760
+ //
3761
+ // Example:
3762
+ // n1.children: [div, VFragment('', div, null, ''), div] => [div, div, null, div]; // marked dynamic
3763
+ // n2.children: [div, null, div] => [div, null, div] // marked ???
3764
+ const {
3765
+ shadowMode,
3766
+ renderMode
3767
+ } = vm;
3768
+ if (shadowMode == 0 /* ShadowMode.Native */ && renderMode !== 0 /* RenderMode.Light */ && hasDynamicChildren(n1.children)) {
3769
+ // No-op if children has already been marked dynamic by 'allocateChildren()'.
3770
+ markAsDynamicChildren(n2.children);
3771
+ }
3603
3772
  }
3604
3773
  // in fallback mode, the children will be always empty, so, nothing
3605
3774
  // will happen, but in native, it does allocate the light dom
@@ -3792,7 +3961,6 @@ function allocateChildren(vnode, vm) {
3792
3961
  //
3793
3962
  // In case #2, we will always get a fresh VCustomElement.
3794
3963
  const children = vnode.aChildren || vnode.children;
3795
- vm.aChildren = children;
3796
3964
  const {
3797
3965
  renderMode,
3798
3966
  shadowMode
@@ -3805,15 +3973,61 @@ function allocateChildren(vnode, vm) {
3805
3973
  logError(`Invalid usage of 'lwc:slot-data' on ${getComponentTag(vm)} tag. Scoped slot content can only be passed to a light dom child.`);
3806
3974
  }
3807
3975
  }
3976
+ // If any of the children being allocated are VFragments, we remove the text delimiters and flatten all immediate
3977
+ // children VFragments to avoid them interfering with default slot behavior.
3978
+ const allocatedChildren = flattenFragmentsInChildren(children);
3979
+ vnode.children = allocatedChildren;
3980
+ vm.aChildren = allocatedChildren;
3808
3981
  if (shadowMode === 1 /* ShadowMode.Synthetic */ || renderMode === 0 /* RenderMode.Light */) {
3809
3982
  // slow path
3810
- allocateInSlot(vm, children, vnode.owner);
3983
+ allocateInSlot(vm, allocatedChildren, vnode.owner);
3811
3984
  // save the allocated children in case this vnode is reused.
3812
- vnode.aChildren = children;
3985
+ vnode.aChildren = allocatedChildren;
3813
3986
  // every child vnode is now allocated, and the host should receive none directly, it receives them via the shadow!
3814
3987
  vnode.children = EmptyArray;
3815
3988
  }
3816
3989
  }
3990
+ /**
3991
+ * Flattens the contents of all VFragments in an array of VNodes, removes the text delimiters on those VFragments, and
3992
+ * marks the resulting children array as dynamic. Uses a stack (array) to iteratively traverse the nested VFragments
3993
+ * and avoid the perf overhead of creating/destroying throwaway arrays/objects in a recursive approach.
3994
+ *
3995
+ * With the delimiters removed, the contents are marked dynamic so they are diffed correctly.
3996
+ *
3997
+ * This function is used for slotted VFragments to avoid the text delimiters interfering with slotting functionality.
3998
+ */
3999
+ function flattenFragmentsInChildren(children) {
4000
+ const flattenedChildren = [];
4001
+ // Initialize our stack with the direct children of the custom component and check whether we have a VFragment.
4002
+ // If no VFragment is found in children, we don't need to traverse anything or mark the children dynamic and can return early.
4003
+ const nodeStack = [];
4004
+ let fragmentFound = false;
4005
+ for (let i = children.length - 1; i > -1; i -= 1) {
4006
+ const child = children[i];
4007
+ shared.ArrayPush.call(nodeStack, child);
4008
+ fragmentFound = fragmentFound || !!(child && isVFragment(child));
4009
+ }
4010
+ if (!fragmentFound) {
4011
+ return children;
4012
+ }
4013
+ let currentNode;
4014
+ while (!shared.isUndefined(currentNode = shared.ArrayPop.call(nodeStack))) {
4015
+ if (!shared.isNull(currentNode) && isVFragment(currentNode)) {
4016
+ const fChildren = currentNode.children;
4017
+ // Ignore the start and end text node delimiters
4018
+ for (let i = fChildren.length - 2; i > 0; i -= 1) {
4019
+ shared.ArrayPush.call(nodeStack, fChildren[i]);
4020
+ }
4021
+ } else {
4022
+ shared.ArrayPush.call(flattenedChildren, currentNode);
4023
+ }
4024
+ }
4025
+ // We always mark the children as dynamic because nothing generates stable VFragments yet.
4026
+ // If/when stable VFragments are generated by the compiler, this code should be updated to
4027
+ // not mark dynamic if all flattened VFragments were stable.
4028
+ markAsDynamicChildren(flattenedChildren);
4029
+ return flattenedChildren;
4030
+ }
3817
4031
  function createViewModelHook(elm, vnode, renderer) {
3818
4032
  let vm = getAssociatedVMIfPresent(elm);
3819
4033
  // There is a possibility that a custom element is registered under tagName, in which case, the
@@ -3838,22 +4052,20 @@ function createViewModelHook(elm, vnode, renderer) {
3838
4052
  }
3839
4053
  return vm;
3840
4054
  }
3841
- /**
3842
- * Collects all slots into a SlotSet, traversing through VFragment Nodes
3843
- */
3844
- function collectSlots(vm, children, cmpSlotsMapping) {
4055
+ function allocateInSlot(vm, children, owner) {
3845
4056
  var _a, _b;
4057
+ const {
4058
+ cmpSlots: {
4059
+ slotAssignments: oldSlotsMapping
4060
+ }
4061
+ } = vm;
4062
+ const cmpSlotsMapping = shared.create(null);
4063
+ // Collect all slots into cmpSlotsMapping
3846
4064
  for (let i = 0, len = children.length; i < len; i += 1) {
3847
4065
  const vnode = children[i];
3848
4066
  if (shared.isNull(vnode)) {
3849
4067
  continue;
3850
4068
  }
3851
- // Dive further iff the content is wrapped in a VFragment
3852
- if (isVFragment(vnode)) {
3853
- // Remove the text delimiter nodes to avoid overriding default slot content
3854
- collectSlots(vm, vnode.children.slice(1, -1), cmpSlotsMapping);
3855
- continue;
3856
- }
3857
4069
  let slotName = '';
3858
4070
  if (isVBaseElement(vnode)) {
3859
4071
  slotName = (_b = (_a = vnode.data.attrs) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : '';
@@ -3863,15 +4075,6 @@ function collectSlots(vm, children, cmpSlotsMapping) {
3863
4075
  const vnodes = cmpSlotsMapping[slotName] = cmpSlotsMapping[slotName] || [];
3864
4076
  shared.ArrayPush.call(vnodes, vnode);
3865
4077
  }
3866
- }
3867
- function allocateInSlot(vm, children, owner) {
3868
- const {
3869
- cmpSlots: {
3870
- slotAssignments: oldSlotsMapping
3871
- }
3872
- } = vm;
3873
- const cmpSlotsMapping = shared.create(null);
3874
- collectSlots(vm, children, cmpSlotsMapping);
3875
4078
  vm.cmpSlots = {
3876
4079
  owner,
3877
4080
  slotAssignments: cmpSlotsMapping
@@ -3902,14 +4105,14 @@ function allocateInSlot(vm, children, owner) {
3902
4105
  }
3903
4106
  }
3904
4107
  // Using a WeakMap instead of a WeakSet because this one works in IE11 :(
3905
- const FromIteration = new WeakMap();
3906
- // dynamic children means it was generated by an iteration
3907
- // in a template, and will require a more complex diffing algo.
4108
+ const DynamicChildren = new WeakMap();
4109
+ // dynamic children means it was either generated by an iteration in a template
4110
+ // or part of an unstable fragment, and will require a more complex diffing algo.
3908
4111
  function markAsDynamicChildren(children) {
3909
- FromIteration.set(children, 1);
4112
+ DynamicChildren.set(children, 1);
3910
4113
  }
3911
4114
  function hasDynamicChildren(children) {
3912
- return FromIteration.has(children);
4115
+ return DynamicChildren.has(children);
3913
4116
  }
3914
4117
  function createKeyToOldIdx(children, beginIdx, endIdx) {
3915
4118
  const map = {};
@@ -4775,7 +4978,7 @@ function evaluateTemplate(vm, html) {
4775
4978
  // Create a brand new template cache for the swapped templated.
4776
4979
  context.tplCache = shared.create(null);
4777
4980
  // Set the computeHasScopedStyles property in the context, to avoid recomputing it repeatedly.
4778
- context.hasScopedStyles = computeHasScopedStyles(html);
4981
+ context.hasScopedStyles = computeHasScopedStyles(html, vm);
4779
4982
  // Update the scoping token on the host element.
4780
4983
  updateStylesheetToken(vm, html);
4781
4984
  // Evaluate, create stylesheet and cache the produced VNode for future
@@ -4818,9 +5021,8 @@ function evaluateTemplate(vm, html) {
4818
5021
  }
4819
5022
  return vnodes;
4820
5023
  }
4821
- function computeHasScopedStyles(template) {
4822
- const { stylesheets } = template;
4823
- if (!shared.isUndefined(stylesheets)) {
5024
+ function computeHasScopedStylesInStylesheets(stylesheets) {
5025
+ if (hasStyles(stylesheets)) {
4824
5026
  for (let i = 0; i < stylesheets.length; i++) {
4825
5027
  if (shared.isTrue(stylesheets[i][shared.KEY__SCOPED_CSS])) {
4826
5028
  return true;
@@ -4829,6 +5031,15 @@ function computeHasScopedStyles(template) {
4829
5031
  }
4830
5032
  return false;
4831
5033
  }
5034
+ function computeHasScopedStyles(template, vm) {
5035
+ const { stylesheets } = template;
5036
+ const vmStylesheets = !shared.isUndefined(vm) ? vm.stylesheets : null;
5037
+ return (computeHasScopedStylesInStylesheets(stylesheets) ||
5038
+ computeHasScopedStylesInStylesheets(vmStylesheets));
5039
+ }
5040
+ function hasStyles(stylesheets) {
5041
+ return !shared.isUndefined(stylesheets) && !shared.isNull(stylesheets) && stylesheets.length > 0;
5042
+ }
4832
5043
 
4833
5044
  /*
4834
5045
  * Copyright (c) 2018, salesforce.com, inc.
@@ -5144,6 +5355,7 @@ function createVM(elm, ctor, renderer, options) {
5144
5355
  // Properties set right after VM creation.
5145
5356
  tro: null,
5146
5357
  shadowMode: null,
5358
+ stylesheets: null,
5147
5359
  // Properties set by the LightningElement constructor.
5148
5360
  component: null,
5149
5361
  shadowRoot: null,
@@ -5156,6 +5368,7 @@ function createVM(elm, ctor, renderer, options) {
5156
5368
  if (process.env.NODE_ENV !== 'production') {
5157
5369
  vm.debugInfo = shared.create(null);
5158
5370
  }
5371
+ vm.stylesheets = computeStylesheets(vm, def.ctor);
5159
5372
  vm.shadowMode = computeShadowMode(vm, renderer);
5160
5373
  vm.tro = getTemplateReactiveObserver(vm);
5161
5374
  if (process.env.NODE_ENV !== 'production') {
@@ -5174,6 +5387,42 @@ function createVM(elm, ctor, renderer, options) {
5174
5387
  }
5175
5388
  return vm;
5176
5389
  }
5390
+ function validateComponentStylesheets(vm, stylesheets) {
5391
+ let valid = true;
5392
+ const validate = arrayOrStylesheet => {
5393
+ if (shared.isArray(arrayOrStylesheet)) {
5394
+ for (let i = 0; i < arrayOrStylesheet.length; i++) {
5395
+ validate(arrayOrStylesheet[i]);
5396
+ }
5397
+ } else if (!shared.isFunction(arrayOrStylesheet)) {
5398
+ // function assumed to be a stylesheet factory
5399
+ valid = false;
5400
+ }
5401
+ };
5402
+ if (!shared.isArray(stylesheets)) {
5403
+ valid = false;
5404
+ } else {
5405
+ validate(stylesheets);
5406
+ }
5407
+ return valid;
5408
+ }
5409
+ // Validate and flatten any stylesheets defined as `static stylesheets`
5410
+ function computeStylesheets(vm, ctor) {
5411
+ if (features.lwcRuntimeFlags.ENABLE_PROGRAMMATIC_STYLESHEETS) {
5412
+ const {
5413
+ stylesheets
5414
+ } = ctor;
5415
+ if (!shared.isUndefined(stylesheets)) {
5416
+ const valid = validateComponentStylesheets(vm, stylesheets);
5417
+ if (valid) {
5418
+ return flattenStylesheets(stylesheets);
5419
+ } else if (process.env.NODE_ENV !== 'production') {
5420
+ logError(`static stylesheets must be an array of CSS stylesheets. Found invalid stylesheets on <${vm.tagName}>`, vm);
5421
+ }
5422
+ }
5423
+ }
5424
+ return null;
5425
+ }
5177
5426
  function computeShadowMode(vm, renderer) {
5178
5427
  const {
5179
5428
  def
@@ -6533,4 +6782,4 @@ exports.swapTemplate = swapTemplate;
6533
6782
  exports.track = track;
6534
6783
  exports.unwrap = unwrap;
6535
6784
  exports.wire = wire;
6536
- /* version: 2.32.1 */
6785
+ /* version: 2.34.0 */