Помните процесс разработки CLI с помощью nodejs

Node.js JavaScript

всех с Новым годом!
Несколько лет назад я получил на работе задание разработать свой CLI, вот и пошел изучать. Его не сложно найти, просто используйте соответствующий API-интерфейс nodejs.

На данный момент реализованы следующие функции:

  1. Введите новую команду, чтобы загрузить шаблон шаблона с github, а затем создайте соответствующее приложение.
  2. Введите команду create, чтобы быстро создать шаблонные файлы.

Далее будет поэтапно проанализирован процесс создания CLI, который также является процессом записи воспоминаний.

1. Создайте проект CLI

Создайте файл package.json проекта с помощью npm init. Затем отредактируйте файл в основном, чтобы добавить

"bin": {
    "jsm": "./bin/jsm.js"
  },

Затем создайте свой собственный файл сценария в текущем каталоге, соответствующий приведенной выше конфигурации, какmkdir bin && touch bin/jsm.js
Отредактируйте созданный файл и добавьте

#!/usr/bin/env node
console.log('Hello CLI')

Далее запустите его в корневом каталоге проектаnpm i -g, теперь вы можете использовать команду jsm в командной строке.
Примечание: обязательно добавьте в начале#!/usr/bin/env node, иначе не получится.

详解:package.json文件只有加上了bin字段,才能在控制台使用你的命令,对应的这里的命令就是jsm,对应的执行文件为bin/jsm.js。 其实"jsm"命令就是 "node bin/jsm.js" 的别称,只有你用npm i -g全局安装后才可以用,开发的过程中直接用node bin/jsm.js即可。

2. Разобрать параметры команды

CLI необходимо вводить различные параметры через командную строку, которые можно анализировать непосредственно с помощью связанного с процессом API-интерфейса nodejs, но более рекомендуется использовать пакет commandr npm, который может значительно упростить процесс анализа.npm i commanderУстановите, затем измените файл сценария перед добавлением

const program = require('commander');

 program
  .command('create <type> [name] [otherParams...]')
  .alias('c')
  .description('Generates new code')
  .action(function (type, name, otherParams) {
    console.log('type', type);
    console.log('name', name);
    console.log('other', otherParams);
    // 在这里执行具体的操作
  });

program.parse(process.argv);

Теперь выполните его в терминалеnode bin/jsm.js c component myComponent state=1 title=HelloCLIВы должны иметь возможность видеть различную введенную информацию. Пока часть разбора команды в основном в порядке, другие варианты использования могут ссылаться на официальныйпример

详解:command第一个参数为命令名称,alias为命令的别称, 其中<>包裹的为必选参数 []为选填参数 带有...的参数为剩余参数的集合。

3. Загрузите шаблон для создания файла

Далее нужно что-то сделать по команде, введенной на предыдущем шаге. В частности, интерфейс командной строки скаффолдинга обычно делает две вещи: быстро создает новый проект и быстро создает соответствующие шаблонные файлы. Поскольку необходимо создавать файлы, использование модуля fs узла nodejs является обязательным.Здесь используется расширенная версия.fs-extra

Ниже приведены две часто используемые функции обработки файлов.

//写入文件
function write(path, str) {
  fs.writeFileSync(path, str);
}
//拷贝文件
function copyTemplate(from, to) {
  from = path.join(__dirname, from);
  write(to, fs.readFileSync(from, 'utf-8'));
}

3.1 Создать новый проект

Команда выглядит следующим образом

program
  .command('new [name]')
  .alias('n')
  .description('Creates a new project')
  .action(function (name) {
    const projectName = name || 'myApp';
    init({ app: projectName })
  });

Функция init в основном делает две вещи:

  • Загрузите шаблон скаффолдинга с github. (Если вы используете локальный шаблон лесов, вы можете пропустить этот шаг)
  • Скопируйте файл скаффолда в каталог, указанный командой, и установите соответствующие зависимости.
const fs = require('fs-extra');
const chalk = require('chalk');
const {basename, join} = require('path');
const readline = require('readline');
const download = require('download-git-repo');
const ora = require('ora');
const vfs = require('vinyl-fs');
const map = require('map-stream');
const template = 'stmu1320/Jsm-boilerplate';

// 创建函数
function createProject(dest) {
  const spinner = ora('downloading template')
  spinner.start()
  if (fs.existsSync(boilerplatePath)) fs.emptyDirSync(boilerplatePath)
  download(template, 'boilerplate', function (err) {
    spinner.stop()
    if (err) {
      console.log(err)
      process.exit()
    }

    fs
    .ensureDir(dest)
    .then(() => {
      vfs
        .src(['**/*', '!node_modules/**/*'], {
          cwd: boilerplatePath,
          cwdbase: true,
          dot: true,
        })
        .pipe(map(copyLog))
        .pipe(vfs.dest(dest))
        .on('end', function() {
          const app = basename(dest);
          const configPath = `${dest}/config.json`;
          const configFile = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
          configFile.dist = `../build/${app}`;
          configFile.title = app;
          configFile.description = `${app}-project`;
          write(configPath, JSON.stringify(configFile, null, 2));
          // 这一部分执行依赖包安装,具体代码请查看文末链接
          message.info('run install packages');
          require('./install')({
            success: initComplete.bind(null, app),
            cwd: dest,
          });
        })
        .resume();
    })
    .catch(err => {
      console.log(err);
      process.exit();
    });
})
}

function init({app}) {
  const dest = process.cwd();
  const appDir = join(dest, `./${app}`);
  createProject(appDir);
}

3.2 Быстрое создание файлов шаблонов

Часть создания файла шаблона на самом деле просто копирует файл в указанное место.Конечно, конкретное содержимое файла должно быть изменено в соответствии с параметрами.

program
  .command('create <type> [name] [otherParams...]')
  .alias('c')
  .description('Generates new code')
  .action(function (type, name, otherParams) {
    const acceptList = ['component', 'route']
    if (!acceptList.find(item => item === type)) {
      message.light('create type must one of [component | route]')
      process.exit()
    }
    const params = paramsToObj(otherParams)
    params.name = name || 'example'
    generate({
      type,
      params
    })
  });

//生成文件入口函数
function generate({type, params}) {
  const pkgPath = findPkgPath(process.cwd())
  if (!pkgPath) {
    message.error('No \'package.json\' file was found for the project.')
    process.exit()
  }
  const dist = path.join(pkgPath, `./src/${type}s`);
  fs
    .ensureDir(dist)
    .then(() => {
      switch (type) {
        case 'component':
          // 具体代码请查看文末链接
          createComponent(dist, params);
          break;

        case 'route':
          createRoute(dist, params);
          break;

        default:
          break;
      }
    })
    .catch(err => {
      console.log(err);
      process.exit(1);
    });
}

Базовый интерфейс командной строки скаффолдинга здесь почти готов, а остальное — это дружественные подсказки, такие как справочная информация. Весь исходный код статьикликните сюда
Вы также можете установить и попробоватьnpm i -g jsm-cli.