在工做中咱們會用到不少便捷的腳手架工具,好比Vue的vue-cli,React的create-react-app等。極大的提升了咱們的工做效率,那麼今天咱們就來學學怎麼製做一款自用的前端腳手架。前端
在根目錄下新建
bin/luchx.js
文件,並添加如下代碼vue
#!/usr/bin/env node
// Check node version before requiring/doing anything else
// The user may be on a very old node version
const chalk = require('chalk')
const program = require('commander')
program
.version(require('../package').version)
.usage('<command> [options]')
program.parse(process.argv)
if (!process.argv.slice(2).length) {
program.outputHelp()
}
複製代碼
首先文件第一行表示該文件運行於node環境,接着引入commander。最後的program.parse方法用於解析命令行中傳入的參數。node
command命令有兩種用法,官方示例以下:react
// Command implemented using action handler (description is supplied separately to `.command`)
// Returns new command for configuring.
program
.command('clone <source> [destination]')
.description('clone a repository into a newly created directory')
.action((source, destination) => {
console.log('clone command called');
});
// Command implemented using separate executable file (description is second parameter to `.command`)
// Returns top-level command for adding more commands.
program
.command('start <service>', 'start named service')
.command('stop [service]', 'stop named service, or all if no name supplied');
複製代碼
其中參數對應的<>, [ ]分別表明必填和選填。這裏咱們使用第一種,添加以下代碼:git
program
.command('create <app-name>')
.description(' Create a project with template already created.')
.action((name, cmd) => {
require('../lib/create')(name)
})
複製代碼
// add some useful info on help
program.on('--help', () => {
console.log()
console.log(` Run ${chalk.cyan('luchx <command> --help')} for detailed usage of given command.`)
console.log()
})
複製代碼
執行結果github
module.exports = async function create (projectName) { }
複製代碼
const path = require('path')
const fs = require('fs-extra')
const inquirer = require('inquirer')
const chalk = require('chalk')
const validateProjectName = require('validate-npm-package-name')
module.exports = async function create (projectName) {
const cwd = process.cwd()
const targetDir = path.resolve(cwd, projectName)
const name = path.relative(cwd, projectName)
const result = validateProjectName(name)
if (!result.validForNewPackages) {
console.error(chalk.red(`Invalid project name: "${name}"`))
result.errors && result.errors.forEach(err => {
console.error(chalk.red.dim('Error: ' + err))
})
result.warnings && result.warnings.forEach(warn => {
console.error(chalk.red.dim('Warning: ' + warn))
})
process.exit(1)
}
if (fs.existsSync(targetDir)) {
const { action } = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
choices: [
{ name: 'Overwrite', value: 'overwrite' },
{ name: 'Cancel', value: false }
]
}
])
if (!action) {
return
} else if (action === 'overwrite') {
console.log(`\nRemoving ${chalk.cyan(targetDir)}...`)
await fs.remove(targetDir)
}
}
// ...
}
複製代碼
const inquirer = require('inquirer')
module.exports = async function create (projectName) {
// ...
const { bolierplateType, author, description, version } = await inquirer.prompt([
{
name: 'bolierplateType',
type: 'list',
default: 'vue',
choices: [
{
name: 'Vue',
value: 'vue'
},
{
name: 'React',
value: 'react'
}
],
message: 'Select the boilerplate type.'
}, {
type: 'input',
name: 'description',
message: 'Please input your project description.',
default: 'description',
validate (val) {
return true
},
transformer (val) {
return val
}
}, {
type: 'input',
name: 'author',
message: 'Please input your author name.',
default: 'author',
validate (val) {
return true
},
transformer (val) {
return val
}
}, {
type: 'input',
name: 'version',
message: 'Please input your version.',
default: '0.0.1',
validate (val) {
return true
},
transformer (val) {
return val
}
}
])
// ...
}
複製代碼
const download = require('download-git-repo')
module.exports = function downloadFromRemote (url, name) {
return new Promise((resolve, reject) => {
download(`direct:${url}`, name, { clone: true }, function (err) {
if (err) {
reject(err)
return
}
resolve()
})
})
}
複製代碼
const fs = require('fs-extra')
const chalk = require('chalk')
const logSymbols = require('log-symbols')
const downloadFromRemote = require('../lib/downloadFromRemote')
module.exports = async function create (projectName) {
// ...
downloadFromRemote(remoteUrl, projectName).then(res => {
fs.readFile(`./${projectName}/package.json`, 'utf8', function (err, data) {
if (err) {
spinner.stop()
console.error(err)
return
}
const packageJson = JSON.parse(data)
packageJson.name = projectName
packageJson.description = description
packageJson.author = author
packageJson.version = version
var updatePackageJson = JSON.stringify(packageJson, null, 2)
fs.writeFile(`./${projectName}/package.json`, updatePackageJson, 'utf8', function (err) {
spinner.stop()
if (err) {
console.error(err)
} else {
console.log(logSymbols.success, chalk.green(`Successfully created project template of ${bolierplateType}\n`))
console.log(`${chalk.grey(`cd ${projectName}`)}\n${chalk.grey('yarn install')}\n${chalk.grey('yarn serve')}\n`)
}
process.exit()
})
})
}).catch((err) => {
console.log(logSymbols.error, err)
spinner.fail(chalk.red('Sorry, it must be something error,please check it out. \n'))
process.exit(-1)
})
}
複製代碼
本項目沒有發佈到npm上,僅做學習研究之用,能夠本身拉取項目而後執行npm link,在本地體驗。爲了能夠全局使用,咱們須要在package.json裏面設置一下,這樣就能夠執行luchx命令開頭的指令了。vue-cli
"bin": {
"luchx": "bin/luchx.js"
},
複製代碼
以上完整的代碼能夠訪問github查看獲取npm