node.js - 如何以编程方式启动/停止 Metro Bundler

标签 node.js react-native microsoft-metro detox

我正在尝试为 React-Native 项目设置持续集成,但在端到端测试中遇到了一些问题,特别是围绕 Metro bundler 。

似乎在这种情况下使用 react-native 脚本并不可靠:

  • iOS 构建自发地在新终端中生成一个 bundler ,并在构建后让它继续运行。
  • Android 构建依赖于必须事先手动启动的运行实例。
  • 除了发出信号(Ctrl+C 或 kill)之外,不能通过其他方式停止 bundler 。
  • 不与构建同步以确保捆绑程序在应用启动时准备好进行处理。

我想编写一个自定义脚本来启动 Metro,在服务器准备就绪后运行测试,最后停止服务器以清理环境。

最佳答案

metro bundler 必须作为一个单独的进程运行才能处理请求。这样做的方法是使用 Child Process : Spawn并妥善清理返回的对象。

这是一个基本脚本,它可以并行启动 Metro 和 Gradle,并等待两者根据其日志输出准备就绪。

'use strict';

const cp = require('child_process');
const fs = require('fs');
const readline = require('readline');

// List of sub processes kept for proper cleanup
const children = {};

async function asyncPoint(ms, callback = () => {}) {
  return await new Promise(resolve => setTimeout(() => {
    resolve(callback());
  }, ms));
}

async function fork(name, cmd, args, {readyRegex, timeout} = {}) {

  return new Promise((resolve) => {

    const close = () => {
      delete children[name];
      resolve(false);
    };

    if(timeout) {
      setTimeout(() => close, timeout);
    }

    const child = cp.spawn(
      cmd,
      args,
      {
        silent: false,
        stdio: [null, 'pipe', 'pipe'],
      },
    );

    child.on('close', close);
    child.on('exit', close);
    child.on('error', close);

    const output = fs.createWriteStream(`./volatile-build-${name}.log`);

    const lineCb = (line) => {
      console.log(`[${name}] ${line}`);
      output.write(line+'\n');
      if (readyRegex && line.match(readyRegex)) {
        resolve(true);
      }
    };

    readline.createInterface({
      input: child.stdout,
    }).on('line', lineCb);

    readline.createInterface({
      input: child.stderr,
    }).on('line', lineCb);

    children[name] = child;
  });
}

async function sighandle() {
  console.log('\nClosing...');
  Object.values(children).forEach(child => child.kill('SIGTERM'));
  await asyncPoint(1000);
  process.exit(0);
}

function setSigHandler() {
  process.on('SIGINT', sighandle);
  process.on('SIGTERM', sighandle);
}

async function main() {

  setSigHandler();

  // Metro Bundler
  const metroSync = fork(
    'metro',
    process.argv0,
    [ // args
      './node_modules/react-native/local-cli/cli.js', 
      'start',
    ],
    { // options
      readyRegex: /Loading dependency graph, done./,
      timeout: 60000,
    }
  );

  // Build APK
  const buildSync = fork(
    'gradle',
    './android/gradlew', 
    [ // args
      `--project-dir=${__dirname}/android`,
      'assembleDebug',
    ],
    { // options
      readyRegex: /BUILD SUCCESSFUL/,
      timeout: 300000,
    }
  );

  if (await metroSync && await buildSync) {

    // TODO: Run tests here

  }

  sighandle();
}

main();

关于node.js - 如何以编程方式启动/停止 Metro Bundler,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52766315/

相关文章:

mysql - 如何改变 Node js 中的授权(权限)级别

javascript - 如何在图像上绘制正方形来标记

react-native - 如何在React Native的ActionSheetIOS中使用iPad的 anchor 选项?

react-native - 以下文件共享它们的名称;请调整你的 hasteImpl : * <rootDir>\package. json * <rootDir>\sanity\package.json

c# - 从 Metro Style App c# 加载 C 库 (gsdll32.dll)

javascript - Windows 8 Metro 界面 - 是 IE10 吗?

java - 如何在 Java 中使用 Vert.X 运行 CPU 密集型并行任务

javascript - 将包含文件传递到 Handlebars 布局中

node.js - 与 postgres 数据库的连接失败

xaml - 如何在 Windows 8 应用程序 C# 中将视频作为 Web UI 嵌入磁贴中