Как мне ждать выполнения следующей команды npm после завершения текущей команды? - PullRequest
0 голосов
/ 25 апреля 2020

Я знаю, это звучит просто. Но читайте дальше, чтобы увидеть проблему, с которой я столкнулся.

Я пытаюсь создать команду 'init', чтобы помочь в настройке клонированного репо для разработки. Мы используем удаленный менеджер пакетов, требующий от разработчика иметь файл .npmrc в репозитории при работе с пакетом (мы не хотим ограничивать пользователя с помощью глобального файла .npmrc). Процесс должен работать следующим образом:

  • запрашивать у пользователя имя пользователя в терминале
  • записывать файл .npmrc в свою систему
  • run npm i
  • run npm run build

Я создал новый 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);
})();

Эта часть, кажется, запускается хорошо, за исключением того, что команда followup (npm i) не использует новый файл .npmrc и установочные бомбы. Я могу (после сбоя) запустить npm i просто отлично, поэтому само содержимое файла кажется правильным. На самом деле, если я вручную помещаю файл .npmrc и запускаю все, , кроме writeNpmRcFile.js, он работает просто отлично. Только когда я включаю эту команду, они терпят неудачу.

Я знаю, что что-то упустил, я просто не знаю что? Я ждал, пока fs.writeFile завершится успешно (возможно, мог просто использовать writeFileSync ...). Как будто файловая система не пропустила go файла .npmrc для следующей команды. В какой-то момент я даже настроил собственный метод asyn c wait(), который удерживался на секунду до выхода из процесса, но это не помогло. Есть идеи?

1 Ответ

0 голосов
/ 25 апреля 2020

Итак, @ Rob C имел в своем комментарии массу смысла, и я проверил свою последующую теорию об использовании child_process для выполнения шага npm i после записи файла .npmrc. Тестирование показало, что я думал правильно, он запускал команду в отдельном контексте, и этот контекст будет использовать новый файл. Для тех, кому интересно, вот мой обновленный writeNpmRcFile.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"
  }
...