脚手架搭建及发布npm
Posted 面条请不要欺负汉堡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了脚手架搭建及发布npm相关的知识,希望对你有一定的参考价值。
一.为什么需要脚手架?
- 减少重复性的工作,不再从零创建一个项目,或者复制粘贴另一个项目的代码 。
- 根据动态交互生成项目结构和配置文件,具备更高的灵活性和人性化定制的能力 。
- 有利于多人开发协作,避免了人工传递文件的繁琐。
- 可以集成多套开发模板,根据项目需要选择合适的模板。
二.第三方库的支持
实现一个脚手架,通常需要以下工具
- commander: 命令行工具
- download-git-repo: 来通过git下载项目模板的插件
- inquirer: 用于命令行交互问询等
- ora: 用于实现node命令环境的loading效果,并显示各种状态的图标,显示 loading 动画
- chalk: chalk是一个颜色的插件。可以通过chalk.green(‘success’)来改变颜色。修改控制台输出内容样式
- log-symbols: 用于在打印输出的内容中加入icon更友好(显示出 √ 或 × 等的图标)
- handlebars.js: 模板引擎工具,可用于修改package.json中相关字段
- child_process:
三.手脚架的项目结构
├── bin
├── www 脚手架的入口文件
├── commands 定义命令
├── add.js 定义新增项目模板命令 add
├── delete.js 定义删除项目模板命令 delete
├── init.js 初始化项目init
├── list.js 目前存在的模板列表
├── node_modules npm install 生成
├── templates 存放模板静态资源
├── util
├──showTable.js 控制台显示的table布局
├── .npmignore 忽略需要发布的文件
├── package.json package.json
└── template.json 存放模板列表的json数据
四.创建
1. 新建ydframe-cli文件
(ydframe-cli 脚手架的名称)
在该目录下执行 npm init -y 进行初始化,生产 package.json 文件
2. 安装第三方工具库
npm install chalk commander download-git-repo inquirer ora log-symbols
或是 执行修改package.json中把相应的开发依赖写入
"dependencies":
"child_process": "^1.0.2",
"chalk": "^4.1.2",
"cli-table": "^0.3.6",
"commander": "^4.1.1",
"inquirer": "^7.0.4",
"download-git-repo": "^1.1.0",
"log-symbols": "^4.0.0",
"ora": "^3.2.0"
,
然后再执行npm i
3. 在根目录下新建bin/www【整个脚手架的入口文件】
#!/usr/bin/env node
console.log('hello world')
执行 node ./bin/www ,在控制台就会打印出 hello world。
4. package.json 进行命令配置
...
"bin":
"per": "bin/www"
....
执行 npm link将命令挂载到全局,然后再输入 per 就可以到达刚才node ./bin/www 的效果了
5. 定义多个命令
再 bin 下面的 cm 文件夹来定义多个命令,此时就用到 commander 了。首先我们来看一下 commander 的用法
usage(): 设置 usage 值
command(): 定义一个命令名字
description(): 设置 description 值
option(): 定义参数,需要设置“关键字”和“描述”,关键字包括“简写”和“全写”两部分,以”,”,”|”,”空格”做分隔。
parse(): 解析命令行参数 argv
action(): 注册一个 callback 函数
version() : 终端输出版本号
根据日常开发需要,我们创建以下几个脚手架命令:
- add 新增一个项目模板
- delete 删除一个项目模板
- list 列举所以项目模板
- init 初始化一个项目模板
编写一下 www 文件
#!/usr/bin/env node
// 声明全局对象 program 变量(Commander 提供了一个全局对象)
const program = require('commander')
// 定义脚手架的用法,在program.help方法中会显示
program.usage('<command>')
// 获取package.json中的version来做为项目的版本号
program.version(require('../package').version)
// 新增一个项目模板
program
.command('add')
.description('add a new template')
.action(() =>
console.log('新增项目模板业务')
require('../commands/add')
)
// 删除项目模板
program
.command('delete')
.description('delete a template')
.action(() =>
console.log('删除项目模板业务')
require('../commands/delete')
)
// list 列举所以项目模板
program
.command('list')
.description('List the templateList')
.action(() =>
console.log('列表项目模板')
require('../commands/list')
)
// init 初始化一个项目模板
program
.command('init')
.description('init a project')
.action(() =>
console.log('项目初始化')
require('../commands/init')
)
// 处理参数,没有被使用的选项会被存放在program.args数组中
program.parse(process.argv)
// 如果有选项被放在program.args,即没有被program.parse处理,则默认使用program.help()将npm包可以执行的命令打印出来
// 可以通过program.on('--help', function())来自定义help
if (program.args.length)
program.help()
/*
command为执行的命令
description为命令的描述
alias为简写
action为命令相应的操作
*/
6. 创建模板command,编写指令
(1)编写指令- inquirer
在这里会用到 inquirer 进行命令行交互,先来看下 inquirer 的用法,它有以下参数可以配置
type:表示提问的类型,包括:input, confirm, list, rawlist, expand, checkbox, password, editor;
name: 存储当前问题回答的变量;
message:问题的描述;
default:默认值;
choices:列表选项,在某些type下可用,并且包含一个分隔符(separator);
validate:对用户的回答进行校验;
filter:对用户的回答进行过滤处理,返回处理后的值;
transformer:对用户回答的显示效果进行处理(如:修改回答的字体或背景颜色),但不会影响最终的答案的内容;
when:根据前面问题的回答,判断当前问题是否需要被回答;
pageSize:修改某些type类型下的渲染行数;
prefix:修改message默认前缀;
suffix:修改message默认后缀。
语法结构如下:
const inquirer = require('inquirer')
const question = [
// 具体交互内容
]
inquirer.prompt(question).then((answers) =>
console.log(answers) // 返回的结果
)
(2)创建模板
根目录下建立\\command文件夹【专门用来存放命令处理文件】
A 添加模板
进入\\command并新建add.js
- 通过命令行交互,让用户输入模板名称和模板的地址
- 将用户输入的模板信息新增写入到template.json文件中
- 打印出所有的项目模板
- 从git 下载到本地
#!/usr/bin/env node
const inquirer = require('inquirer')
const fs = require('fs')
const path = require("path");
const templateList = require(`$__dirname/../template`)
const showTable = require(`$__dirname/../util/showTable`)
const symbols = require('log-symbols')
const exec = require('child_process')
const chalk = require('chalk')
chalk.level = 3
let question = [
name: 'name',// 问题对应的属性
type: 'input',// 问题类型为填空题
message: '请输入模板名称', // 问题描述
validate(val)
if (!val)
return chalk.red('name is required!')
else if (templateList[val])
return 'Template has already existed!'
else
return true
,
name: 'url',
type: 'input',
message: '请输入模板地址',
validate(val)
if (!val)
return chalk.red('The url is required!')
return true
]
inquirer.prompt(question).then((answers) =>
let name, url = answers;
// 打印再控制台上
templateList[name] = url.replace(/[\\u0000-\\u0019]/g, '') // 过滤 unicode 字符
fs.writeFile(`$__dirname/../template.json`, JSON.stringify(templateList), 'utf-8', err =>
if (err) console.log(chalk.red(symbols.error), chalk.red(err))
console.log('\\n')
console.log(chalk.green(symbols.success), chalk.green('Add a template successfully!\\n'))
console.log(chalk.green('The latest templateList is: \\n'))
showTable(templateList)
)
//从git下载到本地指定路径
exec(`git clone $url ` + path.resolve(__dirname, `../templates/$name`), (error, stdout, stderr) =>
if (error) // 当有错误时打印出错误并退出操作
console.log(chalk.red(error))
process.exit()
console.log(chalk.green('初始化完成'))
process.exit() // 退出这次命令行操作
)
)
在这里还用到以下两个第三方库,原来美化相互效果:
- chalk:用来修改控制台输出内容样式的,比如颜色 log-symbols:
- 显示出 √ 或 × 等的图标
此时,执行 per add ,并输入项目模板名称和地址,就能看到以下效果了
本地的文件
B 删除模板
进入\\command并新建delete.js
- 通过命令行交互,让用户输入要删除的项目模板名称
- 删除用户输入的模板数据,然后再将更新的数据写入到template.json文件中
- 打印出所有的项目模板
- 删除本地文件
此时,执行 per delete ,并输入项目模板名称,就能看到以下效果了
C 项目列表
进入\\command并新建list.js
列举所有的项目模板,这个就更简单了,直接上代码
#!/usr/bin/env node
const showTable = require(`$__dirname/../util/showTable`)
const templateList = require(`$__dirname/../template`)
showTable(templateList)
D 初始化安装项目模板
初始化一个项目模板,这是最重要的一部分,步骤如下
- 通过命令行交互,让用户模板的名称和项目的名称
- 校验模板是否存在,项目名称是否填写 开始下载模板,
- 显示加载图标 完成模板下载,隐藏加载图标
用到download-git-repo 下载远程模板,它的使用方法如下
const download = require('download-git-repo')
download(repository, destination, options, callback)
/*
repository 是远程仓库地址
destination 是存放下载的文件路径,也可以直接写文件名,默认就是当前目录
options 是一些选项,比如 clone:boolean 表示用 http download 还是 git clone 的形式下载。
callback 是回调函数
*/
init.js
#!/usr/bin/env node
const inquirer = require('inquirer')
const fs = require('fs')
const ora = require('ora')
const path = require('path');
const download = require('download-git-repo')
const templateList = require(`$__dirname/../template`)
const symbols = require('log-symbols')
const chalk = require('chalk')
const spinner = ora(chalk.green('下载中...'));
chalk.level = 3
const cwd = process.cwd();
// 获取项目模板list
let list = [];
for (let key in templateList)
list.push(key)
let question = [
name: "projectTemplate",
message: "请选择项目模板?",
type: 'list',
choices: list,
validate(val)
,
type: 'input',
name: 'projectName',
message: '请填写项目名称',
validate(val)
if (!val)
return chalk.red('projectName is required!')
else
return true
]
inquirer.prompt(question).then((answers) =>
let name, url = answers;
// 获取当前路径
const targetDir = path.resolve(cwd, answers.projectName || '.');
// 判断当前路径下是否有这个文件夹
if (!fs.existsSync(targetDir))
downloadGitRepo(answers);
else
console.log(chalk.bgRed('当前路径已存在同名目录,请确定后再试'));
)
/**
* 下载 git 仓库
* @param 项目名 projectName
*/
function downloadGitRepo(answers)
const projectTemplate, projectName = answers;
console.log(chalk.blue('正在拉取' + projectTemplate + '项目模板'));
// 出现加载图标
spinner.start();
//git 上的地址
let url = templateList[projectTemplate]
download(
`direct:$url`,
`./$projectName`,
clone: true ,
function (err)
if (err)
// 下载失败
spinner.fail(chalk.bgRed('下载失败'));
else
// 下载成功
// 获取当前路径
// 结束加载图标
spinner.succeed(chalk.bgGreen('下载成功'));
console.log('\\n To get started')
console.log(`\\n cd $projectName \\n`)
);
运行per init
五.发布到 npm
(1)发布流程
发布执行npm login , npm publish
由于发布名字重复了,所以改了文件名
六 使用
npm i scanper-cli -g 全局安装脚手架
执行scanper -h,就说明脚手架已经发布并安装成功了
七 .参考材料
从零搭建一个前端cli脚手架并发布到npm
教你从零开始搭建一款前端脚手架工具
前端工程化 搭建自己的脚手架工具详细步骤
搭建一个自己的前端脚手架(一)
inquirer.js —— 一个用户与命令行交互的工具
以上是关于脚手架搭建及发布npm的主要内容,如果未能解决你的问题,请参考以下文章