先看下效果,如今脚手架还不完善只是完成了初始化功能,具体功能还得等以后慢慢完善 javascript
本前端脚手架的思想,其实就是前端
前端脚手架的好处主要是java
具体看下代码node
初始化 bin/init.jsgit
#!/usr/bin/env node
const program = require('commander')
const path = require('path')
const fs = require('fs')
const glob = require('glob') // npm i glob -D
const download = require('../lib/download') //下载配置
const inquirer = require('inquirer') // 按需引入
const logSymbols = require("log-symbols");
const chalk = require('chalk')
const remove = require('../lib/remove') // 删除文件js
const generator = require('../lib/generator')// 模版插入
const CFonts = require('cfonts');
program.usage('<project-name>')
.parse(process.argv) // 加入这个能获取到项目名称
// 根据输入,获取项目名称
// console.log(program)
let projectName = program.rawArgs[2] // 获取项目名称
if (!projectName) { // project-name 必填 若是没有输入名称执行helphelp
// 至关于执行命令的--help选项,显示help信息,这是commander内置的一个命令选项
program.help()
return
}
// 当前目录为空,若是当前目录的名称和project-name同样,则直接在当前目录下建立工程,不然,在当前目录下建立以project-name做为名称的目录做为工程的根目录
// 当前目录不为空,若是目录中不存在与project-name同名的目录,则建立以project-name做为名称的目录做为工程的根目录,不然提示项目已经存在,结束命令执行。
const list = glob.sync('*') // 遍历当前目录
let next = undefined;
let rootName = path.basename(process.cwd());
if (list.length) { // 若是当前目录不为空
if (list.some(n => {
const fileName = path.resolve(process.cwd(), n);
const isDir = fs.statSync(fileName).isDirectory();
return projectName === n && isDir
})) {
console.log(`项目${projectName}已经存在`);
remove(path.resolve(process.cwd(), projectName)) // 删除重复名字文件,而且重复建立,(逻辑修改)--> 询问是否删除重名文件而后,用户回答是,而后删除文件,并从新覆盖
// return;
}
rootName = projectName;
next = Promise.resolve(projectName);
} else if (rootName === projectName) {
rootName = '.';
next = inquirer.prompt([
{
name: 'buildInCurrent',
message: '当前目录为空,且目录名称和项目名称相同,是否直接在当前目录下建立新项目?',
type: 'confirm',
default: true
}
]).then(answer => {
console.log(answer.buildInCurrent)
return Promise.resolve(answer.buildInCurrent ? '.' : projectName)
})
} else {
rootName = projectName;
next = Promise.resolve(projectName)
}
next && go()
function go () {
// 预留,处理子命令
// console.log(path.resolve(process.cwd(), path.join('.', rootName))) // 打印当前项目目录
// download(rootName)
// .then(target => console.log(target))
// .catch(err => console.log(err))
next.then(projectRoot => { //
if (projectRoot !== '.') {
fs.mkdirSync(projectRoot)
}
CFonts.say('amazing', {
font: 'block', // define the font face
align: 'left', // define text alignment
colors: ['#f80'], // define all colors
background: 'transparent', // define the background color, you can also use `backgroundColor` here as key
letterSpacing: 1, // define letter spacing
lineHeight: 1, // define the line height
space: true, // define if the output text should have empty lines on top and on the bottom
maxLength: '0', // define how many character can be on one line
});
return download(projectRoot).then(target => {
return {
projectRoot,
downloadTemp: target
}
})
}).then(context => {
// console.log(context)
return inquirer.prompt([
{
name: 'projectName',
message: '项目的名称',
default: context.name
}, {
name: 'projectVersion',
message: '项目的版本号',
default: '1.0.0'
}, {
name: 'projectDescription',
message: '项目的简介',
default: `A project named ${context.projectRoot}`
},{
name: 'supportMacawAdmin',
message: '是否使用element',
default: "No",
}
]).then(answers => { // 可选选项回调函数
// return latestVersion('macaw-ui').then(version => {
// answers.supportUiVersion = version
// return {
// ...context,
// metadata: {
// ...answers
// }
// }
// }).catch(err => {
// return Promise.reject(err)
// })
let v = answers.supportMacawAdmin.toUpperCase();
answers.supportMacawAdmin = v === "YES" || v === "Y";
return {
...context,
metadata: {
...answers
}
}
})
}).then(context => {
console.log("生成文件")
console.log(context)
//删除临时文件夹,将文件移动到目标目录下
return generator(context);
}).then(context => {
// 成功用绿色显示,给出积极的反馈
console.log(logSymbols.success, chalk.green('建立成功:)'))
console.log(chalk.green('cd ' + context.projectRoot + '\nnpm install\nnpm run dev'))
}).catch(err => {
console.error(err)
// 失败了用红色,加强提示
console.log(err);
console.error(logSymbols.error, chalk.red(`建立失败:${err.message}`))
})
}
复制代码
删除文件npm
lib/remove.jsjson
// 删除文件系统
const fs =require("fs");
const path=require("path");
function removeDir(dir) {
let files = fs.readdirSync(dir)
for(var i=0;i<files.length;i++){
let newPath = path.join(dir,files[i]);
let stat = fs.statSync(newPath)
if(stat.isDirectory()){
//若是是文件夹就递归下去
removeDir(newPath);
}else {
//删除文件
fs.unlinkSync(newPath);
}
}
fs.rmdirSync(dir)//若是文件夹是空的,就将本身删除掉
}
module.exports=removeDir;
复制代码
下载模版函数
lib/down.js测试
const download = require('download-git-repo')
const path = require("path")
const ora = require('ora')
module.exports = function (target) {
target = path.join(target || '.', '.download-temp');
return new Promise(function (res, rej) {
// 这里能够根据具体的模板地址设置下载的url,注意,若是是git,url后面的branch不能忽略
// 格式是名字/地址 后面不加 .git 可是带着 #分支
let url = '名字/模版#分支'
const spinner = ora(`正在下载项目模板,源地址:${url}`)
spinner.start();
download(url, target, { clone: false }, function (err) { // clone false 设置成false 具体设置看官网设置
if (err) {
spinner.fail()
rej(err)
}
else {
// 下载的模板存放在一个临时路径中,下载完成后,能够向下通知这个临时路径,以便后续处理
spinner.succeed()
res(target)
}
})
})
}
复制代码
注意:其中的写模版名称的时候,格式必须是 名字/模版#分支ui
插值文件
lib/generator
const rm = require('rimraf').sync //以包的形式包装rm -rf命令,用来删除文件和文件夹的,无论文件夹是否为空,均可删除
const Metalsmith = require('metalsmith') // 插值
const Handlebars = require('handlebars') // 模版
const remove = require("../lib/remove") // 删除
const fs = require("fs")
const path = require("path")
/** * 生成文件 * @param 文件的名称 */
module.exports = function (context) {
let metadata = context.metadata; // 用户自定义信息
let src = context.downloadTemp; // 暂时存放文件目录
let dest = './' + context.projectRoot; //项目的根目录
if (!src) {
return Promise.reject(new Error(`无效的source:${src}`))
}
return new Promise((resolve, reject) => {
const metalsmith = Metalsmith(process.cwd())
.metadata(metadata) // 将用户输入信息放入
.clean(false)
.source(src)
.destination(dest);
metalsmith.use((files, metalsmith, done) => {
const meta = metalsmith.metadata()
Object.keys(files).forEach(fileName => {
const t = files[fileName].contents.toString()
console.log("打印差值")
// console.log(t)
files[fileName].contents = new Buffer.from(Handlebars.compile(t)(meta),'UTF-8')
})
done()
}).build(err => {
remove(src);
err ? reject(err) : resolve(context);
})
})
}
复制代码
packjson配置
{
"name": "cli",
"version": "1.0.0",
"description": "amz脚手架1.0",
"bin": {
"amaz": "./bin/init.js"
},
"main": "./bin/macaw-hellow.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"cli"
],
"author": "amaizngli",
"license": "ISC",
"dependencies": {
"cfonts": "^2.4.5",
"commander": "^4.0.0",
"download-git-repo": "^3.0.2",
"glob": "^7.1.5",
"handlebars": "^4.5.1",
"inquirer": "^7.0.0",
"metalsmith": "^2.3.0",
"ora": "^4.0.2"
}
}
复制代码
本地测试可使用npm link连接到全局进行开发测试
其中
特效文字使用的是cFonts插件,具体使用能够去npm查看
其中插值使用Metalsmith模版,胶水式代码,进行插值插入,模版使用的Handlebars模版
开发定制能够采用git 的.gitignore文件的思路进行,可是我尚未应用。以后完善后会一并将代码放出
复制代码
欢迎一块儿踩坑