FortAwesome / react-fontawesome

Font Awesome React component
https://fontawesome.com
MIT License
3.69k stars 265 forks source link

How does explicit import work with tree shacking? #387

Open Vadorequest opened 4 years ago

Vadorequest commented 4 years ago

Per your doc:

import ReactDOM from 'react-dom'
import { library } from '@fortawesome/fontawesome-svg-core'
import { fab } from '@fortawesome/free-brands-svg-icons'
import { faCheckSquare, faCoffee } from '@fortawesome/free-solid-svg-icons'

library.add(fab, faCheckSquare, faCoffee)

fab contains all the font-awesome brand icons. Does that mean they'll be loaded by the browser even if they're not actually used?

I'm working with Next.js and I'm currently naming all icons one by one and it's cumbersome, and I wish to import all icons and kinda hope tree shacking will remove those that aren't actually used.

robmadole commented 4 years ago

Tree-shaking isn't quite this smart. It doesn't know if you are dynamically using an icon.

Right now we don't have anything better than just importing the icons individually. It's a pain, but that's the cost of keeping the bundle size small at the moment. We've got plans for helping with this in the future.

Vadorequest commented 4 years ago

Thanks for confirming, that's what I assumed and I'll keep on importing them one by one until a better alternative comes to light!

jimjeffers commented 4 years ago

Hmm I'm noticing my bundle is huge due to font awesome. It seems the entire lib is importing even though I'm only importing a few dozen icons using the explicit method. Any guidance on how to troubleshoot why the tree shaking might now be working? This is in a CRA boilerplate app using react-scripts for the build process.

Vadorequest commented 4 years ago

Are you talking about the production or development build? Because it seems optimizations happens only for the production build.

robmadole commented 4 years ago

@jimjeffers also, please look at the actual file size (not the web pack analyzer) for the production build. We've seen instances where the analyzer reports larger file sizes than what is really there.

jimjeffers commented 4 years ago

Thanks for getting back to me. I could adjust the project to use deep imports if I have to that just seems like a big hassle so hopefully I can figure out the cause of this. Also for reference this is a mono-repo that has two packages that CRA SPAs.

Right here is the output after running:

NODE_ENV=production yarn build

yarn workspace v1.22.5 yarn run v1.22.5 $ react-scripts build Creating an optimized production build...

File sizes after gzip:

1.69 MB build/static/js/2.8f24654d.chunk.js 55.47 KB build/static/js/main.9f461445.chunk.js 41.39 KB build/static/js/3.39ba6be8.chunk.js 21.57 KB build/static/js/4.e2b6951c.chunk.js 4.35 KB build/static/css/main.5aa5159a.chunk.css 2.81 KB build/static/css/2.19b7558e.chunk.css 1.2 KB build/static/js/runtime-main.97381a6e.js 195 B build/static/js/5.3a6c9a11.chunk.js 195 B build/static/js/6.e5073874.chunk.js

I'm using Node 12.18.4 and here's my package.json:

{
  "name": "web-client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@apollo/client": "^3.2.1",
    "@babel/runtime": "^7.7.2",
    "@fortawesome/fontawesome-svg-core": "^1.2.20",
    "@fortawesome/pro-light-svg-icons": "^5.10.0",
    "@fortawesome/pro-regular-svg-icons": "^5.10.0",
    "@fortawesome/pro-solid-svg-icons": "^5.10.0",
    "@fortawesome/react-fontawesome": "^0.1.4",
    "@hookform/resolvers": "^1.0.0",
    "@sentry/browser": "^5.10.2",
    "@sentry/react": "^5.25.0",
    "@sentry/tracing": "^5.25.0",
    "graphql": "^15.3.0",
    "graphql-tag": "^2.10.1",
    "hsportal-schema": "1.0.0",
    "luxon": "^1.17.2",
    "react": "^16.13.1",
    "react-dnd": "^11.1.3",
    "react-dnd-html5-backend": "^11.1.3",
    "react-dom": "^16.13.1",
    "react-ga": "^2.7.0",
    "react-google-recaptcha": "^1.1.0",
    "react-helmet": "^5.2.1",
    "react-hook-form": "^6.9.6",
    "react-redux": "^7.1.0",
    "react-router": "5.1.2",
    "react-router-dom": "5.1.2",
    "react-scripts": "3.4.3",
    "session": "1.0.0",
    "typescript": "~3.7.2",
    "ui": "1.0.0",
    "utils": "1.0.0",
    "uuid": "^3.3.2",
    "validations": "1.0.0",
    "validator": "^11.1.0",
    "yup": "^0.27.0"
  },
  "devDependencies": {
    "@graphql-codegen/cli": "^1.13.2",
    "@graphql-codegen/fragment-matcher": "^1.13.2",
    "@graphql-codegen/introspection": "^1.13.2",
    "@graphql-codegen/typescript": "^1.13.2",
    "@graphql-codegen/typescript-operations": "^1.13.2",
    "@graphql-codegen/typescript-react-apollo": "^1.13.2",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "@types/draft-js": "^0.10.38",
    "@types/draftjs-to-html": "^0.8.0",
    "@types/graphql": "^14.2.3",
    "@types/humps": "^1.1.2",
    "@types/jest": "^24.0.0",
    "@types/jwt-decode": "^2.2.1",
    "@types/luxon": "^1.15.2",
    "@types/node": "^12.0.0",
    "@types/react": "^16.9.0",
    "@types/react-dom": "^16.9.0",
    "@types/react-helmet": "^5.0.8",
    "@types/react-redux": "^7.1.1",
    "@types/react-router": "^5.1.2",
    "@types/react-router-dom": "^5.1.0",
    "@types/react-text-mask": "^5.4.6",
    "@types/redux": "^3.6.0",
    "@types/uuid": "^3.4.5",
    "@types/yup": "^0.26.22",
    "prettier": "^2.1.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "deploy": "yarn build && sls deploy client",
    "prettier": "prettier --config ../../.prettierrc 'src/**/*.{ts,tsx}' --write"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

But running ls -lh returns the following:

jimjeffers@Jims-iMac mcss % ls -lh packages/web-client/build/static/js
total 34736
-rw-r--r--  1 jimjeffers  staff   5.5M Nov  4 10:14 2.8f24654d.chunk.js
-rw-r--r--  1 jimjeffers  staff   2.2K Nov  4 10:14 2.8f24654d.chunk.js.LICENSE.txt
-rw-r--r--  1 jimjeffers  staff   8.9M Nov  4 10:14 2.8f24654d.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   142K Nov  4 10:14 3.39ba6be8.chunk.js
-rw-r--r--  1 jimjeffers  staff   808B Nov  4 10:14 3.39ba6be8.chunk.js.LICENSE.txt
-rw-r--r--  1 jimjeffers  staff   620K Nov  4 10:14 3.39ba6be8.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   106K Nov  4 10:14 4.e2b6951c.chunk.js
-rw-r--r--  1 jimjeffers  staff   275K Nov  4 10:14 4.e2b6951c.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   233B Nov  4 10:14 5.3a6c9a11.chunk.js
-rw-r--r--  1 jimjeffers  staff   203B Nov  4 10:14 5.3a6c9a11.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   233B Nov  4 10:14 6.e5073874.chunk.js
-rw-r--r--  1 jimjeffers  staff   209B Nov  4 10:14 6.e5073874.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   391K Nov  4 10:14 main.9f461445.chunk.js
-rw-r--r--  1 jimjeffers  staff   1.0M Nov  4 10:14 main.9f461445.chunk.js.map
-rw-r--r--  1 jimjeffers  staff   2.4K Nov  4 10:14 runtime-main.97381a6e.js
-rw-r--r--  1 jimjeffers  staff    12K Nov  4 10:14 runtime-main.97381a6e.js.map

And here's a screenshot of that large bundle after running:

source-map-explorer 'build/static/js/*.js'

Screen Shot 2020-11-04 at 10 24 01 AM
jimjeffers commented 4 years ago

Also worth noting is that the CRA projects are importing a ui package which as a library of shared components and this package also uses font-awesome/react-fonteawsome. Perhaps that is why the tree shaking might not be working as usual?

Just to be sure I found the other chunk where our UI package exists in the build. Doesn't seem that font awesome was duped because of the monorepo but maybe it's still possible:

Screen Shot 2020-11-04 at 10 33 19 AM
jimjeffers commented 3 years ago

Just following up on this one guys. It turns out the other packages in my monorepo were compiling with "CommonJS" which doesn't support tree shaking. I changed the "module" setting to "es6" and my issue went away! I recommend pointing people to webpack's docs on tree shaking -- specifically scroll down to the conclusion section which provides a good checklist:

https://webpack.js.org/guides/tree-shaking/

Screen Shot 2020-12-19 at 10 07 23 AM
thijssmudde commented 2 years ago

@jimjeffers what did you change exactly to make sure only the FA icons are included that you are using in CRA?

jimjeffers commented 2 years ago

@fullhdpixel I just set the "module" attribute in my tsconfig.json to compile my sibling packages as ES6 instead of commonJS. It's been a few years so your mileage may vary with modern projects and FA6. But in my case I was compiling the packages in my monorepo as commonJS so that my jest tests wouldn't complain about transpiling dependencies. Webpack can't perform tree shaking on anything compiled in CommonJS.