首页 > 解决方案 > 当前命令完成后如何等待运行下一个 npm 命令?

问题描述

我知道,这听起来很简单。但请继续阅读以了解我面临的挑战。

我正在尝试创建一个“init”命令,以帮助设置克隆的 repo 以进行开发。我们使用远程包管理器,要求开发人员.npmrc在处理包时在存储库中有一个文件(我们不想通过使用全局.npmrc文件来限制用户)。该过程应该像这样工作:

script在我的package.json

"init": "node ./scripts/writeNpmRc.js && npm i && npm run build && node ./scripts/init.js"

我的writeNpmRc.js文件用于readline获取用户输入,然后写入文件(请记住,此时我们还没有运行npm i,所以我们正在处理节点安装的内容)。

const readline = require('readline');
const fs = require('fs');
const path = require('path');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const writeFile = function(dest, data) {
  return new Promise((resolve, reject) => {
    fs.writeFile(dest, data, 'utf-8', function(err) {
      if (err) {
        reject(err);
      }
      resolve();
    });
  });
};

const getUsername = () => {
  const promise = new Promise((resolve, reject) => {
    try {
      rl.question(
        'What is your username (typically firstname.lastname)? ',
        result => {
          if (!result) {
            console.error('You must provide your username.');
            return getUsername();
          }
          resolve(result);
        }
      );
    } catch (err) {
      reject(err);
    }
  });
  return promise
    .catch(err => {
      console.error('[getUsername] ', err);
    })
    .finally(() => {
      rl.close();
    });
};

(async () => {
  const userName = await getUsername();

  const fileData = `registry=http://my.remote.repo/url/
email=${userName}@email.com`;

  await writeFile(path.resolve(__dirname, '../.npmrc'), fileData).catch(err => {
    console.err('[writeFile Error]', err);
  });

  console.log('[END writeNpmRcFile]');
  process.exit(0);
})();

这部分似乎运行良好,只是后续命令 ( npm i) 没有使用新.npmrc文件和安装炸弹。我可以(失败后)运行npm i得很好,所以文件内容本身似乎是正确的。事实上,如果我手动放置一个.npmrc文件并运行所有内容,除了writeNpmRcFile.js它运行得很好。只有当我链接此命令时,它们才会失败。

我知道我错过了什么,我只是不知道是什么?我等待fs.writeFile成功完成(可能只是使用writeFileSync...)。这就像文件系统没有及时释放.npmrc文件以执行下一个命令。我什至设置了一个自定义异步wait()方法,在退出进程之前暂停了一秒钟,但它没有帮助。有任何想法吗?

标签: javascriptnode.jsnpmnpm-scripts

解决方案


所以,@RobC 在他的评论中很有意义,我在编写文件后测试了我关于使用child_process运行步骤的后续理论。测试表明我认为正确,它在单独的上下文中运行命令,并且该上下文将使用新文件。对于那些好奇的人,这是我的更新,包括安装。npm i.npmrcwriteNpmRcFile.js

const readline = require('readline');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

let rl;
let spinnerInterval;

const getInterface = () =>
  readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

const writeFile = function(dest, data) {
  return new Promise((resolve, reject) => {
    fs.writeFile(dest, data, 'utf-8', function(err) {
      if (err) {
        reject(err);
      }
      resolve();
    });
  });
};

const runInstall = () => {
  console.log('Running npm install (This may take a few, so be patient)');
  spinnerInterval = twirlTimer();
  const promise = new Promise((resolve, reject) => {
    exec(
      'npm i',
      { cwd: path.resolve(__dirname, '../') },
      (err, stdout, stderr) => {
        clearInterval(spinnerInterval);
        if (err) {
          reject(err);
        }
        console.log(`stdout: ${stdout}`);
        console.error(`stderr: ${stderr}`);
        resolve();
      }
    );
  });
  return promise.catch(err => {
    clearInterval(spinnerInterval));
    return Promise.reject(err);
  }
};

const twirlTimer = function() {
  var P = ['\\', '|', '/', '-'];
  var x = 0;
  return setInterval(function() {
    process.stdout.write('\r' + P[x++]);
    x &= 3;
  }, 250);
};

const getUsername = () => {
  if (!rl) rl = getInterface();
  const promise = new Promise((resolve, reject) => {
    try {
      rl.question(
        'What is your username (typically firstname.lastname)? ',
        result => {
          if (!result) {
            console.error('You must provide your username.');
            return getUsername();
          }
          resolve(result);
        }
      );
    } catch (err) {
      reject(err);
    }
  });
  return promise
    .catch(err => {
      console.error('[getUsername] ', err);
    })
    .finally(() => {
      rl.close();
    });
};

(async () => {
  const userName = await getUsername();

  // prettier-ignore
  const fileData = `registry=http://my.remote.repo/url/
email=${userName}@email.com`;

  console.log(`Writing your '.npmrc' file to the directory`);
  await writeFile(path.resolve(__dirname, '../.npmrc'), fileData).catch(err => {
    console.error('[writeFile Error] ', err);
  });

  await runInstall().catch(err => {
    console.log('[runInstall Error] ', err);
  });

  console.log('[END writeNpmRcFile]');
  process.exit(0);
})();

进行此更改后,我对此进行了package.json scripts更改:

  "scripts": {
    "lint": "eslint ./scripts",
    "start": "node ./dist/index.js",
    "release": "standard-version",
    "write-npmrc": "node ./scripts/writeNpmRcFile.js",
    "init": "npm run write-npmrc && npm run build && node ./scripts/init.js",
    "build": "node ./scripts/build.js"
  }

推荐阅读