-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Class declaration in IIFE considered as side effect #1261
Comments
cc @DanielRosenwasser This may be of interest to you. This prevents TS users from being able to have tree shaking/dead code elim with UglifyJs @mishoo if we can help resolve this, please point us in the right direction. |
@BlackSonic Uglify doesn't perform program flow analysis. It won't drop the code because of the the side effects you've noted. You probably want to use something like rollup which supports that sort of speculative unused code dropping. |
Since I don't understand specifically what you mean by speculative code dropping, could you elaborate on this please? |
One truly doesn't truly know the global impact of any program unless you run it or use SSA. Barring that you can trap all assignments and make some assumptions as to what is likely going on. That seems to be what rollup is doing. But one could craft code to make such a scheme fail. That's why it's speculative. |
True, it is possible one could write something like The problem is the interaction with pure declarative code that ends up using IIFE by accident of transpilation... Speaking of "pure declarative", assuming uglify got support for this use case, how would decorators sit in this? Is there something to prevent, say, a class decorator from having global side-effects? I presume class and static member decorators have to execute at class declaration time, so if they can have side-effects, tree-shaking would affect the actual runtime result of the code. |
Certain transforms are simpler to perform at the higher ES6 level, rather than with the lower level transpiled ES5 where it's more difficult to reason about side effects. The following is using Uglify's harmony branch. Notice that the ES6 class Foo will be dropped by uglify harmony if not referenced:
Uglify is pretty conservative in dropping code. Rollup appears to have more aggressive code dropping heuristics. |
Uglify's harmony branch for ES6 is still a work in progress and doesn't support class decorators as far as I know. @avdg could better answer what's supported in harmony. |
On es 6 I'm more focused on getting stuff working (I prefer wide support But we still haven't good destructuring or modules support for example. We have to make decisions on where to spend our time if we can't spend our Although where possible, there is some constant evaluation being done, for Feel free to open issues with clear examples where UglifyJS could do a Op ma 22 aug. 2016 05:20 schreef kzc notifications@github.com:
|
Would it be possible to add this to the list of 'unsafe' compress features. These wrapped IIEF's are generated from transpilers? This is a pretty important optimization to both babel and Typescript users. I can't think of a code example where a transpiler would output cases like the one @kzc described due to the generative nature of the code. |
It's a non-trivial amount of work to recognize code for ES6 classes converted to ES5 IIFEs and drop them if the side effects are collectively deemed to be acceptable to ignore. Uglify does not have any program flow analysis. Pull requests are welcome. As I mentioned before, dealing with this issue at a higher level with rollup is probably your best bet. |
@kzc Is it a trivial amount of work to detect an IIFE or is it also considered program flow analysis? If not, with the flag one can decide if the codebase and the packages used can be trusted and no global variable manipulation occurs there. |
IIFEs are trivial to detect. Deciding whether the code in them can be considered pure and side-effect free in the context of the entire program is not. Then there's the exports assignment issue that I mentioned in the other thread that would prevent the IIFE from being dropped anyway. If you want to put together a PR to address these issues, go for it. |
But it would be the developers choice to decide wether IIFEs can be considered side effect free. The transpiler won't produce side effects. The
|
If you consider it trivial can you give us hints on how and where to implement it? At first sight to understanding 1k+ files seemed hard for me who is not familiar with the codebase. |
This would be a big help for everyone using Webpack with Babel or Typescript, it would be not a trivial nor easy thing to migrate to Rollup. |
Uglify consists of a dozen or so JS files in lib, tools and bin. It's pretty easy to follow. |
Some important api docs can be found on http://lisperator.net/uglifyjs/ |
Is it ok if i add a new option parameter to skip the check on this line? |
@avdg thanks, those docs are really helpful for newcomers |
@BlackSonic wrote:
I wouldn't recommend it. It would break a lot of uglified code. |
How can this break existing things if it is turned off by default? |
Just because you want to drop code with a side effect in a very specific case does not mean it should be done in the general case.
|
Maybe put a check there for IIFE also besides the config check? |
I don't think you understand the nature of the problem you're trying to solve. Please re-read: #1261 (comment) |
Can you explain it in detail?
This part has nothing to do with the |
I've tried to explain that the problem is not as simple as you believe it to be. Just examine the uglify code and you'll figure it out. Good luck. |
This is probably a bad idea due to the additional coupling, but could there be, say, an |
@jods4 Sorry, decorators are out of scope for Uglify and should be implemented at the higher level language level. By the way, dropping unused classes is easy at the ES6 level and uglify harmony already does this without any need for annotations. It's only at the ES5 generated code level does this problem become difficult because any ES5 IIFE can potentially have side effects:
|
@kzc I know. var ClassX = /*#__PURE__*/(function() { ... }());
ClassX = /*#__PURE__*/__decorate([deco1, deco2, deco3], ClassX); Would it be completely dropped ? |
With However, the following would work:
as would:
The reason that the arguments
|
interesting, thanks! |
Furthermore, if the decorators were functions not used elsewhere then they would also be dropped accordingly:
|
The
|
@kzc but this is highly dependent of what the invoked decorators actually do! Some decorators may have wanted side-effects and removing the |
Marking a function as pure via |
@kzc the point is that even if its result (the class) is not used, you may want to keep it because of decorators side-effects. My point is that you can't say globally For example: // Here the decorators are just metadata, you can drop the class if it's not used
@cacheable
class A {}
const A = /*#__PURE__*/__decorate([cacheable], class { });
// Here the decorator registers the class in a pub-sub event bus, you should NOT remove it!
@subscribe('login')
class B { }
const B = __decorate([subscribe('login')], class { }); |
Okay, you have the flexibility to annotate specific |
Related discussion about introducing this annotation in Rollup and Webpack to enable correct (non-speculative) tree shaking: d3/d3#3076 |
@alexlamsl It appears that you quietly implemented this feature - dropping unused side-effect-free class IIFEs in 0b0eac1:
$ cat t1261.js
var Foo = (function() {
function Foo() {
}
Foo.prototype.toString = function() {
return 'V6';
};
return Foo;
}());
var Bar = (function() {
function Bar() {
}
Bar.prototype.toString = function() {
return 'V8';
};
return Bar;
}());
console.log(new Foo().toString()); $ cat t1261.js | bin/uglifyjs -bc toplevel,passes=3
var Foo = function() {
function Foo() {}
return Foo.prototype.toString = function() {
return "V6";
}, Foo;
}();
console.log(new Foo().toString()); Nice! |
I'm sending a bug report
When i declare a class inside an IIFE, but class is not used, it isn't stripped by UglifyJS, because it is considered as a side effect.
The warning i get:
WARN: Side effects in initialization of unused variable V6Engine [./dist/car.bundle.js:74,4]
I suspect it is because it assigns values to the prototype.
The biggest pain point with it that it blocks tree-shaking of exported classes generated by Typescript.
Can it be fixed or is their a class declaration that is not considered a side effect?
Here is an example repository where i use UglifyJS through Webpack with Typescript.
The text was updated successfully, but these errors were encountered: