摘要:管理文件當(dāng)前用戶目錄下文件的增刪改查是配置文件是默認(rèn)的配置發(fā)布將本腳手架發(fā)布至上。
腳手架
vue-cli, create-react-app、react-native-cli 等都是非常優(yōu)秀的腳手架,通過腳手架,我們可以快速初始化一個(gè)項(xiàng)目,無(wú)需自己從零開始一步步配置,有效提升開發(fā)體驗(yàn)。盡管這些腳手架非常優(yōu)秀,但是未必是符合我們的實(shí)際應(yīng)用的,我們可以定制一個(gè)屬于自己的腳手架(或公司通用腳手架),來(lái)提升自己的開發(fā)效率。
更多優(yōu)質(zhì)文章可戳: https://github.com/YvetteLau/...
腳手架的作用
減少重復(fù)性的工作,不需要復(fù)制其他項(xiàng)目再刪除無(wú)關(guān)代碼,或者從零創(chuàng)建一個(gè)項(xiàng)目和文件。
可以根據(jù)交互動(dòng)態(tài)生成項(xiàng)目結(jié)構(gòu)和配置文件。
多人協(xié)作更為方便,不需要把文件傳來(lái)傳去。
實(shí)現(xiàn)的功能在開始之前,我們需要明確自己的腳手架需要哪些功能。vue init template-name project-name 、create-react-app project-name。我們這次編寫的腳手架(eos-cli)具備以下能力(腳手架的名字愛叫啥叫啥,我選用了Eos黎明女神):
eos init template-name project-name 根據(jù)遠(yuǎn)程模板,初始化一個(gè)項(xiàng)目(遠(yuǎn)程模板可配置)
eos config set
eos config get [
eos --version 查看當(dāng)前版本號(hào)
eos -h
大家可以自行擴(kuò)展其它的 commander,本篇文章旨在教大家如何實(shí)現(xiàn)一個(gè)腳手架。
效果展示
初始化一個(gè)項(xiàng)目
修改.eosrc文件,從 vuejs-template 下載模板
需要使用的第三方庫(kù)babel-cli/babel-env: 語(yǔ)法轉(zhuǎn)換
commander: 命令行工具
download-git-repo: 用來(lái)下載遠(yuǎn)程模板
ini: 格式轉(zhuǎn)換
inquirer: 交互式命令行工具
ora: 顯示loading動(dòng)畫
chalk: 修改控制臺(tái)輸出內(nèi)容樣式
log-symbols: 顯示出 √ 或 × 等的圖標(biāo)
關(guān)于這些第三方庫(kù)的說明,可以直接npm上查看相應(yīng)的說明,此處不一一展開。
初始化項(xiàng)目創(chuàng)建一個(gè)空項(xiàng)目(eos-cli),使用 npm init 進(jìn)行初始化。
安裝依賴npm install babel-cli babel-env chalk commander download-git-repo ini inquirer log-symbols ora目錄結(jié)構(gòu)
├── bin │ └── www //可執(zhí)行文件 ├── dist ├── ... //生成文件 └── src ├── config.js //管理eos配置文件 ├── index.js //主流程入口文件 ├── init.js //init command ├── main.js //入口文件 └── utils ├── constants.js //定義常量 ├── get.js //獲取模板 └── rc.js //配置文件 ├── .babelrc //babel配置文件 ├── package.json ├── README.mdbabel 配置
開發(fā)使用了ES6語(yǔ)法,使用 babel 進(jìn)行轉(zhuǎn)義,
.bablerc
{ "presets": [ [ "env", { "targets": { "node": "current" } } ] ] }eos 命令
node.js 內(nèi)置了對(duì)命令行操作的支持,package.json 中的 bin 字段可以定義命令名和關(guān)聯(lián)的執(zhí)行文件。在 package.json 中添加 bin 字段
package.json
{ "name": "eos-cli", "version": "1.0.0", "description": "腳手架", "main": "index.js", "bin": { "eos": "./bin/www" }, "scripts": { "compile": "babel src -d dist", "watch": "npm run compile -- --watch" } }
www 文件
行首加入一行 #!/usr/bin/env node 指定當(dāng)前腳本由node.js進(jìn)行解析
#! /usr/bin/env node require("../dist/main.js");鏈接到全局環(huán)境
開發(fā)過程中為了方便調(diào)試,在當(dāng)前的 eos-cli 目錄下執(zhí)行 npm link,將 eos 命令鏈接到全局環(huán)境。
啟動(dòng)項(xiàng)目npm run watch處理命令行
利用 commander 來(lái)處理命令行。
main
import program from "commander"; import { VERSION } from "./utils/constants"; import apply from "./index"; import chalk from "chalk"; /** * eos commands * - config * - init */ let actionMap = { init: { description: "generate a new project from a template", usages: [ "eos init templateName projectName" ] }, config: { alias: "cfg", description: "config .eosrc", usages: [ "eos config set下載模板", "eos config get ", "eos config remove " ] }, //other commands } // 添加 init / config 命令 Object.keys(actionMap).forEach((action) => { program.command(action) .description(actionMap[action].description) .alias(actionMap[action].alias) //別名 .action(() => { switch (action) { case "config": //配置 apply(action, ...process.argv.slice(3)); break; case "init": apply(action, ...process.argv.slice(3)); break; default: break; } }); }); function help() { console.log(" Usage:"); Object.keys(actionMap).forEach((action) => { actionMap[action].usages.forEach(usage => { console.log(" - " + usage); }); }); console.log(" "); } program.usage(" [options]"); // eos -h program.on("-h", help); program.on("--help", help); // eos -V VERSION 為 package.json 中的版本號(hào) program.version(VERSION, "-V --version").parse(process.argv); // eos 不帶參數(shù)時(shí) if (!process.argv.slice(2).length) { program.outputHelp(make_green); } function make_green(txt) { return chalk.green(txt); }
download-git-repo 支持從 Github、Gitlab 下載遠(yuǎn)程倉(cāng)庫(kù)到本地。
get.js
import { getAll } from "./rc"; import downloadGit from "download-git-repo"; export const downloadLocal = async (templateName, projectName) => { let config = await getAll(); let api = `${config.registry}/${templateName}`; return new Promise((resolve, reject) => { //projectName 為下載到的本地目錄 downloadGit(api, projectName, (err) => { if (err) { reject(err); } resolve(); }); }); }init 命令 命令行交互
在用戶執(zhí)行 init 命令后,向用戶提出問題,接收用戶的輸入并作出相應(yīng)的處理。命令行交互利用 inquirer 來(lái)實(shí)現(xiàn):
inquirer.prompt([ { name: "description", message: "Please enter the project description: " }, { name: "author", message: "Please enter the author name: " } ]).then((answer) => { //... });視覺美化
在用戶輸入之后,開始下載模板,這時(shí)候使用 ora 來(lái)提示用戶正在下載模板,下載結(jié)束之后,也給出提示。
import ora from "ora"; let loading = ora("downloading template ..."); loading.start(); //download loading.succeed(); //或 loading.fail();
index.js
import { downloadLocal } from "./utils/get"; import ora from "ora"; import inquirer from "inquirer"; import fs from "fs"; import chalk from "chalk"; import symbol from "log-symbols"; let init = async (templateName, projectName) => { //項(xiàng)目不存在 if (!fs.existsSync(projectName)) { //命令行交互 inquirer.prompt([ { name: "description", message: "Please enter the project description: " }, { name: "author", message: "Please enter the author name: " } ]).then(async (answer) => { //下載模板 選擇模板 //通過配置文件,獲取模板信息 let loading = ora("downloading template ..."); loading.start(); downloadLocal(templateName, projectName).then(() => { loading.succeed(); const fileName = `${projectName}/package.json`; if(fs.existsSync(fileName)){ const data = fs.readFileSync(fileName).toString(); let json = JSON.parse(data); json.name = projectName; json.author = answer.author; json.description = answer.description; //修改項(xiàng)目文件夾中 package.json 文件 fs.writeFileSync(fileName, JSON.stringify(json, null, " "), "utf-8"); console.log(symbol.success, chalk.green("Project initialization finished!")); } }, () => { loading.fail(); }); }); }else { //項(xiàng)目已經(jīng)存在 console.log(symbol.error, chalk.red("The project already exists")); } } module.exports = init;config 配置
eos config set registry vuejs-templates
config 配置,支持我們使用其它倉(cāng)庫(kù)的模板,例如,我們可以使用 vuejs-templates 中的倉(cāng)庫(kù)作為模板。這樣有一個(gè)好處:更新模板無(wú)需重新發(fā)布腳手架,使用者無(wú)需重新安裝,并且可以自由選擇下載目標(biāo)。
config.js
// 管理 .eosrc 文件 (當(dāng)前用戶目錄下) import { get, set, getAll, remove } from "./utils/rc"; let config = async (action, key, value) => { switch (action) { case "get": if (key) { let result = await get(key); console.log(result); } else { let obj = await getAll(); Object.keys(obj).forEach(key => { console.log(`${key}=${obj[key]}`); }) } break; case "set": set(key, value); break; case "remove": remove(key); break; default: break; } } module.exports = config;
rc.js
.eosrc 文件的增刪改查
import { RC, DEFAULTS } from "./constants"; import { decode, encode } from "ini"; import { promisify } from "util"; import chalk from "chalk"; import fs from "fs"; const exits = promisify(fs.exists); const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); //RC 是配置文件 //DEFAULTS 是默認(rèn)的配置 export const get = async (key) => { const exit = await exits(RC); let opts; if (exit) { opts = await readFile(RC, "utf8"); opts = decode(opts); return opts[key]; } return ""; } export const getAll = async () => { const exit = await exits(RC); let opts; if (exit) { opts = await readFile(RC, "utf8"); opts = decode(opts); return opts; } return {}; } export const set = async (key, value) => { const exit = await exits(RC); let opts; if (exit) { opts = await readFile(RC, "utf8"); opts = decode(opts); if(!key) { console.log(chalk.red(chalk.bold("Error:")), chalk.red("key is required")); return; } if(!value) { console.log(chalk.red(chalk.bold("Error:")), chalk.red("value is required")); return; } Object.assign(opts, { [key]: value }); } else { opts = Object.assign(DEFAULTS, { [key]: value }); } await writeFile(RC, encode(opts), "utf8"); } export const remove = async (key) => { const exit = await exits(RC); let opts; if (exit) { opts = await readFile(RC, "utf8"); opts = decode(opts); delete opts[key]; await writeFile(RC, encode(opts), "utf8"); } }發(fā)布
npm publish 將本腳手架發(fā)布至npm上。其它用戶可以通過 npm install eos-cli -g 全局安裝。
即可使用 eos 命令。
本項(xiàng)目完整代碼請(qǐng)戳: https://github.com/YvetteLau/...
編寫本文,雖然花費(fèi)了一定時(shí)間,但是在這個(gè)過程中,我也學(xué)習(xí)到了很多知識(shí),謝謝各位小伙伴愿意花費(fèi)寶貴的時(shí)間閱讀本文,如果本文給了您一點(diǎn)幫助或者是啟發(fā),請(qǐng)不要吝嗇你的贊和Star,您的肯定是我前進(jìn)的最大動(dòng)力。
https://github.com/YvetteLau/...
[1] npm依賴文檔(https://www.npmjs.com/package...
關(guān)注公眾號(hào),加入技術(shù)交流群。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/106017.html
摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫函數(shù)庫(kù)中高級(jí)前端面試手寫代碼無(wú)敵秘籍如何用不到行代碼寫一款屬于自己的類庫(kù)原理講解實(shí)現(xiàn)一個(gè)對(duì)象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...
摘要:前言一次在使用的時(shí)候,發(fā)現(xiàn)使用命令行的時(shí)候有些關(guān)鍵字會(huì)自動(dòng)提示。介紹隨著,等框架的流行,命令行工具越來(lái)越流行,但是很多時(shí)候命令太多,根本無(wú)法記住所有參數(shù),或者參數(shù)太長(zhǎng)輸入太不方便。下文,我們一起來(lái)優(yōu)化這個(gè)工具。備注不支持自動(dòng)補(bǔ)全 前言 一次在使用symfony的時(shí)候,發(fā)現(xiàn)使用命令行的時(shí)候有些關(guān)鍵字會(huì)自動(dòng)提示。 showImg(https://segmentfault.com/img/b...
摘要:最近項(xiàng)目中遇到一個(gè)需求,需要把一張圖片加上平鋪的水印類似這樣的效果首先想到的是用完成這種功能,因?yàn)槲抑耙矝]有接觸過,所以做這個(gè)功能的時(shí)候,就是一步一步的摸索中學(xué)習(xí),過程還是挺的,接下來(lái)跟我一步步來(lái)實(shí)現(xiàn)這個(gè)功能以及發(fā)現(xiàn)一些的坑吧。 最近項(xiàng)目中遇到一個(gè)需求,需要把一張圖片加上平鋪的水印 類似這樣的效果showImg(https://segmentfault.com/img/remote/...
閱讀 2501·2021-11-25 09:43
閱讀 2611·2021-11-16 11:50
閱讀 3294·2021-10-09 09:44
閱讀 3203·2021-09-26 09:55
閱讀 2844·2019-08-30 13:50
閱讀 1032·2019-08-29 13:24
閱讀 2082·2019-08-26 11:44
閱讀 2805·2019-08-26 11:37