是否有更好的方法使用Node.js运行CLI命令?

是否有更好的方法使用Node.js运行CLI命令?,node.js,command-line-interface,Node.js,Command Line Interface,我刚刚写了一个脚本来发布我正在开发的一个产品的构建。脚本完成了这项工作,但我并不真正喜欢代码本身,它看起来像意大利面条代码和回调地狱的结合 有没有更干净的方法?我希望能够连续运行命令、记录输出(stdout.on('data'))以及任务完成时。(便于进一步调试,在等待任务完成时,可以放心地知道后台发生了什么) 也许使用承诺会有助于清理混乱,但我仍然觉得应该有一种更干净的方法来处理多个命令 有关代码功能的一些说明: 创建一个具有所需提交和所需标记版本的标记,即:git-tag 1.2.5 使用

我刚刚写了一个脚本来发布我正在开发的一个产品的构建。脚本完成了这项工作,但我并不真正喜欢代码本身,它看起来像意大利面条代码和回调地狱的结合

有没有更干净的方法?我希望能够连续运行命令、记录输出(
stdout.on('data')
)以及任务完成时。(便于进一步调试,在等待任务完成时,可以放心地知道后台发生了什么)

也许使用承诺会有助于清理混乱,但我仍然觉得应该有一种更干净的方法来处理多个命令


有关代码功能的一些说明:

  • 创建一个具有所需提交和所需标记版本的标记,即:
    git-tag 1.2.5
  • 使用
    gulpbuild
    构建发布文件
  • 创建文件夹
    doc/
  • doc/doc\u reader.odt
    转换为
    doc//documentation.pdf
    。(打开并导出为PDF)
  • 在创建的文件夹中复制
    build/reader.js
    doc/changelog.txt
  • 压缩3个文件
  • 使用提交消息提交所有内容:
    版本1.2.11
    (例如)
  • 使用刚才推送的commit和相同的标记在GitHub上创建一个新版本

  • 下面是代码,作为示例。(ES5,节点4.6.0+)


    编辑:

    下面是使用async/await(节点7.8.0)的实现 我使用了特殊的
    mkdirp
    exec
    模块,允许与
    wait
    一起使用。但是我找不到
    spawn
    的等价物

    const mkdirp = require('async-mkdirp');
    const fs = require('fs-extra');
    const spawn = require('child-process-promise').spawn;
    const exec = require('mz/child_process').exec;
    const zip = new require('node-zip')();
    const c = require('chalk');
    
    const error = c.bold.red;
    const warn = c.yellow;
    const info = c.cyan;
    const info2 = c.magenta;
    
    const version = require('../package.json').version;
    const releaseDirectory = 'doc'
    
    async function git_tag() {
      async function exec_git_tag() {
        return await exec(`git tag ${version}`);
      }
    
      console.log(info(`Creating git tag ${version}`));
      return exec_git_tag()
        .then(() => {
          console.log(info(`Git tag created for ${version}`))
        })
        .catch((err) => {
          console.log(warn('warn', err));
        })
        // Finally
        .then(() => {
          console.log(info(`"git tag ${version}" - Completed`))
        });
    };
    
    async function gulp_build() {
      async function exec_gulp_build() {
        const promise = spawn('gulp', ['build'])
        const childProcess = promise.childProcess;
    
        childProcess.stdout.on('data', (data) => {
          console.log(info2(data.toString()));
        });
        childProcess.stderr.on('data', (data) => {
          console.log(error(data.toString()));
        });
    
        return promise
          .catch((err) => {
            console.error(error(err));
          })
          // Finally
          .then(() => {
            console.log(info('"gulp build" - Completed'))
          });
      }
    
      console.log(info('Running "gulp build"...'))
      return exec_gulp_build()
    }
    
    async function create_dir() {
      const dirPath = `${releaseDirectory}/${version}`;
      console.log(info(`Creating "${dirPath}" directory.`))
      await mkdirp(`${dirPath}`);
      console.log(info(`Directory ${dirPath} created.`));
    }
    
    async function build_doc() {
      const docFile = `${releaseDirectory}/doc_reader.md`;
      console.log(info(`Converting ${docFile} to pdf ...`));
    
      async function exec_build_doc() {
        return await exec(`npm run build:doc`);
      }
    
      return exec_build_doc()
        .catch((err) => {
          console.error(error(err));
        })
        .then(() => {
          console.log(info(`Doc "${docFile}" created.`));
        })
    }
    
    function copy_files() {
      console.log(info('Copying changelog.txt ...'));
      fs.copySync('doc/changelog.txt', `doc/${version}/changelog.txt`);
      console.log(info('changelog.txt copied.'));
    
      console.log(info(`Copying "build/reader.js" to "doc/reader-${version}.js" and "doc/reader.js" ...`));
      fs.copySync('build/reader.js', `doc/${version}/reader.js`);
      fs.copySync('build/reader.js', `doc/${version}/reader-${version}.js`);
      console.log(info('reader.js copied.'));
    }
    
    function zip_files() {
      console.log(info('Zipping all files ...'));
      zip.file('changelog.txt', fs.readFileSync(`doc/${version}/changelog.txt`));
      zip.file('doc_reader.pdf', fs.readFileSync(`doc/${version}/doc_reader.pdf`));
      zip.file('reader.js', fs.readFileSync(`doc/${version}/reader.js`));
      zip.file(`reader-${version}.js`, fs.readFileSync(`doc/${version}/reader-${version}.js`));
    
      const data = zip.generate({ base64: false, compression: 'DEFLATE' });
      const zipFilename = `doc/${version}/HTML5Reader_${version}.zip`;
      fs.writeFileSync(zipFilename, data, 'binary'); // it's important to use *binary* encode
      console.log(info(`${zipFilename} created.`));
    }
    
    async function release() {
      await git_tag();
      await gulp_build();
      await create_dir();
      await build_doc();
      copy_files();
      zip_files();
    
      console.log(`\nRelease ${version} done. Please add generated files and commit using:`);
      console.log(`\n\tgit add . && git commit -m "Release ${version}"`);
    }
    
    release();
    

    这里有一个非常有用的
    mz
    模块。见:

    这与
    async
    /
    await
    相结合,将允许您编写如下代码:

    let exec = require('mz/child_process').exec;
    
    (async () => {
      let version = await exec('node --version');
      console.log(version);
      let result = await exec('some other command');
      console.log(result);
      // ...
    })();
    
    这是一个简单的示例,但您可以通过这种方式使用
    child\u进程
    fs
    和许多其他模块中的所有函数

    这里重要的是,此代码仍然是异步的和非阻塞的

    请注意,您只能在使用
    async
    关键字创建的函数中使用
    wait
    。有关详细信息,请参阅:

    有关浏览器中的支持,请参阅:

    有关节点中的支持,请参阅:

    在本机不支持
    async
    wait
    的地方,可以使用Babel:

    或者使用稍微不同的语法,使用基于生成器的方法,如
    co
    或Bluebird协同程序:


    谢谢!我要试试看。:)谢谢你的提示,我完成了实现。找不到异步方式来运行
    spawn
    tho。
    let exec = require('mz/child_process').exec;
    
    (async () => {
      let version = await exec('node --version');
      console.log(version);
      let result = await exec('some other command');
      console.log(result);
      // ...
    })();