diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 889c14770ea..e5008ca406d 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -30,6 +30,7 @@ "babel-loader": "8.0.0-beta.0", "babel-plugin-named-asset-import": "^0.1.0", "babel-preset-react-app": "^3.1.1", + "bfj": "5.2.0", "case-sensitive-paths-webpack-plugin": "2.1.2", "chalk": "2.4.1", "css-loader": "0.28.11", diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index f69400dcf63..599aa32ee4e 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -33,6 +33,7 @@ const path = require('path'); const chalk = require('chalk'); const fs = require('fs-extra'); const webpack = require('webpack'); +const bfj = require('bfj'); const config = require('../config/webpack.config.prod'); const paths = require('../config/paths'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); @@ -55,6 +56,10 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); } +// Process CLI arguments +const argv = process.argv.slice(2); +const writeStatsJson = argv.indexOf('--stats') !== -1; + // We require that you explictly set browsers and do not fall back to // browserslist defaults. const { checkBrowsers } = require('react-dev-utils/browsersHelper'); @@ -161,11 +166,20 @@ function build(previousFileSizes) { ); return reject(new Error(messages.warnings.join('\n\n'))); } - return resolve({ + + const resolveArgs = { stats, previousFileSizes, warnings: messages.warnings, - }); + }; + if (writeStatsJson) { + return bfj + .write(paths.appBuild + '/bundle-stats.json', stats.toJson()) + .then(() => resolve(resolveArgs)) + .catch(error => reject(new Error(error))); + } + + return resolve(resolveArgs); }); }); } diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index 437c9f273db..7fe7de4ba46 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -2048,6 +2048,14 @@ will affect your users' experience. ## Analyzing the Bundle Size +When your app grows in size, it's easy for bundles to become bloated. The first step to solving large bundles is understanding what's in them! + +There are many different tools available to analyze bundles, but they typically rely on either **sourcemaps** or **webpack-specific JSON stats**. + +### Using Sourcemaps + +When building for production, sourcemaps are automatically created adjacent to the JS files in `build/static/js`. + [Source map explorer](https://www.npmjs.com/package/source-map-explorer) analyzes JavaScript bundles using the source maps. This helps you understand where code bloat is coming from. @@ -2082,6 +2090,45 @@ npm run build npm run analyze ``` +### Using Webpack Stats JSON + +> Note: this feature is available with react-scripts@2.0 and higher. + +Webpack can produce a JSON manifest that details the bundles, and several tools can use that file to do analysis. + +Unlike with sourcemaps, the JSON file isn't created automatically on build. You must pass a `--stats` flag: + +```sh +npm run build -- --stats +``` + +Once the build is complete, you should have a JSON file located at `build/bundle-stats.json`. + +The quickest way to get insight into your bundle is to drag and drop that JSON file into [Webpack Visualizer](https://chrisbateman.github.io/webpack-visualizer/). + +Another very popular tool is [`webpack-bundle-analyzer`](https://github.com/webpack-contrib/webpack-bundle-analyzer). + +To use `webpack-bundle-analyzer`, start by installing it from NPM: + +```sh +npm install --save webpack-bundle-analyzer +# or, with Yarn: +yarn add webpack-bundle-analyzer +``` + + +In `package.json`, add the following line to `scripts`: + +```diff + "scripts": { ++ "analyze": "npm run build -- --stats && webpack-bundle-analyzer build/bundle-stats.json", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", +``` + +When you run `npm run analyze`, a new build will be created, and a browser tab should open automatically, displaying the sizes of the modules within your bundle. + ## Deployment `npm run build` creates a `build` directory with a production build of your app. Set up your favorite HTTP server so that a visitor to your site is served `index.html`, and requests to static paths like `/static/js/main..js` are served with the contents of the `/static/js/main..js` file.