Constructor.name behaves differently in dev and prod builds for models defined with the ES6 class syntax

I’d like to understand why model.constructor.name is not available in prod builds while it works in dev and test envs for models defined with the ES6 class syntax.

I’ve setup a test app here: GitHub - bartocc/model.constructor.name-prod-bug

To reproduce

  • git clone https://github.com/bartocc/model.constructor.name-prod-bug.git
  • cd model.constructor.name-prod-bug
  • ember serve
  • open http://localhost:4200

You should see:

model.constructor.name: ArticleModel
model.constructor.modelName: article

Now stop the server and start it again in prod env

  • ember serve --environment production
  • open http://localhost:4200

You should see something like this:

model.constructor.name: o
model.constructor.modelName: article

Why do we have such a different behaviour?

I’ve spotted this issue ES6 constructor.name is not unique in production build · Issue #16856 · emberjs/ember.js · GitHub on the same topic

Looks like the name is being mangled during minification. There might be an uglify / terser setting to prevent this, but since by the time it gets to minification (due to targets configuration) I’m not sure that they know which things are constructors vs normal functions.

The reason modelName is present is because ember-data adds it manually after lookup up any given model class.

thx @rwjblue.

I’ve tested this with minifyJS: true in a dev build and constructor.name is working, so minification might not be the only thing involved in this behaviour.

Being able to use modelName here saved my day but as you said, this is thanks to ember-data.

I don’t know to what part of the framework this might be related, but it would be great if we could rely on constructor.name whatever the build env.

Sorry for being unclear above, I’m saying that the combination of transpiling to your production targets (which includes IE11 in the demo repo) and minification that is the cause.

In a dev build, your targets are essentially only evergreen browsers that all support native class syntax directly and therefore the model is not transpiled down to ES5. When terser/uglify runs against a native class it does preserve the constructor.name (as one of your recent comments confirmed).

However, in a prod build your apps targets include IE11 which means that native class syntax is not supported and therefore it is transpiled away before minification (see example of transpilation here). Then when minification happens it sees “just” a function bound to the local closure scope (e.g. function Article(){}) which gets the normal function name mangling.


I don’t know to what part of the framework this might be related, but it would be great if we could rely on constructor.name whatever the build env.

Ultimately, this isn’t really an “Ember” thing its just how transpilation + minification works…

2 Likes

Thanks Robert one again! I’ll keep all this in mind :wink:

As far as I can track back my activity as a web dev, IE has always been bitting me, whatever its flavour :wink:

I guess things are a bit better today though :smiley:

Just for posterity, here is a StackOverflow post with a solution for this.: javascript - Webpack's removes classnames when minifying/uglifying ES6 code with inheritance - Stack Overflow

In brief, compare to the constructor itslf, not the constructor’s name, eg:

if (this.constructor.name === 'Parent') {

becomes

if (this.constructor=== Parent) {

This worked well for me to sidestep the entire issue.