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
Effectful imports should ignore sideEffects field #6571
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. |
added modules: true, localIdentName: '[name]__[local]__[hash:base64:5]' to webpack.config.dev and prod files find test: /\.css$/, => { test: cssRegex, exclude: cssModuleRegex, ... } => change to { test: cssRegex, exclude: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, modules: true, localIdentName: '[name]__[local]__[hash:base64:5]' }), } and in prod file to { test: cssRegex, exclude: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, modules: true, localIdentName: '[name]__[local]__[hash:base64:5]' }), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. // Remove this when webpack adds a warning or an error for this. // See webpack/webpack#6571 sideEffects: true, },
FWIW, I agree with the sentiment that For example here are snippets of some of the files from the project where I ran into this issue: // entry.js
import './icons';
// ..bunch of other irrelevant imports
// icons/index.js
import './library';
export * from './Icon';
// icons/library.js
import { library } from '@fortawesome/fontawesome-svg-core';
import * as icons from './icon-registry';
library.add( ...Object.values( icons ) );
// icons/icon-registry.js
export {
faUserCircle as farUserCircle,
faCircle as farCircle,
faIdCard as farIdCard,
} from '@fortawesome/free-regular-svg-icons'; The problem that I had is that when running "sideEffects": [ "src/icons/library.js" ], This didn't do anything. After some experimentation I arrived at: "sideEffects": [ "src/icons/library.js", "src/icons/index.js" ], So basically it seems that part of the issue with |
I agree wholeheartedly with the principle of least astonishment. I'm astonished that anyone considers "imports should work the way that the JavaScript language says they should work " more astonishing than "a
I'm not clear what you're describing here, does
JavaScript has a clear language-level way to declare side-effectful imports. This proposal doesn't suggest adding anything implicit: it asks that this flag from a build tool not override the language. Developers know what I would ask that the webpack team considers what's expected, surprising, and explicit from the perspective of developers using the JavaScript language itself. Every single time we've used this feature with new teams, developers are astonished that their clear-as-day side-effectful imports ( (sidebar: <3 your avatar.) |
Any updates on this? |
Just found this because I am astonished that Webpack is throwing away all my hard work on a polyfill loader. :) FWIW, I also expected that The documentation on "sideEffects": [
"./dist/polyfills/index.js",
"./dist/index.js"
] (Assuming that |
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 )': |
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: