實戰 | HTML頁面生成器:使用JavaScript和Node建立CLI

上一篇文章:實戰 | 從零開始使用JavaScript製做本身的命令行(CLI工具) 中介紹瞭如何從零開始製做CLI,只是一個簡單的示例,今天更進一步,來製做一個實際的程序,生成HTML模板。javascript

在這篇文章中,咱們將構建一個簡單的CLI,容許用戶生成HTML頁面。咱們首先要生成一個標準的空白頁面,而後讓用戶輸入參數,好比文件名和標題,先經過選項,而後經過提示問題讓用戶輸入參數。html

建立 Hello World CLI

建立用於編寫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!');

建立一個空白的HTML頁面

咱們要建立一個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文件,是否是很是簡單?你能夠經過添加新選項並驗證用戶輸入來改進此示例。


文章首發《前端外文精選》微信公衆號

subscribe2.png

繼續閱讀其餘高贊文章


相關文章
相關標籤/搜索