Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Create a --stats flag for react-scripts build. Update README.
To analyze Webpack bundles, a "stats" JSON is required.

This PR allows that file to be created and saved to the `build`
directory, so that users can use it with Webpack-specific insight
tools like `webpack-bundle-analyzer` without ejecting their
application.

Updated the README to include details for how to do this.
  • Loading branch information
joshwcomeau committed May 31, 2018
commit 7c8593845830c585a780f4e40268e7b4f442c7a4
1 change: 1 addition & 0 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 16 additions & 2 deletions packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Choose a reason for hiding this comment

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

Big Friendly JSON. Is this really needed?

const config = require('../config/webpack.config.prod');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
Expand All @@ -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;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if stats is the best name... Open to suggestions!

Copy link
Contributor

Choose a reason for hiding this comment

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

I think --stats is quite nice, as it pairs well with webpack --stats flag.


// We require that you explictly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
Expand Down Expand Up @@ -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)));

Choose a reason for hiding this comment

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

As I can see you don't really need BFJ because you just write only one chunk.
fs.writeFile(..., JSON.strigify(stats)) should be more effective by my opinion.

Choose a reason for hiding this comment

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

So, this was a suggestion in the issue #1858, for concerns that with very large bundles, JSON.stringify could run out of memory and crash the node process. This was reported in webpack-bundle-analyzer, where they use JSON.stringify(). bfj is streaming, so it should avoid this potential issue.

Hi @joshwcomeau, thanks for fast reply.
So if I understand, it will traverse object prepared for serialization, write it by converting text piece by piece, potentially wasting more CPU, time and memory too, but it uses small bits of memory which can be collected by GC during processing.

}

return resolve(resolveArgs);
});
});
}
Expand Down
48 changes: 48 additions & 0 deletions packages/react-scripts/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -2082,6 +2090,46 @@ npm run build
npm run analyze
```

### Using Webpack Stats JSON
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add a note that this feature is available only in some specific version of react-scripts, like many other parts of the README have?

Note: this feature is available with [email protected] and higher.

I don't know how the process of adding these version notes goes, though. Are they added just before releasing a new version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, good idea!

I'm also unfamiliar with the process, since yeah I don't know which version it'll be released in. Happy to update the PR if this is knowable in advance, though :)

Copy link
Contributor

Choose a reason for hiding this comment

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

This is going to end up in react-scripts 2.0 so it's probably a good idea to include that in the README. I'm actually not 100% on the process for this either. I don't think these notes are generated automatically so you might as well add it yourself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove this part or hide it behind comments. 2.x is the main branch now so that's what you find when you look at the repository. It will be deeply confusing to the users to see this in README now.

Copy link
Contributor

Choose a reason for hiding this comment

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


> Note: this feature is available with [email protected] 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://www.npmjs.com/package/webpack-bundle-analyzer).
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we link to the GitHub page instead? https://github.com/webpack-contrib/webpack-bundle-analyzer

The README gets a bit squished on the https://www.npmjs.com/package/webpack-bundle-analyzer page 😕. In my opinion, the GitHub rendered page looks nicer.

In any case, this is only a nitpick and can be kept as-is ☺️


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",
"build:with-stats": "react-scripts build",
Copy link
Contributor

@valscion valscion May 31, 2018

Choose a reason for hiding this comment

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

Should this build:with-stats line also have a + in the diff code example? Or should we keep the build:with-stats out from this example altogether?

Also, this example doesn't use the --stats flag correctly so it might even be incorrect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah! Yeah, this is old. Will update.

"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.<hash>.js` are served with the contents of the `/static/js/main.<hash>.js` file.
Expand Down