先看下效果,如今腳手架還不完善只是完成了初始化功能,具體功能還得等以後慢慢完善 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文件的思路進行,可是我尚未應用。以後完善後會一併將代碼放出
複製代碼
歡迎一塊兒踩坑