作业
脚手架架构设计图
脚手架准备过程代码
主要函数
async function core() {
try {
// 检查 node 版本号
checkNodeVersion();
// root 降级
checkRoot();
// 检查主目录
checkUserHome();
// 检查入参
checkInputArgs();
// 检查环境变量
checkEnv();
// 检查更新
await checkGlobalUpdate();
// 命令
commands(pkg);
} catch (error) {
log.error(colors.red(error.message));
}
}
检查node版本号
function checkNodeVersion() {
const currentNodeVersion = process.version;
const lowestVersion = constant.LOWEST_NODE_VERSION;
if(semver.lt(currentNodeVersion, lowestVersion)) {
throw new Error(colors.red(`需要安装 ${lowestVersion} 及以上版本的 node`));
}
}
root 降级
function checkRoot() {
const rootCheck = require('root-check');
rootCheck();
}
检查主目录
function checkUserHome() {
if (!userHome || !pathExists(userHome)) {
throw new Error('当前用户主目录不存在!');
}
}
检查入参
function checkInputArgs() {
const minimist = require('minimist');
args = minimist(process.argv.slice(2));
checkArgs();
log.verbose('debug', 'test deubug level');
}
检查更新
async function checkGlobalUpdate() {
const currentVersion = pkg.version;
const npmName = pkg.name;
const lastVersion = await getNpmSemverVersion(currentVersion, npmName);
if (lastVersion && semver.gt(lastVersion, currentVersion)) {
log.warn(colors.yellow(`最新版本号:${lastVersion} 请使用 npm install -g ${npmName} 更新版本! 获得更好的使用体验!`));
}
}
检查变量
function checkEnv() {
const dotEnv = require('dotenv');
const dotEnvPath = path.resolve(userHome);
if (pathExists(dotEnvPath)) {
config = dotEnv.config({
path: dotEnvPath
});
}
createDefaultConfig();
log.verbose('环境变量', config);
}
创建默认配置
function createDefaultConfig() {
const cLiConfig = {};
if (process.env.FE_LAZY_CLI_HOME) {
cLiConfig['feLazyCliHome'] = path.join(userHome, process.env.FE_LAZY_CLI_HOME);
} else {
cLiConfig['feLazyCliHome'] = path.join(userHome, constant.DEFAULT_CLI_HOME)
}
process.env.FE_LAZY_CLI_HOME = cLiConfig.feLazyCliHome;
}
检查是否开启debug模式
function checkArgs() {
if (args.debug) {
process.env.LOG_LEVEL = 'verbose';
} else {
process.env.LOG_LEVEL = 'info';
}
log.level = process.env.LOG_LEVEL;
}
通过 commander 框架实现一个脚手架,包含自定义 option 和 command 功能
文件头
'use strict';
const commander = require('commander');
const colors = require('colors');
const log = require('@fe-lazy-cli/log');
// 获取 commander 的单例
// const { program } = commander;
// 手动实例化一个 commander
const program = new commander.Command();
module.exports = command;
脚手架主命令
function command(pkg) {
program
.name(Object.keys(pkg.bin)[0])
.usage("<command> [option]")
.version(pkg.version)
.option('-d, --debug [debugModule]', '是否开启 debug 模式', false)
.option('-v, -V, --version', "输出版本信息");
regCommand();
regAddCommand();
// 对未知命令进行提示
program.on('command:*',(arg) => {
let commands = program.commands.map(item => {
return {
name: item._name.split(''),
consistency: 0
};
})
commands = commands.map(item => {
let argArr = arg[0].split('');
let consistency = item.consistency
item.name.map(c => {
if (argArr.indexOf(c) >= 0) {
consistency ++;
argArr.splice(argArr.indexOf(c), 1);
}
})
return {
name: item.name.join(''),
consistency
};
});
commands = commands.filter(item => item.consistency > 0).sort((a, b) => b.consistency - a.consistency);
commands = commands && commands.length && commands[0].name || false;
let argText = arg[0] || "";
if (commands) {
throw new Error(colors.red(`抱歉!没有找到 '${argText}' 命令. 是否要运行 '${commands}' 命令. 或者运行 '${Object.keys(pkg.bin)[0]} --help' 查看帮助信息!`));
}
throw new Error(colors.red(`抱歉!没有找到 '${argText}' 命令. 请运行 '${Object.keys(pkg.bin)[0]} --help' 查看帮助信息!`));
});
program
.parse(process.argv);
}
普通命令注册方式
function regCommand() {
const echo = program.command('echo <envName> [argv]');
echo
.description('输出当前指令')
.option('-t, --test', '配置', false)
.action((envName, argv, option) => {
log.info('测试输出', envName, argv, option.test);
})
}
addCommand 命令注册方式
function regAddCommand() {
const print = new commander.Command('print');
print
.alias('p')
.description('输出指定数据')
.command('int <number>')
// .command(
// 'init <number>',
// '描述', // 添加描述后 执行当前脚手架 + '-init'的脚手架: fe-lazy-cli-init
// {
// executableFile: 'fe', // 添加选项 执行 fe 脚手架
// isDefault: true, // 默认的执行命令
// hidden: true // 隐藏命令
// }
// )
.description('输出整数')
.option('--hex <hexNumber>', '进制', 10)
.action((number, option) => {
log.info(`${option.hex} 进制`, parseInt(number, option.hex));
});
program.addCommand(print);
}
通过 webpack 和原生两种方式实现 node 对 es module 的支持
webpack + babel
安装 npm 包
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/runtime-corejs3": "^7.12.5",
"babel-loader": "^8.2.2",
"webpack": "^5.13.0",
"webpack-cli": "^4.3.1"
webpack.config.js
const path = require('path');
module.exports = {
entry: './bin/core.js', // 入口文件
mode: 'development', // 开发模式,development => 开发模式 production =》 生产模式
output: { // 输出
path: path.join(__dirname, '/dist'), // 输出目录
filename: 'core.js' // 输出的文件名
},
target: 'node', // 默认web,因为需要使用到node原生模块,所以需要更改为node
module: {
rules: [{ // 配置babel-loader
test: /\.js$/, // 处理js
exclude: /(node_modules|dist)/, // 排除node_modules和dist目录
use: {
loader: 'babel-loader', // 使用babel对js进行低版本兼容处理
options: {
presets: ['@babel/preset-env'],
plugins: [
[
'@babel/plugin-transform-runtime',
{
'corejs': 3,
'regenerator': true,
"useESModules": true,
'helpers': true
}]
]
}
}
}]
}
}
Node原生支持 ES Module
- 更改js文件后缀为.mjs
- import js的时候,不能省略后缀名
- 是实验性质特性,在node版本v14.x.x以后支持该特性
node --experimental-modules ./index.mjs