Skip to content

Commit

Permalink
feat: init project
Browse files Browse the repository at this point in the history
  • Loading branch information
lipeizhong committed Nov 17, 2019
0 parents commit 8de12b8
Show file tree
Hide file tree
Showing 29 changed files with 1,390 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
104 changes: 104 additions & 0 deletions bin/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env node

// 检测node版本相关依赖
const chalk = require('chalk')
const semver = require('semver')
const requiredVersion = require('../package.json').engines.node

// 检测node版本函数
/**
*
* @param {*} wanted
* @param {*} id
*/
function checkNodeVersion (wanted, id) {
if (!semver.satisfies(process.version, wanted)) {
console.log(chalk.red(
'你是用的Node版本号为: ' + process.version + ', 但 ' + id +
' 需运行在 ' + wanted + '.\n请升级你的Node版本'
))
process.exit(1)
}
}

checkNodeVersion(requiredVersion, 'awesome-test-cli')

if (semver.satisfies(process.version, '9.x')) {
console.log(chalk.red(
`你是用的Node版本是 ${process.version}.\n` +
`强烈建议你使用最新 LTS 版本`
))
}

// 开始处理命令
const program = require('commander')
const minimist = require('minimist')

program
.version(require('../package').version)
.usage('<command> [options]')

// 创建命令
program
.command('create <app-name>')
.description('create a new project')
.option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
.option('-d, --default', 'Skip prompts and use default preset')
.action((name, cmd) => {
const options = cleanArgs(cmd)
if (minimist(process.argv.slice(3))._.length > 1) {
console.log(chalk.yellow('\n ⚠️ 检测到您输入了多个名称,将以第一个参数为项目名,舍弃后续参数哦'))
}
require('../lib/create')(name, options)
})

// 创建页面命令
program
.command('page <page-name>')
.description('create a new page')
.option('-f, --force', 'Overwrite target directory if it exists')
.action((name, cmd) => {
const options = cleanArgs(cmd)
require('../lib/page')(name, options)
})

program
.arguments('<command>')
.action((cmd) => {
program.outputHelp()
console.log(` ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`))
console.log()
// suggestCommands(cmd)
})


// 自定义错误提示信息
const enhanceErrorMessages = require('../lib/utils/enhanceErrorMessages')
// 缺少参数的错误提示
enhanceErrorMessages('missingArgument', argName => {
return `缺少必要参数 ${chalk.yellow(`<${argName}>`)}.`
})

// 调用
program.parse(process.argv)

if (!process.argv.slice(2).length) {
program.outputHelp()
}

function camelize (str) {
return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : '')
}

// 获取参数
function cleanArgs (cmd) {
const args = {}
cmd.options.forEach(o => {
const key = camelize(o.long.replace(/^--/, ''))
// 如果没有传递option或者有与之相同的命令,则不被拷贝
if (typeof cmd[key] !== 'function' && typeof cmd[key] !== 'undefined') {
args[key] = cmd[key]
}
})
return args
}
184 changes: 184 additions & 0 deletions lib/Creator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
const chalk = require('chalk')
const execa = require('execa')
const inquirer = require('inquirer')
const EventEmitter = require('events')
const loadRemotePreset = require('../lib/utils/loadRemotePreset')
const writeFileTree = require('../lib/utils/writeFileTree')
const copyFile = require('../lib/utils/copyFile')
const generateReadme = require('../lib/utils/generateReadme')
const {installDeps} = require('../lib/utils/installDeps')

const {
defaults
} = require('../lib/options')

const {
log,
error,
hasYarn,
hasGit,
hasProjectGit,
logWithSpinner,
clearConsole,
stopSpinner,
exit
} = require('../lib/utils/common')

module.exports = class Creator extends EventEmitter {
constructor(name, context) {
super()

this.name = name
this.context = context

this.run = this.run.bind(this)
}

async create(cliOptions = {}, preset = null) {
const { run, name, context } = this

if (cliOptions.preset) {
// awesome-test create foo --preset mobx
preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone)
} else {
preset = await this.resolvePreset(defaults.presets.default, cliOptions.clone)
}

await clearConsole()
log(chalk.blue.bold(`Awesome-test CLI v${require('../package.json').version}`))
logWithSpinner(`✨`, `正在创建项目 ${chalk.yellow(context)}.`)
this.emit('creation', { event: 'creating' })

stopSpinner()
// 设置文件名,版本号等
const { pkgVers, pkgDes } = await inquirer.prompt([
{
name: 'pkgVers',
message: `请输入项目版本号`,
default: '1.0.0',
},
{
name: 'pkgDes',
message: `请输入项目简介`,
default: 'project created by awesome-test-cli',
}
])

// 将下载的临时文件拷贝到项目中
const pkgJson = await copyFile(preset.tmpdir, preset.targetDir)

const pkg = Object.assign(pkgJson, {
version: pkgVers,
description: pkgDes
})

// write package.json
log()
logWithSpinner('📄', `生成 ${chalk.yellow('package.json')} 等模板文件`)
await writeFileTree(context, {
'package.json': JSON.stringify(pkg, null, 2)
})

// 包管理
const packageManager = (
(hasYarn() ? 'yarn' : null) ||
(hasPnpm3OrLater() ? 'pnpm' : 'npm')
)
await writeFileTree(context, {
'README.md': generateReadme(pkg, packageManager)
})

const shouldInitGit = this.shouldInitGit(cliOptions)
if (shouldInitGit) {
logWithSpinner(`🗃`, `初始化Git仓库`)
this.emit('creation', { event: 'git-init' })
await run('git init')
}

// 安装依赖
stopSpinner()
log()
logWithSpinner(`⚙`, `安装依赖`)
// log(`⚙ 安装依赖中,请稍等...`)

await installDeps(context, packageManager, cliOptions.registry)

// commit initial state
let gitCommitFailed = false
if (shouldInitGit) {
await run('git add -A')
const msg = typeof cliOptions.git === 'string' ? cliOptions.git : 'init'
try {
await run('git', ['commit', '-m', msg])
} catch (e) {
gitCommitFailed = true
}
}

// log instructions
stopSpinner()
log()
log(`🎉 项目创建成功 ${chalk.yellow(name)}.`)
if (!cliOptions.skipGetStarted) {
log(
`👉 请按如下命令,开始愉快开发吧!\n\n` +
(this.context === process.cwd() ? `` : chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`)) +
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn start' : packageManager === 'pnpm' ? 'pnpm run start' : 'npm start'}`)
)
}
log()
this.emit('creation', { event: 'done' })

if (gitCommitFailed) {
warn(
`因您的git username或email配置不正确,无法为您初始化git commit,\n` +
`请稍后自行git commit。\n`
)
}
}

async resolvePreset (name, clone) {
let preset
logWithSpinner(`Fetching remote preset ${chalk.cyan(name)}...`)
this.emit('creation', { event: 'fetch-remote-preset' })
try {
preset = await loadRemotePreset(name, this.context, clone)
stopSpinner()
} catch (e) {
stopSpinner()
error(`Failed fetching remote preset ${chalk.cyan(name)}:`)
throw e
}

// 默认使用default参数
if (name === 'default' && !preset) {
preset = defaults.presets.default
}
if (!preset) {
error(`preset "${name}" not found.`)
exit(1)
}
return preset
}

run (command, args) {
if (!args) { [command, ...args] = command.split(/\s+/) }
return execa(command, args, { cwd: this.context })
}

shouldInitGit (cliOptions) {
if (!hasGit()) {
return false
}
// --git
if (cliOptions.forceGit) {
return true
}
// --no-git
if (cliOptions.git === false || cliOptions.git === 'false') {
return false
}
// default: true unless already in a git repo
return !hasProjectGit(this.context)
}
}
51 changes: 51 additions & 0 deletions lib/PageCreator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const chalk = require('chalk')
const EventEmitter = require('events')
const fs = require('fs-extra')

const generatePage = require('./utils/generatePage')


const {
log,
error,
logWithSpinner,
clearConsole,
stopSpinner,
exit
} = require('../lib/utils/common')

module.exports = class PageCreator extends EventEmitter {
constructor(name, context) {
super()

this.name = name
this.context = context
}

async create(cliOptions = {}) {
const fileNameObj = this.getName()
const {context} = this
await clearConsole()
log(chalk.blue.bold(`Awesome-test CLI v${require('../package.json').version}`))
logWithSpinner(`✨`, `正在创建页面...`)
// 创建文件夹
await fs.mkdir(context, { recursive: true })
this.emit('creation', { event: 'creating' })

stopSpinner()

console.log(context)
await generatePage(context, fileNameObj)
}

getName() {
const originName = this.name
const tailName = originName.slice(1)
const upperName = originName.charAt(0).toUpperCase() + tailName
const lowerName = originName.charAt(0).toLowerCase() + tailName
return {
upperName,
lowerName
}
}
}
Loading

0 comments on commit 8de12b8

Please sign in to comment.