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

docs: add information about "sideEffects" #10691

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

rossrobino
Copy link

@rossrobino rossrobino commented Sep 7, 2023

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • [] Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

sideEffects

I recently discovered that the CSS from every component in my library (you can test by importing from 3.0.6 vs 3.0.7) was being included in the build when only one component was being used. By adding "sideEffects": false to package.json it was resolved. I figured this would be good to add to the docs to minimize bundle sizes for component libraries.

@changeset-bot
Copy link

changeset-bot bot commented Sep 7, 2023

⚠️ No Changeset found

Latest commit: 4974741

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@benmccann
Copy link
Member

This looks really good. The main thing I noted while looking at this is that most Svelte users use Vite and the Vite docs say nothing about sideEffects. I took a quick look though and do see some references to it in the Vite code, so I'd imagine there's some level of support. It may be worth at least filing an issue with Vite noting the absence of this feature from their docs

@rossrobino
Copy link
Author

Thanks! Sounds good, I've opened an issue, we can see what they say.

@rossrobino
Copy link
Author

I do see mention of side effects in rollup's docs, but I can't find it in Vite's either.

@benmccann benmccann added the documentation Improvements or additions to documentation label Sep 7, 2023
documentation/docs/30-advanced/70-packaging.md Outdated Show resolved Hide resolved
documentation/docs/30-advanced/70-packaging.md Outdated Show resolved Hide resolved
documentation/docs/30-advanced/70-packaging.md Outdated Show resolved Hide resolved
rossrobino and others added 3 commits September 8, 2023 09:09
Co-authored-by: Willow (GHOST) <ghostdevbusiness@gmail.com>
Co-authored-by: Willow (GHOST) <ghostdevbusiness@gmail.com>
Co-authored-by: Willow (GHOST) <ghostdevbusiness@gmail.com>

By adding `"sideEffects": false` in the `package.json`, you're signaling to the bundler that your package doesn't include any modules with side effects. This information can help the bundler to be more aggressive in eliminating unused exports from the final bundle, a process known as tree-shaking. This results in smaller and more efficient bundles.

> In the case of a Svelte component library, this prevents CSS from components that are not being used from being included in the final build.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm that setting sideEffects false for Svelte files doesn't have any unintended consequences, eg. even imported components not getting their CSS included?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm definitely not an expert on this, but I have tested with my library and everything is working as expected. I looked at a few other libraries with styles and have found they have this specified as well.

This is the only way I found to prevent the issue of all of the CSS being bundled, but open to suggestions if there is a better way to prevent this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is correct.

Carbon Svelte ships pre-compiled CSS stylesheets that the consumer imports in their app. The sideEffects: ['css/*.css'] field is necessary explicitly for bundlers that tree shake when building for production.

One example is webpack. From the webpack docs:

Note that any imported file is subject to tree shaking. This means if you use something like css-loader in your project and import a CSS file, it needs to be added to the side effect list so it will not be unintentionally dropped in production mode:

In the case of Carbon, if the user has the following:

import "carbon-components-svelte/css/all.css";

When building for production, Webpack will exclude that file if sideEffects does not explicitly include those CSS files. As a result, the stylesheet will be unexpectedly tree-shaken.


On the contrary, if the CSS stylesheet is imported in the Button component (that is then used by the consumer), it will not be excluded as it becomes a part of the dependency tree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like perhaps our recommendation should be to set "sideEffects": ["**/*.css"]? I think that would lead to the desired outcome (webpack-contrib/mini-css-extract-plugin#118 (comment)). Most users will simply be distributing .svelte files in which case I suppose there would be no .css to match and so generally "sideEffects": false would be equivalent, but "sideEffects": ["**/*.css"] could be a little safer and avoid users running into trouble as soon as they add .css. I would love for others to confirm or test this as I don't have a ton of expertise in this area

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, I published my test repo and added this example in case this helps: https://github.com/rossrobino/side-effects-test

You can see the build outputs with sideEffects set to false, true, and ["**/*.css"].

It can be tested by editing node_modules/drab/package.json.

The imported Breakpoint component in +page.svelte has no CSS.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

components using scoped styles exported through a barrel index.js files are the ones having the problem. Their css is added even if you never use them as soon as you import a single one from a barrel, all the css gets included

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I just tried setting sideEffects: ["**/*.css"] in the site-kit package and the built CSS for kit.svelte.dev in this repo seemed to have gotten much larger as a result. I'm not sure which CSS exactly changed. It will take some more investigation

Copy link
Member

@benmccann benmccann Oct 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this is weird. sideEffects: false in site-kit massively increases the CSS for kit.svelte.dev (the same as doing sideEffects: ["**/*.css"]). E.g. you can see the :root CSS ends up in 0.css in addition to one of the _layout CSS files. Omitting the sideEffects field or putting sideEffects: [] result in smaller CSS output. I don't know how that makes any sense because what's the difference between sideEffects: false and sideEffects: []? I filed an issue in Vite for this: vitejs/vite#14558

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is weird. For what it's worth I'm seeing the same in my tester. When I set sideEffects: [], output is the same as sideEffects: true.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vitejs/vite#16152 was merged to fix this. I'm guessing that will be a part of Vite 5.2. We can revisit this docs PR once that's out

@dominikg
Copy link
Member

dominikg commented Sep 9, 2023

see this issue in vite vitejs/vite#4389 (via #10633)

A blanket recommendation for "sideEffects": false can break libraries that actually have side effects and rely on them to work (in my opinion svelte libraries generally should not, and explicitly listing the effectful files is better, but that isn't mentioned at all).

Before we change the documentation i'd like to see a repository demonstrating that and how it works.

Other options to explore:

  1. Tagging the css module returned from vite-plugin-svelte load with moduleSideEffect: false https://rollupjs.org/plugin-development/#load (although i'm not sure if/how vite handles these for css)

  2. Not using a barrel index.js file to list all components, but an exports map that maps deep imports to root, so they are less cumbersome to use

"exports": {
  "svelte": {
    "import": {
      "./Foo.svelte": "./src/components/Foo.svelte" 
    }
  }
}

(I did experiment with globs in exports map a while ago but back then it was buggy for *.svelte)

@benmccann
Copy link
Member

in my opinion svelte libraries generally should not, and explicitly listing the effectful files is better, but that isn't mentioned at all

I agree they shouldn't, so think it would be nice to make that the recommendation. I think this PR both mentions the dangers of getting it wrong and shows how to explicitly list the files with side effects where it says "Make sure that "sideEffects" is correctly set. If a file with side effects is incorrectly marked as having no side effects, it can result in broken functionality. If your package has files with side effects, you can specify them in an array:"

Not using a barrel index.js file to list all components, but an exports map that maps deep imports to root, so they are less cumbersome to use

We could mention that would also address the issue in this section. I don't think we should go as far as recommending users always do it as it seems to be a case-by-case basis as to which might be the better design for a library

@dominikg
Copy link
Member

did some investigation here: sveltejs/vite-plugin-svelte#760

there are several ways in vite/rollup to say some module does not have a side effect, but currently the only one working seems to be the package.json field that's mentioned here. So unless there is a change that allows us to declare the virtual css modules from vite-plugin-svelte as free, the suggestion to add "sideEffects": ["your side effect files here"] to package.json of libraries is probably the best one.

If you are using barrel files inside your own application, you can also add this to the package.json in your app and it'll work.

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
@dummdidumm
Copy link
Member

Judging from the discussion in #10691 (comment) it seems that bundlers work in weird ways (and may work differently across bundlers?) depending on the value of sideEffects. Until we got to the bottom of this I'll put this in draft mode (the text itself is well written though!)

@dummdidumm dummdidumm marked this pull request as draft December 12, 2023 16:16
@bertybot
Copy link
Contributor

bertybot commented Feb 2, 2024

Just throwing my hat into the ring here that this should at least be better advertised due to how focused Sveltekit is on performance. Right now I am maintaining a pretty mature Sveltekit app that imports a core Svelte library everywhere. Implementing this simple trick just gained me like 10 points on my lighthouse scores.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants