bublejs / buble

https://buble.surge.sh
MIT License
871 stars 67 forks source link

[bug] variable name mangling not working as expected with destructuring in for..of #182

Open trusktr opened 5 years ago

trusktr commented 5 years ago

As described in https://github.com/Rich-Harris/buble/issues/181#issuecomment-458631107, the first way doesn't work, the second way does work.

In practice, I have this code:

        for (const [target, weights] of Array.from(weightsPerTarget)) {
            console.log( target, weights )
            debugger

which I see is compiled to the following erroneous output:

    for (var i$4 = 0, list$1 = _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weightsPerTarget); i$4 < list$1.length; i$4 += 1) {
      var ref = list$1[i$4];
      var target = ref[0];
      var weights = ref[1]; // HERE! "weights" should be "weights$1"

      console.log(target, weights$1); // and it is used here
      debugger;

so if I change my source code to

        for (const entry of Array.from(weightsPerTarget)) {
            const [target, weights] = entry
            console.log( target, weights )
            debugger

then I get an output that works:

    for (var i$4 = 0, list$1 = _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weightsPerTarget); i$4 < list$1.length; i$4 += 1) {
      var entry = list$1[i$4];

      var target = entry[0];
      var weights$1 = entry[1];
      console.log(target, weights$1);
      debugger;

I'm on Buble 0.19.0, and haven't updated yet, so I'm not yet sure if this still happens in a newer version.

trusktr commented 5 years ago

I am trying to investigate to see if it's Babel's fault or Buble's fault (or maybe the fault of both used together), as I'm using both to selectively transpile different features.

The line in question is here: https://github.com/trusktr/infamous/blob/master/src/core/Utility.js#L51

trusktr commented 5 years ago

Updated to buble 0.19.6, and have the same issue. Investigating using only Buble with no Babel next...

trusktr commented 5 years ago

I am running the Babel transform first, and I can confirm that after Babel runs, the code that is being sent to Buble is the following:

    for (const [target, weights] of _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weightsPerTarget)) {
      console.log(target, weights);
      debugger;

So it looks like Buble is the one that is not able to handle the destructuring in the for..of.

trusktr commented 5 years ago

Alright, I cloned the buble-demo, and added the transforms: {dangerousForOf: true} option, and pasted the working Babel code in there and was able to reproduce the problem.

So, here's valid working input (produced by Babel and then I copied and pasted it into the left side of the buble-demo site):

__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "epsilon", function() { return epsilon; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "applyCSSLabel", function() { return applyCSSLabel; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "observeChildren", function() { return observeChildren; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getShadowRootVersion", function() { return getShadowRootVersion; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasShadowDomV0", function() { return hasShadowDomV0; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasShadowDomV1", function() { return hasShadowDomV1; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAncestorShadowRoot", function() { return getAncestorShadowRoot; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isInstanceof", function() { return isInstanceof; });
/* harmony import */ var _babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/core-js/symbol/has-instance */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/symbol/has-instance.js");
/* harmony import */ var _babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/core-js/array/from */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/array/from.js");
/* harmony import */ var _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/core-js/map */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/map.js");
/* harmony import */ var _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2__);

function epsilon(value) {
  return Math.abs(value) < 0.000001 ? 0 : value;
}

function applyCSSLabel(value, label) {
  if (value === 0) {
    return '0px';
  } else if (label === '%') {
    return value * 100 + '%';
  } else if (label === 'px') {
    return value + 'px';
  }
}

function observeChildren(target, onConnect, onDisconnect, skipTextNodes) {
  // TODO this Map is never cleaned, leaks memory. Maybe use WeakMap
  const childObserver = createChildObserver(onConnect, onDisconnect, skipTextNodes);
  childObserver.observe(target, {
    childList: true
  });
  return childObserver;
} // NOTE: If a child is disconnected then connected to the same parent in the
// same turn, then the onConnect and onDisconnect callbacks won't be called
// because the DOM tree will be back in the exact state as before (this is
// possible thanks to the logic associated with weightsPerTarget).

function createChildObserver(onConnect, onDisconnect, skipTextNodes = false) {
  return new MutationObserver(changes => {
    const weightsPerTarget = new _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default.a(); // We're just counting how many times each child node was added and
    // removed from the parent we're observing.

    for (let i = 0, l = changes.length; i < l; i += 1) {
      const change = changes[i];
      if (change.type != 'childList') continue;
      if (!weightsPerTarget.has(change.target)) weightsPerTarget.set(change.target, new _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default.a());
      const weights = weightsPerTarget.get(change.target);
      const {
        addedNodes
      } = change;

      for (let l = addedNodes.length, i = 0; i < l; i += 1) weights.set(addedNodes[i], (weights.get(addedNodes[i]) || 0) + 1);

      const {
        removedNodes
      } = change;

      for (let l = removedNodes.length, i = 0; i < l; i += 1) weights.set(removedNodes[i], (weights.get(removedNodes[i]) || 0) - 1);
    }

    for (const [target, weights] of _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weightsPerTarget)) {
      console.log(target, weights);
      debugger;

      for (const entry of _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weights)) {
        const [node, weight] = entry;
        if (skipTextNodes && (node instanceof Text || node instanceof Comment)) continue; // If the number of times a child was added is greater than the
        // number of times it was removed, then the net result is that
        // it was added, so we call onConnect just once.

        if (weight > 0 && typeof onConnect == 'function') onConnect.call(target, node); // If the number of times a child was added is less than the
        // number of times it was removed, then the net result is that
        // it was removed, so we call onDisconnect just once.
        else if (weight < 0 && typeof onDisconnect == 'function') onDisconnect.call(target, node); // If the number of times a child was added is equal to the
        // number of times it was removed, then it was essentially left
        // in place, so we don't call anything.
      }
    }
  });
}

const hasShadowDomV0 = typeof Element.prototype.createShadowRoot == 'function' && typeof HTMLContentElement == 'function' ? true : false;
const hasShadowDomV1 = typeof Element.prototype.attachShadow == 'function' && typeof HTMLSlotElement == 'function' ? true : false;

function getShadowRootVersion(shadowRoot) {
  console.log('getShadowRootVersion');
  if (!shadowRoot) return null;
  const slot = document.createElement('slot');
  shadowRoot.appendChild(slot);
  slot.appendChild(document.createElement('div'));
  const assignedNodes = slot.assignedNodes({
    flatten: true
  });
  slot.remove();
  console.log('hmm', assignedNodes.length, assignedNodes.length > 0 ? 'v1' : 'v0');
  return assignedNodes.length > 0 ? 'v1' : 'v0';
}

function getAncestorShadowRoot(node) {
  let current = node;

  while (current && !(current instanceof ShadowRoot)) {
    current = current.parentNode;
  }

  return current;
} // helper function to use instead of instanceof for classes that implement the
// static Symbol.hasInstance method, because the behavior of instanceof isn't
// polyfillable.

function isInstanceof(lhs, rhs) {
  if (typeof rhs == 'function' && rhs[_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default.a]) return rhs[_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default.a](lhs);else return lhs instanceof rhs;
}

function checkIsNumberArrayString(str) {
  if (!str.match(/^\s*(((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s*,){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))))|((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+))))))\s*$/g)) throw new Error(`Attribute must be a comma- or space-separated sequence of up to three numbers, for example "1 2.5 3". Yours was "${str}".`);
}

function checkIsSizeArrayString(str) {
  if (!str.match(/^\s*(((\s*([a-zA-Z]+)\s*,){0,2}(\s*([a-zA-Z]+)))|((\s*([a-zA-Z]+)\s*){1,3}))\s*$/g)) throw new Error(`Attribute must be a comma- or space-separated sequence of up to three strings, for example "literal proportional". Yours was "${str}".`);
}

and here's the output from Buble on the right side of the buble-demo site:

__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "epsilon", function() { return epsilon; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "applyCSSLabel", function() { return applyCSSLabel; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "observeChildren", function() { return observeChildren; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getShadowRootVersion", function() { return getShadowRootVersion; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasShadowDomV0", function() { return hasShadowDomV0; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasShadowDomV1", function() { return hasShadowDomV1; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAncestorShadowRoot", function() { return getAncestorShadowRoot; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isInstanceof", function() { return isInstanceof; });
/* harmony import */ var _babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/core-js/symbol/has-instance */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/symbol/has-instance.js");
/* harmony import */ var _babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/core-js/array/from */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/array/from.js");
/* harmony import */ var _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/core-js/map */ "../trusktr+builder-js-package/node_modules/@babel/runtime/core-js/map.js");
/* harmony import */ var _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2__);

function epsilon(value) {
  return Math.abs(value) < 0.000001 ? 0 : value;
}

function applyCSSLabel(value, label) {
  if (value === 0) {
    return '0px';
  } else if (label === '%') {
    return value * 100 + '%';
  } else if (label === 'px') {
    return value + 'px';
  }
}

function observeChildren(target, onConnect, onDisconnect, skipTextNodes) {
  // TODO this Map is never cleaned, leaks memory. Maybe use WeakMap
  var childObserver = createChildObserver(onConnect, onDisconnect, skipTextNodes);
  childObserver.observe(target, {
    childList: true
  });
  return childObserver;
} // NOTE: If a child is disconnected then connected to the same parent in the
// same turn, then the onConnect and onDisconnect callbacks won't be called
// because the DOM tree will be back in the exact state as before (this is
// possible thanks to the logic associated with weightsPerTarget).

function createChildObserver(onConnect, onDisconnect, skipTextNodes) {
  if ( skipTextNodes === void 0 ) skipTextNodes = false;

  return new MutationObserver(function (changes) {
    var weightsPerTarget = new _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default.a(); // We're just counting how many times each child node was added and
    // removed from the parent we're observing.

    for (var i = 0, l = changes.length; i < l; i += 1) {
      var change = changes[i];
      if (change.type != 'childList') { continue; }
      if (!weightsPerTarget.has(change.target)) { weightsPerTarget.set(change.target, new _babel_runtime_core_js_map__WEBPACK_IMPORTED_MODULE_2___default.a()); }
      var weights = weightsPerTarget.get(change.target);
      var addedNodes = change.addedNodes;

      for (var l$1 = addedNodes.length, i$1 = 0; i$1 < l$1; i$1 += 1) { weights.set(addedNodes[i$1], (weights.get(addedNodes[i$1]) || 0) + 1); }

      var removedNodes = change.removedNodes;

      for (var l$2 = removedNodes.length, i$2 = 0; i$2 < l$2; i$2 += 1) { weights.set(removedNodes[i$2], (weights.get(removedNodes[i$2]) || 0) - 1); }
    }

    for (var i$4 = 0, list$1 = _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weightsPerTarget); i$4 < list$1.length; i$4 += 1) {
      var ref = list$1[i$4];
      var target = ref[0];
      var weights = ref[1];

      console.log(target, weights$1);
      debugger;

      for (var i$3 = 0, list = _babel_runtime_core_js_array_from__WEBPACK_IMPORTED_MODULE_1___default()(weights$1); i$3 < list.length; i$3 += 1) {
        var entry = list[i$3];

        var node = entry[0];
        var weight = entry[1];
        if (skipTextNodes && (node instanceof Text || node instanceof Comment)) { continue; } // If the number of times a child was added is greater than the
        // number of times it was removed, then the net result is that
        // it was added, so we call onConnect just once.

        if (weight > 0 && typeof onConnect == 'function') { onConnect.call(target, node); } // If the number of times a child was added is less than the
        // number of times it was removed, then the net result is that
        // it was removed, so we call onDisconnect just once.
        else if (weight < 0 && typeof onDisconnect == 'function') { onDisconnect.call(target, node); } // If the number of times a child was added is equal to the
        // number of times it was removed, then it was essentially left
        // in place, so we don't call anything.
      }
    }
  });
}

var hasShadowDomV0 = typeof Element.prototype.createShadowRoot == 'function' && typeof HTMLContentElement == 'function' ? true : false;
var hasShadowDomV1 = typeof Element.prototype.attachShadow == 'function' && typeof HTMLSlotElement == 'function' ? true : false;

function getShadowRootVersion(shadowRoot) {
  console.log('getShadowRootVersion');
  if (!shadowRoot) { return null; }
  var slot = document.createElement('slot');
  shadowRoot.appendChild(slot);
  slot.appendChild(document.createElement('div'));
  var assignedNodes = slot.assignedNodes({
    flatten: true
  });
  slot.remove();
  console.log('hmm', assignedNodes.length, assignedNodes.length > 0 ? 'v1' : 'v0');
  return assignedNodes.length > 0 ? 'v1' : 'v0';
}

function getAncestorShadowRoot(node) {
  var current = node;

  while (current && !(current instanceof ShadowRoot)) {
    current = current.parentNode;
  }

  return current;
} // helper function to use instead of instanceof for classes that implement the
// static Symbol.hasInstance method, because the behavior of instanceof isn't
// polyfillable.

function isInstanceof(lhs, rhs) {
  if (typeof rhs == 'function' && rhs[_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default.a]) { return rhs[_babel_runtime_core_js_symbol_has_instance__WEBPACK_IMPORTED_MODULE_0___default.a](lhs); }else { return lhs instanceof rhs; }
}

function checkIsNumberArrayString(str) {
  if (!str.match(/^\s*(((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s*,){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))))|((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+))))))\s*$/g)) { throw new Error(("Attribute must be a comma- or space-separated sequence of up to three numbers, for example \"1 2.5 3\". Yours was \"" + str + "\".")); }
}

function checkIsSizeArrayString(str) {
  if (!str.match(/^\s*(((\s*([a-zA-Z]+)\s*,){0,2}(\s*([a-zA-Z]+)))|((\s*([a-zA-Z]+)\s*){1,3}))\s*$/g)) { throw new Error(("Attribute must be a comma- or space-separated sequence of up to three strings, for example \"literal proportional\". Yours was \"" + str + "\".")); }
}

Here's the localhost link%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22applyCSSLabel%22%2C%20function()%20%7B%20return%20applyCSSLabel%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22observeChildren%22%2C%20function()%20%7B%20return%20observeChildren%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22getShadowRootVersion%22%2C%20function()%20%7B%20return%20getShadowRootVersion%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22hasShadowDomV0%22%2C%20function()%20%7B%20return%20hasShadowDomV0%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22hasShadowDomV1%22%2C%20function()%20%7B%20return%20hasShadowDomV1%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22getAncestorShadowRoot%22%2C%20function()%20%7B%20return%20getAncestorShadowRoot%3B%20%7D)%3B%0A%2F%20harmony%20export%20(binding)%20%2F%20webpack_require.d(webpack_exports%2C%20%22isInstanceof%22%2C%20function()%20%7B%20return%20isInstanceof%3B%20%7D)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_symbol_has_instanceWEBPACK_IMPORTED_MODULE_0%20%3D%20webpack_require(%2F!%20%40babel%2Fruntime%2Fcore-js%2Fsymbol%2Fhas-instance%20%2F%20%22..%2Ftrusktr%2Bbuilder-js-package%2Fnode_modules%2F%40babel%2Fruntime%2Fcore-js%2Fsymbol%2Fhas-instance.js%22)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_symbol_has_instanceWEBPACK_IMPORTED_MODULE0default%20%3D%20%2F%23PURE%2Fwebpack_require__.n(_babel_runtime_core_js_symbol_has_instanceWEBPACK_IMPORTED_MODULE_0)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_array_fromWEBPACK_IMPORTED_MODULE_1%20%3D%20webpack_require(%2F!%20%40babel%2Fruntime%2Fcore-js%2Farray%2Ffrom%20%2F%20%22..%2Ftrusktr%2Bbuilder-js-package%2Fnode_modules%2F%40babel%2Fruntime%2Fcore-js%2Farray%2Ffrom.js%22)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_array_fromWEBPACK_IMPORTED_MODULE1default%20%3D%20%2F%23PURE%2F__webpack_require.n(_babel_runtime_core_js_array_fromWEBPACK_IMPORTED_MODULE_1__)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_mapWEBPACK_IMPORTED_MODULE_2%20%3D%20webpack_require(%2F!%20%40babel%2Fruntime%2Fcore-js%2Fmap%20%2F%20%22..%2Ftrusktr%2Bbuilder-js-package%2Fnode_modules%2F%40babel%2Fruntime%2Fcore-js%2Fmap.js%22)%3B%0A%2F%20harmony%20import%20%2F%20var%20_babel_runtime_core_js_mapWEBPACK_IMPORTED_MODULE_2_default%20%3D%20%2F%23PURE%2F__webpack_require.n(_babel_runtime_core_js_mapWEBPACK_IMPORTED_MODULE_2__)%3B%0A%0A%0A%0A%0Afunction%20epsilon(value)%20%7B%0A%20%20return%20Math.abs(value)%20%3C%200.000001%20%3F%200%20%3A%20value%3B%0A%7D%0A%0Afunction%20applyCSSLabel(value%2C%20label)%20%7B%0A%20%20if%20(value%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20return%20'0px'%3B%0A%20%20%7D%20else%20if%20(label%20%3D%3D%3D%20'%25')%20%7B%0A%20%20%20%20return%20value%20*%20100%20%2B%20'%25'%3B%0A%20%20%7D%20else%20if%20(label%20%3D%3D%3D%20'px')%20%7B%0A%20%20%20%20return%20value%20%2B%20'px'%3B%0A%20%20%7D%0A%7D%0A%0Afunction%20observeChildren(target%2C%20onConnect%2C%20onDisconnect%2C%20skipTextNodes)%20%7B%0A%20%20%2F%2F%20TODO%20this%20Map%20is%20never%20cleaned%2C%20leaks%20memory.%20Maybe%20use%20WeakMap%0A%20%20const%20childObserver%20%3D%20createChildObserver(onConnect%2C%20onDisconnect%2C%20skipTextNodes)%3B%0A%20%20childObserver.observe(target%2C%20%7B%0A%20%20%20%20childList%3A%20true%0A%20%20%7D)%3B%0A%20%20return%20childObserver%3B%0A%7D%20%2F%2F%20NOTE%3A%20If%20a%20child%20is%20disconnected%20then%20connected%20to%20the%20same%20parent%20in%20the%0A%2F%2F%20same%20turn%2C%20then%20the%20onConnect%20and%20onDisconnect%20callbacks%20won't%20be%20called%0A%2F%2F%20because%20the%20DOM%20tree%20will%20be%20back%20in%20the%20exact%20state%20as%20before%20(this%20is%0A%2F%2F%20possible%20thanks%20to%20the%20logic%20associated%20with%20weightsPerTarget).%0A%0A%0Afunction%20createChildObserver(onConnect%2C%20onDisconnect%2C%20skipTextNodes%20%3D%20false)%20%7B%0A%20%20return%20new%20MutationObserver(changes%20%3D%3E%20%7B%0A%20%20%20%20const%20weightsPerTarget%20%3D%20new%20_babel_runtime_core_js_mapWEBPACK_IMPORTED_MODULE_2_default.a()%3B%20%2F%2F%20We're%20just%20counting%20how%20many%20times%20each%20child%20node%20was%20added%20and%0A%20%20%20%20%2F%2F%20removed%20from%20the%20parent%20we're%20observing.%0A%0A%20%20%20%20for%20(let%20i%20%3D%200%2C%20l%20%3D%20changes.length%3B%20i%20%3C%20l%3B%20i%20%2B%3D%201)%20%7B%0A%20%20%20%20%20%20const%20change%20%3D%20changes%5Bi%5D%3B%0A%20%20%20%20%20%20if%20(change.type%20!%3D%20'childList')%20continue%3B%0A%20%20%20%20%20%20if%20(!weightsPerTarget.has(change.target))%20weightsPerTarget.set(change.target%2C%20new%20_babel_runtime_core_js_mapWEBPACK_IMPORTED_MODULE_2_default.a())%3B%0A%20%20%20%20%20%20const%20weights%20%3D%20weightsPerTarget.get(change.target)%3B%0A%20%20%20%20%20%20const%20%7B%0A%20%20%20%20%20%20%20%20addedNodes%0A%20%20%20%20%20%20%7D%20%3D%20change%3B%0A%0A%20%20%20%20%20%20for%20(let%20l%20%3D%20addedNodes.length%2C%20i%20%3D%200%3B%20i%20%3C%20l%3B%20i%20%2B%3D%201)%20weights.set(addedNodes%5Bi%5D%2C%20(weights.get(addedNodes%5Bi%5D)%20%7C%7C%200)%20%2B%201)%3B%0A%0A%20%20%20%20%20%20const%20%7B%0A%20%20%20%20%20%20%20%20removedNodes%0A%20%20%20%20%20%20%7D%20%3D%20change%3B%0A%0A%20%20%20%20%20%20for%20(let%20l%20%3D%20removedNodes.length%2C%20i%20%3D%200%3B%20i%20%3C%20l%3B%20i%20%2B%3D%201)%20weights.set(removedNodes%5Bi%5D%2C%20(weights.get(removedNodes%5Bi%5D)%20%7C%7C%200)%20-%201)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20for%20(const%20%5Btarget%2C%20weights%5D%20of%20_babel_runtime_core_js_array_fromWEBPACK_IMPORTED_MODULE1default()(weightsPerTarget))%20%7B%0A%20%20%20%20%20%20console.log(target%2C%20weights)%3B%0A%20%20%20%20%20%20debugger%3B%0A%0A%20%20%20%20%20%20for%20(const%20entry%20of%20_babel_runtime_core_js_array_fromWEBPACK_IMPORTED_MODULE1default()(weights))%20%7B%0A%20%20%20%20%20%20%20%20const%20%5Bnode%2C%20weight%5D%20%3D%20entry%3B%0A%20%20%20%20%20%20%20%20if%20(skipTextNodes%20%26%26%20(node%20instanceof%20Text%20%7C%7C%20node%20instanceof%20Comment))%20continue%3B%20%2F%2F%20If%20the%20number%20of%20times%20a%20child%20was%20added%20is%20greater%20than%20the%0A%20%20%20%20%20%20%20%20%2F%2F%20number%20of%20times%20it%20was%20removed%2C%20then%20the%20net%20result%20is%20that%0A%20%20%20%20%20%20%20%20%2F%2F%20it%20was%20added%2C%20so%20we%20call%20onConnect%20just%20once.%0A%0A%20%20%20%20%20%20%20%20if%20(weight%20%3E%200%20%26%26%20typeof%20onConnect%20%3D%3D%20'function')%20onConnect.call(target%2C%20node)%3B%20%2F%2F%20If%20the%20number%20of%20times%20a%20child%20was%20added%20is%20less%20than%20the%0A%20%20%20%20%20%20%20%20%2F%2F%20number%20of%20times%20it%20was%20removed%2C%20then%20the%20net%20result%20is%20that%0A%20%20%20%20%20%20%20%20%2F%2F%20it%20was%20removed%2C%20so%20we%20call%20onDisconnect%20just%20once.%0A%20%20%20%20%20%20%20%20else%20if%20(weight%20%3C%200%20%26%26%20typeof%20onDisconnect%20%3D%3D%20'function')%20onDisconnect.call(target%2C%20node)%3B%20%2F%2F%20If%20the%20number%20of%20times%20a%20child%20was%20added%20is%20equal%20to%20the%0A%20%20%20%20%20%20%20%20%2F%2F%20number%20of%20times%20it%20was%20removed%2C%20then%20it%20was%20essentially%20left%0A%20%20%20%20%20%20%20%20%2F%2F%20in%20place%2C%20so%20we%20don't%20call%20anything.%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D)%3B%0A%7D%0A%0Aconst%20hasShadowDomV0%20%3D%20typeof%20Element.prototype.createShadowRoot%20%3D%3D%20'function'%20%26%26%20typeof%20HTMLContentElement%20%3D%3D%20'function'%20%3F%20true%20%3A%20false%3B%0Aconst%20hasShadowDomV1%20%3D%20typeof%20Element.prototype.attachShadow%20%3D%3D%20'function'%20%26%26%20typeof%20HTMLSlotElement%20%3D%3D%20'function'%20%3F%20true%20%3A%20false%3B%0A%0Afunction%20getShadowRootVersion(shadowRoot)%20%7B%0A%20%20console.log('getShadowRootVersion')%3B%0A%20%20if%20(!shadowRoot)%20return%20null%3B%0A%20%20const%20slot%20%3D%20document.createElement('slot')%3B%0A%20%20shadowRoot.appendChild(slot)%3B%0A%20%20slot.appendChild(document.createElement('div'))%3B%0A%20%20const%20assignedNodes%20%3D%20slot.assignedNodes(%7B%0A%20%20%20%20flatten%3A%20true%0A%20%20%7D)%3B%0A%20%20slot.remove()%3B%0A%20%20console.log('hmm'%2C%20assignedNodes.length%2C%20assignedNodes.length%20%3E%200%20%3F%20'v1'%20%3A%20'v0')%3B%0A%20%20return%20assignedNodes.length%20%3E%200%20%3F%20'v1'%20%3A%20'v0'%3B%0A%7D%0A%0Afunction%20getAncestorShadowRoot(node)%20%7B%0A%20%20let%20current%20%3D%20node%3B%0A%0A%20%20while%20(current%20%26%26%20!(current%20instanceof%20ShadowRoot))%20%7B%0A%20%20%20%20current%20%3D%20current.parentNode%3B%0A%20%20%7D%0A%0A%20%20return%20current%3B%0A%7D%20%2F%2F%20helper%20function%20to%20use%20instead%20of%20instanceof%20for%20classes%20that%20implement%20the%0A%2F%2F%20static%20Symbol.hasInstance%20method%2C%20because%20the%20behavior%20of%20instanceof%20isn't%0A%2F%2F%20polyfillable.%0A%0A%0Afunction%20isInstanceof(lhs%2C%20rhs)%20%7B%0A%20%20if%20(typeof%20rhs%20%3D%3D%20'function'%20%26%26%20rhs%5B_babel_runtime_core_js_symbol_has_instanceWEBPACK_IMPORTED_MODULE_0_default.a%5D)%20return%20rhs%5B_babel_runtime_core_js_symbol_has_instanceWEBPACK_IMPORTED_MODULE_0___default.a%5D(lhs)%3Belse%20return%20lhs%20instanceof%20rhs%3B%0A%7D%0A%0Afunction%20checkIsNumberArrayString(str)%20%7B%0A%20%20if%20(!str.match(%2F%5E%5Cs(((%5Cs(-%7C%5C%2B)%3F((%5C.%5Cd%2B)%7C(%5Cd%2B%5C.%5Cd%2B)%7C(%5Cd%2B)%7C(%5Cd%2B(%5C.%5Cd%2B)%3Fe(-%7C%5C%2B)%3F(%5Cd%2B)))%5Cs%2C)%7B0%2C2%7D(%5Cs(-%7C%5C%2B)%3F((%5C.%5Cd%2B)%7C(%5Cd%2B%5C.%5Cd%2B)%7C(%5Cd%2B)%7C(%5Cd%2B(%5C.%5Cd%2B)%3Fe(-%7C%5C%2B)%3F(%5Cd%2B)))))%7C((%5Cs(-%7C%5C%2B)%3F((%5C.%5Cd%2B)%7C(%5Cd%2B%5C.%5Cd%2B)%7C(%5Cd%2B)%7C(%5Cd%2B(%5C.%5Cd%2B)%3Fe(-%7C%5C%2B)%3F(%5Cd%2B)))%5Cs)%7B0%2C2%7D(%5Cs(-%7C%5C%2B)%3F((%5C.%5Cd%2B)%7C(%5Cd%2B%5C.%5Cd%2B)%7C(%5Cd%2B)%7C(%5Cd%2B(%5C.%5Cd%2B)%3Fe(-%7C%5C%2B)%3F(%5Cd%2B))))))%5Cs%24%2Fg))%20throw%20new%20Error(%60Attribute%20must%20be%20a%20comma-%20or%20space-separated%20sequence%20of%20up%20to%20three%20numbers%2C%20for%20example%20%221%202.5%203%22.%20Yours%20was%20%22%24%7Bstr%7D%22.%60)%3B%0A%7D%0A%0Afunction%20checkIsSizeArrayString(str)%20%7B%0A%20%20if%20(!str.match(%2F%5E%5Cs(((%5Cs(%5Ba-zA-Z%5D%2B)%5Cs%2C)%7B0%2C2%7D(%5Cs(%5Ba-zA-Z%5D%2B)))%7C((%5Cs(%5Ba-zA-Z%5D%2B)%5Cs)%7B1%2C3%7D))%5Cs%24%2Fg))%20throw%20new%20Error(%60Attribute%20must%20be%20a%20comma-%20or%20space-separated%20sequence%20of%20up%20to%20three%20strings%2C%20for%20example%20%22literal%20proportional%22.%20Yours%20was%20%22%24%7Bstr%7D%22.%60)%3B%0A%7D%0A), if you run it locally and add the dangerousForOf option.

mourner commented 5 years ago

Also seems related to #142.