Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix property mangling and implement ES5 compatibility properly #442

Merged
merged 3 commits into from
Jul 6, 2021

Conversation

bakerkretzmar
Copy link
Collaborator

@bakerkretzmar bakerkretzmar commented Jul 3, 2021

This PR re-introduces the changes from d781b16 in a way that I'm pretty certain won't break anything this time. It makes Ziggy's default build ES5-compatible.

TLDR: internal-only property getters like get _location() are mangled by Terser in a way that breaks them, so I changed our one internal getter to a regular method, _location(). It's now mangled correctly, so it's not publicly accessible but everything still works.

Closes #441, and closes #440 properly.


I'm not 100% sure what the root issue is here, but I'm pretty close and I'm sick enough of bundlers to stop playing with it now. This is what I've found so far:

JavaScript classes like Ziggy's Router are transpiled to a combination of objects with custom prototypes and functions to make them ES5 compatible, which looks kind of like this with microbundle's Babel setup:

// Input

class Router {
    current() {
        return this._privateCurrent();
    }
    _privateCurrent() {
        return 'here';
    }
    location() {
        return this._location;
    }
    get _location() {
        return 'there';
    }
}

// Output (lots omitted)

  // ...
  var _proto = Router.prototype;

  _proto.current = function current() {
    return this._privateCurrent();
  };

  _proto._privateCurrent = function _privateCurrent() {
    return 'here';
  };

  _proto.location = function location() {
    return this._location;
  };

  _createClass(Router, [{
    key: "_location",
    get: function get() {
      return 'there';
    }
  }]);
  // ...

Notice that the methods become real properties of the Router object (because microbundle uses Babel's loose mode), while the _location getter is represented by an object with its key and a backing get function. Importantly, the properties and methods of the Router object are not represented by strings—current and _privateCurrent are real JavaScript tokens.

Terser, which microbundle runs our code through after Babel transpiles it, mangles properties and methods starting with _ to shorten their names and make them inaccessible publicly. However this mangling obviously ignores strings, since mangling strings, like 'here' or 'there', would destroy the functionality of the code.

Terser's output looks kind of like this, but more optimized and a lot harder to read:

p.current=function current(){return this.cur()}
p.cur=function cur(){return 'here'}
p.location=function location(){return this.loc}
c(Router, [{key:"_location",get:function get(){return 'there'}}])

_privateCurrent is mangled to cur everywhere it appears, shortening it's actual name and all calls to it, and the location method's internal this._location call is mangled to this.loc, but for the same reason that it doesn't mangle 'here' and 'there', Terser doesn't mangle the "_location" key for that getter! Because its key is a string, _location doesn't get renamed properly, which is why this.loc doesn't exist when Ziggy later tries to call it from the current() method or params getter.

There might be a 'better' fix for this but it almost definitely involves setting up our own custom Rollup build, which I don't feel like doing this weekend 😅 for now, making _location a method solves this problem.

@bakerkretzmar bakerkretzmar requested a review from jakebathman July 3, 2021 16:44
@jakebathman
Copy link
Collaborator

Geez, what a mess. Good detective work to at least get something that works and then we can just never touch it again 😅

@bakerkretzmar bakerkretzmar merged commit d8432a5 into main Jul 6, 2021
@bakerkretzmar bakerkretzmar deleted the jbk/es5 branch July 6, 2021 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants