使用nodejs搭建脚手架工具并发布到npm中
使用nodejs搭建脚手架工具并发布到npm中
- 一、安装环境依赖及脚手架搭建过程
- 二、搭建Monorepo 风格的脚手架工程
- 三、脚手架的必备模块
- 命令参数模块
- 获取命令参数
- 设置子命令
- 用户交互模块
- 文件拷贝模块
- 脚手架中的路径处理
- 目录守卫
- 文件拷贝模块
- 动态文件生成模块
- mustache简介
- 自动安装依赖模块
- 发布与安装
- 四、总结
一、安装环境依赖及脚手架搭建过程
-
安装 vue-cli 脚手架:
npm install -g vue-cli
-
创建项目:
vue create myapp
-
输入cmd:
-
在cmd中输入:
npm init
输入后会生成一个package.json文件
-
在package.json中添加:
./bin/index.js
这样就声明了一个mortal命令,而
./bin/index.js是运行mortal命令后,所运行js的文件相对路径
- 创建bin文件夹、在bin文件夹下创建index.js文件:
注意:必须加
#!/usr/bin/env node
否则会报错
- **在cmd窗口执行mortal命令:**会报错
报错原因:并不是声明命令的方法错误,而是需要将该脚手架发布到npm上,在
package.json
中可以看到name
值为mortal-cli
,所以可以通过运行npm install -g mortal-cli
将脚手架安装到本地,再运行mortal
才会成功(可理解为安装vue-cli脚手架一样的操作过程)
- **实现在本地调试脚手架的能力:**先执行
npm link
再执行mortal
则运行成功
**总结:**到此一个声明命令结束
注意:npm link
的弊端:若本地存在多个版本的脚手架仓库,在A仓库中修改代码,然后运行mortal
命令,会发现所修改的代码并不生效。原因是:在仓库B的脚手架中已经运行了npm link
,从而导致再次运行mortal
时实际上是执行仓库B中的代码。所以要先在仓库B的脚手架工程中先进行释放,运行npm unlink
,然后再在仓库A中执行npm link
、mortal
,此时仓库A中已经修改的代码会生效。所以可以通过pnpm
来搭建monorepo
风格的脚手架工程。
Monorepo风格:即代码管理方式
,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。含有多个子工程,每一个子工程都能独立编译打包,并将产物变成npm
包,所以又叫monorepo为多包工程。
- 修改package.json以便于更好的发布到npm上:
注意:在
dependencies
中声明mortal-cli
依赖包版本需使用workspace:*
来定义,而不是使用具体的版本号来定义。
在 pnpm 中使用 workspace: 协议定义某个依赖包版本号时,pnpm 将只解析存在工作空间内的依赖包,不会去下载解析 npm 上的依赖包。
把 mortal-cli 依赖包引入,执行 pnpm i 安装依赖,其效果就跟执行 npm install -g mortal-cli一样,只不过不是全局安装而已,只在调试子工程内安装 mortal-cli 脚手架。然后调试子工程就直接引用脚手架子工程本地编译打包后的产物,而不是发布到 npm 上的产物,彻底做到本地调试。
另外脚手架子工程和调试子工程是在同一个工程中,这样就做一对一的调试,从而解决了使用 npm link 来实现本地调试的弊端。
同时在 scripts 定义了脚本命令,在调试工程中执行 pnpm mortal 既是执行了 mortal 命令,不用脚手架工程中执行 npm link 就可以运行 mortal 命令。
- **安装pnpm:**先安装
pnpm
安装pnpm地址详细请见该文章 安装命令:iwr https://get.pnpm.io/install.ps1 -useb | iex
二、搭建Monorepo 风格的脚手架工程
-
新建mortal文件夹:
-
使用pnpm搭建monorepo风格脚手架:
pnpm init
-
创建pnpm-workspace.yaml工作空间配置文件: 并添加以下代码
声明了 packages 和 examples 文件夹中子工程是同属一个工作空间的,工作空间中的子工程编译打包的产物都可以被其它子工程引用。
-
使用pnpm初始化:
pnpm init
在packages文件夹下的mortal文件夹下产生一个package.json文件
-
在package.json文件中新建bin字段声明mortal
-
在packages->mortal下新建bin文件夹,在bin文件夹下新建index.js文件
-
在与packages同级目录下新建examples文件夹,在该文件夹下创建app文件夹 输入cmd,再输入
pnpm init
进行初始化生成package.json
文件
-
给example文件夹中的package.json添加代码:
-
在最外层的根目录执行安装命令:
pnpm install
-
当前的目录结构:
Project Tree插件可生成tree目录,或者在该文件夹中cmd到根目录->输入TREE>文件名.txt也可生成tree目录
使用Project Tree插件:Crtl + Shift + P -> 输入Project Tree->生成README.md文件
这里是目录工程化
// 当前项目的目录结构如下所示:
mortal
├─ examples
│ └─ app
│ └─ package.json
├─ package.json
├─ packages
│ └─ mortal-cli
│ ├─ bin
│ │ └─ index.js
│ └─ package.json
├─ pnpm-lock.yaml
└─ pnpm-workspace.yaml
三、脚手架的必备模块
命令参数模块
获取命令参数
- Node.js 中的 process 模块提供了当前 Node.js 进程相关的全局环境信息,比如命令参数、环境变量、命令运行路径process模块 安装命令:
npm install
const process = require('process');
// 获取命令参数
console.log(process.argv);
脚手架提供的 mortal 命令后面还可以设置参数,标准的脚手架命令参数需要支持两种格式:
mortal --name=orderPage
mortal --name orderPage
通过 process.argv 来获取,要额外处理两种不同的命令参数格式不方便,这里使用 yargs 开发脚手架/CLI,这里推荐 yargs 开源库来解析命令参数
- 这里使用 yargs 开发脚手架/CLI:
pnpm add yargs --F mortal-cli
这里要注意,mortal-cli 是取 mortal-cli 子工程中 package.json 中 name 字段的值,而不是 mortal-cli 子工程文件夹的名称。yargs 的使用非常简单,其提供的 argv 属性是对两个格式的命令参数的处理结果。
- 在 bin/index.js 添加如下代码:
#!/usr/bin/env node
const yargs = require('yargs')console.log('name',yargs.argv.name);
注意,以上代码是在 Node.js 环境中运行,Node.js 的模块是遵循 CommonJS 规范的,如果要依赖一个模块,要使用 Node.js 内置 require 系统函数引用模块使用。
- 在example/app文件夹下执行:
pnpm mortal -- --name=orderPage
注意,在
pnpm mortal
后面需要加上两个连字符(--)
,这是为了告诉pnpm
后面的参数是传递给命令mortal
本身的,而不是传递给pnpm
的。结果是name orderPage
设置子命令
假如脚手架要对外提供多个功能,不能将所有的功能都集中在 mortal 命令中实现。可通过 yargs 提供的 command 方法来设置一些子命令,让每个子命令对应各自功能,各司其职。
yargs.command 的用法是 yargs.command(cmd, desc, builder, handler)
- cmd:字符串,子命令名称,也可以传递数组,如 [‘create’, ‘c’],表示子命令叫 create,其别名是 c;
- desc:字符串,子命令描述信息;
- builder:一个返回数组的函数,子命令参数信息配置,比如可以设置参数:
- alias:别名;
- demand:是否必填;
- default:默认值;
- describe:描述信息;
- type:参数类型,string | boolean | number。
- handler: 函数,可以在这个函数中专门处理该子命令参数。
设置一个用来生成一个模板的子命令,把这个子命令命名为create: 修改在 bin/index.js
文件中的代码:
#!/usr/bin/env node
//const yargs = require('yargs');
yargs.command(['create', 'c'],'新建一个模板',function (yargs) {return yargs.option('name', {alias: 'n',demand: true,describe: '模板名称',type: 'string'})},function (argv) {console.log('argv', argv);}
).argv;
在app下执行:pnpm mortal create -- --name=orderPage
和pnpm mortal c -- --name=orderPage
配置了子命令 create 的参数 name 的一些参数信息,如何将这些信息展示给用户:输入子命令的参数有错误,就会在命令行窗口中显示这些参数信息。
在app文件夹目录下:pnpm mortal c -- --abc
最简单地实现了脚手架和用户之间的交互能力,但是如果自定义参数过多,那么命令行参数的交互方法对于用户来说是非常不友好的。所以需要通过用户交互模块桥接与用户进行交互。
用户交互模块
- 下载inquirer: 实现询问式的交互
pnpm add inquirer@8.2.5 --F mortal-cli
为了使用 require 引入 inquirer ,要使用 8.2.5 版本的 inquirer。
inquirer 能力:询问用户问题、获取并解析用户的输入、检测用户的答案是否合法
主要通过 inquirer.prompt() 来实现。prompt 函数接收一个数组,数组的每一项都是一个询问项,询问项有很多配置参数,下面是常用的配置项:
- type:提问的类型,常用的有:
- 输入框:input;
- 确认:confirm;
- 单选组:list;
- 多选组:checkbox;
- name:存储当前问题答案的变量;
- message:问题的描述;
- default:默认值;
- choices:列表选项,在某些type下可用;
- validate:对用户的答案进行校验;
- filter:对用户的答案进行过滤处理,返回处理后的值。
总结:创建一个模板文件,大概会询问用户:模板文件名称、模板类型、使用什么框架开发、使用框架对应的哪个组件库开发等等
- 在 bin 文件夹中新建 inquirer.js 文件夹:
// 添加的内容
const inquirer = require("inquirer");function inquirerPrompt(argv) {const { name } = argv;return new Promise((resolve, reject) => {inquirer.prompt([{type: "input",name: "name",message: "模板名称",default: name,validate: function (val) {if (!/^[a-zA-Z]+$/.test(val)) {return "模板名称只能含有英文";}if (!/^[A-Z]/.test(val)) {return "模板名称首字母必须大写";}return true;},},{type: "list",name: "type",message: "模板类型",choices: ["表单", "动态表单", "嵌套表单"],filter: function (value) {return {表单: "form",动态表单: "dynamicForm",嵌套表单: "nestedForm",}[value];},},{type: "list",message: "使用什么框架开发",choices: ["react", "vue"],name: "frame",},]).then((answers) => {const { frame } = answers;if (frame === "react") {inquirer.prompt([{type: "list",message: "使用什么UI组件库开发",choices: ["Ant Design"],name: "library",},
相关文章:

使用nodejs搭建脚手架工具并发布到npm中
使用nodejs搭建脚手架工具并发布到npm中 一、安装环境依赖及脚手架搭建过程二、搭建Monorepo 风格的脚手架工程三、脚手架的必备模块命令参数模块获取命令参数设置子命令用户交互模块文件拷贝模块脚手架中的路径处理目录守卫文件拷贝模块动态文件生成模块mustache简介自动安装依…...

【面经】3月29日 美团/美团平台/后端/一面/1h
面试官先介绍自己部门的业务:存储中心,涉及到大量数据的离线处理(亿级别)。 手撕(删除链表倒数第k个节点) 自我介绍 项目介绍(还没说完被打断了,面试官说你这个感觉就是把功能说了一…...

CSS:CSS的基础了解
css概述 CSS(Cascading Style Sheets,层叠样式表) 是用于控制网页样式和布局的一种样式表语言。用于描述网页的样式和布局,包括字体、颜色、大小、间距、边框等方面。 前端三🗡客:HTML,CSS,JavaScript&am…...

Android Framework学习笔记(2)----系统启动
Android系统的启动流程 启动过程中,用户可控部分是framework的init流程。init是系统中的第一个进程,其它进程都是它的子进程。 启动逻辑源码参照:system/core/init/main.cpp 关键调用顺序:main->FirstStageMain->SetupSel…...

项目管理中的估算活动资源
在项目管理中,资源估算是一项至关重要的任务。正确地估算活动资源可以确保项目的顺利进行,避免资源浪费和不必要的延误。以下是对项目管理中常见的活动资源类型的详细分析。 一、人力资源 人力资源是项目管理中最基本的资源之一。它包括项目团队成员的技能、知识和经验。在…...

java中的set集合及其子类
Set系列集合:添加的元素是无序(添加的数据的顺序和获取出数据顺序不一样),不重复,无索引 如:HashSet:无序,不可重复,无索引 LinkedHashSet:有序,不重复,无索…...

shell脚本查询匹配文件进行操作
1.寻找文件并赋权 查询当前目录及子目录下所有以“sh”结尾的文件,并赋执行权限。 #!/bin/bash # 将当前目录及子目录下所有以“sh”结尾的文件添加可执行权限 find ./ -name "*.sh" -type f -exec chmod x {} 2.寻找文件并删除 查询当前目录及子目录下存…...

vulnhub----natraj靶机
文章目录 一.信息收集1.网段探测2.端口扫描3.版本服务探测4.漏扫5.目录扫描 二.漏洞利用1.分析信息2..fuzz工具 三.getshell四.提权六.nmap提权 一.信息收集 1.网段探测 因为使用的是VMware,靶机的IP地址是192.168.9.84 ┌──(root㉿kali)-[~/kali/vulnhub] └─…...

Web Component 组件库有什么优势
前言 前端目前比较主流的框架有 react,vuejs,angular 等。 我们通常去搭建组件库的时候都是基于某一种框架去搭建,比如 ant-design 是基于 react 搭建的UI组件库,而 element-plus 则是基于 vuejs 搭建的组件库。 可能你有这种体…...

如何配置vite的proxy
1.前言 vite项目,本地开发环境可以通过配置proxy代理实现跨域请求。但是生产环境,该配置不生效,一般使用 nginx 转发,或者后端配置cors 2.解释 server: {port: 9000,proxy: { // 本地开发环境通过代理实现跨域,生产…...

Linux CentOS基础操作
Linux CentOS基础操作 1. 查看Linux服务器当前主机名等 hostname 2. 查看当前系统日期和时间 date -d -y 3. 显示网络接口信息,获取当前网卡状态,启动、停止网卡,网卡等闪烁显示30秒,配置网卡(网卡名称:eth1)的IP地址…...

最佳情侣身高差
题目描述 专家通过多组情侣研究数据发现,最佳的情侣身高差遵循着一个公式:(女方的身高)1.09 (男方的身高)。如果符合,你俩的身高差不管是牵手、拥抱、接吻,都是最和谐的差度。 下面…...

谷歌开发者账号防关联:如何选择性价比高的VPS,阿里、腾讯、酷鸟、AWS?
在Google Play上架应用的开发者朋友们,可能需要多个开发者账号来上架马甲包或矩阵式上架应用。但谷歌那边又不让一个人搞多个账号,所以,要想不被谷歌抓包,就得做好防关联的功课,确保每个账号都像是独立的个体。 而说到…...

Virtual digital asset $E=$eaco. EarthChain
Virtual digital asset $E$eaco. EarthChain Виртуальный цифровой актив $E $eaco. Цепочка Земля. 仮想デジタル資産$E$eaco.アースチェーン. Activos digitales virtuales $e $oaco. cadena terrestre. Virtuelles digitales Asset $E…...

[计算机网络] 当输入网址到网页
HTTP 首先,对URL进行解析,URL包含了Web服务器和对应的文件(文件路径) URL是请求服务器中的文件资源 通过Web服务器和对应文件来生产HTTP包(超文本传输协议) DNS 根据域名查询对应的IP地址 域名的层级 根…...

五年经验,还不懂小表驱动大表
小表驱动大表,也就是说用小表的数据集驱动大表的数据集。假如有order和user两张表,其中order表有10000条数据,而user表有100条数据。 这时如果想查一下,所有有效的用户下过的订单列表。可以使用in关键字实现: select…...

springboot+websocket+微信小程序实现评论区功能
springbootwebsocket微信小程序实现评论区功能 WebSocketSTOMP协议具体实现1.在pom文件中添加Spring WebSocket依赖2. 创建WebSocket配置类3.接收发送消息4.前端 参考 WebSocket 1. 什么是WebSocket? WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双…...

【项目】如何在面试中介绍自己的项目经验(附如何解决未知的问题成长路线)
【项目】如何在面试中介绍自己的项目经验(附如何解决未知的问题&成长路线) 文章目录 1、为什么要准备项目介绍?2、如何准备一份好的项目介绍?3、如何回答项目提问?4、如何避免低级错误?5、如何通过引导…...

解决Selenium元素拖拽不生效Bug
前几天在使用Selenium进行元素拖拽操作时,发现Selenium自带的元素拖拽方法(dragAndDrop())不生效,网上的回答也是五花八门,比较混乱,尝试了以下几种方法均无法解决。 方案1:通过dragAndDrop()方…...

提示工程中的10个设计模式
我们可以将提示词定义为向大型语言模型(Large Language Model,LLM)提供的一个查询或一组指令,这些指令随后使模型能够维持一定程度的自定义或增强,以改进其功能并影响其输出。我们可以通过提供细节、规则和指导来引出更有针对性的输出&#x…...

提高网站安全性,漏洞扫描能带来什么帮助
随着互联网的蓬勃发展,网站已经成为人们获取信息、交流思想、开展业务的重要平台。然而,与之伴随的是日益严重的网络安全问题,包括恶意攻击、数据泄露、隐私侵犯等。 为了保障网站的安全性,提前做好网站的安全检测非常有必要&…...

不要再使用 @Builder 注解了!有深坑呀!
曾经,我在《千万不要再随便使用 lombok 的 Builder 了!》 一文中提到 Builder 注解的其中一个大坑会导致默认值失效! 最近阅读了 《Oh !! Stop using Builder》 发现 Builder 的问题还不止一个,Builder 会让人误以为是遵循构建器…...

《UE5_C++多人TPS完整教程》学习笔记31 ——《P32 角色移动(Character Movement)》
本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P32 角色移动(Character Movement)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者&…...

怎么使用jwt,token以及redis进行续期?
怎么使用jwt,token以及redis进行续期? 什么是jwt? 什么是token? 结合 JWT、Token 和 Redis 进行续期的一般步骤: 生成 JWT: 用户登录成功后,服务器生成一个 JWT,并返回给客户端。 import io.jsonwebtok…...

AI日报:北大Open Sora视频生成更强了;文心一言可以定制你自己的声音;天工 SkyMusic即将免费开放;
🤖📱💼AI应用 北大Open Sora视频生成更强了!时长可达10秒,分辨率更高 【AiBase提要:】 ⭐️ Open-Sora-Plan v1.0.0模型发布 显著提升视频生成质量和文本控制能力 ⭐️ 支持华为昇腾910b芯片,提升运行效率和质量。 ⭐…...

替换空格(替换特定字符)
😀前言 在字符串处理中,经常会遇到需要替换特定字符的情况。本文将介绍一道经典的字符串替换问题:将字符串中的空格替换成 “%20”。我们将探讨一种高效的解决方法,通过倒序遍历字符串来实现原地替换,避免额外空间的开…...

ctfshow web入门 php特性 web123--web139
web123 必须传CTF_SHOW,CTF_SHOW.COM 不能有fl0g 在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,…...

pta L1-002 打印沙漏
L1-002 打印沙漏 分数 20 全屏浏览 切换布局 作者 陈越 单位 浙江大学 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印 ************ *****所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中…...

【简单讲解下PHP AES加解密示例】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

设计模式总结-外观模式(门面模式)
外观模式 模式动机模式定义模式结构外观模式实例与解析实例一:电源总开关实例二:文件加密 模式动机 引入外观角色之后,用户只需要直接与外观角色交互,用户与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦…...