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
  1. .js -> module.exports exports
  2. .json -> JSON.parse
  3. 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)
  }
}
  1. 获取当前 node 版本号
  2. 比对最低版本号

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
posted on 2022-10-09 14:56  pleaseAnswer  阅读(62)  评论(0编辑  收藏  举报