-
-
Notifications
You must be signed in to change notification settings - Fork 9.1k
Effectful imports should ignore sideEffects field #6571
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
Comments
If You can and should include // package.json of 'list'
{
"sideEffects": [
"./fantasy-land"
]
} |
I understand how to solve it, I've did it just before posting an issue here and I understand this is not the most popular case, but still think that my argument has some merit - |
I agree with @Andarist. If you consider the import statement, a naked import implies “execute everything in this file”, which is side effectful by default. |
I also agree with @Andarist. MDN describes So the developer has already specified that this module has side effects; the requirement to specify it again in a separate location is suboptimal and fragile. |
In practice, it might be nice to configure webpack to assume all modules are side effect free by default, as the whole npm ecosystem doesn't use it yet. If webpack treated an import statement with no specifiers (i.e. For example: import {something} from "foo";
// unused import gets pruned import "foo";
// implies side effects
import {something} from "foo";
// unused import does not get pruned This behavior could be nice combination with:
|
Hi In facebook/create-react-app#5140 the package in question mistakenly set This lead to the discussion here: And similar confusion occurred in #6741 and #7791. @sokra, what do you think about the idea of allowing loaders to declare that their output always has side effects, thereby overriding the package.json |
I disagree that the import form should determine dead code elimination. That sounds like a dangerous heuristic to me because it’s so subtle. I think it’s fair that the package author should declare effectful entry points separately. However I also think webpack could warn when you use such an entry point with a plain import. That said the way webpack handles CSS in this case doesn’t make sense to me. See facebook/create-react-app#5141 (comment). When you import CSS package library can’t “know” how you’ll “interpret” it. So it can’t know whether it will become a side effect or not. The real effect here is in the style loader. So that leads me to thinking loaders should be able to mark their output as effectful. |
It would be nice if the entire npm ecosystem adopted the sideEffects package.json field, but realistically, I think it will be a long time before that is the case. Consider the following scenario:
In this scenario, I think pruning "core-js" is clearly incorrect behavior. I certainly don't think import form should be the only heuristic for determining dead code elimination, but in this specific case where a package's side effectfulness is not explicitly specified, not using that information seems just plain wrong. Until a majority of the npm ecosystem specifies a |
@rtsao I think in the scenario you're describing treating such imports specially only adds to the overall confusion. Because now some cases will work and some wouldn't, but the problem will get less widespread and thus there'll be less incentives to properly fix it. So it could cause more pain in longer term. Your solution is probably reasonable for the behavior when the user explicitly sets top-level But for cases when you don't have anything on the top level, I don't think plain |
The |
The reason I'm opposed to this is because it's unexpected to see refactoring from import 'foo' to import {x} from 'foo' break code. For example, somebody might be running some code just for side effects, relying on your proposed behavior. Then they actually need to add an import, and it's super easy to miss that adding an import would remove the side effects in production only. Of course you can have two import 'foo'
import {x} from 'foo' Anybody can refactor this to "remove duplicate import" (in fact there's probably linter plugins to do that or complain about that). It's too subtle for this to change the behavior. We need to consider not just how the code looks, but how people tend to change it over time, and anticipate the pitfalls. This is why I think webpack should warn when you use plain import (correctly guessing your intent, and surfacing the problem to you). But I don't think it should change the behavior. Because the fix should be in the package that "lies" about its declared side effects. Warning surfaces the problem, package author fixes it, and everybody is happy. |
A warning is definitely a good idea. Using |
Some note about adding |
Regarding importing CSS. While the exact way how CSS is handled is maybe not defined, the semantic of Anyway, you can also set sideEffects in the CSS rule in |
Thanks, this makes sense! |
Do you feel it would be a reasonable default though? We need to decide what CRA should do in facebook/create-react-app#5140. |
If the developer wrote
I agree that’s a surprising result, and I agree that having two imports is gross (and probably lint-breaking). But the refactored code is incorrect (and is therefore not a refactor): importing Anecdotally, every coworker whose opinion I have initially expects side-effect imports to work with |
Sideeffect-full is the default. You opt-in into sideeffect-free with the package.json-flag. Being always sideeffect-full for CSS feels like not respecting the opt-in in the package.json and promoting an incorrect sideEffects config in package.json. Note that CSS modules can be flagged as sideeffect-free. |
If the developer wrote These are two conflicting statements the developer made here. Maybe we should even emit an error here. |
(These are folks who get what the flag is for and think it’s awesome, by the way.) |
They are not: they’re intuitively compatible (“put me in no-side-effects mode and let me clearly declare side effects the usual JS-y way”), and could be made actually compatible if you slightly change the semantics of I would definitely support adding an error over the status quo of silently stripping them against the developer’s clear intentions. But it would be a really silly error. “We know what you meant here, you couldn’t have meant anything else, but error.” |
Based on this, it seems like webpack could always error if you import CSS from package that says it has no side effects? Since it's never correct. |
In Create React App, we've decided to always treat css/sass/scss as side effectful in the module rules. If webpack adds a warning (or better, an error) when CSS is marked as side effect-less, we can remove this customization. But for now it's too subtle and would bite people in production otherwise. |
I agree that this is a cruel trap and that it should at least be consistent between dev and prod |
Are you sure about that? Perhaps it is due to two different optimizations (usedExports vs sideEffects) but if I add an exported function to a module and don't import it webpack definitely drops it. |
In webpack 5 the side effects optimization is enabled for both modes to reduce differences. |
@sokra do the marked side effects also get dropped even without minification? |
Yes |
Wait, so was this feature actually implemented? Is it safe to use: import 'file.css'
// or
import 'file.js'
// With { sideEffects: false } in package.json?
// -------------
// Will the contents of file.js appear in the bundle? |
No, since you have configured |
… being added in production Summary: RainbowKit's CSS doesn't get bundled in production, because the way they're exposing it is with a sideEffect. Webpack's side effects are basically an infinite black hole, as denoted by [[ webpack/webpack#6571 | this issue ]] that's been open for 4.5 years. more context/explanation on [[ https://stackoverflow.com/a/49203452 | this stack overflow answer ]] Test Plan: run `yarn prod` in `landing` with this diff and RainbowKit experience found on `localhost/commlanding/siwe` now has more than 0 styles Reviewers: atul, varun, abosh, ashoat Reviewed By: ashoat Subscribers: tomek, abosh, varun, ashoat, atul Differential Revision: https://phab.comm.dev/D5224
… being added in production Summary: RainbowKit's CSS doesn't get bundled in production, because the way they're exposing it is with a sideEffect. Webpack's side effects are basically an infinite black hole, as denoted by [[ webpack/webpack#6571 | this issue ]] that's been open for 4.5 years. more context/explanation on [[ https://stackoverflow.com/a/49203452 | this stack overflow answer ]] Test Plan: run `yarn prod` in `landing` with this diff and RainbowKit experience found on `localhost/commlanding/siwe` now has more than 0 styles Reviewers: atul, varun, abosh, ashoat Reviewed By: ashoat Subscribers: tomek, abosh, varun, ashoat, atul Differential Revision: https://phab.comm.dev/D5224
Like @gaearon this seems like it should be up to the loader or plugins shouldn't it? Is there a way to explicitly mark a module as "having side effects" from a loader/plugin currently? Edit: My (awful) solution was to write a ts transform (or a loader if not using TS) to rewrite these import statements and use them in a way that webpack doesn't understand which is a pretty bad hack. |
I also agree with @Andarist and was a bit surprised by this today when I decided to finally enable the side effects flag for our monorepo. Tree shaking is way better but alas it also dropped the naked imports which I thought would be enough of a flag for webpack to leave them alone by default )': |
Is there a new update on this or a solution ? |
I disagree the behavior of webpack should differ from a module's configured "sideEffects" flag. Implicit overrides to an explicit configuration would make things even more confusing than they already are. |
@alexander-akait great news, curious when/where this was this completed? |
@dcporter What we should to solve here? Answer is here - #6571 (comment) |
scrolls down You mean way up above the eighty other subsequent comments? What should be solved is webpack considering its made-up config to be superior to most clear possible statement of intent from the JavaScript language itself. |
Or at least opt-in to something implicit to keep those imports:
|
@dcporter We use such approach a lot of time (since 2018), changing it right now will be very destructive (a lot of other tools use the same approach) and a lot of project will lose this optimization (bigger size). By default we use this https://github.com/webpack/webpack/blob/main/lib/config/defaults.js#L1464 for webpack@5, i.e. webpack respects I am not against implementing hints or warnings about potential places, but no one has tried to implement it so far, so I think it is not so popular, and therefore closed the issue. Where and which do you have a real problem right now?
This idea is good, but we have vite/esbuild/rspack/etc and they don't support this value, integrate it can be very long and I don't think library authors really can use it someday (don't forget about enterprise that may use the old version for a long time). I don't think this is a problem specifically with webpack now, although it was originally implemented by webpack, change the behavior or add new values will require coordination of all the authors of the tools, a long time for implementation, releasing, updating, and then a long time for authors. |
Approaching 100% of greenfield projects set up by non webpack experts have this real problem. (It's not as atrocious as it used to be, because now you have to fix it up front instead of discovering it just before you ship.)
I disagree that it would be unduly disruptive to fix this. The deoptimization would be rare since most extant projects have been forced to work through it.
Other bundlers have implemented side-effect flags whose defaults ignore JavaScript semantics? Which ones? |
What projects? If you don't have
I disagree too, it is not rare and I faced with it a lot of time, and a lot of time in big projects, I suggest you provide constructive arguments, rather than stating who agrees or disagrees, this is counter-constructive.
Which not? evanw/esbuild#1241 (comment) I can provide more link if you want Once there was a decision to implement this solution like this, yes it may not be perfect and there are certain gaps in the design, but it was done like this, and now changing it will cause huge problems in the entire ecosystem, and I'd rather not touch it, if we want to improve/change/make it more flexible we will need a new field with new structures and values (for example we can take a If you want to solve this, I suggest you create an RFC, describe your vision and invite the main developers of the tools for discussions, after this is approved we can begin implementation, this is how backward compatibility works and this is how the process in the ecosystem occurs, I can't just change something if it's been working for years just because someone wanted it, sorry. |
Do you want to request a feature or report a bug?
feature
What is the current behavior?
with
sideEffects: false
does not include imported file in the bundle, which is somewhat OK
If the current behavior is a bug, please provide the steps to reproduce.
not a bug
What is the expected behavior?
this import is clearly effectful, so it actually IMHO should not get dropped regardless of the
sideEffects
If this is a feature request, what is motivation or use case for changing the behavior?
described in expected behaviour
Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.
webpack@4, others - irrelevant
The text was updated successfully, but these errors were encountered: