共计 8729 个字符,预计需要花费 22 分钟才能阅读完成。
前言
前端工程化是人们经常提到的货色,其目标基本上都是为了进步开发效率,降低成本以及保证质量。而脚手架工具则是前端工程化中很重要的环节,一个好用的 web 工程通用脚手架工具能够在很大水平上做到下面所提到的。
咱们不仅要会用市面上很多成熟的脚手架,还要能依据理论的我的项目状况,去实现一些适宜本人我的项目的脚手架。本文就将和大家一起实现一个根底的通用脚手架工具,后续就能够随便拓展了。
我的项目构造
我的项目的整体构造如下,前面咱们会一步步编写代码,最终实现整个脚手架工具。
xman-cli
├─ bin
│ └─ xman.js
├─ command
│ ├─ add.js
│ ├─ delete.js
│ ├─ init.js
│ └─ list.js
├─ lib
│ ├─ remove.js
│ └─ update.js
├─ .gitignore
├─ LICENSE
├─ package.json
├─ README.md
└─ templates.json
具体实现
初始化我的项目
能够用 npm init
进行创立,也能够依据上面列出的 package.json
进行批改。
{
"name": "xman-cli",
"version": "1.0.0",
"description": "web 通用脚手架工具",
"bin": {"xman": "bin/xman.js"},
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
"repository": {
"type": "git",
"url": "https://github.com/XmanLin/xman-cli.git"
},
"keywords": ["cli"],
"author": "xmanlin",
"license": "MIT",
"bugs": {"url": "https://github.com/XmanLin/xman-cli/issues"},
"homepage": "https://github.com/XmanLin/xman-cli#readme",
"dependencies": {
"chalk": "^4.1.2",
"clear": "^0.1.0",
"clui": "^0.3.6",
"commander": "^8.2.0",
"figlet": "^1.5.2",
"handlebars": "^4.7.7",
"inquirer": "^8.1.5",
"update-notifier": "^5.1.0"
}
}
这里提两点:
bin
字段:能够自定义脚手架工具的命令,例如下面的xman
,而xman
前面的就是命令的执行脚本。- 我的项目中的依赖前面会用到,用到的时候会介绍。
编写 bin/xman.js
要使得脚本可执行,就须要在 xman.js 的最顶部增加以下代码:
#!/usr/bin/env node
编写好后引入 commander(node.js 命令行界面的残缺解决方案),能够点击链接或者到 npm 官网查看具体 API 的用法,前面一些列的相干依赖都一样。
#!/usr/bin/env node
const {program} = require('commander');
此时,咱们能够定义以后脚手架的版本以及版本查看的命令。
#!/usr/bin/env node
const {program} = require('commander');
program
.version(require('../package').version, '-v, --version');
program.parse(process.argv); // 这里是必要的
if (!program.args.length) {program.help();
}
在以后 xman-cli 目录下,执行 npm link
后,就能够在本地对脚手架工具进行调试了。
而后在当前目录下执行
xman -v
就能看到咱们定义的版本号了,也证实脚手架工具初步搭建胜利。
利用脚手架工具初始化搭建我的项目
这个是脚手架工具的最外围的性能点,通过脚手架工具命令疾速抉择拉取,当时在 git 仓库中构建好根底我的项目模板。咱们能够依据理论需要,自定义我的项目模板,并在我的项目中制订相干的开发标准和约定。
首先在 git 上搭建好本人的根底我的项目,这里须要留神的是:在搭建根底我的项目模板的时候,我的项目的 package.json
中的 name
字段要写成上面这种模式:
{"name": "{{name}}",
}
至于为什么要这样写,前面的代码中会有体现。
而后在根目录下创立 templates.json
:
{
"templates": {
"xman-manage": {
"url": "https://github.com/XmanLin/xman-manage.git",
"branch": "master"
},
"xman-web": {
"url": "https://github.com/XmanLin/xman-web.git",
"branch": "master"
}
}
}
以上 xman-manage
和 xman-web
别离代表不同的我的项目,能够依据理论状况自定义,url
为根底我的项目的地址,branch
为主动拉取时的分支。
接着在 command 文件夹(这个文件夹下会放后续一些列命令的实现逻辑)下创立init.js
:
const fs = require('fs'); // node.js 文件系统
const exec = require('child_process').exec; // 启动一个新过程,用来执行命令
const config = require('../templates'); // 引入定义好的根底我的项目列表
const chalk = require('chalk'); // 给提醒语增加色调
const clear = require('clear'); // 革除命令
const figlet = require('figlet'); // 能够用来定制 CLI 执行时的头部
const inquirer = require('inquirer'); // 提供交互式命令行
const handlebars = require('handlebars'); // 一种简略的模板语言,能够自行百度一下
const clui = require('clui'); // 提供期待的状态
const Spinner = clui.Spinner;
const status = new Spinner('正在下载...');
const removeDir = require('../lib/remove'); // 用来删除文件和文件夹
module.exports = () => {
let gitUrl;
let branch;
clear();
// 定制酷炫 CLI 头部
console.log(chalk.yellow(figlet.textSync('XMAN-CLI', {horizontalLayout: 'full'})));
inquirer.prompt([
{
name: 'templateName',
type: 'list',
message: '请抉择你须要的我的项目模板:',
choices: Object.keys(config.templates),
},
{
name: 'projectName',
type: 'input',
message: '请输出你的项目名称:',
validate: function (value) {if (value.length) {return true;} else {return '请输出你的项目名称';}
},
}
])
.then(answers => {gitUrl = config.templates[answers.templateName].url;
branch = config.templates[answers.templateName].branch;
// 执行的命令,从 git 上克隆想要的我的项目模板
let cmdStr = `git clone ${gitUrl} ${answers.projectName} && cd ${answers.projectName} && git checkout ${branch}`;
status.start();
exec(cmdStr, (error, stdou, stderr) => {status.stop();
if (error) {console.log('产生了一个谬误:', chalk.red(JSON.stringify(error)));
process.exit();}
const meta = {name: answers.projectName};
// 这里须要留神:我的项目模板的 package.json 中的 name 要写成 "name": "{{name}}" 的模式
const content = fs.readFileSync(`${answers.projectName}/package.json`).toString();
// 利用 handlebars.compile 来进行 {{name}} 的填写
const result = handlebars.compile(content)(meta);
fs.writeFileSync(`${answers.projectName}/package.json`, result);
// 删除模板自带的 .git 文件
removeDir(`${answers.projectName}/.git`);
console.log(chalk.green('\n √ 下载实现!'));
console.log(chalk.cyan(`\n cd ${answers.projectName} && yarn \n`));
process.exit();})
})
.catch(error => {console.log(error);
console.log('产生了一个谬误:', chalk.red(JSON.stringify(error)));
process.exit();});
}
lib/remove.js
const fs = require('fs');
let 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); // 获取 fs.Stats 对象
if (stat.isDirectory()) {
// 判断是否是文件夹,如果是文件夹就递归上来
removeDir(newPath);
} else {
// 删除文件
fs.unlinkSync(newPath);
}
}
fs.rmdirSync(dir); // 如果文件夹是空的,就将本人删除掉
};
module.exports = removeDir;
最初持续在 xman.js
定义命令:
#!/usr/bin/env node
const {program} = require('commander');
...
program
.command('init')
.description('Generate a new project')
.alias('i')
.action(() => {require('../command/init')()});
...
轻易再找个文件夹下执行定义好的命令:
xman i
关上咱们下载好的模板我的项目看看:
通过命令增加我的项目模板配置
当初咱们可能通过命令拉取构建我的项目了,然而如果当前有了新的我的项目模板了怎么办?难道每次都是手动去批改 templates.json
吗。这当然是不合理的,所以接下来咱们要实现通过命令增加我的项目模板。
首先在 git 仓库外面新建一个我的项目模板,轻易叫什么,我这里叫 xman-mobile
,而后开始编写我的项目模板增加的逻辑和命令,新建 command/add.js:
const config = require('../templates.json');
const chalk = require('chalk');
const fs = require('fs');
const inquirer = require('inquirer');
const clear = require('clear');
module.exports = () => {clear();
inquirer.prompt([
{
name: 'templateName',
type: 'input',
message: '请输出模板名称:',
validate: function (value) {if (value.length) {if (config.templates[value]) {return '模板已存在,请从新输出';} else {return true;}
} else {return '请输出模板名称';}
},
},
{
name: 'gitLink',
type: 'input',
message: '请输出 Git https link:',
validate: function (value) {if (value.length) {return true;} else {return '请输出 Git https link';}
},
},
{
name: 'branch',
type: 'input',
message: '请输出分支名称:',
validate: function (value) {if (value.length) {return true;} else {return '请输出分支名称';}
},
}
])
.then(res => {config.templates[res.templateName] = {};
config.templates[res.templateName]['url'] = res.gitLink.replace(/[\u0000-\u0019]/g, ''); // 过滤 unicode 字符
config.templates[res.templateName]['branch'] = res.branch;
fs.writeFile(__dirname + '/../templates.json', JSON.stringify(config), 'utf-8', (err) => {if (err) {console.log(err);
} else {console.log(chalk.green('新模板增加胜利!\n'));
}
process.exit();})
})
.catch(error => {console.log(error);
console.log('产生了一个谬误:', chalk.red(JSON.stringify(error)));
process.exit();});
}
持续在 bin/xman.js 中增加命令
#!/usr/bin/env node
const {program} = require('commander');
...
program
.command('add')
.description('Add a new template')
.alias('a')
.action(() => {require('../command/add')()});
...
执行 npm link --force
,而后再执行配置好的命令 xman a
:
能够看到 templates.json
中,新的模板信息曾经被增加上了。
通过命令删除我的项目模板配置
既然有增加,那就必定有删除命令了。同样,新建 command/delete.js:
const fs = require('fs');
const config = require('../templates');
const chalk = require('chalk');
const inquirer = require('inquirer');
const clear = require('clear');
module.exports = () => {clear();
inquirer.prompt([
{
name: 'templateName',
type: 'input',
message: '请输出要删除的模板名称:',
validate: function (value) {if (value.length) {if (!config.templates[value]) {return '模板不存在,请从新输出';} else {return true;}
} else {return '请输出要删除的模板名称';}
},
}
])
.then(res => {config.templates[res.templateName] = undefined;
fs.writeFile(__dirname + '/../templates.json', JSON.stringify(config), 'utf-8', (err) => {if (err) {console.log(err);
} else {console.log(chalk.green('模板已删除!'));
}
process.exit();});
})
.catch(error => {console.log(error);
console.log('产生了一个谬误:', chalk.red(JSON.stringify(error)));
process.exit();});
}
持续增加命令:
#!/usr/bin/env node
const {program} = require('commander');
...
program
.command('delete')
.description('Delete a template')
.alias('d')
.action(() => {require('../command/delete')()});
...
执行 npm link --force
,而后再执行配置好的命令 xman d
。查看 templates.json
,咱们曾经删除了想要删除的模板信息。
通过命令疾速查看已有模板
一般来说咱们不可能记住曾经增加的所有模板,有时候须要去疾速查看。所以接下来咱们将要实现一个简略的疾速查看模板列表的命令:
新建 command/list.js
const config = require('../templates');
const chalk = require('chalk');
module.exports = () => {
let str = '';
Object.keys(config.templates).forEach((item, index, array) => {if (index === array.length - 1) {str += item;} else {str += `${item} \n`;
}
});
console.log(chalk.cyan(str));
process.exit();}
增加命令:
#!/usr/bin/env node
const {program} = require('commander');
...
program
.command('list')
.description('show temlpate list')
.alias('l')
.action(() => {require('../command/list')()});
...
执行 npm link --force
,而后再执行配置好的命令 xman l
:
通过命令查看 CLI 版本是否是最新版本
一个通用的脚手架工具必定不是本人一个人用的,应用的人可能须要晓得 CLI 是不是有最新版本,所以也须要有查看 CLI 版本的性能。
新建 bin/update.js:
const updateNotifier = require('update-notifier'); // 更新 CLI 应用程序的告诉
const chalk = require('chalk');
const pkg = require('../package.json');
const notifier = updateNotifier({
pkg,
updateCheckInterval: 1000 * 60 * 60, // 默认为 1000 * 60 * 60 * 24(1 天)})
function updateChk() {if (notifier.update) {console.log(` 有新版本可用:${chalk.cyan(notifier.update.latest)},建议您在应用前进行更新 `);
notifier.notify();} else {console.log(chalk.cyan('曾经是最新版本'));
}
};
module.exports = updateChk;
增加命令:
#!/usr/bin/env node
const {program} = require('commander');
...
program
.command('upgrade')
.description("Check the js-plugin-cli version.")
.alias('u')
.action(() => {updateChk();
});
...
执行 npm link --force
,而后再执行配置好的命令 xman u
:
到此,咱们曾经实现了一个根底但很残缺的 web 工程通用脚手架工具。大家能够依据本人的理论需要进行批改和拓展了。
总结
一个 web 工程通用脚手架的实质作用其实就是以下几点:
- 疾速的创立根底我的项目构造;
- 提供我的项目开发的标准和约定;
- 依据理论我的项目需要,定制不同的性能,来进步咱们的效率。