首页 > 其他分享 >4-2 脚手架执行流程开发 -- 封装通用的 Package & Commond 类

4-2 脚手架执行流程开发 -- 封装通用的 Package & Commond 类

时间:2022-11-25 15:15:19浏览次数:47  
标签:const Package -- argv js init Commond targetPath

1 脚手架命令动态加载功能架构设计

1.1 指定本地调试文件路径 targetPath

  • core>cli>bin>index.js
function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)
    .option('-d, --debug', '是否开启调试模式', false)
    .option('-tp, --targetPath <targetPath>', '是否指定本地调试文件路径', '')

  program
    .command('init [projectName]')
    .option('-f, --force', '是否强制初始化项目')
    .action(exec)

  // 指定targetPath
  program.on('option:targetPath', function() {
    process.env.CLI_TARGET_PATH = program._optionValues.targetPath
  })

  program.parse(process.argv)
  if(program.args && program.args.length < 1) {
    program.outputHelp()
  }
}
  • commands>init>lib>index.js
function init(projectName, cmdObj) {
  console.log('init', projectName, cmdObj.force, process.env.CLI_TARGET_PATH);
}

1.2 动态执行库 exec 模块创建

lerna create exec

function exec() {
  console.log('targetPath', targetPath)
  console.log('homePath', homePath)
}
module.exports = exec;

1.3 创建 npm 模块通用类 Package

  • models>package>lib>index.js
'use strict';

class Package {
  constructor(options) {
  }
}

module.exports = Package;
  • core>exec>lib>index.js
const Package = require('@zmoon-cli-dev/package')
const log = require('@zmoon-cli-dev/log')
function exec() {
  const targetPath = process.env.CLI_TARGET_PATH
  const homePath = process.env.CLI_HOME_PATH
  log.verbose('targetPath', targetPath)
  log.verbose('homePath', homePath)
  const pkg = new Package({
      targetPath
  })
  console.log(pkg);
}
module.exports = exec;

1.4 Package 类的属性、方法定义及构造函数

1. Package 类的属性、方法定义

class Package {
  constructor(options) {
    // package的路径
    this.targetPath = options.targetPath
    // package的存储路径
    this.storePath = options.storePath
    // package的name
    this.packageName = options.name
    // package的version
    this.packageVersion = options.version
  }
  // 判断当前Package是否存在
  exists() {}
  // 安装Package
  install() {}
  // 更新Package
  update() {}
  // 获取入口文件的路径
  getRootFilePath() {}
}

2. Package 构造函数逻辑开发

  • models>package>lib>index.js
const { isObject } = require('@zmoon-cli-dev/utils') 
class Package {
  constructor(options) {
    if(!options) {
      throw new Error('Package类的options参数不能为空!')
    }
    if(!isObject(options)) {
      throw new Error('Package类的options参数必须为对象!')
    }
    // package的路径
    this.targetPath = options.targetPath
    // package的存储路径
    this.storePath = options.storePath
    // package的name
    this.packageName = options.packageName
    // package的version
    this.packageVersion = options.packageVersion
  }
}
  • utils>utils>lib>index.js
function isObject(o) {
  return Object.prototype.toString.call(o) === '[object Object]'
}
  • core>exec>lib>index.js
const SETTINGS = {
  init: '@zmoon-cli-dev/init'
}

function exec() {
  const targetPath = process.env.CLI_TARGET_PATH
  const homePath = process.env.CLI_HOME_PATH

  const cmdObj = arguments[arguments.length-1]
  const cmdName = cmdObj.name()
  const packageName = SETTINGS[cmdName]
  const packageVersion = 'latest'

  const pkg = new Package({
    targetPath,
    packageName,
    packageVersion
  })
}

1.5 Package 类获取文件入口路径

pkg-dir 应用+解决不同操作系统路径兼容问题

1. 获取package.json所在目录 -- pkg-dir

const pkgDir = require('pkg-dir').sync
getRootFilePath() {
  const dir = pkgDir(this.targetPath)
}

2. 读取package.json -- require() js/json/node

const pkgFile = require(path.resolve(dir, 'package.json'))

3. 寻找main/lib -- path

if(pkgFile && pkgFile.main) {}

4. 路径的兼容(macOS/windows)

if(pkgFile && pkgFile.main) {
  return formatPath(path.resolve(dir, pkgFile.main))
}
  • utils>format-path>lib>index.js
const path = require('path')

function formatPath(p) {
  if(p && typeof p === 'string') {
    // 分隔符
    const sep = path.sep
    if(sep === '/') {
      return p.replace(/\//g, '\\')
    } else {
      return p
    }
  }
  return p
}

1.6 利用 npminstall 库安装 npm 模块

  • models>package>lib>index.js
const npminstall = require('npminstall')
const { getDefaultRegistry } = require('@zmoon-cli-dev/get-npm-info')
// 安装Package
install() {
  return npminstall({
    root: this.targetPath, // 模块路径
    storeDir: this.storeDir,
    registry: getDefaultRegistry(),
    pkgs: [{
      name: this.packageName,
      version: this.packageVersion
    }]
  })
}
  • utils>get-npm-info>lib>index.js
function getDefaultRegistry(isOriginal = false) {
  return isOriginal ? 'https://registry.npmjs.org' : 'https://registry.npm.taobao.org'
}
  • core>exec>lib>index.js
const SETTINGS = {
  init: '@zmoon-cli-dev/init'
}
const CACHE_DIR = 'dependencies'

function exec() {
  let targetPath = process.env.CLI_TARGET_PATH
  const homePath = process.env.CLI_HOME_PATH
  let storeDir = ''
  let pkg

  const cmdObj = arguments[arguments.length-1]
  const cmdName = cmdObj.name()
  const packageName = SETTINGS[cmdName]
  const packageVersion = 'latest'

  if(!targetPath) {
    targetPath = path.resolve(homePath, CACHE_DIR) // 生成缓存路径
    storeDir = path.resolve(targetPath, 'node_modules')
    pkg = new Package({
      targetPath,
      storeDir,
      packageName,
      packageVersion
    })
    if(pkg.exists()) {
      // 更新Package
    } else {
      // 安装Package
      await pkg.install()
    }
  } else {
    pkg = new Package({
      targetPath,
      packageName,
      packageVersion
    })
  }
  const rootFile = pkg.getRootFilePath()
  if(rootFile) {
    require(rootFile).apply(null, arguments) // [] -> 参数列表
  }
}

1.7 Package 类判断模块是否存在

  • models>package>lib>index.js
// package的缓存目录前缀
// this.cacheFilePathPrefix = this.packageName.replace('/', '_')
async exists() {
  if(this.storeDir) {
    await this.prepare()
    return pathExists(this.cacheFilePath)
  } else {
    return pathExists(this.targetPath)
  }
}
async prepare() {
  if(this.packageVersion === 'latest') {
    this.packageVersion = await getNpmLatestVersion(this.packageName)
  }
}

// @imooc-cli/init 1.1.2 -> _@[email protected]@@imooc-cli
get cacheFilePath() {
  return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${this.packageVersion}@${this.packageName}`)
}
  • utils>get-npm-info>lib>index.js
async function getNpmLatestVersion(npmName, registry) {
  const version = await getNpmVersions(npmName, registry)
  if(version) {
    // return version.sort((a, b) => semver.gt(b, a))[0]
    return version[version.length-1]
  }
  return null
}

1.8 Package 类更新模块

  • models>package>lib>index.js
// 最新版本存在则不需要更新
async update() {
  await this.prepare()
  // 1. 获取最新的npm模块版本号
  const latestPackageVersion = await getNpmLatestVersion(this.packageName)
  // 2. 查询最新版本号对应的路径是否存在
  const latestFilePath = this.getSpecificCacheFilePath(latestPackageVersion)
  // 3. 如果不存在,则直接安装最新版本
  if(!pathExists(latestFilePath)) {
    return npminstall({
      root: this.targetPath, // 模块路径
      storeDir: this.storeDir,
      registry: getDefaultRegistry(),
      pkgs: [{
        name: this.packageName,
        version: latestPackageVersion
      }]
    })
  }
  return latestFilePath
}
async prepare() {
  if(this.storeDir && !pathExists(this.storeDir)) {
    fse.mkdirSync(this.storeDir) // 将当前路径不存在的文件都创建
  }
  if(this.packageVersion === 'latest') {
    this.packageVersion = await getNpmLatestVersion(this.packageName)
  }
}
getSpecificCacheFilePath(packageVersion) {
  return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${packageVersion}@${this.packageName}`)
}

1.9 Package 类获取缓存模块入口文件功能改造

  • models>package>lib>index.js
// 获取入口文件的路径
getRootFilePath() {
  function _getRootFile(targetPath) {
    // 1. 获取package.json所在目录 -- pkg-dir
    const dir = pkgDir(targetPath)
    if(dir) {
      // 2. 读取package.json -- require() js/json/node
      const pkgFile = require(path.resolve(dir, 'package.json'))
      // 3. 寻找main/lib -- path
      if(pkgFile && pkgFile.main) {
        // 4. 路径的兼容(macOS/windows)
        return formatPath(path.resolve(dir, pkgFile.main))
      }
    }
    return null
  }
  if(this.storeDir) {
    return _getRootFile(this.cacheFilePath)
  } else {
    return _getRootFile(this.targetPath)
  }
}

2 通用脚手架命令 Command 类封装

2.1 commander 脚手架初始化

  • models>command>lib>index.js
class Command {
  constructor() {}
  init() {} // 准备阶段
  exec() {} // 执行阶段

}
module.exports = Command

2.2 动态加载 initCommand + new initCommand

  • commands>init>lib>index.js
const Command = require('@zmoon-cli-dev/command')
class InitCommand extends Command {}
function init() {
  return new InitCommand()
}
module.exports = init
module.exports.InitCommand = InitCommand

2.3 Command constructor

  • commands>init>lib>index.js
const Command = require('@zmoon-cli-dev/command')
class InitCommand extends Command {}
function init(argv) {
  return new InitCommand(argv)
}
module.exports = init
module.exports.InitCommand = InitCommand
  • models>command>lib>index.js
class Command {
  constructor(argv) {
    console.log('Commond constructor', argv);
    this._argv = argv
    let runner = new Promise((resolve, reject) => {
      let chain = Promise.resolve()
      chain = chain.then(() => {})
    })
  }
  init() {
    throw new Error('init 必须实现')
  }
  exec() {
    throw new Error('init 必须实现')
  }
}

2.4 命令的准备阶段 -- 检查 node 版本

  • models>command>lib>index.js
const semver = require('semver')
const colors = require('colors/safe')
const log = require('@zmoon-cli-dev/log')

const LOWEST_NODE_VERSION = '12.0.0'

class Command {
  constructor(argv) {
    this._argv = argv
    let runner = new Promise((resolve, reject) => {
      let chain = Promise.resolve()
      chain = chain.then(() => this.checkNodeVersion())
      chain.catch(err => {
        log.error(err.message);
      })
    })
  }
  // 检查node版本
  checkNodeVersion() {
    // 1. 获取当前 node 版本号
    const currentVersion = process.version
    // 2. 比对最低版本号
    const lowestNodeVersion = LOWEST_NODE_VERSION
    if (!semver.gte(currentVersion, lowestNodeVersion)) {
      throw new Error(colors.red(`imooc-cli 需要安装v${lowestNodeVersion}以上版本的node.js`))
    }
  }
}

2.5 命令的准备阶段 -- 参数初始化

通过异步执行的命令都需要单独进行错误捕获
即每次新建 promise 都需要有单独的 catch

  • models>command>lib>index.js
class Command {
  constructor(argv) {
    // console.log('Commond constructor', argv);
    if(!argv) {
      throw new Error('参数不能为空!')
    }
    if(!Array.isArray(argv)) {
      throw new Error('参数必须为数组!')
    }
    if(argv.length < 1) {
      throw new Error('参数列表为空!')
    }
    this._argv = argv
    let runner = new Promise((resolve, reject) => {
      let chain = Promise.resolve()
      chain = chain.then(() => this.checkNodeVersion())
      chain = chain.then(() => this.initArgs())
      chain = chain.then(() => this.init())
      chain = chain.then(() => this.exec())
      chain.catch(err => {
        log.error(err.message);
      })
    })
  }
  initArgs() {
    this.cmd = this._argv[this._argv.length-1]
    this._argv = this._argv.slice(0, this._argv.length-1)
  }
  // ...
  init() {
    throw new Error('init 必须实现!')
  }
  exec() {
    throw new Error('exec 必须实现!')
  }
}
  • commands>init>lib>index.js
class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || ''
    this.force = !!this.cmd._optionValues.force
    log.verbose('projectName', this.projectName);
    log.verbose('force', this.force);
  }
  exec() {}
}

标签:const,Package,--,argv,js,init,Commond,targetPath
From: https://www.cnblogs.com/pleaseAnswer/p/16925191.html

相关文章

  • 多态性
     /*面向对象特征之三:多态性​1.理解多态性:可以理解为一个事物的多种形态2.何为多态性:  对象(子类)的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)3.多态......
  • 2023-小侯七四套卷-数学一
    2023-小侯七4-1这张卷子好多小题的计算量都比得上真题的一道大题大题数列题送分,二重积分凭计算量实力压轴,就像我们当年高考排列组合放T22,概率放T23一样奇怪T17,T20还是错......
  • 解析excel后进行excel导入-vue
    遇到了一个很奇葩的需求,就是把excel的每一行都提出来转成json后一个个请求新增接口进行导入需要用到xlsx库npminstall--savexlsximportxlsxfrom'xlsx';用到的......
  • 0004.JQuery介绍
    一、JQuery介绍1.JQuery介绍JQuery是一个JavaScript库,也是一个JS文件。JQ中封装实现了很多方法,让使用变得更加简单不再像js那样需要使用大量的方法调用。但JQ也只是实现......
  • 设计模式
    单例模式:确保全局只有一个该类的实例预加载:占用内存,将类的实例化私有,在类中声明一个静态的实例。外部可以直接调用。懒加载:无需占用内存,将类的实例化私有,且在类中创建一......
  • vue3 父子组件传参,provide/inject 共享方法和参数,父组件调用子组件方法,子组件调用
    一、父组件给子组件传参propsprops用法vueprops:{xxxx:{type:Object,default:null}}以下有注释的部分是需要写的代码以下例子是父组件(列表页)加载公共的操作......
  • TypeScript--高级用法
    TypeScript--高级用法1.运算符可选链运算符?.判断左侧的表达式是否是null或者undefined,如果是,则会停止表达式的运行,减少我们大量的&&运算obj?.propobj?.[i......
  • 云享·人物丨造梦、探梦、筑梦,三位开发者在华为云上的寻梦之旅
    摘要:走近华为云开发者日HDC.CloudDay,看三位特别的开发者用技术改变世界,用创造力让生活更美好。本文分享自华为云社区《云享·人物丨造梦、探梦、筑梦,三位开发者在华为云......
  • TypeScript - -类型实战
    TypeScript--类型实战下面介绍的几个常见实战操作,数量不多,但是提供了一些思路,学习理解这些思路,和js实现的区别。为自己写代码的时候打下小小的基础1.实现返回promi......
  • postMan 测试webService接口 参数传递问题
    一、参数没有子节点的方式<?xmlversion="1.0"encoding="utf-8"?><soap:Envelopexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.......