diff --git a/package.json b/package.json
index 2f428fa0a14..d2f2f86e465 100644
--- a/package.json
+++ b/package.json
@@ -67,8 +67,6 @@
"web:prod": "http-server ./dist --cors"
},
"dependencies": {
- "@babel/plugin-proposal-private-methods": "^7.18.6",
- "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dotlottie/react-player": "^1.6.3",
"@expensify/react-native-live-markdown": "0.1.120",
"@expo/metro-runtime": "~3.1.1",
@@ -77,14 +75,10 @@
"@formatjs/intl-locale": "^4.0.0",
"@formatjs/intl-numberformat": "^8.10.3",
"@formatjs/intl-pluralrules": "^5.2.14",
- "@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
- "@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
"@fullstory/react-native": "^1.4.2",
"@gorhom/portal": "^1.0.14",
"@invertase/react-native-apple-authentication": "^2.2.2",
- "@kie/act-js": "^2.6.2",
- "@kie/mock-github": "2.0.1",
"@onfido/react-native-sdk": "10.6.0",
"@react-native-camera-roll/camera-roll": "7.4.0",
"@react-native-clipboard/clipboard": "^1.13.2",
@@ -102,9 +96,7 @@
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "10.1.26",
"@shopify/flash-list": "1.7.1",
- "@types/mime-db": "^1.43.5",
"@ua/react-native-airship": "19.2.1",
- "@vue/preload-webpack-plugin": "^2.0.0",
"awesome-phonenumber": "^5.4.0",
"babel-polyfill": "^6.26.0",
"canvas-size": "^1.2.6",
@@ -122,8 +114,6 @@
"focus-trap-react": "^10.2.3",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
- "jest-expo": "51.0.3",
- "jest-when": "^3.5.2",
"lodash": "4.17.21",
"lottie-react-native": "6.5.1",
"mapbox-gl": "^2.15.0",
@@ -133,7 +123,6 @@
"react": "18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-collapse": "^5.1.0",
- "react-compiler-runtime": "file:./lib/react-compiler-runtime",
"react-content-loader": "^7.0.0",
"react-dom": "18.3.1",
"react-error-boundary": "^4.0.11",
@@ -189,9 +178,7 @@
"react-plaid-link": "3.3.2",
"react-web-config": "^1.0.0",
"react-webcam": "^7.1.1",
- "react-window": "^1.8.9",
- "semver": "^7.5.2",
- "xlsx": "file:vendor/xlsx-0.20.3.tgz"
+ "react-window": "^1.8.9"
},
"devDependencies": {
"@actions/core": "1.10.0",
@@ -200,6 +187,8 @@
"@babel/parser": "^7.22.16",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.20.0",
"@babel/preset-flow": "^7.12.13",
"@babel/preset-react": "^7.10.4",
@@ -210,7 +199,11 @@
"@callstack/reassure-compare": "^1.0.0-rc.4",
"@dword-design/eslint-plugin-import-alias": "^5.0.0",
"@electron/notarize": "^2.1.0",
+ "@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
+ "@fullstory/babel-plugin-react-native": "^1.2.1",
"@jest/globals": "^29.5.0",
+ "@kie/act-js": "^2.6.2",
+ "@kie/mock-github": "2.0.1",
"@ngneat/falso": "^7.1.1",
"@octokit/core": "4.0.4",
"@octokit/plugin-paginate-rest": "3.1.0",
@@ -242,6 +235,7 @@
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.195",
"@types/mapbox-gl": "^2.7.13",
+ "@types/mime-db": "^1.43.5",
"@types/node": "^20.11.5",
"@types/pusher-js": "^5.1.0",
"@types/react": "^18.2.6",
@@ -258,6 +252,7 @@
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vercel/ncc": "0.38.1",
+ "@vue/preload-webpack-plugin": "^2.0.0",
"@welldone-software/why-did-you-render": "7.0.1",
"ajv-cli": "^5.0.0",
"babel-jest": "29.4.1",
@@ -293,7 +288,9 @@
"jest-circus": "29.4.1",
"jest-cli": "29.4.1",
"jest-environment-jsdom": "^29.4.1",
+ "jest-expo": "51.0.3",
"jest-transformer-svg": "^2.0.1",
+ "jest-when": "^3.5.2",
"link": "^2.1.1",
"memfs": "^4.6.0",
"onchange": "^7.1.0",
@@ -304,10 +301,12 @@
"prettier": "^2.8.8",
"pusher-js-mock": "^0.3.3",
"react-compiler-healthcheck": "^0.0.0-experimental-ab3118d-20240725",
+ "react-compiler-runtime": "file:./lib/react-compiler-runtime",
"react-is": "^18.3.1",
"react-native-clean-project": "^4.0.0-alpha4.0",
"react-test-renderer": "18.3.1",
"reassure": "^1.0.0-rc.4",
+ "semver": "^7.5.2",
"setimmediate": "^1.0.5",
"shellcheck": "^1.1.0",
"source-map": "^0.7.4",
@@ -325,6 +324,7 @@
"webpack-cli": "^5.0.4",
"webpack-dev-server": "^5.0.4",
"webpack-merge": "^5.8.0",
+ "xlsx": "file:vendor/xlsx-0.20.3.tgz",
"yaml": "^2.2.1"
},
"overrides": {
Problem
In our JS bundle we see that we have unrelated code bundled which is never used. This adds up to the bundle size which eventually leads to the slower loading time for the app. For example,
why-did-you-renderis present in the JS bundle which is a red flag, since it is installed as a dev dependency. The other case is withLodashnot being able to tree-shake the unused functions.Apart from this, there are some libraries which are listed in
dependencieswhereas the right place for them isdevDependencies. This doesn't change anything but it's a good practice to keep things where they belong. For example,@kie/act-js,@kie/mock-github,@types/mime-dband others.To summarise, we have three action items that we will discuss in the solutions:
why-did-you-renderin the JS bundleLodashto tree-shake the unused functionsdevDependenciesSolution
Here's the bundle size as of the baseline ~19.18mb
Avoid bundling
why-did-you-renderin the JS bundle:I couldn't figure out the root cause of it. When I comment out the whole
wdyr.ts, then we don't have it bundled. I thought itmight be due to the
import type {} from 'wdyrbut it's not the case either. For some reasons,useWDYRflag is not respected.Now, One of the solution we can apply is to ignore
why-did-you-renderif.envfile is either ofprodorstaging. Below is how we can do this inwebpack.common:Diff
This brings our bundle size to ~19.16mb giving us ~20kb reduction
Allow
Lodashto tree-shake the unused functionsWe have lots of imports like
import {} from 'lodashorimport _ from 'lodash. These all doesn't allow web pack to tree shakeLodash. This brings in a ~110kb increase to the bundle size.See Lodash size
One solution can be to refactor all these imports to named imports like
import isEqual from 'lodash/isEqual'.Otherwise, a better and simple solution with less changes is to use
lodash-esand resolvelodash: lodash-esin from web pack. This avoids the need of a massive refactor. Since esmodules have better tree-shake support with web pack the syntaximport {} from 'lodashworks in terms of tree-shaking. However, we still have to refactor some imports likeimport _ from 'lodash'to the named ones.Opting for the second solution brings us ~70kb reduction and the final bundle size is ~19.09mb
Irrespective of the gains, we see that the size of
lodashandlodash-esis ~110kb. The reason for this is we have the whole imports or de-structured imports inexpensify-common. Once we change those to named imports, everything falls into the place andlodash-esnow takes ~50kb, which reduces the bundle size to ~19.04mb.See bundle size
Move the development libraries to
devDependenciesThis is self explanatory. We just have to move the libraries which are listed in
dependenciesbut their right place isdevDependencies. This doesn't bring any improvement in the bundle size but it's good to place things where they belong.See diff
Bonus
This is something I found out while figuring out different solutions for tree-shaking
Lodash. There's a plugin calledbabel-plugin-lodashwhich only picks the used code fromlodashbut it didn't work. However, while trying it out we have to set:Now, the
lodash-plugintakes no effect on our bundle BUT this target setting reduces the bundle size to 18.4mb. This happens because we are now not targeting the oldest possible browser. This is also recommended inbabeldocs to specify the target for having reduced bundle size.From my testing, setting to
target: node, 20doesn't break anything. We can do QA and see if there's something wrong and adjust the node version accordingly.Final bundle size 18.4mb
Issue Owner
Current Issue Owner: @Upwork Automation - Do Not Edit
Issue Owner
Current Issue Owner: @sobitneupane