带你手摸手从零开始撸一个脚手架

news2025/1/12 4:01:04

文章目录

    • 为什么要搭建脚手架
    • 搭建流程梳理
    • 初始化脚手架
    • 命令参数处理
    • 询问用户信息
    • 下载模版代码
    • 等待提示交互
    • 脚手架命令行美化
    • 发布脚手架到npm

为什么要搭建脚手架

脚手架就是在项目启动的时候询问一些简单的问题,并且通过用户回答的结果去渲染对应的模板文件。

搭建脚手架的目的就是快速搭建项目的基本结构并提供项目规范和约定

目前日常工作中常用的脚手架有 vue-cli、create-react-app、angular-cli 等等,都是通过简单的初始化命令,完成内容的快速构建。

脚手架是我们经常使用的工具,也是团队提效的重要手段,所以系统性的掌握脚手架相关知识,对前端开发者来说是非常重要的。

站在学习的角度,我们创建项目如果只是使用脚手架,我们永远不知道如何搭建一个项目。

👌那这篇文章将手把手教你撸一个属于自己的脚手架!


搭建流程梳理

我们先来看一下脚手架的基本工作流程:

  1. 通过命令行交互询问用户问题
  2. 根据用户回答的结果生成文件

下面呢是我们这次手撸脚手架的步骤:

  • 创建自定义全局命令 npm link

  • 命令参数接受处理 commander

  • 终端交互 inquirer

  • 下载远程代码 download-git-repo

  • 下载等待提示 ora

  • 命令行字体美化 chalk

OK👌,接下来话不多说,直接开撸!


初始化脚手架

我们先来初始化脚手架项目:

mkdir mycli # 新建脚手架项目目录
cd mycli
mkdir bin # 创建 bin 目录
cd bin # 进入 bin 目录
touch cli.js # 新建一个程序入口文件 cli.js 文件
cd ..
npm init # 生成 package.json 文件

此时项目目录结构:

mycli
├─ bin 
	└─ cli.js
└─ package.json  

我们打开 cli.js 文件进行编辑:

#! /usr/bin/env node

// #! 用于指定脚本的解释程序
// Node CLI 应用入口文件必须要有这样的文件头

// 用于检查入口文件是否正常执行
console.log('mycli')

然后我们需要用 npm link 来把这个脚本链接到全局:

sudo npm link

然后我们在命令行输入 mycli,命令行会输入 cli.jslog的内容

在这里插入图片描述


命令参数处理

我们用过很多知名的脚手架,一个成熟的脚手架一定有非常完善的命令参数处理机制。

例如我在这里使用docker --help,控制台会打印:

在这里插入图片描述

接下来我们也来让我们的脚手架实现这样的参数处理功能。

process.argv 属性返回一个数组,这个数组包含了启动 Node.js 进程时的命令行参数

我们在cli.js里打印一下process.argv[2]会得到我们输入的命令行参数

如果我们输入的参数是--help,就打印获取到了help命令参数

// cli.js
if(process.argv[2] == '--help') {
    console.log('获取到了help命令参数');
}

在这里插入图片描述

如果这里通过原生 Node 提供的这个属性去操作命令行中的参数,那肯定是非常的麻烦的,不可能每一个命令我们都自己去实现

所以我们使用第三方提供包,也就是 commander

首先我们npm install commander 来安装commander,然后我们在cli.js引入:

#! /usr/bin/env node

const { program } = require('commander')

console.log(process.argv[2]);

if(process.argv[2] == '--help') {
    console.log('获取到了help命令参数');
}

program.option('-f --framework <framework>','设置框架')
program.parse(process.argv); // parse() 用于解析命名行参数

在这里插入图片描述

我们来优化完善一下配置,增加一个Commands配置用于 create project 创建项目:

program
    .command('create <project> [other...]') // 定义命令
	.alias('crt') // 定义别名
	.description('创建项目') // 描述
	.action((project,args) => { // 定义命令处理方法
    	// 命令行的执行逻辑代码
    	// console.log(project);
    	// console.log(args);
	})

program.parse(process.argv);

在这里插入图片描述

询问用户信息

处理好了命令参数,接下来我们来实现脚手架的询问用户信息的功能

我们这里需要用到inquirer,先来安装一下 npm install inquirer

上面我们写的program.action里面就是命令处理的方法,我们来使用inquirer.prompt来与用户进行信息交互:

program
    .command('create <project> [other...]') // 定义命令
	.alias('crt') // 定义别名
	.description('创建项目') // 描述
	.action((project,args) => { // 定义命令处理方法
    	// 命令行的执行逻辑代码
        inquirer.prompt([
        {
            type: 'input', //type: input, number, list, checkbox ... 
            name: 'username', // key 名
            message: '请输入你的名字', // 提示信息
            // default: 'defaultName' // 默认值
        },
        {
            type: 'list',
            name: 'framework',
            choices: ['express', 'koa', 'egg'],
            message: '请选择你所使用的框架',
        }
    	])
    .then(answer => {
        // 打印用户输入结果
        console.log(answer); // {username:'ruimengmeng',framework:'express'}
    })
	})

program.parse(process.argv);

在这里插入图片描述

下载模版代码

当用户选择完所要用的框架之后,我们就需要从远程仓库下载对应的框架模版代码

download-git-repo 是一个用于下载 GitHub、Gitlab 上的公共仓库的库

我们这里用download-git-repo来实现下载功能,安装npm install download-git-repo,使用方法如下:

    // 下载代码模版
    download(
		'direct:git@gitee.com:xxx/express-template.git',  // 远程仓库的地址
        project,  // './xxx', 下载到本地的路径
        { clone: true },  // 配置参数
        (err) => { // 回调函数
            console.log(err);
        }
    )

好我们来完成下载功能:

program
    .command('create <project> [other...]') // 定义命令
	.alias('crt') // 定义别名
	.description('创建项目') // 描述
	.action((project,args) => { // 定义命令处理方法
    	// 命令行的执行逻辑代码
		const answer = await inquirer.prompt([
            {
                type: 'input',
                name: 'username',
                message: '请输入你的名字',
            },
            {
                type: 'list',
                name: 'framework',
                // choices: ['express', 'koa', 'egg'],
                choices: config.framework,
                message: '请选择你所使用的框架',
            }
    	])
        // .then(answer => {
        //     console.log(answer);
        // })
    
        // 下载代码模版
    	downloadFunction(config.frameworkUrl[answer.framework], project);
	})

const downloadFunction = function (url, project) {
    download(
        "direct:" + url,
        project,
        { clone: true },
        (err) => {
            console.log(err);
        }
    );
};

program.parse(process.argv);

为了代码的可读性和拓展性,我们将选择的框架和框架对应的下载地址放在config.js里:

module.exports = {
    // 可选择的框架
    framework: ['express', 'koa', 'egg'],

    // 框架对应的下载地址
    frameworkUrl: {
        express: 'https://gitee.com/xxx/express-template.git',
        koa: 'https://gitee.com/xxx/koa-template.git',
        egg: 'https://gitee.com/xxx/egg-template.git',
    }
}

我们来试一下我们写的下载功能有没有生效:

在这里插入图片描述

可以看到,选择完框架之后,过了一会框架模版代码成功下载到了bin目录

到目前为止,我们的脚手架基本功能已经实现

但是这个过程非常不友好,没有任何的交互体验可言,就是单纯的用户选择完然后下载,没有任何的下载过程和完成提示

要是网速慢一点,用户简直一脸懵逼,不能忍!我们来优化一下!


等待提示交互

ora 是一个实现命令行 loading 效果的库

安装npm install ora,使用方法如下:

const ora = require('ora')
const spinner = ora().start() // 定义一个loading并启动
spinner.text = 'Loading...'

在这里插入图片描述

使用方法非常简单,接下来我们来添加到我们的脚手架里面:

const downloadFunction = function (url, project) {
    const spinner = ora().start();
    spinner.text = '代码正在下载...';
    // 下载代码模版
    download(
        "direct:" + url,
        project, // './xxx',
        { clone: true },
        (err) => {
            // console.log(err);
            if(!err) {
                spinner.succeed('代码下载成功!🐮');
                console.log('项目已经准备就绪! 😄 你可以运行:');
                console.log('👀 cd ' + project);
                console.log('🌟 npm install ');
                console.log('🔥 npm run dev');
            } else {
                spinner.fail('代码下载失败!🥺')
            }
        }
    );
};

在这里插入图片描述

脚手架命令行美化

chalk 是一个命令行美化工具,能够输出五彩斑斓的命令

// npm install chalk

const chalk = require('chalk');

const downloadFunction = function (url, project) {
    const spinner = ora().start();
    spinner.text = '代码正在下载...';
    // 下载代码模版
    download(
        "direct:" + url,
        project,
        { clone: true },
        (err) => {
            if(!err) {
                spinner.succeed(chalk.green.bold('代码下载成功!🐮'));
                console.log(chalk.blue.bold('项目已经准备就绪! 😄 你可以运行:'));
                console.log(chalk.yellow.bold('👀 cd ' + project));
                console.log(chalk.yellow.bold('🌟 npm install '));
                console.log(chalk.yellow.bold('🔥 npm run dev'));
            } else {
                spinner.fail('代码下载失败!🥺')
            }
        }
    );
};

在这里插入图片描述

到此为止呢,我们的脚手架就算基本完成了

完整代码戳👇此处:https://github.com/KongC-X/mycli

接下来我们就可以将我们的脚手架发布到 npm 上

发布脚手架到npm

第一步:获取 npm 配置

npm config list -l --json

-l 列表表示所有默认配置选项

--json 以 json 格式显示配置选项

这一步是为了确认"registry": "https://registry.npmjs.org/"无误

第二步:登录 npm 账号 npm login

第三步:修改 npm 包版本

更新补丁版本号:npm version patch 0.0.1

更新次要版本号:npm version minor 0.1.1

更新主要版本号:npm version major 1.1.1

第四步:正式发布 sudo npm publish

在这里插入图片描述

发布成功!我们可以在npm官网上找到我们刚刚发布的 npm 包

在这里插入图片描述

我们可以来验证一下:

安装:npm install -g kongccli

测试:kongccli --help

创建项目:kongccli create test-project

在这里插入图片描述

完美!下班!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

四、使用Kibana执行elasticsearch的CRUD操作

目录 使用kibana测试 1、创建一个索引 2、创建索引并添加数据 3、获取索引的具体信息 4、通过GET请求_cat查看所有的_cat命令 5、修改数据 5.1、使用PUT修改 5.2、使用POST最后加上_update修改 6、删除文档 7、删除索引 8、查询操作 Rest风格说明 Rest是一种软件架构…

汽车一键启动开关发动机启动按钮点火开关图解

汽车改装件一键启动按钮单复位汽车点火开关一键启动按钮开关 &#xff08;用于配套启动主机使用&#xff09; 移动管家一键启动开关 YC360外观&#xff1a;黑色按钮上有3种不同的雷射灯&#xff0c;指示汽车不同的状态&#xff0c;和主机配套时可任意选灯的颜色。指示汽车的不…

AFPM系列消防设备电源监控系统的功能和一样

安科瑞虞佳豪 现代建筑内部&#xff0c; 消防报警、防火、灭火系统种类繁多&#xff0c;并分布在建筑内的各个角落。当灾情发生的时候&#xff0c;消防设备能否处于正常的工作状态&#xff0c; 直接关系到人民生命财产安全是否得到及时保全。 消防设备电源监控系统对于确保建…

Node.js包和模块的关系是怎样的?

在Node.js 中&#xff0c;会将某个独立的功能封装起来&#xff0c;用于发布、更新、依赖管理和进行版本控制。Nodejs 根据CommonJS规范实现了包机制&#xff0c;开发了NPM包管理工具&#xff0c;用来解决包的发布和获取需求。 Node.js的包和模块并没有本质的不同&#xff0c;包…

高频前端面试题汇总之CSS篇

一、CSS基础 1. CSS选择器及其优先级 选择器格式优先级权重id选择器#id100类选择器#classname10属性选择器a[ref“eee”]10伪类选择器li:last-child10标签选择器div1伪元素选择器li:after1相邻兄弟选择器h1p0子选择器ul>li0后代选择器li a0通配符选择器*0 对于选择器的优…

亚马逊云科技Serverless为手游《MARVEL SNAP》降低30%成本

经典的漫威IP&#xff0c;酷炫的卡牌对战&#xff0c;丰富的故事情节&#xff0c;这款移动游戏《MARVEL SNAP》一经上线就深得全球玩家喜爱。在The Game Awards 2022的年度颁奖典礼上&#xff0c;它更是以出色的表现&#xff0c;一举斩获最佳移动游戏奖项。 其研发公司Second …

centos7~等 FTP登录时,解决报错530,500,421等错误

问题描述 1、添加了用户但是莫名登录不能用户真的是见鬼了,这那遭得住呀&#xff01;我干&#xff0c;出现的问题如下图所示&#xff1a; cat /etc/passwd 查看是否是之前添加的用户&#xff0c;并确定是否存在。 若没有创建成功&#xff0c;则使用useradd -s /sbin/nologin …

矩阵向量乘法MPI程序的性能评估

文章目录 前言一、实验代码二、实验运行效果三、问题以及思考总结 前言 要求&#xff1a;对矩阵向量乘法MPI程序进行不同输入规模、不同进程数的执行和计时&#xff0c;并对得到运行时间进行计算分析&#xff0c;据此评价该程序的强扩展性和弱扩展性。最后总结学习心得&#x…

feign调用get请求的接口时,出现“Request method ‘POST‘ not supported“

上面是错误的写法 下面是正确的写法 其实就是在feign接口的参数中加了个SpringQueryMap注解 SpringQueryMap是微服务之间调用&#xff0c;使用openfeign通过GET请求方式来处理通过实体类来传参情况的注解。 注意&#xff1a;被SpringQueryMap注解的对象只能有一个 如果需要传…

vulhub-struts2-S2-001 远程代码执行漏洞复现

漏洞原理 该漏洞因用户提交表单数据并且验证失败时&#xff0c;后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析&#xff0c;然后重新填充到对应的表单数据中。如注册或登录页面&#xff0c;提交失败后一般会默认返回之前提交的数据&#xff0c;由于后端使用%{va…

Python 中常用的数据类型及相关操作详解

文章目录 列表&#xff08;Lists&#xff09;创建列表访问列表元素添加元素到列表删除列表元素切片&#xff08;Slicing&#xff09;其他常用操作 元组&#xff08;Tuples&#xff09;创建元组访问元组元素元组长度其他常用操作 字符串&#xff08;Strings&#xff09;创建字符…

低成本渲染提速!掌握这6点,不换电脑也能迅速提升渲染速度

之前一期给大家分享过影响渲染速度的三个“大坑”: 渲染干货 | 渲染速度太慢&#xff1f;很有可能你是踩了这些坑https://blog.csdn.net/LhcyyVSO/article/details/131191936 本期再来谈谈如何在不换电脑配置的情况下&#xff0c;以更低的成本提升你的渲染速度&#xff0c;毕…

无人机电池能不能上高铁以及什么型号的电池可以上高铁

无人机电池能不能上高铁以及什么型号的电池可以上高铁 高铁关于电池的限制电池参数介绍可上高铁的无人机锂电池6S电池3S电池 高铁关于电池的限制 2022年7月1日起施行的《铁路旅客禁止、限制携带和托运物品目录》规定,充电宝、锂电池单块额定能量不超过100Wh 电池参数介绍 明…

致创业者,亚马逊云科技中国峰会“创业者之日”邀您相聚

IDC预测&#xff0c;到2025年全球数据规模将达到175 ZB&#xff0c;这为人工智能模型训练提供了海量数据资源&#xff0c;全球AI产业迅速发展为创业者们带来了巨大的机遇&#xff0c;但也提出了全新的挑战。新的市场、高效的工具和智能的资源&#xff0c;开辟了创新的领域和商业…

和鲸社区数据分析每周挑战【第九十四期:中风患病预测分析】

和鲸社区数据分析每周挑战【第九十四期&#xff1a;中风患病预测分析】 文章目录 和鲸社区数据分析每周挑战【第九十四期&#xff1a;中风患病预测分析】一、前言1、背景描述2、数据说明3、数据集预览 二、数据读取和数据预处理三、探索性数据分析1、绘制相关性矩阵2、中风患病…

1 os|linxu常用命令|vim编辑器

文章目录 oslinux 常用命令用户配置切换用户添加用户设置&#xff08;修改&#xff09;密码删除用户总结 目录以及文件操作查看文件或者目录改变工作目录显示当前工作目录创建目录删除空目录拷贝文件或者目录移动文件或者目录删除文件或者目录修改目录或者文件的权限文字设定法…

年过30的我,今年也遇到了职场危机 ...

在软件测试行业摸爬滚打这么多年&#xff0c;即将人到中年&#xff0c;也会和大家一样面临转型或者跳槽的问题。这里总结了一下自我的看法。 对于找工作这个事情&#xff0c;有这么个普遍现象。工作供求无法匹配~ 1、对于测试招聘者&#xff0c;特别是一、二线互联网公司的招…

12.动态规划:子集状压DP和位运算

参考&#xff1a; 0x3f&#xff1a;从集合论到位运算&#xff0c;常见位运算技巧分类总结&#xff01;https://leetcode.cn/circle/discuss/CaOJ45/ 状态压缩DP详细讲解 https://zhuanlan.zhihu.com/p/599427567 【动态规划学习】状压/子集 DP https://leetcode.cn/circle/arti…

zabbix-4-触发器

4.触发器 4.1什么是触发器 当监控的值发现变化后&#xff0c;对应的值不符合预期&#xff0c;则应该通过触发器通知管理人员介入&#xff1b; 比如&#xff1a;监控TCP的80端口&#xff0c;如果存活则符合预期&#xff0c;如果不存活则不符合预期&#xff0c;应该通过触发器通…

从一个线上 Android Bug 回看 Fragment 的基础知识

作者&#xff1a;Kotlin上海用户组 公司的项目在最近遇到了一个与 Fragment 有关的线上 crash&#xff0c;导致这个问题的根本原因比较复杂&#xff0c;导致修复方案的可选项非常有限&#xff0c;不过这个问题的背景、crash 点&#xff0c;以及修复过程都非常有趣&#xff0c;值…