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

feat: Improve Next.js/CRA/Angular bundle sizes #7842

Merged
merged 15 commits into from
Mar 16, 2021

Conversation

ericclemmons
Copy link
Contributor

@ericclemmons ericclemmons commented Mar 1, 2021

Issue #, if available: Fixes #7570

This PR investigates why Next.js bundle size with Auth is so large.

  • Add Angular bundle size test
  • Tighten Next.js bundle size test
  • Set crypto: false to prevent require('crypto') polyfills in the browser

Workaround – Remove crypto polyfill for Next.js

Because create-react-app resolves .web.js, #7521 improved bundle-size.

We can do the same via next.config.js:

--- a/samples/next/auth/ssr-auth/next.config.js
+++ b/samples/next/auth/ssr-auth/next.config.js
@@ -1,3 +1,8 @@
 module.exports = {
   target: 'serverless',
+  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
+    config.resolve.extensions = ['.web.js', ...config.resolve.extensions]
+
+    return config
+  },
 }

The results are:

Page                                                           Size     First Load JS
┌ ○ /                                                          3.47 kB        66.2 kB
├   /_app                                                      0 B            62.7 kB
├ ○ /404                                                       3.46 kB        66.2 kB
├ ○ /Amplify                                                   875 B          67.4 kB
├ ○ /Amplify+Auth                                              37.9 kB         142 kB
├ ○ /Amplify+Storage                                           42.6 kB         146 kB
└ λ /api/hello                                                 0 B            62.7 kB
+ First Load JS shared by all                                  62.7 kB
  ├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.015d8f.js  13.1 kB
  ├ chunks/framework.e2fe4a.js                                 41.8 kB
  ├ chunks/main.a1fab4.js                                      6.62 kB
  ├ chunks/pages/_app.3da7c8.js                                530 B
  ├ chunks/webpack.50bee0.js                                   751 B
  └ css/2f26bb9842d84a608fa3.css                               202 B

Down to 142kB from 281kB, a savings of 138kB.

My sample application (using Auth and API) seems to work the same, so it looks like Next.js is polyfilling crypto in the browser only:

Screen Shot 2021-03-01 at 4 21 16 PM

Proposals

diff --git a/packages/amazon-cognito-identity-js/package.json b/packages/amazon-cognito-identity-js/package.json
index 35a3c4df3..c155133bf 100644
--- a/packages/amazon-cognito-identity-js/package.json
+++ b/packages/amazon-cognito-identity-js/package.json
@@ -60,6 +60,9 @@
     "lib/index.js": "./enhance-rn.js",
     "./src/StorageHelper": "./src/StorageHelper-rn.js"
   },
+  "browser": {
+    "crypto": false
+  },
   "module": "es/index.js",
   "jsnext:main": "es/index.js",
   "types": "./index.d.ts",
  • Explicitly map .web.js files for Next.js:
--- a/packages/amazon-cognito-identity-js/package.json
+++ b/packages/amazon-cognito-identity-js/package.json
@@ -60,6 +60,10 @@
     "lib/index.js": "./enhance-rn.js",
     "./src/StorageHelper": "./src/StorageHelper-rn.js"
   },
+  "browser": {
+    "./es/utils/cryptoSecureRandomInt.js": "./es/utils/cryptoSecureRandomInt.web.js",
+    "./lib/utils/cryptoSecureRandomInt.js": "./lib/utils/cryptoSecureRandomInt.web.js"
+  },
   "module": "es/index.js",
   "jsnext:main": "es/index.js",
   "types": "./index.d.ts",
  • [

Research

Next.js baseline

Notice the difference between / and /Amplify+Auth (~215kB):

$ next build
Page                                                           Size     First Load JS
┌ ○ /                                                          3.47 kB        66.2 kB
├   /_app                                                      0 B            62.7 kB
├ ○ /404                                                       3.46 kB        66.2 kB
├ ○ /Amplify                                                   875 B          67.4 kB
├ ○ /Amplify+Auth                                              175 kB          281 kB
├ ○ /Amplify+Storage                                           40.8 kB         146 kB
└ λ /api/hello                                                 0 B            62.7 kB
+ First Load JS shared by all                                  62.7 kB
  ├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.015d8f.js  13.1 kB
  ├ chunks/framework.e2fe4a.js                                 41.8 kB
  ├ chunks/main.a1fab4.js                                      6.62 kB
  ├ chunks/pages/_app.9bbe91.js                                532 B
  ├ chunks/webpack.50bee0.js                                   751 B
  └ css/2f26bb9842d84a608fa3.css                               202 B

Screen Shot 2021-03-01 at 2 58 00 PM

This PR also introduces caps per-page to catch any future regressions:

$ yarn calculate

PASS .next/static/chunks/pages/Amplify-89b1e9717f9057dfb11f.js: 875B < 1KB (gzip)
PASS .next/static/chunks/pages/Amplify+Auth-0e436b759b6463284bac.js: 171.27KB < 175KB (gzip)
PASS .next/static/chunks/pages/Amplify+Storage-ab8e12816b1637a7dace.js: 34.86KB < 40KB (gzip)
PASS .next/static/chunks/pages/index-7d4f95fdaf137def4d7f.js: 3.39KB < 5KB (gzip)

CRA baseline

Based on the following App.js:

+ import { useEffect } from 'react';
import logo from './logo.svg';
import './App.css';

+ import { Amplify, Auth } from 'aws-amplify';
+
+ Amplify.configure();

+ function App() {
+	useEffect(() => {
+		Auth.currentAuthenticatedUser()
+			.then(console.log)
+			.catch(console.warn);
+	}, []);
...
$ SKIP_PREFLIGHT_CHECK=true react-scripts build
Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  84.97 KB (+43.64 KB)  build/static/js/2.8d76dd43.chunk.js
  31.08 KB (+30.51 KB)  build/static/js/main.f0f7bab4.chunk.js
  1.59 KB (-1 B)        build/static/js/3.c879f5a9.chunk.js
  1.16 KB (-1 B)        build/static/js/runtime-main.45a4c9fb.js
  531 B                 build/static/css/main.8c8b27cf.chunk.css

Screen Shot 2021-03-01 at 2 59 55 PM

Why so many bn.js?

Looks like crypto polyfills, which #7521 solved for CRA.

$ yarn why bn.js
=> Found "bn.js@4.12.0"
info Has been hoisted to "bn.js"
info Reasons this module exists
   - Hoisted from "next#crypto-browserify#create-ecdh#bn.js"
   - Hoisted from "next#crypto-browserify#diffie-hellman#bn.js"
   - Hoisted from "next#crypto-browserify#public-encrypt#bn.js"
   - Hoisted from "next#crypto-browserify#diffie-hellman#miller-rabin#bn.js"
   - Hoisted from "next#crypto-browserify#browserify-sign#elliptic#bn.js"
   - Hoisted from "next#crypto-browserify#public-encrypt#parse-asn1#asn1.js#bn.js"
info Disk size without dependencies: "108KB"
info Disk size with unique dependencies: "108KB"
info Disk size with transitive dependencies: "108KB"
info Number of shared dependencies: 0
=> Found "browserify-sign#bn.js@5.2.0"
info This module exists because "next#crypto-browserify#browserify-sign" depends on it.
info Disk size without dependencies: "116KB"
info Disk size with unique dependencies: "116KB"
info Disk size with transitive dependencies: "116KB"
info Number of shared dependencies: 0
=> Found "browserify-rsa#bn.js@5.2.0"
info This module exists because "next#crypto-browserify#public-encrypt#browserify-rsa" depends on it.
info Disk size without dependencies: "116KB"
info Disk size with unique dependencies: "116KB"
info Disk size with transitive dependencies: "116KB"
info Number of shared dependencies: 0

Resources


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@aws-amplify aws-amplify deleted a comment from codecov bot Mar 2, 2021
@codecov
Copy link

codecov bot commented Mar 2, 2021

Codecov Report

Merging #7842 (271b80c) into main (f271594) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #7842   +/-   ##
=======================================
  Coverage   74.25%   74.25%           
=======================================
  Files         215      215           
  Lines       13473    13473           
  Branches     2646     2646           
=======================================
  Hits        10004    10004           
  Misses       3271     3271           
  Partials      198      198           
Impacted Files Coverage Δ
packages/core/src/Platform/version.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0218cf5...271b80c. Read the comment docs.

@ericclemmons ericclemmons requested a review from amhinson March 2, 2021 17:54
@@ -60,6 +60,9 @@
"lib/index.js": "./enhance-rn.js",
"./src/StorageHelper": "./src/StorageHelper-rn.js"
},
"browser": {
"crypto": false
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like @manueliglesias to weigh in here. Since we just went through some fixes for Crypto

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's worth another pair of eyes for sure.

For clarity, this line prevents Webpack 4 (& Next.js) from including a crypto polyfill when it sees require('crypto') in our code.

This is because only CRA supports https://github.com/aws-amplify/amplify-js/blob/ad2dd864ffbe3bc5c620c3078f82805a4ed83439/packages/amazon-cognito-identity-js/src/utils/cryptoSecureRandomInt.web.js, but this line makes https://github.com/aws-amplify/amplify-js/blob/main/packages/amazon-cognito-identity-js/src/utils/cryptoSecureRandomInt.js not bloat up because of require('crypto').

@ericclemmons ericclemmons changed the title Investigation of Next.js bundle sizes Investigation of Next.js/CRA/Angular bundle sizes Mar 3, 2021
Copy link
Contributor Author

@ericclemmons ericclemmons left a comment

Choose a reason for hiding this comment

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

I added comments to the whole PR to navigate 👀 to what matters & why.

@@ -0,0 +1,17 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

"Eric, why did you commit all of this Angular stuff!? Such bloat! Such wow!"

The bundle size tests should match what our "Getting Started" docs instruct users to do.

So if the default is ng init, this is the output of ng init + a bit Amplify.

This way, when a new version comes out of Angular, we can rm -rf angular, ng init the new boilerplate, and unstage the few lines of Amplify.

Hand-picking files/folders to delete would be a PITA.

Comment on lines +57 to +61
(window as any).global = window;
(window as any).process = {
env: { DEBUG: undefined },
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comes from our getting started guide ☝️

Comment on lines +7 to +12
import { Amplify, Auth } from 'aws-amplify';
Amplify.configure();
Auth.currentAuthenticatedUser()
.then(console.info)
.catch(console.warn);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is all the Amplify code we need to test tree-shaking ☝️

},
"scripts": {
"preanalyze": "$_ build",
"analyze": "source-map-explorer 'build/static/js/*.js'",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yarn analyze to see the CRA bundle output in a grid view.

"preanalyze": "$_ build",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"precalculate": "$_ run build",
"calculate": "bundlewatch",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yarn calculate is used by the GitHub action to test bundle sizes

},
{
"path": "build/static/js/2.*.chunk.js",
"maxSize": "90kB"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that bundlewatch reports GZIP sizes instead of raw

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same thing as the Angular note above ☝️ rm -rf cra && yarn create react-app should make this a breeze to test against new versions of CRA.

Comment on lines +5 to +14
import { Amplify, Auth } from 'aws-amplify';

Amplify.configure();

function App() {
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(console.log)
.catch(console.warn);
}, []);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All we need to test tree-shaking in CRA

@@ -0,0 +1,5 @@
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yarn analyze will output the server + client grid view of bundles

Comment on lines +7 to +14
Amplify.configure();

export default function Home() {
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(console.log)
.catch(console.warn);
}, []);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All we need to test tree-shaking in Next.js ☝️

@ericclemmons ericclemmons changed the title Investigation of Next.js/CRA/Angular bundle sizes Improve Next.js/CRA/Angular bundle sizes Mar 3, 2021
@ericclemmons ericclemmons changed the title Improve Next.js/CRA/Angular bundle sizes feat: Improve Next.js/CRA/Angular bundle sizes Mar 4, 2021
Copy link
Contributor

@sammartinez sammartinez left a comment

Choose a reason for hiding this comment

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

LGTM 🌮

@github-actions
Copy link

This pull request has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 17, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding Amplify Auth to a Next.js project has a huge impact of the chunk size
3 participants