3-2 脚手架核心流程开发
1 脚手架准备阶段过程开发

1.1 脚手架框架代码拆包 + import-local

import-local
core > cli > bin > index.js
#! /usr/bin/env node
const importLocal = require('import-local')
if (importLocal(__filename)) {
require('npmlog').info('cli', '正在使用 imooc-cli 本地版本')
} else {
require('../lib')(process.argv.slice(2))
}
1.2 检查版本号功能开发(require加载资源类型 + npmlog封装)
core > cli > lib > index.js
'use strict';
module.exports = core;
function core() {
}
1. 检查版本号功能开发
const pkg = require('../package.json')
function checkPkgVersion() {
console.log(pkg.version)
}
2. require加载资源类型
require
加载资源类型:.js
.json
.node
.js
->module.exports
exports
.json
->JSON.parse
any
->.js
3. npmlog封装
- 安装依赖包
npmlog
utils > log > lib > index.js
'use strict';
const log = require('npmlog')
log.level = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : 'info' // 判断debug模式
log.heading = 'zmoon' // 修改前缀
log.headingStyle = { fg: 'red', bg: 'white' }
log.addLevel('success', 2000, { fg: 'green', blod: true }) // 添加自定义命令
module.exports = log
4. 检查版本号功能开发--最终版
const pkg = require('../package.json')
const log = require('@zmoon-cli-dev/log')
function checkPkgVersion() {
log.notice('cli', pkg.version)
}
1.3 检查node版本功能开发
- 安装依赖包
semver
用于版本比对 - 安装依赖包
colors
用于颜色修改
1. 定义常量
core > cli > lib > const.js
const LOWEST_NODE_VERSION = '12.0.0'
module.exports = {
LOWEST_NODE_VERSION
}
2. 检查node版本功能开发
function core() {
try {
checkNodeVersion()
} catch(e) {
log.error(e.message)
}
}
- 获取当前
node
版本号 - 比对最低版本号
core > cli > lib > index.js
const semver = require('semver')
const colors = require('colors/safe')
const constant = require('./const')
function checkNodeVersion() {
const currentVersion = process.version
const lowestNodeVersion = constant.LOWEST_NODE_VERSION
if (!semver.gte(currentVersion, lowestNodeVersion)) {
throw new Error(colors.red(`imooc-cli 需要安装v${lowestNodeVersion}以上版本的node.js`))
}
}
1.4 root账号启动检查和自动降级功能开发
function core() {
try {
checkRoot()
} catch(e) {
log.error(e.message)
}
}
- 关键点:
process.setuid()
function checkRoot() {
// console.log(process.geteuid()); window不支持
const rootCheck = require('root-check')
rootCheck()
}
1.5 用户主目录检查功能开发
- 安装依赖包
user-home
- 安装依赖包
path-exists
const userHome = require('user-home')
const pathExists = require('path-exists').sync
function checkUserHome() {
if(!userHome || !pathExists(userHome)) {
throw new Error(colors.red('当前登录用户主目录不存在!'))
}
}
1.6 入参检查和debug模式开发
- 安装依赖包
minimist
function checkInputArgs() {
const minimist = require('minimist')
args = minimist(process.argv.slice(2))
checkArgs()
}
function checkArgs() {
if(args.debug) {
process.env.LOG_LEVEL = 'verbose'
} else {
process.env.LOG_LEVEL = 'info'
}
log.level = process.env.LOG_LEVEL
}
1.7 环境变量检查功能开发
core > cli > lib > const.js
const DEFAULT_CLI_HOME = '.zmoon-cli-dev'
module.exports = {
DEFAULT_CLI_HOME
}
core > cli > lib > index.js
function checkEnv() {
createDefaultConfig()
log.verbose('环境变量', process.env.CLI_HOME_PATH)
}
function createDefaultConfig() {
const cliConfig = {
home: userHome
}
if(process.env.CLI_HOME) {
cliConfig['cliHome'] = path.join(userHome, process.env.CLI_HOME)
} else {
cliConfig['cliHome'] = path.join(userHome, constant.DEFAULT_CLI_HOME)
}
process.env.CLI_HOME_PATH = cliConfig.cliHome
}
1.8 检查是否为最新版本
1. 通过npm API模块封装
utils > get-npm-info > lib > index.js
const axios = require('axios')
const urlJoin = require('url-join')
const semver = require('semver')
// 获取相关信息
function getNpmInfo(npmName, registry) {
if(!npmName) {
return null
}
registry = registry || getDefaultRegistry()
const npmInfoUrl = urlJoin(registry, npmName)
return axios.get(npmInfoUrl).then(response => {
if(response.status === 200) {
return response.data
}
return null
}).catch(err => {
return Promise.reject(err)
})
}
function getDefaultRegistry(isOriginal = false) {
return isOriginal ? 'https://registry.npmjs.org' : 'https://registry.npm.taobao.org'
}
module.exports = {
getNpmInfo
};
core > cli > lib > index.js
async function checkGlobalUpdate() {
// 1. 获取当前版本号和模块名
const currentVersion = pkg.version
const npmName = pkg.name
// 2. 调用npm API,获取所有版本号
const { getNpmInfo } = require('@zmoon-cli-dev/get-npm-info')
const data = await getNpmInfo(npmName)
console.log(data);
}
2. npm全局更新功能开发
utils > get-npm-info > lib > index.js
// 获取所有版本号
async function getNpmVersions(npmName, registry) {
const data = await getNpmInfo(npmName, registry)
if(data) {
return Object.keys(data.versions)
} else {
return []
}
}
// 获取大于baseVersion的版本
function getSemverVersions(baseVersion, versions) {
versions = versions
.filter(version => semver.satisfies(version, `^${baseVersion}`))
.sort((a, b) => semver.gt(b, a))
return versions
}
// 获取最新版本号
async function getNpmSemverVersion(baseVersion, npmName, registry) {
const versions = await getNpmVersions(npmName, registry)
const newVersions = getSemverVersions(baseVersion, versions)
if(newVersions && newVersions.length) {
return newVersions[0]
} else {
return versions[0]
}
}
core > cli > lib > index.js
async function checkGlobalUpdate() {
// 1. 获取当前版本号和模块名
const currentVersion = pkg.version
const npmName = pkg.name
// 2. 调用npm API,获取所有版本号
const { getNpmSemverVersion } = require('@zmoon-cli-dev/get-npm-info')
// 3. 提取所有版本号,比对哪些版本号是大于当前版本号
const lastVersion = await getNpmSemverVersion(currentVersion, npmName)
// 4. 获取最新的版本号,提示用户更新到该版本
if(lastVersion && semver.gt(lastVersion, currentVersion)) {
log.warn('更新提示',
colors.yellow(`
请手动更新 ${npmName},当前版本:${currentVersion},最新版本:${lastVersion}
更新命令:npm install -g ${npmName}`)
)
}
}
2 注册命令

2.1 快速实现一个commander脚手架
const commander = require('commander')
const pkg = require('../package.json')
-
方法一: 获取
commander
的单例const { program } = commander
-
方法二: 实例化一个
Command
实例const program = new commander.Command()
2.2 commander脚手架全局配置
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
.option('-d, --debug', '是否开启调试模式', false)
.option('-e, --envName <envName>', '获取环境变量名称')
.parse(process.argv)
2.3 commander脚手架命令注册的两种方法
1. command注册命令
const clone = program.command('clone <source> [destination]')
clone
.description('clone a repository')
.option('-f, --force', '是否强制克隆')
.action((source, destination, cmdObj) => {
console.log('do clone', source, destination, cmdObj.force);
})
2. addCommand注册命令
const service = new commander.Command('service')
service
.command('start [port]')
.description('start service st some port')
.action((port) => {
console.log('do service start', port);
})
service
.command('stop')
.description('stop service')
.action(() => {
console.log('stop service');
})
program.addCommand(service)
2.4 commander的5种高级用法
1. 通过arguments监听命令注册
program
.arguments('<cmd> [oprions]')
.description('test command', {
cmd: 'command to run',
options: 'options for command'
})
.action((cmd, env) => {
console.log(cmd, env);
})
2. command
program
.command('install [name]', 'install package')
.alias('i')
program
.arguments('<cmd> [oprions]')
.description('test command', {
cmd: 'command to run',
options: 'options for command'
})
.action((cmd, env) => {
console.log(cmd, env);
})
3. 自定义help信息
program.helpInformation = () => {
return ''
}
// EventImit
program.on('--help', () => {
console.log('your help information');
})
4. 实现debug模式
program.on('option:debug', () => {
if(program.debug) {
program.env.LOG_LEVEL = 'verbose'
}
console.log(process.env.LOG_LEVEL);
})
5. 对未知命令进行监听
program.on('command:*', (obj) => {
console.log(obj);
console.error('未知的命令:' + obj[0]);
const availableCommands = program.commands.map(cmd => cmd.name())
console.log(availableCommands);
console.log('可用命令:' + availableCommands.join(', '));
})
3 Node项目如何支持ESModule
3.1 通过webpack完成ESModule资源构建
- webpack.config.js
commonjs
规范
const path = require('path')
module.exports = {
entry: './bin/core.js',
output: {
path: path.join(__dirname, '/dist'),
filename: 'core.js'
},
mode: 'development'
}
- package.json
"scripts": {
"build": "webpack",
"dev": "webpack -w"
},
3.2 通过webpack target属性支持node内置库
module.exports = {
// ...
target: 'node' // 默认 web
}
3.3 webpack loader配置babel-loader支持低版本node
- 安装依赖
npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/runtime-corejs3
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|dist)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
[
'@babel/plugin-transform-runtime',
{
corejs: 3,
regenerator: true,
useESModule: true,
helpers: true
}
]
]
}
}
},
]
}
3.4 通过node原生支持ESModule
- 文件后缀
.mjs
node --experimental-modules bin/index.mjs
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)