Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make `react-scripts build` quiet?

I am working with a repo with a number of Node packages created with create-react-app, all of which are built and tested by the CI system. Each package's build/test, done with react-scripts build followed by react-scripts test --silent, is currently producing over twenty lines of output resulting in a build log with well over a hundred lines of material such as "File sizes after gzip" and "Find out more about deployment here." This makes it more difficult to see error messages, warnings or other problems in that log.

Is there some way for me to quiet this down short of writing my own custom build script (and possibly test script, too) for each one of the packages? If I do need custom scripts, what's the best way to re-use as much as possible of the existing code that's doing the build and test?

like image 342
cjs Avatar asked Aug 31 '18 11:08

cjs


2 Answers

react-scripts build runs bin/react-scripts.js from the react-scripts package which basically just runs scripts/build.js from that same package.

Sadly, that build.js script (as of 2018-10-15, anyway) is hard-coded to call functions such as printFileSizesAfterBuild() and printHostingInstructions(), without any option to disable these. So there's currently no way to change this except to make a copy of build.js, modify it not to print the things you don't want, and use that instead.

There is a pull request PR #5429 from @LukasGjetting to add a --silent option to the build script. It's been closed due to lack of inactivity, and the create-react-app developers have made it fairly clear in other places that they're not intending to make react-scripts very configurable; the solution they suggest is just to use your own build.js script.

like image 167
cjs Avatar answered Oct 27 '22 01:10

cjs


If you copy the build.js script from /node_modules/react-scripts/scripts/build.js in the root of your application, make the paths relative to a const basepath = __dirname+'/node_modules/react-scripts/scripts/' and elminate unnecessary logs. Adjust the package.json scripts build to: node build and you have a very quiet build for a react app :)

Sample build script:

// @remove-on-eject-begin
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
// @remove-on-eject-end
'use strict';

// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
const basepath = __dirname + '/node_modules/react-scripts/scripts/';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
  throw err;
});

// Ensure environment variables are read.
require(basepath + '../config/env');
// @remove-on-eject-begin
// Do the preflight checks (only happens before eject).
const verifyPackageTree = require(basepath + 'utils/verifyPackageTree');
if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
  verifyPackageTree();
}
const verifyTypeScriptSetup = require(basepath + 'utils/verifyTypeScriptSetup');
verifyTypeScriptSetup();
// @remove-on-eject-end

const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const configFactory = require(basepath + '../config/webpack.config');
const paths = require(basepath + '../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');

const measureFileSizesBeforeBuild =
  FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);

// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;

const isInteractive = process.stdout.isTTY;

// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
  process.exit(1);
}

// Generate configuration
const config = configFactory('production');

// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const {
  checkBrowsers
} = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
  .then(() => {
    // First, read the current file sizes in build directory.
    // This lets us display how much they changed later.
    return measureFileSizesBeforeBuild(paths.appBuild);
  })
  .then(previousFileSizes => {
    // Remove all content but keep the directory so that
    // if you're in it, you don't end up in Trash
    fs.emptyDirSync(paths.appBuild);
    // Merge with the public folder
    copyPublicFolder();
    // Start the webpack build
    return build(previousFileSizes);
  })
  .then(
    ({
      stats,
      previousFileSizes,
      warnings
    }) => {
      // if (warnings.length) {
      //   console.log(chalk.yellow('Compiled with warnings.\n'));
      //   console.log(warnings.join('\n\n'));
      //   console.log(
      //     '\nSearch for the ' +
      //       chalk.underline(chalk.yellow('keywords')) +
      //       ' to learn more about each warning.'
      //   );
      //   console.log(
      //     'To ignore, add ' +
      //       chalk.cyan('// eslint-disable-next-line') +
      //       ' to the line before.\n'
      //   );
      // } else {
      //   console.log(chalk.green('Compiled successfully.\n'));
      // }
      //
      // console.log('File sizes after gzip:\n');
      // printFileSizesAfterBuild(
      //   stats,
      //   previousFileSizes,
      //   paths.appBuild,
      //   WARN_AFTER_BUNDLE_GZIP_SIZE,
      //   WARN_AFTER_CHUNK_GZIP_SIZE
      // );
      // console.log();
      console.log(chalk.green('Compiled successfully.\n'));

      const appPackage = require(paths.appPackageJson);
      const publicUrl = paths.publicUrlOrPath;
      const publicPath = config.output.publicPath;
      const buildFolder = path.relative(process.cwd(), paths.appBuild);
      // printHostingInstructions(
      //   appPackage,
      //   publicUrl,
      //   publicPath,
      //   buildFolder,
      //   useYarn
      // );
    },
    err => {
      const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
      if (tscCompileOnError) {
        console.log(
          chalk.yellow(
            'Compiled with the following type errors (you may want to check these before deploying your app):\n'
          )
        );
        printBuildError(err);
      } else {
        console.log(chalk.red('Failed to compile.\n'));
        printBuildError(err);
        process.exit(1);
      }
    }
  )
  .catch(err => {
    if (err && err.message) {
      console.log(err.message);
    }
    process.exit(1);
  });

// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
  // We used to support resolving modules according to `NODE_PATH`.
  // This now has been deprecated in favor of jsconfig/tsconfig.json
  // This lets you use absolute paths in imports inside large monorepos:
  if (process.env.NODE_PATH) {
    console.log(
      chalk.yellow(
        'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
      )
    );
    console.log();
  }


  const compiler = webpack(config);
  return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      let messages;
      if (err) {
        if (!err.message) {
          return reject(err);
        }

        let errMessage = err.message;

        // Add additional information for postcss errors
        if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
          errMessage +=
            '\nCompileError: Begins at CSS selector ' +
            err['postcssNode'].selector;
        }

        messages = formatWebpackMessages({
          errors: [errMessage],
          warnings: [],
        });
      } else {
        messages = formatWebpackMessages(
          stats.toJson({
            all: false,
            warnings: true,
            errors: true
          })
        );
      }
      if (messages.errors.length) {
        // Only keep the first error. Others are often indicative
        // of the same problem, but confuse the reader with noise.
        if (messages.errors.length > 1) {
          messages.errors.length = 1;
        }
        return reject(new Error(messages.errors.join('\n\n')));
      }
      if (
        process.env.CI &&
        (typeof process.env.CI !== 'string' ||
          process.env.CI.toLowerCase() !== 'false') &&
        messages.warnings.length
      ) {
        console.log(
          chalk.yellow(
            '\nTreating warnings as errors because process.env.CI = true.\n' +
            'Most CI servers set it automatically.\n'
          )
        );
        return reject(new Error(messages.warnings.join('\n\n')));
      }

      return resolve({
        stats,
        previousFileSizes,
        warnings: messages.warnings,
      });
    });
  });
}

function copyPublicFolder() {
  fs.copySync(paths.appPublic, paths.appBuild, {
    dereference: true,
    filter: file => file !== paths.appHtml,
  });
}

and sample package.json script option:

"scripts": {
  "start": "react-scripts start", "build": "node build"
}
like image 33
bluehipy Avatar answered Oct 26 '22 23:10

bluehipy