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

Multiple passes produce incorrect code #308

Closed
marcusdarmstrong opened this issue Mar 6, 2019 · 3 comments · Fixed by #320 or #363
Closed

Multiple passes produce incorrect code #308

marcusdarmstrong opened this issue Mar 6, 2019 · 3 comments · Fixed by #320 or #363
Labels

Comments

@marcusdarmstrong
Copy link

marcusdarmstrong commented Mar 6, 2019

Bug report or Feature request?

Bug.

Version (complete output of terser -V)
terser 3.16.1

Complete CLI command or minify() options used
terser -m -c passes=4 --toplevel test.js

terser input

exports.withStyles = withStyles;

function _inherits(superClass) {
  if (typeof superClass !== "function") {
    throw new TypeError("Super expression must be a function, not " + typeof superClass);
  }
  Object.create(superClass); 
}

function withStyles() {
  var a = EXTERNAL();
  return function(_a){
    _inherits(_a);
    function d() {}
  }(a);
}

terser output or error
terser -m -c passes=3 --toplevel test.js

exports.withStyles=function(){!function(n){throw new TypeError("Super expression must be a function, not undefined")}(EXTERNAL())};

terser -m -c passes=1 --toplevel test.js

exports.withStyles=function(){return function(t){!function(t){if("function"!=typeof t)throw new TypeError("Super expression must be a function, not "+typeof t);Object.create(t)}(t)}(EXTERNAL())};

Expected result
The multi-pass output should not produce invalid code.

Further information

When we moved to up our passes of terser in our production builds this week, we discovered that this triggered an invalid optimization in the code that is bundled by 'react-with-styles' combined with babel's _inherits helper. (See: airbnb/react-with-styles#151)

I've reduced this down as best I can to a minimal test case, as seen above. Best I can guess this is a failure in the IIFE argument pushdown pre-emptively marking a variable as unused.

It's also worth noting that setting the keep_fnames option opts out of this bug, at an obvious optimization cost, even though there's no use of function names in this code:

terser -m -c passes=3,keep_fnames --toplevel test.js

exports.withStyles=function t(){!function t(e){if("function"!=typeof e)throw new TypeError("Super expression must be a function, not "+typeof e);Object.create(e)}(EXTERNAL())};

@L2jLiga
Copy link
Contributor

L2jLiga commented Mar 7, 2019

What I've found:

command:
terser -m -c passes=3 --toplevel test.js

Case 1: After removing function d(){} it works:

Input:

exports.withStyles = withStyles;

function _inherits(superClass) {
    if (typeof superClass !== "function") {
        throw new TypeError("Super expression must be a function, not " + typeof superClass);
    }
    Object.create(superClass);
}

function withStyles() {
    var a = EXTERNAL();
    return function(_a){
        _inherits(_a);
    }(a);
}

Output:

exports.withStyles=function(){!function(t){if("function"!=typeof t)throw new TypeError("Super expression must be a function, not "+typeof t);Object.create(t)}(EXTERNAL())};

Case 2: After moving function d() {} outside of anonymus function it also produces correct code:

Input:

exports.withStyles = withStyles;

function _inherits(superClass) {
    if (typeof superClass !== "function") {
        throw new TypeError("Super expression must be a function, not " + typeof superClass);
    }
    Object.create(superClass);
}

function withStyles() {
    var a = EXTERNAL();
    return function(_a){
        _inherits(_a);
    }(a);
    function d() {}
}

Output:

exports.withStyles=function(){!function(t){if("function"!=typeof t)throw new TypeError("Super expression must be a function, not "+typeof t);Object.create(t)}(EXTERNAL())};

Case 3: Just removing toplevel option make Terser produces right code

Input:

exports.withStyles = withStyles;

function _inherits(superClass) {
    if (typeof superClass !== "function") {
        throw new TypeError("Super expression must be a function, not " + typeof superClass);
    }
    Object.create(superClass);
}

function withStyles() {
    var a = EXTERNAL();
    return function(_a){
        _inherits(_a);
        function d() {}
    }(a);
}

Output:

function _inherits(t){if("function"!=typeof t)throw new TypeError("Super expression must be a function, not "+typeof t);Object.create(t)}function withStyles(){_inherits(EXTERNAL())}exports.withStyles=withStyles;

@roblan
Copy link

roblan commented Mar 7, 2019

I'm not sure, but it looks a little like #294

@L2jLiga
Copy link
Contributor

L2jLiga commented Mar 8, 2019

Workaround is to add evaluate=false to compress options, also it's workaround for #294

I'll try to fix it

apvarun added a commit to apvarun/react-onboarding-pro that referenced this issue Nov 17, 2019
STRML added a commit to react-grid-layout/react-draggable that referenced this issue May 12, 2020
There is nothing special in the browser build that is actually practical
for modern use. The "browser" field, as defined in
https://github.com/defunctzombie/package-browser-field-spec#overview,
indicates that you should use it if you are directly accessing globals,
using browser-specific features, dom manipulation, etc.

React components like react-draggable are built to do minimal raw
DOM manipulation, and to always gate this behind conditionals to ensure
that server-side rendering still works. We don't make any changes
to any of that for the "browser" build, so it's entirely redundant.

Hoping this also fixes the "Super expression must either be null or
a function" error (#472) that some users have experienced with particular
bundler configurations.

The browser build may still be imported at "build/web/react-draggable.min.js".
This is to prevent breakage only. The file is no longer minified to prevent
possible [terser bugs](terser/terser#308).
MaciejCzyzewsk pushed a commit to MaciejCzyzewsk/react-draggable that referenced this issue Feb 20, 2023
There is nothing special in the browser build that is actually practical
for modern use. The "browser" field, as defined in
https://github.com/defunctzombie/package-browser-field-spec#overview,
indicates that you should use it if you are directly accessing globals,
using browser-specific features, dom manipulation, etc.

React components like react-draggable are built to do minimal raw
DOM manipulation, and to always gate this behind conditionals to ensure
that server-side rendering still works. We don't make any changes
to any of that for the "browser" build, so it's entirely redundant.

Hoping this also fixes the "Super expression must either be null or
a function" error (#472) that some users have experienced with particular
bundler configurations.

The browser build may still be imported at "build/web/react-draggable.min.js".
This is to prevent breakage only. The file is no longer minified to prevent
possible [terser bugs](terser/terser#308).
ludarkhorse added a commit to ludarkhorse/ReactGrid that referenced this issue Feb 21, 2023
There is nothing special in the browser build that is actually practical
for modern use. The "browser" field, as defined in
https://github.com/defunctzombie/package-browser-field-spec#overview,
indicates that you should use it if you are directly accessing globals,
using browser-specific features, dom manipulation, etc.

React components like react-draggable are built to do minimal raw
DOM manipulation, and to always gate this behind conditionals to ensure
that server-side rendering still works. We don't make any changes
to any of that for the "browser" build, so it's entirely redundant.

Hoping this also fixes the "Super expression must either be null or
a function" error (#472) that some users have experienced with particular
bundler configurations.

The browser build may still be imported at "build/web/react-draggable.min.js".
This is to prevent breakage only. The file is no longer minified to prevent
possible [terser bugs](terser/terser#308).
Karasu0888 pushed a commit to Karasu0888/react-draggable that referenced this issue Sep 12, 2023
There is nothing special in the browser build that is actually practical
for modern use. The "browser" field, as defined in
https://github.com/defunctzombie/package-browser-field-spec#overview,
indicates that you should use it if you are directly accessing globals,
using browser-specific features, dom manipulation, etc.

React components like react-draggable are built to do minimal raw
DOM manipulation, and to always gate this behind conditionals to ensure
that server-side rendering still works. We don't make any changes
to any of that for the "browser" build, so it's entirely redundant.

Hoping this also fixes the "Super expression must either be null or
a function" error (#472) that some users have experienced with particular
bundler configurations.

The browser build may still be imported at "build/web/react-draggable.min.js".
This is to prevent breakage only. The file is no longer minified to prevent
possible [terser bugs](terser/terser#308).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants