从0-1实现一个前端脚手架

https://gitee.com/childe-jia/kfc-cli.git
gitee完整地址

介绍

为什么需要脚手架?

脚手架本质就是一个工具,作用是能够让使用者专注于写代码,它可以让我们只用一个命令就生成一个已经配置好的项目,而不用我们再花时间去配置和安装相关依赖,可以在很大程度上提升我们的开发效率。比如我们常用的create-vuecreate-react-app就是脚手架,很多大厂也都有自己的脚手架。

一个脚手架应该具备哪些功能?

我们以vue官方的脚手架create-vue为例来分析下一个脚手架应该具备哪些功能?

  1. 运行命令创建项目

    npm create vue@latest
    
  2. 用户根据自己需要选择一些配置项

在这里插入图片描述

  1. 根据选择的配置项会生成一个模版项目

在这里插入图片描述

通过分析create-vue,我们可以知道,一个脚手架如果想要创建一个项目,最少要有以下两点功能:

  1. 可以通过命令行和用户交互
  2. 根据交互的结果去生成对应的模版项目

脚手架实现

初始化项目
  1. 执行如下初始化命令
mkdir kfc-vme50
➜ cd kfc-vme50
➜ npm init -y
  1. 在根目录下创建bin/index.js文件作为入口文件,并添加如下代码
#!/usr/bin/env node
console.log('肯德基疯狂星期四v我50')
  1. 在package.json中添加bin字段
"bin": {"kfc-vme50": "/bin/index.js"
}
  1. 在根目录下执行npm link将项目链接到本地环境,就可以实现kfc-vme50命令全局调用
  2. 运行kfc-vme50并查看控制台输出

在这里插入图片描述

相关依赖

实现一个脚手架,通常会用到以下依赖包

  • commander:命令行处理工具
#!/usr/bin/env node// #! 是shebang的标识,告诉操作系统这是一个脚本文件。
// /usr/bin/env 是一个程序,用来查找环境变量中定义的程序路径。在这个例子中,它用来查找node的路径。
// node 是Node.js的可执行文件名,它是运行JavaScript代码的运行时环境。// 它用于处理命令行参数;
const { program } = require("commander");/***  .name 命令名称出现在帮助中,也用于定位独立的可执行子命令。*  .usage 通过这个选项可以修改帮助信息的首行提示*/program.name("kfc-creat").usage("<command> [option]");/*** 「选项」 定义选项* 使用.option()方法来定义选项,同时可以附加选项的简介。每个选项可以定义一个短选项名称(-后面接单个字符)*    一个长选项名称(--后面接一个或多个单词),使用逗号、空格或|分隔。*    有两种最常用的选项,一类是 boolean 型选项,选项无需配置参数,*    另一类选项则可以设置参数(使用尖括号声明在该选项后,如--expect <value>)。*    如果在命令行中不指定具体的选项及参数,则会被定义为undefined*/program.option("-d, --debug", "output extra debugging").option("-s, --small", "small pizza size").option("-p, --pizza-type <type>", "flavour of pizza");/*** 「命令」 通过.command()或.addCommand()可以配置命令,* .command()的第一个参数为命令名称。命令参数可以跟在名称后面,也可以用.argument()单独指定。* 参数可为必选的(尖括号表示)、可选的(方括号表示)或变长参数(点号表示,如果使用,只能是最后一个参数)。* ----------------------------------------------------------------* description 出现在命令的帮助中。* action 命令触发后的回调函数 [命令行的参数]*/program.command("clone <source> [destination]").description("clone a repository into a newly created directory").action((source, destination) => {console.log("clone command called");console.log(source, destination);});/** * 「parse」解析摩命令行参数* program.parse 它的作用是解析 process.argv 数组,将命令行参数转换为可操作的对象。* process.argv 是 Node.js 中的一个全局变量,它是一个数组,包含了命令行启动脚本时传递给 Node.js 进程的参数。数组的第一个元素 process.argv[0] 总是 node, 表示 Node.js 可执行文件的路径 (安装路径)接下来的元素是脚本文件的路径,即你正在运行的 JavaScript 文件的路径。之后的元素是传递给脚本的命令行参数
*/
program.parse(process.argv);/*** 「opts」获取命令行参数* 解析后的选项可以通过Command对象上的.opts()方法获取,同时会被传递给命令处理函数。*/
const options = program.opts();
console.log(options);console.log("肯德基疯狂星期四v我500");
  • chalk:命令行输出美化工具
#!/usr/bin/env node// 重要提示:Chalk 5 部分是 ESM。如果您想将 Chalk 与 TypeScript 或构建工具一起使用,您现在可能需要使用 Chalk 4/**
ESM 是 "ECMAScript Module" 的缩写,它指的是一种 JavaScript 模块的规范,允许开发者将代码分割成可重用的模块。
ESM 是现代 JavaScript 的一个特性,它支持静态模块的导入和导出,这意味着模块的依赖关系可以在编译时就确定下来,从而提高代码的加载和执行效率。
然而,ESM 也有它的限制,比如它不支持 CommonJS 模块中的 require() 函数,而是使用 import() 来动态加载模块。
此外,ESM 需要在支持 ESM 的环境中使用,比如现代浏览器或者使用 Babel 等工具转换的 Node.js 环境。*/const chalk = require("chalk");// 颜色
console.log(chalk.yellow("Welcome"));
// 加粗
console.log(chalk.red.bold("Welcome"));
// 背景色
console.log(chalk.yellow.bold.bgBlue("Welcome"));
  • inquirer:命令行交互工具
#!/usr/bin/env node
/*** !警告] Inquirer v9 及更高版本是本机 esm 模块,* 这意味着您不能再使用 commonjs 语法 require('inquirer') * 或者,如果您需要 commonjs 模块,则应该依赖旧版本,直到准备好升级环境*/
const inquirer = require("inquirer");/*** inquirer.prompt(questions, answers) -> promise* 启动提示界面(查询会话)*    questions (Array) 包含 Question 对象(使用反应式接口,还可以传递 Rx.Observable 实例)*    答案(对象)包含已回答问题的值。询问者将避免询问此处已提供的答案。默认值 {}。**「Question对象」 问题对象是包含问题相关值的哈希type  (字符串)提示的类型。【input, number, confirm, list, rawlist, expand, checkbox, password, editor】name:(字符串)将答案存储在答案哈希中时使用的名称。如果名称包含句点,它将在答案哈希中定义路径。message:(字符串|函数)要打印的问题。如果定义为函数,第一个参数将是当前询问者会话的答案。默认为 name 的值(后跟冒号)default:(字符串|数字|布尔值|数组|函数)未输入任何内容时使用的默认值,或返回默认值的函数。如果定义为函数,第一个参数将是当前询问者会话的答案**/inquirer.prompt([// 将你的问题放在这{type: "input",name: "food",message: "你吃啥",default: "披萨",},{type: "confirm",name: "hot",message: "吃不吃辣",default: false,},]).then((answers) => {//使用用户反馈。。。无论什么结果console.log(answers);}).catch((error) => {if (error.isTtyError) {// 无法在当前环境中呈现提示} else {//其他问题}});
  • ora:终端loading美化工具
#!/usr/bin/env node
const ora = require("ora");// 启动旋转器。返回实例。如果提供了文本,则设置当前文本。
const spinner = ora("Loading unicorns").start();setTimeout(() => {spinner.color = "yellow";spinner.text = "Loading rainbows";
}, 1000);
// 停止旋转器,将其更改为绿色 ✔ 并保留当前文本或文本(如果提供)。返回实例。请参阅下面的 GIF。// setTimeout(() => {
//   spinner.succeed("succeed");
// }, 2000);// 停止旋转器,将其更改为红色 ✖ 并保留当前文本或文本(如果提供)。返回实例。请参阅下面的 GIF。setTimeout(() => {spinner.fail("fail");
}, 2000);
  • git-clone:下·载项目模版工具
  • figlet:终端生成艺术字
#!/usr/bin/env node
var figlet = require("figlet");// 将 Figlet 对象作为函数调用是调用文本函数的简写。此方法允许您从文本创建 ASCII 艺术。
// 输入文本 - 要转换为 ASCII 艺术的文本字符串。
// 选项 - 指示字体名称的字符串或选项对象(如下所述)
// 回调 - 使用生成的 ASCII Art 执行的函数。figlet("Hello World!!", function (err, data) {if (err) {console.log("Something went wrong...");console.dir(err);return;}console.log(data);
});// 该方法是上述方法的同步版本
// 输入文本 - 要转换为 ASCII 艺术的文本字符串。
// 字体选项 - 指示字体名称的字符串或选项对象(如下所述)。
console.log(figlet.textSync("Boo!", {font: "Ghost", //类型:字符串 默认值:'标准' 指示要使用的 Figlet 字体的字符串值。horizontalLayout: "default", //指示要使用的水平布局的字符串值verticalLayout: "default", //指示要使用的垂直布局的字符串值width: 80, //宽度whitespaceBreak: true, //此选项与“宽度”结合使用。如果此选项设置为 true,则库在限制宽度时将尝试在空白处分解文本。})
);
  • fs-extra:用来操作本地目录
talk is cheap, show me the code
#!/usr/bin/env node// 操作终端命令行
const { program } = require("commander");
// 艺术字
const figlet = require("figlet");
// 操作文件
const fs = require("fs-extra");
// 获取路径
const path = require("path");
// 命令行交互
const inquirer = require("inquirer");
// 彩色输出
const chalk = require("chalk");
//控制台loadding
const ora = require("ora");// clone 项目
const gitClone = require("git-clone");// 项目仓库
const projectList = {vue: "https://gitee.com/y_project/RuoYi-Vue.git",react: "https://gitee.com/whiteshader/ruoyi-react.git","react&ts": "https://gitee.com/whiteshader/ruoyi-react.git","vue&ts": "https://gitee.com/lyforvue/ruoyi_vue3_ts.git",
};// 修改帮助信息的首行展示
program.usage("<command> [options]");// 版本号
program.version(`v${require("../package.json").version}`);// 艺术字展示 监听 help添加提示信息
program.on("--help", function () {console.log(figlet.textSync("kfc vme50", {font: "Ghost",horizontalLayout: "default",verticalLayout: "default",width: 100,whitespaceBreak: true,}));
});// 创建项目的命令
program.command("create <app-name>") // 创建项目的命令 name必填.description("创建新项目") //描述//执行命令后的回调【命令后的值,】.action(async function (name, option) {//创建以一个名为name的文件夹,把代码放到文件夹下const cwd = process.cwd(); //获取命令执行的文件目录// 创建项目的位置const targetPath = path.join(cwd, name);// 如果文件夹存在if (fs.existsSync(targetPath)) {const res = await inquirer.prompt([{name: "action",type: "list",message: "是否覆盖已有文件夹?",choices: [{name: "YES",value: true,},{name: "NO",value: false,},],},]);//不覆盖 取一个新的名字if (!res.action) return;fs.remove(targetPath);console.log(chalk.red("已删除之前的文件夹"));}//新建项目const res = await inquirer.prompt([{name: "type",type: "list",message: "请选择使用的框架",choices: [{name: "Vue",value: "vue",},{name: "React",value: "react",},],},{name: "ts",type: "list",message: "是否使用ts项目",choices: [{name: "YES",value: true,},{name: "NO",value: false,},],},]);// 是否为tsconst rep = res.type + (res.ts ? "&ts" : "");// 拉取项目模板const spinner = ora("正在加载项目模板...").start();gitClone(projectList[rep], //拉去路径targetPath, //保存路径//分支{checkout: "master",},//回调函数(err) => {if (!err) {fs.remove(path.resolve(targetPath, ".git"));spinner.succeed("项目模板加载完成!");console.log("now run:");console.log(chalk.green(`\n  cd ${name}`));console.log(chalk.green("  npm install"));console.log(chalk.green(`  npm run ${res.type === "react" ? "start" : "dev"}\n`));} else {spinner.fail(chalk.red("项目模板加载失败,请重新获取!", err));}});});//解析控制台参数
program.parse(process.argv);
发布
  1. 注册npm账号
  2. 在本地登录并发布
# 登录刚注册的账号npm login
Username: 用户名
Password: 密码
Email: 注册邮箱
Enter one-time password: 一次性密码  邮箱会收到邮件# 在我们脚手架的根目录下执行发布命令npm publish

注意:

  1. 登录和发包前一定要先查看npm的源,需要修改为https://registry.npmjs.org/

  2. 在发布时包名不能重复,所以可以先在线上搜索下看看有没有存在的包,如果出现403错误可能是包名和线上的包重复了,修改package.json中的name即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/371327.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【排序算法】—— 快速排序

快速排序的原理是交换排序&#xff0c;其中qsort函数用的排序原理就是快速排序&#xff0c;它是一种效率较高的不稳定函数&#xff0c;时间复杂度为O(N*longN)&#xff0c;接下来就来学习一下快速排序。 一、快速排序思路 1.整体思路 以升序排序为例&#xff1a; (1)、首先随…

CC工具箱使用指南:【相交占比分析】

一、简介 需求场景如下&#xff0c;有【待分析地块】和【面积占比参考】2个图层。2个图层之间存在空间上的重叠。工具的目的是为了分析出【待分析地块】的每1个图斑中&#xff0c;和【面积占比参考】相交的面积&#xff0c;以及和总面积的占比。 举一个应用场景为例&#xff0…

Idea新增Module报错:sdk ‘1.8‘ type ‘JavaSDK‘ is not registered in ProjectJdkTable

文章目录 一&#xff0c;创建Module报错二&#xff0c;原因分析三&#xff0c;解决方案1&#xff0c;点击上图的加号&#xff0c;把JDK8添加进来即可2&#xff0c;点击左侧[Project]&#xff0c;直接设置SDK为JDK8 四&#xff0c;配置检查与验证 一&#xff0c;创建Module报错 …

【Linux】:程序地址空间

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

PingCAP 成为全球数据库管理系统市场增速最快的厂商

近日&#xff0c;Gartner 发布的《Market Share Analysis: Database Management Systems, Worldwide, 2023》&#xff08;2024 年 6 月&#xff09;报告显示&#xff1a;“2023 年全球数据库管理系统&#xff08;DBMS&#xff09;市场的增长率为 13.4%&#xff0c;略低于去年的…

LLM4Decompile——专门用于反编译的大规模语言模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2403.05286 反编译是一种将已编译的机器语言或字节码转换回原始高级编程语言的技术。该技术用于分析软件的内部工作原理&#xff0c;尤其是在没有源代码的情况下&#xff1b;Ghidra 和 IDA Pro 等专用工具已经开发出来&#…

学习笔记——动态路由——OSPF聚合(汇总)

十一、OSPF聚合(汇总) 1、路由聚合(汇总) 路由汇总是一种重要的思想&#xff0c;在大型的项目中是必须考虑的一个重点事项。随着网络的规模越来越大&#xff0c;网络中的设备所需维护的路由表项也就会越来越多&#xff0c;路由表的规模也就会逐渐变大&#xff0c;而路由表是需…

【TB作品】51单片机 Proteus仿真 超声波LCD1602ADC0832 身高体重测量仪

00024 超声波LCD1602ADC0832 实验报告&#xff1a;基于51单片机的身高体重测量仪设计 背景介绍 本实验设计并实现了一个基于51单片机的身高体重测量仪。该系统利用超声波传感器测量高度&#xff0c;通过ADC0832模数转换芯片获取重量数据&#xff0c;并使用LCD1602显示屏显示…

系统测试-测试方法学习

目录 &#xff08;1&#xff09;等价类 &#xff08;2&#xff09;边界值 &#xff08;3&#xff09;正交&#xff1a;&#xff08;只用于确定排列组合&#xff0c;不确定具体内容&#xff09; (4)判定表法 &#xff08;5&#xff09;流程分析法 &#xff08;6&#xff0…

Day05-04-持续集成总结

Day05-04-持续集成总结 1. 持续集成2. 代码上线目标项目 1. 持续集成 git 基本使用, 拉取代码,上传代码,分支操作,tag标签 gitlab 用户 用户组 项目 , 备份,https,优化. jenkins 工具平台,运维核心, 自由风格工程,maven风格项目,流水线项目, 流水线(pipeline) mavenpom.xmlta…

uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据@getIndex点击事件获取点击的地区下标和地区名

项目场景&#xff1a; uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据getIndex点击事件获取点击的地区下标和地区名 例如&#xff1a; 问题描述 官方给的文档有限&#xff0c;需要自己下载地图json数据然后自己渲染和编写鼠标悬浮显示内容以及获取点击地址…

在DevEco运行typeScript代码,全网详细解决执行Set-ExecutionPolicy RemoteSigned报出的错

目录 基本思路 网络推荐 本人实践 如下操作,报错: 基本思路 //在DevEco运行typeScript代码 /** * 1.保证node -v出现版本,若没有,配置环境变量(此电脑-属性-高级系统变量配置-path-粘贴路径);DevEco在local.properties中可看到当前nodejs的路径 * 2.npm install …

【Transformer】transformer模型结构学习笔记

文章目录 1. transformer架构2. transformer子层解析3. transformer注意力机制4. transformer部分释疑 图1 transformer模型架构 图2 transformer主要模块简介 图3 encoder-decoder示意图N6 图4 encoder-decoder子层示意图 1. transformer架构 encoder-decoder框架是一种处理NL…

strcpy,srtcmp,strlen函数漏洞利用

strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中&#xff0c;遇到空字符 **b’x\00’**时停止&#xff0c;&#xff1a; 所以可以利用 strcpy不检查缓冲区 的漏洞&#xff08;构造的字符串要以\0结尾&#xff09;&#xff0c;…

Android平台崩溃和 ANR 问题进行符号化解析、解析崩溃日志的内存地址

使用Android Logcat Stacktrace Utility | Android Logcat | 1.2.3 1.设置so库路径 2.打开Stacktrace Utility工具 3.在Original粘贴报错内存地址 4.点击Resolve Stacktraces,就会解析出内存地址 如果是红色,解析失败了,缺少原生so库,可以在第一步添加so库文件再次尝试…

freemarker生成pdf,同时pdf插入页脚,以及数据量大时批量处理

最近公司有个需求&#xff0c;就是想根据一个模板生成一个pdf文档&#xff0c;当即我就想到了freemarker这个远古老东西&#xff0c;毕竟freemarker在模板渲染方面还是非常有优势的。 准备依赖&#xff1a; <dependency><groupId>org.springframework.boot</gr…

【植物大战僵尸杂交版】获取+存档插件

文章目录 一、还记得《植物大战僵尸》吗&#xff1f;二、在哪下载&#xff0c;怎么安装&#xff1f;三、杂交版如何进行存档功能概述 一、还记得《植物大战僵尸》吗&#xff1f; 最近&#xff0c;一款曾经在15年前风靡一时的经典游戏《植物大战僵尸》似乎迎来了它的"文艺复…

EN-SLAM:Implicit Event-RGBD Neural SLAM解读

论文路径&#xff1a;https://arxiv.org/pdf/2311.11013.pdf 目录 1 论文背景 2 论文概述 2.1 神经辐射场&#xff08;NeRF&#xff09; 2.2 事件相机&#xff08;Event Camera&#xff09; 2.3 事件时间聚合优化策略&#xff08;ETA&#xff09; 2.4 可微分的CRF渲染技术…

CosyVoice多语言、音色和情感控制模型,one-shot零样本语音克隆模型本地部署(Win/Mac),通义实验室开源

近日&#xff0c;阿里通义实验室开源了CosyVoice语音模型&#xff0c;它支持自然语音生成&#xff0c;支持多语言、音色和情感控制&#xff0c;在多语言语音生成、零样本语音生成、跨语言声音合成和指令执行能力方面表现卓越。 CosyVoice采用了总共超15万小时的数据训练&#…

学习笔记——动态路由——OSPF(邻接/邻居)

十、OSPF的邻接/邻居 1、OSPF路由器之间的关系 (1)基本介绍 在OSPF网络中&#xff0c;为了交换链路状态信息和路由信息&#xff0c;邻居设备之间首先要建立邻接关系&#xff0c;邻居(Neighbors)关系和邻接(Adjacencies)关系是两个不同的概念。 OSPF路由器的两种关系&#x…