node-cli — это инструмент, использующий nodejs для взаимодействия с оболочкой и выполнения указанной работы. Обычно они выглядят так:
sass xx.scss:xx.css
webpack ....
Подождите, мы внедрили этот инструмент, чтобы вытягиватьCavinHuang/webpack-multi-skeleton многостраничный скелет веб-пакетаИнструмент поддержки для локального и быстрого создания проектов, представленный следующими командами:
webpack-template i # install git端所有的模板列表供选择,选择其中之一后进行本地缓存
webpack-template init # 通过一些选项,初始化整个项目
Вся идея наверное такая, начнем с самого простого и реализуем его шаг за шагом.
Реализация собственного узла команды
Мы инициализируем проект напрямую с помощью npm init
npm init node-cli-demo
Полностью да, входим в проект, добавляем следующий код в package.json
"bin": {
"hi": "./bin/hi.js"
},
Создайте каталог bin и hi.js и напишите следующий код в hi.js.
#!/usr/bin/env node
console.log('Hi Welcome node cli')
Используйте командную строку для входа в текущий каталог проекта и введите
hi
Если нет такого командной строки, введите
npm link
First, we all know that the operating system will have a PATH environment variable, when the system call a command and they will find the path registered in the PATH variable, if the path is called a registered there, otherwise it could not find the command быстрый.我们可以通过process.env获取本机系统中所有的环境变量,所以这句话主要是帮助脚本找到node的脚本解释器,可以理解为调用系统中的node来解析我们的脚本。
Обработка аргументов командной строки
Объект процесса узла — это глобальный объект, который предоставляет информацию и управление текущим процессом Node.js и может вызываться без require() в среде узла.
Свойство process.argv возвращает массив, содержащий аргументы командной строки, переданные при запуске процесса Node.js. Первый элемент — это process.execPath, если вам нужно получить доступ к необработанному значению argv[0], вы можете использовать process.argv0, вторым элементом будет путь к файлу JavaScript для выполнения, а остальные элементы будут любыми. другие аргументы командной строки.
#!/usr/bin/env node
console.log('call %s', process.argv[2]);
Затем введите test hello и распечатайте call hello.
Для обработки параметров командной строки мы используем готовый модуль commander, который предоставляет мощные функции для пользовательского ввода командной строки и разбора параметров. Здесь мы используем легкий, выразительный командор для обработки.
Официальный сайт:официальный сайт командира
См. пример с официального сайта
#!/usr/bin/env node
var program = require('commander');
program
.version('0.1.0')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
.option('-T, --no-tests', 'ignore test hook');
program
.command('setup [env]')
.description('run setup commands for all envs')
.option("-s, --setup_mode [mode]", "Which setup mode to use")
.action(function(env, options){
var mode = options.setup_mode || "normal";
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
});
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function(cmd, options){
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function() {
console.log(' Examples:');
console.log();
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
console.log();
});
program
.command('*')
.action(function(env){
console.log('deploying "%s"', env);
});
program.parse(process.argv);
Первая установка командира
yarn add commander # OR npm install commander
Взгляните на эффект:
hi -V
hi setup
hi exec
commander.js API
- Option() ——> Инициализировать объект пользовательского параметра, установить «Ключевое слово» и «Описание»
- Command() ——> Инициализировать объект параметра командной строки, напрямую получить ввод командной строки и вернуть массив или строку
- Команда команды # () -> Определите имя команды
- Command#arguments() ——> Определить параметры исходной команды
- Command#parseExpectedArgs() ——> анализировать ожидаемые аргументы
- Command#action() ——> Зарегистрировать функцию обратного вызова команды
- Command#option() ——> Для определения параметров необходимо задать «ключевое слово» и «описание».Ключевое слово включает в себя «аббревиатуру» и «полный текст», разделенные «,», «|», «пробел».
- Command#allowUnknownOption() ——> Разрешить неизвестные параметры командной строки
- Command#parse() ——> parse process.argv, вызывать команду при установке опций и определений
- Команда #parseOptions() -> парсить параметры
- Команда # OPTS () -> Установленные параметры
- Команда # Описание () -> Добавить команду Описание
- Command#alias() ——> установить псевдоним команды
- Command#usage() ——> установить/получить использование
- Command#name()
- Command#help()
program
.command( 'list' ) //声明hi下有一个命令叫list
.description( 'list files in current working directory' ) //给出list这个命令的描述
.option( '-a, --all', 'Whether to display hidden files' ) //设置list这个命令的参数
.action( function ( options ) { //list命令的实现体
var fs = require( 'fs' );
//获取当前运行目录下的文件信息
fs.readdir( process.cwd(), function ( err, files ) {
var list = files;
if ( !options.all ) { //检查用户是否给了--all或者-a的参数,如果没有,则过滤掉那些以.开头的文件
list = files.filter( function ( file ) {
return file.indexOf( '.' ) !== 0;
} );
}
console.log( list.join( '\n\r' ) ); //控制台将所有文件名打印出来
} );
} );
бегать
hi list # hi list -a 或者 --all来查看效果
портал github, ветка 0.0.1 — это код первой версии
Создайте официальную версию среды разработки для поддержки синтаксиса es6 и eslint.
yarn add -D babel-cli babel-eslint babel-plugin-transform-es2015-modules-commonjs babel-preset-latest-node
Создайте новый .babelrc в корневом каталоге проекта со следующим содержимым:
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
],
"plugins": [
"transform-es2015-modules-commonjs"
]
}
Новый каталог SRC, для разработки, новый каталог SRC / командной команды и каталог SRC / UTILS, используйте для разработки. После создания структуры каталога выглядит следующим образом:
├─bin # 脚本启动文件所在目录
├─node_modules # libraray 目录
│ └─commander
│ └─typings
└─src # 开发目录
├─command # 命令实现目录,一个命令对应一个文件
└─utils # 工具目录
Далее реализуем запись и переносим функцию в соответствующий файл реализации команды для конкретной реализации. Создайте новый index.js для обработки записи, а затем создайте src/index.js для фактической переадресации функций.Содержимое index.js выглядит следующим образом.
// babel解析
require( "babel-register" )
require( "babel-core" )
.transform( "code", {
presets: [ [ require( 'babel-preset-latest-node' ), {
target: 'current'
} ] ]
} );
require( 'babel-polyfill' )
require('./src')
Содержимое src/index.js выглядит следующим образом:
var program = require( 'commander' );
program.parse( process.argv ); //开始解析用户输入的命令
require( './command/' + program.args + '.js' ) // 根据不同的命令转到不同的命令处理文件
Объясните, почему я хочу это сделать:
-
Затем мы можем создать соответствующую запрашиваемую цену, src/command/init.js src/command/install.js два файла обработки команд, содержимое которых выглядит следующим образом:
src/command/list.js:
var program = require( 'commander' );
program
.command( 'init' )
.description( 'init project for local' )
.action( function ( options ) { //list命令的实现体
// to do
console.log( 'init command' );
} );
program.parse( process.argv ); //开始解析用户输入的命令
src/command/install.js:
var program = require( 'commander' );
program
.command( 'install' )
.description( 'install github project to local' )
.action( function ( options ) { //list命令的实现体
// to do
console.log( 'install command' );
} );
program.parse( process.argv ); //开始解析用户输入的命令
Введите следующую команду в командной строке для проверки:
webpack-template install
webpack-template init
Второе издание завершает кодовый адрес:[Адрес Github второго издания, вы можете клонировать попытки вниз]
Далее мы должны установить и запустить функцию инициализации. Сначала установите предусмотренные шаги:
- Вытащите шаблон проекта на склад через github api
- Скачать, выбрав шаблон
- Кэшируется в локальный временный каталог для прямого использования в следующий раз
Во-первых, перейдите кgithub api v3Найдите нужный интерфейс API,
Чтобы облегчить использование шаблонов управления проектами, я создал новую организацию для управления. Итак, я в основном через
/orgs/:org/repos #获取项目
和
/repos/:owner/:repo #获取版本
Проект был построен, вы можете просмотреть детали склада через следующий API 1. Список проектов
url -i https://api.github.com/orgs/cavinHuangORG/repos
2. Версия проекта
curl -i https://api.github.com/repos/cavinHuangORG/webpack-multipage-template/tags
Выбор параметров через командную строку имеет следующие последствия:
inquirer.gif
Здесь мы используем другую библиотеку взаимодействия с командной строкой, inquirer.js, которая в основном используется для выбора и ввода командной строки; сначала мы реализуем простой код в insatll.js для завершения следующего кода:
var inquirer = require( 'inquirer' );
program
.command( 'install' )
.description( 'install github project to local' )
.action( function ( options ) { //list命令的实现体
// to do
console.log( 'install command' );
let choices = [ 'aaa', 'bbb', 'ccc', 'dddd' ];
let questions = [ {
type: 'list',
name: 'repo',
message: 'which repo do you want to install?',
choices
} ];
// 调用问题
inquirer.prompt( questions )
.then( answers => {
console.log( answers ); // 输出最终的答案
} )
} );
program.parse( process.argv ); //开始解析用户输入的命令
Окончательный результат выглядит следующим образом:
install-2.gif
Мы хотим, чтобы этот эффект был почти завершен. Далее я хотел бы ввести некоторые конкретные параметры пользователем для инициализации всего проекта.
download-git-repo
Далее мы будем использовать библиотеку для загрузки кода библиотеки github,download-git-repo, использование выглядит следующим образом:
download(repository, destination, options, callback)
Загрузите репозиторий git в папку назначения с параметрами и обратным вызовом.
-
адрес библиотеки репозитория github
- GitHub — github:владелец/имя или просто владелец/имя
- GitLab - gitlab:owner/name
- Bitbucket - bitbucket:owner/name
-
папка назначения
-
Параметры опций для несут при загрузке
const downloadGitRepo = require('download-git repo')
// 把目标项目下载到当前目录下的test下
downloadGitRepo('CavinHuang/node-cli-demo', './test', false, err => {
console.log(err ? 'SUCCESS' : "FAIL");
} )
Мы выделяем класс для получения списка git-репозитория, информации о версии, загрузки git-кода и других операций. В основном существуют следующие методы. Код не будет опубликован. Весь код находится в ветке 0.0.3 git-репозитория.
/**
* 获取git仓库列表
*/
async fetchRepoList() {}
/**
* 获取仓库所有的版本
* @param {[string]} repo [仓库名称]
* @return {[type]} [description]
*/
async fetchRepoTagList( repo ) {}
/**
* 获取仓库详细信息
* @param {[string]} repo [仓库名称]
* @return {[type]} [description]
*/
async fetchGitInfo( repo ) {}
/**
* 下载git仓库代码到指定文件夹
* @param {[string]} repo [仓库名称]
* @return {[type]} [description]
*/
async downloadGitRepo( repo ) {}
В install.js для начала нам нужно вытащить все шаблоны на складе для выбора, просто заменить выборы на полученный через апи список гит штанов.
import gitCtrl from '../utils/gitCtrl'
import config from '../config'
// 初始化git操作类
let git = new gitCtrl.gitCtrl( config.repoType, config.registry )
Изменение в действии:
// 获取git仓库列表
let choices = await git.fetchRepoList();
Далее следует загрузить код на локальный сервер в соответствии с выбором хранилища пользователем.Мы создаем новую папку конфигурации для хранения некоторых конфигураций, определяем некоторые часто используемые переменные, такие как каталог кеша, версия и т. д., и создаем новую константу. .js
const os = require( 'os' );
import {
name,
version,
engines
} from '../../package.json';
// 系统user文件夹
const home = process.env[ ( process.platform === 'win32' ) ? 'USERPROFILE' : 'HOME' ];
// user agent
export const ua = `${name}-${version}`;
/**
* 文件夹定义
* @type {Object}
*/
export const dirs = {
home,
download: `${home}/.webpack-project`,
rc: `${home}/.webpack-project`,
tmp: os.tmpdir(),
metalsmith: 'metalsmith'
};
/**
* 版本
* @type {Object}
*/
export const versions = {
node: process.version.substr( 1 ),
nodeEngines: engines.node,
[ name ]: version
};
index.js
/**
* 配置文件
*/
export default {
registry: 'cavinHuangORG', // 仓库地址
repoType: 'org', // ['org', 'user']
metalsmith: true
}
С ними давайте загрузим код ниже:
// 下载库
let result = await git.downloadGitRepo( answers.repo )
console.log( result ? 'SUCCESS' : result )
В этот момент мы бежим
webpack-template install
Результат выглядит следующим образом:
install-2.gif
Далее добавляем выбор версии, немного модифицируем код в install.js и добавляем выбор версии:
// 取出选择的git仓库
const repo = answers.repo;
// 获取选择仓库所有的版本
const tags = await git.fetchRepoTagList( repo );
if ( tags.length === 0 ) {
version = '';
} else {
choices = tags.map( ( {
name
} ) => name );
answers = await inquirer.prompt( [
{
type: 'list',
name: 'version',
message: 'which version do you want to install?',
choices
}
] );
version = answers.version;
}
console.log( answers ); // 输出最终的答案
let result = await git.downloadGitRepo( [ repo, version ].join( '@' ) );
console.log( result ? 'SUCCESS' : result )
install-3.gif
В это время заходим в .webpack-project в пользовательской папке системы, и находим замененный нами проект.
На данный момент наш код установки завершен,гитхаб-адрес
выполнить команду инициализации
Команда init инициализирует локальный проект, включая некоторую информацию, заполненную пользователем, по сути, принцип заключается в замене включенных параметров и копировании проекта, загруженного в каталог кеша, в текущий каталог выполнения командной строки. Прежде всего, мы все еще завершаем доход простейшей командной строки, ввод информации пользователем, здесь мы все еще используем опрашиватель для завершения:
// 1、选择哪个模板
// 2、当前项目的名字,也是初始化项目的文件夹名字
let questions = [
{
type: 'list',
name: 'template',
message: 'which template do you want to init?',
choices: list
}, {
type: 'input',
name: 'dir',
message: 'project name',
async validate( input ) {
// 下面这行代码用于通知异步任务
const done = this.async();
if ( input.length === 0 ) {
done( 'You must input project name' );
return;
}
const dir = resolve( process.cwd(), input );
if ( await exists( dir ) ) {
done( 'The project name is already existed. Please change another name' );
}
done( null, true );
}
}
];
const answers = await inquirer.prompt( questions )
справка по использованию ncp
Далее следует собрать более подробную информацию и скопировать загруженные файлы во временный каталог для обработки.Здесь для копирования файлов используется зрелая библиотека ncp, которая представляет собой библиотеку, совместимую с командным интерфейсом linux cp.Официальный сайт, основной метод вызова:ncp [source] [dest] [--limit=concurrency limit] [--filter=filter] --stopOnErr
var ncp = require('ncp').ncp;
ncp.limit = 16;
ncp(source, destination, function (err) {
if (err) {
return console.error(err);
}
console.log('done!');
});
var mkdirp = require('mkdirp');
mkdirp('/tmp/foo/bar/baz', function (err) {
if (err) console.error(err)
else console.log('pow!')
});
По этим двум библиотекам мы устанавливаем инструментальную функцию, специально используемую для копирования нашего проекта
import {
ncp
} from 'ncp';
import mkdirp from 'mkdirp'
import {
exists
} from 'mz/fs'
export default function copyFile( src, dest ) {
return new Promise( async ( resolve, reject ) => {
if ( !( await exists( src ) ) ) {
mkdirp.sync( src ); //异步创建
}
ncp( src, dest, ( err ) => {
if ( err ) {
reject( err );
return;
}
resolve();
} );
} );
}
Скопируйте во временную папку и сгенерируйте проект, пройдите процесс заполнения данных, который в основном использует генератор статических сайтов (Metalsmith) и глоток иКонсолидация библиотеки слияния шаблона двигателя
Добавить действие копирования и действие компиляции в init.js
const answers = await inquirer.prompt( questions )
const metalsmith = config.metalsmith;
if ( metalsmith ) {
const tmp = `${dirs.tmp}/${answers.template}`;
// 复制一份到临时目录,在临时目录编译生成
await copyFile( `${dirs.download}/${answers.template}`, tmp );
await metalsmithACtion( answers.template ); // 根据参数编译
await copyFile( `${tmp}/${dirs.metalsmith}`, answers.dir );
await rmfr( tmp ); // 清除临时文件夹
} else {
await copyFile( `${dirs.download}/${answers.template}`, answers.dir );
}
Наконец, все структура каталога выглядит следующим образом:
│ .babelrc
│ .gitignore
│ index.js
│ package.json
│
├─bin
│ hi.js
│
└─src
│ index.js
│
├─command
│ init.js
│ install.js
│
├─config
│ constant.js
│ index.js
│
└─utils
copyFile.js
gitCtrl.js
initProjectQuestion.js #初始化项目的问题
metalsmithACtion.js #临时文件夹编译动作
render.js #编译模板的插件
Здесь реализованы все функции, чтобы сделать всю команду более удобной, более процессной, вводим эту библиотеку, адрес проекта:ora, основные эффекты следующие:
Создайте новый OraLoading.js в utils
import ora from 'ora';
export default function OraLoading( action = 'getting', repo = '' ) {
const l = ora( `${action} ${repo}` );
return l.start();
}
Ну тут все написали, попробуем эффект: Первое установить
install-last.gif
Init тоже одинаково, нет демонстрации
публиковать нпм
- Перейдите на npm.com, чтобы зарегистрировать свою учетную запись, перейдите в папку текущего каталога из командной строки, выполните команду npm login, введите пароль своей учетной записи и войдите в систему.
воплощать в жизнь
npm publish .
Вы можете опубликовать свой собственный пакет npm, обратите внимание на яму здесь, если вы используете исходный код Taobao, вам нужно переключиться обратно на исходный код npm,
npm config set registry http://registry.npmjs.org
В противном случае проверка не проходит.
Наконец, укажите адрес кода завершения github:гитхаб-портал