Font-Awesome icon indicating copy to clipboard operation
Font-Awesome copied to clipboard

Tree-Shaking broken on fontawesome-svg-core

Open raymondtri opened this issue 5 years ago • 25 comments

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

  • [ ] SVG with JS
  • [ ] Web Fonts with CSS
  • [ ] SVG Sprites
  • [ ] On the Desktop

Bug report checklist

  • [x] I have filled out as much of the above information as I can
  • [x] I have included a test case because my odds go way up that the team can fix this when I do
  • [x] I have searched for existing issues and to the best of my knowledge this is not a duplicate

raymondtri avatar Dec 30 '19 22:12 raymondtri

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?

raymondtri avatar Dec 30 '19 22:12 raymondtri

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

tagliala avatar Dec 31 '19 09:12 tagliala

@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.

raymondtri avatar Dec 31 '19 14:12 raymondtri

@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.

robmadole avatar Dec 31 '19 18:12 robmadole

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>

raymondtri avatar Dec 31 '19 20:12 raymondtri

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",

moltar avatar Apr 07 '20 13:04 moltar

@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?

robmadole avatar Apr 07 '20 14:04 robmadole

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.

moltar avatar Apr 07 '20 14:04 moltar

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.

MattIPv4 avatar Apr 10 '20 11:04 MattIPv4

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

robmadole avatar Apr 10 '20 14:04 robmadole

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).

MattIPv4 avatar Apr 10 '20 15:04 MattIPv4

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.

robmadole avatar Apr 10 '20 15:04 robmadole

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

MattIPv4 avatar Apr 10 '20 15:04 MattIPv4

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.

Tiim avatar Jun 18 '20 16:06 Tiim

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

MattIPv4 avatar Jun 18 '20 17:06 MattIPv4

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 :)

Tiim avatar Jun 19 '20 09:06 Tiim

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.

martsie avatar Aug 02 '20 21:08 martsie

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

villelahdenvuo avatar Jun 27 '22 16:06 villelahdenvuo

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.

villelahdenvuo avatar Jun 28 '22 11:06 villelahdenvuo

Hi @villelahdenvuo, thanks for the insights

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

tagliala avatar Jun 28 '22 11:06 tagliala

@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.

villelahdenvuo avatar Jun 28 '22 15:06 villelahdenvuo

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

Let us know what you think.

robmadole avatar Jun 28 '22 16:06 robmadole

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

villelahdenvuo avatar Jun 29 '22 11:06 villelahdenvuo

@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.

mpcooke3 avatar Sep 26 '22 09:09 mpcooke3

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

ginagr avatar Mar 19 '24 23:03 ginagr