JuniorTour / vue-template-babel-compiler

Enable Optional Chaining(?.), Nullish Coalescing(??) and many new ES syntax for Vue.js SFC based on Babel
https://www.npmjs.com/package/vue-template-babel-compiler
118 stars 9 forks source link

[Bug] Optional function call as event handler #32

Closed lsdsjy closed 2 years ago

lsdsjy commented 2 years ago

Current behavior

<div @click="handler?.()" />

will be transformed to:

var render = function () {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h

  return _c("div", {
    on: {
      click: function click($event) {
        var _vm$handler, _vm

        ;(_vm$handler = (_vm = _vm).handler) === null || _vm$handler === void 0
          ? void 0
          : _vm$handler.call(_vm)
      },
    },
  })
}

Inside function click, babel transforms optional function call expression using a temporary variable _vm, which collides with the _vm declaration outside. So the inner _vm will be undefined, leading to a reading property of undefined error.

Note: this error only occurs in event handler, because in Vue2 if event handler is not a single identifier, the expression will be wrapped with a function, which may lose this.

Expected behavior

Work as intended.

Usage

Extra

If we use babel repl, we can see _vm.a?.() will be transformed into:

"use strict";

var _vm$a, _vm2;

(_vm$a = (_vm2 = _vm).a) === null || _vm$a === void 0 ? void 0 : _vm$a.call(_vm2);

Babel used a different identifier _vm2 to avoid collision. But in this project, when using parseWithStatementToVm, babel behaves differently.

After debugging, I found babel will go through a crawling process on the original source code when transformSync is called, during which the _vm will be added to the current scope as global identifier:

So when babel knows _vm can't be used as a temporary variable. But in this project, _vm. is prepended to handler by the custom plugin on the run (and the _vm declaration is inserted after all the transforms), which missed the crawling process.

So maybe we should call addGlobal manually to fix this? (Or we should insert var _vm = this before all the transformation happens?)

I will create a PR soon.

lsdsjy commented 2 years ago

See #33