Zpět k blogu

CLI aplikace v Node.js

Aleš Dostál

Aleš Dostál

19. 4. 2020
Technologie
CLI aplikace v Node.js

Psát o tom, že JavaScript je jazyk, který má dnes uplatnění i jinde než jen ve webovém prohlížeči, je asi zbytečné. Často se setkáváme s tím, že by se nám hodila vlastní konzolová aplikace, která vykonává naši potřebnou činnost. K tomuto účelu lze využít bash, PowerShell či jazyky jako je C, Python, apod. Nicméně, proč nevyužít něco, co známe. JavaScript nám k tomuto účelu může posloužit více než dobře.

Aby ukázky, které zde naleznete, byly lépe pochopitelné, můžete se rovnou podívat na repozitář, který jsem k tomuto účelu vytvořil: apitree-cli-example.

Prvním krokem je nutná instalace Node.js. Zároveň doporučuji použít Yarn pro spouštění skriptů při vývoji. Každopádně volba, zda použijete Yarn či NPM, je na Vás.

Po úspěšném nainstalování Node.js se můžeme pustit do práce :)

Příprava projektu

Setup projektu můžeme provést jednoduše pomocí příkazu npm:

npm init -y

Poté si naistalujeme knihovny, které budeme v našem projektu využívat:

npm i chalk figlet yargs

Do dev dependencies si nainstalujeme typové definice knihoven, ts-node a TypeScript:

npm i -D @types/node @types/figlet @types/yargs ts-node typescript

Nyní si nakonfigurujeme TypeScript pomocí souboru tsconfig.json (leží v rootu projektu):

{ "compilerOptions": { "target": "es5", "module": "commonjs", "lib": ["es6", "es2015", "dom"], "declaration": true, "outDir": "./lib", "rootDir": "./src", "strict": true, "esModuleInterop": true, "resolveJsonModule": true }, "include": ["src/**/*"] }

Teď už nám nic nebrání a můžeme začít psát náš vysněný kód.

Hello world aplikace

Čím lepším začít než je Hello World.

V adresáři src vytvoříme soubor index.ts s tímto kódem:

console.log('Hello World');

Abychom mohli nyní náš úžasný kód spustit, tak si upravíme package.json, přesněji blok scripts, takto:

"scripts": { "dev": "ts-node src/index.ts" }

Vzhledem k tomu, že jsme si nainstalovali knihovnu ts-node, můžeme nyní spouštět TypeScript soubory přímo, aniž bychom museli provádět kompilaci. Takže jdeme na to. V konzoli spusťte:

npm run dev

Pokud jste vše udělali správně, konzole by Vám měla vypsat Hello World.

Asi se shodneme na tom, že zatím nic moc úžasného se nám nepodařilo. Sice máme svůj první program, ale vypsat do konzole Hello World moc užitečné není. Proto si náš projekt trochu obohatíme.

Využití knihovny yargs

Jistě jste si všimli, že jsme na začátku instalovali různé knihovny a jedna z nich se jmenuje yargs. Tato knihovna nám slouží k tomu, abychom mohli jednoduše napsat program, který přijímá a validuje argumenty z konzole.

Aby náš projekt měl smysl, řekněme, že chceme napsat konzolovou aplikaci, která má dva příkazy: create a update. A u příkazu create je ještě vyžadován parametr --name. Taková aplikace by mohla sloužit jako generátor vlastních aplikací, jako je například create-react-app, kterou jistě mnoho z Vás dobře zná.

Upravíme si tedy index.ts do této podoby:

#!/usr/bin/env node import yargs from 'yargs'; import { ConsoleService } from './service'; const MAIN_CMD = 'at-example'; const TITLE = 'ApiTree CLI'; const log = ConsoleService.getLogger(); enum Command { CREATE = 'create', UPDATE = 'update', } const run = async (command: Command, args: { [appArgs: string]: unknown }) => { log.title(await ConsoleService.getTitle(TITLE)); switch (command) { case Command.CREATE: log.info(`Create awesome application with name: ${args.name}`); return; case Command.UPDATE: log.info('Update awesome application'); return; default: throw new Error(`Command not implement yet`); } }; yargs .usage(TITLE) .command({ command: Command.CREATE, describe: 'Create awesome application', aliases: 'c', builder: (builder) => builder.options({ name: { type: 'string', demandOption: true, alias: 'n' } }), handler: async (args) => { await run(Command.CREATE, args); }, }) .command({ command: Command.UPDATE, describe: 'Update awesome application', aliases: 'u', handler: async (args) => { await run(Command.UPDATE, args); }, }) .example(Command.CREATE, `${MAIN_CMD} ${Command.CREATE} --name awesome-app`) .example(Command.UPDATE, `${MAIN_CMD} ${Command.UPDATE}`) .demandCommand(1, 1) .showHelpOnFail(true) .epilog('ApiTree software') .strict().argv;

To už jistě vypadá zajímavěji. Teď si projdeme jednotlivé části, které zde vidíme.

Na prvním řádku si můžete všimnout (#!/usr/bin/env node). V této chvíli to sice nepotřebujeme, ale až budeme naši konzolovou aplikaci distribuovat, je to přesně tento řádek, který z našeho skriptu udělá samospustitelný skript.

Poté jsme si nadefinovali enum typem všechny příkazy (commands), které naše aplikace bude podporovat. Tedy v našem případě je to create a update.

Dále je zde funkce run, která na základě parametru command a args spustí daný kód. Zde je odkaz na metodu z ConsoleService, kterou naleznete ve zmiňovaném repozitáři.

Poté přichází na řadu konfigurace samotné knihovny yargs. Zde jsou nejzajímavější samotné příkazy v bloku command. Tedy musíme určit název příkazu, jeho popis, alias a poté funkci, která vykoná daný kód, pokud se jedná o daný příkaz. U příkazu create je zde navíc metoda builder, která vrací option parametr name, který je typu string a navíc je povinný. Kromě definice příkazů je dobré nastavit ještě další parametry jako je například demandCommand(min, max). Tato metoda říká, kolik příkazů je v rámci jednoho spuštění možné zadat. My zůstaneme u toho, že min i max je 1, a tudíž je možné spustit pouze jeden příkaz.

Nyní si pojďme vyzkoušet, zda naše aplikace funguje. Využijeme k tomu již nadefinovaný skript v package.json. Skript budeme spouštět pomocí Yarnu:

yarn dev

Pokud jste vše udělali správně, skript by měl skončit chybovou hláškou, která říká, že musíte zadat platný argument:

Not enough non-option arguments: got 0, need at least 1

Proto si spusťme program s danými parametry:

yarn dev create --name awesome-app

Nyní bychom měli dostat úspěšnou hlášku:

Create awesome application with name: awesome-app

Zároveň můžeme využít i naše aliasy:

yarn run dev c -n test-app

To už vypadá o něco lépe. Knihovna yargs navíc implementuje i argumenty pro nápovědu a verzi. Pojďme si to vyzkoušet:

yarn run dev --help

V takovém případě bychom měli dostat kompletní výpis možností, které máme:

ApiTree CLI Commands: index.ts create Create awesome application [aliases: c] index.ts update Update awesome application [aliases: u] Options: --help Show help [boolean] --version Show version number [boolean] Examples: create at-example create --name awesome-app update at-example update ApiTree software

Také je možné help využít na samotných příkazech. Viz:

yarn run dev create --help

Zde si můžeme všimnout, že nám přibyla další volba, která je označena jako required:

--name, -n [string] [required]

Myslím, že naši aplikaci máme hotovou. O tom, co přesně a jak má dělat, je již čistě na Vás. Jako poslední věc si nyní ukážeme, jak takovou aplikaci vlastně distribuovat.

Distribuce aplikace

Abychom mohli naši aplikaci distribuovat, musíme upravit náš package.json:

"files": [ "lib/**/*" ], "main": "./lib/index.js", "bin": { "at-example": "./lib/index.js" }, "scripts": { "dev": "ts-node src/index.ts", "build": "tsc" }

Blokem files říkáme, které soubory či adresář budeme distribuovat. V našem případě je to adresář lib, do kterého se bude kompilovat výsledný TypeScript soubor.

Dále řekneme, který soubor je výchozí (main). V tomto případě je to index.js v adresáři lib. Ten vznikne samotnou kompilací.

Dalším důležitým nastavením je název samotného skriptu. Zde je dobré definovat takový název, který je dostatečně popisný a zároveň nekoliduje s jinými příkazy v operačním systému. V našem případě se příkaz bude jmenovat at-example.

Poslední částí je build, který je nadefinován v bloku scripts.

Lokální distribuce

Před tím, než náš úžasný program budeme distribuovat do NPM repozitáře, je dobré si ho lokálně otestovat. K tomuto účelu využijeme npm.

Nejdříve náš projekt zkompilujeme:

npm run build

Po kompilaci vznikne adresář lib, ve kterém je náš projekt zkompilován do JavaScriptu s TypeScript definicí.

Nyní si náš projekt zabalíme pomocí NPM, abychom ho zpětně globálně nainstalovali:

npm pack

Po tomto příkazu Vám v rootu projektu vznikne tgz soubor, který má název podle názvu a verze v package.json. V mém případě to je: apitree-apitree-cli-example-0.1.0-alpha.1.tgz, protože název projektu mám: @apitree/apitree-cli-example a verzi: 0.1.0-alpha.1.

Nyní si tento soubor globálně nainstalujeme:

npm i -g apitree-apitree-cli-example-0.1.0-alpha.1.tgz

Po úspěšné instalaci již můžeme spouštět náš projekt pomocí příkazu at-example:

at-example --help

Výsledek je stejný jako v případě spouštění přes ts-node.

Vzdálená distribuce

Pro vzdálenou distribuci je nutné mít NPM repozitář. Buď na npmjs.com či vlastní. Zároveň je nutné se do tohoto repozitáře přihlásit. Bud pomocí npm login nebo přes .npmrc soubor, ve kterém se nadefinuje přístupový token.

Pro vzdálenou distribuci již stačí jen spustit:

npm publish

A pro samotnou instalaci poté:

npm i -g @company/my-name

Závěr

Samotné psaní konzolových (CLI) aplikací v JavaScriptu je vcelku jednoduchá záležitost. Největší překážkou je zde spíše samotná počáteční konfigurace, která může leckoho odradit. Nicméně, pokud se prokoušete tímto začátkem, máte nepřeberné množství variant, co taková aplikace může dělat. Spíše můžete narážet na rozdíly mezi jednotlivými operačními systémy. Například tento projekt byl vytvořen na MacOS a tudíž pro zajištění kompatibility by bylo třeba samotný projekt otestovat také na dalších systémech.

Promluvme si o vašem projektu

Nechte nám zprávu pomocí kontaktního formuláře, nebo se obraťte přímo na naše obchodní oddělení. Domluvíme si s vámi schůzku a promluvíme si o vašich potřebách. Společně zvážíme možnosti a navrhneme nejvhodnější řešení.

Přímý kontakt na obchodní oddělení:

ApiTree s.r.o.

Francouzská 75/4, Praha 2 Vinohrady, 120 00

Společnost ApiTree s.r.o je zapsána v obchodním rejstříku u Městského soudu v Praze, pod spis. zn. C 279944

IČO: 06308643
DIČ: CZ06308643

Bankovní informace

Česká spořitelna
Číslo účtu: 4885827379/0800
IBAN: CZ21 0800 0000 0048 8582 7379
SWIFT: GIBACZPX
ČSOB
Číslo účtu: 340250698/0300
IBAN: CZ31 0300 0000 0003 4025 0698
SWIFT: CEKOCZPP
Copyright 2020 ApiTree s.r.o. Všechna práva vyhrazena. Web vytvořilo a designovalo ApiTree s.r.o.