chameleon-tool
Version:
chameleon 脚手架工具
373 lines (343 loc) • 12.5 kB
JavaScript
let fs = null;
let path = null;
let tpl = null;
let inquirer = null;
let ora = null;
let shelljs = null;
let fse = null;
let chalk = null;
let glob = null;
const platformMap = require('./platform.json');
const platformTotal = Object.keys(platformMap).length;
exports.name = 'init';
exports.usage = '[command] [options]';
exports.desc = 'initialize template for chameleon project';
const cmdOptions = {
lang: 'cml',
tpl: 'html',
demo: 'blank'
}
/* istanbul ignore next */
exports.register = function (commander) {
commander
.option('-r, --root [root]', 'specify project root')
.option('-g, --lang [cml|vue]', 'specify project template default language, example: cml init project -g vue ')
.option('-t, --tpl [html|smarty]', 'specify project templateType, example: cml init project -t html')
.option('-d, --demo [blank|todo]', 'specify init project demo, example: cml init project -d todo ')
commander
.action(function (...args) {
/* eslint-disable */
fs = require('fs');
path = require('path');
tpl = require('chameleon-templates');
inquirer = require('inquirer');
ora = require('ora');
shelljs = require('shelljs');
fse = require('fs-extra');
glob = require('glob');
chalk = require('chalk');
/* eslint-disable */
// 不能删除
var options = args.pop(); // eslint-disable-line
if (options.lang === 'vue') {
cmdOptions.lang = options.lang;
}
if (options.tpl === 'smarty') {
cmdOptions.tpl = options.tpl;
}
if (~['todo'].indexOf(options.demo)) {
cmdOptions.demo = options.demo;
}
var type = args.shift();
if (type) {
initMethod(type);
} else {
let questions = [{
type: 'list',
name: 'type',
message: 'Which do you want to init?',
choices: [
'project',
'page',
'component'
]
}]
inquirer.prompt(questions).then(answers => {
initMethod(answers.type)
})
}
})
commander.on('--help', function() {
var cmd = `
Commands:
project initialize a chameleon project
page initialize a chameleon page in project
component initialize a chameleon component in project
server initialize the php server for dev
Examples:
cml init project
cml init page
cml init component
`
console.log(cmd)
})
function initMethod(type) {
if (!~['project', 'page', 'component'].indexOf(type)) {
cml.log.error(`init type must in ['project', 'page', 'component']`);
return;
}
if (type !== 'project') {
cml.utils.checkProjectConfig();
}
switch (type) {
case 'project':
initProject();
break;
case 'page':
initPage();
break;
case 'component':
initComponent();
break;
default: break;
}
}
function initProject() {
let questions = [
{
type: 'input',
name: 'projectName',
message: 'please input the name of project:',
validate: function (value) {
if (!value) {
return 'project name can not be empty'
}
let nameReg = /^[0-9a-zA-Z_\-\.]+$/;
if (!nameReg.test(value)) {
return `project name characters must only in [0-9a-zA-Z_\-\.]`
}
if (fs.existsSync(value)) {
return 'There is already a project with the same name in the current directory,please change one'
}
return true;
}
}
]
inquirer.prompt(questions).then(answers => {
let {projectName } = answers;
let platforms = ['h5', 'weex', '微信小程序','支付宝小程序','百度小程序','qq小程序'];
let templateType = cmdOptions.tpl;
let templateLang = cmdOptions.lang;
platforms = platforms.map(item => platformMap[item]);
let pagedir = path.join(cml.projectRoot, projectName);
let projectMap = {
blank: tpl.blankDemoTpl,
todo: tpl.todoDemoTpl
}
let projectTpl = projectMap[cmdOptions.demo];
fse.copySync(projectTpl, pagedir);
// package.json文件中的name需要更改
var packagePath = path.join(pagedir, 'package.json');
let packageContent = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
packageContent.name = projectName;
fs.writeFileSync(packagePath, JSON.stringify(packageContent, '', 2))
// 修改配置文件
let configFile = path.join(pagedir, 'chameleon.config.js');
let content = fs.readFileSync(configFile, {encoding: 'utf-8'}).replace(/templateLang\s*:\s*["'](cml|vue)["']/g, function() {
return `templateLang: "${templateLang}"`
})
.replace(/templateType\s*:\s*["'](smarty|html)["']/g, function() {
return `templateType: "${templateType}"`
})
.replace(/platforms\s*:\s*\[(.+?)\]/g, function() {
return `platforms: ${JSON.stringify(platforms)}`
})
fs.writeFileSync(configFile, content);
var npmignore = path.join(pagedir, '.npmignore');
// npm包中的.gitignore变成了.npmignore
if (cml.utils.isFile(npmignore)) {
fse.moveSync(npmignore, path.join(pagedir, '.gitignore'));
}
// 处理平台文件
if (platforms.length < platformTotal) {
let { ignorePlatform, ignoreReg } = exports.getIgnorePlatform(platforms);
let currentPlatforms = platforms;
let currentReg = new RegExp(`\\.(${currentPlatforms.join('|')})\\.cml$`);
// 处理.cml文件
let cmlFilePath = path.join(pagedir, '**/*.cml');
glob.sync(cmlFilePath).forEach(cfp => {
// 删除忽略平台的文件
if (ignoreReg.test(cfp)) {
fse.removeSync(cfp);
} else {
if (cfp.endsWith('.cml') && !currentReg.test(cfp)) {
let content = fs.readFileSync(cfp, {encoding: 'utf-8'});
let splitContent = cml.utils.getScriptPart({content, cmlType: 'json'});
let jsonObj = JSON.parse(splitContent.content);
ignorePlatform.forEach(item => {
delete jsonObj[item];
});
content = content.replace(splitContent.content, `\n${JSON.stringify(jsonObj, null, 2)}\n`);
fs.writeFileSync(cfp, content);
}
}
})
// 处理 .interface文件
let interfaceFilePath = path.join(pagedir, '**/*.interface');
glob.sync(interfaceFilePath).forEach(ifp => {
content = fs.readFileSync(ifp, {encoding: 'utf-8'});
ignorePlatform.forEach(item => {
content = cml.utils.deleteScript({
content,
cmlType: item
});
});
fs.writeFileSync(ifp, content);
})
}
const installSpinner = ora('npm installing...').start()
const install = shelljs.exec(`cd ${projectName} && npm install`, {
silent: true
})
if (install.code === 0) {
installSpinner.color = 'green'
installSpinner.succeed('install success')
console.log(chalk.yellow('To get started:\n'))
console.log(chalk.yellow(`cd ${projectName}\n`))
console.log(chalk.yellow(`cml dev\n`))
// installSpinner.succeed(`Please exec: cd ${projectName}; cml dev`)
// console.log(`${install.stderr}${install.stdout}`)
} else {
installSpinner.color = 'red'
installSpinner.fail(chalk.red('install fail, please exec npm install youself'))
console.log(`${install.stderr}${install.stdout}`)
}
})
}
function initPage() {
let questions = [{
type: 'input',
name: 'pageName',
message: 'please input page name:',
validate: function (value) {
if (!value) {
return 'page name can not be empty'
}
if (fs.existsSync(path.join(cml.projectRoot, `src/pages/${value}`))) {
return 'There is already a page with the same name in the current project, please change one'
}
return true;
}
}]
inquirer.prompt(questions).then(answers => {
let {pageName} = answers;
let pagedir = path.join(cml.projectRoot, `src/pages/${pageName}`)
let UpperName = exports.toUpperCase(pageName);
fs.readdirSync(tpl.pageTpl).forEach(fileName => {
let filePath = path.join(tpl.pageTpl, fileName);
let newFilePath = path.join(pagedir, fileName.replace('index', pageName));
let content = fs.readFileSync(filePath, {encoding: 'utf-8'}).replace(/Replace/ig, UpperName);
if (cml.config.get().templateLang === 'vue') {
content = content.replace(/<template>/, '<template lang="vue">')
}
content = exports.contentIgnoreHanle(content, 'cml');
fse.outputFile(newFilePath, content);
})
cml.log.notice(`init page ${pageName} success!`)
})
}
function initComponent() {
let questions = [{
type: 'list',
name: 'componentType',
message: 'please select the type of component',
choices: [
'Normal component',
'Polymorphic component',
'Polymorphic function'
]
}, {
type: 'input',
name: 'componentName',
message: 'please input component name:',
validate: function (value) {
if (!value) {
return 'component name can not be empty'
}
if (fs.existsSync(path.join(cml.projectRoot, `src/components/${value}`))) {
return 'There is already a component with the same name in the current project, please change one'
}
return true;
}
}]
inquirer.prompt(questions).then(answers => {
let {componentName, componentType} = answers;
let comdir = path.join(cml.projectRoot, `src/components/${componentName}`);
let comPathMap = {
'Normal component': 'component',
'Polymorphic component': 'interface-component',
'Polymorphic function': 'interface-js'
}
let tplPath = path.join(tpl.componentTpl, comPathMap[componentType]);
let UpperName = exports.toUpperCase(componentName);
let { ignoreReg } = exports.getIgnorePlatform();
fs.readdirSync(tplPath).forEach(fileName => {
if (ignoreReg.test(fileName)) {
return;
}
let filePath = path.join(tplPath, fileName);
let newFilePath = path.join(comdir, fileName.replace('index', componentName));
let content = fs.readFileSync(filePath, {encoding: 'utf-8'}).replace(/Replace/ig, UpperName);
if (cml.config.get().templateLang === 'vue') {
content = content.replace(/<template>/, '<template lang="vue">')
}
if (newFilePath.endsWith('.cml')) {
content = exports.contentIgnoreHanle(content, 'cml');
}
if (newFilePath.endsWith('.interface')) {
content = exports.contentIgnoreHanle(content, 'interface');
}
fse.outputFile(newFilePath, content);
})
cml.log.notice(`init component ${componentName} success!`)
})
}
}
exports.toUpperCase = function (content) {
content = content[0].toUpperCase() + content.slice(1);
return content.replace(/-(\w)/ig, function(m, s1) {
return s1.toUpperCase()
})
}
exports.getIgnorePlatform = function (platforms) {
platforms = platforms || cml.config.get().platforms.map(item => item.trim());
let platformValues = Object.values(platformMap);
let ignorePlatform = platformValues.filter(item => platforms.indexOf(item) === -1);
let ignoreReg = new RegExp(`\\.(${ignorePlatform.join('|')})\\.cml$`);
return {
ignorePlatform,
ignoreReg
}
}
// 处理文件
exports.contentIgnoreHanle = function (content, type) {
let { ignorePlatform } = exports.getIgnorePlatform();
if (type === 'interface') {
ignorePlatform.forEach(item => {
content = cml.utils.deleteScript({
content,
cmlType: item
});
});
} else if (type === 'cml') {
let splitContent = cml.utils.getScriptPart({content, cmlType: 'json'});
if (splitContent) {
let jsonObj = JSON.parse(splitContent.content);
ignorePlatform.forEach(item => {
delete jsonObj[item];
});
content = content.replace(splitContent.content, `\n${JSON.stringify(jsonObj, null, 2)}\n`);
}
}
return content;
}