Как я могу заставить `реагировать-скрипты строить` тихо?

Я работаю с репозиторием с несколькими пакетами Node, созданными с помощью create-react-app, и все они созданы и протестированы системой CI. Сборка/тестирование каждого пакета, выполненное с помощью react-scripts build, за которым следует react-scripts test --silent, в настоящее время выдает более двадцати строк вывода, в результате чего журнал сборки содержит более ста строк материала, например «Размеры файлов после gzip» и «Узнайте больше о развертывании здесь». ." Это затрудняет просмотр сообщений об ошибках, предупреждений или других проблем в этом журнале.

Есть ли какой-нибудь способ успокоить это, если не писать собственный сценарий сборки (и, возможно, тестовый сценарий) для каждого из пакетов? Если мне нужны пользовательские сценарии, как лучше всего повторно использовать как можно больше существующего кода, выполняющего сборку и тестирование?


person cjs    schedule 31.08.2018    source источник
comment
Вы придумали решение этой проблемы?   -  person Lukas Gjetting    schedule 14.10.2018
comment
@LukasGjetting Я провел расследование, и на данный момент единственным решением является использование другого сценария сборки. Подробности смотрите в ответе, который я только что разместил ниже. И спасибо за PR, чтобы исправить это.   -  person cjs    schedule 15.10.2018
comment
вы можете создать пакетный файл, который выполняет react-scripts. Это позволяет сделать этот пакетный файл тихим. например в Windows вы бы поместили @echo off в первую строку этого пакетного файла. Или, альтернативно, перенаправить его вывод в файл с расширением react-scripts build > file.txt.   -  person bvdb    schedule 19.11.2018
comment
@bvdb Сборка запускается сценарием оболочки, поэтому она уже молчит в смысле @echo off в файле .BAT. Проблема в том, что вывод идет не из командного файла, а из самой сборки. И да, я мог бы перенаправить его, но тогда это также перенаправило бы сообщения об ошибках и предупреждения, скрытые во всех этих подробных выводах сборки.   -  person cjs    schedule 20.11.2018
comment
@CurtJ.Sampson Я не знаю, как они реализовали свой вывод, но принято считать, что сообщения об ошибках помещаются в отдельный вывод ошибок. то есть foo.exe 1> stdout.txt 2> errout.txt. Но я предполагаю, что вы правы, это, вероятно, не сработает. Но теоретически вы должны иметь возможность react-scripts build 1> ignorestdout.txt сохранять только вывод ошибок.   -  person bvdb    schedule 20.11.2018
comment
@ CurtJ.Sampson Вы правы насчет @echo off, забирая это.   -  person bvdb    schedule 20.11.2018
comment
@bvdb Нередко информация о проблемах выводится на стандартный вывод, а не на стандартный вывод. (Это происходит по разным причинам, например, разработчик инструмента считает, что предупреждение не очень важно.) Поэтому я делаю политикой всегда захватывать как stderr, так и stdout и проверять их на наличие всего, что может указывать на проблемы.   -  person cjs    schedule 22.11.2018
comment
@CurtJ.Sampson, тогда это может вам помочь: foo.exe | find /V "ignore this" | find /V "and ignore this"   -  person bvdb    schedule 22.11.2018


Ответы (3)


react-scripts build запускает bin/react-scripts.js из пакета react-scripts, который просто запускает scripts/build.js из того же пакета.

К сожалению, этот скрипт build.js (во всяком случае, по состоянию на 15 октября 2018 г.) жестко закодирован для вызова таких функций, как printFileSizesAfterBuild() и printHostingInstructions(), без какой-либо возможности отключить их. Таким образом, в настоящее время нет никакого способа изменить это, кроме как сделать копию build.js, изменить ее, чтобы не печатать то, что вам не нужно, и использовать ее вместо этого.

Существует запрос на включение PR #5429 от @LukasGjetting на добавление --silent вариант скрипта сборки. Он был закрыт из-за бездействия, и разработчики create-react-app ясно дали понять в других местах, что они не собираются делать react-scripts очень настраиваемым; решение, которое они предлагают, состоит в том, чтобы просто использовать ваш собственный скрипт build.js.

person cjs    schedule 15.10.2018
comment
Это потребует, чтобы проект был выброшен, верно? - person paul23; 23.02.2020
comment
@ paul23 Вам нужно сделать собственную копию build.js; решили ли вы сделать это с помощью извлечения (которое, IIRC, также копирует множество других скриптов) или нет, зависит от вас. - person cjs; 23.02.2020

Я спонтанно вспомнил мел. По крайней мере, вы можете раскрасить ответы. Похоже, приложение, с которым вы работаете, (еще) не удалено. Только когда приложение будет извлечено, вы сможете изменить create-react-app базовые файлы. К сожалению, после выброса вы не можете отменить эффекты. Лайк если поможет

person HJW    schedule 15.10.2018
comment
Часть смысла заключалась в том, чтобы не извлекать приложение, потому что тогда вам нужно вручную копировать любые обновления скрипта, а не просто обновлять зависимый create-react-app пакет. - person cjs; 15.10.2018
comment
Просто указал на вариант. Теперь вопрос только в том, насколько сильно ты этого хочешь. Не стоит ИМО. Извините, я не мог больше помочь - person HJW; 15.10.2018
comment
Что ж, спасибо, но я надеялся, что после написания собственного пользовательского сценария сборки уже стало ясно, что я хочу использовать стандартный сценарий из пакета. - person cjs; 15.10.2018

Если вы скопируете скрипт build.js из /node_modules/react-scripts/scripts/build.js в корень вашего приложения, сделайте пути относительно const basepath = __dirname+'/node_modules/react-scripts/scripts/' и удалите ненужные журналы. Измените сборку скриптов package.json на: node build и у вас будет очень тихая сборка для реагирующего приложения :)

Пример скрипта сборки:

// @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,
  });
}

и образец сценария package.json:

"scripts": {
  "start": "react-scripts start", "build": "node build"
}
person bluehipy    schedule 11.02.2021
comment
Спасибо за ваш код, хотя я должен указать, что вопрос был сосредоточен на том, чтобы сделать это без необходимости извлекать и настраивать сценарии сборки. (Не так уж плохо делать это для одного проекта, но делать и поддерживать это для дюжины или около того проектов — это гораздо больше работы.) - person cjs; 15.02.2021
comment
Изменения настолько просты, что можно было бы легко написать сценарий для автоматизации процесса: справиться со сборкой сценария из пути, внедрить этот базовый путь, найти пути и изменить их, создать новый сценарий узла оболочки, удалить сценарий постфактум. Правильно :) - person bluehipy; 15.02.2021
comment
Я думаю, что это, вероятно, проще и дешевле для долгосрочной поддержки, если кто-то собирается начать писать больше кода, просто создать новый пакет на основе существующего с предпочтительными модификациями. (Хотя, в конце концов, я считаю, что мы интегрировали желаемые изменения в нашу собственную систему сборки.) - person cjs; 01.03.2021