Node自定义脚手架
自定义脚手架
创建自定义全局命令
-
新建一个文件夹(项目)并创建一个js文件(全局命令文件)
-
在项目根目录下初始化项目
-
npm init
-
-
在项目根目录下(将项目挂载在全局命令行中)
-
npm link
-
-
在js文件首行输入(解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件)
#!/usr/bin/env node
命令参数接收处理
-
方法一:process.argv
-
方法二:commander包——命令行参数处理工具
-
使用
const program = require('commander') // or const {program} = require('commander') // 查看版本号 program.version(require('./package.json').version); program.parse(process.argv)
-
命令行测试
> mycli --help Usage: index [options] Options: -h, --help display help for command
-
添加选项
const program = require('commander') program.option('-f --framwork <framwork>', 'select framwork') // -f简写,--framwork全称 <framwork>表示参数后必须跟一个选项 select framwork简介 program.parse(process.argv)
-
处理自定义指令选项
const program = require('commander') program .command('create <project> [other...]') //自定义命令 .alisa('crt')// 简写 .description('clone a repository into a folder') .action((project,argv)={ //命令行逻辑代码 });
-
终端交互
-
inquirer包
import inquirer from 'inquirer'; inquirer .prompt([ /* Pass your questions in here */ { type:'list',//问题的类型:input, number, confirm, list, rawlist, expand, checkbox, password, editor name:'framwork',// 问题的答案将以name的值为key作为对象返回 choices:['koa','express','egg'],//list选项 message:"请选择", }, {...} ]) .then((answers) => { // Use user feedback for... whatever!! // 例:{framwork:'koa'} }) .catch((error) => { if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } });
下载远程项目代码
-
download-git-repo包
const download = require('download-git-repo') download('direct:' + url, target, { clone: true }, (err) => { console.log(err ? 'Error' : 'Success') }) // url 远程代码地址 target 下载到的目标目录
-
下载等待提示交互
-
ora 包
- node中使用CommonJS模块化,不支持ES Module。无特殊必要可使用
ora@5
,ora@6
以后为ES Module方式引入该模块
const ora = require('ora') //const spinner = ora('Loading unicorns').start(); const spinner = ora().start() spinner.text = '代码正在下载……' setTimeout(() => { //spinner.color = 'yellow'; //spinner.text = 'Loading rainbows'; spinner.succeed('代码下载成功') //spinner.fail('代码下载失败') }, 1000);
- node中使用CommonJS模块化,不支持ES Module。无特殊必要可使用
-
-
示例代码
const download = require('download-git-repo') const ora = require('ora') const chalk = require('chalk') const downloadFun = function (url, project) { const spinner = ora().start() spinner.text = '代码正在下载……' download('direct:' + url, project, { clone: true }, (err) => { if (!err) { spinner.succeed('代码下载成功') console.log(chalk.blue.bold('Done!'), chalk.bold('you run:')); console.log('cd ' + project); console.log('npm install '); console.log('npm run dev '); } else { spinner.fail('代码下载失败') } }) }
项目初始化完成提示
-
chalk 包(改变命令行字体样式)
- chalk@5之后的版本使用ES Module
const chalk = require('chalk') console.log(chalk.blue.bold('Done!'), chalk.bold('you run:'));
补充
将callback转化成promise/(async await)
const { promisify } = require('util')//node自带工具包
// callback -> promisify(函数) -> Promise -> async await
const download = promisify(require('download-git-repo'))
const { codeUrl } = require('../config/repo-config');
const createProjectAction = async (project) => {
// download('direct:' + url, project, { clone: true }, (err) => {
// if (!err) {
// spinner.succeed('代码下载成功')
// console.log(chalk.blue.bold('Done!'), chalk.bold('you run:'));
// console.log('cd ' + project);
// console.log('npm install ');
// console.log('npm run dev ');
// } else {
// spinner.fail('代码下载失败')
// }
// })
await download(codeUrl, project, { clone: true });
}
执行终端命令开启子进程
const { spawn } = require('child_process');// 开启子进程
// npm install 本质上会开启一个子进程
//spawn(command,args,options)
const commandSpawn = (...args) => {
return new Promise((resolve, reject) => {
const childProcess = spawn(...args);//...args —— es6参数解构
// childPromise进程中会有很多执行命令过程中的打印信息
childProcess.stdout.pipe(process.stdout);// 显示打印信息——将当前进程的输出流放到ChildProcess
childProcess.stderr.pipe(process.stderr);// 显示错误信息
// 监听进程是否关闭
childProcess.on("close", () => {
resolve();
})
}).catch((error => {
console.error(error)
}))
}
module.exports = {
commandSpawn
}
下载远程代码(升级)
- 下载远程代码后安装依赖并运行项目
const { codeUrl } = require('../config/repo-config');
const createProjectAction = async (project) => {
// 1.clone项目
await download(codeUrl, project, { clone: true });
// 2.执行npm install
// command:要执行的命令;[]:命令执行的参数;cwd:子进程工作目录
// 在window系统中执行npm的本质是执行npm.cmd
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
await commandSpawn(command, ['install'], { cwd: `./${project}` })
// 3.运行npm run serve
commandSpawn(command, ['run', 'serve'], { cwd: `./${project}` });
// 4.打开浏览器
// open("http://localhost:8080/");
}
创建模板文件
-
.ejs→.vue
-
ejs 包
-
步骤
- 对应的ejs模块
- 编译ejs模块得到一个result
- 将result写入到.vue文件中
- 将文件放到对应的文件夹中
-
对应的ejs模块
// vue-components.ejs <template> <div class="<%= data.lowerName %>"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: '<%= data.name %>', props: { msg: String }, components: { }, mixins: [], data: function () { return { message: "<%= data.name %>" } }, created: function () { }, mounted: function () { }, computed: { }, methods: { } } </script> <style scoped> .<%=data.lowerName %> {} </style> // vue-router.ejs // 普通加载路由 // import <%= data.name %> from './<%= data.name %>.vue' // 懒加载路由 const <%= data.name %> = () => import('./<%= data.name %>.vue') export default { path: '/<%= data.lowerName %>', name: '<%= data.name %>', component: <%= data.name %>, children: [ ] }
-
编译ejs模块得到一个result
const path = require('path'); const ejs = require('ejs'); const compile = (templateName, data) => { const templatePosition = `../templates/${templateName}` const templatePath = path.resolve(__dirname, templatePosition) return new Promise((resolve, reject) => { ejs.renderFile(templatePath, { data }, {}, (err, result) => { if (err) { console.log(err); reject(err) return } resolve(result) }) }) }
-
将文件放到对应的文件夹中
const path = require('path'); const fs = require('fs'); const writeToFile = (path, content) => { return fs.promises.writeFile(path, content) } // 创建路径文件夹 // source/components/category const createDirSync = (dirPath) => { if (fs.existsSync(dirPath)) { return true } else { if (createDirSync(path.dirname(dirPath))) { fs.mkdirSync(dirPath) return true } } }
-
创建模板文件action
const { compile, writeToFile, createDirSync } = require('../utils/utils') const addPageAndRouteAction = async (name, dest) => { // 1.编译ejs模板 result const data = { name, lowerName: name.toLowerCase() } const page = await compile("vue-components.ejs", data) const router = await compile("vue-router.ejs", data) // 2.写入文件的操作 const targetPath = path.resolve(dest, name.toLowerCase()) if (createDirSync(targetPath)) { const pagePath = path.resolve(targetPath, `${name}.vue`) const routerPath = path.resolve(targetPath, `router.js`) writeToFile(pagePath, page) writeToFile(routerPath, router) } }
转载自:https://juejin.cn/post/7133916293702516749