В этой статье мы изучим, что такое 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)
Инициализация проекта
Для начала нам нужно создать первый файл шаблона, который является компонентом 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`,
});
Экспорт файла шаблона
import componentMain from "./component-main.mjs";
const templates = [componentMain];
export default templates;
Нам нужен основной файл, который экспортирует все компоненты (например, 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;
Напишите основной логический код в файле
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);
}
Приступаем к созданию компонента, открываем терминал, например, мы хотим создать компонент Button.
Успешно, мы создали компонент Button, и экспортируемый связанный код автоматически загружается в components/index.ts, отлично.
Перевод статьи подготовлен в рамках специализации "Fullstack Developer".
Комментарии (6)
d_hawk
08.06.2022 08:17+1Я точно такие же скрипты на обычном node.js могу писать, зачем мне этот пакет?
EvilShadow
08.06.2022 16:42Командная оболочка Bash хороша, но когда дело доходит до написания
скриптов, люди часто выбирают более удобный язык программированияДа
и JavaScript прекрасно для этого подходит.
Да, если знаешь только JavaScript. Сделать выбор из одного варианта несложно. Если же знаешь что-то ещё - однозначно нет.
Шелл-скрипты предназначены для того, чтобы быть клеем между остальными утилитами. Именно так их и следует использовать. По мере появления более сложных задач появляются и другие языки, более сложные и с разным предназначением: от обработки текста до полноценного ООП. Это awk, perl, ruby, python.
Пожалуйста, используйте JavaScript в той области, для которой он создан - для выполнения скриптов в браузере. Все остальные задачи решаются более подходящими инструментами.
ZiggiPop
>Командная оболочка Bash хороша, но когда дело доходит до написания скриптов, люди часто выбирают более удобный язык программирования, и JavaScript прекрасно для этого подходит.
То, что люди не хотят потрать несколько часов на изучение shell никак не говорит о том, что шелл «менее удобный». Даже привычный для админов python в каких-то чисто шелловских задачах излишен, и решения на шелле будут элегантнее и проще. Зачем тащить еще один тулчейн в сферу, в которой есть гораздо более привычные инструменты — мне не очень понятно.
А в приведенном примере как бы совсем не видны обещанные свойства "серебряной пули". Код получился громоздким, менее очевидным, чем шелловский, понятным только тем(извините), кто пишет на JS, и самое главное — задача совершенно не шелл-специфичная, и непонятно, зачем тут вообще вспоминали про шелл. Грубо говоря, пишите на JS и вам нужна тулза для вашего JS-тулчейна — прекрасно, пишите на JS, только за пределы своего JS-мира лучше не навязывайте.
zede
В корне не согласен с "потратить несколько часов на изучение shell". Язык баш абсолютный антипод к правилу "один раз научившись ездить на велосипеде ты будешь ездить на нем всегда". Я раза 4 с нуля учил баш и стоит им не пользоваться в течении полугода-года, так код на нем вновь превращается в головоломку и приходится по новой открывать мануалы.
Если ты каждый день пишешь скрипты - ок используй шелл на здоровье win win. Если шелл тебе нужен ну очень ситуативно пару раз в год, то лучше не тратить на него свое время и воспользоваться более сподручным инструментом.
ZiggiPop
>Я раза 4 с нуля учил баш и стоит им не пользоваться в течении полугода-года, так код на нем вновь превращается в головоломку
Как-то так ¯\_(ツ)_/¯
khajiit
Язык или инструменты командной строки (которые выполняют функцию скорее библиотек и фреймворков в более обычных языках, а то и реализуют собственные языки)?
А то выйдет как в том анекдоте: