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

Implement granular chunks #7696

Merged
merged 101 commits into from Aug 8, 2019
Merged

Implement granular chunks #7696

merged 101 commits into from Aug 8, 2019

Conversation

@atcastle
Copy link
Contributor

atcastle commented Jun 27, 2019

This PR implements the new Webpack SplitChunksPlugin configuration described in #7631 behind a new experimental flag called "granularChunks".

This also refactors the various SplitChunksPlugin configs out of the main config object, as it was getting unwieldy with the introduction of the large new config. However, without enabling the granularChunks flag, this change should have no effect at all on the build.

@github-actions

This comment has been minimized.

Copy link

github-actions bot commented Jun 28, 2019

Stats from current PR

Click to expand stats
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 13.3s 13.2s -105ms
node_modules Size 41.3 MB 41.3 MB ⚠️ +2.47 kB
Total Bundle (main, webpack, commons) Size 213 kB 213 kB
Total Bundle (main, webpack, commons) gzip Size 70.1 kB 70.1 kB
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.8 kB 8.8 kB
Client _error gzip Size 3.31 kB 3.31 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.86 kB 4.86 kB
Client pages/link gzip Size 2.08 kB 2.08 kB
Client pages/routerDirect Size 411 B 411 B
Client pages/routerDirect gzip Size 296 B 296 B
Client pages/withRouter Size 393 B 393 B
Client pages/withRouter gzip Size 280 B 280 B
Client main Size 27.6 kB 27.6 kB
Client main gzip Size 9.14 kB 9.14 kB
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.6 kB 59.6 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.34 kB 1.34 kB
Build Dir Size 805 kB 805 kB
Click to expand serverless stats
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 14.7s 14.5s -204ms
node_modules Size 41.3 MB 41.3 MB ⚠️ +2.47 kB
Total Bundle (main, webpack, commons) Size 213 kB 213 kB
Total Bundle (main, webpack, commons) gzip Size 70.1 kB 70.1 kB
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.8 kB 8.8 kB
Client _error gzip Size 3.31 kB 3.31 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.86 kB 4.86 kB
Client pages/link gzip Size 2.08 kB 2.08 kB
Client pages/routerDirect Size 411 B 411 B
Client pages/routerDirect gzip Size 296 B 296 B
Client pages/withRouter Size 393 B 393 B
Client pages/withRouter gzip Size 280 B 280 B
Client main Size 27.6 kB 27.6 kB
Client main gzip Size 9.14 kB 9.14 kB
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.6 kB 59.6 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 340 kB 340 kB
Serverless pages/link gzip Size 87.2 kB 87.2 kB ⚠️ +1 B
Serverless pages/index Size 331 kB 331 kB
Serverless pages/index gzip Size 84.7 kB 84.7 kB ⚠️ +1 B
Serverless pages/_error Size 330 kB 330 kB
Serverless pages/_error gzip Size 84.4 kB 84.4 kB ⚠️ +2 B
Serverless pages/routerDirect Size 331 kB 331 kB
Serverless pages/routerDirect gzip Size 84.7 kB 84.7 kB ⚠️ +2 B
Serverless pages/withRouter Size 331 kB 331 kB
Serverless pages/withRouter gzip Size 84.6 kB 84.6 kB ⚠️ +1 B
Build Dir Size 2.3 MB 2.3 MB
@github-actions

This comment has been minimized.

Copy link

github-actions bot commented Jul 8, 2019

Stats from current PR

Click to expand stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 14.3s 14.5s ⚠️ +197ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +7.93 kB
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.77 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +625 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.76 kB 4.76 kB
Client pages/link gzip Size 2.03 kB 2.03 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 24.3 kB 26 kB ⚠️ +1.77 kB
Client main gzip Size 7.97 kB 8.6 kB ⚠️ +625 B
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.5 kB 59.5 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.36 kB 1.36 kB
Build Dir Size 785 kB 791 kB ⚠️ +6.22 kB
Click to expand serverless stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 16s 16.1s ⚠️ +119ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +7.93 kB
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.77 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +625 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.76 kB 4.76 kB
Client pages/link gzip Size 2.03 kB 2.03 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 24.3 kB 26 kB ⚠️ +1.77 kB
Client main gzip Size 7.97 kB 8.6 kB ⚠️ +625 B
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.5 kB 59.5 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 338 kB 338 kB
Serverless pages/link gzip Size 86.7 kB 86.7 kB
Serverless pages/index Size Error getting size Error getting size
Serverless pages/index gzip Size Error getting size Error getting size
Serverless pages/_error Size 328 kB 328 kB
Serverless pages/_error gzip Size 84 kB 84 kB
Serverless pages/routerDirect Size 329 kB 329 kB
Serverless pages/routerDirect gzip Size 84.3 kB 84.3 kB
Serverless pages/withRouter Size 329 kB 329 kB
Serverless pages/withRouter gzip Size 84.2 kB 84.2 kB
Build Dir Size 2.04 MB 2.04 MB ⚠️ +6.22 kB
@Timer Timer removed the request for review from dav-is Jul 9, 2019
@github-actions

This comment has been minimized.

Copy link

github-actions bot commented Jul 9, 2019

Stats from current PR

Click to expand stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 13.8s 13.7s -114ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +7.93 kB
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.77 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +625 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.76 kB 4.76 kB
Client pages/link gzip Size 2.03 kB 2.03 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 24.3 kB 26 kB ⚠️ +1.77 kB
Client main gzip Size 7.97 kB 8.6 kB ⚠️ +625 B
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.5 kB 59.5 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.36 kB 1.36 kB
Build Dir Size 785 kB 791 kB ⚠️ +6.22 kB
Click to expand serverless stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 15.8s 15.8s ⚠️ +15ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +7.93 kB
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.77 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +625 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.76 kB 4.76 kB
Client pages/link gzip Size 2.03 kB 2.03 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 24.3 kB 26 kB ⚠️ +1.77 kB
Client main gzip Size 7.97 kB 8.6 kB ⚠️ +625 B
Client commons Size 183 kB 183 kB
Client commons gzip Size 59.5 kB 59.5 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 338 kB 338 kB
Serverless pages/link gzip Size 86.7 kB 86.7 kB
Serverless pages/index Size Error getting size Error getting size
Serverless pages/index gzip Size Error getting size Error getting size
Serverless pages/_error Size 328 kB 328 kB
Serverless pages/_error gzip Size 84 kB 84 kB
Serverless pages/routerDirect Size 329 kB 329 kB
Serverless pages/routerDirect gzip Size 84.3 kB 84.3 kB -2 B
Serverless pages/withRouter Size 329 kB 329 kB
Serverless pages/withRouter gzip Size 84.2 kB 84.2 kB ⚠️ +1 B
Build Dir Size 2.04 MB 2.04 MB ⚠️ +6.22 kB
packages/next/client/page-loader.js Outdated Show resolved Hide resolved
packages/next/client/page-loader.js Outdated Show resolved Hide resolved
packages/next/client/page-loader.js Outdated Show resolved Hide resolved
packages/next/client/page-loader.js Outdated Show resolved Hide resolved
packages/next/build/webpack-config.ts Outdated Show resolved Hide resolved
packages/next/build/webpack-config.ts Outdated Show resolved Hide resolved
@github-actions

This comment has been minimized.

Copy link

github-actions bot commented Jul 11, 2019

Stats from current PR

Click to expand stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 14.3s 14.4s ⚠️ +63ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +117 B
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.75 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +643 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.19 kB 4.76 kB ⚠️ +564 B
Client pages/link gzip Size 1.82 kB 2.03 kB ⚠️ +208 B
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 23.7 kB 26 kB ⚠️ +2.35 kB
Client main gzip Size 7.81 kB 8.6 kB ⚠️ +786 B
Client commons Size 184 kB 183 kB -602 B
Client commons gzip Size 59.7 kB 59.5 kB -143 B
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.36 kB 1.36 kB
Build Dir Size 757 kB 791 kB ⚠️ +34.7 kB
Click to expand serverless stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 15.3s 16.4s ⚠️ +1.1s
node_modules Size 45.4 MB 45.4 MB ⚠️ +117 B
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.75 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +643 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.19 kB 4.76 kB ⚠️ +564 B
Client pages/link gzip Size 1.82 kB 2.03 kB ⚠️ +208 B
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 23.7 kB 26 kB ⚠️ +2.35 kB
Client main gzip Size 7.81 kB 8.6 kB ⚠️ +786 B
Client commons Size 184 kB 183 kB -602 B
Client commons gzip Size 59.7 kB 59.5 kB -143 B
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 338 kB 338 kB -31 B
Serverless pages/link gzip Size 86.8 kB 86.7 kB -26 B
Serverless pages/index Size Error getting size Error getting size
Serverless pages/index gzip Size Error getting size Error getting size
Serverless pages/_error Size 328 kB 328 kB -31 B
Serverless pages/_error gzip Size 84 kB 84 kB -26 B
Serverless pages/routerDirect Size 329 kB 329 kB -31 B
Serverless pages/routerDirect gzip Size 84.3 kB 84.3 kB -26 B
Serverless pages/withRouter Size 329 kB 329 kB -31 B
Serverless pages/withRouter gzip Size 84.3 kB 84.2 kB -24 B
Build Dir Size 1.94 MB 2.04 MB ⚠️ +98.3 kB
@github-actions

This comment has been minimized.

Copy link

github-actions bot commented Jul 11, 2019

Stats from current PR

Click to expand stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 14.5s 14.8s ⚠️ +228ms
node_modules Size 45.4 MB 45.4 MB ⚠️ +21 B
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.71 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +640 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.19 kB 4.76 kB ⚠️ +564 B
Client pages/link gzip Size 1.82 kB 2.03 kB ⚠️ +208 B
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 23.7 kB 26 kB ⚠️ +2.32 kB
Client main gzip Size 7.81 kB 8.6 kB ⚠️ +783 B
Client commons Size 184 kB 183 kB -602 B
Client commons gzip Size 59.7 kB 59.5 kB -143 B
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.36 kB 1.36 kB
Build Dir Size 757 kB 791 kB ⚠️ +34.5 kB
Click to expand serverless stats ⚠️ Total Bundle Size Increase ⚠️
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 15.5s 16.7s ⚠️ +1.2s
node_modules Size 45.4 MB 45.4 MB ⚠️ +21 B
Total Bundle (main, webpack, commons) Size 210 kB 212 kB ⚠️ +1.71 kB
Total Bundle (main, webpack, commons) gzip Size 68.8 kB 69.4 kB ⚠️ +640 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.45 kB 8.45 kB
Client _error gzip Size 3.24 kB 3.24 kB
Client pages/index Size 296 B 296 B
Client pages/index gzip Size 222 B 222 B
Client pages/link Size 4.19 kB 4.76 kB ⚠️ +564 B
Client pages/link gzip Size 1.82 kB 2.03 kB ⚠️ +208 B
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 23.7 kB 26 kB ⚠️ +2.32 kB
Client main gzip Size 7.81 kB 8.6 kB ⚠️ +783 B
Client commons Size 184 kB 183 kB -602 B
Client commons gzip Size 59.7 kB 59.5 kB -143 B
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 338 kB 338 kB -31 B
Serverless pages/link gzip Size 86.8 kB 86.7 kB -27 B
Serverless pages/index Size Error getting size Error getting size
Serverless pages/index gzip Size Error getting size Error getting size
Serverless pages/_error Size 328 kB 328 kB -31 B
Serverless pages/_error gzip Size 84 kB 84 kB -28 B
Serverless pages/routerDirect Size 329 kB 329 kB -31 B
Serverless pages/routerDirect gzip Size 84.3 kB 84.3 kB -29 B
Serverless pages/withRouter Size 329 kB 329 kB -31 B
Serverless pages/withRouter gzip Size 84.3 kB 84.2 kB -25 B
Build Dir Size 1.94 MB 2.04 MB ⚠️ +98.1 kB
Co-Authored-By: Jason Miller <developit@users.noreply.github.com>
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 7, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 22.9s 22s -880ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +2.41 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 23.6s 23.6s ⚠️ +18ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +82 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB -1 B
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 300 B -1 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB -1 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +2.41 kB
Diff for main.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,31 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+    this.promisedBuildManifest = new _promise["default"](function (resolve) {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = function () {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1156,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1193,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1211,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1263,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1289,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1311,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1387,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,23 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+    this.promisedBuildManifest = new _promise.default(resolve => {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = () => {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +931,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +962,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1030,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1042,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1057,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 7, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 21.5s 21.9s ⚠️ +377ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +82 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB ⚠️ +1 B
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 300 B 301 B ⚠️ +1 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +81 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +2.41 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 23.8s 23.2s -583ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB ⚠️ +2 B
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB ⚠️ +3 B
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB ⚠️ +2 B
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB ⚠️ +2 B
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB ⚠️ +2 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +2.41 kB
Diff for main.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,31 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+    this.promisedBuildManifest = new _promise["default"](function (resolve) {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = function () {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1156,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1193,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1211,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1263,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1289,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1311,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1387,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,23 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+    this.promisedBuildManifest = new _promise.default(resolve => {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = () => {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +931,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +962,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1030,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1042,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1057,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 7, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 20.5s 22.1s ⚠️ +1.6s
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +82 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB ⚠️ +1 B
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 300 B 301 B ⚠️ +1 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +81 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +2.41 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 22.2s 22s -192ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB ⚠️ +1 B
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB ⚠️ +1 B
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB ⚠️ +1 B
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB ⚠️ +2 B
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB ⚠️ +1 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +2.41 kB
Diff for main.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,31 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+    this.promisedBuildManifest = new _promise["default"](function (resolve) {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = function () {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1156,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1193,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1211,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1263,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1289,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1311,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1387,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,23 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+    this.promisedBuildManifest = new _promise.default(resolve => {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = () => {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +931,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +962,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1030,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1042,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1057,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
Co-Authored-By: Joe Haddad <timer150@gmail.com>
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 7, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 22.3s 22.1s -191ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +2.41 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 23s 23.6s ⚠️ +661ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB ⚠️ +2 B
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB ⚠️ +3 B
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB ⚠️ +3 B
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB ⚠️ +2 B
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB ⚠️ +2 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +2.41 kB
Diff for main.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,31 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+    this.promisedBuildManifest = new _promise["default"](function (resolve) {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = function () {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1156,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1193,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1211,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1263,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1289,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1311,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1387,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,23 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+    this.promisedBuildManifest = new _promise.default(resolve => {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = () => {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +931,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +962,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1030,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1042,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1057,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 8, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 20.1s 19.9s -179ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +2.41 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 20.8s 21.2s ⚠️ +363ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.5 kB
Total Bundle (main, webpack, commons) Size 207 kB 207 kB -34 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +83 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -265 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -16 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.8 kB -34 B
Client main gzip Size 5.46 kB 5.54 kB ⚠️ +83 B
Client main Modern Size 12.8 kB 12.6 kB -265 B
Client main Modern gzip Size 4.83 kB 4.82 kB -16 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB ⚠️ +3 B
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB ⚠️ +3 B
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB ⚠️ +3 B
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB ⚠️ +3 B
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB ⚠️ +1 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +2.41 kB
Diff for main.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,31 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+    this.promisedBuildManifest = new _promise["default"](function (resolve) {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = function () {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1156,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1193,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1211,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1263,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1289,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1311,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1387,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,20 +1,5 @@
 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
-
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,23 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+    this.promisedBuildManifest = new _promise.default(resolve => {
+      if (window.__BUILD_MANIFEST) {
+        resolve(window.__BUILD_MANIFEST);
+      } else {
+        window.__BUILD_MANIFEST_CB = () => {
+          resolve(window.__BUILD_MANIFEST);
+        };
+      }
+    });
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +931,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +962,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1030,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1042,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1057,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
@ijjk

This comment has been minimized.

Copy link
Member

ijjk commented Aug 8, 2019

Stats from current PR

Click to expand stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 20.7s 20.2s -498ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.6 kB
Total Bundle (main, webpack, commons) Size 207 kB 206 kB -206 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +40 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -423 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -53 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.6 kB -206 B
Client main gzip Size 5.46 kB 5.5 kB ⚠️ +40 B
Client main Modern Size 12.8 kB 12.4 kB -423 B
Client main Modern gzip Size 4.83 kB 4.78 kB -53 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Base Rendered Size 2.76 kB 2.76 kB
Build Dir Size 1.39 MB 1.39 MB ⚠️ +1.74 kB
Click to expand serverless stats Total Bundle Size Decrease
zeit/next.js canary atcastle/next.js implement-granular-chunks Change
Build Duration 22s 22.2s ⚠️ +268ms
node_modules Size 43.6 MB 43.6 MB ⚠️ +12.6 kB
Total Bundle (main, webpack, commons) Size 207 kB 206 kB -206 B
Total Bundle (main, webpack, commons) gzip Size 68.1 kB 68.2 kB ⚠️ +40 B
Total Bundle (main, webpack, commons) Modern Size 182 kB 181 kB -423 B
Total Bundle (main, webpack, commons) Modern gzip Size 59.9 kB 59.9 kB -53 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _app Modern Size 1.83 kB 1.83 kB
Client _app gzip Modern Size 890 B 890 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client _error Modern Size 5.85 kB 5.85 kB
Client _error gzip Modern Size 2.33 kB 2.33 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/index Modern Size 319 B 319 B
Client pages/index gzip Modern Size 254 B 254 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/link Modern Size 3.7 kB 3.7 kB
Client pages/link gzip Modern Size 1.7 kB 1.7 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/routerDirect Modern Size 411 B 411 B
Client pages/routerDirect gzip Modern Size 314 B 314 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client pages/withRouter Modern Size 423 B 423 B
Client pages/withRouter gzip Modern Size 309 B 309 B
Client main Size 15.8 kB 15.6 kB -206 B
Client main gzip Size 5.46 kB 5.5 kB ⚠️ +40 B
Client main Modern Size 12.8 kB 12.4 kB -423 B
Client main Modern gzip Size 4.83 kB 4.78 kB -53 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.3 kB 61.3 kB
Client commons Modern Size 169 kB 169 kB
Client commons Modern gzip Size 55.1 kB 55.1 kB
Client webpack Size 1.53 kB 1.53 kB
Client webpack gzip Size 778 B 778 B
Client webpack Modern Size 1.53 kB 1.53 kB
Client webpack Modern gzip Size 785 B 785 B
Serverless pages/link Size 255 kB 255 kB
Serverless pages/link gzip Size 68.6 kB 68.6 kB ⚠️ +2 B
Serverless pages/index Size 248 kB 248 kB
Serverless pages/index gzip Size 66.4 kB 66.4 kB ⚠️ +2 B
Serverless pages/_error Size 247 kB 247 kB
Serverless pages/_error gzip Size 66.1 kB 66.1 kB
Serverless pages/routerDirect Size 248 kB 248 kB
Serverless pages/routerDirect gzip Size 66.3 kB 66.3 kB ⚠️ +1 B
Serverless pages/withRouter Size 248 kB 248 kB
Serverless pages/withRouter gzip Size 66.4 kB 66.4 kB ⚠️ +2 B
Build Dir Size 2.59 MB 2.6 MB ⚠️ +1.74 kB
Diff for main.js
@@ -1,19 +1,4 @@
-(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[2],{
-
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
+(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -919,15 +882,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -995,17 +949,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1083,22 +1026,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -1122,12 +1049,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -1157,13 +1082,24 @@ function () {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set["default"]();
     this.pageRegisterEvents = (0, _mitt["default"])();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise["default"].resolve();
-  }
+
+    if (false) {}
+  } // Returns a promise for the dependencies for a particular route
+
 
   (0, _createClass2["default"])(PageLoader, [{
+    key: "getDependencies",
+    value: function getDependencies(route) {
+      return this.promisedBuildManifest.then(function (man) {
+        return man[route] && man[route].map(function (url) {
+          return "/_next/" + url;
+        }) || [];
+      });
+    }
+  }, {
     key: "normalizeRoute",
     value: function normalizeRoute(route) {
       if (route[0] !== '/') {
@@ -1213,13 +1149,14 @@ function () {
 
         if (document.getElementById("__NEXT_PAGE__" + route)) {
           return;
-        } // Load the script if not asked to load yet.
-
+        }
 
         if (!_this3.loadingRoutes[route]) {
-          _this3.loadScript(route);
+          if (false) {} else {
+            _this3.loadRoute(route);
 
-          _this3.loadingRoutes[route] = true;
+            _this3.loadingRoutes[route] = true;
+          }
         }
       });
     }
@@ -1249,14 +1186,14 @@ function () {
       });
     }
   }, {
-    key: "loadScript",
-    value: function loadScript(route) {
+    key: "loadRoute",
+    value: function loadRoute(route) {
       var _this = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee() {
-        var scriptRoute, script, url;
+        var scriptRoute, url;
         return _regenerator["default"].wrap(function _callee$(_context) {
           while (1) {
             switch (_context.prev = _context.next) {
@@ -1267,41 +1204,51 @@ function () {
               case 2:
                 route = _this.normalizeRoute(route);
                 scriptRoute = route === '/' ? '/index.js' : route + ".js";
-                script = document.createElement('script');
-
-                if ( true && 'noModule' in script) {
-                  script.type = 'module';
-                  scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-                }
-
                 url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-                script.crossOrigin = "anonymous";
-                script.src = url;
-
-                script.onerror = function () {
-                  var error = new Error("Error loading script " + url);
-                  error.code = 'PAGE_LOAD_ERROR';
 
-                  _this.pageRegisterEvents.emit(route, {
-                    error: error
-                  });
-                };
+                _this.loadScript(url, route, true);
 
-                document.body.appendChild(script);
-
-              case 11:
+              case 6:
               case "end":
                 return _context.stop();
             }
           }
         }, _callee);
       }))();
+    }
+  }, {
+    key: "loadScript",
+    value: function loadScript(url, route, isPage) {
+      var _this5 = this;
+
+      var script = document.createElement('script');
+
+      if ( true && 'noModule' in script) {
+        script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+        // dependencies already have it added during build manifest creation
+
+        if (isPage) url = url.replace(/\.js$/, '.module.js');
+      }
+
+      script.crossOrigin = "anonymous";
+      script.src = url;
+
+      script.onerror = function () {
+        var error = new Error("Error loading script " + url);
+        error.code = 'PAGE_LOAD_ERROR';
+
+        _this5.pageRegisterEvents.emit(route, {
+          error: error
+        });
+      };
+
+      document.body.appendChild(script);
     } // This method if called by the route code.
 
   }, {
     key: "registerPage",
     value: function registerPage(route, regFn) {
-      var _this5 = this;
+      var _this6 = this;
 
       var register = function register() {
         try {
@@ -1309,21 +1256,21 @@ function () {
               error = _regFn.error,
               page = _regFn.page;
 
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error,
             page: page
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error,
             page: page
           });
         } catch (error) {
-          _this5.pageCache[route] = {
+          _this6.pageCache[route] = {
             error: error
           };
 
-          _this5.pageRegisterEvents.emit(route, {
+          _this6.pageRegisterEvents.emit(route, {
             error: error
           });
         }
@@ -1335,17 +1282,21 @@ function () {
     }
   }, {
     key: "prefetch",
-    value: function prefetch(route) {
+    value: function prefetch(route, isDependency) {
       var _this2 = this;
 
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, cn;
+        var scriptRoute, url, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
               case 0:
+                _context2.next = 2;
+                return _this2.promisedBuildId;
+
+              case 2:
                 route = _this2.normalizeRoute(route);
                 scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1353,51 +1304,72 @@ function () {
                   scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
                 }
 
-                if (!(_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route))) {
-                  _context2.next = 5;
+                url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+                // its own deduping mechanism.
+
+                if (!(document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route))) {
+                  _context2.next = 8;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 5:
-                _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-
-
+              case 8:
                 if (!(cn = navigator.connection)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
-                  _context2.next = 9;
+                  _context2.next = 11;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 9:
+              case 11:
+                if (true) {
+                  _context2.next = 17;
+                  break;
+                }
+
+                ;
+                _context2.next = 15;
+                return _this2.getDependencies(route);
+
+              case 15:
+                _context2.t0 = function (url) {
+                  _this2.prefetch(url, true);
+                };
+
+                _context2.sent.forEach(_context2.t0);
+
+              case 17:
                 if (!hasPreload) {
-                  _context2.next = 14;
+                  _context2.next = 20;
                   break;
                 }
 
-                _context2.next = 12;
-                return _this2.promisedBuildId;
+                preloadScript(url);
+                return _context2.abrupt("return");
+
+              case 20:
+                if (!isDependency) {
+                  _context2.next = 22;
+                  break;
+                }
 
-              case 12:
-                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 14:
+              case 22:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 18;
+                  _context2.next = 26;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 18:
+              case 26:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1408,7 +1380,7 @@ function () {
                   });
                 }));
 
-              case 19:
+              case 27:
               case "end":
                 return _context2.stop();
             }
Diff for mainModern.js
@@ -1,19 +1,4 @@
-(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[2],{
-
-/***/ "+iuc":
-/***/ (function(module, exports, __webpack_require__) {
-
-__webpack_require__("wgeU");
-__webpack_require__("FlQf");
-__webpack_require__("bBy9");
-__webpack_require__("B9jh");
-__webpack_require__("dL40");
-__webpack_require__("xvv9");
-__webpack_require__("V+O7");
-module.exports = __webpack_require__("WEpk").Set;
-
-
-/***/ }),
+(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[8],{
 
 /***/ "+oT+":
 /***/ (function(module, exports, __webpack_require__) {
@@ -69,28 +54,6 @@ __webpack_require__("cHUd")('Map');
 
 /***/ }),
 
-/***/ "B9jh":
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-var strong = __webpack_require__("Wu5q");
-var validate = __webpack_require__("n3ko");
-var SET = 'Set';
-
-// 23.2 Set Objects
-module.exports = __webpack_require__("raTm")(SET, function (get) {
-  return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
-}, {
-  // 23.2.3.1 Set.prototype.add(value)
-  add: function add(value) {
-    return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
-  }
-}, strong);
-
-
-/***/ }),
-
 /***/ "BMP1":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -729,15 +692,6 @@ module.exports = __webpack_require__("WEpk").Map;
 
 /***/ }),
 
-/***/ "V+O7":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
-__webpack_require__("aPfg")('Set');
-
-
-/***/ }),
-
 /***/ "XLbu":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -792,17 +746,6 @@ exports.DataManager = DataManager;
 
 /***/ }),
 
-/***/ "dL40":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://github.com/DavidBruant/Map-Set.prototype.toJSON
-var $export = __webpack_require__("Y7ZC");
-
-$export($export.P + $export.R, 'Set', { toJSON: __webpack_require__("8iia")('Set') });
-
-
-/***/ }),
-
 /***/ "dVTT":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -878,22 +821,6 @@ exports.DataManagerContext = React.createContext(null);
 
 /***/ }),
 
-/***/ "ttDY":
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__("+iuc");
-
-/***/ }),
-
-/***/ "xvv9":
-/***/ (function(module, exports, __webpack_require__) {
-
-// https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
-__webpack_require__("cHUd")('Set');
-
-
-/***/ }),
-
 /***/ "zmvN":
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -909,12 +836,10 @@ var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__("+oT+"));
 
 var _promise = _interopRequireDefault(__webpack_require__("eVuF"));
 
-var _set = _interopRequireDefault(__webpack_require__("ttDY"));
-
 var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
-/* global document */
+/* global document, window */
 
 
 function supportsPreload(el) {
@@ -941,10 +866,16 @@ class PageLoader {
     this.buildId = buildId;
     this.assetPrefix = assetPrefix;
     this.pageCache = {};
-    this.prefetchCache = new _set.default();
     this.pageRegisterEvents = (0, _mitt.default)();
     this.loadingRoutes = {};
     this.promisedBuildId = _promise.default.resolve();
+
+    if (false) {}
+  } // Returns a promise for the dependencies for a particular route
+
+
+  getDependencies(route) {
+    return this.promisedBuildManifest.then(man => man[route] && man[route].map(url => "/_next/" + url) || []);
   }
 
   normalizeRoute(route) {
@@ -993,12 +924,13 @@ class PageLoader {
 
       if (document.getElementById("__NEXT_PAGE__" + route)) {
         return;
-      } // Load the script if not asked to load yet.
-
+      }
 
       if (!this.loadingRoutes[route]) {
-        this.loadScript(route);
-        this.loadingRoutes[route] = true;
+        if (false) {} else {
+          this.loadRoute(route);
+          this.loadingRoutes[route] = true;
+        }
       }
     });
   }
@@ -1023,35 +955,41 @@ class PageLoader {
     });
   }
 
-  loadScript(route) {
+  loadRoute(route) {
     var _this = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
       yield _this.promisedBuildId;
       route = _this.normalizeRoute(route);
       let scriptRoute = route === '/' ? '/index.js' : route + ".js";
-      const script = document.createElement('script');
+      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
 
-      if ( true && 'noModule' in script) {
-        script.type = 'module';
-        scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
-      }
+      _this.loadScript(url, route, true);
+    })();
+  }
 
-      const url = _this.assetPrefix + "/_next/static/" + encodeURIComponent(_this.buildId) + "/pages" + scriptRoute;
-      script.crossOrigin = "anonymous";
-      script.src = url;
+  loadScript(url, route, isPage) {
+    const script = document.createElement('script');
 
-      script.onerror = () => {
-        const error = new Error("Error loading script " + url);
-        error.code = 'PAGE_LOAD_ERROR';
+    if ( true && 'noModule' in script) {
+      script.type = 'module'; // Only page bundle scripts need to have .module added to url,
+      // dependencies already have it added during build manifest creation
 
-        _this.pageRegisterEvents.emit(route, {
-          error
-        });
-      };
+      if (isPage) url = url.replace(/\.js$/, '.module.js');
+    }
 
-      document.body.appendChild(script);
-    })();
+    script.crossOrigin = "anonymous";
+    script.src = url;
+
+    script.onerror = () => {
+      const error = new Error("Error loading script " + url);
+      error.code = 'PAGE_LOAD_ERROR';
+      this.pageRegisterEvents.emit(route, {
+        error
+      });
+    };
+
+    document.body.appendChild(script);
   } // This method if called by the route code.
 
 
@@ -1085,10 +1023,11 @@ class PageLoader {
     register();
   }
 
-  prefetch(route) {
+  prefetch(route, isDependency) {
     var _this2 = this;
 
     return (0, _asyncToGenerator2.default)(function* () {
+      yield _this2.promisedBuildId;
       route = _this2.normalizeRoute(route);
       let scriptRoute = (route === '/' ? '/index' : route) + ".js";
 
@@ -1096,11 +1035,12 @@ class PageLoader {
         scriptRoute = scriptRoute.replace(/\.js$/, '.module.js');
       }
 
-      if (_this2.prefetchCache.has(scriptRoute) || document.getElementById("__NEXT_PAGE__" + route)) {
-        return;
-      }
+      const url = isDependency ? route : _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute; // n.b. If preload is not supported, we fall back to `loadPage` which has
+      // its own deduping mechanism.
 
-      _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
+      if (document.querySelector("link[rel=\"preload\"][href^=\"" + url + "\"]") || document.getElementById("__NEXT_PAGE__" + route)) {
+        return;
+      } // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
 
 
       let cn;
@@ -1110,14 +1050,21 @@ class PageLoader {
         if ((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData) {
           return;
         }
-      } // Feature detection is used to see if preload is supported
+      }
+
+      if (false) {} // Feature detection is used to see if preload is supported
       // If not fall back to loading script tags before the page is loaded
       // https://caniuse.com/#feat=link-rel-preload
 
 
       if (hasPreload) {
-        yield _this2.promisedBuildId;
-        preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
+        preloadScript(url);
+        return;
+      }
+
+      if (isDependency) {
+        // loadPage will automatically handle depencies, so no need to
+        // preload them manually
         return;
       }
 
@Timer
Timer approved these changes Aug 8, 2019
Copy link
Member

Timer left a comment

Fantastic, thank you so much!

@Timer Timer merged commit 3e8b36e into zeit:canary Aug 8, 2019
2 of 4 checks passed
2 of 4 checks passed
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
zeit.next.js in progress
Details
ci/circleci: build Your tests passed on CircleCI!
Details
ci/circleci: test Your tests passed on CircleCI!
Details
@timneutkens

This comment has been minimized.

Copy link
Member

timneutkens commented Aug 8, 2019

So excited for this 🚀❤️

@slorber

This comment has been minimized.

Copy link

slorber commented Aug 15, 2019

Hi,

Just wanted to ask, has anyone done any benchmark and see an improvement with this new config? I'd like to explore the same kind of setup for Gatsby, I guess the heuristics make sense for both Next and Gatsby (gatsbyjs/gatsby#16661)

@stubbornella

This comment has been minimized.

Copy link
Collaborator

stubbornella commented Aug 15, 2019

@slorber we are in the process of benchmarking the improvements against https://zeit.co. It will take a few more weeks to get that finished, but we're happy to share. @atcastle can you share the data you have so far?

@Tom910

This comment has been minimized.

Copy link
Contributor

Tom910 commented Sep 13, 2019

@stubbornella Hi, any updates?

@abraxxas

This comment has been minimized.

Copy link

abraxxas commented Sep 19, 2019

We tried this feature on our project and discovered some weird problem with css chunks. When loading the page via ssr everything works. When navigating to the page via csr we get following error unexpected token .

I checked and bot the name and content of the css chunks are the same in csr and ssr (_next/static/css/1whVq9IAqZiB1lomB8S8Pbp4uA=.14c83442.chunk.css) but chrome somehow complains about the css file when it is loaded via client side render

@stubbornella

This comment has been minimized.

Copy link
Collaborator

stubbornella commented Oct 21, 2019

@abraxxas we think we cleared up this bug. If you see it in latest can you let @atcastle and I know?

@abraxxas

This comment has been minimized.

Copy link

abraxxas commented Oct 22, 2019

@stubbornella just checked with 9.1.2-canary.7 still getting the same error as described above. Is there anything i can do to provide more information for you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.