FortAwesome / Font-Awesome

The iconic SVG, font, and CSS toolkit
https://fontawesome.com
Other
73.67k stars 12.19k forks source link

Tree-Shaking broken on fontawesome-svg-core #16005

Open raymondtri opened 4 years ago

raymondtri commented 4 years ago

Describe the bug fontawesome-svg-core tree shaking does not work properly and instead imports all of the modules at once, leading to nearly 60kb in unused bloat on every pageload.

To Reproduce Simply stand up a Vue application, then test before and after as described in my comment on this issue: https://github.com/FortAwesome/vue-fontawesome/issues/172

Even though my environment is Vue-based, I believe this is affecting anyone dependent on the fontawesome-svg-core library.

Expected behavior I would like to only import the library module import { library } from '@fortawesome/fontawesome-svg-core' as detailed in the documentation found here: https://github.com/FortAwesome/vue-fontawesome#tree-shaking-alternative

Screenshots Screenshots are available on the other issue. https://github.com/FortAwesome/vue-fontawesome/issues/172

Version and implementation Version:

        "@fortawesome/fontawesome-svg-core": "^1.2.26",
        "@fortawesome/free-brands-svg-icons": "^5.12.0",
        "@fortawesome/free-regular-svg-icons": "^5.12.0",
        "@fortawesome/free-solid-svg-icons": "^5.12.0",
        "@fortawesome/vue-fontawesome": "^0.1.7",
        "babel-plugin-dynamic-import-webpack": "^1.1.0",
        "babel-plugin-syntax-dynamic-import": "^6.18.0",
        "compression-webpack-plugin": "^2.0.0",
        "laravel-mix": "^4.0.7",
        "laravel-mix-merge-manifest": "^0.1.2",
        "sass": "^1.15.2",
        "sass-loader": "^7.1.0",
        "svgo": "^1.3.0",
        "uglify-js": "^3.7.2",
        "vue": "^2.5.17",
        "webpack-bundle-analyzer": "^3.6.0",
        "webpack-chunk-rename-plugin": "^1.0.3"

Browser and version: N/A

Bug report checklist

raymondtri commented 4 years ago

And I will say that if for some reason I am missing something here, can we please get some better documentation on the requirements to implement this?

tagliala commented 4 years ago

@raymondtri do you have the same results with FA 5.11.2?

raymondtri commented 4 years ago

@tagliala The last version I tested this on was FA 5.9.0, and tree-shaking was still not working on that branch either. Specifically for the svg core portion.

robmadole commented 4 years ago

@raymondtri you'll have to provide a reproducible test case (codesandbox, GitHub repo, something) because tree-shaking is very dependent on the tool and versions used. We can take a look at it after that. Based on your package file you are using a Laravel Mix version that is a year old. My first thought goes there.

raymondtri commented 4 years ago

Good catch, I just updated to ^5.0.0 but unfortunately it still didn't solve anything.

I'll work on a reproducible test case, but a Laravel 6.x base setup should work.

In the meantime for anyone else experiencing this issue, @tagliala I'm using the individual component pattern. I simply wrote a new Vue web component that mirrors the font-awesome-icon Vue component almost exactly, and uses the font-awesome-icon Vue component as the only contents of mine. This way I don't have to worry about loading the entire 90kb svg-core into my build and it is only loaded on pages where it is used, and even deferred there.

<template>
  <font-awesome-icon
    :border="border"
    :fixedWidth="fixedWidth"
    :flip="flip"
    :icon="icon"
    :mask="mask"
    :listItem="listItem"
    :pull="pull"
    :pulse="pulse"
    :rotation="rotation"
    :swapOpacity="swapOpacity"
    :size="size"
    :spin="spin"
    :transform="transform"
    :symbol="symbol"
    :title="title"
  ></font-awesome-icon>
</template>
<script>

import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { config, dom } from '@fortawesome/fontawesome-svg-core';

import { library } from "@fortawesome/fontawesome-svg-core";

import { faFire } from "@fortawesome/free-solid-svg-icons/faFire";

config.autoAddCss = false

export default {
  props: {
    border: {
      type: Boolean,
      default: false
    },
    fixedWidth: {
      type: Boolean,
      default: false
    },
    flip: {
      type: String,
      default: null,
      validator: (value) => ['horizontal', 'vertical', 'both'].indexOf(value) > -1
    },
    icon: {
      type: [Object, Array, String],
      required: true
    },
    mask: {
      type: [Object, Array, String],
      default: null
    },
    listItem: {
      type: Boolean,
      default: false
    },
    pull: {
      type: String,
      default: null,
      validator: (value) => ['right', 'left'].indexOf(value) > -1
    },
    pulse: {
      type: Boolean,
      default: false
    },
    rotation: {
      type: [String, Number],
      default: null,
      validator: (value) => [90, 180, 270].indexOf(parseInt(value, 10)) > -1
    },
    swapOpacity: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: null,
      validator: (value) => ['lg', 'xs', 'sm', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1
    },
    spin: {
      type: Boolean,
      default: false
    },
    transform: {
      type: [String, Object],
      default: null
    },
    symbol: {
      type: [Boolean, String],
      default: false
    },
    title: {
      type: String,
      default: null
    }
  },
  components: {
    FontAwesomeIcon
  },
  mounted () {
    const id = 'fa-styles';
    if (!document.getElementById(`${id}`)) {
      const faStyles = document.createElement('style')
      faStyles.setAttribute('id', id)
      faStyles.textContent = dom.css()
      document.head.appendChild(faStyles);
    }
  }
}
</script>
moltar commented 4 years ago

Having the same issue. Bundle size is huge.

Screen-Shot-2020-04-07-20-50-31

Using with Nuxt:

  [
    'nuxt-fontawesome',
    {
      imports: [
        {
          set: '@fortawesome/free-solid-svg-icons',
          icons: [
            'faLevelUpAlt',
            'faSearchMinus',
            'faSearchPlus',
            'faSearch',
            'faChevronRight',
            'faChevronDown',
            'faCompressArrowsAlt',
            'faExpandArrowsAlt'
          ]
        }
      ]
    }
  ],
    "@fortawesome/fontawesome-svg-core": "1.2.28",
    "@fortawesome/free-solid-svg-icons": "5.13.0",
    "@fortawesome/vue-fontawesome": "0.1.9",
    "nuxt-fontawesome": "0.4.0",
robmadole commented 4 years ago

@moltar we've seen the bundle analyzer report larger sizes than what is actually outputted (is that a word?) Can you look at the actual file in the file system?

moltar commented 4 years ago

What I actually did was get rid of nuxt-fontawesome completely.

And I am using @fortawesome/vue-fontawesome component directly.

import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faSearchPlus, faSearchMinus, faSearch } from '@fortawesome/free-solid-svg-icons'

And then using these included icons as needed.

Bundle size went down to virtually zero now. Can't even find it in the report.

MattIPv4 commented 4 years ago

Hm. I'm facing a similar issue with https://cdnjs.dev (https://github.com/cdnjs/static-website). Only importing the component & icons where needed, but seeing a massive block for fortawesome in my vendors bundle :(

image

The largest part of this is the svg-core, which I'm not even using directly, though it seems to be used by the vue icon component. Really sucks that this is taking such a large part of my vendor bundle.

robmadole commented 4 years ago

@MattIPv4 interesting. Give me a bit, I'm going to load up the static-website locally.

MattIPv4 commented 4 years ago

Apologies for the lack of info in the readme. npm run dev runs the site in dev mode locally. npm run dev:generate:noroutes will run the site generated on a local dev server. npm run dev:analyze will build the site with webpack analyzer (for the above bundle explorer).

robmadole commented 4 years ago

Yep @MattIPv4 I took a look at npm run dev:analyze and the numbers look right. The SVG core has a lot of functionality built into it. Looks like you really aren't using advanced features (masking, transforming, etc) in the site. If that size is objectionable I'd recommend going directly with the SVGS. They can be accessed through the @fortawesome/fontawesome-free package.

MattIPv4 commented 4 years ago

Ah okay, makes sense. Thanks for taking a look! Will have a play with just using the SVGs :)

Tiim commented 4 years ago

I'm having the same issue. I only import @fortawesome/fontawesome-svg-core for the library function and it shows up in my bundle size as over 45 KB.

import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { faBars, ...} from '@fortawesome/free-solid-svg-icons';

export const icons = [ faBars, ... ];

library.add(...icons);

export default function registerIcons(Vue) {
  Vue.component('FA', FontAwesomeIcon);
}

grafik

How would one go about using fontawesome without the fontawesome-svg-core package? I coudn't find any documentation on that.

MattIPv4 commented 4 years ago

If it helps, you can see how we're doing it in cdnjs/static-website:

We're importing the svg directly https://github.com/cdnjs/static-website/blob/master/components/corner.vue Using the svg nuxt module, which returns the svg as a component https://github.com/cdnjs/static-website/blob/master/nuxt.config.js#L99

Tiim commented 4 years ago

I ended up using https://github.com/Cweili/vue-fa instead of vue-fontawesome and fontawesome-svg-core. This saved me about 33k on the resulting bundle size. Quite a big chunk for freatures I never even used :)

martsie commented 4 years ago

I ended up using https://github.com/Cweili/vue-fa instead of vue-fontawesome and fontawesome-svg-core. This saved me about 33k on the resulting bundle size. Quite a big chunk for freatures I never even used :)

Thanks for this suggestion. vue-fa educed our bundle size significantly too.

villelahdenvuo commented 2 years ago

This issue also appears with @fortawesome/angular-fontawesome when updating @fortawesome/fontawesome-svg-core from 1.2.36 to 6.1.1

villelahdenvuo commented 2 years ago

I did some webpack debugging with angular-fontawesome and found this in the stats:

  "usedExports": ["counter", "findIconDefinition", "icon", "parse", "text"],
  "providedExports": [
    "api",
    "config",
    "counter",
    "dom",
    "findIconDefinition",
    "icon",
    "layer",
    "library",
    "noAuto",
    "parse",
    "text",
    "toHtml"
  ],
  "optimizationBailout": [
    "Statement (VariableDeclaration) with side effects in source code at 234:0-17"
  ],

Looking at that line it seems to be indeed causing some side effects using the window object:

image

I think the best solution would be to separate the library functions to a separate module and put the code with side effects to another module that can be avoided by the framework libraries.

tagliala commented 2 years ago

Hi @villelahdenvuo, thanks for the insights

Is this issue exclusively related to the @fortawesome/angular-fontawesome package?

villelahdenvuo commented 2 years ago

@tagliala I haven't tried, but based on what I have seen it seems to be a problem with the svg-core, so even if I used the icon function from svg-core manually it would bundle the whole svg-core module.

It would be great to break the svg-core into tiny modules each implementing a feature/mixin so that libraries/consumers can only import what's needed. For example if I don't use transforms or masks I should not have those in my bundle.

robmadole commented 2 years ago

@villelahdenvuo please check out this: https://fontawesome.com/docs/apis/javascript/plugins

Let us know what you think.

villelahdenvuo commented 2 years ago

Hey @robmadole thanks for the link! I didn't find that before. However, I cannot directly benefit from that since I am using the angular-fontawesome library.

Furthermore the example says that the "lite" version is only 45k. I understand the library has a lot of functionality and it needs to be refactored into smaller plugins, but I think it's too bloated as-is. I decided to make my own icon component that supports the basic features we need:

image

Here's the code if anyone is interested: https://gist.github.com/villelahdenvuo/7b552071613f7dd751ed3af391261060

mpcooke3 commented 2 years ago

@villelahdenvuo please check out this: https://fontawesome.com/docs/apis/javascript/plugins

Let us know what you think.

Hi - I am trying it out but running into some issues with next.js where I need to use it with SSR. My issue is I need autoAddCss:false, as documented here: https://fontawesome.com/docs/web/use-with/react/use-with

The only way to override the default conf i can see when using plugins currently seems to be by putting a FontAwesomeConfig object on the window object, which I don't have during SSR. If I delay doing the register and api.library.add until the client side then the execution of the font awesome code client side seems to be causing mismatch hydration warnings between the server and client side versions of the render.

Aside from these practical issues, it did appear to help a little bit with size and possibly also with chunking/ load-order. However due to the apparent lack of a config option when using the plugins, I haven't found a workaround for adding autoAddCss:false and executing the code in a way that is guaranteed to be early enough and client/server consistent.

ginagr commented 7 months ago

Hey, any updates on this? I'm still experiencing the issue with @fortawesome/angular-fontawesome and @fortawesome/free-solid-svg-icons not tree shaking