成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

手把手帶你擼一個cli工具

xingqiba / 2637人閱讀

摘要:最近,筆者就在為組里的框架去做一套基本的工具。通過這邊文章,筆者希望大家都能簡單的去實現(xiàn)一個屬于自己的腳手架工具。我們在下新增文件,這個文件導(dǎo)出一個的類。結(jié)語到此,一個簡單的就制作完成了,大家可以參考等優(yōu)秀的適當(dāng)?shù)臄U展自己的工具。

你有沒有遇到過在沒有vue-cli、create-react-app這樣子的腳手架的時候一個文件一個文件的去拷貝老項目的配置文件。最近,筆者就在為組里的框架去做一套基本的cli工具。通過這邊文章,筆者希望大家都能簡單的去實現(xiàn)一個屬于自己的腳手架工具。

原文鏈接: https://juejin.im/user/57ac15...
做好準(zhǔn)備工作

首先,我們需要去新建一個項目并初始化package.json

mkdir my-cli && cd my-cli
npm init

然后我們需要在項目中新建bin文件夾,并將package.json中提供一個bin字段并指向我們的bin文件夾下,這樣通過npm我們就可以實現(xiàn)指令的軟鏈了。

"bin": {
  "mycli": "bin/mycli"
},

在mycli中,我們要在頭部增加這樣一句注釋,作用是"指定由哪個解釋器來執(zhí)行腳本"。

#!/usr/bin/env node

console.log("hello world");

接下來,全局安裝我們這個包,這樣我們就可以直接在本地使用mycli這個指令了。

sudo npm install -g
提供基本模版

既然我們要去做一個初始化項目的cli,那么項目模版就必不可少了,筆者在這里提前準(zhǔn)備了一個demo的項目目錄模版,這里就不展開贅述了。

編寫邏輯

其實核心邏輯很簡單,就是通過控制臺獲取到用戶的一些自定義選項,然后根據(jù)選項去從本地或者遠(yuǎn)程倉庫拿到我們提前準(zhǔn)備好的模版,將配置寫入模版并最后拷貝模版到本地就行了。

我們在src下新增creator.js文件,這個文件導(dǎo)出一個Creator的類。在這個類中現(xiàn)在僅需要三個簡單的方法:init用于初始化、ask用于和命令行交互獲取用戶選擇輸入的數(shù)據(jù)、write用于調(diào)用模版的構(gòu)建方法去執(zhí)行拷貝文件寫數(shù)據(jù)的任務(wù)。

class Creator {
  constructor() {
    // 存儲命令行獲取的數(shù)據(jù),作為demo這里只要這兩個;
    this.options = {
      name: "",
      description: "",
    };
  }
  // 初始化;
  init() {}
  // 和命令行交互;
  ask() {}
  // 拷貝&寫數(shù)據(jù);
  write() {}
}

module.exports = Creator;

先去完善init方法,這個方法里我們僅需要調(diào)用ask方法和命令行交互并做一些提示即可(可以通過chalk這個庫去豐富我們的命令行交互色彩)

// ...
init() {
  console.log(chalk.green("my cli 開始"));
  console.log();
  this.ask();
}
// ...

接下來是ask方法,在這個方法中,我們需要根據(jù)提示引導(dǎo)用戶輸入問題并獲取用戶的輸入,這里用到inquirer這個庫來和命令行交互。

// ...
ask() {
  // 問題
  const prompt = [];

  prompt.push({
    type: "input",
    name: "name",
    message: "請輸入項目名稱",
    validate(input) {
      if (!input) {
        return "請輸入項目名稱!";
      }

      if (fs.existsSync(input)) {
        return "項目名已重復(fù)!"
      }

      return true;
    }
  });

  prompt.push({
    type: "input",
    name: "description",
    message: "請輸入項目描述",
  });

  // 返回promise
  return inquirer.prompt(prompt);
}
// ...

修改剛才的init方法,將ask方法改為Promise調(diào)用。

init() {
  console.log(chalk.green("my cli 開始"));
  console.log();
  this.ask().then((answers) => {
    this.options = Object.assign({}, this.options, answers);
    console.log(this.options);
  });
}

現(xiàn)在我們?nèi)ッ钚性囈幌?,修改bin/mycli文件,然后去運行mycli命令。

#!/usr/bin/env node

const Creator = require("../src/creator.js");

const project = new Creator();

project.init();

在和用戶交互完畢并獲取到數(shù)據(jù)后,我們要做的就是去調(diào)用write方法執(zhí)行拷貝構(gòu)建了??紤]到日后可能增加很多的模版目錄,不妨我們將每一類的模版拷貝構(gòu)建工作放到模版中的腳本去做,從而增大可擴展性,新增template/index.js文件。

接下來首先根據(jù)項目目錄結(jié)構(gòu)創(chuàng)建文件夾(注意區(qū)分項目的執(zhí)行目錄和項目目錄的關(guān)系)。

module.exports = function(creator, options, callback) {
  const { name, description } = options;

  // 獲取當(dāng)前命令的執(zhí)行目錄,注意和項目目錄區(qū)分
  const cwd = process.cwd();
  // 項目目錄
  const projectPath = path.join(cwd, name);
  const buildPath = path.join(projectPath, "build");
  const pagePath = path.join(projectPath, "page");
  const srcPath = path.join(projectPath, "src");

  // 新建項目目錄
  // 同步創(chuàng)建目錄,以免文件目錄不對齊
  fs.mkdirSync(projectPath);
  fs.mkdirSync(buildPath);
  fs.mkdirSync(pagePath);
  fs.mkdirSync(srcPath);

  callback();
}

然后回到creator.js文件,在Creator中的write調(diào)用這個方法。

// ...
init() {
  console.log(chalk.green("my cli 開始"));
  console.log();
  this.ask().then((answers) => {
    this.options = Object.assign({}, this.options, answers);

    this.write();
  });
}

// ...

write() {
  console.log(chalk.green("my cli 構(gòu)建開始"));
  const tplBuilder = require("../template/index.js");
  tplBuilder(this, this.options, () => {
    console.log(chalk.green("my cli 構(gòu)建完成"));
    console.log();
    console.log(chalk.grey(`開始項目:  cd ${this.options.name } && npm install`));
  });
}
// ...

在開啟文件拷貝寫數(shù)據(jù)之前,我們需要用到兩個庫mem-fsmem-fs-editor,前者可以幫助我們在內(nèi)存中創(chuàng)建一個臨時的文件store,后者可以以ejs的形式去編輯我們的文件。

現(xiàn)在constructor中初始化store。

constructor() {
  // 創(chuàng)建內(nèi)存store
  const store = memFs.create();
  this.fs = memFsEditor.create(store);

  this.options = {
    name: "",
    description: "",
  };

  this.rootPath = path.resolve(__dirname, "../");
  this.tplDirPath = path.join(this.rootPath, "template");
}

接下來在Creator中增加兩個方法copy和copyTpl分別用于直接拷貝文件和拷貝文件并注入數(shù)據(jù)。

getTplPath(file) {
  return path.join(this.tplDirPath, file);
}

copyTpl(file, to, data = {}) {
  const tplPath = this.getTplPath(file);
  this.fs.copyTpl(tplPath, to, data);
}

copy(file, to) {
  const tplPath = this.getTplPath(file);
  this.fs.copy(tplPath, to);
}

然后我們根據(jù)ejs的語法修改模版中的package.json文件以實現(xiàn)數(shù)據(jù)注入的功能

{
  "name": "<%= name %>",
  "version": "1.0.0",
  "description": "<%= description %>",
  "main": "index.js",
  "scripts": {},
  "author": "",
  "license": "ISC"
}

回到template/index.js中,對模版中的文件進行相應(yīng)的拷貝和數(shù)據(jù)注入操作,最后打印一些可視化的信息。

module.exports = function(creator, options, callback) {
  const { name, description } = options;

  // 獲取當(dāng)前命令的執(zhí)行目錄,注意和項目目錄區(qū)分
  const cwd = process.cwd();
  // 項目目錄
  const projectPath = path.join(cwd, name);
  const buildPath = path.join(projectPath, "build");
  const pagePath = path.join(projectPath, "page");
  const srcPath = path.join(projectPath, "src");

  // 新建項目目錄
  // 同步創(chuàng)建目錄,以免文件目錄不對齊
  fs.mkdirSync(projectPath);
  fs.mkdirSync(buildPath);
  fs.mkdirSync(pagePath);
  fs.mkdirSync(srcPath);

  creator.copyTpl("packagejson", path.join(projectPath, "package.json"), {
    name,
    description,
  });

  creator.copy("build/build.js", path.join(buildPath, "build.js"));

  creator.copy("page/index.html", path.join(pagePath, "index.html"));

  creator.copy("src/index.js", path.join(srcPath, "index.js"));

  creator.fs.commit(() => {
    console.log();
    console.log(`${chalk.grey(`創(chuàng)建項目: ${name}`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建目錄: ${name}/build`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建目錄: ${name}/page`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建目錄: ${name}/src`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建文件: ${name}/build/build.js`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建文件: ${name}/page/index.html`)} ${chalk.green("? ")}`);
    console.log(`${chalk.grey(`創(chuàng)建文件: ${name}/src/index.js`)} ${chalk.green("? ")}`);

    callback();
  });
}

執(zhí)行mycli指令創(chuàng)建項目,一個簡單的cli就完成了。

結(jié)語

到此,一個簡單的cli就制作完成了,大家可以參考vue-cli、create-react-app等優(yōu)秀的cli適當(dāng)?shù)臄U展自己的cli工具。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102989.html

相關(guān)文章

  • 了解MVVM及Vue實現(xiàn)原理,把手你擼源碼。

    摘要:方法實現(xiàn)將所有屬性掛載在觀察對象,將每一項做一個數(shù)據(jù)劫持就是將中每一項用定義新屬性并返回這個對象。當(dāng)和發(fā)生變化時,自動會觸發(fā)視圖更新,獲取得到的也就是最新值。 MVVM及Vue實現(xiàn)原理 Github源碼地址:https://github.com/wyj2443573... mvvm 雙向數(shù)據(jù)綁定數(shù)據(jù)影響視圖,視圖影響數(shù)據(jù)angular 臟值檢測 vue數(shù)據(jù)劫持+發(fā)布訂閱模式vue 不...

    cooxer 評論0 收藏0
  • 把手你擼個vue2.0彈窗組件

    摘要:組件結(jié)構(gòu)同組件結(jié)構(gòu)通過方法獲取元素的大小及其相對于視口的位置,之后對提示信息進行定位??梢杂脕磉M行一些復(fù)雜帶校驗的彈窗信息展示,也可以只用于簡單信息的展示。可以通過屬性來顯示任意標(biāo)題,通過屬性來修改顯示區(qū)域的寬度。 手把手教你擼個vue2.0彈窗組件 在開始之前需要了解一下開發(fā)vue插件的前置知識,推薦先看一下vue官網(wǎng)的插件介紹 預(yù)覽地址 http://haogewudi.me/k...

    mrli2016 評論0 收藏0
  • 把手你擼個vue2.0彈窗組件

    摘要:組件結(jié)構(gòu)同組件結(jié)構(gòu)通過方法獲取元素的大小及其相對于視口的位置,之后對提示信息進行定位。可以用來進行一些復(fù)雜帶校驗的彈窗信息展示,也可以只用于簡單信息的展示??梢酝ㄟ^屬性來顯示任意標(biāo)題,通過屬性來修改顯示區(qū)域的寬度。 手把手教你擼個vue2.0彈窗組件 在開始之前需要了解一下開發(fā)vue插件的前置知識,推薦先看一下vue官網(wǎng)的插件介紹 預(yù)覽地址 http://haogewudi.me/k...

    taoszu 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<