Создайте свою собственную npm init команду для своего проекта

Я делюсь одной уловкой в ​​день до первоначально запланированной даты окончания карантина COVID-19 в Швейцарии, 19 апреля 2020 года. Четырнадцать дней осталось до этого первого рубежа. Надеюсь, впереди лучшие дни.

В этой статье я покажу вам, как создать команду CLI npm для вашего проекта с нуля.

Примечание. Эта статья и наш Cli более чем немного вдохновлены удивительным Stencil’s Cli.

NPM Init

Многие веб-проекты предлагают Cli для облегчения создания новых проектов. Используя их, без какой-либо другой предварительной установки, кроме Node и npm, мы можем запустить команду в нашем терминале, чтобы запустить новый новый проект. Например, npm init stencil создает компонент или приложение с помощью Stencil, или npm init react-app запускает новое приложение React.

Это возможно благодаря поддержке npm и их команд Cli.

Чтобы создать такой инструмент, мы должны создать и опубликовать проект - сам Cli - который содержит index.js и при вызове выполняет основную функцию.

Но что действительно важно, так это название проекта. На самом деле, он должен иметь префикс create-, чтобы позже он был разрешен вашей командной строкой.

Например, имя нашего проекта - DeckDeckGo, следовательно, имя связанного проекта Cli - create-deckdeckgo. При этом каждый раз, когда кто-то запускает npm init deckdeckgo в своем терминале, npm выполняет поиск соответствующего проекта create-. Если он найден, он загружает его локально и запускает основную функцию.

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

Давайте попробуем создать наш собственный Cli под названием «Hello».

Как объяснялось выше, перед именем Cli проекта должен стоять префикс create-, поэтому мы создаем новую папку create-hello:

mkdir create-hello && cd create-hello

Затем мы определяем package.json, который, помимо определения точки входа index.js для bin, устанавливает сценарии и зависимости, чтобы иметь возможность разрабатывать и строить наш проект с помощью Rollup и TypeScript:

{
  "name": "create-hello",
  "version": "1.0.0",
  "main": "dist/index.js",
  "scripts": {
    "start": "node dist/index.js",
    "build.tsc": "tsc",
    "build.bundle": "rollup -c",
    "minify": "terser --compress --mangle --toplevel --output dist/index.js -- dist/index.js",
    "build": "npm run build.tsc && npm run build.bundle && npm run minify",
    "build.dev": "npm run build.tsc && npm run build.bundle",
    "dev": "npm run build.dev && npm start",
    "version": "npm build"
  },
  "files": [
    "dist/index.js"
  ],
  "bin": {
    "create-hello": "dist/index.js"
  },
  "devDependencies": {
    "rollup": "^2.3.3",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-json": "^4.0.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "terser": "^4.6.10",
    "tslint": "^6.1.1",
    "tslint-ionic-rules": "0.0.21",
    "typescript": "^3.8.3"
  },
  "dependencies": {}
}

Использование TypeScript означает определение tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es2015",
    "allowJs": true,
    "module": "es2015",
    "lib": ["es2015"],
    "strict": true,
    "noEmitOnError": false,
    "sourceMap": false,
    "declaration": false,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "dist/src",
    "strictNullChecks": false
  },
  "files": [
    "src/index.ts"
  ]
}

И некоторые правила линтера:

{
  "extends": "tslint-ionic-rules",
  "rules": {
    "no-conditional-assignment": false,
    "no-non-null-assertion": false,
    "no-unnecessary-type-assertion": false,
    "prefer-for-of": false,
    "no-import-side-effect": false,
    "ordered-imports": [true, {
      "named-imports-order": "lowercase-last"
    }]
  }
}

Наконец, нам также необходимо настроить нашу сборку Rollup, в частности, чтобы иметь возможность запускать команды, которые взаимодействуют с файловой системой. Это не является целью данной статьи, но это может быть полезно, если мы хотим создать настоящий Cli для создания новых локальных проектов:

import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json';

export default {
    input: 'dist/src/index.js',
    output: {
        file: 'dist/index.js',
        format: 'cjs',
        strict: false,
        banner: '#! /usr/bin/env node\n',
    },
    plugins: [resolve(), json(),
              commonjs({include: 'node_modules/**'})],
    external: [
        'child_process',
        'fs',
        'path',
        'os',
        'https',
        'readline',
        'zlib',
        'events',
        'stream',
        'util',
        'buffer'
    ]
};

Кодируйте свой Cli

Все готово для развития нашего Cli. Как было сказано выше, это не что иное, как index запись с функцией main. Итак, давайте создадим новый файл src/index.ts, который всего лишь распечатает «Hello World»:

async function run() {
    console.log('Hello World');
}

run();

После установки зависимостей (npm install) мы сможем построить и запустить проект:

npm run build && npm run start

Если все работает, как ожидалось, вы должны заметить, что на вашем терминале распечатано «Hello World».

Опубликуйте свой Cli

Даже если это еще не так, мы уже можем опубликовать наш клиентский интерфейс в npm (npm publish). Если мы это сделаем, то после успешной публикации любой, кто запускает npm init hello, напечатает «Hello World» в своем терминале!

Идти дальше

Вот пара вещей, которые могут вас заинтересовать, если вы планируете по-настоящему разработать собственный Cli.

Аргументы

Мы можем захотеть прослушать некоторые аргументы (args. Обычно мы можем захотеть распечатать некоторую информацию, если пользователь передает аргументы --help.

function run() {
    const args = process.argv.slice(2);

    const help = args.indexOf('--help') >= 0 || 
                 args.indexOf('-h') >= 0;

    if (help) {
        console.log('Run your command without arguments.');
        return;
    }

    console.log('Hello World');
}

run();

Мы можем проверить это, запустив командную строку npm run build && npm run start -- --help. Обратите внимание, что двойной -- нужен только для локального тестирования нашего пакета.

Цвета

Жизнь без красок грустна. Давайте воспользуемся Colorette (npm install colorette --save), чтобы немного скрасить наш Hello World:

import {magenta} from 'colorette';

function run() {
    console.log(magenta('Hello World'));
}

run();

Взгляните на этот красивый пурпурный цвет, не правда ли, удобнее для пользователя?

Интерактивная командная строка

Мы можем не только аргументировать, но и задать пользователю несколько вопросов или дать ему несколько вариантов при выполнении нашего Cli. Для этой цели я люблю использовать запросчик (npm install inquirer --save и npm install @types/inquirer --save-dev):

import {cyan, magenta} from 'colorette';

function run() {
    console.log(magenta('Hello World'));

    const inquirerHappy = require('inquirer');

    const questionHappy = [
        {
            type: 'confirm',
            name: 'happy',
            message: 'Are you happy today?',
            default: true
        }
    ];

    const answer = await inquirerHappy.prompt(questionHappy);

    console.log(cyan(
           `You are${answer.happy ? '' : ' not'} happy today.`));
}

run();

Очевидно, что если я выполню вышеуказанное, я отвечу «да»!

Эффективное создание проекта

Если вы хотите создать свой собственный Cli, есть вероятность, что ваша цель - создавать новые проекты на местном уровне. Позвольте мне рассказать вам о своем подходе к этой проблеме. Cli может решить эту проблему, выполнив следующие действия:

function createProject() {
    downloadFromGitHub();

    installDependencies();

    updateLocalValues();
}

Во-первых, он загружает проект из GitHub или другого репо. Это может быть git clone или cURL или любая другая команда. Идея состоит в том, чтобы получить исходный код из репо и создать копию локально.

Затем, если это проект npm, мы можем захотеть установить зависимости. Обратите внимание, что в таком случае важно отслеживать команды и обязательно выполнять cleanUp в конце процесса.

Наконец, когда мы копируем или клонируем проект, нам может потребоваться обновить некоторые переменные, о которых мы просили ранее. Обычно с помощью нашего Cli мы спрашиваем: «Как называется ваша презентация?» Или «Как вас зовут?» Эта информация заменяется автоматически.

Чтобы обработать все эти шаги, проверьте или клонируйте наше репо.

Резюме

Я нахожу действительно крутым, даже если ваш проект такой же любимый, как наш, иметь возможность создавать Cli.

Надеюсь, эта статья поможет вам создать свою. Если вам нужно подготовить несколько слайдов, попробуйте DeckDeckgo: npm init deckdeckgo.

Оставайся дома, будь в безопасности!