facebook / react

The library for web and native user interfaces.

Home Page:https://react.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rollup build script --unsafe-partial flag is broken

bvaughn opened this issue · comments

The following example command sequence will fail:

# Grab the latest build artifacts from CI
scripts/release/download-experimental-build.js --commit=main

# Rebuild only the local NODE bundle from the source of react-dom
# Leave all other artifacts untouched
yarn build --unsafe-partial --type=NODE_DEV react-dom/index

# Throws

The above sequence should work but instead fails with one of the two errors below:

Error: ENOENT: no such file or directory

Error: SyntaxError: Unexpected end of JSON input

This leaves the repo in a broken state (tests won't run, DevTools test shell will crash, etc).

These errors are thrown because this function prematurely resolves:

function asyncCopyTo(from, to) {
return asyncMkDirP(path.dirname(to)).then(
() =>
new Promise((resolve, reject) => {
ncp(from, to, error => {
if (error) {
// Wrap to have a useful stack trace.
reject(new Error(error));
return;
}
resolve();
});
})
);
}

Either before the package.json file has been copied, or after it has been copied (but while its contents are still empty).

This causes the subsequent read of package.json to throw:

function filterOutEntrypoints(name) {
// Remove entry point files that are not built in this configuration.
let jsonPath = `build/node_modules/${name}/package.json`;
let packageJSON = JSON.parse(readFileSync(jsonPath));

The error above can be verified by adding logging to the asyncTopyTo method:

function asyncCopyTo(from, to) {
  return asyncMkDirP(path.dirname(to)).then(
    () =>
      new Promise((resolve, reject) => {
        ncp(from, to, error => {
          if (error) {
            // Wrap to have a useful stack trace.
            reject(new Error(error));
            return;
          }
          if (to.includes('package.json')){
            console.log(`asyncCopyTo() -> ncp("${from}", "${to}") -> resolve`);

            // This line will either throw (no file) or print an empty string in many cases:
            console.log(require('fs').readFileSync(to).toString());
          }
          resolve();
        });
      })
  );
}

I'm able to "fix" this issue by introducing a small amount of delay, e.g.

diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js
index eaf78959a6..5844d9116f 100644
--- a/scripts/rollup/packaging.js
+++ b/scripts/rollup/packaging.js
@@ -196,6 +196,10 @@ async function prepareNpmPackage(name) {
     ),
     asyncCopyTo(`packages/${name}/npm`, `build/node_modules/${name}`),
   ]);
+
+  // Wait for copied files to exist; asyncCopyTo() completes prematurely.
+  await new Promise(resolve => setTimeout(resolve, 100));
+
   filterOutEntrypoints(name);
   const tgzName = (
     await asyncExecuteCommand(`npm pack build/node_modules/${name}`)

But this feels pretty hacky and fragile.

Seems like this is perhaps related to AvianFlu/ncp#127