В этой статье мы изучим, что такое zx.js, какие возможности он предоставляет и как мы можем применить его для написания скриптов, а затем научимся использовать все его фичи, разработав инструмент, который можно запустить из командной строки.

Код инструмента zx доступен на github.

Что такое zx.js и почему его используют?

Командная оболочка Bash хороша, но когда дело доходит до написания скриптов, люди часто выбирают более удобный язык программирования, и JavaScript прекрасно для этого подходит. Перед началом работы стандартная библиотека Node.js требует множества дополнительных операций, таких как инсталляция и ее внедрение. zx обеспечивает обертку модулю child_process, которая экранирует параметры и предоставляет смердженные значения по умолчанию.

Сначала рассмотрим официальный пример:

#!/usr/bin/env zx

// From https://github.com/google/zx#-zx
await $`cat package.json | grep name`  

let branch = await $`git branch --show-current`  
await $`dep deploy --branch=${branch}`  

await Promise.all([  
  $`sleep 1; echo 1`,  
  $`sleep 2; echo 2`,  
  $`sleep 3; echo 3`,  
])  

let name = 'foo bar'  
await $`mkdir /tmp/${name}`

Официальный пример

Синтаксис JavaScript в приведенном выше примере может показаться немного странным. Он использует фичу языка, которая называется тегированные шаблонные литералы (tagged template literals).

Но это же круто, правда? Так что давайте начнем!

Инсталляция

// Глобальная инсталляция.
// Также вы можете установить его отдельно в своем проекте.
npm i zx -g

Требование:

Убедитесь, что версия вашего node >= 16.00.0.

Принцип работы zx.js

Он предоставляет функции, которые оборачивают процедуру создания дочерних процессов и обработку stdout и stderr из них.

Использование

  • Для быстрого тестирования скрипта можно использовать символ пайплайна

$ echo "console.log(await (await fetch('http://orange.tw')).text())" | zx
$ fetch http://orange.tw
183.133.12.121
Your country
  • Другой способ — разработать скрипт в проекте, как мы часто делаем.

Сначала инициализируйте проект,

mkdir zx-demo
cd zx-demo
touch index.mjs
npm init -y

// Note: the zx documentation suggests installing the library globally with npm. 
// By installing it as a local dependency of our project instead, 
// we can ensure that zx is always installed, as well as control the version that our shell scripts use.
npm i zx -D

Основной файл должен содержать следующие заголовки файлов:

#!/usr/bin/env zx

Сначала нужно добавить разрешения на выполнение, а затем запустить скрипт:

chmod +x ./index.mjs  
./index.mjs  

// Or use this command 
zx ./index.mjs

Теперь он может отображать какое-то содержимое, zx.js предоставляет много черной магии, у него есть встроенные fetch, question и асинхронные операции (круто), и теоретически он способен выполнять shell и другие скрипты типа tree и прочие команды, установленные самостоятельно, что очень мощно.

Дальше давайте имплементируем инструмент командной строки, который обычно используется для создания библиотеки компонентов!

Инструмент командной строки

Этот проект поможет нам генерировать общие каталоги и файлы компонентов, а также автоматически заполнять components.ts, что значительно упрощает повторные операции по созданию новых файлов.

Сначала необходимо создать проект ноды в соответствии с приведенной выше частью, а затем создать соответствующий каталог/файл, как показано на рисунке. После завершения операции структура ваших каталогов должна выглядеть следующим образом. Не забудьте создать новый файл index.ts в каталоге компонентов (игнорируйте файл протокола и каталог .vscode)

Инициализация проекта

  1. Для начала нам нужно создать первый файл шаблона, который является компонентом React и по умолчанию экспортирует тип Props, и, в итоге, генерирует содержимое каталога и файла.

export default (fileName) => ({
  content: `
// Generated with index.js, Assume this is a react project
import React from "react";
export interface I${fileName}Props {}
const ${fileName}: React.FC<I${fileName}Props> = () => {
  return (
    <div>{/** Todo... */}</div>
  );
}
export default ${fileName};
`,
  suffix: `tsx`,
});
  1. Экспорт файла шаблона

import componentMain from "./component-main.mjs";

const templates = [componentMain];

export default templates;
  1. Нам нужен основной файл, который экспортирует все компоненты (например, Button, Select, Card); это является хорошей практикой программирования.

#!/usr/bin/env zx

import _fs from "fs";
import { chalk } from "zx";

const fs = _fs.promises;

/**
 * @description This function is subduquered according to the file name generated by `npm run generate your filename`,
 * then automatically injects imported import export in src/components/index.js.
 *
 * @param { string } directoryName
 * @returns { void }
 */
const additionalEntryContent = async (directoryName) => {
  const originalFile = "./src/components/index.ts";

  // Read original file content
  const entryFileContent = await fs.readFile(originalFile, "utf-8");

  // Additional content
  fs.writeFile(
    originalFile,
    entryFileContent.concat(
      `
export type { I${directoryName}Props } './${directoryName}';
export { default as ${directoryName} } from "./${directoryName}";\n`
    )
  );

  console.log(
    chalk.green(`Successfully additional content in: ${originalFile}`)
  );
};

export default additionalEntryContent;
  1. Напишите основной логический код в файле index.mjs, который отвечает за получение информации о параметрах командной строки, вводимой пользователем, определение существования каталога, создание каталога и окончательное обновление каждого файла.

#!/usr/bin/env zx

import { $, chalk } from "zx";
import fs from "fs";

import templates from "./scripts/templates/index.mjs";
import additionalEntryContent from "./scripts/additional-entry.mjs";

$.verbose = false;

const directoryName = process.argv[2];
if (!directoryName) {
  console.error(
    chalk.red("[Error]: Create file error, please supply a valid directoryName")
  );
  process.exit(1);
}

// The Directory address of the generated file
const componentDirectory = `./src/components/${directoryName}`;

if (fs.existsSync(componentDirectory)) {
  console.error(chalk.red(`Directory ${directoryName} already exists`));
  process.exit(1);
}

fs.mkdirSync(componentDirectory);

try {
  const generatedTemplates = templates.map((template) =>
    template(directoryName)
  );

  generatedTemplates.forEach((template) => {
    fs.writeFileSync(
      `${componentDirectory}/index.${template.suffix}`,
      template.content
    );
  });

  console.log(chalk.green("Successfully created directory:", directoryName));

  // Add the context to the imported content in Index.js
  additionalEntryContent(directoryName);
} catch (error) {
  console.error(error);
  process.exit(1);
}
  1. Приступаем к созданию компонента, открываем терминал, например, мы хотим создать компонент Button.

Готово
Готово

Успешно, мы создали компонент Button, и экспортируемый связанный код автоматически загружается в components/index.ts, отлично.


Перевод статьи подготовлен в рамках специализации "Fullstack Developer".

Комментарии (6)


  1. ZiggiPop
    07.06.2022 18:29
    +3

    >Командная оболочка Bash хороша, но когда дело доходит до написания скриптов, люди часто выбирают более удобный язык программирования, и JavaScript прекрасно для этого подходит.


    То, что люди не хотят потрать несколько часов на изучение shell никак не говорит о том, что шелл «менее удобный». Даже привычный для админов python в каких-то чисто шелловских задачах излишен, и решения на шелле будут элегантнее и проще. Зачем тащить еще один тулчейн в сферу, в которой есть гораздо более привычные инструменты — мне не очень понятно.

    А в приведенном примере как бы совсем не видны обещанные свойства "серебряной пули". Код получился громоздким, менее очевидным, чем шелловский, понятным только тем(извините), кто пишет на JS, и самое главное — задача совершенно не шелл-специфичная, и непонятно, зачем тут вообще вспоминали про шелл. Грубо говоря, пишите на JS и вам нужна тулза для вашего JS-тулчейна — прекрасно, пишите на JS, только за пределы своего JS-мира лучше не навязывайте.


    1. zede
      07.06.2022 19:52
      +7

      В корне не согласен с "потратить несколько часов на изучение shell". Язык баш абсолютный антипод к правилу "один раз научившись ездить на велосипеде ты будешь ездить на нем всегда". Я раза 4 с нуля учил баш и стоит им не пользоваться в течении полугода-года, так код на нем вновь превращается в головоломку и приходится по новой открывать мануалы.

      Если ты каждый день пишешь скрипты - ок используй шелл на здоровье win win. Если шелл тебе нужен ну очень ситуативно пару раз в год, то лучше не тратить на него свое время и воспользоваться более сподручным инструментом.


      1. ZiggiPop
        07.06.2022 20:35
        +2

        >Я раза 4 с нуля учил баш и стоит им не пользоваться в течении полугода-года, так код на нем вновь превращается в головоломку

        Как-то так ¯\_(ツ)_/¯


      1. khajiit
        07.06.2022 21:17

        Язык или инструменты командной строки (которые выполняют функцию скорее библиотек и фреймворков в более обычных языках, а то и реализуют собственные языки)?
        А то выйдет как в том анекдоте:


        — Ну что, господа студенты, кто назовет мою фамилию, тому сразу 5 в зачетку.
        Тишина.
        — Ладно, вопрос на 4: какой предмет сдаем?
        Голос с камчатки:
        — Во валит!


  1. d_hawk
    08.06.2022 08:17
    +1

    Я точно такие же скрипты на обычном node.js могу писать, зачем мне этот пакет?


  1. EvilShadow
    08.06.2022 16:42

    Командная оболочка Bash хороша, но когда дело доходит до написания
    скриптов, люди часто выбирают более удобный язык программирования

    Да

    и JavaScript прекрасно для этого подходит.

    Да, если знаешь только JavaScript. Сделать выбор из одного варианта несложно. Если же знаешь что-то ещё - однозначно нет.

    Шелл-скрипты предназначены для того, чтобы быть клеем между остальными утилитами. Именно так их и следует использовать. По мере появления более сложных задач появляются и другие языки, более сложные и с разным предназначением: от обработки текста до полноценного ООП. Это awk, perl, ruby, python.

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