Pete's Log: create-react-app woes

Entry #1932, (Coding, Hacking, & CS stuff)
(posted when I was 42 years old.)

We create a lot of React apps at work. Enough so that I've invested a good amount of time in customizing the template we use to create apps. Over time, this has gotten easier. Initially it was a set of instructions on how to customize the app after create-react-app completed. Then it evolved into a fork of the react-scripts package and using the --scripts-version flag to create-react-app. With create-react-app it became even simpler and I now only have to maintain a fork of cra-template-typescript with our customizations.

So creating an app looks something like

npx create-react-app@4.0.3 appname --template g:\path\to\cra-template-customized-1.1.2.tgz

However, recently this has started failing. I verified that it does not fail when using a template from npmjs.org. But it does fail if I download one of their templates and use it from a local path as above. So it's not my template package. The error happens at least as far back as create-react-app 3.4.1 and with the latest version (4.0.3). The error looks as follows:

Could not extract the package name from the archive: Cannot find module 'C:\Users\prijks\AppData\Local\Temp\tmp-26504QtrZqwVdlRb4\package.json' Require stack: - C:\Users\prijks\AppData\Local\npm-cache\_npx\a489907dd8abe499\node_modules\create-react-app\createReactApp.js - C:\Users\prijks\AppData\Local\npm-cache\_npx\a489907dd8abe499\node_modules\create-react-app\index.js Aborting installation. Unexpected error. Please report it as a bug: TypeError: Cannot read property '1' of null at C:\Users\prijks\AppData\Local\npm-cache\_npx\a489907dd8abe499\node_modules\create-react-app\createReactApp.js:735:10 at processTicksAndRejections (internal/process/task_queues.js:93:5) at async Promise.all (index 1)

Line 735 that is causing the error is a red herring, but looks as follows:

const assumedProjectName = installPackage.match( /^.+\/(.+?)(?:-\d+.+)?\.(tgz|tar\.gz)$/ )[1];

Since this is not the source of the issue, I'm not going to reverse engineer the regex. But this line is reached when create-react-app can't locate a package.json file within the tgz template package. When it can't find a package.json file, it tries to assume a project name based on the template file name, but apparently this regex doesn't work in my case.

But my tgz has a package.json file. Why does create-react-app think it doesn't? For some time I didn't want to deal with that question, and I found I can point create-react-app at a directory for my template instead of a tarball, so I didn't invest any further time until now. Now I want to know.

When I began my investigations again, it became clear pretty quickly that create-react-app was trying to read the package.json file before the untar operation completed. The function in createReactApp.js that untars the package looks as follows:

function extractStream(stream, dest) { return new Promise((resolve, reject) => { stream.pipe( unpack(dest, err => { if (err) { reject(err); } else { resolve(dest); } }) ); }); }

Changing resolve(dest) to setTimeout(() => resolve(dest), 10000) fixes the problem for me. Which is obviously a hack.

The unpack function being used is provided by the tar-pack package, which appears to be unmaintained (on GitHub there's a note that says "These projects are not maintained. Send me an e-mail if you want to take over one of them." so that seems pretty clear). This package in turn uses node-tar which does look active. However, it uses version 2.2.1 of node-tar and the most recent release is 6.1.0.

But looking at the history of createReactApp.js on GitHub, the extractStream function and tar-pack dependency don't appear to have changed in years. So why did this suddenly stop working for me? Well, as luck would have it, a number of potential reasons exist:

  • My work laptop recently underwent a major Windows update
  • Anti-virus is always seeing updates
  • I recently updated my version of Node

I looked through the create-react-app issues on GitHub and found a couple (none of them resolved) that could be related

That second one has a few interesting insights: it happens on MacOS and it doesn't happen in Node 12, only in Node 14+. So that gives me a starting point. So I wrote a little test app that just tries to untar a tar file using a few different methods and the see if a file is available instantly after the method completes.

My methods:

  • tar 2.2.1 (the version tar-pack uses)
  • tar 6.1.0 (the latest version)
  • tar-pack

One quick thing I found is tar 2.2.1 doesn't support tgz files, while tar 6.1.0 does. So tar-pack actually handles gunzipping the tgz file before sending it to tar. Beyond that, on my system, I found that both versions of tar seem to work properly, while tar-pack in this simple scenario runs the callback before the files are saved to disk.

Next, on a Windows VM with no previous Node installs, I installed Node 12. I ran my test app, and sure enough, all three methods worked fine. So next I updated to Node 14, and the tar-pack method starts failing. It extracts the files, but it calls the "done" callback before the files are saved.

I suppose at this point I could file an issue with create-react-app with more details, but I feel like even though I don't really have the time for it, I'd like to explore this rabbit hole some more. It's taught me some interesting Node things. It feels like there are two paths forward:

  • Fix tar-pack. Since the underlying tar package works, this should be doable. However since the package is unmaintained, I'm not certain where this takes me.
  • Update create-react-app to not use tar-pack and instead use tar directly. Since the latest version of tar supports tgz files, that eliminates one of the things tar-pack was needed for. But tar-pack also does some things around file permissions and I'd need to figure out if that needs replicating.

Anyway, it's well past my bedtime, so I'm going to call it a night. At least maybe somebody will stumble upon this post and it will help them.