上一篇文章:實戰 | 從零開始使用JavaScript製做本身的命令行(CLI工具) 中介紹瞭如何從零開始製做CLI,只是一個簡單的示例,今天更進一步,來製做一個實際的程序,生成HTML模板。javascript
在這篇文章中,咱們將構建一個簡單的CLI,容許用戶生成HTML頁面。咱們首先要生成一個標準的空白頁面,而後讓用戶輸入參數,好比文件名和標題,先經過選項,而後經過提示問題讓用戶輸入參數。html
建立用於編寫CLI的文件夾。我將其命名爲 html-generator-cli。打開一個終端,而後在此文件夾中運行:前端
npm init
該命令會有幾個問題要問你,順便說一下,這正是咱們最終但願在空白HTML頁面生成器中包含的內容。這將在文件夾中生成 package.json
文件:java
咱們須要建立包的 index.js
文件做爲入口在package.json中引入。在這個文件中,寫入下面代碼:node
console.log('Hello World!');
如今咱們須要建立運行這段代碼的命令。web
{ "name": "html-generator-cli", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "bin": "index.js" }
將最後一行添加到package.json中。如今,咱們能夠測試咱們很是簡單的CLI。在項目文件夾中局安裝咱們新建立的包到本機:面試
npm install -g ../html-generator-cli
打開一個新終端並運行:shell
html-generator-cli
若是您使用Windows,如今應該會看到「Hello World!」。在您的終端中。若是您使用的是基於UNIX的操做系統,則應該獲得一個錯誤,可能與語法錯誤和意外的token有關。我本人用的是Mac,結果人以下npm
這是由於與Windows不一樣,基於UNIX的系統不關心文件的擴展名(此處爲「.js」),所以不知道使用哪一種語言。咱們必須告訴系統使用Node運行腳本。爲此,咱們在文件的開頭添加一條註釋行:json
#!/usr/bin/env node console.log('Hello World!');
咱們要建立一個CLI來生成HTML文件,爲此,咱們將使用Node.js文件系統模塊。該模塊是Node內置模塊,提供與文件系統交互的API,也就是說能夠建立、讀取、修改和刪除文件。咱們只須要使用文件系統模塊的 writeFile
方法便可,該方法容許你建立文件。
#!/usr/bin/env node const fs = require('fs'); const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Title</title> </head> <body> </body> </html>`; fs.writeFile('index.html', html, error => { if (error) { console.log(error); } });
若是您再次在終端中保存並運行 html-generator-cli
,如今應該在文件夾中看到一個 index.html
文件。
如今咱們生產的文件名和HTML中的 title
標籤內容是寫死的,咱們應該能夠將文件名和標題做爲參數傳遞給CLI。要傳遞參數,你只需在命令以後寫上參數,而後這些參數就能夠在一個名爲 argv
的變量中提供給進程。
在代碼中編寫以下代碼:
const args = process.argv; console.log(args);
並在終端中運行它:
html-generator-cli hello haha
而後,你應該在控制檯中看到一個包含參數做爲字符串的數組:
傳遞的參數在數組的最後兩項,咱們只須要使用數組的 slice(2)
方法便可拿到。咱們決定第一個輸入參數是文件名(不帶HTML擴展名),第二個參數將是HTML頁面的標題。這些參數都不是必需的,若是沒有提供名稱和標題,則咱們將文件稱爲index.html,標題爲「Title」。
#!/usr/bin/env node const fs = require('fs'); const args = process.argv.slice(2); let fileName = args[0] ? `${args[0]}.html` : 'index.html'; let title = args[1] || 'Title'; const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>${title}</title> </head> <body> </body> </html>`; fs.writeFile(fileName, html, error => { if (error) { console.log(error); } });
咱們保持簡單,不驗證用戶輸入的狀況,用戶可能會給該文件指定了無效的名稱,這是你在實際工做中必須驗證的內容。
如今,你能夠在終端中嘗試如下操做:
html-generator-cli page "new generator"
結果
先前的方法易於實現,但有一些缺點:用戶必須知道指望哪些參數以及以什麼順序。若是他不想給出文件名,他也沒有辦法給出標題,咱們能夠經過建立選項來改善這一點。
與其一個接一個地寫參數,咱們能夠構建咱們的CLI,讓用戶輸入相似於這樣的文件名和/或標題。
html-generator-cli --file-name page --html-title "new generator"
寫起來有點長,可是用戶更清楚他給出的參數是什麼,順序再也不起做用,你能夠給出一個標題,即便你沒有給出任何文件名。
const args = process.argv; const FILE_NAME_OPTION = '--file-name'; const HTML_TITLE_OPTION = '--html-title'; const fileNameOptionIndex = args.findIndex(arg => arg === FILE_NAME_OPTION); const htmlTitleOptionIndex = args.findIndex(arg => arg === HTML_TITLE_OPTION); const fileNameOption = fileNameOptionIndex > -1 && args[fileNameOptionIndex + 1]; const titleOption = htmlTitleOptionIndex > -1 && args[htmlTitleOptionIndex + 1]; let fileName = fileNameOption ? `${fileNameOption}.html` : 'index.html'; let title = titleOption || 'Title';
如今,咱們在參數數組 args
中得到選項 --file-name
或 --html-title
的索引。若是存在一個選項,那麼要給文件名或標題的值就是參數數組中 --file-name
或 --html-file
以後的元素。若是不存在選項,則其索引將爲 -1
。若是此索引爲 -1
或參數數組中該選項以後沒有任何值,咱們分別爲文件名或標題提供默認值。其他代碼未更改。
你能夠運行新的CLI,若是沒有選擇,它將建立標題爲「Title」的index.html文件。若是你編寫一個選項但忘記提供一個值,它將也提供默認值。若是你正確地使用給定的選項編寫命令,那麼它應該建立一個具備正確名稱和正確HTML標題的文件。
html-generator-cli --file-name hello --html-title "CLI helloworld!"
效果
一樣,在實際的CLI中,你會但願多檢查一些輸入,首先要確保用戶輸入的值是有效的,但也要在缺失值或選項出現兩次的狀況下警告他們。
使用選項已是一種改進了,可是它仍然須要用戶知道他能夠傳遞什麼參數以及使用哪一個標記。當你初始化你的npm項目時,你能夠經過不少東西做爲選項。CLI會直接問您一些問題,所以您無需閱讀文檔便可瞭解如何提供項目名稱,版本等信息。
要從控制檯讀取用戶輸入,咱們須要Node(自版本7)提供的模塊 readline
。你可使用如下代碼在終端中對其進行測試:
const readline = require('readline'); const interface = readline.createInterface({ input: process.stdin, output: process.stdout }); interface.question('你叫什麼名字?', answer => { console.log(`Hello ${answer}`); interface.close(); });
效果以下
爲了生成咱們的HTML頁面,咱們首先要詢問文件名,而後詢問標題。若是用戶沒有輸入任何內容,咱們將得到默認值。咱們向用戶顯示默認值是什麼,以便在默認值正確的狀況下能夠跳過該問題。
#!/usr/bin/env node const fs = require('fs'); const readline = require('readline'); let fileName = 'index.html'; let title = 'Title'; const interface = readline.createInterface({ input: process.stdin, output: process.stdout }); interface.question(`File name (${fileName}): `, answer => { if (answer && answer.length) { fileName = `${answer}.html`; } interface.question(`HTML title (${title}): `, answer => { if (answer && answer.length) { title = answer; } interface.close(); const html = `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>${title}</title> </head> <body> </body> </html>`; fs.writeFile(fileName, html, error => { if (error) { console.log(error); } }); }); });
若是你在終端中運行它,將會詢問兩個問題。
用戶沒必要了解您的CLI選項,全部重要的事情均可以直接詢問。可是,你應該只以這種方式詢問主要配置問題,並讓用戶閱讀文檔以瞭解不太常見的選項。
咱們使用Node和npm建立了一個簡單的CLI,容許用戶生成一個空白的HTML文件,是否是很是簡單?你能夠經過添加新選項並驗證用戶輸入來改進此示例。
文章首發《前端外文精選》微信公衆號
繼續閱讀其餘高贊文章