如何以编程方式启动/停止 Metro Bundler

Posted

技术标签:

【中文标题】如何以编程方式启动/停止 Metro Bundler【英文标题】:How to programmatically start/stop Metro Bundler 【发布时间】:2019-03-16 21:43:34 【问题描述】:

我正在尝试为 React-Native 项目设置持续集成,但在端到端测试中遇到了一些问题,尤其是在 Metro 捆绑器周围。

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

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

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

【问题讨论】:

经过大量实验后,我认为 Metro 必须作为单独的进程运行,否则将无法响应客户端请求。所以挑战是派生一个新进程,解析控制台输出并保持执行直到 Metro 准备好。 【参考方案1】:

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();

【讨论】:

以上是关于如何以编程方式启动/停止 Metro Bundler的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式停止/启动远程计算机上的计划任务?

在 MahApps Metro DataGridCheckBoxColumn 中,我如何以编程方式返回复选框值? (并修复额外的行)

如何在 Java 中以编程方式启动和停止 Amazon EC2 实例

如何使用 MsBuild 以编程方式在 IIS(6.0 和 7.0)中停止或启动网站?

以编程方式添加 MahApps.Metro 上下文菜单

以编程方式启动和停止 IIS Express