I'm one of the maintainers of the fork-ts-checker-webpack-plugin. Hi there!
Recently, various issues have been raised against create-react-app (which uses fork-ts-checker-webpack-plugin) as well as against the plugin itself. They've been related to the level of CPU usage in watch mode on idle; i.e. it's high!
Now, under the covers, the
fork-ts-checker-webpack-plugin uses the TypeScript watch API.
The marvellous John (not me - another John) did some digging and discovered the root cause came down to the way that the TypeScript watch API watches files:
TS uses internally the
fs.watchFileAPI functions of nodejs for their watch mode. The latter function is even not recommended by nodejs documentation for performance reasons, and urges to use
Using fs.watch() is more efficient than fs.watchFile and fs.unwatchFile. fs.watch should be used instead of fs.watchFile and fs.unwatchFile when possible.
"there is another"
John also found that there are other file watching behaviours offered by TypeScript. What's more, the file watching behaviour is configurable with an environment variable. That's right, if an environment variable called
TSC_WATCHFILE is set, it controls the file watching approach used. Big news!
John did some rough benchmarking of the performance of the different options that be set on his PC running linux 64 bit. Here's how it came out:
| Value | CPU usage on idle |
| ------------------------------------- | ----------------- |
| TS default _(TSC_WATCHFILE not set)_ | **7\.4%** |
| UseFsEventsWithFallbackDynamicPolling | 0\.2% |
| UseFsEventsOnParentDirectory | 0\.2% |
| PriorityPollingInterval | **6\.2%** |
| DynamicPriorityPolling | 0\.5% |
| UseFsEvents | 0\.2% |
As you can see, the default performs poorly. On the other hand, an option like
UseFsEventsWithFallbackDynamicPolling is comparative greasy lightning.
To get this better experience into your world now, you could just set an environment variable on your machine. However, that doesn't scale; let's instead look at introducing the environment variable into your project explicitly.
We're going to do this in a cross platform way using
cross-env. This is a mighty useful utility by Kent C Dodds which allows you to set environment variables in a way that will work on Windows, Mac and Linux. Imagine it as the jQuery of the environment variables world :-)
Let's add it as a
yarn add -D cross-env
Then take a look at your
package.json. You've probably got a
start script that looks something like this:
"start": "webpack-dev-server --progress --color --mode development --config webpack.config.development.js",
Or if you're a create-react-app user maybe this:
"start": "react-scripts start",
start script with
cross-env TSC_WATCHFILE=UseFsEventsWithFallbackDynamicPolling. This will, when run, initialise an environment variable called
TSC_WATCHFILE with the value
UseFsEventsWithFallbackDynamicPolling. Then it will start your development server as it did before. When TypeScript is fired up by webpack it will see this environment variable and use it to configure the file watching behaviour to one of the more performant options.
So, in the case of a
create-react-app user, your finished
start script would look like this:
"start": "cross-env TSC_WATCHFILE=UseFsEventsWithFallbackDynamicPolling react-scripts start",
There's a possibility that the default watch behaviour may change in TypeScript in future. It's currently under discussion, you can read more here.