207 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| const prompt = require('inquirer').createPromptModule()
 | |
| const shellJs = require('shelljs')
 | |
| const path = require('path')
 | |
| const bluebird = require('bluebird')
 | |
| const ora = require('ora')
 | |
| const moment = require('moment')
 | |
| const userName = require('git-user-name')
 | |
| const userEmail = require('git-user-email')
 | |
| const fs = bluebird.promisifyAll(require('fs'))
 | |
| 
 | |
| // path
 | |
| const CWD = process.cwd()
 | |
| const COMPONENTS_PATH = path.resolve(CWD, './components')
 | |
| const EXPECT_SHELL = path.resolve(CWD, './build/template.exp')
 | |
| const DEMO_INDEX_PATH = path.resolve(CWD, './examples/demo-index.js')
 | |
| const TYPES = path.resolve(CWD, './types/index.d.ts')
 | |
| const COMPONENT_INDEX = path.resolve(CWD, './components/index.js')
 | |
| const COMPONENT_JSON = path.resolve(CWD, './examples/components.json')
 | |
| 
 | |
| function init (answers) {
 | |
|   return Promise.resolve(answers)
 | |
|     .then(checkNoRepeat)
 | |
|     .then(create)
 | |
|     .then(sync)
 | |
|     .catch(err => {
 | |
|       console.warn(err)
 | |
|     })
 | |
| }
 | |
| 
 | |
| function checkNoRepeat(answers) {
 | |
|   return fs.readdirAsync(COMPONENTS_PATH)
 | |
|     .then(files => {
 | |
|       return Promise.all(files.map(file => checkFile(COMPONENTS_PATH, file, answers.componentName))).then(() => answers)
 | |
|     })
 | |
| }
 | |
| 
 | |
| function upperFirst(str) {
 | |
|   return String.prototype.replace.call(str, /^\w/, function (match) {
 | |
|     return String.prototype.toUpperCase.call(match)
 | |
|   })
 | |
| }
 | |
| 
 | |
| function changeKebabToCamel(str) {
 | |
|   return String.prototype.replace.call(str, /\-(\w)/g, function(match, p1) {
 | |
|     return String.prototype.toUpperCase.call(p1)
 | |
|   })
 | |
| }
 | |
| 
 | |
| function sync(answers) {
 | |
|   return Promise.all([syncToComponentJson(answers), syncToExample(answers), syncToIndex(answers), syncToTypes(answers)]).then(() =>answers)
 | |
| }
 | |
| 
 | |
| function syncToExample(answers) {
 | |
|   fs.readFileAsync(DEMO_INDEX_PATH, 'utf8')
 | |
|     .then(str => {
 | |
|       return compile(answers, str)
 | |
|     })
 | |
|     .then(str => fs.writeFileAsync(DEMO_INDEX_PATH, str, 'utf8'))
 | |
|     .then(() => answers)
 | |
| }
 | |
| function syncToTypes(answers) {
 | |
|   fs.readFileAsync(TYPES, 'utf8')
 | |
|     .then(str => {
 | |
|       return compile(answers, str)
 | |
|     })
 | |
|     .then(str => fs.writeFileAsync(TYPES, str, 'utf8'))
 | |
|     .then(() => answers)
 | |
| }
 | |
| 
 | |
| function syncToIndex(answers) {
 | |
|   /* 同步components/index文件 */
 | |
|   fs.readFileAsync(COMPONENT_INDEX, 'utf8')
 | |
|     .then(str => {
 | |
|       return compile(answers, str)
 | |
|     })
 | |
|     .then(str => fs.writeFileAsync(COMPONENT_INDEX, str, 'utf8'))
 | |
|     .then(() => answers)
 | |
| }
 | |
| 
 | |
| function syncToComponentJson(answers) {
 | |
|   const json = require(COMPONENT_JSON)
 | |
|   let index = json.findIndex(item => item.category === answers.componentType)
 | |
|   if (index === -1) {
 | |
|     index = json.length
 | |
|     json.push({
 | |
|       category: answers.componentType,
 | |
|       list: [],
 | |
|     })
 | |
|   }
 | |
|   json[index].list.push({
 | |
|     name: answers.componentNameUpper,
 | |
|     path: `/${answers.componentName}`,
 | |
|     icon: answers.componentName,
 | |
|     text: answers.componentCnName,
 | |
|   })
 | |
|   Array.prototype.forEach.call(json, element => {
 | |
|     Array.prototype.sort.call(element.list, function(a, b) {
 | |
|       return a.name > b.name ? 1 : -1
 | |
|     })
 | |
|   })
 | |
|   Array.prototype.sort.call(json, (a, b) => {
 | |
|     return a.category > b.category ? 1: -1
 | |
|   })
 | |
|   return fs.writeFileAsync(COMPONENT_JSON, JSON.stringify(json), 'utf8')
 | |
|             .then(answers => answers)
 | |
| }
 | |
| 
 | |
| function compile(metaData, fileStr) {
 | |
|   return String.prototype.replace.call(fileStr, /\/\*.*@init<%(.*)%>.*\*\//g, function (match, p1) {
 | |
|     return (String.prototype.replace.call(p1, /\${(\w*)}/g, function (innMatch, innP1) {
 | |
|       return metaData[innP1]
 | |
|     })) + '\r' +match
 | |
|   })
 | |
| }
 | |
| 
 | |
| function checkFile(dir, file, name) {
 | |
|   const filePath = path.resolve(dir, `./${file}`)
 | |
|   return fs.statAsync(filePath)
 | |
|     .then(stat => {
 | |
|       if (stat.isDirectory()) {
 | |
|         if (name === file) {
 | |
|           return Promise.reject(`组件库中已经存在名为${name}的组件!请仔细核对后重新创建`)
 | |
|         }
 | |
|       }
 | |
|       return
 | |
|     })
 | |
| }
 | |
| 
 | |
| function exec (command, argvs) {
 | |
|   const spinner = ora('Loading...').start()
 | |
|   const result = shellJs.exec(`${command} ${argvs.map(item => `\'${item}\'`).join(' ')} >> /dev/null`)
 | |
|   spinner.succeed(['执行完毕'])
 | |
|   return result
 | |
| }
 | |
| 
 | |
| function create(answers) {
 | |
|   exec('expect', [EXPECT_SHELL, answers.componentCnName, answers.componentName, answers.componentType, answers.componentDesc, answers.author, answers.time, COMPONENTS_PATH])
 | |
|   return answers
 | |
| }
 | |
| 
 | |
| function getUserInfo() {
 | |
|   let user = userName()
 | |
|   if (!user) {
 | |
|     user = 'anonymous'
 | |
|   }
 | |
|   const email = userEmail()
 | |
|   if (email) {
 | |
|     user += ` <${email}>`
 | |
|   }
 | |
|   return user
 | |
| }
 | |
| 
 | |
| function launch() {
 | |
|   return prompt([
 | |
|     {
 | |
|       type: 'input',
 | |
|       name: 'componentName',
 | |
|       message: '请输入要创建的组件名称(kebab-case):',
 | |
|       validate: function (str) {
 | |
|         return /^[a-z][a-z|-]*[a-z]$/.test(str)
 | |
|       }
 | |
|     },
 | |
|     {
 | |
|       type: 'input',
 | |
|       name: 'componentCnName',
 | |
|       message: '请输入要创建的组件中文名称(中文):',
 | |
|     },  
 | |
|     {
 | |
|       type: 'list',
 | |
|       choices: [
 | |
|         "basic",
 | |
|         "feedback",
 | |
|         "form",
 | |
|         "business",
 | |
|         "gesture"
 | |
|       ],
 | |
|       name: 'componentType',
 | |
|       message: '组件类型',
 | |
|     },
 | |
|     {
 | |
|       type: 'input',
 | |
|       name: 'componentDesc',
 | |
|       message: '组件描述',
 | |
|     },
 | |
|     {
 | |
|       type: 'input',
 | |
|       name: 'author',
 | |
|       message: '作者',
 | |
|       default: getUserInfo(),
 | |
|     },
 | |
|     {
 | |
|       type: 'input',
 | |
|       name: 'time',
 | |
|       message: 'time',
 | |
|       default: moment().format('YYYY年MM月DD日')
 | |
|     }
 | |
|   ])
 | |
|     .then(answers => {
 | |
|       answers = Object.assign(answers, {
 | |
|         componentNameUpper: upperFirst(changeKebabToCamel(answers.componentName))
 | |
|       })
 | |
|       return init(answers)
 | |
|     })
 | |
| }
 | |
| 
 | |
| launch()
 |